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, ""));
4469 * @param {Mixed} s The value being converted
4470 * @return {Number} The comparison value
4472 asInt : function(s) {
4473 var val = parseInt(String(s).replace(/,/g, ""));
4481 * Ext JS Library 1.1.1
4482 * Copyright(c) 2006-2007, Ext JS, LLC.
4484 * Originally Released Under LGPL - original licence link has changed is not relivant.
4487 * <script type="text/javascript">
4491 * @class Roo.data.Record
4492 * Instances of this class encapsulate both record <em>definition</em> information, and record
4493 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4494 * to access Records cached in an {@link Roo.data.Store} object.<br>
4496 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4497 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4500 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4502 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4503 * {@link #create}. The parameters are the same.
4504 * @param {Array} data An associative Array of data values keyed by the field name.
4505 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4506 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4507 * not specified an integer id is generated.
4509 Roo.data.Record = function(data, id){
4510 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4515 * Generate a constructor for a specific record layout.
4516 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4517 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4518 * Each field definition object may contain the following properties: <ul>
4519 * <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,
4520 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4521 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4522 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4523 * is being used, then this is a string containing the javascript expression to reference the data relative to
4524 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4525 * to the data item relative to the record element. If the mapping expression is the same as the field name,
4526 * this may be omitted.</p></li>
4527 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4528 * <ul><li>auto (Default, implies no conversion)</li>
4533 * <li>date</li></ul></p></li>
4534 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4535 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4536 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4537 * by the Reader into an object that will be stored in the Record. It is passed the
4538 * following parameters:<ul>
4539 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4541 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4543 * <br>usage:<br><pre><code>
4544 var TopicRecord = Roo.data.Record.create(
4545 {name: 'title', mapping: 'topic_title'},
4546 {name: 'author', mapping: 'username'},
4547 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4548 {name: 'lastPost', mapping: 'post_time', type: 'date'},
4549 {name: 'lastPoster', mapping: 'user2'},
4550 {name: 'excerpt', mapping: 'post_text'}
4553 var myNewRecord = new TopicRecord({
4554 title: 'Do my job please',
4557 lastPost: new Date(),
4558 lastPoster: 'Animal',
4559 excerpt: 'No way dude!'
4561 myStore.add(myNewRecord);
4566 Roo.data.Record.create = function(o){
4568 f.superclass.constructor.apply(this, arguments);
4570 Roo.extend(f, Roo.data.Record);
4571 var p = f.prototype;
4572 p.fields = new Roo.util.MixedCollection(false, function(field){
4575 for(var i = 0, len = o.length; i < len; i++){
4576 p.fields.add(new Roo.data.Field(o[i]));
4578 f.getField = function(name){
4579 return p.fields.get(name);
4584 Roo.data.Record.AUTO_ID = 1000;
4585 Roo.data.Record.EDIT = 'edit';
4586 Roo.data.Record.REJECT = 'reject';
4587 Roo.data.Record.COMMIT = 'commit';
4589 Roo.data.Record.prototype = {
4591 * Readonly flag - true if this record has been modified.
4600 join : function(store){
4605 * Set the named field to the specified value.
4606 * @param {String} name The name of the field to set.
4607 * @param {Object} value The value to set the field to.
4609 set : function(name, value){
4610 if(this.data[name] == value){
4617 if(typeof this.modified[name] == 'undefined'){
4618 this.modified[name] = this.data[name];
4620 this.data[name] = value;
4621 if(!this.editing && this.store){
4622 this.store.afterEdit(this);
4627 * Get the value of the named field.
4628 * @param {String} name The name of the field to get the value of.
4629 * @return {Object} The value of the field.
4631 get : function(name){
4632 return this.data[name];
4636 beginEdit : function(){
4637 this.editing = true;
4642 cancelEdit : function(){
4643 this.editing = false;
4644 delete this.modified;
4648 endEdit : function(){
4649 this.editing = false;
4650 if(this.dirty && this.store){
4651 this.store.afterEdit(this);
4656 * Usually called by the {@link Roo.data.Store} which owns the Record.
4657 * Rejects all changes made to the Record since either creation, or the last commit operation.
4658 * Modified fields are reverted to their original values.
4660 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4661 * of reject operations.
4663 reject : function(){
4664 var m = this.modified;
4666 if(typeof m[n] != "function"){
4667 this.data[n] = m[n];
4671 delete this.modified;
4672 this.editing = false;
4674 this.store.afterReject(this);
4679 * Usually called by the {@link Roo.data.Store} which owns the Record.
4680 * Commits all changes made to the Record since either creation, or the last commit operation.
4682 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4683 * of commit operations.
4685 commit : function(){
4687 delete this.modified;
4688 this.editing = false;
4690 this.store.afterCommit(this);
4695 hasError : function(){
4696 return this.error != null;
4700 clearError : function(){
4705 * Creates a copy of this record.
4706 * @param {String} id (optional) A new record id if you don't want to use this record's id
4709 copy : function(newId) {
4710 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4714 * Ext JS Library 1.1.1
4715 * Copyright(c) 2006-2007, Ext JS, LLC.
4717 * Originally Released Under LGPL - original licence link has changed is not relivant.
4720 * <script type="text/javascript">
4726 * @class Roo.data.Store
4727 * @extends Roo.util.Observable
4728 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4729 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4731 * 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
4732 * has no knowledge of the format of the data returned by the Proxy.<br>
4734 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4735 * instances from the data object. These records are cached and made available through accessor functions.
4737 * Creates a new Store.
4738 * @param {Object} config A config object containing the objects needed for the Store to access data,
4739 * and read the data into Records.
4741 Roo.data.Store = function(config){
4742 this.data = new Roo.util.MixedCollection(false);
4743 this.data.getKey = function(o){
4746 this.baseParams = {};
4753 "multisort" : "_multisort"
4756 if(config && config.data){
4757 this.inlineData = config.data;
4761 Roo.apply(this, config);
4763 if(this.reader){ // reader passed
4764 this.reader = Roo.factory(this.reader, Roo.data);
4765 this.reader.xmodule = this.xmodule || false;
4766 if(!this.recordType){
4767 this.recordType = this.reader.recordType;
4769 if(this.reader.onMetaChange){
4770 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4774 if(this.recordType){
4775 this.fields = this.recordType.prototype.fields;
4781 * @event datachanged
4782 * Fires when the data cache has changed, and a widget which is using this Store
4783 * as a Record cache should refresh its view.
4784 * @param {Store} this
4789 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4790 * @param {Store} this
4791 * @param {Object} meta The JSON metadata
4796 * Fires when Records have been added to the Store
4797 * @param {Store} this
4798 * @param {Roo.data.Record[]} records The array of Records added
4799 * @param {Number} index The index at which the record(s) were added
4804 * Fires when a Record has been removed from the Store
4805 * @param {Store} this
4806 * @param {Roo.data.Record} record The Record that was removed
4807 * @param {Number} index The index at which the record was removed
4812 * Fires when a Record has been updated
4813 * @param {Store} this
4814 * @param {Roo.data.Record} record The Record that was updated
4815 * @param {String} operation The update operation being performed. Value may be one of:
4817 Roo.data.Record.EDIT
4818 Roo.data.Record.REJECT
4819 Roo.data.Record.COMMIT
4825 * Fires when the data cache has been cleared.
4826 * @param {Store} this
4831 * Fires before a request is made for a new data object. If the beforeload handler returns false
4832 * the load action will be canceled.
4833 * @param {Store} this
4834 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4838 * @event beforeloadadd
4839 * Fires after a new set of Records has been loaded.
4840 * @param {Store} this
4841 * @param {Roo.data.Record[]} records The Records that were loaded
4842 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4844 beforeloadadd : true,
4847 * Fires after a new set of Records has been loaded, before they are added to the store.
4848 * @param {Store} this
4849 * @param {Roo.data.Record[]} records The Records that were loaded
4850 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4851 * @params {Object} return from reader
4855 * @event loadexception
4856 * Fires if an exception occurs in the Proxy during loading.
4857 * Called with the signature of the Proxy's "loadexception" event.
4858 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4861 * @param {Object} return from JsonData.reader() - success, totalRecords, records
4862 * @param {Object} load options
4863 * @param {Object} jsonData from your request (normally this contains the Exception)
4865 loadexception : true
4869 this.proxy = Roo.factory(this.proxy, Roo.data);
4870 this.proxy.xmodule = this.xmodule || false;
4871 this.relayEvents(this.proxy, ["loadexception"]);
4873 this.sortToggle = {};
4874 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4876 Roo.data.Store.superclass.constructor.call(this);
4878 if(this.inlineData){
4879 this.loadData(this.inlineData);
4880 delete this.inlineData;
4884 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4886 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
4887 * without a remote query - used by combo/forms at present.
4891 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4894 * @cfg {Array} data Inline data to be loaded when the store is initialized.
4897 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4898 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4901 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4902 * on any HTTP request
4905 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4908 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4912 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4913 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4918 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4919 * loaded or when a record is removed. (defaults to false).
4921 pruneModifiedRecords : false,
4927 * Add Records to the Store and fires the add event.
4928 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4930 add : function(records){
4931 records = [].concat(records);
4932 for(var i = 0, len = records.length; i < len; i++){
4933 records[i].join(this);
4935 var index = this.data.length;
4936 this.data.addAll(records);
4937 this.fireEvent("add", this, records, index);
4941 * Remove a Record from the Store and fires the remove event.
4942 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4944 remove : function(record){
4945 var index = this.data.indexOf(record);
4946 this.data.removeAt(index);
4947 if(this.pruneModifiedRecords){
4948 this.modified.remove(record);
4950 this.fireEvent("remove", this, record, index);
4954 * Remove all Records from the Store and fires the clear event.
4956 removeAll : function(){
4958 if(this.pruneModifiedRecords){
4961 this.fireEvent("clear", this);
4965 * Inserts Records to the Store at the given index and fires the add event.
4966 * @param {Number} index The start index at which to insert the passed Records.
4967 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4969 insert : function(index, records){
4970 records = [].concat(records);
4971 for(var i = 0, len = records.length; i < len; i++){
4972 this.data.insert(index, records[i]);
4973 records[i].join(this);
4975 this.fireEvent("add", this, records, index);
4979 * Get the index within the cache of the passed Record.
4980 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4981 * @return {Number} The index of the passed Record. Returns -1 if not found.
4983 indexOf : function(record){
4984 return this.data.indexOf(record);
4988 * Get the index within the cache of the Record with the passed id.
4989 * @param {String} id The id of the Record to find.
4990 * @return {Number} The index of the Record. Returns -1 if not found.
4992 indexOfId : function(id){
4993 return this.data.indexOfKey(id);
4997 * Get the Record with the specified id.
4998 * @param {String} id The id of the Record to find.
4999 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
5001 getById : function(id){
5002 return this.data.key(id);
5006 * Get the Record at the specified index.
5007 * @param {Number} index The index of the Record to find.
5008 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5010 getAt : function(index){
5011 return this.data.itemAt(index);
5015 * Returns a range of Records between specified indices.
5016 * @param {Number} startIndex (optional) The starting index (defaults to 0)
5017 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5018 * @return {Roo.data.Record[]} An array of Records
5020 getRange : function(start, end){
5021 return this.data.getRange(start, end);
5025 storeOptions : function(o){
5026 o = Roo.apply({}, o);
5029 this.lastOptions = o;
5033 * Loads the Record cache from the configured Proxy using the configured Reader.
5035 * If using remote paging, then the first load call must specify the <em>start</em>
5036 * and <em>limit</em> properties in the options.params property to establish the initial
5037 * position within the dataset, and the number of Records to cache on each read from the Proxy.
5039 * <strong>It is important to note that for remote data sources, loading is asynchronous,
5040 * and this call will return before the new data has been loaded. Perform any post-processing
5041 * in a callback function, or in a "load" event handler.</strong>
5043 * @param {Object} options An object containing properties which control loading options:<ul>
5044 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5045 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5046 * passed the following arguments:<ul>
5047 * <li>r : Roo.data.Record[]</li>
5048 * <li>options: Options object from the load call</li>
5049 * <li>success: Boolean success indicator</li></ul></li>
5050 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5051 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5054 load : function(options){
5055 options = options || {};
5056 if(this.fireEvent("beforeload", this, options) !== false){
5057 this.storeOptions(options);
5058 var p = Roo.apply(options.params || {}, this.baseParams);
5059 // if meta was not loaded from remote source.. try requesting it.
5060 if (!this.reader.metaFromRemote) {
5063 if(this.sortInfo && this.remoteSort){
5064 var pn = this.paramNames;
5065 p[pn["sort"]] = this.sortInfo.field;
5066 p[pn["dir"]] = this.sortInfo.direction;
5068 if (this.multiSort) {
5069 var pn = this.paramNames;
5070 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5073 this.proxy.load(p, this.reader, this.loadRecords, this, options);
5078 * Reloads the Record cache from the configured Proxy using the configured Reader and
5079 * the options from the last load operation performed.
5080 * @param {Object} options (optional) An object containing properties which may override the options
5081 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5082 * the most recently used options are reused).
5084 reload : function(options){
5085 this.load(Roo.applyIf(options||{}, this.lastOptions));
5089 // Called as a callback by the Reader during a load operation.
5090 loadRecords : function(o, options, success){
5091 if(!o || success === false){
5092 if(success !== false){
5093 this.fireEvent("load", this, [], options, o);
5095 if(options.callback){
5096 options.callback.call(options.scope || this, [], options, false);
5100 // if data returned failure - throw an exception.
5101 if (o.success === false) {
5102 // show a message if no listener is registered.
5103 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5104 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5106 // loadmask wil be hooked into this..
5107 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5110 var r = o.records, t = o.totalRecords || r.length;
5112 this.fireEvent("beforeloadadd", this, r, options, o);
5114 if(!options || options.add !== true){
5115 if(this.pruneModifiedRecords){
5118 for(var i = 0, len = r.length; i < len; i++){
5122 this.data = this.snapshot;
5123 delete this.snapshot;
5126 this.data.addAll(r);
5127 this.totalLength = t;
5129 this.fireEvent("datachanged", this);
5131 this.totalLength = Math.max(t, this.data.length+r.length);
5134 this.fireEvent("load", this, r, options, o);
5135 if(options.callback){
5136 options.callback.call(options.scope || this, r, options, true);
5142 * Loads data from a passed data block. A Reader which understands the format of the data
5143 * must have been configured in the constructor.
5144 * @param {Object} data The data block from which to read the Records. The format of the data expected
5145 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5146 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5148 loadData : function(o, append){
5149 var r = this.reader.readRecords(o);
5150 this.loadRecords(r, {add: append}, true);
5154 * Gets the number of cached records.
5156 * <em>If using paging, this may not be the total size of the dataset. If the data object
5157 * used by the Reader contains the dataset size, then the getTotalCount() function returns
5158 * the data set size</em>
5160 getCount : function(){
5161 return this.data.length || 0;
5165 * Gets the total number of records in the dataset as returned by the server.
5167 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5168 * the dataset size</em>
5170 getTotalCount : function(){
5171 return this.totalLength || 0;
5175 * Returns the sort state of the Store as an object with two properties:
5177 field {String} The name of the field by which the Records are sorted
5178 direction {String} The sort order, "ASC" or "DESC"
5181 getSortState : function(){
5182 return this.sortInfo;
5186 applySort : function(){
5187 if(this.sortInfo && !this.remoteSort){
5188 var s = this.sortInfo, f = s.field;
5189 var st = this.fields.get(f).sortType;
5190 var fn = function(r1, r2){
5191 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5192 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5194 this.data.sort(s.direction, fn);
5195 if(this.snapshot && this.snapshot != this.data){
5196 this.snapshot.sort(s.direction, fn);
5202 * Sets the default sort column and order to be used by the next load operation.
5203 * @param {String} fieldName The name of the field to sort by.
5204 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5206 setDefaultSort : function(field, dir){
5207 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5212 * If remote sorting is used, the sort is performed on the server, and the cache is
5213 * reloaded. If local sorting is used, the cache is sorted internally.
5214 * @param {String} fieldName The name of the field to sort by.
5215 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5217 sort : function(fieldName, dir){
5218 var f = this.fields.get(fieldName);
5220 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5222 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5223 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5228 this.sortToggle[f.name] = dir;
5229 this.sortInfo = {field: f.name, direction: dir};
5230 if(!this.remoteSort){
5232 this.fireEvent("datachanged", this);
5234 this.load(this.lastOptions);
5239 * Calls the specified function for each of the Records in the cache.
5240 * @param {Function} fn The function to call. The Record is passed as the first parameter.
5241 * Returning <em>false</em> aborts and exits the iteration.
5242 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5244 each : function(fn, scope){
5245 this.data.each(fn, scope);
5249 * Gets all records modified since the last commit. Modified records are persisted across load operations
5250 * (e.g., during paging).
5251 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5253 getModifiedRecords : function(){
5254 return this.modified;
5258 createFilterFn : function(property, value, anyMatch){
5259 if(!value.exec){ // not a regex
5260 value = String(value);
5261 if(value.length == 0){
5264 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5267 return value.test(r.data[property]);
5272 * Sums the value of <i>property</i> for each record between start and end and returns the result.
5273 * @param {String} property A field on your records
5274 * @param {Number} start The record index to start at (defaults to 0)
5275 * @param {Number} end The last record index to include (defaults to length - 1)
5276 * @return {Number} The sum
5278 sum : function(property, start, end){
5279 var rs = this.data.items, v = 0;
5281 end = (end || end === 0) ? end : rs.length-1;
5283 for(var i = start; i <= end; i++){
5284 v += (rs[i].data[property] || 0);
5290 * Filter the records by a specified property.
5291 * @param {String} field A field on your records
5292 * @param {String/RegExp} value Either a string that the field
5293 * should start with or a RegExp to test against the field
5294 * @param {Boolean} anyMatch True to match any part not just the beginning
5296 filter : function(property, value, anyMatch){
5297 var fn = this.createFilterFn(property, value, anyMatch);
5298 return fn ? this.filterBy(fn) : this.clearFilter();
5302 * Filter by a function. The specified function will be called with each
5303 * record in this data source. If the function returns true the record is included,
5304 * otherwise it is filtered.
5305 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5306 * @param {Object} scope (optional) The scope of the function (defaults to this)
5308 filterBy : function(fn, scope){
5309 this.snapshot = this.snapshot || this.data;
5310 this.data = this.queryBy(fn, scope||this);
5311 this.fireEvent("datachanged", this);
5315 * Query the records by a specified property.
5316 * @param {String} field A field on your records
5317 * @param {String/RegExp} value Either a string that the field
5318 * should start with or a RegExp to test against the field
5319 * @param {Boolean} anyMatch True to match any part not just the beginning
5320 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5322 query : function(property, value, anyMatch){
5323 var fn = this.createFilterFn(property, value, anyMatch);
5324 return fn ? this.queryBy(fn) : this.data.clone();
5328 * Query by a function. The specified function will be called with each
5329 * record in this data source. If the function returns true the record is included
5331 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5332 * @param {Object} scope (optional) The scope of the function (defaults to this)
5333 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5335 queryBy : function(fn, scope){
5336 var data = this.snapshot || this.data;
5337 return data.filterBy(fn, scope||this);
5341 * Collects unique values for a particular dataIndex from this store.
5342 * @param {String} dataIndex The property to collect
5343 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5344 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5345 * @return {Array} An array of the unique values
5347 collect : function(dataIndex, allowNull, bypassFilter){
5348 var d = (bypassFilter === true && this.snapshot) ?
5349 this.snapshot.items : this.data.items;
5350 var v, sv, r = [], l = {};
5351 for(var i = 0, len = d.length; i < len; i++){
5352 v = d[i].data[dataIndex];
5354 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5363 * Revert to a view of the Record cache with no filtering applied.
5364 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5366 clearFilter : function(suppressEvent){
5367 if(this.snapshot && this.snapshot != this.data){
5368 this.data = this.snapshot;
5369 delete this.snapshot;
5370 if(suppressEvent !== true){
5371 this.fireEvent("datachanged", this);
5377 afterEdit : function(record){
5378 if(this.modified.indexOf(record) == -1){
5379 this.modified.push(record);
5381 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5385 afterReject : function(record){
5386 this.modified.remove(record);
5387 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5391 afterCommit : function(record){
5392 this.modified.remove(record);
5393 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5397 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5398 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5400 commitChanges : function(){
5401 var m = this.modified.slice(0);
5403 for(var i = 0, len = m.length; i < len; i++){
5409 * Cancel outstanding changes on all changed records.
5411 rejectChanges : function(){
5412 var m = this.modified.slice(0);
5414 for(var i = 0, len = m.length; i < len; i++){
5419 onMetaChange : function(meta, rtype, o){
5420 this.recordType = rtype;
5421 this.fields = rtype.prototype.fields;
5422 delete this.snapshot;
5423 this.sortInfo = meta.sortInfo || this.sortInfo;
5425 this.fireEvent('metachange', this, this.reader.meta);
5428 moveIndex : function(data, type)
5430 var index = this.indexOf(data);
5432 var newIndex = index + type;
5436 this.insert(newIndex, data);
5441 * Ext JS Library 1.1.1
5442 * Copyright(c) 2006-2007, Ext JS, LLC.
5444 * Originally Released Under LGPL - original licence link has changed is not relivant.
5447 * <script type="text/javascript">
5451 * @class Roo.data.SimpleStore
5452 * @extends Roo.data.Store
5453 * Small helper class to make creating Stores from Array data easier.
5454 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5455 * @cfg {Array} fields An array of field definition objects, or field name strings.
5456 * @cfg {Array} data The multi-dimensional array of data
5458 * @param {Object} config
5460 Roo.data.SimpleStore = function(config){
5461 Roo.data.SimpleStore.superclass.constructor.call(this, {
5463 reader: new Roo.data.ArrayReader({
5466 Roo.data.Record.create(config.fields)
5468 proxy : new Roo.data.MemoryProxy(config.data)
5472 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5474 * Ext JS Library 1.1.1
5475 * Copyright(c) 2006-2007, Ext JS, LLC.
5477 * Originally Released Under LGPL - original licence link has changed is not relivant.
5480 * <script type="text/javascript">
5485 * @extends Roo.data.Store
5486 * @class Roo.data.JsonStore
5487 * Small helper class to make creating Stores for JSON data easier. <br/>
5489 var store = new Roo.data.JsonStore({
5490 url: 'get-images.php',
5492 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5495 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5496 * JsonReader and HttpProxy (unless inline data is provided).</b>
5497 * @cfg {Array} fields An array of field definition objects, or field name strings.
5499 * @param {Object} config
5501 Roo.data.JsonStore = function(c){
5502 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5503 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5504 reader: new Roo.data.JsonReader(c, c.fields)
5507 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5509 * Ext JS Library 1.1.1
5510 * Copyright(c) 2006-2007, Ext JS, LLC.
5512 * Originally Released Under LGPL - original licence link has changed is not relivant.
5515 * <script type="text/javascript">
5519 Roo.data.Field = function(config){
5520 if(typeof config == "string"){
5521 config = {name: config};
5523 Roo.apply(this, config);
5529 var st = Roo.data.SortTypes;
5530 // named sortTypes are supported, here we look them up
5531 if(typeof this.sortType == "string"){
5532 this.sortType = st[this.sortType];
5535 // set default sortType for strings and dates
5539 this.sortType = st.asUCString;
5542 this.sortType = st.asDate;
5545 this.sortType = st.none;
5550 var stripRe = /[\$,%]/g;
5552 // prebuilt conversion function for this field, instead of
5553 // switching every time we're reading a value
5555 var cv, dateFormat = this.dateFormat;
5560 cv = function(v){ return v; };
5563 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5567 return v !== undefined && v !== null && v !== '' ?
5568 parseInt(String(v).replace(stripRe, ""), 10) : '';
5573 return v !== undefined && v !== null && v !== '' ?
5574 parseFloat(String(v).replace(stripRe, ""), 10) : '';
5579 cv = function(v){ return v === true || v === "true" || v == 1; };
5586 if(v instanceof Date){
5590 if(dateFormat == "timestamp"){
5591 return new Date(v*1000);
5593 return Date.parseDate(v, dateFormat);
5595 var parsed = Date.parse(v);
5596 return parsed ? new Date(parsed) : null;
5605 Roo.data.Field.prototype = {
5613 * Ext JS Library 1.1.1
5614 * Copyright(c) 2006-2007, Ext JS, LLC.
5616 * Originally Released Under LGPL - original licence link has changed is not relivant.
5619 * <script type="text/javascript">
5622 // Base class for reading structured data from a data source. This class is intended to be
5623 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5626 * @class Roo.data.DataReader
5627 * Base class for reading structured data from a data source. This class is intended to be
5628 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5631 Roo.data.DataReader = function(meta, recordType){
5635 this.recordType = recordType instanceof Array ?
5636 Roo.data.Record.create(recordType) : recordType;
5639 Roo.data.DataReader.prototype = {
5641 * Create an empty record
5642 * @param {Object} data (optional) - overlay some values
5643 * @return {Roo.data.Record} record created.
5645 newRow : function(d) {
5647 this.recordType.prototype.fields.each(function(c) {
5649 case 'int' : da[c.name] = 0; break;
5650 case 'date' : da[c.name] = new Date(); break;
5651 case 'float' : da[c.name] = 0.0; break;
5652 case 'boolean' : da[c.name] = false; break;
5653 default : da[c.name] = ""; break;
5657 return new this.recordType(Roo.apply(da, d));
5662 * Ext JS Library 1.1.1
5663 * Copyright(c) 2006-2007, Ext JS, LLC.
5665 * Originally Released Under LGPL - original licence link has changed is not relivant.
5668 * <script type="text/javascript">
5672 * @class Roo.data.DataProxy
5673 * @extends Roo.data.Observable
5674 * This class is an abstract base class for implementations which provide retrieval of
5675 * unformatted data objects.<br>
5677 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5678 * (of the appropriate type which knows how to parse the data object) to provide a block of
5679 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5681 * Custom implementations must implement the load method as described in
5682 * {@link Roo.data.HttpProxy#load}.
5684 Roo.data.DataProxy = function(){
5688 * Fires before a network request is made to retrieve a data object.
5689 * @param {Object} This DataProxy object.
5690 * @param {Object} params The params parameter to the load function.
5695 * Fires before the load method's callback is called.
5696 * @param {Object} This DataProxy object.
5697 * @param {Object} o The data object.
5698 * @param {Object} arg The callback argument object passed to the load function.
5702 * @event loadexception
5703 * Fires if an Exception occurs during data retrieval.
5704 * @param {Object} This DataProxy object.
5705 * @param {Object} o The data object.
5706 * @param {Object} arg The callback argument object passed to the load function.
5707 * @param {Object} e The Exception.
5709 loadexception : true
5711 Roo.data.DataProxy.superclass.constructor.call(this);
5714 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5717 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5721 * Ext JS Library 1.1.1
5722 * Copyright(c) 2006-2007, Ext JS, LLC.
5724 * Originally Released Under LGPL - original licence link has changed is not relivant.
5727 * <script type="text/javascript">
5730 * @class Roo.data.MemoryProxy
5731 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5732 * to the Reader when its load method is called.
5734 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5736 Roo.data.MemoryProxy = function(data){
5740 Roo.data.MemoryProxy.superclass.constructor.call(this);
5744 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5746 * Load data from the requested source (in this case an in-memory
5747 * data object passed to the constructor), read the data object into
5748 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5749 * process that block using the passed callback.
5750 * @param {Object} params This parameter is not used by the MemoryProxy class.
5751 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5752 * object into a block of Roo.data.Records.
5753 * @param {Function} callback The function into which to pass the block of Roo.data.records.
5754 * The function must be passed <ul>
5755 * <li>The Record block object</li>
5756 * <li>The "arg" argument from the load function</li>
5757 * <li>A boolean success indicator</li>
5759 * @param {Object} scope The scope in which to call the callback
5760 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5762 load : function(params, reader, callback, scope, arg){
5763 params = params || {};
5766 result = reader.readRecords(this.data);
5768 this.fireEvent("loadexception", this, arg, null, e);
5769 callback.call(scope, null, arg, false);
5772 callback.call(scope, result, arg, true);
5776 update : function(params, records){
5781 * Ext JS Library 1.1.1
5782 * Copyright(c) 2006-2007, Ext JS, LLC.
5784 * Originally Released Under LGPL - original licence link has changed is not relivant.
5787 * <script type="text/javascript">
5790 * @class Roo.data.HttpProxy
5791 * @extends Roo.data.DataProxy
5792 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5793 * configured to reference a certain URL.<br><br>
5795 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5796 * from which the running page was served.<br><br>
5798 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5800 * Be aware that to enable the browser to parse an XML document, the server must set
5801 * the Content-Type header in the HTTP response to "text/xml".
5803 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5804 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
5805 * will be used to make the request.
5807 Roo.data.HttpProxy = function(conn){
5808 Roo.data.HttpProxy.superclass.constructor.call(this);
5809 // is conn a conn config or a real conn?
5811 this.useAjax = !conn || !conn.events;
5815 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5816 // thse are take from connection...
5819 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5822 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5823 * extra parameters to each request made by this object. (defaults to undefined)
5826 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5827 * to each request made by this object. (defaults to undefined)
5830 * @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)
5833 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5836 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5842 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5846 * Return the {@link Roo.data.Connection} object being used by this Proxy.
5847 * @return {Connection} The Connection object. This object may be used to subscribe to events on
5848 * a finer-grained basis than the DataProxy events.
5850 getConnection : function(){
5851 return this.useAjax ? Roo.Ajax : this.conn;
5855 * Load data from the configured {@link Roo.data.Connection}, read the data object into
5856 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5857 * process that block using the passed callback.
5858 * @param {Object} params An object containing properties which are to be used as HTTP parameters
5859 * for the request to the remote server.
5860 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5861 * object into a block of Roo.data.Records.
5862 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5863 * The function must be passed <ul>
5864 * <li>The Record block object</li>
5865 * <li>The "arg" argument from the load function</li>
5866 * <li>A boolean success indicator</li>
5868 * @param {Object} scope The scope in which to call the callback
5869 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5871 load : function(params, reader, callback, scope, arg){
5872 if(this.fireEvent("beforeload", this, params) !== false){
5874 params : params || {},
5876 callback : callback,
5881 callback : this.loadResponse,
5885 Roo.applyIf(o, this.conn);
5886 if(this.activeRequest){
5887 Roo.Ajax.abort(this.activeRequest);
5889 this.activeRequest = Roo.Ajax.request(o);
5891 this.conn.request(o);
5894 callback.call(scope||this, null, arg, false);
5899 loadResponse : function(o, success, response){
5900 delete this.activeRequest;
5902 this.fireEvent("loadexception", this, o, response);
5903 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5908 result = o.reader.read(response);
5910 this.fireEvent("loadexception", this, o, response, e);
5911 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5915 this.fireEvent("load", this, o, o.request.arg);
5916 o.request.callback.call(o.request.scope, result, o.request.arg, true);
5920 update : function(dataSet){
5925 updateResponse : function(dataSet){
5930 * Ext JS Library 1.1.1
5931 * Copyright(c) 2006-2007, Ext JS, LLC.
5933 * Originally Released Under LGPL - original licence link has changed is not relivant.
5936 * <script type="text/javascript">
5940 * @class Roo.data.ScriptTagProxy
5941 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5942 * other than the originating domain of the running page.<br><br>
5944 * <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
5945 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5947 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5948 * source code that is used as the source inside a <script> tag.<br><br>
5950 * In order for the browser to process the returned data, the server must wrap the data object
5951 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5952 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5953 * depending on whether the callback name was passed:
5956 boolean scriptTag = false;
5957 String cb = request.getParameter("callback");
5960 response.setContentType("text/javascript");
5962 response.setContentType("application/x-json");
5964 Writer out = response.getWriter();
5966 out.write(cb + "(");
5968 out.print(dataBlock.toJsonString());
5975 * @param {Object} config A configuration object.
5977 Roo.data.ScriptTagProxy = function(config){
5978 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5979 Roo.apply(this, config);
5980 this.head = document.getElementsByTagName("head")[0];
5983 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5985 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5987 * @cfg {String} url The URL from which to request the data object.
5990 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5994 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5995 * the server the name of the callback function set up by the load call to process the returned data object.
5996 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5997 * javascript output which calls this named function passing the data object as its only parameter.
5999 callbackParam : "callback",
6001 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
6002 * name to the request.
6007 * Load data from the configured URL, read the data object into
6008 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6009 * process that block using the passed callback.
6010 * @param {Object} params An object containing properties which are to be used as HTTP parameters
6011 * for the request to the remote server.
6012 * @param {Roo.data.DataReader} reader The Reader object which converts the data
6013 * object into a block of Roo.data.Records.
6014 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6015 * The function must be passed <ul>
6016 * <li>The Record block object</li>
6017 * <li>The "arg" argument from the load function</li>
6018 * <li>A boolean success indicator</li>
6020 * @param {Object} scope The scope in which to call the callback
6021 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6023 load : function(params, reader, callback, scope, arg){
6024 if(this.fireEvent("beforeload", this, params) !== false){
6026 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6029 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6031 url += "&_dc=" + (new Date().getTime());
6033 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6036 cb : "stcCallback"+transId,
6037 scriptId : "stcScript"+transId,
6041 callback : callback,
6047 window[trans.cb] = function(o){
6048 conn.handleResponse(o, trans);
6051 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6053 if(this.autoAbort !== false){
6057 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6059 var script = document.createElement("script");
6060 script.setAttribute("src", url);
6061 script.setAttribute("type", "text/javascript");
6062 script.setAttribute("id", trans.scriptId);
6063 this.head.appendChild(script);
6067 callback.call(scope||this, null, arg, false);
6072 isLoading : function(){
6073 return this.trans ? true : false;
6077 * Abort the current server request.
6080 if(this.isLoading()){
6081 this.destroyTrans(this.trans);
6086 destroyTrans : function(trans, isLoaded){
6087 this.head.removeChild(document.getElementById(trans.scriptId));
6088 clearTimeout(trans.timeoutId);
6090 window[trans.cb] = undefined;
6092 delete window[trans.cb];
6095 // if hasn't been loaded, wait for load to remove it to prevent script error
6096 window[trans.cb] = function(){
6097 window[trans.cb] = undefined;
6099 delete window[trans.cb];
6106 handleResponse : function(o, trans){
6108 this.destroyTrans(trans, true);
6111 result = trans.reader.readRecords(o);
6113 this.fireEvent("loadexception", this, o, trans.arg, e);
6114 trans.callback.call(trans.scope||window, null, trans.arg, false);
6117 this.fireEvent("load", this, o, trans.arg);
6118 trans.callback.call(trans.scope||window, result, trans.arg, true);
6122 handleFailure : function(trans){
6124 this.destroyTrans(trans, false);
6125 this.fireEvent("loadexception", this, null, trans.arg);
6126 trans.callback.call(trans.scope||window, null, trans.arg, false);
6130 * Ext JS Library 1.1.1
6131 * Copyright(c) 2006-2007, Ext JS, LLC.
6133 * Originally Released Under LGPL - original licence link has changed is not relivant.
6136 * <script type="text/javascript">
6140 * @class Roo.data.JsonReader
6141 * @extends Roo.data.DataReader
6142 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6143 * based on mappings in a provided Roo.data.Record constructor.
6145 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6146 * in the reply previously.
6151 var RecordDef = Roo.data.Record.create([
6152 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6153 {name: 'occupation'} // This field will use "occupation" as the mapping.
6155 var myReader = new Roo.data.JsonReader({
6156 totalProperty: "results", // The property which contains the total dataset size (optional)
6157 root: "rows", // The property which contains an Array of row objects
6158 id: "id" // The property within each row object that provides an ID for the record (optional)
6162 * This would consume a JSON file like this:
6164 { 'results': 2, 'rows': [
6165 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6166 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6169 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6170 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6171 * paged from the remote server.
6172 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6173 * @cfg {String} root name of the property which contains the Array of row objects.
6174 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6175 * @cfg {Array} fields Array of field definition objects
6177 * Create a new JsonReader
6178 * @param {Object} meta Metadata configuration options
6179 * @param {Object} recordType Either an Array of field definition objects,
6180 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6182 Roo.data.JsonReader = function(meta, recordType){
6185 // set some defaults:
6187 totalProperty: 'total',
6188 successProperty : 'success',
6193 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6195 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6198 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
6199 * Used by Store query builder to append _requestMeta to params.
6202 metaFromRemote : false,
6204 * This method is only used by a DataProxy which has retrieved data from a remote server.
6205 * @param {Object} response The XHR object which contains the JSON data in its responseText.
6206 * @return {Object} data A data block which is used by an Roo.data.Store object as
6207 * a cache of Roo.data.Records.
6209 read : function(response){
6210 var json = response.responseText;
6212 var o = /* eval:var:o */ eval("("+json+")");
6214 throw {message: "JsonReader.read: Json object not found"};
6220 this.metaFromRemote = true;
6221 this.meta = o.metaData;
6222 this.recordType = Roo.data.Record.create(o.metaData.fields);
6223 this.onMetaChange(this.meta, this.recordType, o);
6225 return this.readRecords(o);
6228 // private function a store will implement
6229 onMetaChange : function(meta, recordType, o){
6236 simpleAccess: function(obj, subsc) {
6243 getJsonAccessor: function(){
6245 return function(expr) {
6247 return(re.test(expr))
6248 ? new Function("obj", "return obj." + expr)
6258 * Create a data block containing Roo.data.Records from an XML document.
6259 * @param {Object} o An object which contains an Array of row objects in the property specified
6260 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6261 * which contains the total size of the dataset.
6262 * @return {Object} data A data block which is used by an Roo.data.Store object as
6263 * a cache of Roo.data.Records.
6265 readRecords : function(o){
6267 * After any data loads, the raw JSON data is available for further custom processing.
6271 var s = this.meta, Record = this.recordType,
6272 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6274 // Generate extraction functions for the totalProperty, the root, the id, and for each field
6276 if(s.totalProperty) {
6277 this.getTotal = this.getJsonAccessor(s.totalProperty);
6279 if(s.successProperty) {
6280 this.getSuccess = this.getJsonAccessor(s.successProperty);
6282 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6284 var g = this.getJsonAccessor(s.id);
6285 this.getId = function(rec) {
6287 return (r === undefined || r === "") ? null : r;
6290 this.getId = function(){return null;};
6293 for(var jj = 0; jj < fl; jj++){
6295 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6296 this.ef[jj] = this.getJsonAccessor(map);
6300 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6301 if(s.totalProperty){
6302 var vt = parseInt(this.getTotal(o), 10);
6307 if(s.successProperty){
6308 var vs = this.getSuccess(o);
6309 if(vs === false || vs === 'false'){
6314 for(var i = 0; i < c; i++){
6317 var id = this.getId(n);
6318 for(var j = 0; j < fl; j++){
6320 var v = this.ef[j](n);
6322 Roo.log('missing convert for ' + f.name);
6326 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6328 var record = new Record(values, id);
6330 records[i] = record;
6336 totalRecords : totalRecords
6341 * Ext JS Library 1.1.1
6342 * Copyright(c) 2006-2007, Ext JS, LLC.
6344 * Originally Released Under LGPL - original licence link has changed is not relivant.
6347 * <script type="text/javascript">
6351 * @class Roo.data.XmlReader
6352 * @extends Roo.data.DataReader
6353 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6354 * based on mappings in a provided Roo.data.Record constructor.<br><br>
6356 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6357 * header in the HTTP response must be set to "text/xml".</em>
6361 var RecordDef = Roo.data.Record.create([
6362 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6363 {name: 'occupation'} // This field will use "occupation" as the mapping.
6365 var myReader = new Roo.data.XmlReader({
6366 totalRecords: "results", // The element which contains the total dataset size (optional)
6367 record: "row", // The repeated element which contains row information
6368 id: "id" // The element within the row that provides an ID for the record (optional)
6372 * This would consume an XML file like this:
6376 <results>2</results>
6379 <name>Bill</name>
6380 <occupation>Gardener</occupation>
6384 <name>Ben</name>
6385 <occupation>Horticulturalist</occupation>
6389 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6390 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6391 * paged from the remote server.
6392 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6393 * @cfg {String} success The DomQuery path to the success attribute used by forms.
6394 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6395 * a record identifier value.
6397 * Create a new XmlReader
6398 * @param {Object} meta Metadata configuration options
6399 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
6400 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6401 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
6403 Roo.data.XmlReader = function(meta, recordType){
6405 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6407 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6409 * This method is only used by a DataProxy which has retrieved data from a remote server.
6410 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
6411 * to contain a method called 'responseXML' that returns an XML document object.
6412 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6413 * a cache of Roo.data.Records.
6415 read : function(response){
6416 var doc = response.responseXML;
6418 throw {message: "XmlReader.read: XML Document not available"};
6420 return this.readRecords(doc);
6424 * Create a data block containing Roo.data.Records from an XML document.
6425 * @param {Object} doc A parsed XML document.
6426 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6427 * a cache of Roo.data.Records.
6429 readRecords : function(doc){
6431 * After any data loads/reads, the raw XML Document is available for further custom processing.
6435 var root = doc.documentElement || doc;
6436 var q = Roo.DomQuery;
6437 var recordType = this.recordType, fields = recordType.prototype.fields;
6438 var sid = this.meta.id;
6439 var totalRecords = 0, success = true;
6440 if(this.meta.totalRecords){
6441 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6444 if(this.meta.success){
6445 var sv = q.selectValue(this.meta.success, root, true);
6446 success = sv !== false && sv !== 'false';
6449 var ns = q.select(this.meta.record, root);
6450 for(var i = 0, len = ns.length; i < len; i++) {
6453 var id = sid ? q.selectValue(sid, n) : undefined;
6454 for(var j = 0, jlen = fields.length; j < jlen; j++){
6455 var f = fields.items[j];
6456 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6460 var record = new recordType(values, id);
6462 records[records.length] = record;
6468 totalRecords : totalRecords || records.length
6473 * Ext JS Library 1.1.1
6474 * Copyright(c) 2006-2007, Ext JS, LLC.
6476 * Originally Released Under LGPL - original licence link has changed is not relivant.
6479 * <script type="text/javascript">
6483 * @class Roo.data.ArrayReader
6484 * @extends Roo.data.DataReader
6485 * Data reader class to create an Array of Roo.data.Record objects from an Array.
6486 * Each element of that Array represents a row of data fields. The
6487 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6488 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6492 var RecordDef = Roo.data.Record.create([
6493 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
6494 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
6496 var myReader = new Roo.data.ArrayReader({
6497 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
6501 * This would consume an Array like this:
6503 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6505 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6507 * Create a new JsonReader
6508 * @param {Object} meta Metadata configuration options.
6509 * @param {Object} recordType Either an Array of field definition objects
6510 * as specified to {@link Roo.data.Record#create},
6511 * or an {@link Roo.data.Record} object
6512 * created using {@link Roo.data.Record#create}.
6514 Roo.data.ArrayReader = function(meta, recordType){
6515 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6518 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6520 * Create a data block containing Roo.data.Records from an XML document.
6521 * @param {Object} o An Array of row objects which represents the dataset.
6522 * @return {Object} data A data block which is used by an Roo.data.Store object as
6523 * a cache of Roo.data.Records.
6525 readRecords : function(o){
6526 var sid = this.meta ? this.meta.id : null;
6527 var recordType = this.recordType, fields = recordType.prototype.fields;
6530 for(var i = 0; i < root.length; i++){
6533 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6534 for(var j = 0, jlen = fields.length; j < jlen; j++){
6535 var f = fields.items[j];
6536 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6537 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6541 var record = new recordType(values, id);
6543 records[records.length] = record;
6547 totalRecords : records.length
6552 * Ext JS Library 1.1.1
6553 * Copyright(c) 2006-2007, Ext JS, LLC.
6555 * Originally Released Under LGPL - original licence link has changed is not relivant.
6558 * <script type="text/javascript">
6563 * @class Roo.data.Tree
6564 * @extends Roo.util.Observable
6565 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6566 * in the tree have most standard DOM functionality.
6568 * @param {Node} root (optional) The root node
6570 Roo.data.Tree = function(root){
6573 * The root node for this tree
6578 this.setRootNode(root);
6583 * Fires when a new child node is appended to a node in this tree.
6584 * @param {Tree} tree The owner tree
6585 * @param {Node} parent The parent node
6586 * @param {Node} node The newly appended node
6587 * @param {Number} index The index of the newly appended node
6592 * Fires when a child node is removed from a node in this tree.
6593 * @param {Tree} tree The owner tree
6594 * @param {Node} parent The parent node
6595 * @param {Node} node The child node removed
6600 * Fires when a node is moved to a new location in the tree
6601 * @param {Tree} tree The owner tree
6602 * @param {Node} node The node moved
6603 * @param {Node} oldParent The old parent of this node
6604 * @param {Node} newParent The new parent of this node
6605 * @param {Number} index The index it was moved to
6610 * Fires when a new child node is inserted in a node in this tree.
6611 * @param {Tree} tree The owner tree
6612 * @param {Node} parent The parent node
6613 * @param {Node} node The child node inserted
6614 * @param {Node} refNode The child node the node was inserted before
6618 * @event beforeappend
6619 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6620 * @param {Tree} tree The owner tree
6621 * @param {Node} parent The parent node
6622 * @param {Node} node The child node to be appended
6624 "beforeappend" : true,
6626 * @event beforeremove
6627 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6628 * @param {Tree} tree The owner tree
6629 * @param {Node} parent The parent node
6630 * @param {Node} node The child node to be removed
6632 "beforeremove" : true,
6635 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6636 * @param {Tree} tree The owner tree
6637 * @param {Node} node The node being moved
6638 * @param {Node} oldParent The parent of the node
6639 * @param {Node} newParent The new parent the node is moving to
6640 * @param {Number} index The index it is being moved to
6642 "beforemove" : true,
6644 * @event beforeinsert
6645 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6646 * @param {Tree} tree The owner tree
6647 * @param {Node} parent The parent node
6648 * @param {Node} node The child node to be inserted
6649 * @param {Node} refNode The child node the node is being inserted before
6651 "beforeinsert" : true
6654 Roo.data.Tree.superclass.constructor.call(this);
6657 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6660 proxyNodeEvent : function(){
6661 return this.fireEvent.apply(this, arguments);
6665 * Returns the root node for this tree.
6668 getRootNode : function(){
6673 * Sets the root node for this tree.
6674 * @param {Node} node
6677 setRootNode : function(node){
6679 node.ownerTree = this;
6681 this.registerNode(node);
6686 * Gets a node in this tree by its id.
6687 * @param {String} id
6690 getNodeById : function(id){
6691 return this.nodeHash[id];
6694 registerNode : function(node){
6695 this.nodeHash[node.id] = node;
6698 unregisterNode : function(node){
6699 delete this.nodeHash[node.id];
6702 toString : function(){
6703 return "[Tree"+(this.id?" "+this.id:"")+"]";
6708 * @class Roo.data.Node
6709 * @extends Roo.util.Observable
6710 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6711 * @cfg {String} id The id for this node. If one is not specified, one is generated.
6713 * @param {Object} attributes The attributes/config for the node
6715 Roo.data.Node = function(attributes){
6717 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6720 this.attributes = attributes || {};
6721 this.leaf = this.attributes.leaf;
6723 * The node id. @type String
6725 this.id = this.attributes.id;
6727 this.id = Roo.id(null, "ynode-");
6728 this.attributes.id = this.id;
6733 * All child nodes of this node. @type Array
6735 this.childNodes = [];
6736 if(!this.childNodes.indexOf){ // indexOf is a must
6737 this.childNodes.indexOf = function(o){
6738 for(var i = 0, len = this.length; i < len; i++){
6747 * The parent node for this node. @type Node
6749 this.parentNode = null;
6751 * The first direct child node of this node, or null if this node has no child nodes. @type Node
6753 this.firstChild = null;
6755 * The last direct child node of this node, or null if this node has no child nodes. @type Node
6757 this.lastChild = null;
6759 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6761 this.previousSibling = null;
6763 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6765 this.nextSibling = null;
6770 * Fires when a new child node is appended
6771 * @param {Tree} tree The owner tree
6772 * @param {Node} this This node
6773 * @param {Node} node The newly appended node
6774 * @param {Number} index The index of the newly appended node
6779 * Fires when a child node is removed
6780 * @param {Tree} tree The owner tree
6781 * @param {Node} this This node
6782 * @param {Node} node The removed node
6787 * Fires when this node is moved to a new location in the tree
6788 * @param {Tree} tree The owner tree
6789 * @param {Node} this This node
6790 * @param {Node} oldParent The old parent of this node
6791 * @param {Node} newParent The new parent of this node
6792 * @param {Number} index The index it was moved to
6797 * Fires when a new child node is inserted.
6798 * @param {Tree} tree The owner tree
6799 * @param {Node} this This node
6800 * @param {Node} node The child node inserted
6801 * @param {Node} refNode The child node the node was inserted before
6805 * @event beforeappend
6806 * Fires before a new child is appended, return false to cancel the append.
6807 * @param {Tree} tree The owner tree
6808 * @param {Node} this This node
6809 * @param {Node} node The child node to be appended
6811 "beforeappend" : true,
6813 * @event beforeremove
6814 * Fires before a child is removed, return false to cancel the remove.
6815 * @param {Tree} tree The owner tree
6816 * @param {Node} this This node
6817 * @param {Node} node The child node to be removed
6819 "beforeremove" : true,
6822 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6823 * @param {Tree} tree The owner tree
6824 * @param {Node} this This node
6825 * @param {Node} oldParent The parent of this node
6826 * @param {Node} newParent The new parent this node is moving to
6827 * @param {Number} index The index it is being moved to
6829 "beforemove" : true,
6831 * @event beforeinsert
6832 * Fires before a new child is inserted, return false to cancel the insert.
6833 * @param {Tree} tree The owner tree
6834 * @param {Node} this This node
6835 * @param {Node} node The child node to be inserted
6836 * @param {Node} refNode The child node the node is being inserted before
6838 "beforeinsert" : true
6840 this.listeners = this.attributes.listeners;
6841 Roo.data.Node.superclass.constructor.call(this);
6844 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6845 fireEvent : function(evtName){
6846 // first do standard event for this node
6847 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6850 // then bubble it up to the tree if the event wasn't cancelled
6851 var ot = this.getOwnerTree();
6853 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6861 * Returns true if this node is a leaf
6864 isLeaf : function(){
6865 return this.leaf === true;
6869 setFirstChild : function(node){
6870 this.firstChild = node;
6874 setLastChild : function(node){
6875 this.lastChild = node;
6880 * Returns true if this node is the last child of its parent
6883 isLast : function(){
6884 return (!this.parentNode ? true : this.parentNode.lastChild == this);
6888 * Returns true if this node is the first child of its parent
6891 isFirst : function(){
6892 return (!this.parentNode ? true : this.parentNode.firstChild == this);
6895 hasChildNodes : function(){
6896 return !this.isLeaf() && this.childNodes.length > 0;
6900 * Insert node(s) as the last child node of this node.
6901 * @param {Node/Array} node The node or Array of nodes to append
6902 * @return {Node} The appended node if single append, or null if an array was passed
6904 appendChild : function(node){
6906 if(node instanceof Array){
6908 }else if(arguments.length > 1){
6911 // if passed an array or multiple args do them one by one
6913 for(var i = 0, len = multi.length; i < len; i++) {
6914 this.appendChild(multi[i]);
6917 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6920 var index = this.childNodes.length;
6921 var oldParent = node.parentNode;
6922 // it's a move, make sure we move it cleanly
6924 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6927 oldParent.removeChild(node);
6929 index = this.childNodes.length;
6931 this.setFirstChild(node);
6933 this.childNodes.push(node);
6934 node.parentNode = this;
6935 var ps = this.childNodes[index-1];
6937 node.previousSibling = ps;
6938 ps.nextSibling = node;
6940 node.previousSibling = null;
6942 node.nextSibling = null;
6943 this.setLastChild(node);
6944 node.setOwnerTree(this.getOwnerTree());
6945 this.fireEvent("append", this.ownerTree, this, node, index);
6947 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6954 * Removes a child node from this node.
6955 * @param {Node} node The node to remove
6956 * @return {Node} The removed node
6958 removeChild : function(node){
6959 var index = this.childNodes.indexOf(node);
6963 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6967 // remove it from childNodes collection
6968 this.childNodes.splice(index, 1);
6971 if(node.previousSibling){
6972 node.previousSibling.nextSibling = node.nextSibling;
6974 if(node.nextSibling){
6975 node.nextSibling.previousSibling = node.previousSibling;
6978 // update child refs
6979 if(this.firstChild == node){
6980 this.setFirstChild(node.nextSibling);
6982 if(this.lastChild == node){
6983 this.setLastChild(node.previousSibling);
6986 node.setOwnerTree(null);
6987 // clear any references from the node
6988 node.parentNode = null;
6989 node.previousSibling = null;
6990 node.nextSibling = null;
6991 this.fireEvent("remove", this.ownerTree, this, node);
6996 * Inserts the first node before the second node in this nodes childNodes collection.
6997 * @param {Node} node The node to insert
6998 * @param {Node} refNode The node to insert before (if null the node is appended)
6999 * @return {Node} The inserted node
7001 insertBefore : function(node, refNode){
7002 if(!refNode){ // like standard Dom, refNode can be null for append
7003 return this.appendChild(node);
7006 if(node == refNode){
7010 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7013 var index = this.childNodes.indexOf(refNode);
7014 var oldParent = node.parentNode;
7015 var refIndex = index;
7017 // when moving internally, indexes will change after remove
7018 if(oldParent == this && this.childNodes.indexOf(node) < index){
7022 // it's a move, make sure we move it cleanly
7024 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7027 oldParent.removeChild(node);
7030 this.setFirstChild(node);
7032 this.childNodes.splice(refIndex, 0, node);
7033 node.parentNode = this;
7034 var ps = this.childNodes[refIndex-1];
7036 node.previousSibling = ps;
7037 ps.nextSibling = node;
7039 node.previousSibling = null;
7041 node.nextSibling = refNode;
7042 refNode.previousSibling = node;
7043 node.setOwnerTree(this.getOwnerTree());
7044 this.fireEvent("insert", this.ownerTree, this, node, refNode);
7046 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7052 * Returns the child node at the specified index.
7053 * @param {Number} index
7056 item : function(index){
7057 return this.childNodes[index];
7061 * Replaces one child node in this node with another.
7062 * @param {Node} newChild The replacement node
7063 * @param {Node} oldChild The node to replace
7064 * @return {Node} The replaced node
7066 replaceChild : function(newChild, oldChild){
7067 this.insertBefore(newChild, oldChild);
7068 this.removeChild(oldChild);
7073 * Returns the index of a child node
7074 * @param {Node} node
7075 * @return {Number} The index of the node or -1 if it was not found
7077 indexOf : function(child){
7078 return this.childNodes.indexOf(child);
7082 * Returns the tree this node is in.
7085 getOwnerTree : function(){
7086 // if it doesn't have one, look for one
7087 if(!this.ownerTree){
7091 this.ownerTree = p.ownerTree;
7097 return this.ownerTree;
7101 * Returns depth of this node (the root node has a depth of 0)
7104 getDepth : function(){
7107 while(p.parentNode){
7115 setOwnerTree : function(tree){
7116 // if it's move, we need to update everyone
7117 if(tree != this.ownerTree){
7119 this.ownerTree.unregisterNode(this);
7121 this.ownerTree = tree;
7122 var cs = this.childNodes;
7123 for(var i = 0, len = cs.length; i < len; i++) {
7124 cs[i].setOwnerTree(tree);
7127 tree.registerNode(this);
7133 * Returns the path for this node. The path can be used to expand or select this node programmatically.
7134 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7135 * @return {String} The path
7137 getPath : function(attr){
7138 attr = attr || "id";
7139 var p = this.parentNode;
7140 var b = [this.attributes[attr]];
7142 b.unshift(p.attributes[attr]);
7145 var sep = this.getOwnerTree().pathSeparator;
7146 return sep + b.join(sep);
7150 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7151 * function call will be the scope provided or the current node. The arguments to the function
7152 * will be the args provided or the current node. If the function returns false at any point,
7153 * the bubble is stopped.
7154 * @param {Function} fn The function to call
7155 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7156 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7158 bubble : function(fn, scope, args){
7161 if(fn.call(scope || p, args || p) === false){
7169 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7170 * function call will be the scope provided or the current node. The arguments to the function
7171 * will be the args provided or the current node. If the function returns false at any point,
7172 * the cascade is stopped on that branch.
7173 * @param {Function} fn The function to call
7174 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7175 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7177 cascade : function(fn, scope, args){
7178 if(fn.call(scope || this, args || this) !== false){
7179 var cs = this.childNodes;
7180 for(var i = 0, len = cs.length; i < len; i++) {
7181 cs[i].cascade(fn, scope, args);
7187 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7188 * function call will be the scope provided or the current node. The arguments to the function
7189 * will be the args provided or the current node. If the function returns false at any point,
7190 * the iteration stops.
7191 * @param {Function} fn The function to call
7192 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7193 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7195 eachChild : function(fn, scope, args){
7196 var cs = this.childNodes;
7197 for(var i = 0, len = cs.length; i < len; i++) {
7198 if(fn.call(scope || this, args || cs[i]) === false){
7205 * Finds the first child that has the attribute with the specified value.
7206 * @param {String} attribute The attribute name
7207 * @param {Mixed} value The value to search for
7208 * @return {Node} The found child or null if none was found
7210 findChild : function(attribute, value){
7211 var cs = this.childNodes;
7212 for(var i = 0, len = cs.length; i < len; i++) {
7213 if(cs[i].attributes[attribute] == value){
7221 * Finds the first child by a custom function. The child matches if the function passed
7223 * @param {Function} fn
7224 * @param {Object} scope (optional)
7225 * @return {Node} The found child or null if none was found
7227 findChildBy : function(fn, scope){
7228 var cs = this.childNodes;
7229 for(var i = 0, len = cs.length; i < len; i++) {
7230 if(fn.call(scope||cs[i], cs[i]) === true){
7238 * Sorts this nodes children using the supplied sort function
7239 * @param {Function} fn
7240 * @param {Object} scope (optional)
7242 sort : function(fn, scope){
7243 var cs = this.childNodes;
7244 var len = cs.length;
7246 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7248 for(var i = 0; i < len; i++){
7250 n.previousSibling = cs[i-1];
7251 n.nextSibling = cs[i+1];
7253 this.setFirstChild(n);
7256 this.setLastChild(n);
7263 * Returns true if this node is an ancestor (at any point) of the passed node.
7264 * @param {Node} node
7267 contains : function(node){
7268 return node.isAncestor(this);
7272 * Returns true if the passed node is an ancestor (at any point) of this node.
7273 * @param {Node} node
7276 isAncestor : function(node){
7277 var p = this.parentNode;
7287 toString : function(){
7288 return "[Node"+(this.id?" "+this.id:"")+"]";
7292 * Ext JS Library 1.1.1
7293 * Copyright(c) 2006-2007, Ext JS, LLC.
7295 * Originally Released Under LGPL - original licence link has changed is not relivant.
7298 * <script type="text/javascript">
7303 * @extends Roo.Element
7304 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7305 * automatic maintaining of shadow/shim positions.
7306 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7307 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7308 * you can pass a string with a CSS class name. False turns off the shadow.
7309 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7310 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7311 * @cfg {String} cls CSS class to add to the element
7312 * @cfg {Number} zindex Starting z-index (defaults to 11000)
7313 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7315 * @param {Object} config An object with config options.
7316 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7319 Roo.Layer = function(config, existingEl){
7320 config = config || {};
7321 var dh = Roo.DomHelper;
7322 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7324 this.dom = Roo.getDom(existingEl);
7327 var o = config.dh || {tag: "div", cls: "x-layer"};
7328 this.dom = dh.append(pel, o);
7331 this.addClass(config.cls);
7333 this.constrain = config.constrain !== false;
7334 this.visibilityMode = Roo.Element.VISIBILITY;
7336 this.id = this.dom.id = config.id;
7338 this.id = Roo.id(this.dom);
7340 this.zindex = config.zindex || this.getZIndex();
7341 this.position("absolute", this.zindex);
7343 this.shadowOffset = config.shadowOffset || 4;
7344 this.shadow = new Roo.Shadow({
7345 offset : this.shadowOffset,
7346 mode : config.shadow
7349 this.shadowOffset = 0;
7351 this.useShim = config.shim !== false && Roo.useShims;
7352 this.useDisplay = config.useDisplay;
7356 var supr = Roo.Element.prototype;
7358 // shims are shared among layer to keep from having 100 iframes
7361 Roo.extend(Roo.Layer, Roo.Element, {
7363 getZIndex : function(){
7364 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7367 getShim : function(){
7374 var shim = shims.shift();
7376 shim = this.createShim();
7377 shim.enableDisplayMode('block');
7378 shim.dom.style.display = 'none';
7379 shim.dom.style.visibility = 'visible';
7381 var pn = this.dom.parentNode;
7382 if(shim.dom.parentNode != pn){
7383 pn.insertBefore(shim.dom, this.dom);
7385 shim.setStyle('z-index', this.getZIndex()-2);
7390 hideShim : function(){
7392 this.shim.setDisplayed(false);
7393 shims.push(this.shim);
7398 disableShadow : function(){
7400 this.shadowDisabled = true;
7402 this.lastShadowOffset = this.shadowOffset;
7403 this.shadowOffset = 0;
7407 enableShadow : function(show){
7409 this.shadowDisabled = false;
7410 this.shadowOffset = this.lastShadowOffset;
7411 delete this.lastShadowOffset;
7419 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7420 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7421 sync : function(doShow){
7422 var sw = this.shadow;
7423 if(!this.updating && this.isVisible() && (sw || this.useShim)){
7424 var sh = this.getShim();
7426 var w = this.getWidth(),
7427 h = this.getHeight();
7429 var l = this.getLeft(true),
7430 t = this.getTop(true);
7432 if(sw && !this.shadowDisabled){
7433 if(doShow && !sw.isVisible()){
7436 sw.realign(l, t, w, h);
7442 // fit the shim behind the shadow, so it is shimmed too
7443 var a = sw.adjusts, s = sh.dom.style;
7444 s.left = (Math.min(l, l+a.l))+"px";
7445 s.top = (Math.min(t, t+a.t))+"px";
7446 s.width = (w+a.w)+"px";
7447 s.height = (h+a.h)+"px";
7454 sh.setLeftTop(l, t);
7461 destroy : function(){
7466 this.removeAllListeners();
7467 var pn = this.dom.parentNode;
7469 pn.removeChild(this.dom);
7471 Roo.Element.uncache(this.id);
7474 remove : function(){
7479 beginUpdate : function(){
7480 this.updating = true;
7484 endUpdate : function(){
7485 this.updating = false;
7490 hideUnders : function(negOffset){
7498 constrainXY : function(){
7500 var vw = Roo.lib.Dom.getViewWidth(),
7501 vh = Roo.lib.Dom.getViewHeight();
7502 var s = Roo.get(document).getScroll();
7504 var xy = this.getXY();
7505 var x = xy[0], y = xy[1];
7506 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7507 // only move it if it needs it
7509 // first validate right/bottom
7510 if((x + w) > vw+s.left){
7511 x = vw - w - this.shadowOffset;
7514 if((y + h) > vh+s.top){
7515 y = vh - h - this.shadowOffset;
7518 // then make sure top/left isn't negative
7529 var ay = this.avoidY;
7530 if(y <= ay && (y+h) >= ay){
7536 supr.setXY.call(this, xy);
7542 isVisible : function(){
7543 return this.visible;
7547 showAction : function(){
7548 this.visible = true; // track visibility to prevent getStyle calls
7549 if(this.useDisplay === true){
7550 this.setDisplayed("");
7551 }else if(this.lastXY){
7552 supr.setXY.call(this, this.lastXY);
7553 }else if(this.lastLT){
7554 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7559 hideAction : function(){
7560 this.visible = false;
7561 if(this.useDisplay === true){
7562 this.setDisplayed(false);
7564 this.setLeftTop(-10000,-10000);
7568 // overridden Element method
7569 setVisible : function(v, a, d, c, e){
7574 var cb = function(){
7579 }.createDelegate(this);
7580 supr.setVisible.call(this, true, true, d, cb, e);
7583 this.hideUnders(true);
7592 }.createDelegate(this);
7594 supr.setVisible.call(this, v, a, d, cb, e);
7603 storeXY : function(xy){
7608 storeLeftTop : function(left, top){
7610 this.lastLT = [left, top];
7614 beforeFx : function(){
7615 this.beforeAction();
7616 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7620 afterFx : function(){
7621 Roo.Layer.superclass.afterFx.apply(this, arguments);
7622 this.sync(this.isVisible());
7626 beforeAction : function(){
7627 if(!this.updating && this.shadow){
7632 // overridden Element method
7633 setLeft : function(left){
7634 this.storeLeftTop(left, this.getTop(true));
7635 supr.setLeft.apply(this, arguments);
7639 setTop : function(top){
7640 this.storeLeftTop(this.getLeft(true), top);
7641 supr.setTop.apply(this, arguments);
7645 setLeftTop : function(left, top){
7646 this.storeLeftTop(left, top);
7647 supr.setLeftTop.apply(this, arguments);
7651 setXY : function(xy, a, d, c, e){
7653 this.beforeAction();
7655 var cb = this.createCB(c);
7656 supr.setXY.call(this, xy, a, d, cb, e);
7663 createCB : function(c){
7674 // overridden Element method
7675 setX : function(x, a, d, c, e){
7676 this.setXY([x, this.getY()], a, d, c, e);
7679 // overridden Element method
7680 setY : function(y, a, d, c, e){
7681 this.setXY([this.getX(), y], a, d, c, e);
7684 // overridden Element method
7685 setSize : function(w, h, a, d, c, e){
7686 this.beforeAction();
7687 var cb = this.createCB(c);
7688 supr.setSize.call(this, w, h, a, d, cb, e);
7694 // overridden Element method
7695 setWidth : function(w, a, d, c, e){
7696 this.beforeAction();
7697 var cb = this.createCB(c);
7698 supr.setWidth.call(this, w, a, d, cb, e);
7704 // overridden Element method
7705 setHeight : function(h, a, d, c, e){
7706 this.beforeAction();
7707 var cb = this.createCB(c);
7708 supr.setHeight.call(this, h, a, d, cb, e);
7714 // overridden Element method
7715 setBounds : function(x, y, w, h, a, d, c, e){
7716 this.beforeAction();
7717 var cb = this.createCB(c);
7719 this.storeXY([x, y]);
7720 supr.setXY.call(this, [x, y]);
7721 supr.setSize.call(this, w, h, a, d, cb, e);
7724 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7730 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7731 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7732 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7733 * @param {Number} zindex The new z-index to set
7734 * @return {this} The Layer
7736 setZIndex : function(zindex){
7737 this.zindex = zindex;
7738 this.setStyle("z-index", zindex + 2);
7740 this.shadow.setZIndex(zindex + 1);
7743 this.shim.setStyle("z-index", zindex);
7749 * Ext JS Library 1.1.1
7750 * Copyright(c) 2006-2007, Ext JS, LLC.
7752 * Originally Released Under LGPL - original licence link has changed is not relivant.
7755 * <script type="text/javascript">
7761 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
7762 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
7763 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7765 * Create a new Shadow
7766 * @param {Object} config The config object
7768 Roo.Shadow = function(config){
7769 Roo.apply(this, config);
7770 if(typeof this.mode != "string"){
7771 this.mode = this.defaultMode;
7773 var o = this.offset, a = {h: 0};
7774 var rad = Math.floor(this.offset/2);
7775 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7781 a.l -= this.offset + rad;
7782 a.t -= this.offset + rad;
7793 a.l -= (this.offset - rad);
7794 a.t -= this.offset + rad;
7796 a.w -= (this.offset - rad)*2;
7807 a.l -= (this.offset - rad);
7808 a.t -= (this.offset - rad);
7810 a.w -= (this.offset + rad + 1);
7811 a.h -= (this.offset + rad);
7820 Roo.Shadow.prototype = {
7822 * @cfg {String} mode
7823 * The shadow display mode. Supports the following options:<br />
7824 * sides: Shadow displays on both sides and bottom only<br />
7825 * frame: Shadow displays equally on all four sides<br />
7826 * drop: Traditional bottom-right drop shadow (default)
7829 * @cfg {String} offset
7830 * The number of pixels to offset the shadow from the element (defaults to 4)
7835 defaultMode: "drop",
7838 * Displays the shadow under the target element
7839 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7841 show : function(target){
7842 target = Roo.get(target);
7844 this.el = Roo.Shadow.Pool.pull();
7845 if(this.el.dom.nextSibling != target.dom){
7846 this.el.insertBefore(target);
7849 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7851 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7854 target.getLeft(true),
7855 target.getTop(true),
7859 this.el.dom.style.display = "block";
7863 * Returns true if the shadow is visible, else false
7865 isVisible : function(){
7866 return this.el ? true : false;
7870 * Direct alignment when values are already available. Show must be called at least once before
7871 * calling this method to ensure it is initialized.
7872 * @param {Number} left The target element left position
7873 * @param {Number} top The target element top position
7874 * @param {Number} width The target element width
7875 * @param {Number} height The target element height
7877 realign : function(l, t, w, h){
7881 var a = this.adjusts, d = this.el.dom, s = d.style;
7883 s.left = (l+a.l)+"px";
7884 s.top = (t+a.t)+"px";
7885 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7887 if(s.width != sws || s.height != shs){
7891 var cn = d.childNodes;
7892 var sww = Math.max(0, (sw-12))+"px";
7893 cn[0].childNodes[1].style.width = sww;
7894 cn[1].childNodes[1].style.width = sww;
7895 cn[2].childNodes[1].style.width = sww;
7896 cn[1].style.height = Math.max(0, (sh-12))+"px";
7906 this.el.dom.style.display = "none";
7907 Roo.Shadow.Pool.push(this.el);
7913 * Adjust the z-index of this shadow
7914 * @param {Number} zindex The new z-index
7916 setZIndex : function(z){
7919 this.el.setStyle("z-index", z);
7924 // Private utility class that manages the internal Shadow cache
7925 Roo.Shadow.Pool = function(){
7927 var markup = Roo.isIE ?
7928 '<div class="x-ie-shadow"></div>' :
7929 '<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>';
7934 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7935 sh.autoBoxAdjust = false;
7940 push : function(sh){
7946 * Ext JS Library 1.1.1
7947 * Copyright(c) 2006-2007, Ext JS, LLC.
7949 * Originally Released Under LGPL - original licence link has changed is not relivant.
7952 * <script type="text/javascript">
7957 * @class Roo.SplitBar
7958 * @extends Roo.util.Observable
7959 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7963 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7964 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7965 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7966 split.minSize = 100;
7967 split.maxSize = 600;
7968 split.animate = true;
7969 split.on('moved', splitterMoved);
7972 * Create a new SplitBar
7973 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
7974 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
7975 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7976 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
7977 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7978 position of the SplitBar).
7980 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7983 this.el = Roo.get(dragElement, true);
7984 this.el.dom.unselectable = "on";
7986 this.resizingEl = Roo.get(resizingElement, true);
7990 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7991 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7994 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7997 * The minimum size of the resizing element. (Defaults to 0)
8003 * The maximum size of the resizing element. (Defaults to 2000)
8006 this.maxSize = 2000;
8009 * Whether to animate the transition to the new size
8012 this.animate = false;
8015 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8018 this.useShim = false;
8025 this.proxy = Roo.SplitBar.createProxy(this.orientation);
8027 this.proxy = Roo.get(existingProxy).dom;
8030 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8033 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8036 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8039 this.dragSpecs = {};
8042 * @private The adapter to use to positon and resize elements
8044 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8045 this.adapter.init(this);
8047 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8049 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8050 this.el.addClass("x-splitbar-h");
8053 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8054 this.el.addClass("x-splitbar-v");
8060 * Fires when the splitter is moved (alias for {@link #event-moved})
8061 * @param {Roo.SplitBar} this
8062 * @param {Number} newSize the new width or height
8067 * Fires when the splitter is moved
8068 * @param {Roo.SplitBar} this
8069 * @param {Number} newSize the new width or height
8073 * @event beforeresize
8074 * Fires before the splitter is dragged
8075 * @param {Roo.SplitBar} this
8077 "beforeresize" : true,
8079 "beforeapply" : true
8082 Roo.util.Observable.call(this);
8085 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8086 onStartProxyDrag : function(x, y){
8087 this.fireEvent("beforeresize", this);
8089 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
8091 o.enableDisplayMode("block");
8092 // all splitbars share the same overlay
8093 Roo.SplitBar.prototype.overlay = o;
8095 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8096 this.overlay.show();
8097 Roo.get(this.proxy).setDisplayed("block");
8098 var size = this.adapter.getElementSize(this);
8099 this.activeMinSize = this.getMinimumSize();;
8100 this.activeMaxSize = this.getMaximumSize();;
8101 var c1 = size - this.activeMinSize;
8102 var c2 = Math.max(this.activeMaxSize - size, 0);
8103 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8104 this.dd.resetConstraints();
8105 this.dd.setXConstraint(
8106 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
8107 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8109 this.dd.setYConstraint(0, 0);
8111 this.dd.resetConstraints();
8112 this.dd.setXConstraint(0, 0);
8113 this.dd.setYConstraint(
8114 this.placement == Roo.SplitBar.TOP ? c1 : c2,
8115 this.placement == Roo.SplitBar.TOP ? c2 : c1
8118 this.dragSpecs.startSize = size;
8119 this.dragSpecs.startPoint = [x, y];
8120 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8124 * @private Called after the drag operation by the DDProxy
8126 onEndProxyDrag : function(e){
8127 Roo.get(this.proxy).setDisplayed(false);
8128 var endPoint = Roo.lib.Event.getXY(e);
8130 this.overlay.hide();
8133 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8134 newSize = this.dragSpecs.startSize +
8135 (this.placement == Roo.SplitBar.LEFT ?
8136 endPoint[0] - this.dragSpecs.startPoint[0] :
8137 this.dragSpecs.startPoint[0] - endPoint[0]
8140 newSize = this.dragSpecs.startSize +
8141 (this.placement == Roo.SplitBar.TOP ?
8142 endPoint[1] - this.dragSpecs.startPoint[1] :
8143 this.dragSpecs.startPoint[1] - endPoint[1]
8146 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8147 if(newSize != this.dragSpecs.startSize){
8148 if(this.fireEvent('beforeapply', this, newSize) !== false){
8149 this.adapter.setElementSize(this, newSize);
8150 this.fireEvent("moved", this, newSize);
8151 this.fireEvent("resize", this, newSize);
8157 * Get the adapter this SplitBar uses
8158 * @return The adapter object
8160 getAdapter : function(){
8161 return this.adapter;
8165 * Set the adapter this SplitBar uses
8166 * @param {Object} adapter A SplitBar adapter object
8168 setAdapter : function(adapter){
8169 this.adapter = adapter;
8170 this.adapter.init(this);
8174 * Gets the minimum size for the resizing element
8175 * @return {Number} The minimum size
8177 getMinimumSize : function(){
8178 return this.minSize;
8182 * Sets the minimum size for the resizing element
8183 * @param {Number} minSize The minimum size
8185 setMinimumSize : function(minSize){
8186 this.minSize = minSize;
8190 * Gets the maximum size for the resizing element
8191 * @return {Number} The maximum size
8193 getMaximumSize : function(){
8194 return this.maxSize;
8198 * Sets the maximum size for the resizing element
8199 * @param {Number} maxSize The maximum size
8201 setMaximumSize : function(maxSize){
8202 this.maxSize = maxSize;
8206 * Sets the initialize size for the resizing element
8207 * @param {Number} size The initial size
8209 setCurrentSize : function(size){
8210 var oldAnimate = this.animate;
8211 this.animate = false;
8212 this.adapter.setElementSize(this, size);
8213 this.animate = oldAnimate;
8217 * Destroy this splitbar.
8218 * @param {Boolean} removeEl True to remove the element
8220 destroy : function(removeEl){
8225 this.proxy.parentNode.removeChild(this.proxy);
8233 * @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.
8235 Roo.SplitBar.createProxy = function(dir){
8236 var proxy = new Roo.Element(document.createElement("div"));
8237 proxy.unselectable();
8238 var cls = 'x-splitbar-proxy';
8239 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8240 document.body.appendChild(proxy.dom);
8245 * @class Roo.SplitBar.BasicLayoutAdapter
8246 * Default Adapter. It assumes the splitter and resizing element are not positioned
8247 * elements and only gets/sets the width of the element. Generally used for table based layouts.
8249 Roo.SplitBar.BasicLayoutAdapter = function(){
8252 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8253 // do nothing for now
8258 * Called before drag operations to get the current size of the resizing element.
8259 * @param {Roo.SplitBar} s The SplitBar using this adapter
8261 getElementSize : function(s){
8262 if(s.orientation == Roo.SplitBar.HORIZONTAL){
8263 return s.resizingEl.getWidth();
8265 return s.resizingEl.getHeight();
8270 * Called after drag operations to set the size of the resizing element.
8271 * @param {Roo.SplitBar} s The SplitBar using this adapter
8272 * @param {Number} newSize The new size to set
8273 * @param {Function} onComplete A function to be invoked when resizing is complete
8275 setElementSize : function(s, newSize, onComplete){
8276 if(s.orientation == Roo.SplitBar.HORIZONTAL){
8278 s.resizingEl.setWidth(newSize);
8280 onComplete(s, newSize);
8283 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8288 s.resizingEl.setHeight(newSize);
8290 onComplete(s, newSize);
8293 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8300 *@class Roo.SplitBar.AbsoluteLayoutAdapter
8301 * @extends Roo.SplitBar.BasicLayoutAdapter
8302 * Adapter that moves the splitter element to align with the resized sizing element.
8303 * Used with an absolute positioned SplitBar.
8304 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8305 * document.body, make sure you assign an id to the body element.
8307 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8308 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8309 this.container = Roo.get(container);
8312 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8317 getElementSize : function(s){
8318 return this.basic.getElementSize(s);
8321 setElementSize : function(s, newSize, onComplete){
8322 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8325 moveSplitter : function(s){
8326 var yes = Roo.SplitBar;
8327 switch(s.placement){
8329 s.el.setX(s.resizingEl.getRight());
8332 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8335 s.el.setY(s.resizingEl.getBottom());
8338 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8345 * Orientation constant - Create a vertical SplitBar
8349 Roo.SplitBar.VERTICAL = 1;
8352 * Orientation constant - Create a horizontal SplitBar
8356 Roo.SplitBar.HORIZONTAL = 2;
8359 * Placement constant - The resizing element is to the left of the splitter element
8363 Roo.SplitBar.LEFT = 1;
8366 * Placement constant - The resizing element is to the right of the splitter element
8370 Roo.SplitBar.RIGHT = 2;
8373 * Placement constant - The resizing element is positioned above the splitter element
8377 Roo.SplitBar.TOP = 3;
8380 * Placement constant - The resizing element is positioned under splitter element
8384 Roo.SplitBar.BOTTOM = 4;
8387 * Ext JS Library 1.1.1
8388 * Copyright(c) 2006-2007, Ext JS, LLC.
8390 * Originally Released Under LGPL - original licence link has changed is not relivant.
8393 * <script type="text/javascript">
8398 * @extends Roo.util.Observable
8399 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
8400 * This class also supports single and multi selection modes. <br>
8401 * Create a data model bound view:
8403 var store = new Roo.data.Store(...);
8405 var view = new Roo.View({
8407 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
8410 selectedClass: "ydataview-selected",
8414 // listen for node click?
8415 view.on("click", function(vw, index, node, e){
8416 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8420 dataModel.load("foobar.xml");
8422 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8424 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8425 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8427 * Note: old style constructor is still suported (container, template, config)
8431 * @param {Object} config The config object
8434 Roo.View = function(config, depreciated_tpl, depreciated_config){
8436 this.parent = false;
8438 if (typeof(depreciated_tpl) == 'undefined') {
8439 // new way.. - universal constructor.
8440 Roo.apply(this, config);
8441 this.el = Roo.get(this.el);
8444 this.el = Roo.get(config);
8445 this.tpl = depreciated_tpl;
8446 Roo.apply(this, depreciated_config);
8448 this.wrapEl = this.el.wrap().wrap();
8449 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8452 if(typeof(this.tpl) == "string"){
8453 this.tpl = new Roo.Template(this.tpl);
8455 // support xtype ctors..
8456 this.tpl = new Roo.factory(this.tpl, Roo);
8465 * @event beforeclick
8466 * Fires before a click is processed. Returns false to cancel the default action.
8467 * @param {Roo.View} this
8468 * @param {Number} index The index of the target node
8469 * @param {HTMLElement} node The target node
8470 * @param {Roo.EventObject} e The raw event object
8472 "beforeclick" : true,
8475 * Fires when a template node is clicked.
8476 * @param {Roo.View} this
8477 * @param {Number} index The index of the target node
8478 * @param {HTMLElement} node The target node
8479 * @param {Roo.EventObject} e The raw event object
8484 * Fires when a template node is double clicked.
8485 * @param {Roo.View} this
8486 * @param {Number} index The index of the target node
8487 * @param {HTMLElement} node The target node
8488 * @param {Roo.EventObject} e The raw event object
8492 * @event contextmenu
8493 * Fires when a template node is right clicked.
8494 * @param {Roo.View} this
8495 * @param {Number} index The index of the target node
8496 * @param {HTMLElement} node The target node
8497 * @param {Roo.EventObject} e The raw event object
8499 "contextmenu" : true,
8501 * @event selectionchange
8502 * Fires when the selected nodes change.
8503 * @param {Roo.View} this
8504 * @param {Array} selections Array of the selected nodes
8506 "selectionchange" : true,
8509 * @event beforeselect
8510 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8511 * @param {Roo.View} this
8512 * @param {HTMLElement} node The node to be selected
8513 * @param {Array} selections Array of currently selected nodes
8515 "beforeselect" : true,
8517 * @event preparedata
8518 * Fires on every row to render, to allow you to change the data.
8519 * @param {Roo.View} this
8520 * @param {Object} data to be rendered (change this)
8522 "preparedata" : true
8530 "click": this.onClick,
8531 "dblclick": this.onDblClick,
8532 "contextmenu": this.onContextMenu,
8536 this.selections = [];
8538 this.cmp = new Roo.CompositeElementLite([]);
8540 this.store = Roo.factory(this.store, Roo.data);
8541 this.setStore(this.store, true);
8544 if ( this.footer && this.footer.xtype) {
8546 var fctr = this.wrapEl.appendChild(document.createElement("div"));
8548 this.footer.dataSource = this.store;
8549 this.footer.container = fctr;
8550 this.footer = Roo.factory(this.footer, Roo);
8551 fctr.insertFirst(this.el);
8553 // this is a bit insane - as the paging toolbar seems to detach the el..
8554 // dom.parentNode.parentNode.parentNode
8555 // they get detached?
8559 Roo.View.superclass.constructor.call(this);
8564 Roo.extend(Roo.View, Roo.util.Observable, {
8567 * @cfg {Roo.data.Store} store Data store to load data from.
8572 * @cfg {String|Roo.Element} el The container element.
8577 * @cfg {String|Roo.Template} tpl The template used by this View
8581 * @cfg {String} dataName the named area of the template to use as the data area
8582 * Works with domtemplates roo-name="name"
8586 * @cfg {String} selectedClass The css class to add to selected nodes
8588 selectedClass : "x-view-selected",
8590 * @cfg {String} emptyText The empty text to show when nothing is loaded.
8595 * @cfg {String} text to display on mask (default Loading)
8599 * @cfg {Boolean} multiSelect Allow multiple selection
8601 multiSelect : false,
8603 * @cfg {Boolean} singleSelect Allow single selection
8605 singleSelect: false,
8608 * @cfg {Boolean} toggleSelect - selecting
8610 toggleSelect : false,
8613 * @cfg {Boolean} tickable - selecting
8618 * Returns the element this view is bound to.
8619 * @return {Roo.Element}
8628 * Refreshes the view. - called by datachanged on the store. - do not call directly.
8630 refresh : function(){
8631 //Roo.log('refresh');
8634 // if we are using something like 'domtemplate', then
8635 // the what gets used is:
8636 // t.applySubtemplate(NAME, data, wrapping data..)
8637 // the outer template then get' applied with
8638 // the store 'extra data'
8639 // and the body get's added to the
8640 // roo-name="data" node?
8641 // <span class='roo-tpl-{name}'></span> ?????
8645 this.clearSelections();
8648 var records = this.store.getRange();
8649 if(records.length < 1) {
8651 // is this valid?? = should it render a template??
8653 this.el.update(this.emptyText);
8657 if (this.dataName) {
8658 this.el.update(t.apply(this.store.meta)); //????
8659 el = this.el.child('.roo-tpl-' + this.dataName);
8662 for(var i = 0, len = records.length; i < len; i++){
8663 var data = this.prepareData(records[i].data, i, records[i]);
8664 this.fireEvent("preparedata", this, data, i, records[i]);
8666 var d = Roo.apply({}, data);
8669 Roo.apply(d, {'roo-id' : Roo.id()});
8673 Roo.each(this.parent.item, function(item){
8674 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8677 Roo.apply(d, {'roo-data-checked' : 'checked'});
8681 html[html.length] = Roo.util.Format.trim(
8683 t.applySubtemplate(this.dataName, d, this.store.meta) :
8690 el.update(html.join(""));
8691 this.nodes = el.dom.childNodes;
8692 this.updateIndexes(0);
8697 * Function to override to reformat the data that is sent to
8698 * the template for each node.
8699 * DEPRICATED - use the preparedata event handler.
8700 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8701 * a JSON object for an UpdateManager bound view).
8703 prepareData : function(data, index, record)
8705 this.fireEvent("preparedata", this, data, index, record);
8709 onUpdate : function(ds, record){
8710 // Roo.log('on update');
8711 this.clearSelections();
8712 var index = this.store.indexOf(record);
8713 var n = this.nodes[index];
8714 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8715 n.parentNode.removeChild(n);
8716 this.updateIndexes(index, index);
8722 onAdd : function(ds, records, index)
8724 //Roo.log(['on Add', ds, records, index] );
8725 this.clearSelections();
8726 if(this.nodes.length == 0){
8730 var n = this.nodes[index];
8731 for(var i = 0, len = records.length; i < len; i++){
8732 var d = this.prepareData(records[i].data, i, records[i]);
8734 this.tpl.insertBefore(n, d);
8737 this.tpl.append(this.el, d);
8740 this.updateIndexes(index);
8743 onRemove : function(ds, record, index){
8744 // Roo.log('onRemove');
8745 this.clearSelections();
8746 var el = this.dataName ?
8747 this.el.child('.roo-tpl-' + this.dataName) :
8750 el.dom.removeChild(this.nodes[index]);
8751 this.updateIndexes(index);
8755 * Refresh an individual node.
8756 * @param {Number} index
8758 refreshNode : function(index){
8759 this.onUpdate(this.store, this.store.getAt(index));
8762 updateIndexes : function(startIndex, endIndex){
8763 var ns = this.nodes;
8764 startIndex = startIndex || 0;
8765 endIndex = endIndex || ns.length - 1;
8766 for(var i = startIndex; i <= endIndex; i++){
8767 ns[i].nodeIndex = i;
8772 * Changes the data store this view uses and refresh the view.
8773 * @param {Store} store
8775 setStore : function(store, initial){
8776 if(!initial && this.store){
8777 this.store.un("datachanged", this.refresh);
8778 this.store.un("add", this.onAdd);
8779 this.store.un("remove", this.onRemove);
8780 this.store.un("update", this.onUpdate);
8781 this.store.un("clear", this.refresh);
8782 this.store.un("beforeload", this.onBeforeLoad);
8783 this.store.un("load", this.onLoad);
8784 this.store.un("loadexception", this.onLoad);
8788 store.on("datachanged", this.refresh, this);
8789 store.on("add", this.onAdd, this);
8790 store.on("remove", this.onRemove, this);
8791 store.on("update", this.onUpdate, this);
8792 store.on("clear", this.refresh, this);
8793 store.on("beforeload", this.onBeforeLoad, this);
8794 store.on("load", this.onLoad, this);
8795 store.on("loadexception", this.onLoad, this);
8803 * onbeforeLoad - masks the loading area.
8806 onBeforeLoad : function(store,opts)
8808 //Roo.log('onBeforeLoad');
8812 this.el.mask(this.mask ? this.mask : "Loading" );
8814 onLoad : function ()
8821 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8822 * @param {HTMLElement} node
8823 * @return {HTMLElement} The template node
8825 findItemFromChild : function(node){
8826 var el = this.dataName ?
8827 this.el.child('.roo-tpl-' + this.dataName,true) :
8830 if(!node || node.parentNode == el){
8833 var p = node.parentNode;
8834 while(p && p != el){
8835 if(p.parentNode == el){
8844 onClick : function(e){
8845 var item = this.findItemFromChild(e.getTarget());
8847 var index = this.indexOf(item);
8848 if(this.onItemClick(item, index, e) !== false){
8849 this.fireEvent("click", this, index, item, e);
8852 this.clearSelections();
8857 onContextMenu : function(e){
8858 var item = this.findItemFromChild(e.getTarget());
8860 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8865 onDblClick : function(e){
8866 var item = this.findItemFromChild(e.getTarget());
8868 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8872 onItemClick : function(item, index, e)
8874 if(this.fireEvent("beforeclick", this, index, item, e) === false){
8877 if (this.toggleSelect) {
8878 var m = this.isSelected(item) ? 'unselect' : 'select';
8881 _t[m](item, true, false);
8884 if(this.multiSelect || this.singleSelect){
8885 if(this.multiSelect && e.shiftKey && this.lastSelection){
8886 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8888 this.select(item, this.multiSelect && e.ctrlKey);
8889 this.lastSelection = item;
8901 * Get the number of selected nodes.
8904 getSelectionCount : function(){
8905 return this.selections.length;
8909 * Get the currently selected nodes.
8910 * @return {Array} An array of HTMLElements
8912 getSelectedNodes : function(){
8913 return this.selections;
8917 * Get the indexes of the selected nodes.
8920 getSelectedIndexes : function(){
8921 var indexes = [], s = this.selections;
8922 for(var i = 0, len = s.length; i < len; i++){
8923 indexes.push(s[i].nodeIndex);
8929 * Clear all selections
8930 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8932 clearSelections : function(suppressEvent){
8933 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8934 this.cmp.elements = this.selections;
8935 this.cmp.removeClass(this.selectedClass);
8936 this.selections = [];
8938 this.fireEvent("selectionchange", this, this.selections);
8944 * Returns true if the passed node is selected
8945 * @param {HTMLElement/Number} node The node or node index
8948 isSelected : function(node){
8949 var s = this.selections;
8953 node = this.getNode(node);
8954 return s.indexOf(node) !== -1;
8959 * @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
8960 * @param {Boolean} keepExisting (optional) true to keep existing selections
8961 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8963 select : function(nodeInfo, keepExisting, suppressEvent){
8964 if(nodeInfo instanceof Array){
8966 this.clearSelections(true);
8968 for(var i = 0, len = nodeInfo.length; i < len; i++){
8969 this.select(nodeInfo[i], true, true);
8973 var node = this.getNode(nodeInfo);
8974 if(!node || this.isSelected(node)){
8975 return; // already selected.
8978 this.clearSelections(true);
8981 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8982 Roo.fly(node).addClass(this.selectedClass);
8983 this.selections.push(node);
8985 this.fireEvent("selectionchange", this, this.selections);
8993 * @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
8994 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8995 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8997 unselect : function(nodeInfo, keepExisting, suppressEvent)
8999 if(nodeInfo instanceof Array){
9000 Roo.each(this.selections, function(s) {
9001 this.unselect(s, nodeInfo);
9005 var node = this.getNode(nodeInfo);
9006 if(!node || !this.isSelected(node)){
9007 //Roo.log("not selected");
9008 return; // not selected.
9012 Roo.each(this.selections, function(s) {
9014 Roo.fly(node).removeClass(this.selectedClass);
9021 this.selections= ns;
9022 this.fireEvent("selectionchange", this, this.selections);
9026 * Gets a template node.
9027 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9028 * @return {HTMLElement} The node or null if it wasn't found
9030 getNode : function(nodeInfo){
9031 if(typeof nodeInfo == "string"){
9032 return document.getElementById(nodeInfo);
9033 }else if(typeof nodeInfo == "number"){
9034 return this.nodes[nodeInfo];
9040 * Gets a range template nodes.
9041 * @param {Number} startIndex
9042 * @param {Number} endIndex
9043 * @return {Array} An array of nodes
9045 getNodes : function(start, end){
9046 var ns = this.nodes;
9048 end = typeof end == "undefined" ? ns.length - 1 : end;
9051 for(var i = start; i <= end; i++){
9055 for(var i = start; i >= end; i--){
9063 * Finds the index of the passed node
9064 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9065 * @return {Number} The index of the node or -1
9067 indexOf : function(node){
9068 node = this.getNode(node);
9069 if(typeof node.nodeIndex == "number"){
9070 return node.nodeIndex;
9072 var ns = this.nodes;
9073 for(var i = 0, len = ns.length; i < len; i++){
9083 * Ext JS Library 1.1.1
9084 * Copyright(c) 2006-2007, Ext JS, LLC.
9086 * Originally Released Under LGPL - original licence link has changed is not relivant.
9089 * <script type="text/javascript">
9093 * @class Roo.JsonView
9095 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9097 var view = new Roo.JsonView({
9098 container: "my-element",
9099 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
9104 // listen for node click?
9105 view.on("click", function(vw, index, node, e){
9106 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9109 // direct load of JSON data
9110 view.load("foobar.php");
9112 // Example from my blog list
9113 var tpl = new Roo.Template(
9114 '<div class="entry">' +
9115 '<a class="entry-title" href="{link}">{title}</a>' +
9116 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
9117 "</div><hr />"
9120 var moreView = new Roo.JsonView({
9121 container : "entry-list",
9125 moreView.on("beforerender", this.sortEntries, this);
9127 url: "/blog/get-posts.php",
9128 params: "allposts=true",
9129 text: "Loading Blog Entries..."
9133 * Note: old code is supported with arguments : (container, template, config)
9137 * Create a new JsonView
9139 * @param {Object} config The config object
9142 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9145 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9147 var um = this.el.getUpdateManager();
9148 um.setRenderer(this);
9149 um.on("update", this.onLoad, this);
9150 um.on("failure", this.onLoadException, this);
9153 * @event beforerender
9154 * Fires before rendering of the downloaded JSON data.
9155 * @param {Roo.JsonView} this
9156 * @param {Object} data The JSON data loaded
9160 * Fires when data is loaded.
9161 * @param {Roo.JsonView} this
9162 * @param {Object} data The JSON data loaded
9163 * @param {Object} response The raw Connect response object
9166 * @event loadexception
9167 * Fires when loading fails.
9168 * @param {Roo.JsonView} this
9169 * @param {Object} response The raw Connect response object
9172 'beforerender' : true,
9174 'loadexception' : true
9177 Roo.extend(Roo.JsonView, Roo.View, {
9179 * @type {String} The root property in the loaded JSON object that contains the data
9184 * Refreshes the view.
9186 refresh : function(){
9187 this.clearSelections();
9190 var o = this.jsonData;
9191 if(o && o.length > 0){
9192 for(var i = 0, len = o.length; i < len; i++){
9193 var data = this.prepareData(o[i], i, o);
9194 html[html.length] = this.tpl.apply(data);
9197 html.push(this.emptyText);
9199 this.el.update(html.join(""));
9200 this.nodes = this.el.dom.childNodes;
9201 this.updateIndexes(0);
9205 * 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.
9206 * @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:
9209 url: "your-url.php",
9210 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9211 callback: yourFunction,
9212 scope: yourObject, //(optional scope)
9220 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9221 * 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.
9222 * @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}
9223 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9224 * @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.
9227 var um = this.el.getUpdateManager();
9228 um.update.apply(um, arguments);
9231 // note - render is a standard framework call...
9232 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
9233 render : function(el, response){
9235 this.clearSelections();
9239 if (response != '') {
9240 o = Roo.util.JSON.decode(response.responseText);
9243 o = o[this.jsonRoot];
9249 * The current JSON data or null
9252 this.beforeRender();
9257 * Get the number of records in the current JSON dataset
9260 getCount : function(){
9261 return this.jsonData ? this.jsonData.length : 0;
9265 * Returns the JSON object for the specified node(s)
9266 * @param {HTMLElement/Array} node The node or an array of nodes
9267 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9268 * you get the JSON object for the node
9270 getNodeData : function(node){
9271 if(node instanceof Array){
9273 for(var i = 0, len = node.length; i < len; i++){
9274 data.push(this.getNodeData(node[i]));
9278 return this.jsonData[this.indexOf(node)] || null;
9281 beforeRender : function(){
9282 this.snapshot = this.jsonData;
9284 this.sort.apply(this, this.sortInfo);
9286 this.fireEvent("beforerender", this, this.jsonData);
9289 onLoad : function(el, o){
9290 this.fireEvent("load", this, this.jsonData, o);
9293 onLoadException : function(el, o){
9294 this.fireEvent("loadexception", this, o);
9298 * Filter the data by a specific property.
9299 * @param {String} property A property on your JSON objects
9300 * @param {String/RegExp} value Either string that the property values
9301 * should start with, or a RegExp to test against the property
9303 filter : function(property, value){
9306 var ss = this.snapshot;
9307 if(typeof value == "string"){
9308 var vlen = value.length;
9313 value = value.toLowerCase();
9314 for(var i = 0, len = ss.length; i < len; i++){
9316 if(o[property].substr(0, vlen).toLowerCase() == value){
9320 } else if(value.exec){ // regex?
9321 for(var i = 0, len = ss.length; i < len; i++){
9323 if(value.test(o[property])){
9330 this.jsonData = data;
9336 * Filter by a function. The passed function will be called with each
9337 * object in the current dataset. If the function returns true the value is kept,
9338 * otherwise it is filtered.
9339 * @param {Function} fn
9340 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9342 filterBy : function(fn, scope){
9345 var ss = this.snapshot;
9346 for(var i = 0, len = ss.length; i < len; i++){
9348 if(fn.call(scope || this, o)){
9352 this.jsonData = data;
9358 * Clears the current filter.
9360 clearFilter : function(){
9361 if(this.snapshot && this.jsonData != this.snapshot){
9362 this.jsonData = this.snapshot;
9369 * Sorts the data for this view and refreshes it.
9370 * @param {String} property A property on your JSON objects to sort on
9371 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9372 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9374 sort : function(property, dir, sortType){
9375 this.sortInfo = Array.prototype.slice.call(arguments, 0);
9378 var dsc = dir && dir.toLowerCase() == "desc";
9379 var f = function(o1, o2){
9380 var v1 = sortType ? sortType(o1[p]) : o1[p];
9381 var v2 = sortType ? sortType(o2[p]) : o2[p];
9384 return dsc ? +1 : -1;
9386 return dsc ? -1 : +1;
9391 this.jsonData.sort(f);
9393 if(this.jsonData != this.snapshot){
9394 this.snapshot.sort(f);
9400 * Ext JS Library 1.1.1
9401 * Copyright(c) 2006-2007, Ext JS, LLC.
9403 * Originally Released Under LGPL - original licence link has changed is not relivant.
9406 * <script type="text/javascript">
9411 * @class Roo.ColorPalette
9412 * @extends Roo.Component
9413 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
9414 * Here's an example of typical usage:
9416 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
9417 cp.render('my-div');
9419 cp.on('select', function(palette, selColor){
9420 // do something with selColor
9424 * Create a new ColorPalette
9425 * @param {Object} config The config object
9427 Roo.ColorPalette = function(config){
9428 Roo.ColorPalette.superclass.constructor.call(this, config);
9432 * Fires when a color is selected
9433 * @param {ColorPalette} this
9434 * @param {String} color The 6-digit color hex code (without the # symbol)
9440 this.on("select", this.handler, this.scope, true);
9443 Roo.extend(Roo.ColorPalette, Roo.Component, {
9445 * @cfg {String} itemCls
9446 * The CSS class to apply to the containing element (defaults to "x-color-palette")
9448 itemCls : "x-color-palette",
9450 * @cfg {String} value
9451 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
9452 * the hex codes are case-sensitive.
9457 ctype: "Roo.ColorPalette",
9460 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9462 allowReselect : false,
9465 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
9466 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
9467 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9468 * of colors with the width setting until the box is symmetrical.</p>
9469 * <p>You can override individual colors if needed:</p>
9471 var cp = new Roo.ColorPalette();
9472 cp.colors[0] = "FF0000"; // change the first box to red
9475 Or you can provide a custom array of your own for complete control:
9477 var cp = new Roo.ColorPalette();
9478 cp.colors = ["000000", "993300", "333300"];
9483 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9484 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9485 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9486 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9487 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9491 onRender : function(container, position){
9492 var t = new Roo.MasterTemplate(
9493 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
9495 var c = this.colors;
9496 for(var i = 0, len = c.length; i < len; i++){
9499 var el = document.createElement("div");
9500 el.className = this.itemCls;
9502 container.dom.insertBefore(el, position);
9503 this.el = Roo.get(el);
9504 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
9505 if(this.clickEvent != 'click'){
9506 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
9511 afterRender : function(){
9512 Roo.ColorPalette.superclass.afterRender.call(this);
9521 handleClick : function(e, t){
9524 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9525 this.select(c.toUpperCase());
9530 * Selects the specified color in the palette (fires the select event)
9531 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9533 select : function(color){
9534 color = color.replace("#", "");
9535 if(color != this.value || this.allowReselect){
9538 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9540 el.child("a.color-"+color).addClass("x-color-palette-sel");
9542 this.fireEvent("select", this, color);
9547 * Ext JS Library 1.1.1
9548 * Copyright(c) 2006-2007, Ext JS, LLC.
9550 * Originally Released Under LGPL - original licence link has changed is not relivant.
9553 * <script type="text/javascript">
9557 * @class Roo.DatePicker
9558 * @extends Roo.Component
9559 * Simple date picker class.
9561 * Create a new DatePicker
9562 * @param {Object} config The config object
9564 Roo.DatePicker = function(config){
9565 Roo.DatePicker.superclass.constructor.call(this, config);
9567 this.value = config && config.value ?
9568 config.value.clearTime() : new Date().clearTime();
9573 * Fires when a date is selected
9574 * @param {DatePicker} this
9575 * @param {Date} date The selected date
9579 * @event monthchange
9580 * Fires when the displayed month changes
9581 * @param {DatePicker} this
9582 * @param {Date} date The selected month
9588 this.on("select", this.handler, this.scope || this);
9590 // build the disabledDatesRE
9591 if(!this.disabledDatesRE && this.disabledDates){
9592 var dd = this.disabledDates;
9594 for(var i = 0; i < dd.length; i++){
9596 if(i != dd.length-1) {
9600 this.disabledDatesRE = new RegExp(re + ")");
9604 Roo.extend(Roo.DatePicker, Roo.Component, {
9606 * @cfg {String} todayText
9607 * The text to display on the button that selects the current date (defaults to "Today")
9609 todayText : "Today",
9611 * @cfg {String} okText
9612 * The text to display on the ok button
9614 okText : " OK ", //   to give the user extra clicking room
9616 * @cfg {String} cancelText
9617 * The text to display on the cancel button
9619 cancelText : "Cancel",
9621 * @cfg {String} todayTip
9622 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9624 todayTip : "{0} (Spacebar)",
9626 * @cfg {Date} minDate
9627 * Minimum allowable date (JavaScript date object, defaults to null)
9631 * @cfg {Date} maxDate
9632 * Maximum allowable date (JavaScript date object, defaults to null)
9636 * @cfg {String} minText
9637 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9639 minText : "This date is before the minimum date",
9641 * @cfg {String} maxText
9642 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9644 maxText : "This date is after the maximum date",
9646 * @cfg {String} format
9647 * The default date format string which can be overriden for localization support. The format must be
9648 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9652 * @cfg {Array} disabledDays
9653 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9655 disabledDays : null,
9657 * @cfg {String} disabledDaysText
9658 * The tooltip to display when the date falls on a disabled day (defaults to "")
9660 disabledDaysText : "",
9662 * @cfg {RegExp} disabledDatesRE
9663 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9665 disabledDatesRE : null,
9667 * @cfg {String} disabledDatesText
9668 * The tooltip text to display when the date falls on a disabled date (defaults to "")
9670 disabledDatesText : "",
9672 * @cfg {Boolean} constrainToViewport
9673 * True to constrain the date picker to the viewport (defaults to true)
9675 constrainToViewport : true,
9677 * @cfg {Array} monthNames
9678 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9680 monthNames : Date.monthNames,
9682 * @cfg {Array} dayNames
9683 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9685 dayNames : Date.dayNames,
9687 * @cfg {String} nextText
9688 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9690 nextText: 'Next Month (Control+Right)',
9692 * @cfg {String} prevText
9693 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9695 prevText: 'Previous Month (Control+Left)',
9697 * @cfg {String} monthYearText
9698 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9700 monthYearText: 'Choose a month (Control+Up/Down to move years)',
9702 * @cfg {Number} startDay
9703 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9707 * @cfg {Bool} showClear
9708 * Show a clear button (usefull for date form elements that can be blank.)
9714 * Sets the value of the date field
9715 * @param {Date} value The date to set
9717 setValue : function(value){
9718 var old = this.value;
9720 if (typeof(value) == 'string') {
9722 value = Date.parseDate(value, this.format);
9728 this.value = value.clearTime(true);
9730 this.update(this.value);
9735 * Gets the current selected value of the date field
9736 * @return {Date} The selected date
9738 getValue : function(){
9745 this.update(this.activeDate);
9750 onRender : function(container, position){
9753 '<table cellspacing="0">',
9754 '<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>',
9755 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9756 var dn = this.dayNames;
9757 for(var i = 0; i < 7; i++){
9758 var d = this.startDay+i;
9762 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9764 m[m.length] = "</tr></thead><tbody><tr>";
9765 for(var i = 0; i < 42; i++) {
9766 if(i % 7 == 0 && i != 0){
9767 m[m.length] = "</tr><tr>";
9769 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9771 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9772 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9774 var el = document.createElement("div");
9775 el.className = "x-date-picker";
9776 el.innerHTML = m.join("");
9778 container.dom.insertBefore(el, position);
9780 this.el = Roo.get(el);
9781 this.eventEl = Roo.get(el.firstChild);
9783 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9784 handler: this.showPrevMonth,
9786 preventDefault:true,
9790 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9791 handler: this.showNextMonth,
9793 preventDefault:true,
9797 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
9799 this.monthPicker = this.el.down('div.x-date-mp');
9800 this.monthPicker.enableDisplayMode('block');
9802 var kn = new Roo.KeyNav(this.eventEl, {
9803 "left" : function(e){
9805 this.showPrevMonth() :
9806 this.update(this.activeDate.add("d", -1));
9809 "right" : function(e){
9811 this.showNextMonth() :
9812 this.update(this.activeDate.add("d", 1));
9817 this.showNextYear() :
9818 this.update(this.activeDate.add("d", -7));
9821 "down" : function(e){
9823 this.showPrevYear() :
9824 this.update(this.activeDate.add("d", 7));
9827 "pageUp" : function(e){
9828 this.showNextMonth();
9831 "pageDown" : function(e){
9832 this.showPrevMonth();
9835 "enter" : function(e){
9836 e.stopPropagation();
9843 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
9845 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
9847 this.el.unselectable();
9849 this.cells = this.el.select("table.x-date-inner tbody td");
9850 this.textNodes = this.el.query("table.x-date-inner tbody span");
9852 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9854 tooltip: this.monthYearText
9857 this.mbtn.on('click', this.showMonthPicker, this);
9858 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9861 var today = (new Date()).dateFormat(this.format);
9863 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9864 if (this.showClear) {
9865 baseTb.add( new Roo.Toolbar.Fill());
9868 text: String.format(this.todayText, today),
9869 tooltip: String.format(this.todayTip, today),
9870 handler: this.selectToday,
9874 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9877 if (this.showClear) {
9879 baseTb.add( new Roo.Toolbar.Fill());
9882 cls: 'x-btn-icon x-btn-clear',
9883 handler: function() {
9885 this.fireEvent("select", this, '');
9895 this.update(this.value);
9898 createMonthPicker : function(){
9899 if(!this.monthPicker.dom.firstChild){
9900 var buf = ['<table border="0" cellspacing="0">'];
9901 for(var i = 0; i < 6; i++){
9903 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9904 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9906 '<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>' :
9907 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9911 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9913 '</button><button type="button" class="x-date-mp-cancel">',
9915 '</button></td></tr>',
9918 this.monthPicker.update(buf.join(''));
9919 this.monthPicker.on('click', this.onMonthClick, this);
9920 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9922 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9923 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9925 this.mpMonths.each(function(m, a, i){
9928 m.dom.xmonth = 5 + Math.round(i * .5);
9930 m.dom.xmonth = Math.round((i-1) * .5);
9936 showMonthPicker : function(){
9937 this.createMonthPicker();
9938 var size = this.el.getSize();
9939 this.monthPicker.setSize(size);
9940 this.monthPicker.child('table').setSize(size);
9942 this.mpSelMonth = (this.activeDate || this.value).getMonth();
9943 this.updateMPMonth(this.mpSelMonth);
9944 this.mpSelYear = (this.activeDate || this.value).getFullYear();
9945 this.updateMPYear(this.mpSelYear);
9947 this.monthPicker.slideIn('t', {duration:.2});
9950 updateMPYear : function(y){
9952 var ys = this.mpYears.elements;
9953 for(var i = 1; i <= 10; i++){
9954 var td = ys[i-1], y2;
9956 y2 = y + Math.round(i * .5);
9957 td.firstChild.innerHTML = y2;
9960 y2 = y - (5-Math.round(i * .5));
9961 td.firstChild.innerHTML = y2;
9964 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9968 updateMPMonth : function(sm){
9969 this.mpMonths.each(function(m, a, i){
9970 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9974 selectMPMonth: function(m){
9978 onMonthClick : function(e, t){
9980 var el = new Roo.Element(t), pn;
9981 if(el.is('button.x-date-mp-cancel')){
9982 this.hideMonthPicker();
9984 else if(el.is('button.x-date-mp-ok')){
9985 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9986 this.hideMonthPicker();
9988 else if(pn = el.up('td.x-date-mp-month', 2)){
9989 this.mpMonths.removeClass('x-date-mp-sel');
9990 pn.addClass('x-date-mp-sel');
9991 this.mpSelMonth = pn.dom.xmonth;
9993 else if(pn = el.up('td.x-date-mp-year', 2)){
9994 this.mpYears.removeClass('x-date-mp-sel');
9995 pn.addClass('x-date-mp-sel');
9996 this.mpSelYear = pn.dom.xyear;
9998 else if(el.is('a.x-date-mp-prev')){
9999 this.updateMPYear(this.mpyear-10);
10001 else if(el.is('a.x-date-mp-next')){
10002 this.updateMPYear(this.mpyear+10);
10006 onMonthDblClick : function(e, t){
10008 var el = new Roo.Element(t), pn;
10009 if(pn = el.up('td.x-date-mp-month', 2)){
10010 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10011 this.hideMonthPicker();
10013 else if(pn = el.up('td.x-date-mp-year', 2)){
10014 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10015 this.hideMonthPicker();
10019 hideMonthPicker : function(disableAnim){
10020 if(this.monthPicker){
10021 if(disableAnim === true){
10022 this.monthPicker.hide();
10024 this.monthPicker.slideOut('t', {duration:.2});
10030 showPrevMonth : function(e){
10031 this.update(this.activeDate.add("mo", -1));
10035 showNextMonth : function(e){
10036 this.update(this.activeDate.add("mo", 1));
10040 showPrevYear : function(){
10041 this.update(this.activeDate.add("y", -1));
10045 showNextYear : function(){
10046 this.update(this.activeDate.add("y", 1));
10050 handleMouseWheel : function(e){
10051 var delta = e.getWheelDelta();
10053 this.showPrevMonth();
10055 } else if(delta < 0){
10056 this.showNextMonth();
10062 handleDateClick : function(e, t){
10064 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10065 this.setValue(new Date(t.dateValue));
10066 this.fireEvent("select", this, this.value);
10071 selectToday : function(){
10072 this.setValue(new Date().clearTime());
10073 this.fireEvent("select", this, this.value);
10077 update : function(date)
10079 var vd = this.activeDate;
10080 this.activeDate = date;
10082 var t = date.getTime();
10083 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10084 this.cells.removeClass("x-date-selected");
10085 this.cells.each(function(c){
10086 if(c.dom.firstChild.dateValue == t){
10087 c.addClass("x-date-selected");
10088 setTimeout(function(){
10089 try{c.dom.firstChild.focus();}catch(e){}
10098 var days = date.getDaysInMonth();
10099 var firstOfMonth = date.getFirstDateOfMonth();
10100 var startingPos = firstOfMonth.getDay()-this.startDay;
10102 if(startingPos <= this.startDay){
10106 var pm = date.add("mo", -1);
10107 var prevStart = pm.getDaysInMonth()-startingPos;
10109 var cells = this.cells.elements;
10110 var textEls = this.textNodes;
10111 days += startingPos;
10113 // convert everything to numbers so it's fast
10114 var day = 86400000;
10115 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10116 var today = new Date().clearTime().getTime();
10117 var sel = date.clearTime().getTime();
10118 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10119 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10120 var ddMatch = this.disabledDatesRE;
10121 var ddText = this.disabledDatesText;
10122 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10123 var ddaysText = this.disabledDaysText;
10124 var format = this.format;
10126 var setCellClass = function(cal, cell){
10128 var t = d.getTime();
10129 cell.firstChild.dateValue = t;
10131 cell.className += " x-date-today";
10132 cell.title = cal.todayText;
10135 cell.className += " x-date-selected";
10136 setTimeout(function(){
10137 try{cell.firstChild.focus();}catch(e){}
10142 cell.className = " x-date-disabled";
10143 cell.title = cal.minText;
10147 cell.className = " x-date-disabled";
10148 cell.title = cal.maxText;
10152 if(ddays.indexOf(d.getDay()) != -1){
10153 cell.title = ddaysText;
10154 cell.className = " x-date-disabled";
10157 if(ddMatch && format){
10158 var fvalue = d.dateFormat(format);
10159 if(ddMatch.test(fvalue)){
10160 cell.title = ddText.replace("%0", fvalue);
10161 cell.className = " x-date-disabled";
10167 for(; i < startingPos; i++) {
10168 textEls[i].innerHTML = (++prevStart);
10169 d.setDate(d.getDate()+1);
10170 cells[i].className = "x-date-prevday";
10171 setCellClass(this, cells[i]);
10173 for(; i < days; i++){
10174 intDay = i - startingPos + 1;
10175 textEls[i].innerHTML = (intDay);
10176 d.setDate(d.getDate()+1);
10177 cells[i].className = "x-date-active";
10178 setCellClass(this, cells[i]);
10181 for(; i < 42; i++) {
10182 textEls[i].innerHTML = (++extraDays);
10183 d.setDate(d.getDate()+1);
10184 cells[i].className = "x-date-nextday";
10185 setCellClass(this, cells[i]);
10188 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10189 this.fireEvent('monthchange', this, date);
10191 if(!this.internalRender){
10192 var main = this.el.dom.firstChild;
10193 var w = main.offsetWidth;
10194 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10195 Roo.fly(main).setWidth(w);
10196 this.internalRender = true;
10197 // opera does not respect the auto grow header center column
10198 // then, after it gets a width opera refuses to recalculate
10199 // without a second pass
10200 if(Roo.isOpera && !this.secondPass){
10201 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10202 this.secondPass = true;
10203 this.update.defer(10, this, [date]);
10211 * Ext JS Library 1.1.1
10212 * Copyright(c) 2006-2007, Ext JS, LLC.
10214 * Originally Released Under LGPL - original licence link has changed is not relivant.
10217 * <script type="text/javascript">
10220 * @class Roo.TabPanel
10221 * @extends Roo.util.Observable
10222 * A lightweight tab container.
10226 // basic tabs 1, built from existing content
10227 var tabs = new Roo.TabPanel("tabs1");
10228 tabs.addTab("script", "View Script");
10229 tabs.addTab("markup", "View Markup");
10230 tabs.activate("script");
10232 // more advanced tabs, built from javascript
10233 var jtabs = new Roo.TabPanel("jtabs");
10234 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10236 // set up the UpdateManager
10237 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10238 var updater = tab2.getUpdateManager();
10239 updater.setDefaultUrl("ajax1.htm");
10240 tab2.on('activate', updater.refresh, updater, true);
10242 // Use setUrl for Ajax loading
10243 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10244 tab3.setUrl("ajax2.htm", null, true);
10247 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10250 jtabs.activate("jtabs-1");
10253 * Create a new TabPanel.
10254 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10255 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10257 Roo.TabPanel = function(container, config){
10259 * The container element for this TabPanel.
10260 * @type Roo.Element
10262 this.el = Roo.get(container, true);
10264 if(typeof config == "boolean"){
10265 this.tabPosition = config ? "bottom" : "top";
10267 Roo.apply(this, config);
10270 if(this.tabPosition == "bottom"){
10271 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10272 this.el.addClass("x-tabs-bottom");
10274 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10275 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10276 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10278 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10280 if(this.tabPosition != "bottom"){
10281 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10282 * @type Roo.Element
10284 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10285 this.el.addClass("x-tabs-top");
10289 this.bodyEl.setStyle("position", "relative");
10291 this.active = null;
10292 this.activateDelegate = this.activate.createDelegate(this);
10297 * Fires when the active tab changes
10298 * @param {Roo.TabPanel} this
10299 * @param {Roo.TabPanelItem} activePanel The new active tab
10303 * @event beforetabchange
10304 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10305 * @param {Roo.TabPanel} this
10306 * @param {Object} e Set cancel to true on this object to cancel the tab change
10307 * @param {Roo.TabPanelItem} tab The tab being changed to
10309 "beforetabchange" : true
10312 Roo.EventManager.onWindowResize(this.onResize, this);
10313 this.cpad = this.el.getPadding("lr");
10314 this.hiddenCount = 0;
10317 // toolbar on the tabbar support...
10318 if (this.toolbar) {
10319 var tcfg = this.toolbar;
10320 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
10321 this.toolbar = new Roo.Toolbar(tcfg);
10322 if (Roo.isSafari) {
10323 var tbl = tcfg.container.child('table', true);
10324 tbl.setAttribute('width', '100%');
10331 Roo.TabPanel.superclass.constructor.call(this);
10334 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10336 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10338 tabPosition : "top",
10340 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10342 currentTabWidth : 0,
10344 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10348 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10352 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10354 preferredTabWidth : 175,
10356 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10358 resizeTabs : false,
10360 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10362 monitorResize : true,
10364 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
10369 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10370 * @param {String} id The id of the div to use <b>or create</b>
10371 * @param {String} text The text for the tab
10372 * @param {String} content (optional) Content to put in the TabPanelItem body
10373 * @param {Boolean} closable (optional) True to create a close icon on the tab
10374 * @return {Roo.TabPanelItem} The created TabPanelItem
10376 addTab : function(id, text, content, closable){
10377 var item = new Roo.TabPanelItem(this, id, text, closable);
10378 this.addTabItem(item);
10380 item.setContent(content);
10386 * Returns the {@link Roo.TabPanelItem} with the specified id/index
10387 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10388 * @return {Roo.TabPanelItem}
10390 getTab : function(id){
10391 return this.items[id];
10395 * Hides the {@link Roo.TabPanelItem} with the specified id/index
10396 * @param {String/Number} id The id or index of the TabPanelItem to hide.
10398 hideTab : function(id){
10399 var t = this.items[id];
10402 this.hiddenCount++;
10403 this.autoSizeTabs();
10408 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10409 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10411 unhideTab : function(id){
10412 var t = this.items[id];
10414 t.setHidden(false);
10415 this.hiddenCount--;
10416 this.autoSizeTabs();
10421 * Adds an existing {@link Roo.TabPanelItem}.
10422 * @param {Roo.TabPanelItem} item The TabPanelItem to add
10424 addTabItem : function(item){
10425 this.items[item.id] = item;
10426 this.items.push(item);
10427 if(this.resizeTabs){
10428 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10429 this.autoSizeTabs();
10436 * Removes a {@link Roo.TabPanelItem}.
10437 * @param {String/Number} id The id or index of the TabPanelItem to remove.
10439 removeTab : function(id){
10440 var items = this.items;
10441 var tab = items[id];
10442 if(!tab) { return; }
10443 var index = items.indexOf(tab);
10444 if(this.active == tab && items.length > 1){
10445 var newTab = this.getNextAvailable(index);
10450 this.stripEl.dom.removeChild(tab.pnode.dom);
10451 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10452 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10454 items.splice(index, 1);
10455 delete this.items[tab.id];
10456 tab.fireEvent("close", tab);
10457 tab.purgeListeners();
10458 this.autoSizeTabs();
10461 getNextAvailable : function(start){
10462 var items = this.items;
10464 // look for a next tab that will slide over to
10465 // replace the one being removed
10466 while(index < items.length){
10467 var item = items[++index];
10468 if(item && !item.isHidden()){
10472 // if one isn't found select the previous tab (on the left)
10475 var item = items[--index];
10476 if(item && !item.isHidden()){
10484 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10485 * @param {String/Number} id The id or index of the TabPanelItem to disable.
10487 disableTab : function(id){
10488 var tab = this.items[id];
10489 if(tab && this.active != tab){
10495 * Enables a {@link Roo.TabPanelItem} that is disabled.
10496 * @param {String/Number} id The id or index of the TabPanelItem to enable.
10498 enableTab : function(id){
10499 var tab = this.items[id];
10504 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10505 * @param {String/Number} id The id or index of the TabPanelItem to activate.
10506 * @return {Roo.TabPanelItem} The TabPanelItem.
10508 activate : function(id){
10509 var tab = this.items[id];
10513 if(tab == this.active || tab.disabled){
10517 this.fireEvent("beforetabchange", this, e, tab);
10518 if(e.cancel !== true && !tab.disabled){
10520 this.active.hide();
10522 this.active = this.items[id];
10523 this.active.show();
10524 this.fireEvent("tabchange", this, this.active);
10530 * Gets the active {@link Roo.TabPanelItem}.
10531 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10533 getActiveTab : function(){
10534 return this.active;
10538 * Updates the tab body element to fit the height of the container element
10539 * for overflow scrolling
10540 * @param {Number} targetHeight (optional) Override the starting height from the elements height
10542 syncHeight : function(targetHeight){
10543 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10544 var bm = this.bodyEl.getMargins();
10545 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10546 this.bodyEl.setHeight(newHeight);
10550 onResize : function(){
10551 if(this.monitorResize){
10552 this.autoSizeTabs();
10557 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10559 beginUpdate : function(){
10560 this.updating = true;
10564 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10566 endUpdate : function(){
10567 this.updating = false;
10568 this.autoSizeTabs();
10572 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10574 autoSizeTabs : function(){
10575 var count = this.items.length;
10576 var vcount = count - this.hiddenCount;
10577 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
10580 var w = Math.max(this.el.getWidth() - this.cpad, 10);
10581 var availWidth = Math.floor(w / vcount);
10582 var b = this.stripBody;
10583 if(b.getWidth() > w){
10584 var tabs = this.items;
10585 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10586 if(availWidth < this.minTabWidth){
10587 /*if(!this.sleft){ // incomplete scrolling code
10588 this.createScrollButtons();
10591 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10594 if(this.currentTabWidth < this.preferredTabWidth){
10595 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10601 * Returns the number of tabs in this TabPanel.
10604 getCount : function(){
10605 return this.items.length;
10609 * Resizes all the tabs to the passed width
10610 * @param {Number} The new width
10612 setTabWidth : function(width){
10613 this.currentTabWidth = width;
10614 for(var i = 0, len = this.items.length; i < len; i++) {
10615 if(!this.items[i].isHidden()) {
10616 this.items[i].setWidth(width);
10622 * Destroys this TabPanel
10623 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10625 destroy : function(removeEl){
10626 Roo.EventManager.removeResizeListener(this.onResize, this);
10627 for(var i = 0, len = this.items.length; i < len; i++){
10628 this.items[i].purgeListeners();
10630 if(removeEl === true){
10631 this.el.update("");
10638 * @class Roo.TabPanelItem
10639 * @extends Roo.util.Observable
10640 * Represents an individual item (tab plus body) in a TabPanel.
10641 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10642 * @param {String} id The id of this TabPanelItem
10643 * @param {String} text The text for the tab of this TabPanelItem
10644 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10646 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10648 * The {@link Roo.TabPanel} this TabPanelItem belongs to
10649 * @type Roo.TabPanel
10651 this.tabPanel = tabPanel;
10653 * The id for this TabPanelItem
10658 this.disabled = false;
10662 this.loaded = false;
10663 this.closable = closable;
10666 * The body element for this TabPanelItem.
10667 * @type Roo.Element
10669 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10670 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10671 this.bodyEl.setStyle("display", "block");
10672 this.bodyEl.setStyle("zoom", "1");
10675 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10677 this.el = Roo.get(els.el, true);
10678 this.inner = Roo.get(els.inner, true);
10679 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10680 this.pnode = Roo.get(els.el.parentNode, true);
10681 this.el.on("mousedown", this.onTabMouseDown, this);
10682 this.el.on("click", this.onTabClick, this);
10685 var c = Roo.get(els.close, true);
10686 c.dom.title = this.closeText;
10687 c.addClassOnOver("close-over");
10688 c.on("click", this.closeClick, this);
10694 * Fires when this tab becomes the active tab.
10695 * @param {Roo.TabPanel} tabPanel The parent TabPanel
10696 * @param {Roo.TabPanelItem} this
10700 * @event beforeclose
10701 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10702 * @param {Roo.TabPanelItem} this
10703 * @param {Object} e Set cancel to true on this object to cancel the close.
10705 "beforeclose": true,
10708 * Fires when this tab is closed.
10709 * @param {Roo.TabPanelItem} this
10713 * @event deactivate
10714 * Fires when this tab is no longer the active tab.
10715 * @param {Roo.TabPanel} tabPanel The parent TabPanel
10716 * @param {Roo.TabPanelItem} this
10718 "deactivate" : true
10720 this.hidden = false;
10722 Roo.TabPanelItem.superclass.constructor.call(this);
10725 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10726 purgeListeners : function(){
10727 Roo.util.Observable.prototype.purgeListeners.call(this);
10728 this.el.removeAllListeners();
10731 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10734 this.pnode.addClass("on");
10737 this.tabPanel.stripWrap.repaint();
10739 this.fireEvent("activate", this.tabPanel, this);
10743 * Returns true if this tab is the active tab.
10744 * @return {Boolean}
10746 isActive : function(){
10747 return this.tabPanel.getActiveTab() == this;
10751 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10754 this.pnode.removeClass("on");
10756 this.fireEvent("deactivate", this.tabPanel, this);
10759 hideAction : function(){
10760 this.bodyEl.hide();
10761 this.bodyEl.setStyle("position", "absolute");
10762 this.bodyEl.setLeft("-20000px");
10763 this.bodyEl.setTop("-20000px");
10766 showAction : function(){
10767 this.bodyEl.setStyle("position", "relative");
10768 this.bodyEl.setTop("");
10769 this.bodyEl.setLeft("");
10770 this.bodyEl.show();
10774 * Set the tooltip for the tab.
10775 * @param {String} tooltip The tab's tooltip
10777 setTooltip : function(text){
10778 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10779 this.textEl.dom.qtip = text;
10780 this.textEl.dom.removeAttribute('title');
10782 this.textEl.dom.title = text;
10786 onTabClick : function(e){
10787 e.preventDefault();
10788 this.tabPanel.activate(this.id);
10791 onTabMouseDown : function(e){
10792 e.preventDefault();
10793 this.tabPanel.activate(this.id);
10796 getWidth : function(){
10797 return this.inner.getWidth();
10800 setWidth : function(width){
10801 var iwidth = width - this.pnode.getPadding("lr");
10802 this.inner.setWidth(iwidth);
10803 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10804 this.pnode.setWidth(width);
10808 * Show or hide the tab
10809 * @param {Boolean} hidden True to hide or false to show.
10811 setHidden : function(hidden){
10812 this.hidden = hidden;
10813 this.pnode.setStyle("display", hidden ? "none" : "");
10817 * Returns true if this tab is "hidden"
10818 * @return {Boolean}
10820 isHidden : function(){
10821 return this.hidden;
10825 * Returns the text for this tab
10828 getText : function(){
10832 autoSize : function(){
10833 //this.el.beginMeasure();
10834 this.textEl.setWidth(1);
10836 * #2804 [new] Tabs in Roojs
10837 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10839 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10840 //this.el.endMeasure();
10844 * Sets the text for the tab (Note: this also sets the tooltip text)
10845 * @param {String} text The tab's text and tooltip
10847 setText : function(text){
10849 this.textEl.update(text);
10850 this.setTooltip(text);
10851 if(!this.tabPanel.resizeTabs){
10856 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10858 activate : function(){
10859 this.tabPanel.activate(this.id);
10863 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10865 disable : function(){
10866 if(this.tabPanel.active != this){
10867 this.disabled = true;
10868 this.pnode.addClass("disabled");
10873 * Enables this TabPanelItem if it was previously disabled.
10875 enable : function(){
10876 this.disabled = false;
10877 this.pnode.removeClass("disabled");
10881 * Sets the content for this TabPanelItem.
10882 * @param {String} content The content
10883 * @param {Boolean} loadScripts true to look for and load scripts
10885 setContent : function(content, loadScripts){
10886 this.bodyEl.update(content, loadScripts);
10890 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10891 * @return {Roo.UpdateManager} The UpdateManager
10893 getUpdateManager : function(){
10894 return this.bodyEl.getUpdateManager();
10898 * Set a URL to be used to load the content for this TabPanelItem.
10899 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10900 * @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)
10901 * @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)
10902 * @return {Roo.UpdateManager} The UpdateManager
10904 setUrl : function(url, params, loadOnce){
10905 if(this.refreshDelegate){
10906 this.un('activate', this.refreshDelegate);
10908 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10909 this.on("activate", this.refreshDelegate);
10910 return this.bodyEl.getUpdateManager();
10914 _handleRefresh : function(url, params, loadOnce){
10915 if(!loadOnce || !this.loaded){
10916 var updater = this.bodyEl.getUpdateManager();
10917 updater.update(url, params, this._setLoaded.createDelegate(this));
10922 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
10923 * Will fail silently if the setUrl method has not been called.
10924 * This does not activate the panel, just updates its content.
10926 refresh : function(){
10927 if(this.refreshDelegate){
10928 this.loaded = false;
10929 this.refreshDelegate();
10934 _setLoaded : function(){
10935 this.loaded = true;
10939 closeClick : function(e){
10942 this.fireEvent("beforeclose", this, o);
10943 if(o.cancel !== true){
10944 this.tabPanel.removeTab(this.id);
10948 * The text displayed in the tooltip for the close icon.
10951 closeText : "Close this tab"
10955 Roo.TabPanel.prototype.createStrip = function(container){
10956 var strip = document.createElement("div");
10957 strip.className = "x-tabs-wrap";
10958 container.appendChild(strip);
10962 Roo.TabPanel.prototype.createStripList = function(strip){
10963 // div wrapper for retard IE
10964 // returns the "tr" element.
10965 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10966 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10967 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10968 return strip.firstChild.firstChild.firstChild.firstChild;
10971 Roo.TabPanel.prototype.createBody = function(container){
10972 var body = document.createElement("div");
10973 Roo.id(body, "tab-body");
10974 Roo.fly(body).addClass("x-tabs-body");
10975 container.appendChild(body);
10979 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10980 var body = Roo.getDom(id);
10982 body = document.createElement("div");
10985 Roo.fly(body).addClass("x-tabs-item-body");
10986 bodyEl.insertBefore(body, bodyEl.firstChild);
10990 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10991 var td = document.createElement("td");
10992 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10993 //stripEl.appendChild(td);
10995 td.className = "x-tabs-closable";
10996 if(!this.closeTpl){
10997 this.closeTpl = new Roo.Template(
10998 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10999 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11000 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
11003 var el = this.closeTpl.overwrite(td, {"text": text});
11004 var close = el.getElementsByTagName("div")[0];
11005 var inner = el.getElementsByTagName("em")[0];
11006 return {"el": el, "close": close, "inner": inner};
11009 this.tabTpl = new Roo.Template(
11010 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11011 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11014 var el = this.tabTpl.overwrite(td, {"text": text});
11015 var inner = el.getElementsByTagName("em")[0];
11016 return {"el": el, "inner": inner};
11020 * Ext JS Library 1.1.1
11021 * Copyright(c) 2006-2007, Ext JS, LLC.
11023 * Originally Released Under LGPL - original licence link has changed is not relivant.
11026 * <script type="text/javascript">
11030 * @class Roo.Button
11031 * @extends Roo.util.Observable
11032 * Simple Button class
11033 * @cfg {String} text The button text
11034 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11035 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11036 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11037 * @cfg {Object} scope The scope of the handler
11038 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11039 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11040 * @cfg {Boolean} hidden True to start hidden (defaults to false)
11041 * @cfg {Boolean} disabled True to start disabled (defaults to false)
11042 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11043 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11044 applies if enableToggle = true)
11045 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11046 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11047 an {@link Roo.util.ClickRepeater} config object (defaults to false).
11049 * Create a new button
11050 * @param {Object} config The config object
11052 Roo.Button = function(renderTo, config)
11056 renderTo = config.renderTo || false;
11059 Roo.apply(this, config);
11063 * Fires when this button is clicked
11064 * @param {Button} this
11065 * @param {EventObject} e The click event
11070 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11071 * @param {Button} this
11072 * @param {Boolean} pressed
11077 * Fires when the mouse hovers over the button
11078 * @param {Button} this
11079 * @param {Event} e The event object
11081 'mouseover' : true,
11084 * Fires when the mouse exits the button
11085 * @param {Button} this
11086 * @param {Event} e The event object
11091 * Fires when the button is rendered
11092 * @param {Button} this
11097 this.menu = Roo.menu.MenuMgr.get(this.menu);
11099 // register listeners first!! - so render can be captured..
11100 Roo.util.Observable.call(this);
11102 this.render(renderTo);
11108 Roo.extend(Roo.Button, Roo.util.Observable, {
11114 * Read-only. True if this button is hidden
11119 * Read-only. True if this button is disabled
11124 * Read-only. True if this button is pressed (only if enableToggle = true)
11130 * @cfg {Number} tabIndex
11131 * The DOM tabIndex for this button (defaults to undefined)
11133 tabIndex : undefined,
11136 * @cfg {Boolean} enableToggle
11137 * True to enable pressed/not pressed toggling (defaults to false)
11139 enableToggle: false,
11141 * @cfg {Mixed} menu
11142 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11146 * @cfg {String} menuAlign
11147 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11149 menuAlign : "tl-bl?",
11152 * @cfg {String} iconCls
11153 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11155 iconCls : undefined,
11157 * @cfg {String} type
11158 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
11163 menuClassTarget: 'tr',
11166 * @cfg {String} clickEvent
11167 * The type of event to map to the button's event handler (defaults to 'click')
11169 clickEvent : 'click',
11172 * @cfg {Boolean} handleMouseEvents
11173 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11175 handleMouseEvents : true,
11178 * @cfg {String} tooltipType
11179 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11181 tooltipType : 'qtip',
11184 * @cfg {String} cls
11185 * A CSS class to apply to the button's main element.
11189 * @cfg {Roo.Template} template (Optional)
11190 * An {@link Roo.Template} with which to create the Button's main element. This Template must
11191 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11192 * require code modifications if required elements (e.g. a button) aren't present.
11196 render : function(renderTo){
11198 if(this.hideParent){
11199 this.parentEl = Roo.get(renderTo);
11201 if(!this.dhconfig){
11202 if(!this.template){
11203 if(!Roo.Button.buttonTemplate){
11204 // hideous table template
11205 Roo.Button.buttonTemplate = new Roo.Template(
11206 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11207 '<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>',
11208 "</tr></tbody></table>");
11210 this.template = Roo.Button.buttonTemplate;
11212 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
11213 var btnEl = btn.child("button:first");
11214 btnEl.on('focus', this.onFocus, this);
11215 btnEl.on('blur', this.onBlur, this);
11217 btn.addClass(this.cls);
11220 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11223 btnEl.addClass(this.iconCls);
11225 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11228 if(this.tabIndex !== undefined){
11229 btnEl.dom.tabIndex = this.tabIndex;
11232 if(typeof this.tooltip == 'object'){
11233 Roo.QuickTips.tips(Roo.apply({
11237 btnEl.dom[this.tooltipType] = this.tooltip;
11241 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11245 this.el.dom.id = this.el.id = this.id;
11248 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11249 this.menu.on("show", this.onMenuShow, this);
11250 this.menu.on("hide", this.onMenuHide, this);
11252 btn.addClass("x-btn");
11253 if(Roo.isIE && !Roo.isIE7){
11254 this.autoWidth.defer(1, this);
11258 if(this.handleMouseEvents){
11259 btn.on("mouseover", this.onMouseOver, this);
11260 btn.on("mouseout", this.onMouseOut, this);
11261 btn.on("mousedown", this.onMouseDown, this);
11263 btn.on(this.clickEvent, this.onClick, this);
11264 //btn.on("mouseup", this.onMouseUp, this);
11271 Roo.ButtonToggleMgr.register(this);
11273 this.el.addClass("x-btn-pressed");
11276 var repeater = new Roo.util.ClickRepeater(btn,
11277 typeof this.repeat == "object" ? this.repeat : {}
11279 repeater.on("click", this.onClick, this);
11282 this.fireEvent('render', this);
11286 * Returns the button's underlying element
11287 * @return {Roo.Element} The element
11289 getEl : function(){
11294 * Destroys this Button and removes any listeners.
11296 destroy : function(){
11297 Roo.ButtonToggleMgr.unregister(this);
11298 this.el.removeAllListeners();
11299 this.purgeListeners();
11304 autoWidth : function(){
11306 this.el.setWidth("auto");
11307 if(Roo.isIE7 && Roo.isStrict){
11308 var ib = this.el.child('button');
11309 if(ib && ib.getWidth() > 20){
11311 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11316 this.el.beginMeasure();
11318 if(this.el.getWidth() < this.minWidth){
11319 this.el.setWidth(this.minWidth);
11322 this.el.endMeasure();
11329 * Assigns this button's click handler
11330 * @param {Function} handler The function to call when the button is clicked
11331 * @param {Object} scope (optional) Scope for the function passed in
11333 setHandler : function(handler, scope){
11334 this.handler = handler;
11335 this.scope = scope;
11339 * Sets this button's text
11340 * @param {String} text The button text
11342 setText : function(text){
11345 this.el.child("td.x-btn-center button.x-btn-text").update(text);
11351 * Gets the text for this button
11352 * @return {String} The button text
11354 getText : function(){
11362 this.hidden = false;
11364 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11372 this.hidden = true;
11374 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11379 * Convenience function for boolean show/hide
11380 * @param {Boolean} visible True to show, false to hide
11382 setVisible: function(visible){
11391 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11392 * @param {Boolean} state (optional) Force a particular state
11394 toggle : function(state){
11395 state = state === undefined ? !this.pressed : state;
11396 if(state != this.pressed){
11398 this.el.addClass("x-btn-pressed");
11399 this.pressed = true;
11400 this.fireEvent("toggle", this, true);
11402 this.el.removeClass("x-btn-pressed");
11403 this.pressed = false;
11404 this.fireEvent("toggle", this, false);
11406 if(this.toggleHandler){
11407 this.toggleHandler.call(this.scope || this, this, state);
11415 focus : function(){
11416 this.el.child('button:first').focus();
11420 * Disable this button
11422 disable : function(){
11424 this.el.addClass("x-btn-disabled");
11426 this.disabled = true;
11430 * Enable this button
11432 enable : function(){
11434 this.el.removeClass("x-btn-disabled");
11436 this.disabled = false;
11440 * Convenience function for boolean enable/disable
11441 * @param {Boolean} enabled True to enable, false to disable
11443 setDisabled : function(v){
11444 this[v !== true ? "enable" : "disable"]();
11448 onClick : function(e)
11451 e.preventDefault();
11456 if(!this.disabled){
11457 if(this.enableToggle){
11460 if(this.menu && !this.menu.isVisible()){
11461 this.menu.show(this.el, this.menuAlign);
11463 this.fireEvent("click", this, e);
11465 this.el.removeClass("x-btn-over");
11466 this.handler.call(this.scope || this, this, e);
11471 onMouseOver : function(e){
11472 if(!this.disabled){
11473 this.el.addClass("x-btn-over");
11474 this.fireEvent('mouseover', this, e);
11478 onMouseOut : function(e){
11479 if(!e.within(this.el, true)){
11480 this.el.removeClass("x-btn-over");
11481 this.fireEvent('mouseout', this, e);
11485 onFocus : function(e){
11486 if(!this.disabled){
11487 this.el.addClass("x-btn-focus");
11491 onBlur : function(e){
11492 this.el.removeClass("x-btn-focus");
11495 onMouseDown : function(e){
11496 if(!this.disabled && e.button == 0){
11497 this.el.addClass("x-btn-click");
11498 Roo.get(document).on('mouseup', this.onMouseUp, this);
11502 onMouseUp : function(e){
11504 this.el.removeClass("x-btn-click");
11505 Roo.get(document).un('mouseup', this.onMouseUp, this);
11509 onMenuShow : function(e){
11510 this.el.addClass("x-btn-menu-active");
11513 onMenuHide : function(e){
11514 this.el.removeClass("x-btn-menu-active");
11518 // Private utility class used by Button
11519 Roo.ButtonToggleMgr = function(){
11522 function toggleGroup(btn, state){
11524 var g = groups[btn.toggleGroup];
11525 for(var i = 0, l = g.length; i < l; i++){
11527 g[i].toggle(false);
11534 register : function(btn){
11535 if(!btn.toggleGroup){
11538 var g = groups[btn.toggleGroup];
11540 g = groups[btn.toggleGroup] = [];
11543 btn.on("toggle", toggleGroup);
11546 unregister : function(btn){
11547 if(!btn.toggleGroup){
11550 var g = groups[btn.toggleGroup];
11553 btn.un("toggle", toggleGroup);
11559 * Ext JS Library 1.1.1
11560 * Copyright(c) 2006-2007, Ext JS, LLC.
11562 * Originally Released Under LGPL - original licence link has changed is not relivant.
11565 * <script type="text/javascript">
11569 * @class Roo.SplitButton
11570 * @extends Roo.Button
11571 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11572 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
11573 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11574 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11575 * @cfg {String} arrowTooltip The title attribute of the arrow
11577 * Create a new menu button
11578 * @param {String/HTMLElement/Element} renderTo The element to append the button to
11579 * @param {Object} config The config object
11581 Roo.SplitButton = function(renderTo, config){
11582 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11584 * @event arrowclick
11585 * Fires when this button's arrow is clicked
11586 * @param {SplitButton} this
11587 * @param {EventObject} e The click event
11589 this.addEvents({"arrowclick":true});
11592 Roo.extend(Roo.SplitButton, Roo.Button, {
11593 render : function(renderTo){
11594 // this is one sweet looking template!
11595 var tpl = new Roo.Template(
11596 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11597 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11598 '<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>',
11599 "</tbody></table></td><td>",
11600 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11601 '<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>',
11602 "</tbody></table></td></tr></table>"
11604 var btn = tpl.append(renderTo, [this.text, this.type], true);
11605 var btnEl = btn.child("button");
11607 btn.addClass(this.cls);
11610 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11613 btnEl.addClass(this.iconCls);
11615 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11619 if(this.handleMouseEvents){
11620 btn.on("mouseover", this.onMouseOver, this);
11621 btn.on("mouseout", this.onMouseOut, this);
11622 btn.on("mousedown", this.onMouseDown, this);
11623 btn.on("mouseup", this.onMouseUp, this);
11625 btn.on(this.clickEvent, this.onClick, this);
11627 if(typeof this.tooltip == 'object'){
11628 Roo.QuickTips.tips(Roo.apply({
11632 btnEl.dom[this.tooltipType] = this.tooltip;
11635 if(this.arrowTooltip){
11636 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11645 this.el.addClass("x-btn-pressed");
11647 if(Roo.isIE && !Roo.isIE7){
11648 this.autoWidth.defer(1, this);
11653 this.menu.on("show", this.onMenuShow, this);
11654 this.menu.on("hide", this.onMenuHide, this);
11656 this.fireEvent('render', this);
11660 autoWidth : function(){
11662 var tbl = this.el.child("table:first");
11663 var tbl2 = this.el.child("table:last");
11664 this.el.setWidth("auto");
11665 tbl.setWidth("auto");
11666 if(Roo.isIE7 && Roo.isStrict){
11667 var ib = this.el.child('button:first');
11668 if(ib && ib.getWidth() > 20){
11670 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11675 this.el.beginMeasure();
11677 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11678 tbl.setWidth(this.minWidth-tbl2.getWidth());
11681 this.el.endMeasure();
11684 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11688 * Sets this button's click handler
11689 * @param {Function} handler The function to call when the button is clicked
11690 * @param {Object} scope (optional) Scope for the function passed above
11692 setHandler : function(handler, scope){
11693 this.handler = handler;
11694 this.scope = scope;
11698 * Sets this button's arrow click handler
11699 * @param {Function} handler The function to call when the arrow is clicked
11700 * @param {Object} scope (optional) Scope for the function passed above
11702 setArrowHandler : function(handler, scope){
11703 this.arrowHandler = handler;
11704 this.scope = scope;
11710 focus : function(){
11712 this.el.child("button:first").focus();
11717 onClick : function(e){
11718 e.preventDefault();
11719 if(!this.disabled){
11720 if(e.getTarget(".x-btn-menu-arrow-wrap")){
11721 if(this.menu && !this.menu.isVisible()){
11722 this.menu.show(this.el, this.menuAlign);
11724 this.fireEvent("arrowclick", this, e);
11725 if(this.arrowHandler){
11726 this.arrowHandler.call(this.scope || this, this, e);
11729 this.fireEvent("click", this, e);
11731 this.handler.call(this.scope || this, this, e);
11737 onMouseDown : function(e){
11738 if(!this.disabled){
11739 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11743 onMouseUp : function(e){
11744 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11749 // backwards compat
11750 Roo.MenuButton = Roo.SplitButton;/*
11752 * Ext JS Library 1.1.1
11753 * Copyright(c) 2006-2007, Ext JS, LLC.
11755 * Originally Released Under LGPL - original licence link has changed is not relivant.
11758 * <script type="text/javascript">
11762 * @class Roo.Toolbar
11763 * Basic Toolbar class.
11765 * Creates a new Toolbar
11766 * @param {Object} container The config object
11768 Roo.Toolbar = function(container, buttons, config)
11770 /// old consturctor format still supported..
11771 if(container instanceof Array){ // omit the container for later rendering
11772 buttons = container;
11776 if (typeof(container) == 'object' && container.xtype) {
11777 config = container;
11778 container = config.container;
11779 buttons = config.buttons || []; // not really - use items!!
11782 if (config && config.items) {
11783 xitems = config.items;
11784 delete config.items;
11786 Roo.apply(this, config);
11787 this.buttons = buttons;
11790 this.render(container);
11792 this.xitems = xitems;
11793 Roo.each(xitems, function(b) {
11799 Roo.Toolbar.prototype = {
11801 * @cfg {Array} items
11802 * array of button configs or elements to add (will be converted to a MixedCollection)
11806 * @cfg {String/HTMLElement/Element} container
11807 * The id or element that will contain the toolbar
11810 render : function(ct){
11811 this.el = Roo.get(ct);
11813 this.el.addClass(this.cls);
11815 // using a table allows for vertical alignment
11816 // 100% width is needed by Safari...
11817 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11818 this.tr = this.el.child("tr", true);
11820 this.items = new Roo.util.MixedCollection(false, function(o){
11821 return o.id || ("item" + (++autoId));
11824 this.add.apply(this, this.buttons);
11825 delete this.buttons;
11830 * Adds element(s) to the toolbar -- this function takes a variable number of
11831 * arguments of mixed type and adds them to the toolbar.
11832 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11834 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11835 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11836 * <li>Field: Any form field (equivalent to {@link #addField})</li>
11837 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11838 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11839 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11840 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11841 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11842 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11844 * @param {Mixed} arg2
11845 * @param {Mixed} etc.
11848 var a = arguments, l = a.length;
11849 for(var i = 0; i < l; i++){
11854 _add : function(el) {
11857 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11860 if (el.applyTo){ // some kind of form field
11861 return this.addField(el);
11863 if (el.render){ // some kind of Toolbar.Item
11864 return this.addItem(el);
11866 if (typeof el == "string"){ // string
11867 if(el == "separator" || el == "-"){
11868 return this.addSeparator();
11871 return this.addSpacer();
11874 return this.addFill();
11876 return this.addText(el);
11879 if(el.tagName){ // element
11880 return this.addElement(el);
11882 if(typeof el == "object"){ // must be button config?
11883 return this.addButton(el);
11885 // and now what?!?!
11891 * Add an Xtype element
11892 * @param {Object} xtype Xtype Object
11893 * @return {Object} created Object
11895 addxtype : function(e){
11896 return this.add(e);
11900 * Returns the Element for this toolbar.
11901 * @return {Roo.Element}
11903 getEl : function(){
11909 * @return {Roo.Toolbar.Item} The separator item
11911 addSeparator : function(){
11912 return this.addItem(new Roo.Toolbar.Separator());
11916 * Adds a spacer element
11917 * @return {Roo.Toolbar.Spacer} The spacer item
11919 addSpacer : function(){
11920 return this.addItem(new Roo.Toolbar.Spacer());
11924 * Adds a fill element that forces subsequent additions to the right side of the toolbar
11925 * @return {Roo.Toolbar.Fill} The fill item
11927 addFill : function(){
11928 return this.addItem(new Roo.Toolbar.Fill());
11932 * Adds any standard HTML element to the toolbar
11933 * @param {String/HTMLElement/Element} el The element or id of the element to add
11934 * @return {Roo.Toolbar.Item} The element's item
11936 addElement : function(el){
11937 return this.addItem(new Roo.Toolbar.Item(el));
11940 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11941 * @type Roo.util.MixedCollection
11946 * Adds any Toolbar.Item or subclass
11947 * @param {Roo.Toolbar.Item} item
11948 * @return {Roo.Toolbar.Item} The item
11950 addItem : function(item){
11951 var td = this.nextBlock();
11953 this.items.add(item);
11958 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11959 * @param {Object/Array} config A button config or array of configs
11960 * @return {Roo.Toolbar.Button/Array}
11962 addButton : function(config){
11963 if(config instanceof Array){
11965 for(var i = 0, len = config.length; i < len; i++) {
11966 buttons.push(this.addButton(config[i]));
11971 if(!(config instanceof Roo.Toolbar.Button)){
11973 new Roo.Toolbar.SplitButton(config) :
11974 new Roo.Toolbar.Button(config);
11976 var td = this.nextBlock();
11983 * Adds text to the toolbar
11984 * @param {String} text The text to add
11985 * @return {Roo.Toolbar.Item} The element's item
11987 addText : function(text){
11988 return this.addItem(new Roo.Toolbar.TextItem(text));
11992 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11993 * @param {Number} index The index where the item is to be inserted
11994 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11995 * @return {Roo.Toolbar.Button/Item}
11997 insertButton : function(index, item){
11998 if(item instanceof Array){
12000 for(var i = 0, len = item.length; i < len; i++) {
12001 buttons.push(this.insertButton(index + i, item[i]));
12005 if (!(item instanceof Roo.Toolbar.Button)){
12006 item = new Roo.Toolbar.Button(item);
12008 var td = document.createElement("td");
12009 this.tr.insertBefore(td, this.tr.childNodes[index]);
12011 this.items.insert(index, item);
12016 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12017 * @param {Object} config
12018 * @return {Roo.Toolbar.Item} The element's item
12020 addDom : function(config, returnEl){
12021 var td = this.nextBlock();
12022 Roo.DomHelper.overwrite(td, config);
12023 var ti = new Roo.Toolbar.Item(td.firstChild);
12025 this.items.add(ti);
12030 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12031 * @type Roo.util.MixedCollection
12036 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12037 * Note: the field should not have been rendered yet. For a field that has already been
12038 * rendered, use {@link #addElement}.
12039 * @param {Roo.form.Field} field
12040 * @return {Roo.ToolbarItem}
12044 addField : function(field) {
12045 if (!this.fields) {
12047 this.fields = new Roo.util.MixedCollection(false, function(o){
12048 return o.id || ("item" + (++autoId));
12053 var td = this.nextBlock();
12055 var ti = new Roo.Toolbar.Item(td.firstChild);
12057 this.items.add(ti);
12058 this.fields.add(field);
12069 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12070 this.el.child('div').hide();
12078 this.el.child('div').show();
12082 nextBlock : function(){
12083 var td = document.createElement("td");
12084 this.tr.appendChild(td);
12089 destroy : function(){
12090 if(this.items){ // rendered?
12091 Roo.destroy.apply(Roo, this.items.items);
12093 if(this.fields){ // rendered?
12094 Roo.destroy.apply(Roo, this.fields.items);
12096 Roo.Element.uncache(this.el, this.tr);
12101 * @class Roo.Toolbar.Item
12102 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12104 * Creates a new Item
12105 * @param {HTMLElement} el
12107 Roo.Toolbar.Item = function(el){
12109 if (typeof (el.xtype) != 'undefined') {
12114 this.el = Roo.getDom(el);
12115 this.id = Roo.id(this.el);
12116 this.hidden = false;
12121 * Fires when the button is rendered
12122 * @param {Button} this
12126 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12128 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12129 //Roo.Toolbar.Item.prototype = {
12132 * Get this item's HTML Element
12133 * @return {HTMLElement}
12135 getEl : function(){
12140 render : function(td){
12143 td.appendChild(this.el);
12145 this.fireEvent('render', this);
12149 * Removes and destroys this item.
12151 destroy : function(){
12152 this.td.parentNode.removeChild(this.td);
12159 this.hidden = false;
12160 this.td.style.display = "";
12167 this.hidden = true;
12168 this.td.style.display = "none";
12172 * Convenience function for boolean show/hide.
12173 * @param {Boolean} visible true to show/false to hide
12175 setVisible: function(visible){
12184 * Try to focus this item.
12186 focus : function(){
12187 Roo.fly(this.el).focus();
12191 * Disables this item.
12193 disable : function(){
12194 Roo.fly(this.td).addClass("x-item-disabled");
12195 this.disabled = true;
12196 this.el.disabled = true;
12200 * Enables this item.
12202 enable : function(){
12203 Roo.fly(this.td).removeClass("x-item-disabled");
12204 this.disabled = false;
12205 this.el.disabled = false;
12211 * @class Roo.Toolbar.Separator
12212 * @extends Roo.Toolbar.Item
12213 * A simple toolbar separator class
12215 * Creates a new Separator
12217 Roo.Toolbar.Separator = function(cfg){
12219 var s = document.createElement("span");
12220 s.className = "ytb-sep";
12225 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12227 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12228 enable:Roo.emptyFn,
12229 disable:Roo.emptyFn,
12234 * @class Roo.Toolbar.Spacer
12235 * @extends Roo.Toolbar.Item
12236 * A simple element that adds extra horizontal space to a toolbar.
12238 * Creates a new Spacer
12240 Roo.Toolbar.Spacer = function(cfg){
12241 var s = document.createElement("div");
12242 s.className = "ytb-spacer";
12246 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12248 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12249 enable:Roo.emptyFn,
12250 disable:Roo.emptyFn,
12255 * @class Roo.Toolbar.Fill
12256 * @extends Roo.Toolbar.Spacer
12257 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12259 * Creates a new Spacer
12261 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12263 render : function(td){
12264 td.style.width = '100%';
12265 Roo.Toolbar.Fill.superclass.render.call(this, td);
12270 * @class Roo.Toolbar.TextItem
12271 * @extends Roo.Toolbar.Item
12272 * A simple class that renders text directly into a toolbar.
12274 * Creates a new TextItem
12275 * @param {String} text
12277 Roo.Toolbar.TextItem = function(cfg){
12278 var text = cfg || "";
12279 if (typeof(cfg) == 'object') {
12280 text = cfg.text || "";
12284 var s = document.createElement("span");
12285 s.className = "ytb-text";
12286 s.innerHTML = text;
12291 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
12293 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12296 enable:Roo.emptyFn,
12297 disable:Roo.emptyFn,
12302 * @class Roo.Toolbar.Button
12303 * @extends Roo.Button
12304 * A button that renders into a toolbar.
12306 * Creates a new Button
12307 * @param {Object} config A standard {@link Roo.Button} config object
12309 Roo.Toolbar.Button = function(config){
12310 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12312 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12313 render : function(td){
12315 Roo.Toolbar.Button.superclass.render.call(this, td);
12319 * Removes and destroys this button
12321 destroy : function(){
12322 Roo.Toolbar.Button.superclass.destroy.call(this);
12323 this.td.parentNode.removeChild(this.td);
12327 * Shows this button
12330 this.hidden = false;
12331 this.td.style.display = "";
12335 * Hides this button
12338 this.hidden = true;
12339 this.td.style.display = "none";
12343 * Disables this item
12345 disable : function(){
12346 Roo.fly(this.td).addClass("x-item-disabled");
12347 this.disabled = true;
12351 * Enables this item
12353 enable : function(){
12354 Roo.fly(this.td).removeClass("x-item-disabled");
12355 this.disabled = false;
12358 // backwards compat
12359 Roo.ToolbarButton = Roo.Toolbar.Button;
12362 * @class Roo.Toolbar.SplitButton
12363 * @extends Roo.SplitButton
12364 * A menu button that renders into a toolbar.
12366 * Creates a new SplitButton
12367 * @param {Object} config A standard {@link Roo.SplitButton} config object
12369 Roo.Toolbar.SplitButton = function(config){
12370 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12372 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12373 render : function(td){
12375 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12379 * Removes and destroys this button
12381 destroy : function(){
12382 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12383 this.td.parentNode.removeChild(this.td);
12387 * Shows this button
12390 this.hidden = false;
12391 this.td.style.display = "";
12395 * Hides this button
12398 this.hidden = true;
12399 this.td.style.display = "none";
12403 // backwards compat
12404 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12406 * Ext JS Library 1.1.1
12407 * Copyright(c) 2006-2007, Ext JS, LLC.
12409 * Originally Released Under LGPL - original licence link has changed is not relivant.
12412 * <script type="text/javascript">
12416 * @class Roo.PagingToolbar
12417 * @extends Roo.Toolbar
12418 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12420 * Create a new PagingToolbar
12421 * @param {Object} config The config object
12423 Roo.PagingToolbar = function(el, ds, config)
12425 // old args format still supported... - xtype is prefered..
12426 if (typeof(el) == 'object' && el.xtype) {
12427 // created from xtype...
12429 ds = el.dataSource;
12430 el = config.container;
12433 if (config.items) {
12434 items = config.items;
12438 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12441 this.renderButtons(this.el);
12444 // supprot items array.
12446 Roo.each(items, function(e) {
12447 this.add(Roo.factory(e));
12452 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12454 * @cfg {Roo.data.Store} dataSource
12455 * The underlying data store providing the paged data
12458 * @cfg {String/HTMLElement/Element} container
12459 * container The id or element that will contain the toolbar
12462 * @cfg {Boolean} displayInfo
12463 * True to display the displayMsg (defaults to false)
12466 * @cfg {Number} pageSize
12467 * The number of records to display per page (defaults to 20)
12471 * @cfg {String} displayMsg
12472 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12474 displayMsg : 'Displaying {0} - {1} of {2}',
12476 * @cfg {String} emptyMsg
12477 * The message to display when no records are found (defaults to "No data to display")
12479 emptyMsg : 'No data to display',
12481 * Customizable piece of the default paging text (defaults to "Page")
12484 beforePageText : "Page",
12486 * Customizable piece of the default paging text (defaults to "of %0")
12489 afterPageText : "of {0}",
12491 * Customizable piece of the default paging text (defaults to "First Page")
12494 firstText : "First Page",
12496 * Customizable piece of the default paging text (defaults to "Previous Page")
12499 prevText : "Previous Page",
12501 * Customizable piece of the default paging text (defaults to "Next Page")
12504 nextText : "Next Page",
12506 * Customizable piece of the default paging text (defaults to "Last Page")
12509 lastText : "Last Page",
12511 * Customizable piece of the default paging text (defaults to "Refresh")
12514 refreshText : "Refresh",
12517 renderButtons : function(el){
12518 Roo.PagingToolbar.superclass.render.call(this, el);
12519 this.first = this.addButton({
12520 tooltip: this.firstText,
12521 cls: "x-btn-icon x-grid-page-first",
12523 handler: this.onClick.createDelegate(this, ["first"])
12525 this.prev = this.addButton({
12526 tooltip: this.prevText,
12527 cls: "x-btn-icon x-grid-page-prev",
12529 handler: this.onClick.createDelegate(this, ["prev"])
12531 //this.addSeparator();
12532 this.add(this.beforePageText);
12533 this.field = Roo.get(this.addDom({
12538 cls: "x-grid-page-number"
12540 this.field.on("keydown", this.onPagingKeydown, this);
12541 this.field.on("focus", function(){this.dom.select();});
12542 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12543 this.field.setHeight(18);
12544 //this.addSeparator();
12545 this.next = this.addButton({
12546 tooltip: this.nextText,
12547 cls: "x-btn-icon x-grid-page-next",
12549 handler: this.onClick.createDelegate(this, ["next"])
12551 this.last = this.addButton({
12552 tooltip: this.lastText,
12553 cls: "x-btn-icon x-grid-page-last",
12555 handler: this.onClick.createDelegate(this, ["last"])
12557 //this.addSeparator();
12558 this.loading = this.addButton({
12559 tooltip: this.refreshText,
12560 cls: "x-btn-icon x-grid-loading",
12561 handler: this.onClick.createDelegate(this, ["refresh"])
12564 if(this.displayInfo){
12565 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12570 updateInfo : function(){
12571 if(this.displayEl){
12572 var count = this.ds.getCount();
12573 var msg = count == 0 ?
12577 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
12579 this.displayEl.update(msg);
12584 onLoad : function(ds, r, o){
12585 this.cursor = o.params ? o.params.start : 0;
12586 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12588 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12589 this.field.dom.value = ap;
12590 this.first.setDisabled(ap == 1);
12591 this.prev.setDisabled(ap == 1);
12592 this.next.setDisabled(ap == ps);
12593 this.last.setDisabled(ap == ps);
12594 this.loading.enable();
12599 getPageData : function(){
12600 var total = this.ds.getTotalCount();
12603 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12604 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12609 onLoadError : function(){
12610 this.loading.enable();
12614 onPagingKeydown : function(e){
12615 var k = e.getKey();
12616 var d = this.getPageData();
12618 var v = this.field.dom.value, pageNum;
12619 if(!v || isNaN(pageNum = parseInt(v, 10))){
12620 this.field.dom.value = d.activePage;
12623 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12624 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12627 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))
12629 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12630 this.field.dom.value = pageNum;
12631 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12634 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12636 var v = this.field.dom.value, pageNum;
12637 var increment = (e.shiftKey) ? 10 : 1;
12638 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
12641 if(!v || isNaN(pageNum = parseInt(v, 10))) {
12642 this.field.dom.value = d.activePage;
12645 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12647 this.field.dom.value = parseInt(v, 10) + increment;
12648 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12649 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12656 beforeLoad : function(){
12658 this.loading.disable();
12663 onClick : function(which){
12667 ds.load({params:{start: 0, limit: this.pageSize}});
12670 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12673 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12676 var total = ds.getTotalCount();
12677 var extra = total % this.pageSize;
12678 var lastStart = extra ? (total - extra) : total-this.pageSize;
12679 ds.load({params:{start: lastStart, limit: this.pageSize}});
12682 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12688 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12689 * @param {Roo.data.Store} store The data store to unbind
12691 unbind : function(ds){
12692 ds.un("beforeload", this.beforeLoad, this);
12693 ds.un("load", this.onLoad, this);
12694 ds.un("loadexception", this.onLoadError, this);
12695 ds.un("remove", this.updateInfo, this);
12696 ds.un("add", this.updateInfo, this);
12697 this.ds = undefined;
12701 * Binds the paging toolbar to the specified {@link Roo.data.Store}
12702 * @param {Roo.data.Store} store The data store to bind
12704 bind : function(ds){
12705 ds.on("beforeload", this.beforeLoad, this);
12706 ds.on("load", this.onLoad, this);
12707 ds.on("loadexception", this.onLoadError, this);
12708 ds.on("remove", this.updateInfo, this);
12709 ds.on("add", this.updateInfo, this);
12714 * Ext JS Library 1.1.1
12715 * Copyright(c) 2006-2007, Ext JS, LLC.
12717 * Originally Released Under LGPL - original licence link has changed is not relivant.
12720 * <script type="text/javascript">
12724 * @class Roo.Resizable
12725 * @extends Roo.util.Observable
12726 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12727 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12728 * 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
12729 * the element will be wrapped for you automatically.</p>
12730 * <p>Here is the list of valid resize handles:</p>
12733 ------ -------------------
12742 'hd' horizontal drag
12745 * <p>Here's an example showing the creation of a typical Resizable:</p>
12747 var resizer = new Roo.Resizable("element-id", {
12755 resizer.on("resize", myHandler);
12757 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12758 * resizer.east.setDisplayed(false);</p>
12759 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12760 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12761 * resize operation's new size (defaults to [0, 0])
12762 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12763 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12764 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12765 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12766 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12767 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12768 * @cfg {Number} width The width of the element in pixels (defaults to null)
12769 * @cfg {Number} height The height of the element in pixels (defaults to null)
12770 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12771 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12772 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12773 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12774 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
12775 * in favor of the handles config option (defaults to false)
12776 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12777 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12778 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12779 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12780 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12781 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12782 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12783 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12784 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12785 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12786 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12788 * Create a new resizable component
12789 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12790 * @param {Object} config configuration options
12792 Roo.Resizable = function(el, config)
12794 this.el = Roo.get(el);
12796 if(config && config.wrap){
12797 config.resizeChild = this.el;
12798 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12799 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12800 this.el.setStyle("overflow", "hidden");
12801 this.el.setPositioning(config.resizeChild.getPositioning());
12802 config.resizeChild.clearPositioning();
12803 if(!config.width || !config.height){
12804 var csize = config.resizeChild.getSize();
12805 this.el.setSize(csize.width, csize.height);
12807 if(config.pinned && !config.adjustments){
12808 config.adjustments = "auto";
12812 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12813 this.proxy.unselectable();
12814 this.proxy.enableDisplayMode('block');
12816 Roo.apply(this, config);
12819 this.disableTrackOver = true;
12820 this.el.addClass("x-resizable-pinned");
12822 // if the element isn't positioned, make it relative
12823 var position = this.el.getStyle("position");
12824 if(position != "absolute" && position != "fixed"){
12825 this.el.setStyle("position", "relative");
12827 if(!this.handles){ // no handles passed, must be legacy style
12828 this.handles = 's,e,se';
12829 if(this.multiDirectional){
12830 this.handles += ',n,w';
12833 if(this.handles == "all"){
12834 this.handles = "n s e w ne nw se sw";
12836 var hs = this.handles.split(/\s*?[,;]\s*?| /);
12837 var ps = Roo.Resizable.positions;
12838 for(var i = 0, len = hs.length; i < len; i++){
12839 if(hs[i] && ps[hs[i]]){
12840 var pos = ps[hs[i]];
12841 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12845 this.corner = this.southeast;
12847 // updateBox = the box can move..
12848 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12849 this.updateBox = true;
12852 this.activeHandle = null;
12854 if(this.resizeChild){
12855 if(typeof this.resizeChild == "boolean"){
12856 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12858 this.resizeChild = Roo.get(this.resizeChild, true);
12862 if(this.adjustments == "auto"){
12863 var rc = this.resizeChild;
12864 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12865 if(rc && (hw || hn)){
12866 rc.position("relative");
12867 rc.setLeft(hw ? hw.el.getWidth() : 0);
12868 rc.setTop(hn ? hn.el.getHeight() : 0);
12870 this.adjustments = [
12871 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12872 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12876 if(this.draggable){
12877 this.dd = this.dynamic ?
12878 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12879 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12885 * @event beforeresize
12886 * Fired before resize is allowed. Set enabled to false to cancel resize.
12887 * @param {Roo.Resizable} this
12888 * @param {Roo.EventObject} e The mousedown event
12890 "beforeresize" : true,
12893 * Fired a resizing.
12894 * @param {Roo.Resizable} this
12895 * @param {Number} x The new x position
12896 * @param {Number} y The new y position
12897 * @param {Number} w The new w width
12898 * @param {Number} h The new h hight
12899 * @param {Roo.EventObject} e The mouseup event
12904 * Fired after a resize.
12905 * @param {Roo.Resizable} this
12906 * @param {Number} width The new width
12907 * @param {Number} height The new height
12908 * @param {Roo.EventObject} e The mouseup event
12913 if(this.width !== null && this.height !== null){
12914 this.resizeTo(this.width, this.height);
12916 this.updateChildSize();
12919 this.el.dom.style.zoom = 1;
12921 Roo.Resizable.superclass.constructor.call(this);
12924 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12925 resizeChild : false,
12926 adjustments : [0, 0],
12936 multiDirectional : false,
12937 disableTrackOver : false,
12938 easing : 'easeOutStrong',
12939 widthIncrement : 0,
12940 heightIncrement : 0,
12944 preserveRatio : false,
12945 transparent: false,
12951 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12953 constrainTo: undefined,
12955 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12957 resizeRegion: undefined,
12961 * Perform a manual resize
12962 * @param {Number} width
12963 * @param {Number} height
12965 resizeTo : function(width, height){
12966 this.el.setSize(width, height);
12967 this.updateChildSize();
12968 this.fireEvent("resize", this, width, height, null);
12972 startSizing : function(e, handle){
12973 this.fireEvent("beforeresize", this, e);
12974 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12977 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
12978 this.overlay.unselectable();
12979 this.overlay.enableDisplayMode("block");
12980 this.overlay.on("mousemove", this.onMouseMove, this);
12981 this.overlay.on("mouseup", this.onMouseUp, this);
12983 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12985 this.resizing = true;
12986 this.startBox = this.el.getBox();
12987 this.startPoint = e.getXY();
12988 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12989 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12991 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12992 this.overlay.show();
12994 if(this.constrainTo) {
12995 var ct = Roo.get(this.constrainTo);
12996 this.resizeRegion = ct.getRegion().adjust(
12997 ct.getFrameWidth('t'),
12998 ct.getFrameWidth('l'),
12999 -ct.getFrameWidth('b'),
13000 -ct.getFrameWidth('r')
13004 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13006 this.proxy.setBox(this.startBox);
13008 this.proxy.setStyle('visibility', 'visible');
13014 onMouseDown : function(handle, e){
13017 this.activeHandle = handle;
13018 this.startSizing(e, handle);
13023 onMouseUp : function(e){
13024 var size = this.resizeElement();
13025 this.resizing = false;
13027 this.overlay.hide();
13029 this.fireEvent("resize", this, size.width, size.height, e);
13033 updateChildSize : function(){
13035 if(this.resizeChild){
13037 var child = this.resizeChild;
13038 var adj = this.adjustments;
13039 if(el.dom.offsetWidth){
13040 var b = el.getSize(true);
13041 child.setSize(b.width+adj[0], b.height+adj[1]);
13043 // Second call here for IE
13044 // The first call enables instant resizing and
13045 // the second call corrects scroll bars if they
13048 setTimeout(function(){
13049 if(el.dom.offsetWidth){
13050 var b = el.getSize(true);
13051 child.setSize(b.width+adj[0], b.height+adj[1]);
13059 snap : function(value, inc, min){
13060 if(!inc || !value) {
13063 var newValue = value;
13064 var m = value % inc;
13067 newValue = value + (inc-m);
13069 newValue = value - m;
13072 return Math.max(min, newValue);
13076 resizeElement : function(){
13077 var box = this.proxy.getBox();
13078 if(this.updateBox){
13079 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13081 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13083 this.updateChildSize();
13091 constrain : function(v, diff, m, mx){
13094 }else if(v - diff > mx){
13101 onMouseMove : function(e){
13104 try{// try catch so if something goes wrong the user doesn't get hung
13106 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13110 //var curXY = this.startPoint;
13111 var curSize = this.curSize || this.startBox;
13112 var x = this.startBox.x, y = this.startBox.y;
13113 var ox = x, oy = y;
13114 var w = curSize.width, h = curSize.height;
13115 var ow = w, oh = h;
13116 var mw = this.minWidth, mh = this.minHeight;
13117 var mxw = this.maxWidth, mxh = this.maxHeight;
13118 var wi = this.widthIncrement;
13119 var hi = this.heightIncrement;
13121 var eventXY = e.getXY();
13122 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13123 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13125 var pos = this.activeHandle.position;
13130 w = Math.min(Math.max(mw, w), mxw);
13135 h = Math.min(Math.max(mh, h), mxh);
13140 w = Math.min(Math.max(mw, w), mxw);
13141 h = Math.min(Math.max(mh, h), mxh);
13144 diffY = this.constrain(h, diffY, mh, mxh);
13151 var adiffX = Math.abs(diffX);
13152 var sub = (adiffX % wi); // how much
13153 if (sub > (wi/2)) { // far enough to snap
13154 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13156 // remove difference..
13157 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13161 x = Math.max(this.minX, x);
13164 diffX = this.constrain(w, diffX, mw, mxw);
13170 w = Math.min(Math.max(mw, w), mxw);
13171 diffY = this.constrain(h, diffY, mh, mxh);
13176 diffX = this.constrain(w, diffX, mw, mxw);
13177 diffY = this.constrain(h, diffY, mh, mxh);
13184 diffX = this.constrain(w, diffX, mw, mxw);
13186 h = Math.min(Math.max(mh, h), mxh);
13192 var sw = this.snap(w, wi, mw);
13193 var sh = this.snap(h, hi, mh);
13194 if(sw != w || sh != h){
13217 if(this.preserveRatio){
13222 h = Math.min(Math.max(mh, h), mxh);
13227 w = Math.min(Math.max(mw, w), mxw);
13232 w = Math.min(Math.max(mw, w), mxw);
13238 w = Math.min(Math.max(mw, w), mxw);
13244 h = Math.min(Math.max(mh, h), mxh);
13252 h = Math.min(Math.max(mh, h), mxh);
13262 h = Math.min(Math.max(mh, h), mxh);
13270 if (pos == 'hdrag') {
13273 this.proxy.setBounds(x, y, w, h);
13275 this.resizeElement();
13279 this.fireEvent("resizing", this, x, y, w, h, e);
13283 handleOver : function(){
13285 this.el.addClass("x-resizable-over");
13290 handleOut : function(){
13291 if(!this.resizing){
13292 this.el.removeClass("x-resizable-over");
13297 * Returns the element this component is bound to.
13298 * @return {Roo.Element}
13300 getEl : function(){
13305 * Returns the resizeChild element (or null).
13306 * @return {Roo.Element}
13308 getResizeChild : function(){
13309 return this.resizeChild;
13311 groupHandler : function()
13316 * Destroys this resizable. If the element was wrapped and
13317 * removeEl is not true then the element remains.
13318 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13320 destroy : function(removeEl){
13321 this.proxy.remove();
13323 this.overlay.removeAllListeners();
13324 this.overlay.remove();
13326 var ps = Roo.Resizable.positions;
13328 if(typeof ps[k] != "function" && this[ps[k]]){
13329 var h = this[ps[k]];
13330 h.el.removeAllListeners();
13335 this.el.update("");
13342 // hash to map config positions to true positions
13343 Roo.Resizable.positions = {
13344 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
13349 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13351 // only initialize the template if resizable is used
13352 var tpl = Roo.DomHelper.createTemplate(
13353 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13356 Roo.Resizable.Handle.prototype.tpl = tpl;
13358 this.position = pos;
13360 // show north drag fro topdra
13361 var handlepos = pos == 'hdrag' ? 'north' : pos;
13363 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13364 if (pos == 'hdrag') {
13365 this.el.setStyle('cursor', 'pointer');
13367 this.el.unselectable();
13369 this.el.setOpacity(0);
13371 this.el.on("mousedown", this.onMouseDown, this);
13372 if(!disableTrackOver){
13373 this.el.on("mouseover", this.onMouseOver, this);
13374 this.el.on("mouseout", this.onMouseOut, this);
13379 Roo.Resizable.Handle.prototype = {
13380 afterResize : function(rz){
13385 onMouseDown : function(e){
13386 this.rz.onMouseDown(this, e);
13389 onMouseOver : function(e){
13390 this.rz.handleOver(this, e);
13393 onMouseOut : function(e){
13394 this.rz.handleOut(this, e);
13398 * Ext JS Library 1.1.1
13399 * Copyright(c) 2006-2007, Ext JS, LLC.
13401 * Originally Released Under LGPL - original licence link has changed is not relivant.
13404 * <script type="text/javascript">
13408 * @class Roo.Editor
13409 * @extends Roo.Component
13410 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13412 * Create a new Editor
13413 * @param {Roo.form.Field} field The Field object (or descendant)
13414 * @param {Object} config The config object
13416 Roo.Editor = function(field, config){
13417 Roo.Editor.superclass.constructor.call(this, config);
13418 this.field = field;
13421 * @event beforestartedit
13422 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
13423 * false from the handler of this event.
13424 * @param {Editor} this
13425 * @param {Roo.Element} boundEl The underlying element bound to this editor
13426 * @param {Mixed} value The field value being set
13428 "beforestartedit" : true,
13431 * Fires when this editor is displayed
13432 * @param {Roo.Element} boundEl The underlying element bound to this editor
13433 * @param {Mixed} value The starting field value
13435 "startedit" : true,
13437 * @event beforecomplete
13438 * Fires after a change has been made to the field, but before the change is reflected in the underlying
13439 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
13440 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13441 * event will not fire since no edit actually occurred.
13442 * @param {Editor} this
13443 * @param {Mixed} value The current field value
13444 * @param {Mixed} startValue The original field value
13446 "beforecomplete" : true,
13449 * Fires after editing is complete and any changed value has been written to the underlying field.
13450 * @param {Editor} this
13451 * @param {Mixed} value The current field value
13452 * @param {Mixed} startValue The original field value
13456 * @event specialkey
13457 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
13458 * {@link Roo.EventObject#getKey} to determine which key was pressed.
13459 * @param {Roo.form.Field} this
13460 * @param {Roo.EventObject} e The event object
13462 "specialkey" : true
13466 Roo.extend(Roo.Editor, Roo.Component, {
13468 * @cfg {Boolean/String} autosize
13469 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13470 * or "height" to adopt the height only (defaults to false)
13473 * @cfg {Boolean} revertInvalid
13474 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13475 * validation fails (defaults to true)
13478 * @cfg {Boolean} ignoreNoChange
13479 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13480 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
13481 * will never be ignored.
13484 * @cfg {Boolean} hideEl
13485 * False to keep the bound element visible while the editor is displayed (defaults to true)
13488 * @cfg {Mixed} value
13489 * The data value of the underlying field (defaults to "")
13493 * @cfg {String} alignment
13494 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13498 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13499 * for bottom-right shadow (defaults to "frame")
13503 * @cfg {Boolean} constrain True to constrain the editor to the viewport
13507 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13509 completeOnEnter : false,
13511 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13513 cancelOnEsc : false,
13515 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13520 onRender : function(ct, position){
13521 this.el = new Roo.Layer({
13522 shadow: this.shadow,
13528 constrain: this.constrain
13530 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13531 if(this.field.msgTarget != 'title'){
13532 this.field.msgTarget = 'qtip';
13534 this.field.render(this.el);
13536 this.field.el.dom.setAttribute('autocomplete', 'off');
13538 this.field.on("specialkey", this.onSpecialKey, this);
13539 if(this.swallowKeys){
13540 this.field.el.swallowEvent(['keydown','keypress']);
13543 this.field.on("blur", this.onBlur, this);
13544 if(this.field.grow){
13545 this.field.on("autosize", this.el.sync, this.el, {delay:1});
13549 onSpecialKey : function(field, e)
13551 //Roo.log('editor onSpecialKey');
13552 if(this.completeOnEnter && e.getKey() == e.ENTER){
13554 this.completeEdit();
13557 // do not fire special key otherwise it might hide close the editor...
13558 if(e.getKey() == e.ENTER){
13561 if(this.cancelOnEsc && e.getKey() == e.ESC){
13565 this.fireEvent('specialkey', field, e);
13570 * Starts the editing process and shows the editor.
13571 * @param {String/HTMLElement/Element} el The element to edit
13572 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13573 * to the innerHTML of el.
13575 startEdit : function(el, value){
13577 this.completeEdit();
13579 this.boundEl = Roo.get(el);
13580 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13581 if(!this.rendered){
13582 this.render(this.parentEl || document.body);
13584 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13587 this.startValue = v;
13588 this.field.setValue(v);
13590 var sz = this.boundEl.getSize();
13591 switch(this.autoSize){
13593 this.setSize(sz.width, "");
13596 this.setSize("", sz.height);
13599 this.setSize(sz.width, sz.height);
13602 this.el.alignTo(this.boundEl, this.alignment);
13603 this.editing = true;
13605 Roo.QuickTips.disable();
13611 * Sets the height and width of this editor.
13612 * @param {Number} width The new width
13613 * @param {Number} height The new height
13615 setSize : function(w, h){
13616 this.field.setSize(w, h);
13623 * Realigns the editor to the bound field based on the current alignment config value.
13625 realign : function(){
13626 this.el.alignTo(this.boundEl, this.alignment);
13630 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13631 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13633 completeEdit : function(remainVisible){
13637 var v = this.getValue();
13638 if(this.revertInvalid !== false && !this.field.isValid()){
13639 v = this.startValue;
13640 this.cancelEdit(true);
13642 if(String(v) === String(this.startValue) && this.ignoreNoChange){
13643 this.editing = false;
13647 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13648 this.editing = false;
13649 if(this.updateEl && this.boundEl){
13650 this.boundEl.update(v);
13652 if(remainVisible !== true){
13655 this.fireEvent("complete", this, v, this.startValue);
13660 onShow : function(){
13662 if(this.hideEl !== false){
13663 this.boundEl.hide();
13666 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13667 this.fixIEFocus = true;
13668 this.deferredFocus.defer(50, this);
13670 this.field.focus();
13672 this.fireEvent("startedit", this.boundEl, this.startValue);
13675 deferredFocus : function(){
13677 this.field.focus();
13682 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
13683 * reverted to the original starting value.
13684 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13685 * cancel (defaults to false)
13687 cancelEdit : function(remainVisible){
13689 this.setValue(this.startValue);
13690 if(remainVisible !== true){
13697 onBlur : function(){
13698 if(this.allowBlur !== true && this.editing){
13699 this.completeEdit();
13704 onHide : function(){
13706 this.completeEdit();
13710 if(this.field.collapse){
13711 this.field.collapse();
13714 if(this.hideEl !== false){
13715 this.boundEl.show();
13718 Roo.QuickTips.enable();
13723 * Sets the data value of the editor
13724 * @param {Mixed} value Any valid value supported by the underlying field
13726 setValue : function(v){
13727 this.field.setValue(v);
13731 * Gets the data value of the editor
13732 * @return {Mixed} The data value
13734 getValue : function(){
13735 return this.field.getValue();
13739 * Ext JS Library 1.1.1
13740 * Copyright(c) 2006-2007, Ext JS, LLC.
13742 * Originally Released Under LGPL - original licence link has changed is not relivant.
13745 * <script type="text/javascript">
13749 * @class Roo.BasicDialog
13750 * @extends Roo.util.Observable
13751 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
13753 var dlg = new Roo.BasicDialog("my-dlg", {
13762 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13763 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
13764 dlg.addButton('Cancel', dlg.hide, dlg);
13767 <b>A Dialog should always be a direct child of the body element.</b>
13768 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13769 * @cfg {String} title Default text to display in the title bar (defaults to null)
13770 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
13771 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
13772 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13773 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13774 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13775 * (defaults to null with no animation)
13776 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13777 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13778 * property for valid values (defaults to 'all')
13779 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13780 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13781 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13782 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13783 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13784 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13785 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13786 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13787 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13788 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13789 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13790 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13791 * draggable = true (defaults to false)
13792 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13793 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13794 * shadow (defaults to false)
13795 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13796 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13797 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13798 * @cfg {Array} buttons Array of buttons
13799 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13801 * Create a new BasicDialog.
13802 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13803 * @param {Object} config Configuration options
13805 Roo.BasicDialog = function(el, config){
13806 this.el = Roo.get(el);
13807 var dh = Roo.DomHelper;
13808 if(!this.el && config && config.autoCreate){
13809 if(typeof config.autoCreate == "object"){
13810 if(!config.autoCreate.id){
13811 config.autoCreate.id = el;
13813 this.el = dh.append(document.body,
13814 config.autoCreate, true);
13816 this.el = dh.append(document.body,
13817 {tag: "div", id: el, style:'visibility:hidden;'}, true);
13821 el.setDisplayed(true);
13822 el.hide = this.hideAction;
13824 el.addClass("x-dlg");
13826 Roo.apply(this, config);
13828 this.proxy = el.createProxy("x-dlg-proxy");
13829 this.proxy.hide = this.hideAction;
13830 this.proxy.setOpacity(.5);
13834 el.setWidth(config.width);
13837 el.setHeight(config.height);
13839 this.size = el.getSize();
13840 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13841 this.xy = [config.x,config.y];
13843 this.xy = el.getCenterXY(true);
13845 /** The header element @type Roo.Element */
13846 this.header = el.child("> .x-dlg-hd");
13847 /** The body element @type Roo.Element */
13848 this.body = el.child("> .x-dlg-bd");
13849 /** The footer element @type Roo.Element */
13850 this.footer = el.child("> .x-dlg-ft");
13853 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
13856 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13859 this.header.unselectable();
13861 this.header.update(this.title);
13863 // this element allows the dialog to be focused for keyboard event
13864 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13865 this.focusEl.swallowEvent("click", true);
13867 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13869 // wrap the body and footer for special rendering
13870 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13872 this.bwrap.dom.appendChild(this.footer.dom);
13875 this.bg = this.el.createChild({
13876 tag: "div", cls:"x-dlg-bg",
13877 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
13879 this.centerBg = this.bg.child("div.x-dlg-bg-center");
13882 if(this.autoScroll !== false && !this.autoTabs){
13883 this.body.setStyle("overflow", "auto");
13886 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13888 if(this.closable !== false){
13889 this.el.addClass("x-dlg-closable");
13890 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13891 this.close.on("click", this.closeClick, this);
13892 this.close.addClassOnOver("x-dlg-close-over");
13894 if(this.collapsible !== false){
13895 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13896 this.collapseBtn.on("click", this.collapseClick, this);
13897 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13898 this.header.on("dblclick", this.collapseClick, this);
13900 if(this.resizable !== false){
13901 this.el.addClass("x-dlg-resizable");
13902 this.resizer = new Roo.Resizable(el, {
13903 minWidth: this.minWidth || 80,
13904 minHeight:this.minHeight || 80,
13905 handles: this.resizeHandles || "all",
13908 this.resizer.on("beforeresize", this.beforeResize, this);
13909 this.resizer.on("resize", this.onResize, this);
13911 if(this.draggable !== false){
13912 el.addClass("x-dlg-draggable");
13913 if (!this.proxyDrag) {
13914 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13917 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13919 dd.setHandleElId(this.header.id);
13920 dd.endDrag = this.endMove.createDelegate(this);
13921 dd.startDrag = this.startMove.createDelegate(this);
13922 dd.onDrag = this.onDrag.createDelegate(this);
13927 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13928 this.mask.enableDisplayMode("block");
13930 this.el.addClass("x-dlg-modal");
13933 this.shadow = new Roo.Shadow({
13934 mode : typeof this.shadow == "string" ? this.shadow : "sides",
13935 offset : this.shadowOffset
13938 this.shadowOffset = 0;
13940 if(Roo.useShims && this.shim !== false){
13941 this.shim = this.el.createShim();
13942 this.shim.hide = this.hideAction;
13950 if (this.buttons) {
13951 var bts= this.buttons;
13953 Roo.each(bts, function(b) {
13962 * Fires when a key is pressed
13963 * @param {Roo.BasicDialog} this
13964 * @param {Roo.EventObject} e
13969 * Fires when this dialog is moved by the user.
13970 * @param {Roo.BasicDialog} this
13971 * @param {Number} x The new page X
13972 * @param {Number} y The new page Y
13977 * Fires when this dialog is resized by the user.
13978 * @param {Roo.BasicDialog} this
13979 * @param {Number} width The new width
13980 * @param {Number} height The new height
13984 * @event beforehide
13985 * Fires before this dialog is hidden.
13986 * @param {Roo.BasicDialog} this
13988 "beforehide" : true,
13991 * Fires when this dialog is hidden.
13992 * @param {Roo.BasicDialog} this
13996 * @event beforeshow
13997 * Fires before this dialog is shown.
13998 * @param {Roo.BasicDialog} this
14000 "beforeshow" : true,
14003 * Fires when this dialog is shown.
14004 * @param {Roo.BasicDialog} this
14008 el.on("keydown", this.onKeyDown, this);
14009 el.on("mousedown", this.toFront, this);
14010 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14012 Roo.DialogManager.register(this);
14013 Roo.BasicDialog.superclass.constructor.call(this);
14016 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14017 shadowOffset: Roo.isIE ? 6 : 5,
14020 minButtonWidth: 75,
14021 defaultButton: null,
14022 buttonAlign: "right",
14027 * Sets the dialog title text
14028 * @param {String} text The title text to display
14029 * @return {Roo.BasicDialog} this
14031 setTitle : function(text){
14032 this.header.update(text);
14037 closeClick : function(){
14042 collapseClick : function(){
14043 this[this.collapsed ? "expand" : "collapse"]();
14047 * Collapses the dialog to its minimized state (only the title bar is visible).
14048 * Equivalent to the user clicking the collapse dialog button.
14050 collapse : function(){
14051 if(!this.collapsed){
14052 this.collapsed = true;
14053 this.el.addClass("x-dlg-collapsed");
14054 this.restoreHeight = this.el.getHeight();
14055 this.resizeTo(this.el.getWidth(), this.header.getHeight());
14060 * Expands a collapsed dialog back to its normal state. Equivalent to the user
14061 * clicking the expand dialog button.
14063 expand : function(){
14064 if(this.collapsed){
14065 this.collapsed = false;
14066 this.el.removeClass("x-dlg-collapsed");
14067 this.resizeTo(this.el.getWidth(), this.restoreHeight);
14072 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14073 * @return {Roo.TabPanel} The tabs component
14075 initTabs : function(){
14076 var tabs = this.getTabs();
14077 while(tabs.getTab(0)){
14080 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14082 tabs.addTab(Roo.id(dom), dom.title);
14090 beforeResize : function(){
14091 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14095 onResize : function(){
14096 this.refreshSize();
14097 this.syncBodyHeight();
14098 this.adjustAssets();
14100 this.fireEvent("resize", this, this.size.width, this.size.height);
14104 onKeyDown : function(e){
14105 if(this.isVisible()){
14106 this.fireEvent("keydown", this, e);
14111 * Resizes the dialog.
14112 * @param {Number} width
14113 * @param {Number} height
14114 * @return {Roo.BasicDialog} this
14116 resizeTo : function(width, height){
14117 this.el.setSize(width, height);
14118 this.size = {width: width, height: height};
14119 this.syncBodyHeight();
14120 if(this.fixedcenter){
14123 if(this.isVisible()){
14124 this.constrainXY();
14125 this.adjustAssets();
14127 this.fireEvent("resize", this, width, height);
14133 * Resizes the dialog to fit the specified content size.
14134 * @param {Number} width
14135 * @param {Number} height
14136 * @return {Roo.BasicDialog} this
14138 setContentSize : function(w, h){
14139 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14140 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14141 //if(!this.el.isBorderBox()){
14142 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14143 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14146 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14147 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14149 this.resizeTo(w, h);
14154 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
14155 * executed in response to a particular key being pressed while the dialog is active.
14156 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14157 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14158 * @param {Function} fn The function to call
14159 * @param {Object} scope (optional) The scope of the function
14160 * @return {Roo.BasicDialog} this
14162 addKeyListener : function(key, fn, scope){
14163 var keyCode, shift, ctrl, alt;
14164 if(typeof key == "object" && !(key instanceof Array)){
14165 keyCode = key["key"];
14166 shift = key["shift"];
14167 ctrl = key["ctrl"];
14172 var handler = function(dlg, e){
14173 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
14174 var k = e.getKey();
14175 if(keyCode instanceof Array){
14176 for(var i = 0, len = keyCode.length; i < len; i++){
14177 if(keyCode[i] == k){
14178 fn.call(scope || window, dlg, k, e);
14184 fn.call(scope || window, dlg, k, e);
14189 this.on("keydown", handler);
14194 * Returns the TabPanel component (creates it if it doesn't exist).
14195 * Note: If you wish to simply check for the existence of tabs without creating them,
14196 * check for a null 'tabs' property.
14197 * @return {Roo.TabPanel} The tabs component
14199 getTabs : function(){
14201 this.el.addClass("x-dlg-auto-tabs");
14202 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14203 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14209 * Adds a button to the footer section of the dialog.
14210 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14211 * object or a valid Roo.DomHelper element config
14212 * @param {Function} handler The function called when the button is clicked
14213 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14214 * @return {Roo.Button} The new button
14216 addButton : function(config, handler, scope){
14217 var dh = Roo.DomHelper;
14219 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14221 if(!this.btnContainer){
14222 var tb = this.footer.createChild({
14224 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14225 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14227 this.btnContainer = tb.firstChild.firstChild.firstChild;
14232 minWidth: this.minButtonWidth,
14235 if(typeof config == "string"){
14236 bconfig.text = config;
14239 bconfig.dhconfig = config;
14241 Roo.apply(bconfig, config);
14245 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14246 bconfig.position = Math.max(0, bconfig.position);
14247 fc = this.btnContainer.childNodes[bconfig.position];
14250 var btn = new Roo.Button(
14252 this.btnContainer.insertBefore(document.createElement("td"),fc)
14253 : this.btnContainer.appendChild(document.createElement("td")),
14254 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
14257 this.syncBodyHeight();
14260 * Array of all the buttons that have been added to this dialog via addButton
14265 this.buttons.push(btn);
14270 * Sets the default button to be focused when the dialog is displayed.
14271 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14272 * @return {Roo.BasicDialog} this
14274 setDefaultButton : function(btn){
14275 this.defaultButton = btn;
14280 getHeaderFooterHeight : function(safe){
14283 height += this.header.getHeight();
14286 var fm = this.footer.getMargins();
14287 height += (this.footer.getHeight()+fm.top+fm.bottom);
14289 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14290 height += this.centerBg.getPadding("tb");
14295 syncBodyHeight : function()
14297 var bd = this.body, // the text
14298 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14300 var height = this.size.height - this.getHeaderFooterHeight(false);
14301 bd.setHeight(height-bd.getMargins("tb"));
14302 var hh = this.header.getHeight();
14303 var h = this.size.height-hh;
14306 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14307 bw.setHeight(h-cb.getPadding("tb"));
14309 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14310 bd.setWidth(bw.getWidth(true));
14312 this.tabs.syncHeight();
14314 this.tabs.el.repaint();
14320 * Restores the previous state of the dialog if Roo.state is configured.
14321 * @return {Roo.BasicDialog} this
14323 restoreState : function(){
14324 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14325 if(box && box.width){
14326 this.xy = [box.x, box.y];
14327 this.resizeTo(box.width, box.height);
14333 beforeShow : function(){
14335 if(this.fixedcenter){
14336 this.xy = this.el.getCenterXY(true);
14339 Roo.get(document.body).addClass("x-body-masked");
14340 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14343 this.constrainXY();
14347 animShow : function(){
14348 var b = Roo.get(this.animateTarget).getBox();
14349 this.proxy.setSize(b.width, b.height);
14350 this.proxy.setLocation(b.x, b.y);
14352 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14353 true, .35, this.showEl.createDelegate(this));
14357 * Shows the dialog.
14358 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14359 * @return {Roo.BasicDialog} this
14361 show : function(animateTarget){
14362 if (this.fireEvent("beforeshow", this) === false){
14365 if(this.syncHeightBeforeShow){
14366 this.syncBodyHeight();
14367 }else if(this.firstShow){
14368 this.firstShow = false;
14369 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14371 this.animateTarget = animateTarget || this.animateTarget;
14372 if(!this.el.isVisible()){
14374 if(this.animateTarget && Roo.get(this.animateTarget)){
14384 showEl : function(){
14386 this.el.setXY(this.xy);
14388 this.adjustAssets(true);
14391 // IE peekaboo bug - fix found by Dave Fenwick
14395 this.fireEvent("show", this);
14399 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
14400 * dialog itself will receive focus.
14402 focus : function(){
14403 if(this.defaultButton){
14404 this.defaultButton.focus();
14406 this.focusEl.focus();
14411 constrainXY : function(){
14412 if(this.constraintoviewport !== false){
14413 if(!this.viewSize){
14414 if(this.container){
14415 var s = this.container.getSize();
14416 this.viewSize = [s.width, s.height];
14418 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14421 var s = Roo.get(this.container||document).getScroll();
14423 var x = this.xy[0], y = this.xy[1];
14424 var w = this.size.width, h = this.size.height;
14425 var vw = this.viewSize[0], vh = this.viewSize[1];
14426 // only move it if it needs it
14428 // first validate right/bottom
14429 if(x + w > vw+s.left){
14433 if(y + h > vh+s.top){
14437 // then make sure top/left isn't negative
14449 if(this.isVisible()){
14450 this.el.setLocation(x, y);
14451 this.adjustAssets();
14458 onDrag : function(){
14459 if(!this.proxyDrag){
14460 this.xy = this.el.getXY();
14461 this.adjustAssets();
14466 adjustAssets : function(doShow){
14467 var x = this.xy[0], y = this.xy[1];
14468 var w = this.size.width, h = this.size.height;
14469 if(doShow === true){
14471 this.shadow.show(this.el);
14477 if(this.shadow && this.shadow.isVisible()){
14478 this.shadow.show(this.el);
14480 if(this.shim && this.shim.isVisible()){
14481 this.shim.setBounds(x, y, w, h);
14486 adjustViewport : function(w, h){
14488 w = Roo.lib.Dom.getViewWidth();
14489 h = Roo.lib.Dom.getViewHeight();
14492 this.viewSize = [w, h];
14493 if(this.modal && this.mask.isVisible()){
14494 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14495 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14497 if(this.isVisible()){
14498 this.constrainXY();
14503 * Destroys this dialog and all its supporting elements (including any tabs, shim,
14504 * shadow, proxy, mask, etc.) Also removes all event listeners.
14505 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14507 destroy : function(removeEl){
14508 if(this.isVisible()){
14509 this.animateTarget = null;
14512 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14514 this.tabs.destroy(removeEl);
14527 for(var i = 0, len = this.buttons.length; i < len; i++){
14528 this.buttons[i].destroy();
14531 this.el.removeAllListeners();
14532 if(removeEl === true){
14533 this.el.update("");
14536 Roo.DialogManager.unregister(this);
14540 startMove : function(){
14541 if(this.proxyDrag){
14544 if(this.constraintoviewport !== false){
14545 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14550 endMove : function(){
14551 if(!this.proxyDrag){
14552 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14554 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14557 this.refreshSize();
14558 this.adjustAssets();
14560 this.fireEvent("move", this, this.xy[0], this.xy[1]);
14564 * Brings this dialog to the front of any other visible dialogs
14565 * @return {Roo.BasicDialog} this
14567 toFront : function(){
14568 Roo.DialogManager.bringToFront(this);
14573 * Sends this dialog to the back (under) of any other visible dialogs
14574 * @return {Roo.BasicDialog} this
14576 toBack : function(){
14577 Roo.DialogManager.sendToBack(this);
14582 * Centers this dialog in the viewport
14583 * @return {Roo.BasicDialog} this
14585 center : function(){
14586 var xy = this.el.getCenterXY(true);
14587 this.moveTo(xy[0], xy[1]);
14592 * Moves the dialog's top-left corner to the specified point
14593 * @param {Number} x
14594 * @param {Number} y
14595 * @return {Roo.BasicDialog} this
14597 moveTo : function(x, y){
14599 if(this.isVisible()){
14600 this.el.setXY(this.xy);
14601 this.adjustAssets();
14607 * Aligns the dialog to the specified element
14608 * @param {String/HTMLElement/Roo.Element} element The element to align to.
14609 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14610 * @param {Array} offsets (optional) Offset the positioning by [x, y]
14611 * @return {Roo.BasicDialog} this
14613 alignTo : function(element, position, offsets){
14614 this.xy = this.el.getAlignToXY(element, position, offsets);
14615 if(this.isVisible()){
14616 this.el.setXY(this.xy);
14617 this.adjustAssets();
14623 * Anchors an element to another element and realigns it when the window is resized.
14624 * @param {String/HTMLElement/Roo.Element} element The element to align to.
14625 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14626 * @param {Array} offsets (optional) Offset the positioning by [x, y]
14627 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14628 * is a number, it is used as the buffer delay (defaults to 50ms).
14629 * @return {Roo.BasicDialog} this
14631 anchorTo : function(el, alignment, offsets, monitorScroll){
14632 var action = function(){
14633 this.alignTo(el, alignment, offsets);
14635 Roo.EventManager.onWindowResize(action, this);
14636 var tm = typeof monitorScroll;
14637 if(tm != 'undefined'){
14638 Roo.EventManager.on(window, 'scroll', action, this,
14639 {buffer: tm == 'number' ? monitorScroll : 50});
14646 * Returns true if the dialog is visible
14647 * @return {Boolean}
14649 isVisible : function(){
14650 return this.el.isVisible();
14654 animHide : function(callback){
14655 var b = Roo.get(this.animateTarget).getBox();
14657 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14659 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14660 this.hideEl.createDelegate(this, [callback]));
14664 * Hides the dialog.
14665 * @param {Function} callback (optional) Function to call when the dialog is hidden
14666 * @return {Roo.BasicDialog} this
14668 hide : function(callback){
14669 if (this.fireEvent("beforehide", this) === false){
14673 this.shadow.hide();
14678 // sometimes animateTarget seems to get set.. causing problems...
14679 // this just double checks..
14680 if(this.animateTarget && Roo.get(this.animateTarget)) {
14681 this.animHide(callback);
14684 this.hideEl(callback);
14690 hideEl : function(callback){
14694 Roo.get(document.body).removeClass("x-body-masked");
14696 this.fireEvent("hide", this);
14697 if(typeof callback == "function"){
14703 hideAction : function(){
14704 this.setLeft("-10000px");
14705 this.setTop("-10000px");
14706 this.setStyle("visibility", "hidden");
14710 refreshSize : function(){
14711 this.size = this.el.getSize();
14712 this.xy = this.el.getXY();
14713 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14717 // z-index is managed by the DialogManager and may be overwritten at any time
14718 setZIndex : function(index){
14720 this.mask.setStyle("z-index", index);
14723 this.shim.setStyle("z-index", ++index);
14726 this.shadow.setZIndex(++index);
14728 this.el.setStyle("z-index", ++index);
14730 this.proxy.setStyle("z-index", ++index);
14733 this.resizer.proxy.setStyle("z-index", ++index);
14736 this.lastZIndex = index;
14740 * Returns the element for this dialog
14741 * @return {Roo.Element} The underlying dialog Element
14743 getEl : function(){
14749 * @class Roo.DialogManager
14750 * Provides global access to BasicDialogs that have been created and
14751 * support for z-indexing (layering) multiple open dialogs.
14753 Roo.DialogManager = function(){
14755 var accessList = [];
14759 var sortDialogs = function(d1, d2){
14760 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14764 var orderDialogs = function(){
14765 accessList.sort(sortDialogs);
14766 var seed = Roo.DialogManager.zseed;
14767 for(var i = 0, len = accessList.length; i < len; i++){
14768 var dlg = accessList[i];
14770 dlg.setZIndex(seed + (i*10));
14777 * The starting z-index for BasicDialogs (defaults to 9000)
14778 * @type Number The z-index value
14783 register : function(dlg){
14784 list[dlg.id] = dlg;
14785 accessList.push(dlg);
14789 unregister : function(dlg){
14790 delete list[dlg.id];
14793 if(!accessList.indexOf){
14794 for( i = 0, len = accessList.length; i < len; i++){
14795 if(accessList[i] == dlg){
14796 accessList.splice(i, 1);
14801 i = accessList.indexOf(dlg);
14803 accessList.splice(i, 1);
14809 * Gets a registered dialog by id
14810 * @param {String/Object} id The id of the dialog or a dialog
14811 * @return {Roo.BasicDialog} this
14813 get : function(id){
14814 return typeof id == "object" ? id : list[id];
14818 * Brings the specified dialog to the front
14819 * @param {String/Object} dlg The id of the dialog or a dialog
14820 * @return {Roo.BasicDialog} this
14822 bringToFront : function(dlg){
14823 dlg = this.get(dlg);
14826 dlg._lastAccess = new Date().getTime();
14833 * Sends the specified dialog to the back
14834 * @param {String/Object} dlg The id of the dialog or a dialog
14835 * @return {Roo.BasicDialog} this
14837 sendToBack : function(dlg){
14838 dlg = this.get(dlg);
14839 dlg._lastAccess = -(new Date().getTime());
14845 * Hides all dialogs
14847 hideAll : function(){
14848 for(var id in list){
14849 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14858 * @class Roo.LayoutDialog
14859 * @extends Roo.BasicDialog
14860 * Dialog which provides adjustments for working with a layout in a Dialog.
14861 * Add your necessary layout config options to the dialog's config.<br>
14862 * Example usage (including a nested layout):
14865 dialog = new Roo.LayoutDialog("download-dlg", {
14874 // layout config merges with the dialog config
14876 tabPosition: "top",
14877 alwaysShowTabs: true
14880 dialog.addKeyListener(27, dialog.hide, dialog);
14881 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14882 dialog.addButton("Build It!", this.getDownload, this);
14884 // we can even add nested layouts
14885 var innerLayout = new Roo.BorderLayout("dl-inner", {
14895 innerLayout.beginUpdate();
14896 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14897 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14898 innerLayout.endUpdate(true);
14900 var layout = dialog.getLayout();
14901 layout.beginUpdate();
14902 layout.add("center", new Roo.ContentPanel("standard-panel",
14903 {title: "Download the Source", fitToFrame:true}));
14904 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14905 {title: "Build your own roo.js"}));
14906 layout.getRegion("center").showPanel(sp);
14907 layout.endUpdate();
14911 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14912 * @param {Object} config configuration options
14914 Roo.LayoutDialog = function(el, cfg){
14917 if (typeof(cfg) == 'undefined') {
14918 config = Roo.apply({}, el);
14919 // not sure why we use documentElement here.. - it should always be body.
14920 // IE7 borks horribly if we use documentElement.
14921 // webkit also does not like documentElement - it creates a body element...
14922 el = Roo.get( document.body || document.documentElement ).createChild();
14923 //config.autoCreate = true;
14927 config.autoTabs = false;
14928 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14929 this.body.setStyle({overflow:"hidden", position:"relative"});
14930 this.layout = new Roo.BorderLayout(this.body.dom, config);
14931 this.layout.monitorWindowResize = false;
14932 this.el.addClass("x-dlg-auto-layout");
14933 // fix case when center region overwrites center function
14934 this.center = Roo.BasicDialog.prototype.center;
14935 this.on("show", this.layout.layout, this.layout, true);
14936 if (config.items) {
14937 var xitems = config.items;
14938 delete config.items;
14939 Roo.each(xitems, this.addxtype, this);
14944 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14946 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14949 endUpdate : function(){
14950 this.layout.endUpdate();
14954 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14957 beginUpdate : function(){
14958 this.layout.beginUpdate();
14962 * Get the BorderLayout for this dialog
14963 * @return {Roo.BorderLayout}
14965 getLayout : function(){
14966 return this.layout;
14969 showEl : function(){
14970 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14972 this.layout.layout();
14977 // Use the syncHeightBeforeShow config option to control this automatically
14978 syncBodyHeight : function(){
14979 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14980 if(this.layout){this.layout.layout();}
14984 * Add an xtype element (actually adds to the layout.)
14985 * @return {Object} xdata xtype object data.
14988 addxtype : function(c) {
14989 return this.layout.addxtype(c);
14993 * Ext JS Library 1.1.1
14994 * Copyright(c) 2006-2007, Ext JS, LLC.
14996 * Originally Released Under LGPL - original licence link has changed is not relivant.
14999 * <script type="text/javascript">
15003 * @class Roo.MessageBox
15004 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
15008 Roo.Msg.alert('Status', 'Changes saved successfully.');
15010 // Prompt for user data:
15011 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15013 // process text value...
15017 // Show a dialog using config options:
15019 title:'Save Changes?',
15020 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15021 buttons: Roo.Msg.YESNOCANCEL,
15028 Roo.MessageBox = function(){
15029 var dlg, opt, mask, waitTimer;
15030 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15031 var buttons, activeTextEl, bwidth;
15034 var handleButton = function(button){
15036 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15040 var handleHide = function(){
15041 if(opt && opt.cls){
15042 dlg.el.removeClass(opt.cls);
15045 Roo.TaskMgr.stop(waitTimer);
15051 var updateButtons = function(b){
15054 buttons["ok"].hide();
15055 buttons["cancel"].hide();
15056 buttons["yes"].hide();
15057 buttons["no"].hide();
15058 dlg.footer.dom.style.display = 'none';
15061 dlg.footer.dom.style.display = '';
15062 for(var k in buttons){
15063 if(typeof buttons[k] != "function"){
15066 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15067 width += buttons[k].el.getWidth()+15;
15077 var handleEsc = function(d, k, e){
15078 if(opt && opt.closable !== false){
15088 * Returns a reference to the underlying {@link Roo.BasicDialog} element
15089 * @return {Roo.BasicDialog} The BasicDialog element
15091 getDialog : function(){
15093 dlg = new Roo.BasicDialog("x-msg-box", {
15098 constraintoviewport:false,
15100 collapsible : false,
15103 width:400, height:100,
15104 buttonAlign:"center",
15105 closeClick : function(){
15106 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15107 handleButton("no");
15109 handleButton("cancel");
15113 dlg.on("hide", handleHide);
15115 dlg.addKeyListener(27, handleEsc);
15117 var bt = this.buttonText;
15118 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15119 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15120 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15121 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15122 bodyEl = dlg.body.createChild({
15124 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>'
15126 msgEl = bodyEl.dom.firstChild;
15127 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15128 textboxEl.enableDisplayMode();
15129 textboxEl.addKeyListener([10,13], function(){
15130 if(dlg.isVisible() && opt && opt.buttons){
15131 if(opt.buttons.ok){
15132 handleButton("ok");
15133 }else if(opt.buttons.yes){
15134 handleButton("yes");
15138 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15139 textareaEl.enableDisplayMode();
15140 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15141 progressEl.enableDisplayMode();
15142 var pf = progressEl.dom.firstChild;
15144 pp = Roo.get(pf.firstChild);
15145 pp.setHeight(pf.offsetHeight);
15153 * Updates the message box body text
15154 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15155 * the XHTML-compliant non-breaking space character '&#160;')
15156 * @return {Roo.MessageBox} This message box
15158 updateText : function(text){
15159 if(!dlg.isVisible() && !opt.width){
15160 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15162 msgEl.innerHTML = text || ' ';
15164 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15165 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15167 Math.min(opt.width || cw , this.maxWidth),
15168 Math.max(opt.minWidth || this.minWidth, bwidth)
15171 activeTextEl.setWidth(w);
15173 if(dlg.isVisible()){
15174 dlg.fixedcenter = false;
15176 // to big, make it scroll. = But as usual stupid IE does not support
15179 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15180 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15181 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15183 bodyEl.dom.style.height = '';
15184 bodyEl.dom.style.overflowY = '';
15187 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15189 bodyEl.dom.style.overflowX = '';
15192 dlg.setContentSize(w, bodyEl.getHeight());
15193 if(dlg.isVisible()){
15194 dlg.fixedcenter = true;
15200 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
15201 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15202 * @param {Number} value Any number between 0 and 1 (e.g., .5)
15203 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15204 * @return {Roo.MessageBox} This message box
15206 updateProgress : function(value, text){
15208 this.updateText(text);
15210 if (pp) { // weird bug on my firefox - for some reason this is not defined
15211 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15217 * Returns true if the message box is currently displayed
15218 * @return {Boolean} True if the message box is visible, else false
15220 isVisible : function(){
15221 return dlg && dlg.isVisible();
15225 * Hides the message box if it is displayed
15228 if(this.isVisible()){
15234 * Displays a new message box, or reinitializes an existing message box, based on the config options
15235 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15236 * The following config object properties are supported:
15238 Property Type Description
15239 ---------- --------------- ------------------------------------------------------------------------------------
15240 animEl String/Element An id or Element from which the message box should animate as it opens and
15241 closes (defaults to undefined)
15242 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15243 cancel:'Bar'}), or false to not show any buttons (defaults to false)
15244 closable Boolean False to hide the top-right close button (defaults to true). Note that
15245 progress and wait dialogs will ignore this property and always hide the
15246 close button as they can only be closed programmatically.
15247 cls String A custom CSS class to apply to the message box element
15248 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
15249 displayed (defaults to 75)
15250 fn Function A callback function to execute after closing the dialog. The arguments to the
15251 function will be btn (the name of the button that was clicked, if applicable,
15252 e.g. "ok"), and text (the value of the active text field, if applicable).
15253 Progress and wait dialogs will ignore this option since they do not respond to
15254 user actions and can only be closed programmatically, so any required function
15255 should be called by the same code after it closes the dialog.
15256 icon String A CSS class that provides a background image to be used as an icon for
15257 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15258 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
15259 minWidth Number The minimum width in pixels of the message box (defaults to 100)
15260 modal Boolean False to allow user interaction with the page while the message box is
15261 displayed (defaults to true)
15262 msg String A string that will replace the existing message box body text (defaults
15263 to the XHTML-compliant non-breaking space character ' ')
15264 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
15265 progress Boolean True to display a progress bar (defaults to false)
15266 progressText String The text to display inside the progress bar if progress = true (defaults to '')
15267 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
15268 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
15269 title String The title text
15270 value String The string value to set into the active textbox element if displayed
15271 wait Boolean True to display a progress bar (defaults to false)
15272 width Number The width of the dialog in pixels
15279 msg: 'Please enter your address:',
15281 buttons: Roo.MessageBox.OKCANCEL,
15284 animEl: 'addAddressBtn'
15287 * @param {Object} config Configuration options
15288 * @return {Roo.MessageBox} This message box
15290 show : function(options)
15293 // this causes nightmares if you show one dialog after another
15294 // especially on callbacks..
15296 if(this.isVisible()){
15299 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15300 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
15301 Roo.log("New Dialog Message:" + options.msg )
15302 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15303 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15306 var d = this.getDialog();
15308 d.setTitle(opt.title || " ");
15309 d.close.setDisplayed(opt.closable !== false);
15310 activeTextEl = textboxEl;
15311 opt.prompt = opt.prompt || (opt.multiline ? true : false);
15316 textareaEl.setHeight(typeof opt.multiline == "number" ?
15317 opt.multiline : this.defaultTextHeight);
15318 activeTextEl = textareaEl;
15327 progressEl.setDisplayed(opt.progress === true);
15328 this.updateProgress(0);
15329 activeTextEl.dom.value = opt.value || "";
15331 dlg.setDefaultButton(activeTextEl);
15333 var bs = opt.buttons;
15336 db = buttons["ok"];
15337 }else if(bs && bs.yes){
15338 db = buttons["yes"];
15340 dlg.setDefaultButton(db);
15342 bwidth = updateButtons(opt.buttons);
15343 this.updateText(opt.msg);
15345 d.el.addClass(opt.cls);
15347 d.proxyDrag = opt.proxyDrag === true;
15348 d.modal = opt.modal !== false;
15349 d.mask = opt.modal !== false ? mask : false;
15350 if(!d.isVisible()){
15351 // force it to the end of the z-index stack so it gets a cursor in FF
15352 document.body.appendChild(dlg.el.dom);
15353 d.animateTarget = null;
15354 d.show(options.animEl);
15360 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
15361 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15362 * and closing the message box when the process is complete.
15363 * @param {String} title The title bar text
15364 * @param {String} msg The message box body text
15365 * @return {Roo.MessageBox} This message box
15367 progress : function(title, msg){
15374 minWidth: this.minProgressWidth,
15381 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15382 * If a callback function is passed it will be called after the user clicks the button, and the
15383 * id of the button that was clicked will be passed as the only parameter to the callback
15384 * (could also be the top-right close button).
15385 * @param {String} title The title bar text
15386 * @param {String} msg The message box body text
15387 * @param {Function} fn (optional) The callback function invoked after the message box is closed
15388 * @param {Object} scope (optional) The scope of the callback function
15389 * @return {Roo.MessageBox} This message box
15391 alert : function(title, msg, fn, scope){
15404 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
15405 * interaction while waiting for a long-running process to complete that does not have defined intervals.
15406 * You are responsible for closing the message box when the process is complete.
15407 * @param {String} msg The message box body text
15408 * @param {String} title (optional) The title bar text
15409 * @return {Roo.MessageBox} This message box
15411 wait : function(msg, title){
15422 waitTimer = Roo.TaskMgr.start({
15424 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15432 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15433 * If a callback function is passed it will be called after the user clicks either button, and the id of the
15434 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15435 * @param {String} title The title bar text
15436 * @param {String} msg The message box body text
15437 * @param {Function} fn (optional) The callback function invoked after the message box is closed
15438 * @param {Object} scope (optional) The scope of the callback function
15439 * @return {Roo.MessageBox} This message box
15441 confirm : function(title, msg, fn, scope){
15445 buttons: this.YESNO,
15454 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15455 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
15456 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15457 * (could also be the top-right close button) and the text that was entered will be passed as the two
15458 * parameters to the callback.
15459 * @param {String} title The title bar text
15460 * @param {String} msg The message box body text
15461 * @param {Function} fn (optional) The callback function invoked after the message box is closed
15462 * @param {Object} scope (optional) The scope of the callback function
15463 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15464 * property, or the height in pixels to create the textbox (defaults to false / single-line)
15465 * @return {Roo.MessageBox} This message box
15467 prompt : function(title, msg, fn, scope, multiline){
15471 buttons: this.OKCANCEL,
15476 multiline: multiline,
15483 * Button config that displays a single OK button
15488 * Button config that displays Yes and No buttons
15491 YESNO : {yes:true, no:true},
15493 * Button config that displays OK and Cancel buttons
15496 OKCANCEL : {ok:true, cancel:true},
15498 * Button config that displays Yes, No and Cancel buttons
15501 YESNOCANCEL : {yes:true, no:true, cancel:true},
15504 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15507 defaultTextHeight : 75,
15509 * The maximum width in pixels of the message box (defaults to 600)
15514 * The minimum width in pixels of the message box (defaults to 100)
15519 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
15520 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15523 minProgressWidth : 250,
15525 * An object containing the default button text strings that can be overriden for localized language support.
15526 * Supported properties are: ok, cancel, yes and no.
15527 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15540 * Shorthand for {@link Roo.MessageBox}
15542 Roo.Msg = Roo.MessageBox;/*
15544 * Ext JS Library 1.1.1
15545 * Copyright(c) 2006-2007, Ext JS, LLC.
15547 * Originally Released Under LGPL - original licence link has changed is not relivant.
15550 * <script type="text/javascript">
15553 * @class Roo.QuickTips
15554 * Provides attractive and customizable tooltips for any element.
15557 Roo.QuickTips = function(){
15558 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15559 var ce, bd, xy, dd;
15560 var visible = false, disabled = true, inited = false;
15561 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15563 var onOver = function(e){
15567 var t = e.getTarget();
15568 if(!t || t.nodeType !== 1 || t == document || t == document.body){
15571 if(ce && t == ce.el){
15572 clearTimeout(hideProc);
15575 if(t && tagEls[t.id]){
15576 tagEls[t.id].el = t;
15577 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15580 var ttp, et = Roo.fly(t);
15581 var ns = cfg.namespace;
15582 if(tm.interceptTitles && t.title){
15585 t.removeAttribute("title");
15586 e.preventDefault();
15588 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
15591 showProc = show.defer(tm.showDelay, tm, [{
15594 width: et.getAttributeNS(ns, cfg.width),
15595 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15596 title: et.getAttributeNS(ns, cfg.title),
15597 cls: et.getAttributeNS(ns, cfg.cls)
15602 var onOut = function(e){
15603 clearTimeout(showProc);
15604 var t = e.getTarget();
15605 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15606 hideProc = setTimeout(hide, tm.hideDelay);
15610 var onMove = function(e){
15616 if(tm.trackMouse && ce){
15621 var onDown = function(e){
15622 clearTimeout(showProc);
15623 clearTimeout(hideProc);
15625 if(tm.hideOnClick){
15628 tm.enable.defer(100, tm);
15633 var getPad = function(){
15634 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15637 var show = function(o){
15641 clearTimeout(dismissProc);
15643 if(removeCls){ // in case manually hidden
15644 el.removeClass(removeCls);
15648 el.addClass(ce.cls);
15649 removeCls = ce.cls;
15652 tipTitle.update(ce.title);
15655 tipTitle.update('');
15658 el.dom.style.width = tm.maxWidth+'px';
15659 //tipBody.dom.style.width = '';
15660 tipBodyText.update(o.text);
15661 var p = getPad(), w = ce.width;
15663 var td = tipBodyText.dom;
15664 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15665 if(aw > tm.maxWidth){
15667 }else if(aw < tm.minWidth){
15673 //tipBody.setWidth(w);
15674 el.setWidth(parseInt(w, 10) + p);
15675 if(ce.autoHide === false){
15676 close.setDisplayed(true);
15681 close.setDisplayed(false);
15687 el.avoidY = xy[1]-18;
15692 el.setStyle("visibility", "visible");
15693 el.fadeIn({callback: afterShow});
15699 var afterShow = function(){
15703 if(tm.autoDismiss && ce.autoHide !== false){
15704 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15709 var hide = function(noanim){
15710 clearTimeout(dismissProc);
15711 clearTimeout(hideProc);
15713 if(el.isVisible()){
15715 if(noanim !== true && tm.animate){
15716 el.fadeOut({callback: afterHide});
15723 var afterHide = function(){
15726 el.removeClass(removeCls);
15733 * @cfg {Number} minWidth
15734 * The minimum width of the quick tip (defaults to 40)
15738 * @cfg {Number} maxWidth
15739 * The maximum width of the quick tip (defaults to 300)
15743 * @cfg {Boolean} interceptTitles
15744 * True to automatically use the element's DOM title value if available (defaults to false)
15746 interceptTitles : false,
15748 * @cfg {Boolean} trackMouse
15749 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15751 trackMouse : false,
15753 * @cfg {Boolean} hideOnClick
15754 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15756 hideOnClick : true,
15758 * @cfg {Number} showDelay
15759 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15763 * @cfg {Number} hideDelay
15764 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15768 * @cfg {Boolean} autoHide
15769 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15770 * Used in conjunction with hideDelay.
15775 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15776 * (defaults to true). Used in conjunction with autoDismissDelay.
15778 autoDismiss : true,
15781 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15783 autoDismissDelay : 5000,
15785 * @cfg {Boolean} animate
15786 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15791 * @cfg {String} title
15792 * Title text to display (defaults to ''). This can be any valid HTML markup.
15796 * @cfg {String} text
15797 * Body text to display (defaults to ''). This can be any valid HTML markup.
15801 * @cfg {String} cls
15802 * A CSS class to apply to the base quick tip element (defaults to '').
15806 * @cfg {Number} width
15807 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
15808 * minWidth or maxWidth.
15813 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
15814 * or display QuickTips in a page.
15817 tm = Roo.QuickTips;
15818 cfg = tm.tagConfig;
15820 if(!Roo.isReady){ // allow calling of init() before onReady
15821 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15824 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15825 el.fxDefaults = {stopFx: true};
15826 // maximum custom styling
15827 //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>');
15828 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>');
15829 tipTitle = el.child('h3');
15830 tipTitle.enableDisplayMode("block");
15831 tipBody = el.child('div.x-tip-bd');
15832 tipBodyText = el.child('div.x-tip-bd-inner');
15833 //bdLeft = el.child('div.x-tip-bd-left');
15834 //bdRight = el.child('div.x-tip-bd-right');
15835 close = el.child('div.x-tip-close');
15836 close.enableDisplayMode("block");
15837 close.on("click", hide);
15838 var d = Roo.get(document);
15839 d.on("mousedown", onDown);
15840 d.on("mouseover", onOver);
15841 d.on("mouseout", onOut);
15842 d.on("mousemove", onMove);
15843 esc = d.addKeyListener(27, hide);
15846 dd = el.initDD("default", null, {
15847 onDrag : function(){
15851 dd.setHandleElId(tipTitle.id);
15860 * Configures a new quick tip instance and assigns it to a target element. The following config options
15863 Property Type Description
15864 ---------- --------------------- ------------------------------------------------------------------------
15865 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
15867 * @param {Object} config The config object
15869 register : function(config){
15870 var cs = config instanceof Array ? config : arguments;
15871 for(var i = 0, len = cs.length; i < len; i++) {
15873 var target = c.target;
15875 if(target instanceof Array){
15876 for(var j = 0, jlen = target.length; j < jlen; j++){
15877 tagEls[target[j]] = c;
15880 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15887 * Removes this quick tip from its element and destroys it.
15888 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15890 unregister : function(el){
15891 delete tagEls[Roo.id(el)];
15895 * Enable this quick tip.
15897 enable : function(){
15898 if(inited && disabled){
15900 if(locks.length < 1){
15907 * Disable this quick tip.
15909 disable : function(){
15911 clearTimeout(showProc);
15912 clearTimeout(hideProc);
15913 clearTimeout(dismissProc);
15921 * Returns true if the quick tip is enabled, else false.
15923 isEnabled : function(){
15929 namespace : "roo", // was ext?? this may break..
15930 alt_namespace : "ext",
15931 attribute : "qtip",
15941 // backwards compat
15942 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15944 * Ext JS Library 1.1.1
15945 * Copyright(c) 2006-2007, Ext JS, LLC.
15947 * Originally Released Under LGPL - original licence link has changed is not relivant.
15950 * <script type="text/javascript">
15955 * @class Roo.tree.TreePanel
15956 * @extends Roo.data.Tree
15958 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15959 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15960 * @cfg {Boolean} enableDD true to enable drag and drop
15961 * @cfg {Boolean} enableDrag true to enable just drag
15962 * @cfg {Boolean} enableDrop true to enable just drop
15963 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15964 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15965 * @cfg {String} ddGroup The DD group this TreePanel belongs to
15966 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15967 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15968 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15969 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15970 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15971 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15972 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15973 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15974 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15975 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15976 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15977 * @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>
15978 * @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>
15981 * @param {String/HTMLElement/Element} el The container element
15982 * @param {Object} config
15984 Roo.tree.TreePanel = function(el, config){
15986 var loader = false;
15988 root = config.root;
15989 delete config.root;
15991 if (config.loader) {
15992 loader = config.loader;
15993 delete config.loader;
15996 Roo.apply(this, config);
15997 Roo.tree.TreePanel.superclass.constructor.call(this);
15998 this.el = Roo.get(el);
15999 this.el.addClass('x-tree');
16000 //console.log(root);
16002 this.setRootNode( Roo.factory(root, Roo.tree));
16005 this.loader = Roo.factory(loader, Roo.tree);
16008 * Read-only. The id of the container element becomes this TreePanel's id.
16010 this.id = this.el.id;
16013 * @event beforeload
16014 * Fires before a node is loaded, return false to cancel
16015 * @param {Node} node The node being loaded
16017 "beforeload" : true,
16020 * Fires when a node is loaded
16021 * @param {Node} node The node that was loaded
16025 * @event textchange
16026 * Fires when the text for a node is changed
16027 * @param {Node} node The node
16028 * @param {String} text The new text
16029 * @param {String} oldText The old text
16031 "textchange" : true,
16033 * @event beforeexpand
16034 * Fires before a node is expanded, return false to cancel.
16035 * @param {Node} node The node
16036 * @param {Boolean} deep
16037 * @param {Boolean} anim
16039 "beforeexpand" : true,
16041 * @event beforecollapse
16042 * Fires before a node is collapsed, return false to cancel.
16043 * @param {Node} node The node
16044 * @param {Boolean} deep
16045 * @param {Boolean} anim
16047 "beforecollapse" : true,
16050 * Fires when a node is expanded
16051 * @param {Node} node The node
16055 * @event disabledchange
16056 * Fires when the disabled status of a node changes
16057 * @param {Node} node The node
16058 * @param {Boolean} disabled
16060 "disabledchange" : true,
16063 * Fires when a node is collapsed
16064 * @param {Node} node The node
16068 * @event beforeclick
16069 * Fires before click processing on a node. Return false to cancel the default action.
16070 * @param {Node} node The node
16071 * @param {Roo.EventObject} e The event object
16073 "beforeclick":true,
16075 * @event checkchange
16076 * Fires when a node with a checkbox's checked property changes
16077 * @param {Node} this This node
16078 * @param {Boolean} checked
16080 "checkchange":true,
16083 * Fires when a node is clicked
16084 * @param {Node} node The node
16085 * @param {Roo.EventObject} e The event object
16090 * Fires when a node is double clicked
16091 * @param {Node} node The node
16092 * @param {Roo.EventObject} e The event object
16096 * @event contextmenu
16097 * Fires when a node is right clicked
16098 * @param {Node} node The node
16099 * @param {Roo.EventObject} e The event object
16101 "contextmenu":true,
16103 * @event beforechildrenrendered
16104 * Fires right before the child nodes for a node are rendered
16105 * @param {Node} node The node
16107 "beforechildrenrendered":true,
16110 * Fires when a node starts being dragged
16111 * @param {Roo.tree.TreePanel} this
16112 * @param {Roo.tree.TreeNode} node
16113 * @param {event} e The raw browser event
16115 "startdrag" : true,
16118 * Fires when a drag operation is complete
16119 * @param {Roo.tree.TreePanel} this
16120 * @param {Roo.tree.TreeNode} node
16121 * @param {event} e The raw browser event
16126 * Fires when a dragged node is dropped on a valid DD target
16127 * @param {Roo.tree.TreePanel} this
16128 * @param {Roo.tree.TreeNode} node
16129 * @param {DD} dd The dd it was dropped on
16130 * @param {event} e The raw browser event
16134 * @event beforenodedrop
16135 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16136 * passed to handlers has the following properties:<br />
16137 * <ul style="padding:5px;padding-left:16px;">
16138 * <li>tree - The TreePanel</li>
16139 * <li>target - The node being targeted for the drop</li>
16140 * <li>data - The drag data from the drag source</li>
16141 * <li>point - The point of the drop - append, above or below</li>
16142 * <li>source - The drag source</li>
16143 * <li>rawEvent - Raw mouse event</li>
16144 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16145 * to be inserted by setting them on this object.</li>
16146 * <li>cancel - Set this to true to cancel the drop.</li>
16148 * @param {Object} dropEvent
16150 "beforenodedrop" : true,
16153 * Fires after a DD object is dropped on a node in this tree. The dropEvent
16154 * passed to handlers has the following properties:<br />
16155 * <ul style="padding:5px;padding-left:16px;">
16156 * <li>tree - The TreePanel</li>
16157 * <li>target - The node being targeted for the drop</li>
16158 * <li>data - The drag data from the drag source</li>
16159 * <li>point - The point of the drop - append, above or below</li>
16160 * <li>source - The drag source</li>
16161 * <li>rawEvent - Raw mouse event</li>
16162 * <li>dropNode - Dropped node(s).</li>
16164 * @param {Object} dropEvent
16168 * @event nodedragover
16169 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16170 * passed to handlers has the following properties:<br />
16171 * <ul style="padding:5px;padding-left:16px;">
16172 * <li>tree - The TreePanel</li>
16173 * <li>target - The node being targeted for the drop</li>
16174 * <li>data - The drag data from the drag source</li>
16175 * <li>point - The point of the drop - append, above or below</li>
16176 * <li>source - The drag source</li>
16177 * <li>rawEvent - Raw mouse event</li>
16178 * <li>dropNode - Drop node(s) provided by the source.</li>
16179 * <li>cancel - Set this to true to signal drop not allowed.</li>
16181 * @param {Object} dragOverEvent
16183 "nodedragover" : true
16186 if(this.singleExpand){
16187 this.on("beforeexpand", this.restrictExpand, this);
16190 this.editor.tree = this;
16191 this.editor = Roo.factory(this.editor, Roo.tree);
16194 if (this.selModel) {
16195 this.selModel = Roo.factory(this.selModel, Roo.tree);
16199 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16200 rootVisible : true,
16201 animate: Roo.enableFx,
16204 hlDrop : Roo.enableFx,
16208 rendererTip: false,
16210 restrictExpand : function(node){
16211 var p = node.parentNode;
16213 if(p.expandedChild && p.expandedChild.parentNode == p){
16214 p.expandedChild.collapse();
16216 p.expandedChild = node;
16220 // private override
16221 setRootNode : function(node){
16222 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16223 if(!this.rootVisible){
16224 node.ui = new Roo.tree.RootTreeNodeUI(node);
16230 * Returns the container element for this TreePanel
16232 getEl : function(){
16237 * Returns the default TreeLoader for this TreePanel
16239 getLoader : function(){
16240 return this.loader;
16246 expandAll : function(){
16247 this.root.expand(true);
16251 * Collapse all nodes
16253 collapseAll : function(){
16254 this.root.collapse(true);
16258 * Returns the selection model used by this TreePanel
16260 getSelectionModel : function(){
16261 if(!this.selModel){
16262 this.selModel = new Roo.tree.DefaultSelectionModel();
16264 return this.selModel;
16268 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16269 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16270 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16273 getChecked : function(a, startNode){
16274 startNode = startNode || this.root;
16276 var f = function(){
16277 if(this.attributes.checked){
16278 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16281 startNode.cascade(f);
16286 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16287 * @param {String} path
16288 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16289 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16290 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16292 expandPath : function(path, attr, callback){
16293 attr = attr || "id";
16294 var keys = path.split(this.pathSeparator);
16295 var curNode = this.root;
16296 if(curNode.attributes[attr] != keys[1]){ // invalid root
16298 callback(false, null);
16303 var f = function(){
16304 if(++index == keys.length){
16306 callback(true, curNode);
16310 var c = curNode.findChild(attr, keys[index]);
16313 callback(false, curNode);
16318 c.expand(false, false, f);
16320 curNode.expand(false, false, f);
16324 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16325 * @param {String} path
16326 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16327 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16328 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16330 selectPath : function(path, attr, callback){
16331 attr = attr || "id";
16332 var keys = path.split(this.pathSeparator);
16333 var v = keys.pop();
16334 if(keys.length > 0){
16335 var f = function(success, node){
16336 if(success && node){
16337 var n = node.findChild(attr, v);
16343 }else if(callback){
16344 callback(false, n);
16348 callback(false, n);
16352 this.expandPath(keys.join(this.pathSeparator), attr, f);
16354 this.root.select();
16356 callback(true, this.root);
16361 getTreeEl : function(){
16366 * Trigger rendering of this TreePanel
16368 render : function(){
16369 if (this.innerCt) {
16370 return this; // stop it rendering more than once!!
16373 this.innerCt = this.el.createChild({tag:"ul",
16374 cls:"x-tree-root-ct " +
16375 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16377 if(this.containerScroll){
16378 Roo.dd.ScrollManager.register(this.el);
16380 if((this.enableDD || this.enableDrop) && !this.dropZone){
16382 * The dropZone used by this tree if drop is enabled
16383 * @type Roo.tree.TreeDropZone
16385 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16386 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16389 if((this.enableDD || this.enableDrag) && !this.dragZone){
16391 * The dragZone used by this tree if drag is enabled
16392 * @type Roo.tree.TreeDragZone
16394 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16395 ddGroup: this.ddGroup || "TreeDD",
16396 scroll: this.ddScroll
16399 this.getSelectionModel().init(this);
16401 Roo.log("ROOT not set in tree");
16404 this.root.render();
16405 if(!this.rootVisible){
16406 this.root.renderChildren();
16412 * Ext JS Library 1.1.1
16413 * Copyright(c) 2006-2007, Ext JS, LLC.
16415 * Originally Released Under LGPL - original licence link has changed is not relivant.
16418 * <script type="text/javascript">
16423 * @class Roo.tree.DefaultSelectionModel
16424 * @extends Roo.util.Observable
16425 * The default single selection for a TreePanel.
16426 * @param {Object} cfg Configuration
16428 Roo.tree.DefaultSelectionModel = function(cfg){
16429 this.selNode = null;
16435 * @event selectionchange
16436 * Fires when the selected node changes
16437 * @param {DefaultSelectionModel} this
16438 * @param {TreeNode} node the new selection
16440 "selectionchange" : true,
16443 * @event beforeselect
16444 * Fires before the selected node changes, return false to cancel the change
16445 * @param {DefaultSelectionModel} this
16446 * @param {TreeNode} node the new selection
16447 * @param {TreeNode} node the old selection
16449 "beforeselect" : true
16452 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16455 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16456 init : function(tree){
16458 tree.getTreeEl().on("keydown", this.onKeyDown, this);
16459 tree.on("click", this.onNodeClick, this);
16462 onNodeClick : function(node, e){
16463 if (e.ctrlKey && this.selNode == node) {
16464 this.unselect(node);
16472 * @param {TreeNode} node The node to select
16473 * @return {TreeNode} The selected node
16475 select : function(node){
16476 var last = this.selNode;
16477 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16479 last.ui.onSelectedChange(false);
16481 this.selNode = node;
16482 node.ui.onSelectedChange(true);
16483 this.fireEvent("selectionchange", this, node, last);
16490 * @param {TreeNode} node The node to unselect
16492 unselect : function(node){
16493 if(this.selNode == node){
16494 this.clearSelections();
16499 * Clear all selections
16501 clearSelections : function(){
16502 var n = this.selNode;
16504 n.ui.onSelectedChange(false);
16505 this.selNode = null;
16506 this.fireEvent("selectionchange", this, null);
16512 * Get the selected node
16513 * @return {TreeNode} The selected node
16515 getSelectedNode : function(){
16516 return this.selNode;
16520 * Returns true if the node is selected
16521 * @param {TreeNode} node The node to check
16522 * @return {Boolean}
16524 isSelected : function(node){
16525 return this.selNode == node;
16529 * Selects the node above the selected node in the tree, intelligently walking the nodes
16530 * @return TreeNode The new selection
16532 selectPrevious : function(){
16533 var s = this.selNode || this.lastSelNode;
16537 var ps = s.previousSibling;
16539 if(!ps.isExpanded() || ps.childNodes.length < 1){
16540 return this.select(ps);
16542 var lc = ps.lastChild;
16543 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16546 return this.select(lc);
16548 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16549 return this.select(s.parentNode);
16555 * Selects the node above the selected node in the tree, intelligently walking the nodes
16556 * @return TreeNode The new selection
16558 selectNext : function(){
16559 var s = this.selNode || this.lastSelNode;
16563 if(s.firstChild && s.isExpanded()){
16564 return this.select(s.firstChild);
16565 }else if(s.nextSibling){
16566 return this.select(s.nextSibling);
16567 }else if(s.parentNode){
16569 s.parentNode.bubble(function(){
16570 if(this.nextSibling){
16571 newS = this.getOwnerTree().selModel.select(this.nextSibling);
16580 onKeyDown : function(e){
16581 var s = this.selNode || this.lastSelNode;
16582 // undesirable, but required
16587 var k = e.getKey();
16595 this.selectPrevious();
16598 e.preventDefault();
16599 if(s.hasChildNodes()){
16600 if(!s.isExpanded()){
16602 }else if(s.firstChild){
16603 this.select(s.firstChild, e);
16608 e.preventDefault();
16609 if(s.hasChildNodes() && s.isExpanded()){
16611 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16612 this.select(s.parentNode, e);
16620 * @class Roo.tree.MultiSelectionModel
16621 * @extends Roo.util.Observable
16622 * Multi selection for a TreePanel.
16623 * @param {Object} cfg Configuration
16625 Roo.tree.MultiSelectionModel = function(){
16626 this.selNodes = [];
16630 * @event selectionchange
16631 * Fires when the selected nodes change
16632 * @param {MultiSelectionModel} this
16633 * @param {Array} nodes Array of the selected nodes
16635 "selectionchange" : true
16637 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16641 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16642 init : function(tree){
16644 tree.getTreeEl().on("keydown", this.onKeyDown, this);
16645 tree.on("click", this.onNodeClick, this);
16648 onNodeClick : function(node, e){
16649 this.select(node, e, e.ctrlKey);
16654 * @param {TreeNode} node The node to select
16655 * @param {EventObject} e (optional) An event associated with the selection
16656 * @param {Boolean} keepExisting True to retain existing selections
16657 * @return {TreeNode} The selected node
16659 select : function(node, e, keepExisting){
16660 if(keepExisting !== true){
16661 this.clearSelections(true);
16663 if(this.isSelected(node)){
16664 this.lastSelNode = node;
16667 this.selNodes.push(node);
16668 this.selMap[node.id] = node;
16669 this.lastSelNode = node;
16670 node.ui.onSelectedChange(true);
16671 this.fireEvent("selectionchange", this, this.selNodes);
16677 * @param {TreeNode} node The node to unselect
16679 unselect : function(node){
16680 if(this.selMap[node.id]){
16681 node.ui.onSelectedChange(false);
16682 var sn = this.selNodes;
16685 index = sn.indexOf(node);
16687 for(var i = 0, len = sn.length; i < len; i++){
16695 this.selNodes.splice(index, 1);
16697 delete this.selMap[node.id];
16698 this.fireEvent("selectionchange", this, this.selNodes);
16703 * Clear all selections
16705 clearSelections : function(suppressEvent){
16706 var sn = this.selNodes;
16708 for(var i = 0, len = sn.length; i < len; i++){
16709 sn[i].ui.onSelectedChange(false);
16711 this.selNodes = [];
16713 if(suppressEvent !== true){
16714 this.fireEvent("selectionchange", this, this.selNodes);
16720 * Returns true if the node is selected
16721 * @param {TreeNode} node The node to check
16722 * @return {Boolean}
16724 isSelected : function(node){
16725 return this.selMap[node.id] ? true : false;
16729 * Returns an array of the selected nodes
16732 getSelectedNodes : function(){
16733 return this.selNodes;
16736 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16738 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16740 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16743 * Ext JS Library 1.1.1
16744 * Copyright(c) 2006-2007, Ext JS, LLC.
16746 * Originally Released Under LGPL - original licence link has changed is not relivant.
16749 * <script type="text/javascript">
16753 * @class Roo.tree.TreeNode
16754 * @extends Roo.data.Node
16755 * @cfg {String} text The text for this node
16756 * @cfg {Boolean} expanded true to start the node expanded
16757 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16758 * @cfg {Boolean} allowDrop false if this node cannot be drop on
16759 * @cfg {Boolean} disabled true to start the node disabled
16760 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16761 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16762 * @cfg {String} cls A css class to be added to the node
16763 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16764 * @cfg {String} href URL of the link used for the node (defaults to #)
16765 * @cfg {String} hrefTarget target frame for the link
16766 * @cfg {String} qtip An Ext QuickTip for the node
16767 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16768 * @cfg {Boolean} singleClickExpand True for single click expand on this node
16769 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16770 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16771 * (defaults to undefined with no checkbox rendered)
16773 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16775 Roo.tree.TreeNode = function(attributes){
16776 attributes = attributes || {};
16777 if(typeof attributes == "string"){
16778 attributes = {text: attributes};
16780 this.childrenRendered = false;
16781 this.rendered = false;
16782 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16783 this.expanded = attributes.expanded === true;
16784 this.isTarget = attributes.isTarget !== false;
16785 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16786 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16789 * Read-only. The text for this node. To change it use setText().
16792 this.text = attributes.text;
16794 * True if this node is disabled.
16797 this.disabled = attributes.disabled === true;
16801 * @event textchange
16802 * Fires when the text for this node is changed
16803 * @param {Node} this This node
16804 * @param {String} text The new text
16805 * @param {String} oldText The old text
16807 "textchange" : true,
16809 * @event beforeexpand
16810 * Fires before this node is expanded, return false to cancel.
16811 * @param {Node} this This node
16812 * @param {Boolean} deep
16813 * @param {Boolean} anim
16815 "beforeexpand" : true,
16817 * @event beforecollapse
16818 * Fires before this node is collapsed, return false to cancel.
16819 * @param {Node} this This node
16820 * @param {Boolean} deep
16821 * @param {Boolean} anim
16823 "beforecollapse" : true,
16826 * Fires when this node is expanded
16827 * @param {Node} this This node
16831 * @event disabledchange
16832 * Fires when the disabled status of this node changes
16833 * @param {Node} this This node
16834 * @param {Boolean} disabled
16836 "disabledchange" : true,
16839 * Fires when this node is collapsed
16840 * @param {Node} this This node
16844 * @event beforeclick
16845 * Fires before click processing. Return false to cancel the default action.
16846 * @param {Node} this This node
16847 * @param {Roo.EventObject} e The event object
16849 "beforeclick":true,
16851 * @event checkchange
16852 * Fires when a node with a checkbox's checked property changes
16853 * @param {Node} this This node
16854 * @param {Boolean} checked
16856 "checkchange":true,
16859 * Fires when this node is clicked
16860 * @param {Node} this This node
16861 * @param {Roo.EventObject} e The event object
16866 * Fires when this node is double clicked
16867 * @param {Node} this This node
16868 * @param {Roo.EventObject} e The event object
16872 * @event contextmenu
16873 * Fires when this node is right clicked
16874 * @param {Node} this This node
16875 * @param {Roo.EventObject} e The event object
16877 "contextmenu":true,
16879 * @event beforechildrenrendered
16880 * Fires right before the child nodes for this node are rendered
16881 * @param {Node} this This node
16883 "beforechildrenrendered":true
16886 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16889 * Read-only. The UI for this node
16892 this.ui = new uiClass(this);
16894 // finally support items[]
16895 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16900 Roo.each(this.attributes.items, function(c) {
16901 this.appendChild(Roo.factory(c,Roo.Tree));
16903 delete this.attributes.items;
16908 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16909 preventHScroll: true,
16911 * Returns true if this node is expanded
16912 * @return {Boolean}
16914 isExpanded : function(){
16915 return this.expanded;
16919 * Returns the UI object for this node
16920 * @return {TreeNodeUI}
16922 getUI : function(){
16926 // private override
16927 setFirstChild : function(node){
16928 var of = this.firstChild;
16929 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16930 if(this.childrenRendered && of && node != of){
16931 of.renderIndent(true, true);
16934 this.renderIndent(true, true);
16938 // private override
16939 setLastChild : function(node){
16940 var ol = this.lastChild;
16941 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16942 if(this.childrenRendered && ol && node != ol){
16943 ol.renderIndent(true, true);
16946 this.renderIndent(true, true);
16950 // these methods are overridden to provide lazy rendering support
16951 // private override
16952 appendChild : function()
16954 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16955 if(node && this.childrenRendered){
16958 this.ui.updateExpandIcon();
16962 // private override
16963 removeChild : function(node){
16964 this.ownerTree.getSelectionModel().unselect(node);
16965 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16966 // if it's been rendered remove dom node
16967 if(this.childrenRendered){
16970 if(this.childNodes.length < 1){
16971 this.collapse(false, false);
16973 this.ui.updateExpandIcon();
16975 if(!this.firstChild) {
16976 this.childrenRendered = false;
16981 // private override
16982 insertBefore : function(node, refNode){
16983 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16984 if(newNode && refNode && this.childrenRendered){
16987 this.ui.updateExpandIcon();
16992 * Sets the text for this node
16993 * @param {String} text
16995 setText : function(text){
16996 var oldText = this.text;
16998 this.attributes.text = text;
16999 if(this.rendered){ // event without subscribing
17000 this.ui.onTextChange(this, text, oldText);
17002 this.fireEvent("textchange", this, text, oldText);
17006 * Triggers selection of this node
17008 select : function(){
17009 this.getOwnerTree().getSelectionModel().select(this);
17013 * Triggers deselection of this node
17015 unselect : function(){
17016 this.getOwnerTree().getSelectionModel().unselect(this);
17020 * Returns true if this node is selected
17021 * @return {Boolean}
17023 isSelected : function(){
17024 return this.getOwnerTree().getSelectionModel().isSelected(this);
17028 * Expand this node.
17029 * @param {Boolean} deep (optional) True to expand all children as well
17030 * @param {Boolean} anim (optional) false to cancel the default animation
17031 * @param {Function} callback (optional) A callback to be called when
17032 * expanding this node completes (does not wait for deep expand to complete).
17033 * Called with 1 parameter, this node.
17035 expand : function(deep, anim, callback){
17036 if(!this.expanded){
17037 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17040 if(!this.childrenRendered){
17041 this.renderChildren();
17043 this.expanded = true;
17044 if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17045 this.ui.animExpand(function(){
17046 this.fireEvent("expand", this);
17047 if(typeof callback == "function"){
17051 this.expandChildNodes(true);
17053 }.createDelegate(this));
17057 this.fireEvent("expand", this);
17058 if(typeof callback == "function"){
17063 if(typeof callback == "function"){
17068 this.expandChildNodes(true);
17072 isHiddenRoot : function(){
17073 return this.isRoot && !this.getOwnerTree().rootVisible;
17077 * Collapse this node.
17078 * @param {Boolean} deep (optional) True to collapse all children as well
17079 * @param {Boolean} anim (optional) false to cancel the default animation
17081 collapse : function(deep, anim){
17082 if(this.expanded && !this.isHiddenRoot()){
17083 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17086 this.expanded = false;
17087 if((this.getOwnerTree().animate && anim !== false) || anim){
17088 this.ui.animCollapse(function(){
17089 this.fireEvent("collapse", this);
17091 this.collapseChildNodes(true);
17093 }.createDelegate(this));
17096 this.ui.collapse();
17097 this.fireEvent("collapse", this);
17101 var cs = this.childNodes;
17102 for(var i = 0, len = cs.length; i < len; i++) {
17103 cs[i].collapse(true, false);
17109 delayedExpand : function(delay){
17110 if(!this.expandProcId){
17111 this.expandProcId = this.expand.defer(delay, this);
17116 cancelExpand : function(){
17117 if(this.expandProcId){
17118 clearTimeout(this.expandProcId);
17120 this.expandProcId = false;
17124 * Toggles expanded/collapsed state of the node
17126 toggle : function(){
17135 * Ensures all parent nodes are expanded
17137 ensureVisible : function(callback){
17138 var tree = this.getOwnerTree();
17139 tree.expandPath(this.parentNode.getPath(), false, function(){
17140 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17141 Roo.callback(callback);
17142 }.createDelegate(this));
17146 * Expand all child nodes
17147 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17149 expandChildNodes : function(deep){
17150 var cs = this.childNodes;
17151 for(var i = 0, len = cs.length; i < len; i++) {
17152 cs[i].expand(deep);
17157 * Collapse all child nodes
17158 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17160 collapseChildNodes : function(deep){
17161 var cs = this.childNodes;
17162 for(var i = 0, len = cs.length; i < len; i++) {
17163 cs[i].collapse(deep);
17168 * Disables this node
17170 disable : function(){
17171 this.disabled = true;
17173 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17174 this.ui.onDisableChange(this, true);
17176 this.fireEvent("disabledchange", this, true);
17180 * Enables this node
17182 enable : function(){
17183 this.disabled = false;
17184 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17185 this.ui.onDisableChange(this, false);
17187 this.fireEvent("disabledchange", this, false);
17191 renderChildren : function(suppressEvent){
17192 if(suppressEvent !== false){
17193 this.fireEvent("beforechildrenrendered", this);
17195 var cs = this.childNodes;
17196 for(var i = 0, len = cs.length; i < len; i++){
17197 cs[i].render(true);
17199 this.childrenRendered = true;
17203 sort : function(fn, scope){
17204 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17205 if(this.childrenRendered){
17206 var cs = this.childNodes;
17207 for(var i = 0, len = cs.length; i < len; i++){
17208 cs[i].render(true);
17214 render : function(bulkRender){
17215 this.ui.render(bulkRender);
17216 if(!this.rendered){
17217 this.rendered = true;
17219 this.expanded = false;
17220 this.expand(false, false);
17226 renderIndent : function(deep, refresh){
17228 this.ui.childIndent = null;
17230 this.ui.renderIndent();
17231 if(deep === true && this.childrenRendered){
17232 var cs = this.childNodes;
17233 for(var i = 0, len = cs.length; i < len; i++){
17234 cs[i].renderIndent(true, refresh);
17240 * Ext JS Library 1.1.1
17241 * Copyright(c) 2006-2007, Ext JS, LLC.
17243 * Originally Released Under LGPL - original licence link has changed is not relivant.
17246 * <script type="text/javascript">
17250 * @class Roo.tree.AsyncTreeNode
17251 * @extends Roo.tree.TreeNode
17252 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17254 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17256 Roo.tree.AsyncTreeNode = function(config){
17257 this.loaded = false;
17258 this.loading = false;
17259 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17261 * @event beforeload
17262 * Fires before this node is loaded, return false to cancel
17263 * @param {Node} this This node
17265 this.addEvents({'beforeload':true, 'load': true});
17268 * Fires when this node is loaded
17269 * @param {Node} this This node
17272 * The loader used by this node (defaults to using the tree's defined loader)
17277 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17278 expand : function(deep, anim, callback){
17279 if(this.loading){ // if an async load is already running, waiting til it's done
17281 var f = function(){
17282 if(!this.loading){ // done loading
17283 clearInterval(timer);
17284 this.expand(deep, anim, callback);
17286 }.createDelegate(this);
17287 timer = setInterval(f, 200);
17291 if(this.fireEvent("beforeload", this) === false){
17294 this.loading = true;
17295 this.ui.beforeLoad(this);
17296 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17298 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17302 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17306 * Returns true if this node is currently loading
17307 * @return {Boolean}
17309 isLoading : function(){
17310 return this.loading;
17313 loadComplete : function(deep, anim, callback){
17314 this.loading = false;
17315 this.loaded = true;
17316 this.ui.afterLoad(this);
17317 this.fireEvent("load", this);
17318 this.expand(deep, anim, callback);
17322 * Returns true if this node has been loaded
17323 * @return {Boolean}
17325 isLoaded : function(){
17326 return this.loaded;
17329 hasChildNodes : function(){
17330 if(!this.isLeaf() && !this.loaded){
17333 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17338 * Trigger a reload for this node
17339 * @param {Function} callback
17341 reload : function(callback){
17342 this.collapse(false, false);
17343 while(this.firstChild){
17344 this.removeChild(this.firstChild);
17346 this.childrenRendered = false;
17347 this.loaded = false;
17348 if(this.isHiddenRoot()){
17349 this.expanded = false;
17351 this.expand(false, false, callback);
17355 * Ext JS Library 1.1.1
17356 * Copyright(c) 2006-2007, Ext JS, LLC.
17358 * Originally Released Under LGPL - original licence link has changed is not relivant.
17361 * <script type="text/javascript">
17365 * @class Roo.tree.TreeNodeUI
17367 * @param {Object} node The node to render
17368 * The TreeNode UI implementation is separate from the
17369 * tree implementation. Unless you are customizing the tree UI,
17370 * you should never have to use this directly.
17372 Roo.tree.TreeNodeUI = function(node){
17374 this.rendered = false;
17375 this.animating = false;
17376 this.emptyIcon = Roo.BLANK_IMAGE_URL;
17379 Roo.tree.TreeNodeUI.prototype = {
17380 removeChild : function(node){
17382 this.ctNode.removeChild(node.ui.getEl());
17386 beforeLoad : function(){
17387 this.addClass("x-tree-node-loading");
17390 afterLoad : function(){
17391 this.removeClass("x-tree-node-loading");
17394 onTextChange : function(node, text, oldText){
17396 this.textNode.innerHTML = text;
17400 onDisableChange : function(node, state){
17401 this.disabled = state;
17403 this.addClass("x-tree-node-disabled");
17405 this.removeClass("x-tree-node-disabled");
17409 onSelectedChange : function(state){
17412 this.addClass("x-tree-selected");
17415 this.removeClass("x-tree-selected");
17419 onMove : function(tree, node, oldParent, newParent, index, refNode){
17420 this.childIndent = null;
17422 var targetNode = newParent.ui.getContainer();
17423 if(!targetNode){//target not rendered
17424 this.holder = document.createElement("div");
17425 this.holder.appendChild(this.wrap);
17428 var insertBefore = refNode ? refNode.ui.getEl() : null;
17430 targetNode.insertBefore(this.wrap, insertBefore);
17432 targetNode.appendChild(this.wrap);
17434 this.node.renderIndent(true);
17438 addClass : function(cls){
17440 Roo.fly(this.elNode).addClass(cls);
17444 removeClass : function(cls){
17446 Roo.fly(this.elNode).removeClass(cls);
17450 remove : function(){
17452 this.holder = document.createElement("div");
17453 this.holder.appendChild(this.wrap);
17457 fireEvent : function(){
17458 return this.node.fireEvent.apply(this.node, arguments);
17461 initEvents : function(){
17462 this.node.on("move", this.onMove, this);
17463 var E = Roo.EventManager;
17464 var a = this.anchor;
17466 var el = Roo.fly(a, '_treeui');
17468 if(Roo.isOpera){ // opera render bug ignores the CSS
17469 el.setStyle("text-decoration", "none");
17472 el.on("click", this.onClick, this);
17473 el.on("dblclick", this.onDblClick, this);
17476 Roo.EventManager.on(this.checkbox,
17477 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17480 el.on("contextmenu", this.onContextMenu, this);
17482 var icon = Roo.fly(this.iconNode);
17483 icon.on("click", this.onClick, this);
17484 icon.on("dblclick", this.onDblClick, this);
17485 icon.on("contextmenu", this.onContextMenu, this);
17486 E.on(this.ecNode, "click", this.ecClick, this, true);
17488 if(this.node.disabled){
17489 this.addClass("x-tree-node-disabled");
17491 if(this.node.hidden){
17492 this.addClass("x-tree-node-disabled");
17494 var ot = this.node.getOwnerTree();
17495 var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17496 if(dd && (!this.node.isRoot || ot.rootVisible)){
17497 Roo.dd.Registry.register(this.elNode, {
17499 handles: this.getDDHandles(),
17505 getDDHandles : function(){
17506 return [this.iconNode, this.textNode];
17511 this.wrap.style.display = "none";
17517 this.wrap.style.display = "";
17521 onContextMenu : function(e){
17522 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17523 e.preventDefault();
17525 this.fireEvent("contextmenu", this.node, e);
17529 onClick : function(e){
17534 if(this.fireEvent("beforeclick", this.node, e) !== false){
17535 if(!this.disabled && this.node.attributes.href){
17536 this.fireEvent("click", this.node, e);
17539 e.preventDefault();
17544 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17545 this.node.toggle();
17548 this.fireEvent("click", this.node, e);
17554 onDblClick : function(e){
17555 e.preventDefault();
17560 this.toggleCheck();
17562 if(!this.animating && this.node.hasChildNodes()){
17563 this.node.toggle();
17565 this.fireEvent("dblclick", this.node, e);
17568 onCheckChange : function(){
17569 var checked = this.checkbox.checked;
17570 this.node.attributes.checked = checked;
17571 this.fireEvent('checkchange', this.node, checked);
17574 ecClick : function(e){
17575 if(!this.animating && this.node.hasChildNodes()){
17576 this.node.toggle();
17580 startDrop : function(){
17581 this.dropping = true;
17584 // delayed drop so the click event doesn't get fired on a drop
17585 endDrop : function(){
17586 setTimeout(function(){
17587 this.dropping = false;
17588 }.createDelegate(this), 50);
17591 expand : function(){
17592 this.updateExpandIcon();
17593 this.ctNode.style.display = "";
17596 focus : function(){
17597 if(!this.node.preventHScroll){
17598 try{this.anchor.focus();
17600 }else if(!Roo.isIE){
17602 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17603 var l = noscroll.scrollLeft;
17604 this.anchor.focus();
17605 noscroll.scrollLeft = l;
17610 toggleCheck : function(value){
17611 var cb = this.checkbox;
17613 cb.checked = (value === undefined ? !cb.checked : value);
17619 this.anchor.blur();
17623 animExpand : function(callback){
17624 var ct = Roo.get(this.ctNode);
17626 if(!this.node.hasChildNodes()){
17627 this.updateExpandIcon();
17628 this.ctNode.style.display = "";
17629 Roo.callback(callback);
17632 this.animating = true;
17633 this.updateExpandIcon();
17636 callback : function(){
17637 this.animating = false;
17638 Roo.callback(callback);
17641 duration: this.node.ownerTree.duration || .25
17645 highlight : function(){
17646 var tree = this.node.getOwnerTree();
17647 Roo.fly(this.wrap).highlight(
17648 tree.hlColor || "C3DAF9",
17649 {endColor: tree.hlBaseColor}
17653 collapse : function(){
17654 this.updateExpandIcon();
17655 this.ctNode.style.display = "none";
17658 animCollapse : function(callback){
17659 var ct = Roo.get(this.ctNode);
17660 ct.enableDisplayMode('block');
17663 this.animating = true;
17664 this.updateExpandIcon();
17667 callback : function(){
17668 this.animating = false;
17669 Roo.callback(callback);
17672 duration: this.node.ownerTree.duration || .25
17676 getContainer : function(){
17677 return this.ctNode;
17680 getEl : function(){
17684 appendDDGhost : function(ghostNode){
17685 ghostNode.appendChild(this.elNode.cloneNode(true));
17688 getDDRepairXY : function(){
17689 return Roo.lib.Dom.getXY(this.iconNode);
17692 onRender : function(){
17696 render : function(bulkRender){
17697 var n = this.node, a = n.attributes;
17698 var targetNode = n.parentNode ?
17699 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17701 if(!this.rendered){
17702 this.rendered = true;
17704 this.renderElements(n, a, targetNode, bulkRender);
17707 if(this.textNode.setAttributeNS){
17708 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17710 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17713 this.textNode.setAttribute("ext:qtip", a.qtip);
17715 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17718 }else if(a.qtipCfg){
17719 a.qtipCfg.target = Roo.id(this.textNode);
17720 Roo.QuickTips.register(a.qtipCfg);
17723 if(!this.node.expanded){
17724 this.updateExpandIcon();
17727 if(bulkRender === true) {
17728 targetNode.appendChild(this.wrap);
17733 renderElements : function(n, a, targetNode, bulkRender)
17735 // add some indent caching, this helps performance when rendering a large tree
17736 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17737 var t = n.getOwnerTree();
17738 var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17739 if (typeof(n.attributes.html) != 'undefined') {
17740 txt = n.attributes.html;
17742 var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17743 var cb = typeof a.checked == 'boolean';
17744 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17745 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17746 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17747 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17748 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17749 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17750 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17751 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
17752 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17753 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17756 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17757 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17758 n.nextSibling.ui.getEl(), buf.join(""));
17760 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17763 this.elNode = this.wrap.childNodes[0];
17764 this.ctNode = this.wrap.childNodes[1];
17765 var cs = this.elNode.childNodes;
17766 this.indentNode = cs[0];
17767 this.ecNode = cs[1];
17768 this.iconNode = cs[2];
17771 this.checkbox = cs[3];
17774 this.anchor = cs[index];
17775 this.textNode = cs[index].firstChild;
17778 getAnchor : function(){
17779 return this.anchor;
17782 getTextEl : function(){
17783 return this.textNode;
17786 getIconEl : function(){
17787 return this.iconNode;
17790 isChecked : function(){
17791 return this.checkbox ? this.checkbox.checked : false;
17794 updateExpandIcon : function(){
17796 var n = this.node, c1, c2;
17797 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17798 var hasChild = n.hasChildNodes();
17802 c1 = "x-tree-node-collapsed";
17803 c2 = "x-tree-node-expanded";
17806 c1 = "x-tree-node-expanded";
17807 c2 = "x-tree-node-collapsed";
17810 this.removeClass("x-tree-node-leaf");
17811 this.wasLeaf = false;
17813 if(this.c1 != c1 || this.c2 != c2){
17814 Roo.fly(this.elNode).replaceClass(c1, c2);
17815 this.c1 = c1; this.c2 = c2;
17818 // this changes non-leafs into leafs if they have no children.
17819 // it's not very rational behaviour..
17821 if(!this.wasLeaf && this.node.leaf){
17822 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17825 this.wasLeaf = true;
17828 var ecc = "x-tree-ec-icon "+cls;
17829 if(this.ecc != ecc){
17830 this.ecNode.className = ecc;
17836 getChildIndent : function(){
17837 if(!this.childIndent){
17841 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17843 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17845 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17850 this.childIndent = buf.join("");
17852 return this.childIndent;
17855 renderIndent : function(){
17858 var p = this.node.parentNode;
17860 indent = p.ui.getChildIndent();
17862 if(this.indentMarkup != indent){ // don't rerender if not required
17863 this.indentNode.innerHTML = indent;
17864 this.indentMarkup = indent;
17866 this.updateExpandIcon();
17871 Roo.tree.RootTreeNodeUI = function(){
17872 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17874 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17875 render : function(){
17876 if(!this.rendered){
17877 var targetNode = this.node.ownerTree.innerCt.dom;
17878 this.node.expanded = true;
17879 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17880 this.wrap = this.ctNode = targetNode.firstChild;
17883 collapse : function(){
17885 expand : function(){
17889 * Ext JS Library 1.1.1
17890 * Copyright(c) 2006-2007, Ext JS, LLC.
17892 * Originally Released Under LGPL - original licence link has changed is not relivant.
17895 * <script type="text/javascript">
17898 * @class Roo.tree.TreeLoader
17899 * @extends Roo.util.Observable
17900 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17901 * nodes from a specified URL. The response must be a javascript Array definition
17902 * who's elements are node definition objects. eg:
17907 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17908 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17915 * The old style respose with just an array is still supported, but not recommended.
17918 * A server request is sent, and child nodes are loaded only when a node is expanded.
17919 * The loading node's id is passed to the server under the parameter name "node" to
17920 * enable the server to produce the correct child nodes.
17922 * To pass extra parameters, an event handler may be attached to the "beforeload"
17923 * event, and the parameters specified in the TreeLoader's baseParams property:
17925 myTreeLoader.on("beforeload", function(treeLoader, node) {
17926 this.baseParams.category = node.attributes.category;
17929 * This would pass an HTTP parameter called "category" to the server containing
17930 * the value of the Node's "category" attribute.
17932 * Creates a new Treeloader.
17933 * @param {Object} config A config object containing config properties.
17935 Roo.tree.TreeLoader = function(config){
17936 this.baseParams = {};
17937 this.requestMethod = "POST";
17938 Roo.apply(this, config);
17943 * @event beforeload
17944 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17945 * @param {Object} This TreeLoader object.
17946 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17947 * @param {Object} callback The callback function specified in the {@link #load} call.
17952 * Fires when the node has been successfuly loaded.
17953 * @param {Object} This TreeLoader object.
17954 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17955 * @param {Object} response The response object containing the data from the server.
17959 * @event loadexception
17960 * Fires if the network request failed.
17961 * @param {Object} This TreeLoader object.
17962 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17963 * @param {Object} response The response object containing the data from the server.
17965 loadexception : true,
17968 * Fires before a node is created, enabling you to return custom Node types
17969 * @param {Object} This TreeLoader object.
17970 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17975 Roo.tree.TreeLoader.superclass.constructor.call(this);
17978 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17980 * @cfg {String} dataUrl The URL from which to request a Json string which
17981 * specifies an array of node definition object representing the child nodes
17985 * @cfg {String} requestMethod either GET or POST
17986 * defaults to POST (due to BC)
17990 * @cfg {Object} baseParams (optional) An object containing properties which
17991 * specify HTTP parameters to be passed to each request for child nodes.
17994 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17995 * created by this loader. If the attributes sent by the server have an attribute in this object,
17996 * they take priority.
17999 * @cfg {Object} uiProviders (optional) An object containing properties which
18001 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18002 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18003 * <i>uiProvider</i> attribute of a returned child node is a string rather
18004 * than a reference to a TreeNodeUI implementation, this that string value
18005 * is used as a property name in the uiProviders object. You can define the provider named
18006 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18011 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18012 * child nodes before loading.
18014 clearOnLoad : true,
18017 * @cfg {String} root (optional) Default to false. Use this to read data from an object
18018 * property on loading, rather than expecting an array. (eg. more compatible to a standard
18019 * Grid query { data : [ .....] }
18024 * @cfg {String} queryParam (optional)
18025 * Name of the query as it will be passed on the querystring (defaults to 'node')
18026 * eg. the request will be ?node=[id]
18033 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18034 * This is called automatically when a node is expanded, but may be used to reload
18035 * a node (or append new children if the {@link #clearOnLoad} option is false.)
18036 * @param {Roo.tree.TreeNode} node
18037 * @param {Function} callback
18039 load : function(node, callback){
18040 if(this.clearOnLoad){
18041 while(node.firstChild){
18042 node.removeChild(node.firstChild);
18045 if(node.attributes.children){ // preloaded json children
18046 var cs = node.attributes.children;
18047 for(var i = 0, len = cs.length; i < len; i++){
18048 node.appendChild(this.createNode(cs[i]));
18050 if(typeof callback == "function"){
18053 }else if(this.dataUrl){
18054 this.requestData(node, callback);
18058 getParams: function(node){
18059 var buf = [], bp = this.baseParams;
18060 for(var key in bp){
18061 if(typeof bp[key] != "function"){
18062 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18065 var n = this.queryParam === false ? 'node' : this.queryParam;
18066 buf.push(n + "=", encodeURIComponent(node.id));
18067 return buf.join("");
18070 requestData : function(node, callback){
18071 if(this.fireEvent("beforeload", this, node, callback) !== false){
18072 this.transId = Roo.Ajax.request({
18073 method:this.requestMethod,
18074 url: this.dataUrl||this.url,
18075 success: this.handleResponse,
18076 failure: this.handleFailure,
18078 argument: {callback: callback, node: node},
18079 params: this.getParams(node)
18082 // if the load is cancelled, make sure we notify
18083 // the node that we are done
18084 if(typeof callback == "function"){
18090 isLoading : function(){
18091 return this.transId ? true : false;
18094 abort : function(){
18095 if(this.isLoading()){
18096 Roo.Ajax.abort(this.transId);
18101 createNode : function(attr)
18103 // apply baseAttrs, nice idea Corey!
18104 if(this.baseAttrs){
18105 Roo.applyIf(attr, this.baseAttrs);
18107 if(this.applyLoader !== false){
18108 attr.loader = this;
18110 // uiProvider = depreciated..
18112 if(typeof(attr.uiProvider) == 'string'){
18113 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
18114 /** eval:var:attr */ eval(attr.uiProvider);
18116 if(typeof(this.uiProviders['default']) != 'undefined') {
18117 attr.uiProvider = this.uiProviders['default'];
18120 this.fireEvent('create', this, attr);
18122 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18124 new Roo.tree.TreeNode(attr) :
18125 new Roo.tree.AsyncTreeNode(attr));
18128 processResponse : function(response, node, callback)
18130 var json = response.responseText;
18133 var o = Roo.decode(json);
18135 if (this.root === false && typeof(o.success) != undefined) {
18136 this.root = 'data'; // the default behaviour for list like data..
18139 if (this.root !== false && !o.success) {
18140 // it's a failure condition.
18141 var a = response.argument;
18142 this.fireEvent("loadexception", this, a.node, response);
18143 Roo.log("Load failed - should have a handler really");
18149 if (this.root !== false) {
18153 for(var i = 0, len = o.length; i < len; i++){
18154 var n = this.createNode(o[i]);
18156 node.appendChild(n);
18159 if(typeof callback == "function"){
18160 callback(this, node);
18163 this.handleFailure(response);
18167 handleResponse : function(response){
18168 this.transId = false;
18169 var a = response.argument;
18170 this.processResponse(response, a.node, a.callback);
18171 this.fireEvent("load", this, a.node, response);
18174 handleFailure : function(response)
18176 // should handle failure better..
18177 this.transId = false;
18178 var a = response.argument;
18179 this.fireEvent("loadexception", this, a.node, response);
18180 if(typeof a.callback == "function"){
18181 a.callback(this, a.node);
18186 * Ext JS Library 1.1.1
18187 * Copyright(c) 2006-2007, Ext JS, LLC.
18189 * Originally Released Under LGPL - original licence link has changed is not relivant.
18192 * <script type="text/javascript">
18196 * @class Roo.tree.TreeFilter
18197 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18198 * @param {TreePanel} tree
18199 * @param {Object} config (optional)
18201 Roo.tree.TreeFilter = function(tree, config){
18203 this.filtered = {};
18204 Roo.apply(this, config);
18207 Roo.tree.TreeFilter.prototype = {
18214 * Filter the data by a specific attribute.
18215 * @param {String/RegExp} value Either string that the attribute value
18216 * should start with or a RegExp to test against the attribute
18217 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18218 * @param {TreeNode} startNode (optional) The node to start the filter at.
18220 filter : function(value, attr, startNode){
18221 attr = attr || "text";
18223 if(typeof value == "string"){
18224 var vlen = value.length;
18225 // auto clear empty filter
18226 if(vlen == 0 && this.clearBlank){
18230 value = value.toLowerCase();
18232 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18234 }else if(value.exec){ // regex?
18236 return value.test(n.attributes[attr]);
18239 throw 'Illegal filter type, must be string or regex';
18241 this.filterBy(f, null, startNode);
18245 * Filter by a function. The passed function will be called with each
18246 * node in the tree (or from the startNode). If the function returns true, the node is kept
18247 * otherwise it is filtered. If a node is filtered, its children are also filtered.
18248 * @param {Function} fn The filter function
18249 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18251 filterBy : function(fn, scope, startNode){
18252 startNode = startNode || this.tree.root;
18253 if(this.autoClear){
18256 var af = this.filtered, rv = this.reverse;
18257 var f = function(n){
18258 if(n == startNode){
18264 var m = fn.call(scope || n, n);
18272 startNode.cascade(f);
18275 if(typeof id != "function"){
18277 if(n && n.parentNode){
18278 n.parentNode.removeChild(n);
18286 * Clears the current filter. Note: with the "remove" option
18287 * set a filter cannot be cleared.
18289 clear : function(){
18291 var af = this.filtered;
18293 if(typeof id != "function"){
18300 this.filtered = {};
18305 * Ext JS Library 1.1.1
18306 * Copyright(c) 2006-2007, Ext JS, LLC.
18308 * Originally Released Under LGPL - original licence link has changed is not relivant.
18311 * <script type="text/javascript">
18316 * @class Roo.tree.TreeSorter
18317 * Provides sorting of nodes in a TreePanel
18319 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18320 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18321 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18322 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18323 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18324 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18326 * @param {TreePanel} tree
18327 * @param {Object} config
18329 Roo.tree.TreeSorter = function(tree, config){
18330 Roo.apply(this, config);
18331 tree.on("beforechildrenrendered", this.doSort, this);
18332 tree.on("append", this.updateSort, this);
18333 tree.on("insert", this.updateSort, this);
18335 var dsc = this.dir && this.dir.toLowerCase() == "desc";
18336 var p = this.property || "text";
18337 var sortType = this.sortType;
18338 var fs = this.folderSort;
18339 var cs = this.caseSensitive === true;
18340 var leafAttr = this.leafAttr || 'leaf';
18342 this.sortFn = function(n1, n2){
18344 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18347 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18351 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18352 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18354 return dsc ? +1 : -1;
18356 return dsc ? -1 : +1;
18363 Roo.tree.TreeSorter.prototype = {
18364 doSort : function(node){
18365 node.sort(this.sortFn);
18368 compareNodes : function(n1, n2){
18369 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18372 updateSort : function(tree, node){
18373 if(node.childrenRendered){
18374 this.doSort.defer(1, this, [node]);
18379 * Ext JS Library 1.1.1
18380 * Copyright(c) 2006-2007, Ext JS, LLC.
18382 * Originally Released Under LGPL - original licence link has changed is not relivant.
18385 * <script type="text/javascript">
18388 if(Roo.dd.DropZone){
18390 Roo.tree.TreeDropZone = function(tree, config){
18391 this.allowParentInsert = false;
18392 this.allowContainerDrop = false;
18393 this.appendOnly = false;
18394 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18396 this.lastInsertClass = "x-tree-no-status";
18397 this.dragOverData = {};
18400 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18401 ddGroup : "TreeDD",
18404 expandDelay : 1000,
18406 expandNode : function(node){
18407 if(node.hasChildNodes() && !node.isExpanded()){
18408 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18412 queueExpand : function(node){
18413 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18416 cancelExpand : function(){
18417 if(this.expandProcId){
18418 clearTimeout(this.expandProcId);
18419 this.expandProcId = false;
18423 isValidDropPoint : function(n, pt, dd, e, data){
18424 if(!n || !data){ return false; }
18425 var targetNode = n.node;
18426 var dropNode = data.node;
18427 // default drop rules
18428 if(!(targetNode && targetNode.isTarget && pt)){
18431 if(pt == "append" && targetNode.allowChildren === false){
18434 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18437 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18440 // reuse the object
18441 var overEvent = this.dragOverData;
18442 overEvent.tree = this.tree;
18443 overEvent.target = targetNode;
18444 overEvent.data = data;
18445 overEvent.point = pt;
18446 overEvent.source = dd;
18447 overEvent.rawEvent = e;
18448 overEvent.dropNode = dropNode;
18449 overEvent.cancel = false;
18450 var result = this.tree.fireEvent("nodedragover", overEvent);
18451 return overEvent.cancel === false && result !== false;
18454 getDropPoint : function(e, n, dd)
18458 return tn.allowChildren !== false ? "append" : false; // always append for root
18460 var dragEl = n.ddel;
18461 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18462 var y = Roo.lib.Event.getPageY(e);
18463 //var noAppend = tn.allowChildren === false || tn.isLeaf();
18465 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18466 var noAppend = tn.allowChildren === false;
18467 if(this.appendOnly || tn.parentNode.allowChildren === false){
18468 return noAppend ? false : "append";
18470 var noBelow = false;
18471 if(!this.allowParentInsert){
18472 noBelow = tn.hasChildNodes() && tn.isExpanded();
18474 var q = (b - t) / (noAppend ? 2 : 3);
18475 if(y >= t && y < (t + q)){
18477 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18484 onNodeEnter : function(n, dd, e, data)
18486 this.cancelExpand();
18489 onNodeOver : function(n, dd, e, data)
18492 var pt = this.getDropPoint(e, n, dd);
18495 // auto node expand check
18496 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18497 this.queueExpand(node);
18498 }else if(pt != "append"){
18499 this.cancelExpand();
18502 // set the insert point style on the target node
18503 var returnCls = this.dropNotAllowed;
18504 if(this.isValidDropPoint(n, pt, dd, e, data)){
18509 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18510 cls = "x-tree-drag-insert-above";
18511 }else if(pt == "below"){
18512 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18513 cls = "x-tree-drag-insert-below";
18515 returnCls = "x-tree-drop-ok-append";
18516 cls = "x-tree-drag-append";
18518 if(this.lastInsertClass != cls){
18519 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18520 this.lastInsertClass = cls;
18527 onNodeOut : function(n, dd, e, data){
18529 this.cancelExpand();
18530 this.removeDropIndicators(n);
18533 onNodeDrop : function(n, dd, e, data){
18534 var point = this.getDropPoint(e, n, dd);
18535 var targetNode = n.node;
18536 targetNode.ui.startDrop();
18537 if(!this.isValidDropPoint(n, point, dd, e, data)){
18538 targetNode.ui.endDrop();
18541 // first try to find the drop node
18542 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18545 target: targetNode,
18550 dropNode: dropNode,
18553 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18554 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18555 targetNode.ui.endDrop();
18558 // allow target changing
18559 targetNode = dropEvent.target;
18560 if(point == "append" && !targetNode.isExpanded()){
18561 targetNode.expand(false, null, function(){
18562 this.completeDrop(dropEvent);
18563 }.createDelegate(this));
18565 this.completeDrop(dropEvent);
18570 completeDrop : function(de){
18571 var ns = de.dropNode, p = de.point, t = de.target;
18572 if(!(ns instanceof Array)){
18576 for(var i = 0, len = ns.length; i < len; i++){
18579 t.parentNode.insertBefore(n, t);
18580 }else if(p == "below"){
18581 t.parentNode.insertBefore(n, t.nextSibling);
18587 if(this.tree.hlDrop){
18591 this.tree.fireEvent("nodedrop", de);
18594 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18595 if(this.tree.hlDrop){
18596 dropNode.ui.focus();
18597 dropNode.ui.highlight();
18599 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18602 getTree : function(){
18606 removeDropIndicators : function(n){
18609 Roo.fly(el).removeClass([
18610 "x-tree-drag-insert-above",
18611 "x-tree-drag-insert-below",
18612 "x-tree-drag-append"]);
18613 this.lastInsertClass = "_noclass";
18617 beforeDragDrop : function(target, e, id){
18618 this.cancelExpand();
18622 afterRepair : function(data){
18623 if(data && Roo.enableFx){
18624 data.node.ui.highlight();
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">
18644 if(Roo.dd.DragZone){
18645 Roo.tree.TreeDragZone = function(tree, config){
18646 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18650 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18651 ddGroup : "TreeDD",
18653 onBeforeDrag : function(data, e){
18655 return n && n.draggable && !n.disabled;
18659 onInitDrag : function(e){
18660 var data = this.dragData;
18661 this.tree.getSelectionModel().select(data.node);
18662 this.proxy.update("");
18663 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18664 this.tree.fireEvent("startdrag", this.tree, data.node, e);
18667 getRepairXY : function(e, data){
18668 return data.node.ui.getDDRepairXY();
18671 onEndDrag : function(data, e){
18672 this.tree.fireEvent("enddrag", this.tree, data.node, e);
18677 onValidDrop : function(dd, e, id){
18678 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18682 beforeInvalidDrop : function(e, id){
18683 // this scrolls the original position back into view
18684 var sm = this.tree.getSelectionModel();
18685 sm.clearSelections();
18686 sm.select(this.dragData.node);
18691 * Ext JS Library 1.1.1
18692 * Copyright(c) 2006-2007, Ext JS, LLC.
18694 * Originally Released Under LGPL - original licence link has changed is not relivant.
18697 * <script type="text/javascript">
18700 * @class Roo.tree.TreeEditor
18701 * @extends Roo.Editor
18702 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
18703 * as the editor field.
18705 * @param {Object} config (used to be the tree panel.)
18706 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18708 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18709 * @cfg {Roo.form.TextField|Object} field The field configuration
18713 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18716 if (oldconfig) { // old style..
18717 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18720 tree = config.tree;
18721 config.field = config.field || {};
18722 config.field.xtype = 'TextField';
18723 field = Roo.factory(config.field, Roo.form);
18725 config = config || {};
18730 * @event beforenodeedit
18731 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
18732 * false from the handler of this event.
18733 * @param {Editor} this
18734 * @param {Roo.tree.Node} node
18736 "beforenodeedit" : true
18740 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18744 tree.on('beforeclick', this.beforeNodeClick, this);
18745 tree.getTreeEl().on('mousedown', this.hide, this);
18746 this.on('complete', this.updateNode, this);
18747 this.on('beforestartedit', this.fitToTree, this);
18748 this.on('startedit', this.bindScroll, this, {delay:10});
18749 this.on('specialkey', this.onSpecialKey, this);
18752 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18754 * @cfg {String} alignment
18755 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18761 * @cfg {Boolean} hideEl
18762 * True to hide the bound element while the editor is displayed (defaults to false)
18766 * @cfg {String} cls
18767 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18769 cls: "x-small-editor x-tree-editor",
18771 * @cfg {Boolean} shim
18772 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18778 * @cfg {Number} maxWidth
18779 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
18780 * the containing tree element's size, it will be automatically limited for you to the container width, taking
18781 * scroll and client offsets into account prior to each edit.
18788 fitToTree : function(ed, el){
18789 var td = this.tree.getTreeEl().dom, nd = el.dom;
18790 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
18791 td.scrollLeft = nd.offsetLeft;
18795 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18796 this.setSize(w, '');
18798 return this.fireEvent('beforenodeedit', this, this.editNode);
18803 triggerEdit : function(node){
18804 this.completeEdit();
18805 this.editNode = node;
18806 this.startEdit(node.ui.textNode, node.text);
18810 bindScroll : function(){
18811 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18815 beforeNodeClick : function(node, e){
18816 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18817 this.lastClick = new Date();
18818 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18820 this.triggerEdit(node);
18827 updateNode : function(ed, value){
18828 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18829 this.editNode.setText(value);
18833 onHide : function(){
18834 Roo.tree.TreeEditor.superclass.onHide.call(this);
18836 this.editNode.ui.focus();
18841 onSpecialKey : function(field, e){
18842 var k = e.getKey();
18846 }else if(k == e.ENTER && !e.hasModifier()){
18848 this.completeEdit();
18851 });//<Script type="text/javascript">
18854 * Ext JS Library 1.1.1
18855 * Copyright(c) 2006-2007, Ext JS, LLC.
18857 * Originally Released Under LGPL - original licence link has changed is not relivant.
18860 * <script type="text/javascript">
18864 * Not documented??? - probably should be...
18867 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18868 //focus: Roo.emptyFn, // prevent odd scrolling behavior
18870 renderElements : function(n, a, targetNode, bulkRender){
18871 //consel.log("renderElements?");
18872 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18874 var t = n.getOwnerTree();
18875 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18877 var cols = t.columns;
18878 var bw = t.borderWidth;
18880 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18881 var cb = typeof a.checked == "boolean";
18882 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18883 var colcls = 'x-t-' + tid + '-c0';
18885 '<li class="x-tree-node">',
18888 '<div class="x-tree-node-el ', a.cls,'">',
18890 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18893 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18894 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
18895 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18896 (a.icon ? ' x-tree-node-inline-icon' : ''),
18897 (a.iconCls ? ' '+a.iconCls : ''),
18898 '" unselectable="on" />',
18899 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
18900 (a.checked ? 'checked="checked" />' : ' />')) : ''),
18902 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18903 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18904 '<span unselectable="on" qtip="' + tx + '">',
18908 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18909 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18911 for(var i = 1, len = cols.length; i < len; i++){
18913 colcls = 'x-t-' + tid + '-c' +i;
18914 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18915 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18916 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18922 '<div class="x-clear"></div></div>',
18923 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18926 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18927 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18928 n.nextSibling.ui.getEl(), buf.join(""));
18930 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18932 var el = this.wrap.firstChild;
18934 this.elNode = el.firstChild;
18935 this.ranchor = el.childNodes[1];
18936 this.ctNode = this.wrap.childNodes[1];
18937 var cs = el.firstChild.childNodes;
18938 this.indentNode = cs[0];
18939 this.ecNode = cs[1];
18940 this.iconNode = cs[2];
18943 this.checkbox = cs[3];
18946 this.anchor = cs[index];
18948 this.textNode = cs[index].firstChild;
18950 //el.on("click", this.onClick, this);
18951 //el.on("dblclick", this.onDblClick, this);
18954 // console.log(this);
18956 initEvents : function(){
18957 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18960 var a = this.ranchor;
18962 var el = Roo.get(a);
18964 if(Roo.isOpera){ // opera render bug ignores the CSS
18965 el.setStyle("text-decoration", "none");
18968 el.on("click", this.onClick, this);
18969 el.on("dblclick", this.onDblClick, this);
18970 el.on("contextmenu", this.onContextMenu, this);
18974 /*onSelectedChange : function(state){
18977 this.addClass("x-tree-selected");
18980 this.removeClass("x-tree-selected");
18983 addClass : function(cls){
18985 Roo.fly(this.elRow).addClass(cls);
18991 removeClass : function(cls){
18993 Roo.fly(this.elRow).removeClass(cls);
18999 });//<Script type="text/javascript">
19003 * Ext JS Library 1.1.1
19004 * Copyright(c) 2006-2007, Ext JS, LLC.
19006 * Originally Released Under LGPL - original licence link has changed is not relivant.
19009 * <script type="text/javascript">
19014 * @class Roo.tree.ColumnTree
19015 * @extends Roo.data.TreePanel
19016 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
19017 * @cfg {int} borderWidth compined right/left border allowance
19019 * @param {String/HTMLElement/Element} el The container element
19020 * @param {Object} config
19022 Roo.tree.ColumnTree = function(el, config)
19024 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19028 * Fire this event on a container when it resizes
19029 * @param {int} w Width
19030 * @param {int} h Height
19034 this.on('resize', this.onResize, this);
19037 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19041 borderWidth: Roo.isBorderBox ? 0 : 2,
19044 render : function(){
19045 // add the header.....
19047 Roo.tree.ColumnTree.superclass.render.apply(this);
19049 this.el.addClass('x-column-tree');
19051 this.headers = this.el.createChild(
19052 {cls:'x-tree-headers'},this.innerCt.dom);
19054 var cols = this.columns, c;
19055 var totalWidth = 0;
19057 var len = cols.length;
19058 for(var i = 0; i < len; i++){
19060 totalWidth += c.width;
19061 this.headEls.push(this.headers.createChild({
19062 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19064 cls:'x-tree-hd-text',
19067 style:'width:'+(c.width-this.borderWidth)+'px;'
19070 this.headers.createChild({cls:'x-clear'});
19071 // prevent floats from wrapping when clipped
19072 this.headers.setWidth(totalWidth);
19073 //this.innerCt.setWidth(totalWidth);
19074 this.innerCt.setStyle({ overflow: 'auto' });
19075 this.onResize(this.width, this.height);
19079 onResize : function(w,h)
19084 this.innerCt.setWidth(this.width);
19085 this.innerCt.setHeight(this.height-20);
19088 var cols = this.columns, c;
19089 var totalWidth = 0;
19091 var len = cols.length;
19092 for(var i = 0; i < len; i++){
19094 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19095 // it's the expander..
19096 expEl = this.headEls[i];
19099 totalWidth += c.width;
19103 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
19105 this.headers.setWidth(w-20);
19114 * Ext JS Library 1.1.1
19115 * Copyright(c) 2006-2007, Ext JS, LLC.
19117 * Originally Released Under LGPL - original licence link has changed is not relivant.
19120 * <script type="text/javascript">
19124 * @class Roo.menu.Menu
19125 * @extends Roo.util.Observable
19126 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
19127 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19129 * Creates a new Menu
19130 * @param {Object} config Configuration options
19132 Roo.menu.Menu = function(config){
19133 Roo.apply(this, config);
19134 this.id = this.id || Roo.id();
19137 * @event beforeshow
19138 * Fires before this menu is displayed
19139 * @param {Roo.menu.Menu} this
19143 * @event beforehide
19144 * Fires before this menu is hidden
19145 * @param {Roo.menu.Menu} this
19150 * Fires after this menu is displayed
19151 * @param {Roo.menu.Menu} this
19156 * Fires after this menu is hidden
19157 * @param {Roo.menu.Menu} this
19162 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19163 * @param {Roo.menu.Menu} this
19164 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19165 * @param {Roo.EventObject} e
19170 * Fires when the mouse is hovering over this menu
19171 * @param {Roo.menu.Menu} this
19172 * @param {Roo.EventObject} e
19173 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19178 * Fires when the mouse exits this menu
19179 * @param {Roo.menu.Menu} this
19180 * @param {Roo.EventObject} e
19181 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19186 * Fires when a menu item contained in this menu is clicked
19187 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19188 * @param {Roo.EventObject} e
19192 if (this.registerMenu) {
19193 Roo.menu.MenuMgr.register(this);
19196 var mis = this.items;
19197 this.items = new Roo.util.MixedCollection();
19199 this.add.apply(this, mis);
19203 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19205 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19209 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19210 * for bottom-right shadow (defaults to "sides")
19214 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19215 * this menu (defaults to "tl-tr?")
19217 subMenuAlign : "tl-tr?",
19219 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19220 * relative to its element of origin (defaults to "tl-bl?")
19222 defaultAlign : "tl-bl?",
19224 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19226 allowOtherMenus : false,
19228 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19230 registerMenu : true,
19235 render : function(){
19239 var el = this.el = new Roo.Layer({
19241 shadow:this.shadow,
19243 parentEl: this.parentEl || document.body,
19247 this.keyNav = new Roo.menu.MenuNav(this);
19250 el.addClass("x-menu-plain");
19253 el.addClass(this.cls);
19255 // generic focus element
19256 this.focusEl = el.createChild({
19257 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19259 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19260 //disabling touch- as it's causing issues ..
19261 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
19262 ul.on('click' , this.onClick, this);
19265 ul.on("mouseover", this.onMouseOver, this);
19266 ul.on("mouseout", this.onMouseOut, this);
19267 this.items.each(function(item){
19272 var li = document.createElement("li");
19273 li.className = "x-menu-list-item";
19274 ul.dom.appendChild(li);
19275 item.render(li, this);
19282 autoWidth : function(){
19283 var el = this.el, ul = this.ul;
19287 var w = this.width;
19290 }else if(Roo.isIE){
19291 el.setWidth(this.minWidth);
19292 var t = el.dom.offsetWidth; // force recalc
19293 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19298 delayAutoWidth : function(){
19301 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19303 this.awTask.delay(20);
19308 findTargetItem : function(e){
19309 var t = e.getTarget(".x-menu-list-item", this.ul, true);
19310 if(t && t.menuItemId){
19311 return this.items.get(t.menuItemId);
19316 onClick : function(e){
19317 Roo.log("menu.onClick");
19318 var t = this.findTargetItem(e);
19323 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
19324 if(t == this.activeItem && t.shouldDeactivate(e)){
19325 this.activeItem.deactivate();
19326 delete this.activeItem;
19330 this.setActiveItem(t, true);
19338 this.fireEvent("click", this, t, e);
19342 setActiveItem : function(item, autoExpand){
19343 if(item != this.activeItem){
19344 if(this.activeItem){
19345 this.activeItem.deactivate();
19347 this.activeItem = item;
19348 item.activate(autoExpand);
19349 }else if(autoExpand){
19355 tryActivate : function(start, step){
19356 var items = this.items;
19357 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19358 var item = items.get(i);
19359 if(!item.disabled && item.canActivate){
19360 this.setActiveItem(item, false);
19368 onMouseOver : function(e){
19370 if(t = this.findTargetItem(e)){
19371 if(t.canActivate && !t.disabled){
19372 this.setActiveItem(t, true);
19375 this.fireEvent("mouseover", this, e, t);
19379 onMouseOut : function(e){
19381 if(t = this.findTargetItem(e)){
19382 if(t == this.activeItem && t.shouldDeactivate(e)){
19383 this.activeItem.deactivate();
19384 delete this.activeItem;
19387 this.fireEvent("mouseout", this, e, t);
19391 * Read-only. Returns true if the menu is currently displayed, else false.
19394 isVisible : function(){
19395 return this.el && !this.hidden;
19399 * Displays this menu relative to another element
19400 * @param {String/HTMLElement/Roo.Element} element The element to align to
19401 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19402 * the element (defaults to this.defaultAlign)
19403 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19405 show : function(el, pos, parentMenu){
19406 this.parentMenu = parentMenu;
19410 this.fireEvent("beforeshow", this);
19411 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19415 * Displays this menu at a specific xy position
19416 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19417 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19419 showAt : function(xy, parentMenu, /* private: */_e){
19420 this.parentMenu = parentMenu;
19425 this.fireEvent("beforeshow", this);
19426 xy = this.el.adjustForConstraints(xy);
19430 this.hidden = false;
19432 this.fireEvent("show", this);
19435 focus : function(){
19437 this.doFocus.defer(50, this);
19441 doFocus : function(){
19443 this.focusEl.focus();
19448 * Hides this menu and optionally all parent menus
19449 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19451 hide : function(deep){
19452 if(this.el && this.isVisible()){
19453 this.fireEvent("beforehide", this);
19454 if(this.activeItem){
19455 this.activeItem.deactivate();
19456 this.activeItem = null;
19459 this.hidden = true;
19460 this.fireEvent("hide", this);
19462 if(deep === true && this.parentMenu){
19463 this.parentMenu.hide(true);
19468 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19469 * Any of the following are valid:
19471 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19472 * <li>An HTMLElement object which will be converted to a menu item</li>
19473 * <li>A menu item config object that will be created as a new menu item</li>
19474 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19475 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19480 var menu = new Roo.menu.Menu();
19482 // Create a menu item to add by reference
19483 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19485 // Add a bunch of items at once using different methods.
19486 // Only the last item added will be returned.
19487 var item = menu.add(
19488 menuItem, // add existing item by ref
19489 'Dynamic Item', // new TextItem
19490 '-', // new separator
19491 { text: 'Config Item' } // new item by config
19494 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19495 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19498 var a = arguments, l = a.length, item;
19499 for(var i = 0; i < l; i++){
19501 if ((typeof(el) == "object") && el.xtype && el.xns) {
19502 el = Roo.factory(el, Roo.menu);
19505 if(el.render){ // some kind of Item
19506 item = this.addItem(el);
19507 }else if(typeof el == "string"){ // string
19508 if(el == "separator" || el == "-"){
19509 item = this.addSeparator();
19511 item = this.addText(el);
19513 }else if(el.tagName || el.el){ // element
19514 item = this.addElement(el);
19515 }else if(typeof el == "object"){ // must be menu item config?
19516 item = this.addMenuItem(el);
19523 * Returns this menu's underlying {@link Roo.Element} object
19524 * @return {Roo.Element} The element
19526 getEl : function(){
19534 * Adds a separator bar to the menu
19535 * @return {Roo.menu.Item} The menu item that was added
19537 addSeparator : function(){
19538 return this.addItem(new Roo.menu.Separator());
19542 * Adds an {@link Roo.Element} object to the menu
19543 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19544 * @return {Roo.menu.Item} The menu item that was added
19546 addElement : function(el){
19547 return this.addItem(new Roo.menu.BaseItem(el));
19551 * Adds an existing object based on {@link Roo.menu.Item} to the menu
19552 * @param {Roo.menu.Item} item The menu item to add
19553 * @return {Roo.menu.Item} The menu item that was added
19555 addItem : function(item){
19556 this.items.add(item);
19558 var li = document.createElement("li");
19559 li.className = "x-menu-list-item";
19560 this.ul.dom.appendChild(li);
19561 item.render(li, this);
19562 this.delayAutoWidth();
19568 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19569 * @param {Object} config A MenuItem config object
19570 * @return {Roo.menu.Item} The menu item that was added
19572 addMenuItem : function(config){
19573 if(!(config instanceof Roo.menu.Item)){
19574 if(typeof config.checked == "boolean"){ // must be check menu item config?
19575 config = new Roo.menu.CheckItem(config);
19577 config = new Roo.menu.Item(config);
19580 return this.addItem(config);
19584 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19585 * @param {String} text The text to display in the menu item
19586 * @return {Roo.menu.Item} The menu item that was added
19588 addText : function(text){
19589 return this.addItem(new Roo.menu.TextItem({ text : text }));
19593 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19594 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19595 * @param {Roo.menu.Item} item The menu item to add
19596 * @return {Roo.menu.Item} The menu item that was added
19598 insert : function(index, item){
19599 this.items.insert(index, item);
19601 var li = document.createElement("li");
19602 li.className = "x-menu-list-item";
19603 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19604 item.render(li, this);
19605 this.delayAutoWidth();
19611 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19612 * @param {Roo.menu.Item} item The menu item to remove
19614 remove : function(item){
19615 this.items.removeKey(item.id);
19620 * Removes and destroys all items in the menu
19622 removeAll : function(){
19624 while(f = this.items.first()){
19630 // MenuNav is a private utility class used internally by the Menu
19631 Roo.menu.MenuNav = function(menu){
19632 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19633 this.scope = this.menu = menu;
19636 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19637 doRelay : function(e, h){
19638 var k = e.getKey();
19639 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19640 this.menu.tryActivate(0, 1);
19643 return h.call(this.scope || this, e, this.menu);
19646 up : function(e, m){
19647 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19648 m.tryActivate(m.items.length-1, -1);
19652 down : function(e, m){
19653 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19654 m.tryActivate(0, 1);
19658 right : function(e, m){
19660 m.activeItem.expandMenu(true);
19664 left : function(e, m){
19666 if(m.parentMenu && m.parentMenu.activeItem){
19667 m.parentMenu.activeItem.activate();
19671 enter : function(e, m){
19673 e.stopPropagation();
19674 m.activeItem.onClick(e);
19675 m.fireEvent("click", this, m.activeItem);
19681 * Ext JS Library 1.1.1
19682 * Copyright(c) 2006-2007, Ext JS, LLC.
19684 * Originally Released Under LGPL - original licence link has changed is not relivant.
19687 * <script type="text/javascript">
19691 * @class Roo.menu.MenuMgr
19692 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19695 Roo.menu.MenuMgr = function(){
19696 var menus, active, groups = {}, attached = false, lastShow = new Date();
19698 // private - called when first menu is created
19701 active = new Roo.util.MixedCollection();
19702 Roo.get(document).addKeyListener(27, function(){
19703 if(active.length > 0){
19710 function hideAll(){
19711 if(active && active.length > 0){
19712 var c = active.clone();
19713 c.each(function(m){
19720 function onHide(m){
19722 if(active.length < 1){
19723 Roo.get(document).un("mousedown", onMouseDown);
19729 function onShow(m){
19730 var last = active.last();
19731 lastShow = new Date();
19734 Roo.get(document).on("mousedown", onMouseDown);
19738 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19739 m.parentMenu.activeChild = m;
19740 }else if(last && last.isVisible()){
19741 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19746 function onBeforeHide(m){
19748 m.activeChild.hide();
19750 if(m.autoHideTimer){
19751 clearTimeout(m.autoHideTimer);
19752 delete m.autoHideTimer;
19757 function onBeforeShow(m){
19758 var pm = m.parentMenu;
19759 if(!pm && !m.allowOtherMenus){
19761 }else if(pm && pm.activeChild && active != m){
19762 pm.activeChild.hide();
19767 function onMouseDown(e){
19768 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19774 function onBeforeCheck(mi, state){
19776 var g = groups[mi.group];
19777 for(var i = 0, l = g.length; i < l; i++){
19779 g[i].setChecked(false);
19788 * Hides all menus that are currently visible
19790 hideAll : function(){
19795 register : function(menu){
19799 menus[menu.id] = menu;
19800 menu.on("beforehide", onBeforeHide);
19801 menu.on("hide", onHide);
19802 menu.on("beforeshow", onBeforeShow);
19803 menu.on("show", onShow);
19804 var g = menu.group;
19805 if(g && menu.events["checkchange"]){
19809 groups[g].push(menu);
19810 menu.on("checkchange", onCheck);
19815 * Returns a {@link Roo.menu.Menu} object
19816 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19817 * be used to generate and return a new Menu instance.
19819 get : function(menu){
19820 if(typeof menu == "string"){ // menu id
19821 return menus[menu];
19822 }else if(menu.events){ // menu instance
19824 }else if(typeof menu.length == 'number'){ // array of menu items?
19825 return new Roo.menu.Menu({items:menu});
19826 }else{ // otherwise, must be a config
19827 return new Roo.menu.Menu(menu);
19832 unregister : function(menu){
19833 delete menus[menu.id];
19834 menu.un("beforehide", onBeforeHide);
19835 menu.un("hide", onHide);
19836 menu.un("beforeshow", onBeforeShow);
19837 menu.un("show", onShow);
19838 var g = menu.group;
19839 if(g && menu.events["checkchange"]){
19840 groups[g].remove(menu);
19841 menu.un("checkchange", onCheck);
19846 registerCheckable : function(menuItem){
19847 var g = menuItem.group;
19852 groups[g].push(menuItem);
19853 menuItem.on("beforecheckchange", onBeforeCheck);
19858 unregisterCheckable : function(menuItem){
19859 var g = menuItem.group;
19861 groups[g].remove(menuItem);
19862 menuItem.un("beforecheckchange", onBeforeCheck);
19868 * Ext JS Library 1.1.1
19869 * Copyright(c) 2006-2007, Ext JS, LLC.
19871 * Originally Released Under LGPL - original licence link has changed is not relivant.
19874 * <script type="text/javascript">
19879 * @class Roo.menu.BaseItem
19880 * @extends Roo.Component
19881 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
19882 * management and base configuration options shared by all menu components.
19884 * Creates a new BaseItem
19885 * @param {Object} config Configuration options
19887 Roo.menu.BaseItem = function(config){
19888 Roo.menu.BaseItem.superclass.constructor.call(this, config);
19893 * Fires when this item is clicked
19894 * @param {Roo.menu.BaseItem} this
19895 * @param {Roo.EventObject} e
19900 * Fires when this item is activated
19901 * @param {Roo.menu.BaseItem} this
19905 * @event deactivate
19906 * Fires when this item is deactivated
19907 * @param {Roo.menu.BaseItem} this
19913 this.on("click", this.handler, this.scope, true);
19917 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19919 * @cfg {Function} handler
19920 * A function that will handle the click event of this menu item (defaults to undefined)
19923 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19925 canActivate : false,
19928 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19933 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19935 activeClass : "x-menu-item-active",
19937 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19939 hideOnClick : true,
19941 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19946 ctype: "Roo.menu.BaseItem",
19949 actionMode : "container",
19952 render : function(container, parentMenu){
19953 this.parentMenu = parentMenu;
19954 Roo.menu.BaseItem.superclass.render.call(this, container);
19955 this.container.menuItemId = this.id;
19959 onRender : function(container, position){
19960 this.el = Roo.get(this.el);
19961 container.dom.appendChild(this.el.dom);
19965 onClick : function(e){
19966 if(!this.disabled && this.fireEvent("click", this, e) !== false
19967 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19968 this.handleClick(e);
19975 activate : function(){
19979 var li = this.container;
19980 li.addClass(this.activeClass);
19981 this.region = li.getRegion().adjust(2, 2, -2, -2);
19982 this.fireEvent("activate", this);
19987 deactivate : function(){
19988 this.container.removeClass(this.activeClass);
19989 this.fireEvent("deactivate", this);
19993 shouldDeactivate : function(e){
19994 return !this.region || !this.region.contains(e.getPoint());
19998 handleClick : function(e){
19999 if(this.hideOnClick){
20000 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20005 expandMenu : function(autoActivate){
20010 hideMenu : function(){
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.Adapter
20026 * @extends Roo.menu.BaseItem
20027 * 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.
20028 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20030 * Creates a new Adapter
20031 * @param {Object} config Configuration options
20033 Roo.menu.Adapter = function(component, config){
20034 Roo.menu.Adapter.superclass.constructor.call(this, config);
20035 this.component = component;
20037 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20039 canActivate : true,
20042 onRender : function(container, position){
20043 this.component.render(container);
20044 this.el = this.component.getEl();
20048 activate : function(){
20052 this.component.focus();
20053 this.fireEvent("activate", this);
20058 deactivate : function(){
20059 this.fireEvent("deactivate", this);
20063 disable : function(){
20064 this.component.disable();
20065 Roo.menu.Adapter.superclass.disable.call(this);
20069 enable : function(){
20070 this.component.enable();
20071 Roo.menu.Adapter.superclass.enable.call(this);
20075 * Ext JS Library 1.1.1
20076 * Copyright(c) 2006-2007, Ext JS, LLC.
20078 * Originally Released Under LGPL - original licence link has changed is not relivant.
20081 * <script type="text/javascript">
20085 * @class Roo.menu.TextItem
20086 * @extends Roo.menu.BaseItem
20087 * Adds a static text string to a menu, usually used as either a heading or group separator.
20088 * Note: old style constructor with text is still supported.
20091 * Creates a new TextItem
20092 * @param {Object} cfg Configuration
20094 Roo.menu.TextItem = function(cfg){
20095 if (typeof(cfg) == 'string') {
20098 Roo.apply(this,cfg);
20101 Roo.menu.TextItem.superclass.constructor.call(this);
20104 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20106 * @cfg {Boolean} text Text to show on item.
20111 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20113 hideOnClick : false,
20115 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20117 itemCls : "x-menu-text",
20120 onRender : function(){
20121 var s = document.createElement("span");
20122 s.className = this.itemCls;
20123 s.innerHTML = this.text;
20125 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20129 * Ext JS Library 1.1.1
20130 * Copyright(c) 2006-2007, Ext JS, LLC.
20132 * Originally Released Under LGPL - original licence link has changed is not relivant.
20135 * <script type="text/javascript">
20139 * @class Roo.menu.Separator
20140 * @extends Roo.menu.BaseItem
20141 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20142 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20144 * @param {Object} config Configuration options
20146 Roo.menu.Separator = function(config){
20147 Roo.menu.Separator.superclass.constructor.call(this, config);
20150 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20152 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20154 itemCls : "x-menu-sep",
20156 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20158 hideOnClick : false,
20161 onRender : function(li){
20162 var s = document.createElement("span");
20163 s.className = this.itemCls;
20164 s.innerHTML = " ";
20166 li.addClass("x-menu-sep-li");
20167 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20171 * Ext JS Library 1.1.1
20172 * Copyright(c) 2006-2007, Ext JS, LLC.
20174 * Originally Released Under LGPL - original licence link has changed is not relivant.
20177 * <script type="text/javascript">
20180 * @class Roo.menu.Item
20181 * @extends Roo.menu.BaseItem
20182 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20183 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20184 * activation and click handling.
20186 * Creates a new Item
20187 * @param {Object} config Configuration options
20189 Roo.menu.Item = function(config){
20190 Roo.menu.Item.superclass.constructor.call(this, config);
20192 this.menu = Roo.menu.MenuMgr.get(this.menu);
20195 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20198 * @cfg {String} text
20199 * The text to show on the menu item.
20203 * @cfg {String} HTML to render in menu
20204 * The text to show on the menu item (HTML version).
20208 * @cfg {String} icon
20209 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20213 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20215 itemCls : "x-menu-item",
20217 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20219 canActivate : true,
20221 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20224 // doc'd in BaseItem
20228 ctype: "Roo.menu.Item",
20231 onRender : function(container, position){
20232 var el = document.createElement("a");
20233 el.hideFocus = true;
20234 el.unselectable = "on";
20235 el.href = this.href || "#";
20236 if(this.hrefTarget){
20237 el.target = this.hrefTarget;
20239 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
20241 var html = this.html.length ? this.html : String.format('{0}',this.text);
20243 el.innerHTML = String.format(
20244 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20245 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20247 Roo.menu.Item.superclass.onRender.call(this, container, position);
20251 * Sets the text to display in this menu item
20252 * @param {String} text The text to display
20253 * @param {Boolean} isHTML true to indicate text is pure html.
20255 setText : function(text, isHTML){
20263 var html = this.html.length ? this.html : String.format('{0}',this.text);
20265 this.el.update(String.format(
20266 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20267 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20268 this.parentMenu.autoWidth();
20273 handleClick : function(e){
20274 if(!this.href){ // if no link defined, stop the event automatically
20277 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20281 activate : function(autoExpand){
20282 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20292 shouldDeactivate : function(e){
20293 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20294 if(this.menu && this.menu.isVisible()){
20295 return !this.menu.getEl().getRegion().contains(e.getPoint());
20303 deactivate : function(){
20304 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20309 expandMenu : function(autoActivate){
20310 if(!this.disabled && this.menu){
20311 clearTimeout(this.hideTimer);
20312 delete this.hideTimer;
20313 if(!this.menu.isVisible() && !this.showTimer){
20314 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20315 }else if (this.menu.isVisible() && autoActivate){
20316 this.menu.tryActivate(0, 1);
20322 deferExpand : function(autoActivate){
20323 delete this.showTimer;
20324 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20326 this.menu.tryActivate(0, 1);
20331 hideMenu : function(){
20332 clearTimeout(this.showTimer);
20333 delete this.showTimer;
20334 if(!this.hideTimer && this.menu && this.menu.isVisible()){
20335 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20340 deferHide : function(){
20341 delete this.hideTimer;
20346 * Ext JS Library 1.1.1
20347 * Copyright(c) 2006-2007, Ext JS, LLC.
20349 * Originally Released Under LGPL - original licence link has changed is not relivant.
20352 * <script type="text/javascript">
20356 * @class Roo.menu.CheckItem
20357 * @extends Roo.menu.Item
20358 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20360 * Creates a new CheckItem
20361 * @param {Object} config Configuration options
20363 Roo.menu.CheckItem = function(config){
20364 Roo.menu.CheckItem.superclass.constructor.call(this, config);
20367 * @event beforecheckchange
20368 * Fires before the checked value is set, providing an opportunity to cancel if needed
20369 * @param {Roo.menu.CheckItem} this
20370 * @param {Boolean} checked The new checked value that will be set
20372 "beforecheckchange" : true,
20374 * @event checkchange
20375 * Fires after the checked value has been set
20376 * @param {Roo.menu.CheckItem} this
20377 * @param {Boolean} checked The checked value that was set
20379 "checkchange" : true
20381 if(this.checkHandler){
20382 this.on('checkchange', this.checkHandler, this.scope);
20385 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20387 * @cfg {String} group
20388 * All check items with the same group name will automatically be grouped into a single-select
20389 * radio button group (defaults to '')
20392 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20394 itemCls : "x-menu-item x-menu-check-item",
20396 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20398 groupClass : "x-menu-group-item",
20401 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
20402 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20403 * initialized with checked = true will be rendered as checked.
20408 ctype: "Roo.menu.CheckItem",
20411 onRender : function(c){
20412 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20414 this.el.addClass(this.groupClass);
20416 Roo.menu.MenuMgr.registerCheckable(this);
20418 this.checked = false;
20419 this.setChecked(true, true);
20424 destroy : function(){
20426 Roo.menu.MenuMgr.unregisterCheckable(this);
20428 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20432 * Set the checked state of this item
20433 * @param {Boolean} checked The new checked value
20434 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20436 setChecked : function(state, suppressEvent){
20437 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20438 if(this.container){
20439 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20441 this.checked = state;
20442 if(suppressEvent !== true){
20443 this.fireEvent("checkchange", this, state);
20449 handleClick : function(e){
20450 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20451 this.setChecked(!this.checked);
20453 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20457 * Ext JS Library 1.1.1
20458 * Copyright(c) 2006-2007, Ext JS, LLC.
20460 * Originally Released Under LGPL - original licence link has changed is not relivant.
20463 * <script type="text/javascript">
20467 * @class Roo.menu.DateItem
20468 * @extends Roo.menu.Adapter
20469 * A menu item that wraps the {@link Roo.DatPicker} component.
20471 * Creates a new DateItem
20472 * @param {Object} config Configuration options
20474 Roo.menu.DateItem = function(config){
20475 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20476 /** The Roo.DatePicker object @type Roo.DatePicker */
20477 this.picker = this.component;
20478 this.addEvents({select: true});
20480 this.picker.on("render", function(picker){
20481 picker.getEl().swallowEvent("click");
20482 picker.container.addClass("x-menu-date-item");
20485 this.picker.on("select", this.onSelect, this);
20488 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20490 onSelect : function(picker, date){
20491 this.fireEvent("select", this, date, picker);
20492 Roo.menu.DateItem.superclass.handleClick.call(this);
20496 * Ext JS Library 1.1.1
20497 * Copyright(c) 2006-2007, Ext JS, LLC.
20499 * Originally Released Under LGPL - original licence link has changed is not relivant.
20502 * <script type="text/javascript">
20506 * @class Roo.menu.ColorItem
20507 * @extends Roo.menu.Adapter
20508 * A menu item that wraps the {@link Roo.ColorPalette} component.
20510 * Creates a new ColorItem
20511 * @param {Object} config Configuration options
20513 Roo.menu.ColorItem = function(config){
20514 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20515 /** The Roo.ColorPalette object @type Roo.ColorPalette */
20516 this.palette = this.component;
20517 this.relayEvents(this.palette, ["select"]);
20518 if(this.selectHandler){
20519 this.on('select', this.selectHandler, this.scope);
20522 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20524 * Ext JS Library 1.1.1
20525 * Copyright(c) 2006-2007, Ext JS, LLC.
20527 * Originally Released Under LGPL - original licence link has changed is not relivant.
20530 * <script type="text/javascript">
20535 * @class Roo.menu.DateMenu
20536 * @extends Roo.menu.Menu
20537 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20539 * Creates a new DateMenu
20540 * @param {Object} config Configuration options
20542 Roo.menu.DateMenu = function(config){
20543 Roo.menu.DateMenu.superclass.constructor.call(this, config);
20545 var di = new Roo.menu.DateItem(config);
20548 * The {@link Roo.DatePicker} instance for this DateMenu
20551 this.picker = di.picker;
20554 * @param {DatePicker} picker
20555 * @param {Date} date
20557 this.relayEvents(di, ["select"]);
20558 this.on('beforeshow', function(){
20560 this.picker.hideMonthPicker(false);
20564 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20568 * Ext JS Library 1.1.1
20569 * Copyright(c) 2006-2007, Ext JS, LLC.
20571 * Originally Released Under LGPL - original licence link has changed is not relivant.
20574 * <script type="text/javascript">
20579 * @class Roo.menu.ColorMenu
20580 * @extends Roo.menu.Menu
20581 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20583 * Creates a new ColorMenu
20584 * @param {Object} config Configuration options
20586 Roo.menu.ColorMenu = function(config){
20587 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20589 var ci = new Roo.menu.ColorItem(config);
20592 * The {@link Roo.ColorPalette} instance for this ColorMenu
20593 * @type ColorPalette
20595 this.palette = ci.palette;
20598 * @param {ColorPalette} palette
20599 * @param {String} color
20601 this.relayEvents(ci, ["select"]);
20603 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20605 * Ext JS Library 1.1.1
20606 * Copyright(c) 2006-2007, Ext JS, LLC.
20608 * Originally Released Under LGPL - original licence link has changed is not relivant.
20611 * <script type="text/javascript">
20615 * @class Roo.form.Field
20616 * @extends Roo.BoxComponent
20617 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20619 * Creates a new Field
20620 * @param {Object} config Configuration options
20622 Roo.form.Field = function(config){
20623 Roo.form.Field.superclass.constructor.call(this, config);
20626 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
20628 * @cfg {String} fieldLabel Label to use when rendering a form.
20631 * @cfg {String} qtip Mouse over tip
20635 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20637 invalidClass : "x-form-invalid",
20639 * @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")
20641 invalidText : "The value in this field is invalid",
20643 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20645 focusClass : "x-form-focus",
20647 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20648 automatic validation (defaults to "keyup").
20650 validationEvent : "keyup",
20652 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20654 validateOnBlur : true,
20656 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20658 validationDelay : 250,
20660 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20661 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20663 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20665 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20667 fieldClass : "x-form-field",
20669 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
20672 ----------- ----------------------------------------------------------------------
20673 qtip Display a quick tip when the user hovers over the field
20674 title Display a default browser title attribute popup
20675 under Add a block div beneath the field containing the error text
20676 side Add an error icon to the right of the field with a popup on hover
20677 [element id] Add the error text directly to the innerHTML of the specified element
20680 msgTarget : 'qtip',
20682 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20687 * @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.
20692 * @cfg {Boolean} disabled True to disable the field (defaults to false).
20697 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20699 inputType : undefined,
20702 * @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).
20704 tabIndex : undefined,
20707 isFormField : true,
20712 * @property {Roo.Element} fieldEl
20713 * Element Containing the rendered Field (with label etc.)
20716 * @cfg {Mixed} value A value to initialize this field with.
20721 * @cfg {String} name The field's HTML name attribute.
20724 * @cfg {String} cls A CSS class to apply to the field's underlying element.
20727 loadedValue : false,
20731 initComponent : function(){
20732 Roo.form.Field.superclass.initComponent.call(this);
20736 * Fires when this field receives input focus.
20737 * @param {Roo.form.Field} this
20742 * Fires when this field loses input focus.
20743 * @param {Roo.form.Field} this
20747 * @event specialkey
20748 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
20749 * {@link Roo.EventObject#getKey} to determine which key was pressed.
20750 * @param {Roo.form.Field} this
20751 * @param {Roo.EventObject} e The event object
20756 * Fires just before the field blurs if the field value has changed.
20757 * @param {Roo.form.Field} this
20758 * @param {Mixed} newValue The new value
20759 * @param {Mixed} oldValue The original value
20764 * Fires after the field has been marked as invalid.
20765 * @param {Roo.form.Field} this
20766 * @param {String} msg The validation message
20771 * Fires after the field has been validated with no errors.
20772 * @param {Roo.form.Field} this
20777 * Fires after the key up
20778 * @param {Roo.form.Field} this
20779 * @param {Roo.EventObject} e The event Object
20786 * Returns the name attribute of the field if available
20787 * @return {String} name The field name
20789 getName: function(){
20790 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20794 onRender : function(ct, position){
20795 Roo.form.Field.superclass.onRender.call(this, ct, position);
20797 var cfg = this.getAutoCreate();
20799 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20801 if (!cfg.name.length) {
20804 if(this.inputType){
20805 cfg.type = this.inputType;
20807 this.el = ct.createChild(cfg, position);
20809 var type = this.el.dom.type;
20811 if(type == 'password'){
20814 this.el.addClass('x-form-'+type);
20817 this.el.dom.readOnly = true;
20819 if(this.tabIndex !== undefined){
20820 this.el.dom.setAttribute('tabIndex', this.tabIndex);
20823 this.el.addClass([this.fieldClass, this.cls]);
20828 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20829 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20830 * @return {Roo.form.Field} this
20832 applyTo : function(target){
20833 this.allowDomMove = false;
20834 this.el = Roo.get(target);
20835 this.render(this.el.dom.parentNode);
20840 initValue : function(){
20841 if(this.value !== undefined){
20842 this.setValue(this.value);
20843 }else if(this.el.dom.value.length > 0){
20844 this.setValue(this.el.dom.value);
20849 * Returns true if this field has been changed since it was originally loaded and is not disabled.
20850 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
20852 isDirty : function() {
20853 if(this.disabled) {
20856 return String(this.getValue()) !== String(this.originalValue);
20860 * stores the current value in loadedValue
20862 resetHasChanged : function()
20864 this.loadedValue = String(this.getValue());
20867 * checks the current value against the 'loaded' value.
20868 * Note - will return false if 'resetHasChanged' has not been called first.
20870 hasChanged : function()
20872 if(this.disabled || this.readOnly) {
20875 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
20881 afterRender : function(){
20882 Roo.form.Field.superclass.afterRender.call(this);
20887 fireKey : function(e){
20888 //Roo.log('field ' + e.getKey());
20889 if(e.isNavKeyPress()){
20890 this.fireEvent("specialkey", this, e);
20895 * Resets the current field value to the originally loaded value and clears any validation messages
20897 reset : function(){
20898 this.setValue(this.resetValue);
20899 this.clearInvalid();
20903 initEvents : function(){
20904 // safari killled keypress - so keydown is now used..
20905 this.el.on("keydown" , this.fireKey, this);
20906 this.el.on("focus", this.onFocus, this);
20907 this.el.on("blur", this.onBlur, this);
20908 this.el.relayEvent('keyup', this);
20910 // reference to original value for reset
20911 this.originalValue = this.getValue();
20912 this.resetValue = this.getValue();
20916 onFocus : function(){
20917 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20918 this.el.addClass(this.focusClass);
20920 if(!this.hasFocus){
20921 this.hasFocus = true;
20922 this.startValue = this.getValue();
20923 this.fireEvent("focus", this);
20927 beforeBlur : Roo.emptyFn,
20930 onBlur : function(){
20932 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20933 this.el.removeClass(this.focusClass);
20935 this.hasFocus = false;
20936 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20939 var v = this.getValue();
20940 if(String(v) !== String(this.startValue)){
20941 this.fireEvent('change', this, v, this.startValue);
20943 this.fireEvent("blur", this);
20947 * Returns whether or not the field value is currently valid
20948 * @param {Boolean} preventMark True to disable marking the field invalid
20949 * @return {Boolean} True if the value is valid, else false
20951 isValid : function(preventMark){
20955 var restore = this.preventMark;
20956 this.preventMark = preventMark === true;
20957 var v = this.validateValue(this.processValue(this.getRawValue()));
20958 this.preventMark = restore;
20963 * Validates the field value
20964 * @return {Boolean} True if the value is valid, else false
20966 validate : function(){
20967 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20968 this.clearInvalid();
20974 processValue : function(value){
20979 // Subclasses should provide the validation implementation by overriding this
20980 validateValue : function(value){
20985 * Mark this field as invalid
20986 * @param {String} msg The validation message
20988 markInvalid : function(msg){
20989 if(!this.rendered || this.preventMark){ // not rendered
20993 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20995 obj.el.addClass(this.invalidClass);
20996 msg = msg || this.invalidText;
20997 switch(this.msgTarget){
20999 obj.el.dom.qtip = msg;
21000 obj.el.dom.qclass = 'x-form-invalid-tip';
21001 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21002 Roo.QuickTips.enable();
21006 this.el.dom.title = msg;
21010 var elp = this.el.findParent('.x-form-element', 5, true);
21011 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21012 this.errorEl.setWidth(elp.getWidth(true)-20);
21014 this.errorEl.update(msg);
21015 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21018 if(!this.errorIcon){
21019 var elp = this.el.findParent('.x-form-element', 5, true);
21020 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21022 this.alignErrorIcon();
21023 this.errorIcon.dom.qtip = msg;
21024 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21025 this.errorIcon.show();
21026 this.on('resize', this.alignErrorIcon, this);
21029 var t = Roo.getDom(this.msgTarget);
21031 t.style.display = this.msgDisplay;
21034 this.fireEvent('invalid', this, msg);
21038 alignErrorIcon : function(){
21039 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21043 * Clear any invalid styles/messages for this field
21045 clearInvalid : function(){
21046 if(!this.rendered || this.preventMark){ // not rendered
21049 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21051 obj.el.removeClass(this.invalidClass);
21052 switch(this.msgTarget){
21054 obj.el.dom.qtip = '';
21057 this.el.dom.title = '';
21061 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21065 if(this.errorIcon){
21066 this.errorIcon.dom.qtip = '';
21067 this.errorIcon.hide();
21068 this.un('resize', this.alignErrorIcon, this);
21072 var t = Roo.getDom(this.msgTarget);
21074 t.style.display = 'none';
21077 this.fireEvent('valid', this);
21081 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
21082 * @return {Mixed} value The field value
21084 getRawValue : function(){
21085 var v = this.el.getValue();
21091 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
21092 * @return {Mixed} value The field value
21094 getValue : function(){
21095 var v = this.el.getValue();
21101 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
21102 * @param {Mixed} value The value to set
21104 setRawValue : function(v){
21105 return this.el.dom.value = (v === null || v === undefined ? '' : v);
21109 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
21110 * @param {Mixed} value The value to set
21112 setValue : function(v){
21115 this.el.dom.value = (v === null || v === undefined ? '' : v);
21120 adjustSize : function(w, h){
21121 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21122 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21126 adjustWidth : function(tag, w){
21127 tag = tag.toLowerCase();
21128 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21129 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21130 if(tag == 'input'){
21133 if(tag == 'textarea'){
21136 }else if(Roo.isOpera){
21137 if(tag == 'input'){
21140 if(tag == 'textarea'){
21150 // anything other than normal should be considered experimental
21151 Roo.form.Field.msgFx = {
21153 show: function(msgEl, f){
21154 msgEl.setDisplayed('block');
21157 hide : function(msgEl, f){
21158 msgEl.setDisplayed(false).update('');
21163 show: function(msgEl, f){
21164 msgEl.slideIn('t', {stopFx:true});
21167 hide : function(msgEl, f){
21168 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21173 show: function(msgEl, f){
21174 msgEl.fixDisplay();
21175 msgEl.alignTo(f.el, 'tl-tr');
21176 msgEl.slideIn('l', {stopFx:true});
21179 hide : function(msgEl, f){
21180 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21185 * Ext JS Library 1.1.1
21186 * Copyright(c) 2006-2007, Ext JS, LLC.
21188 * Originally Released Under LGPL - original licence link has changed is not relivant.
21191 * <script type="text/javascript">
21196 * @class Roo.form.TextField
21197 * @extends Roo.form.Field
21198 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
21199 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21201 * Creates a new TextField
21202 * @param {Object} config Configuration options
21204 Roo.form.TextField = function(config){
21205 Roo.form.TextField.superclass.constructor.call(this, config);
21209 * Fires when the autosize function is triggered. The field may or may not have actually changed size
21210 * according to the default logic, but this event provides a hook for the developer to apply additional
21211 * logic at runtime to resize the field if needed.
21212 * @param {Roo.form.Field} this This text field
21213 * @param {Number} width The new field width
21219 Roo.extend(Roo.form.TextField, Roo.form.Field, {
21221 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21225 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21229 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21233 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21237 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21241 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21243 disableKeyFilter : false,
21245 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21249 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21253 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21255 maxLength : Number.MAX_VALUE,
21257 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21259 minLengthText : "The minimum length for this field is {0}",
21261 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21263 maxLengthText : "The maximum length for this field is {0}",
21265 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21267 selectOnFocus : false,
21269 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21271 blankText : "This field is required",
21273 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21274 * If available, this function will be called only after the basic validators all return true, and will be passed the
21275 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21279 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21280 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21281 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
21285 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21289 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21295 initEvents : function()
21297 if (this.emptyText) {
21298 this.el.attr('placeholder', this.emptyText);
21301 Roo.form.TextField.superclass.initEvents.call(this);
21302 if(this.validationEvent == 'keyup'){
21303 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21304 this.el.on('keyup', this.filterValidation, this);
21306 else if(this.validationEvent !== false){
21307 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21310 if(this.selectOnFocus){
21311 this.on("focus", this.preFocus, this);
21314 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21315 this.el.on("keypress", this.filterKeys, this);
21318 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
21319 this.el.on("click", this.autoSize, this);
21321 if(this.el.is('input[type=password]') && Roo.isSafari){
21322 this.el.on('keydown', this.SafariOnKeyDown, this);
21326 processValue : function(value){
21327 if(this.stripCharsRe){
21328 var newValue = value.replace(this.stripCharsRe, '');
21329 if(newValue !== value){
21330 this.setRawValue(newValue);
21337 filterValidation : function(e){
21338 if(!e.isNavKeyPress()){
21339 this.validationTask.delay(this.validationDelay);
21344 onKeyUp : function(e){
21345 if(!e.isNavKeyPress()){
21351 * Resets the current field value to the originally-loaded value and clears any validation messages.
21354 reset : function(){
21355 Roo.form.TextField.superclass.reset.call(this);
21361 preFocus : function(){
21363 if(this.selectOnFocus){
21364 this.el.dom.select();
21370 filterKeys : function(e){
21371 var k = e.getKey();
21372 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21375 var c = e.getCharCode(), cc = String.fromCharCode(c);
21376 if(Roo.isIE && (e.isSpecialKey() || !cc)){
21379 if(!this.maskRe.test(cc)){
21384 setValue : function(v){
21386 Roo.form.TextField.superclass.setValue.apply(this, arguments);
21392 * Validates a value according to the field's validation rules and marks the field as invalid
21393 * if the validation fails
21394 * @param {Mixed} value The value to validate
21395 * @return {Boolean} True if the value is valid, else false
21397 validateValue : function(value){
21398 if(value.length < 1) { // if it's blank
21399 if(this.allowBlank){
21400 this.clearInvalid();
21403 this.markInvalid(this.blankText);
21407 if(value.length < this.minLength){
21408 this.markInvalid(String.format(this.minLengthText, this.minLength));
21411 if(value.length > this.maxLength){
21412 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21416 var vt = Roo.form.VTypes;
21417 if(!vt[this.vtype](value, this)){
21418 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21422 if(typeof this.validator == "function"){
21423 var msg = this.validator(value);
21425 this.markInvalid(msg);
21429 if(this.regex && !this.regex.test(value)){
21430 this.markInvalid(this.regexText);
21437 * Selects text in this field
21438 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21439 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21441 selectText : function(start, end){
21442 var v = this.getRawValue();
21444 start = start === undefined ? 0 : start;
21445 end = end === undefined ? v.length : end;
21446 var d = this.el.dom;
21447 if(d.setSelectionRange){
21448 d.setSelectionRange(start, end);
21449 }else if(d.createTextRange){
21450 var range = d.createTextRange();
21451 range.moveStart("character", start);
21452 range.moveEnd("character", v.length-end);
21459 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21460 * This only takes effect if grow = true, and fires the autosize event.
21462 autoSize : function(){
21463 if(!this.grow || !this.rendered){
21467 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21470 var v = el.dom.value;
21471 var d = document.createElement('div');
21472 d.appendChild(document.createTextNode(v));
21476 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21477 this.el.setWidth(w);
21478 this.fireEvent("autosize", this, w);
21482 SafariOnKeyDown : function(event)
21484 // this is a workaround for a password hang bug on chrome/ webkit.
21486 var isSelectAll = false;
21488 if(this.el.dom.selectionEnd > 0){
21489 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21491 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21492 event.preventDefault();
21497 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21499 event.preventDefault();
21500 // this is very hacky as keydown always get's upper case.
21502 var cc = String.fromCharCode(event.getCharCode());
21505 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
21513 * Ext JS Library 1.1.1
21514 * Copyright(c) 2006-2007, Ext JS, LLC.
21516 * Originally Released Under LGPL - original licence link has changed is not relivant.
21519 * <script type="text/javascript">
21523 * @class Roo.form.Hidden
21524 * @extends Roo.form.TextField
21525 * Simple Hidden element used on forms
21527 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21530 * Creates a new Hidden form element.
21531 * @param {Object} config Configuration options
21536 // easy hidden field...
21537 Roo.form.Hidden = function(config){
21538 Roo.form.Hidden.superclass.constructor.call(this, config);
21541 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21543 inputType: 'hidden',
21546 labelSeparator: '',
21548 itemCls : 'x-form-item-display-none'
21556 * Ext JS Library 1.1.1
21557 * Copyright(c) 2006-2007, Ext JS, LLC.
21559 * Originally Released Under LGPL - original licence link has changed is not relivant.
21562 * <script type="text/javascript">
21566 * @class Roo.form.TriggerField
21567 * @extends Roo.form.TextField
21568 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21569 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21570 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21571 * for which you can provide a custom implementation. For example:
21573 var trigger = new Roo.form.TriggerField();
21574 trigger.onTriggerClick = myTriggerFn;
21575 trigger.applyTo('my-field');
21578 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21579 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21580 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
21581 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21583 * Create a new TriggerField.
21584 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21585 * to the base TextField)
21587 Roo.form.TriggerField = function(config){
21588 this.mimicing = false;
21589 Roo.form.TriggerField.superclass.constructor.call(this, config);
21592 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
21594 * @cfg {String} triggerClass A CSS class to apply to the trigger
21597 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21598 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21600 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21602 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21606 /** @cfg {Boolean} grow @hide */
21607 /** @cfg {Number} growMin @hide */
21608 /** @cfg {Number} growMax @hide */
21614 autoSize: Roo.emptyFn,
21618 deferHeight : true,
21621 actionMode : 'wrap',
21623 onResize : function(w, h){
21624 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21625 if(typeof w == 'number'){
21626 var x = w - this.trigger.getWidth();
21627 this.el.setWidth(this.adjustWidth('input', x));
21628 this.trigger.setStyle('left', x+'px');
21633 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21636 getResizeEl : function(){
21641 getPositionEl : function(){
21646 alignErrorIcon : function(){
21647 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21651 onRender : function(ct, position){
21652 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21653 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21654 this.trigger = this.wrap.createChild(this.triggerConfig ||
21655 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21656 if(this.hideTrigger){
21657 this.trigger.setDisplayed(false);
21659 this.initTrigger();
21661 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21666 initTrigger : function(){
21667 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21668 this.trigger.addClassOnOver('x-form-trigger-over');
21669 this.trigger.addClassOnClick('x-form-trigger-click');
21673 onDestroy : function(){
21675 this.trigger.removeAllListeners();
21676 this.trigger.remove();
21679 this.wrap.remove();
21681 Roo.form.TriggerField.superclass.onDestroy.call(this);
21685 onFocus : function(){
21686 Roo.form.TriggerField.superclass.onFocus.call(this);
21687 if(!this.mimicing){
21688 this.wrap.addClass('x-trigger-wrap-focus');
21689 this.mimicing = true;
21690 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21691 if(this.monitorTab){
21692 this.el.on("keydown", this.checkTab, this);
21698 checkTab : function(e){
21699 if(e.getKey() == e.TAB){
21700 this.triggerBlur();
21705 onBlur : function(){
21710 mimicBlur : function(e, t){
21711 if(!this.wrap.contains(t) && this.validateBlur()){
21712 this.triggerBlur();
21717 triggerBlur : function(){
21718 this.mimicing = false;
21719 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21720 if(this.monitorTab){
21721 this.el.un("keydown", this.checkTab, this);
21723 this.wrap.removeClass('x-trigger-wrap-focus');
21724 Roo.form.TriggerField.superclass.onBlur.call(this);
21728 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21729 validateBlur : function(e, t){
21734 onDisable : function(){
21735 Roo.form.TriggerField.superclass.onDisable.call(this);
21737 this.wrap.addClass('x-item-disabled');
21742 onEnable : function(){
21743 Roo.form.TriggerField.superclass.onEnable.call(this);
21745 this.wrap.removeClass('x-item-disabled');
21750 onShow : function(){
21751 var ae = this.getActionEl();
21754 ae.dom.style.display = '';
21755 ae.dom.style.visibility = 'visible';
21761 onHide : function(){
21762 var ae = this.getActionEl();
21763 ae.dom.style.display = 'none';
21767 * The function that should handle the trigger's click event. This method does nothing by default until overridden
21768 * by an implementing function.
21770 * @param {EventObject} e
21772 onTriggerClick : Roo.emptyFn
21775 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
21776 // to be extended by an implementing class. For an example of implementing this class, see the custom
21777 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21778 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21779 initComponent : function(){
21780 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21782 this.triggerConfig = {
21783 tag:'span', cls:'x-form-twin-triggers', cn:[
21784 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21785 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21789 getTrigger : function(index){
21790 return this.triggers[index];
21793 initTrigger : function(){
21794 var ts = this.trigger.select('.x-form-trigger', true);
21795 this.wrap.setStyle('overflow', 'hidden');
21796 var triggerField = this;
21797 ts.each(function(t, all, index){
21798 t.hide = function(){
21799 var w = triggerField.wrap.getWidth();
21800 this.dom.style.display = 'none';
21801 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21803 t.show = function(){
21804 var w = triggerField.wrap.getWidth();
21805 this.dom.style.display = '';
21806 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21808 var triggerIndex = 'Trigger'+(index+1);
21810 if(this['hide'+triggerIndex]){
21811 t.dom.style.display = 'none';
21813 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21814 t.addClassOnOver('x-form-trigger-over');
21815 t.addClassOnClick('x-form-trigger-click');
21817 this.triggers = ts.elements;
21820 onTrigger1Click : Roo.emptyFn,
21821 onTrigger2Click : Roo.emptyFn
21824 * Ext JS Library 1.1.1
21825 * Copyright(c) 2006-2007, Ext JS, LLC.
21827 * Originally Released Under LGPL - original licence link has changed is not relivant.
21830 * <script type="text/javascript">
21834 * @class Roo.form.TextArea
21835 * @extends Roo.form.TextField
21836 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
21837 * support for auto-sizing.
21839 * Creates a new TextArea
21840 * @param {Object} config Configuration options
21842 Roo.form.TextArea = function(config){
21843 Roo.form.TextArea.superclass.constructor.call(this, config);
21844 // these are provided exchanges for backwards compat
21845 // minHeight/maxHeight were replaced by growMin/growMax to be
21846 // compatible with TextField growing config values
21847 if(this.minHeight !== undefined){
21848 this.growMin = this.minHeight;
21850 if(this.maxHeight !== undefined){
21851 this.growMax = this.maxHeight;
21855 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
21857 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21861 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21865 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21866 * in the field (equivalent to setting overflow: hidden, defaults to false)
21868 preventScrollbars: false,
21870 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21871 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21875 onRender : function(ct, position){
21877 this.defaultAutoCreate = {
21879 style:"width:300px;height:60px;",
21880 autocomplete: "new-password"
21883 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21885 this.textSizeEl = Roo.DomHelper.append(document.body, {
21886 tag: "pre", cls: "x-form-grow-sizer"
21888 if(this.preventScrollbars){
21889 this.el.setStyle("overflow", "hidden");
21891 this.el.setHeight(this.growMin);
21895 onDestroy : function(){
21896 if(this.textSizeEl){
21897 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21899 Roo.form.TextArea.superclass.onDestroy.call(this);
21903 onKeyUp : function(e){
21904 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21910 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21911 * This only takes effect if grow = true, and fires the autosize event if the height changes.
21913 autoSize : function(){
21914 if(!this.grow || !this.textSizeEl){
21918 var v = el.dom.value;
21919 var ts = this.textSizeEl;
21922 ts.appendChild(document.createTextNode(v));
21925 Roo.fly(ts).setWidth(this.el.getWidth());
21927 v = "  ";
21930 v = v.replace(/\n/g, '<p> </p>');
21932 v += " \n ";
21935 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21936 if(h != this.lastHeight){
21937 this.lastHeight = h;
21938 this.el.setHeight(h);
21939 this.fireEvent("autosize", this, h);
21944 * Ext JS Library 1.1.1
21945 * Copyright(c) 2006-2007, Ext JS, LLC.
21947 * Originally Released Under LGPL - original licence link has changed is not relivant.
21950 * <script type="text/javascript">
21955 * @class Roo.form.NumberField
21956 * @extends Roo.form.TextField
21957 * Numeric text field that provides automatic keystroke filtering and numeric validation.
21959 * Creates a new NumberField
21960 * @param {Object} config Configuration options
21962 Roo.form.NumberField = function(config){
21963 Roo.form.NumberField.superclass.constructor.call(this, config);
21966 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
21968 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21970 fieldClass: "x-form-field x-form-num-field",
21972 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21974 allowDecimals : true,
21976 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21978 decimalSeparator : ".",
21980 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21982 decimalPrecision : 2,
21984 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21986 allowNegative : true,
21988 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21990 minValue : Number.NEGATIVE_INFINITY,
21992 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21994 maxValue : Number.MAX_VALUE,
21996 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21998 minText : "The minimum value for this field is {0}",
22000 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22002 maxText : "The maximum value for this field is {0}",
22004 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
22005 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22007 nanText : "{0} is not a valid number",
22010 initEvents : function(){
22011 Roo.form.NumberField.superclass.initEvents.call(this);
22012 var allowed = "0123456789";
22013 if(this.allowDecimals){
22014 allowed += this.decimalSeparator;
22016 if(this.allowNegative){
22019 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22020 var keyPress = function(e){
22021 var k = e.getKey();
22022 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22025 var c = e.getCharCode();
22026 if(allowed.indexOf(String.fromCharCode(c)) === -1){
22030 this.el.on("keypress", keyPress, this);
22034 validateValue : function(value){
22035 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22038 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22041 var num = this.parseValue(value);
22043 this.markInvalid(String.format(this.nanText, value));
22046 if(num < this.minValue){
22047 this.markInvalid(String.format(this.minText, this.minValue));
22050 if(num > this.maxValue){
22051 this.markInvalid(String.format(this.maxText, this.maxValue));
22057 getValue : function(){
22058 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22062 parseValue : function(value){
22063 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22064 return isNaN(value) ? '' : value;
22068 fixPrecision : function(value){
22069 var nan = isNaN(value);
22070 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22071 return nan ? '' : value;
22073 return parseFloat(value).toFixed(this.decimalPrecision);
22076 setValue : function(v){
22077 v = this.fixPrecision(v);
22078 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22082 decimalPrecisionFcn : function(v){
22083 return Math.floor(v);
22086 beforeBlur : function(){
22087 var v = this.parseValue(this.getRawValue());
22094 * Ext JS Library 1.1.1
22095 * Copyright(c) 2006-2007, Ext JS, LLC.
22097 * Originally Released Under LGPL - original licence link has changed is not relivant.
22100 * <script type="text/javascript">
22104 * @class Roo.form.DateField
22105 * @extends Roo.form.TriggerField
22106 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22108 * Create a new DateField
22109 * @param {Object} config
22111 Roo.form.DateField = function(config){
22112 Roo.form.DateField.superclass.constructor.call(this, config);
22118 * Fires when a date is selected
22119 * @param {Roo.form.DateField} combo This combo box
22120 * @param {Date} date The date selected
22127 if(typeof this.minValue == "string") {
22128 this.minValue = this.parseDate(this.minValue);
22130 if(typeof this.maxValue == "string") {
22131 this.maxValue = this.parseDate(this.maxValue);
22133 this.ddMatch = null;
22134 if(this.disabledDates){
22135 var dd = this.disabledDates;
22137 for(var i = 0; i < dd.length; i++){
22139 if(i != dd.length-1) {
22143 this.ddMatch = new RegExp(re + ")");
22147 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
22149 * @cfg {String} format
22150 * The default date format string which can be overriden for localization support. The format must be
22151 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22155 * @cfg {String} altFormats
22156 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22157 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22159 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22161 * @cfg {Array} disabledDays
22162 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22164 disabledDays : null,
22166 * @cfg {String} disabledDaysText
22167 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22169 disabledDaysText : "Disabled",
22171 * @cfg {Array} disabledDates
22172 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22173 * expression so they are very powerful. Some examples:
22175 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22176 * <li>["03/08", "09/16"] would disable those days for every year</li>
22177 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22178 * <li>["03/../2006"] would disable every day in March 2006</li>
22179 * <li>["^03"] would disable every day in every March</li>
22181 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22182 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22184 disabledDates : null,
22186 * @cfg {String} disabledDatesText
22187 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22189 disabledDatesText : "Disabled",
22191 * @cfg {Date/String} minValue
22192 * The minimum allowed date. Can be either a Javascript date object or a string date in a
22193 * valid format (defaults to null).
22197 * @cfg {Date/String} maxValue
22198 * The maximum allowed date. Can be either a Javascript date object or a string date in a
22199 * valid format (defaults to null).
22203 * @cfg {String} minText
22204 * The error text to display when the date in the cell is before minValue (defaults to
22205 * 'The date in this field must be after {minValue}').
22207 minText : "The date in this field must be equal to or after {0}",
22209 * @cfg {String} maxText
22210 * The error text to display when the date in the cell is after maxValue (defaults to
22211 * 'The date in this field must be before {maxValue}').
22213 maxText : "The date in this field must be equal to or before {0}",
22215 * @cfg {String} invalidText
22216 * The error text to display when the date in the field is invalid (defaults to
22217 * '{value} is not a valid date - it must be in the format {format}').
22219 invalidText : "{0} is not a valid date - it must be in the format {1}",
22221 * @cfg {String} triggerClass
22222 * An additional CSS class used to style the trigger button. The trigger will always get the
22223 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22224 * which displays a calendar icon).
22226 triggerClass : 'x-form-date-trigger',
22230 * @cfg {Boolean} useIso
22231 * if enabled, then the date field will use a hidden field to store the
22232 * real value as iso formated date. default (false)
22236 * @cfg {String/Object} autoCreate
22237 * A DomHelper element spec, or true for a default element spec (defaults to
22238 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22241 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22244 hiddenField: false,
22246 onRender : function(ct, position)
22248 Roo.form.DateField.superclass.onRender.call(this, ct, position);
22250 //this.el.dom.removeAttribute('name');
22251 Roo.log("Changing name?");
22252 this.el.dom.setAttribute('name', this.name + '____hidden___' );
22253 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22255 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22256 // prevent input submission
22257 this.hiddenName = this.name;
22264 validateValue : function(value)
22266 value = this.formatDate(value);
22267 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22268 Roo.log('super failed');
22271 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22274 var svalue = value;
22275 value = this.parseDate(value);
22277 Roo.log('parse date failed' + svalue);
22278 this.markInvalid(String.format(this.invalidText, svalue, this.format));
22281 var time = value.getTime();
22282 if(this.minValue && time < this.minValue.getTime()){
22283 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22286 if(this.maxValue && time > this.maxValue.getTime()){
22287 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22290 if(this.disabledDays){
22291 var day = value.getDay();
22292 for(var i = 0; i < this.disabledDays.length; i++) {
22293 if(day === this.disabledDays[i]){
22294 this.markInvalid(this.disabledDaysText);
22299 var fvalue = this.formatDate(value);
22300 if(this.ddMatch && this.ddMatch.test(fvalue)){
22301 this.markInvalid(String.format(this.disabledDatesText, fvalue));
22308 // Provides logic to override the default TriggerField.validateBlur which just returns true
22309 validateBlur : function(){
22310 return !this.menu || !this.menu.isVisible();
22313 getName: function()
22315 // returns hidden if it's set..
22316 if (!this.rendered) {return ''};
22317 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
22322 * Returns the current date value of the date field.
22323 * @return {Date} The date value
22325 getValue : function(){
22327 return this.hiddenField ?
22328 this.hiddenField.value :
22329 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22333 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
22334 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22335 * (the default format used is "m/d/y").
22338 //All of these calls set the same date value (May 4, 2006)
22340 //Pass a date object:
22341 var dt = new Date('5/4/06');
22342 dateField.setValue(dt);
22344 //Pass a date string (default format):
22345 dateField.setValue('5/4/06');
22347 //Pass a date string (custom format):
22348 dateField.format = 'Y-m-d';
22349 dateField.setValue('2006-5-4');
22351 * @param {String/Date} date The date or valid date string
22353 setValue : function(date){
22354 if (this.hiddenField) {
22355 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22357 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22358 // make sure the value field is always stored as a date..
22359 this.value = this.parseDate(date);
22365 parseDate : function(value){
22366 if(!value || value instanceof Date){
22369 var v = Date.parseDate(value, this.format);
22370 if (!v && this.useIso) {
22371 v = Date.parseDate(value, 'Y-m-d');
22373 if(!v && this.altFormats){
22374 if(!this.altFormatsArray){
22375 this.altFormatsArray = this.altFormats.split("|");
22377 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22378 v = Date.parseDate(value, this.altFormatsArray[i]);
22385 formatDate : function(date, fmt){
22386 return (!date || !(date instanceof Date)) ?
22387 date : date.dateFormat(fmt || this.format);
22392 select: function(m, d){
22395 this.fireEvent('select', this, d);
22397 show : function(){ // retain focus styling
22401 this.focus.defer(10, this);
22402 var ml = this.menuListeners;
22403 this.menu.un("select", ml.select, this);
22404 this.menu.un("show", ml.show, this);
22405 this.menu.un("hide", ml.hide, this);
22410 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22411 onTriggerClick : function(){
22415 if(this.menu == null){
22416 this.menu = new Roo.menu.DateMenu();
22418 Roo.apply(this.menu.picker, {
22419 showClear: this.allowBlank,
22420 minDate : this.minValue,
22421 maxDate : this.maxValue,
22422 disabledDatesRE : this.ddMatch,
22423 disabledDatesText : this.disabledDatesText,
22424 disabledDays : this.disabledDays,
22425 disabledDaysText : this.disabledDaysText,
22426 format : this.useIso ? 'Y-m-d' : this.format,
22427 minText : String.format(this.minText, this.formatDate(this.minValue)),
22428 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22430 this.menu.on(Roo.apply({}, this.menuListeners, {
22433 this.menu.picker.setValue(this.getValue() || new Date());
22434 this.menu.show(this.el, "tl-bl?");
22437 beforeBlur : function(){
22438 var v = this.parseDate(this.getRawValue());
22448 isDirty : function() {
22449 if(this.disabled) {
22453 if(typeof(this.startValue) === 'undefined'){
22457 return String(this.getValue()) !== String(this.startValue);
22462 * Ext JS Library 1.1.1
22463 * Copyright(c) 2006-2007, Ext JS, LLC.
22465 * Originally Released Under LGPL - original licence link has changed is not relivant.
22468 * <script type="text/javascript">
22472 * @class Roo.form.MonthField
22473 * @extends Roo.form.TriggerField
22474 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22476 * Create a new MonthField
22477 * @param {Object} config
22479 Roo.form.MonthField = function(config){
22481 Roo.form.MonthField.superclass.constructor.call(this, config);
22487 * Fires when a date is selected
22488 * @param {Roo.form.MonthFieeld} combo This combo box
22489 * @param {Date} date The date selected
22496 if(typeof this.minValue == "string") {
22497 this.minValue = this.parseDate(this.minValue);
22499 if(typeof this.maxValue == "string") {
22500 this.maxValue = this.parseDate(this.maxValue);
22502 this.ddMatch = null;
22503 if(this.disabledDates){
22504 var dd = this.disabledDates;
22506 for(var i = 0; i < dd.length; i++){
22508 if(i != dd.length-1) {
22512 this.ddMatch = new RegExp(re + ")");
22516 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
22518 * @cfg {String} format
22519 * The default date format string which can be overriden for localization support. The format must be
22520 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22524 * @cfg {String} altFormats
22525 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22526 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22528 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22530 * @cfg {Array} disabledDays
22531 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22533 disabledDays : [0,1,2,3,4,5,6],
22535 * @cfg {String} disabledDaysText
22536 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22538 disabledDaysText : "Disabled",
22540 * @cfg {Array} disabledDates
22541 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22542 * expression so they are very powerful. Some examples:
22544 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22545 * <li>["03/08", "09/16"] would disable those days for every year</li>
22546 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22547 * <li>["03/../2006"] would disable every day in March 2006</li>
22548 * <li>["^03"] would disable every day in every March</li>
22550 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22551 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22553 disabledDates : null,
22555 * @cfg {String} disabledDatesText
22556 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22558 disabledDatesText : "Disabled",
22560 * @cfg {Date/String} minValue
22561 * The minimum allowed date. Can be either a Javascript date object or a string date in a
22562 * valid format (defaults to null).
22566 * @cfg {Date/String} maxValue
22567 * The maximum allowed date. Can be either a Javascript date object or a string date in a
22568 * valid format (defaults to null).
22572 * @cfg {String} minText
22573 * The error text to display when the date in the cell is before minValue (defaults to
22574 * 'The date in this field must be after {minValue}').
22576 minText : "The date in this field must be equal to or after {0}",
22578 * @cfg {String} maxTextf
22579 * The error text to display when the date in the cell is after maxValue (defaults to
22580 * 'The date in this field must be before {maxValue}').
22582 maxText : "The date in this field must be equal to or before {0}",
22584 * @cfg {String} invalidText
22585 * The error text to display when the date in the field is invalid (defaults to
22586 * '{value} is not a valid date - it must be in the format {format}').
22588 invalidText : "{0} is not a valid date - it must be in the format {1}",
22590 * @cfg {String} triggerClass
22591 * An additional CSS class used to style the trigger button. The trigger will always get the
22592 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22593 * which displays a calendar icon).
22595 triggerClass : 'x-form-date-trigger',
22599 * @cfg {Boolean} useIso
22600 * if enabled, then the date field will use a hidden field to store the
22601 * real value as iso formated date. default (true)
22605 * @cfg {String/Object} autoCreate
22606 * A DomHelper element spec, or true for a default element spec (defaults to
22607 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22610 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22613 hiddenField: false,
22615 hideMonthPicker : false,
22617 onRender : function(ct, position)
22619 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22621 this.el.dom.removeAttribute('name');
22622 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22624 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22625 // prevent input submission
22626 this.hiddenName = this.name;
22633 validateValue : function(value)
22635 value = this.formatDate(value);
22636 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22639 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22642 var svalue = value;
22643 value = this.parseDate(value);
22645 this.markInvalid(String.format(this.invalidText, svalue, this.format));
22648 var time = value.getTime();
22649 if(this.minValue && time < this.minValue.getTime()){
22650 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22653 if(this.maxValue && time > this.maxValue.getTime()){
22654 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22657 /*if(this.disabledDays){
22658 var day = value.getDay();
22659 for(var i = 0; i < this.disabledDays.length; i++) {
22660 if(day === this.disabledDays[i]){
22661 this.markInvalid(this.disabledDaysText);
22667 var fvalue = this.formatDate(value);
22668 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22669 this.markInvalid(String.format(this.disabledDatesText, fvalue));
22677 // Provides logic to override the default TriggerField.validateBlur which just returns true
22678 validateBlur : function(){
22679 return !this.menu || !this.menu.isVisible();
22683 * Returns the current date value of the date field.
22684 * @return {Date} The date value
22686 getValue : function(){
22690 return this.hiddenField ?
22691 this.hiddenField.value :
22692 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22696 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
22697 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22698 * (the default format used is "m/d/y").
22701 //All of these calls set the same date value (May 4, 2006)
22703 //Pass a date object:
22704 var dt = new Date('5/4/06');
22705 monthField.setValue(dt);
22707 //Pass a date string (default format):
22708 monthField.setValue('5/4/06');
22710 //Pass a date string (custom format):
22711 monthField.format = 'Y-m-d';
22712 monthField.setValue('2006-5-4');
22714 * @param {String/Date} date The date or valid date string
22716 setValue : function(date){
22717 Roo.log('month setValue' + date);
22718 // can only be first of month..
22720 var val = this.parseDate(date);
22722 if (this.hiddenField) {
22723 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22725 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22726 this.value = this.parseDate(date);
22730 parseDate : function(value){
22731 if(!value || value instanceof Date){
22732 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22735 var v = Date.parseDate(value, this.format);
22736 if (!v && this.useIso) {
22737 v = Date.parseDate(value, 'Y-m-d');
22741 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22745 if(!v && this.altFormats){
22746 if(!this.altFormatsArray){
22747 this.altFormatsArray = this.altFormats.split("|");
22749 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22750 v = Date.parseDate(value, this.altFormatsArray[i]);
22757 formatDate : function(date, fmt){
22758 return (!date || !(date instanceof Date)) ?
22759 date : date.dateFormat(fmt || this.format);
22764 select: function(m, d){
22766 this.fireEvent('select', this, d);
22768 show : function(){ // retain focus styling
22772 this.focus.defer(10, this);
22773 var ml = this.menuListeners;
22774 this.menu.un("select", ml.select, this);
22775 this.menu.un("show", ml.show, this);
22776 this.menu.un("hide", ml.hide, this);
22780 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22781 onTriggerClick : function(){
22785 if(this.menu == null){
22786 this.menu = new Roo.menu.DateMenu();
22790 Roo.apply(this.menu.picker, {
22792 showClear: this.allowBlank,
22793 minDate : this.minValue,
22794 maxDate : this.maxValue,
22795 disabledDatesRE : this.ddMatch,
22796 disabledDatesText : this.disabledDatesText,
22798 format : this.useIso ? 'Y-m-d' : this.format,
22799 minText : String.format(this.minText, this.formatDate(this.minValue)),
22800 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22803 this.menu.on(Roo.apply({}, this.menuListeners, {
22811 // hide month picker get's called when we called by 'before hide';
22813 var ignorehide = true;
22814 p.hideMonthPicker = function(disableAnim){
22818 if(this.monthPicker){
22819 Roo.log("hideMonthPicker called");
22820 if(disableAnim === true){
22821 this.monthPicker.hide();
22823 this.monthPicker.slideOut('t', {duration:.2});
22824 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22825 p.fireEvent("select", this, this.value);
22831 Roo.log('picker set value');
22832 Roo.log(this.getValue());
22833 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22834 m.show(this.el, 'tl-bl?');
22835 ignorehide = false;
22836 // this will trigger hideMonthPicker..
22839 // hidden the day picker
22840 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22846 p.showMonthPicker.defer(100, p);
22852 beforeBlur : function(){
22853 var v = this.parseDate(this.getRawValue());
22859 /** @cfg {Boolean} grow @hide */
22860 /** @cfg {Number} growMin @hide */
22861 /** @cfg {Number} growMax @hide */
22868 * Ext JS Library 1.1.1
22869 * Copyright(c) 2006-2007, Ext JS, LLC.
22871 * Originally Released Under LGPL - original licence link has changed is not relivant.
22874 * <script type="text/javascript">
22879 * @class Roo.form.ComboBox
22880 * @extends Roo.form.TriggerField
22881 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22883 * Create a new ComboBox.
22884 * @param {Object} config Configuration options
22886 Roo.form.ComboBox = function(config){
22887 Roo.form.ComboBox.superclass.constructor.call(this, config);
22891 * Fires when the dropdown list is expanded
22892 * @param {Roo.form.ComboBox} combo This combo box
22897 * Fires when the dropdown list is collapsed
22898 * @param {Roo.form.ComboBox} combo This combo box
22902 * @event beforeselect
22903 * Fires before a list item is selected. Return false to cancel the selection.
22904 * @param {Roo.form.ComboBox} combo This combo box
22905 * @param {Roo.data.Record} record The data record returned from the underlying store
22906 * @param {Number} index The index of the selected item in the dropdown list
22908 'beforeselect' : true,
22911 * Fires when a list item is selected
22912 * @param {Roo.form.ComboBox} combo This combo box
22913 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22914 * @param {Number} index The index of the selected item in the dropdown list
22918 * @event beforequery
22919 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22920 * The event object passed has these properties:
22921 * @param {Roo.form.ComboBox} combo This combo box
22922 * @param {String} query The query
22923 * @param {Boolean} forceAll true to force "all" query
22924 * @param {Boolean} cancel true to cancel the query
22925 * @param {Object} e The query event object
22927 'beforequery': true,
22930 * Fires when the 'add' icon is pressed (add a listener to enable add button)
22931 * @param {Roo.form.ComboBox} combo This combo box
22936 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22937 * @param {Roo.form.ComboBox} combo This combo box
22938 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22944 if(this.transform){
22945 this.allowDomMove = false;
22946 var s = Roo.getDom(this.transform);
22947 if(!this.hiddenName){
22948 this.hiddenName = s.name;
22951 this.mode = 'local';
22952 var d = [], opts = s.options;
22953 for(var i = 0, len = opts.length;i < len; i++){
22955 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22957 this.value = value;
22959 d.push([value, o.text]);
22961 this.store = new Roo.data.SimpleStore({
22963 fields: ['value', 'text'],
22966 this.valueField = 'value';
22967 this.displayField = 'text';
22969 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22970 if(!this.lazyRender){
22971 this.target = true;
22972 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22973 s.parentNode.removeChild(s); // remove it
22974 this.render(this.el.parentNode);
22976 s.parentNode.removeChild(s); // remove it
22981 this.store = Roo.factory(this.store, Roo.data);
22984 this.selectedIndex = -1;
22985 if(this.mode == 'local'){
22986 if(config.queryDelay === undefined){
22987 this.queryDelay = 10;
22989 if(config.minChars === undefined){
22995 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22997 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23000 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23001 * rendering into an Roo.Editor, defaults to false)
23004 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23005 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23008 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23011 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23012 * the dropdown list (defaults to undefined, with no header element)
23016 * @cfg {String/Roo.Template} tpl The template to use to render the output
23020 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23022 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23024 listWidth: undefined,
23026 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23027 * mode = 'remote' or 'text' if mode = 'local')
23029 displayField: undefined,
23031 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23032 * mode = 'remote' or 'value' if mode = 'local').
23033 * Note: use of a valueField requires the user make a selection
23034 * in order for a value to be mapped.
23036 valueField: undefined,
23040 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23041 * field's data value (defaults to the underlying DOM element's name)
23043 hiddenName: undefined,
23045 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23049 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23051 selectedClass: 'x-combo-selected',
23053 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
23054 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23055 * which displays a downward arrow icon).
23057 triggerClass : 'x-form-arrow-trigger',
23059 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23063 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23064 * anchor positions (defaults to 'tl-bl')
23066 listAlign: 'tl-bl?',
23068 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23072 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
23073 * query specified by the allQuery config option (defaults to 'query')
23075 triggerAction: 'query',
23077 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23078 * (defaults to 4, does not apply if editable = false)
23082 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23083 * delay (typeAheadDelay) if it matches a known value (defaults to false)
23087 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23088 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23092 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23093 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
23097 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
23098 * when editable = true (defaults to false)
23100 selectOnFocus:false,
23102 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23104 queryParam: 'query',
23106 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
23107 * when mode = 'remote' (defaults to 'Loading...')
23109 loadingText: 'Loading...',
23111 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23115 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23119 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23120 * traditional select (defaults to true)
23124 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23128 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23132 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23133 * listWidth has a higher value)
23137 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23138 * allow the user to set arbitrary text into the field (defaults to false)
23140 forceSelection:false,
23142 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23143 * if typeAhead = true (defaults to 250)
23145 typeAheadDelay : 250,
23147 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23148 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23150 valueNotFoundText : undefined,
23152 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23154 blockFocus : false,
23157 * @cfg {Boolean} disableClear Disable showing of clear button.
23159 disableClear : false,
23161 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
23163 alwaysQuery : false,
23169 // element that contains real text value.. (when hidden is used..)
23172 onRender : function(ct, position){
23173 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23174 if(this.hiddenName){
23175 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
23177 this.hiddenField.value =
23178 this.hiddenValue !== undefined ? this.hiddenValue :
23179 this.value !== undefined ? this.value : '';
23181 // prevent input submission
23182 this.el.dom.removeAttribute('name');
23187 this.el.dom.setAttribute('autocomplete', 'off');
23190 var cls = 'x-combo-list';
23192 this.list = new Roo.Layer({
23193 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23196 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23197 this.list.setWidth(lw);
23198 this.list.swallowEvent('mousewheel');
23199 this.assetHeight = 0;
23202 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23203 this.assetHeight += this.header.getHeight();
23206 this.innerList = this.list.createChild({cls:cls+'-inner'});
23207 this.innerList.on('mouseover', this.onViewOver, this);
23208 this.innerList.on('mousemove', this.onViewMove, this);
23209 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23211 if(this.allowBlank && !this.pageSize && !this.disableClear){
23212 this.footer = this.list.createChild({cls:cls+'-ft'});
23213 this.pageTb = new Roo.Toolbar(this.footer);
23217 this.footer = this.list.createChild({cls:cls+'-ft'});
23218 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23219 {pageSize: this.pageSize});
23223 if (this.pageTb && this.allowBlank && !this.disableClear) {
23225 this.pageTb.add(new Roo.Toolbar.Fill(), {
23226 cls: 'x-btn-icon x-btn-clear',
23228 handler: function()
23231 _this.clearValue();
23232 _this.onSelect(false, -1);
23237 this.assetHeight += this.footer.getHeight();
23242 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23245 this.view = new Roo.View(this.innerList, this.tpl, {
23246 singleSelect:true, store: this.store, selectedClass: this.selectedClass
23249 this.view.on('click', this.onViewClick, this);
23251 this.store.on('beforeload', this.onBeforeLoad, this);
23252 this.store.on('load', this.onLoad, this);
23253 this.store.on('loadexception', this.onLoadException, this);
23255 if(this.resizable){
23256 this.resizer = new Roo.Resizable(this.list, {
23257 pinned:true, handles:'se'
23259 this.resizer.on('resize', function(r, w, h){
23260 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23261 this.listWidth = w;
23262 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23263 this.restrictHeight();
23265 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23267 if(!this.editable){
23268 this.editable = true;
23269 this.setEditable(false);
23273 if (typeof(this.events.add.listeners) != 'undefined') {
23275 this.addicon = this.wrap.createChild(
23276 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
23278 this.addicon.on('click', function(e) {
23279 this.fireEvent('add', this);
23282 if (typeof(this.events.edit.listeners) != 'undefined') {
23284 this.editicon = this.wrap.createChild(
23285 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
23286 if (this.addicon) {
23287 this.editicon.setStyle('margin-left', '40px');
23289 this.editicon.on('click', function(e) {
23291 // we fire even if inothing is selected..
23292 this.fireEvent('edit', this, this.lastData );
23302 initEvents : function(){
23303 Roo.form.ComboBox.superclass.initEvents.call(this);
23305 this.keyNav = new Roo.KeyNav(this.el, {
23306 "up" : function(e){
23307 this.inKeyMode = true;
23311 "down" : function(e){
23312 if(!this.isExpanded()){
23313 this.onTriggerClick();
23315 this.inKeyMode = true;
23320 "enter" : function(e){
23321 this.onViewClick();
23325 "esc" : function(e){
23329 "tab" : function(e){
23330 this.onViewClick(false);
23331 this.fireEvent("specialkey", this, e);
23337 doRelay : function(foo, bar, hname){
23338 if(hname == 'down' || this.scope.isExpanded()){
23339 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23346 this.queryDelay = Math.max(this.queryDelay || 10,
23347 this.mode == 'local' ? 10 : 250);
23348 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23349 if(this.typeAhead){
23350 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23352 if(this.editable !== false){
23353 this.el.on("keyup", this.onKeyUp, this);
23355 if(this.forceSelection){
23356 this.on('blur', this.doForce, this);
23360 onDestroy : function(){
23362 this.view.setStore(null);
23363 this.view.el.removeAllListeners();
23364 this.view.el.remove();
23365 this.view.purgeListeners();
23368 this.list.destroy();
23371 this.store.un('beforeload', this.onBeforeLoad, this);
23372 this.store.un('load', this.onLoad, this);
23373 this.store.un('loadexception', this.onLoadException, this);
23375 Roo.form.ComboBox.superclass.onDestroy.call(this);
23379 fireKey : function(e){
23380 if(e.isNavKeyPress() && !this.list.isVisible()){
23381 this.fireEvent("specialkey", this, e);
23386 onResize: function(w, h){
23387 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23389 if(typeof w != 'number'){
23390 // we do not handle it!?!?
23393 var tw = this.trigger.getWidth();
23394 tw += this.addicon ? this.addicon.getWidth() : 0;
23395 tw += this.editicon ? this.editicon.getWidth() : 0;
23397 this.el.setWidth( this.adjustWidth('input', x));
23399 this.trigger.setStyle('left', x+'px');
23401 if(this.list && this.listWidth === undefined){
23402 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23403 this.list.setWidth(lw);
23404 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23412 * Allow or prevent the user from directly editing the field text. If false is passed,
23413 * the user will only be able to select from the items defined in the dropdown list. This method
23414 * is the runtime equivalent of setting the 'editable' config option at config time.
23415 * @param {Boolean} value True to allow the user to directly edit the field text
23417 setEditable : function(value){
23418 if(value == this.editable){
23421 this.editable = value;
23423 this.el.dom.setAttribute('readOnly', true);
23424 this.el.on('mousedown', this.onTriggerClick, this);
23425 this.el.addClass('x-combo-noedit');
23427 this.el.dom.setAttribute('readOnly', false);
23428 this.el.un('mousedown', this.onTriggerClick, this);
23429 this.el.removeClass('x-combo-noedit');
23434 onBeforeLoad : function(){
23435 if(!this.hasFocus){
23438 this.innerList.update(this.loadingText ?
23439 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23440 this.restrictHeight();
23441 this.selectedIndex = -1;
23445 onLoad : function(){
23446 if(!this.hasFocus){
23449 if(this.store.getCount() > 0){
23451 this.restrictHeight();
23452 if(this.lastQuery == this.allQuery){
23454 this.el.dom.select();
23456 if(!this.selectByValue(this.value, true)){
23457 this.select(0, true);
23461 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23462 this.taTask.delay(this.typeAheadDelay);
23466 this.onEmptyResults();
23471 onLoadException : function()
23474 Roo.log(this.store.reader.jsonData);
23475 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23476 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23482 onTypeAhead : function(){
23483 if(this.store.getCount() > 0){
23484 var r = this.store.getAt(0);
23485 var newValue = r.data[this.displayField];
23486 var len = newValue.length;
23487 var selStart = this.getRawValue().length;
23488 if(selStart != len){
23489 this.setRawValue(newValue);
23490 this.selectText(selStart, newValue.length);
23496 onSelect : function(record, index){
23497 if(this.fireEvent('beforeselect', this, record, index) !== false){
23498 this.setFromData(index > -1 ? record.data : false);
23500 this.fireEvent('select', this, record, index);
23505 * Returns the currently selected field value or empty string if no value is set.
23506 * @return {String} value The selected value
23508 getValue : function(){
23509 if(this.valueField){
23510 return typeof this.value != 'undefined' ? this.value : '';
23512 return Roo.form.ComboBox.superclass.getValue.call(this);
23516 * Clears any text/value currently set in the field
23518 clearValue : function(){
23519 if(this.hiddenField){
23520 this.hiddenField.value = '';
23523 this.setRawValue('');
23524 this.lastSelectionText = '';
23529 * Sets the specified value into the field. If the value finds a match, the corresponding record text
23530 * will be displayed in the field. If the value does not match the data value of an existing item,
23531 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23532 * Otherwise the field will be blank (although the value will still be set).
23533 * @param {String} value The value to match
23535 setValue : function(v){
23537 if(this.valueField){
23538 var r = this.findRecord(this.valueField, v);
23540 text = r.data[this.displayField];
23541 }else if(this.valueNotFoundText !== undefined){
23542 text = this.valueNotFoundText;
23545 this.lastSelectionText = text;
23546 if(this.hiddenField){
23547 this.hiddenField.value = v;
23549 Roo.form.ComboBox.superclass.setValue.call(this, text);
23553 * @property {Object} the last set data for the element
23558 * Sets the value of the field based on a object which is related to the record format for the store.
23559 * @param {Object} value the value to set as. or false on reset?
23561 setFromData : function(o){
23562 var dv = ''; // display value
23563 var vv = ''; // value value..
23565 if (this.displayField) {
23566 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23568 // this is an error condition!!!
23569 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
23572 if(this.valueField){
23573 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23575 if(this.hiddenField){
23576 this.hiddenField.value = vv;
23578 this.lastSelectionText = dv;
23579 Roo.form.ComboBox.superclass.setValue.call(this, dv);
23583 // no hidden field.. - we store the value in 'value', but still display
23584 // display field!!!!
23585 this.lastSelectionText = dv;
23586 Roo.form.ComboBox.superclass.setValue.call(this, dv);
23592 reset : function(){
23593 // overridden so that last data is reset..
23594 this.setValue(this.resetValue);
23595 this.clearInvalid();
23596 this.lastData = false;
23598 this.view.clearSelections();
23602 findRecord : function(prop, value){
23604 if(this.store.getCount() > 0){
23605 this.store.each(function(r){
23606 if(r.data[prop] == value){
23616 getName: function()
23618 // returns hidden if it's set..
23619 if (!this.rendered) {return ''};
23620 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
23624 onViewMove : function(e, t){
23625 this.inKeyMode = false;
23629 onViewOver : function(e, t){
23630 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23633 var item = this.view.findItemFromChild(t);
23635 var index = this.view.indexOf(item);
23636 this.select(index, false);
23641 onViewClick : function(doFocus)
23643 var index = this.view.getSelectedIndexes()[0];
23644 var r = this.store.getAt(index);
23646 this.onSelect(r, index);
23648 if(doFocus !== false && !this.blockFocus){
23654 restrictHeight : function(){
23655 this.innerList.dom.style.height = '';
23656 var inner = this.innerList.dom;
23657 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23658 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23659 this.list.beginUpdate();
23660 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23661 this.list.alignTo(this.el, this.listAlign);
23662 this.list.endUpdate();
23666 onEmptyResults : function(){
23671 * Returns true if the dropdown list is expanded, else false.
23673 isExpanded : function(){
23674 return this.list.isVisible();
23678 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23679 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23680 * @param {String} value The data value of the item to select
23681 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23682 * selected item if it is not currently in view (defaults to true)
23683 * @return {Boolean} True if the value matched an item in the list, else false
23685 selectByValue : function(v, scrollIntoView){
23686 if(v !== undefined && v !== null){
23687 var r = this.findRecord(this.valueField || this.displayField, v);
23689 this.select(this.store.indexOf(r), scrollIntoView);
23697 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23698 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23699 * @param {Number} index The zero-based index of the list item to select
23700 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23701 * selected item if it is not currently in view (defaults to true)
23703 select : function(index, scrollIntoView){
23704 this.selectedIndex = index;
23705 this.view.select(index);
23706 if(scrollIntoView !== false){
23707 var el = this.view.getNode(index);
23709 this.innerList.scrollChildIntoView(el, false);
23715 selectNext : function(){
23716 var ct = this.store.getCount();
23718 if(this.selectedIndex == -1){
23720 }else if(this.selectedIndex < ct-1){
23721 this.select(this.selectedIndex+1);
23727 selectPrev : function(){
23728 var ct = this.store.getCount();
23730 if(this.selectedIndex == -1){
23732 }else if(this.selectedIndex != 0){
23733 this.select(this.selectedIndex-1);
23739 onKeyUp : function(e){
23740 if(this.editable !== false && !e.isSpecialKey()){
23741 this.lastKey = e.getKey();
23742 this.dqTask.delay(this.queryDelay);
23747 validateBlur : function(){
23748 return !this.list || !this.list.isVisible();
23752 initQuery : function(){
23753 this.doQuery(this.getRawValue());
23757 doForce : function(){
23758 if(this.el.dom.value.length > 0){
23759 this.el.dom.value =
23760 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23766 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
23767 * query allowing the query action to be canceled if needed.
23768 * @param {String} query The SQL query to execute
23769 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23770 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
23771 * saved in the current store (defaults to false)
23773 doQuery : function(q, forceAll){
23774 if(q === undefined || q === null){
23779 forceAll: forceAll,
23783 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23787 forceAll = qe.forceAll;
23788 if(forceAll === true || (q.length >= this.minChars)){
23789 if(this.lastQuery != q || this.alwaysQuery){
23790 this.lastQuery = q;
23791 if(this.mode == 'local'){
23792 this.selectedIndex = -1;
23794 this.store.clearFilter();
23796 this.store.filter(this.displayField, q);
23800 this.store.baseParams[this.queryParam] = q;
23802 params: this.getParams(q)
23807 this.selectedIndex = -1;
23814 getParams : function(q){
23816 //p[this.queryParam] = q;
23819 p.limit = this.pageSize;
23825 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23827 collapse : function(){
23828 if(!this.isExpanded()){
23832 Roo.get(document).un('mousedown', this.collapseIf, this);
23833 Roo.get(document).un('mousewheel', this.collapseIf, this);
23834 if (!this.editable) {
23835 Roo.get(document).un('keydown', this.listKeyPress, this);
23837 this.fireEvent('collapse', this);
23841 collapseIf : function(e){
23842 if(!e.within(this.wrap) && !e.within(this.list)){
23848 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23850 expand : function(){
23851 if(this.isExpanded() || !this.hasFocus){
23854 this.list.alignTo(this.el, this.listAlign);
23856 Roo.get(document).on('mousedown', this.collapseIf, this);
23857 Roo.get(document).on('mousewheel', this.collapseIf, this);
23858 if (!this.editable) {
23859 Roo.get(document).on('keydown', this.listKeyPress, this);
23862 this.fireEvent('expand', this);
23866 // Implements the default empty TriggerField.onTriggerClick function
23867 onTriggerClick : function(){
23871 if(this.isExpanded()){
23873 if (!this.blockFocus) {
23878 this.hasFocus = true;
23879 if(this.triggerAction == 'all') {
23880 this.doQuery(this.allQuery, true);
23882 this.doQuery(this.getRawValue());
23884 if (!this.blockFocus) {
23889 listKeyPress : function(e)
23891 //Roo.log('listkeypress');
23892 // scroll to first matching element based on key pres..
23893 if (e.isSpecialKey()) {
23896 var k = String.fromCharCode(e.getKey()).toUpperCase();
23899 var csel = this.view.getSelectedNodes();
23900 var cselitem = false;
23902 var ix = this.view.indexOf(csel[0]);
23903 cselitem = this.store.getAt(ix);
23904 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23910 this.store.each(function(v) {
23912 // start at existing selection.
23913 if (cselitem.id == v.id) {
23919 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23920 match = this.store.indexOf(v);
23925 if (match === false) {
23926 return true; // no more action?
23929 this.view.select(match);
23930 var sn = Roo.get(this.view.getSelectedNodes()[0]);
23931 sn.scrollIntoView(sn.dom.parentNode, false);
23935 * @cfg {Boolean} grow
23939 * @cfg {Number} growMin
23943 * @cfg {Number} growMax
23951 * Copyright(c) 2010-2012, Roo J Solutions Limited
23958 * @class Roo.form.ComboBoxArray
23959 * @extends Roo.form.TextField
23960 * A facebook style adder... for lists of email / people / countries etc...
23961 * pick multiple items from a combo box, and shows each one.
23963 * Fred [x] Brian [x] [Pick another |v]
23966 * For this to work: it needs various extra information
23967 * - normal combo problay has
23969 * + displayField, valueField
23971 * For our purpose...
23974 * If we change from 'extends' to wrapping...
23981 * Create a new ComboBoxArray.
23982 * @param {Object} config Configuration options
23986 Roo.form.ComboBoxArray = function(config)
23990 * @event beforeremove
23991 * Fires before remove the value from the list
23992 * @param {Roo.form.ComboBoxArray} _self This combo box array
23993 * @param {Roo.form.ComboBoxArray.Item} item removed item
23995 'beforeremove' : true,
23998 * Fires when remove the value from the list
23999 * @param {Roo.form.ComboBoxArray} _self This combo box array
24000 * @param {Roo.form.ComboBoxArray.Item} item removed item
24007 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24009 this.items = new Roo.util.MixedCollection(false);
24011 // construct the child combo...
24021 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24024 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24029 // behavies liek a hiddne field
24030 inputType: 'hidden',
24032 * @cfg {Number} width The width of the box that displays the selected element
24039 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
24043 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
24045 hiddenName : false,
24048 // private the array of items that are displayed..
24050 // private - the hidden field el.
24052 // private - the filed el..
24055 //validateValue : function() { return true; }, // all values are ok!
24056 //onAddClick: function() { },
24058 onRender : function(ct, position)
24061 // create the standard hidden element
24062 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24065 // give fake names to child combo;
24066 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24067 this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24069 this.combo = Roo.factory(this.combo, Roo.form);
24070 this.combo.onRender(ct, position);
24071 if (typeof(this.combo.width) != 'undefined') {
24072 this.combo.onResize(this.combo.width,0);
24075 this.combo.initEvents();
24077 // assigned so form know we need to do this..
24078 this.store = this.combo.store;
24079 this.valueField = this.combo.valueField;
24080 this.displayField = this.combo.displayField ;
24083 this.combo.wrap.addClass('x-cbarray-grp');
24085 var cbwrap = this.combo.wrap.createChild(
24086 {tag: 'div', cls: 'x-cbarray-cb'},
24091 this.hiddenEl = this.combo.wrap.createChild({
24092 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
24094 this.el = this.combo.wrap.createChild({
24095 tag: 'input', type:'hidden' , name: this.name, value : ''
24097 // this.el.dom.removeAttribute("name");
24100 this.outerWrap = this.combo.wrap;
24101 this.wrap = cbwrap;
24103 this.outerWrap.setWidth(this.width);
24104 this.outerWrap.dom.removeChild(this.el.dom);
24106 this.wrap.dom.appendChild(this.el.dom);
24107 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24108 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24110 this.combo.trigger.setStyle('position','relative');
24111 this.combo.trigger.setStyle('left', '0px');
24112 this.combo.trigger.setStyle('top', '2px');
24114 this.combo.el.setStyle('vertical-align', 'text-bottom');
24116 //this.trigger.setStyle('vertical-align', 'top');
24118 // this should use the code from combo really... on('add' ....)
24122 this.adder = this.outerWrap.createChild(
24123 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
24125 this.adder.on('click', function(e) {
24126 _t.fireEvent('adderclick', this, e);
24130 //this.adder.on('click', this.onAddClick, _t);
24133 this.combo.on('select', function(cb, rec, ix) {
24134 this.addItem(rec.data);
24137 cb.el.dom.value = '';
24138 //cb.lastData = rec.data;
24147 getName: function()
24149 // returns hidden if it's set..
24150 if (!this.rendered) {return ''};
24151 return this.hiddenName ? this.hiddenName : this.name;
24156 onResize: function(w, h){
24159 // not sure if this is needed..
24160 //this.combo.onResize(w,h);
24162 if(typeof w != 'number'){
24163 // we do not handle it!?!?
24166 var tw = this.combo.trigger.getWidth();
24167 tw += this.addicon ? this.addicon.getWidth() : 0;
24168 tw += this.editicon ? this.editicon.getWidth() : 0;
24170 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24172 this.combo.trigger.setStyle('left', '0px');
24174 if(this.list && this.listWidth === undefined){
24175 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24176 this.list.setWidth(lw);
24177 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24184 addItem: function(rec)
24186 var valueField = this.combo.valueField;
24187 var displayField = this.combo.displayField;
24188 if (this.items.indexOfKey(rec[valueField]) > -1) {
24189 //console.log("GOT " + rec.data.id);
24193 var x = new Roo.form.ComboBoxArray.Item({
24194 //id : rec[this.idField],
24196 displayField : displayField ,
24197 tipField : displayField ,
24201 this.items.add(rec[valueField],x);
24202 // add it before the element..
24203 this.updateHiddenEl();
24204 x.render(this.outerWrap, this.wrap.dom);
24205 // add the image handler..
24208 updateHiddenEl : function()
24211 if (!this.hiddenEl) {
24215 var idField = this.combo.valueField;
24217 this.items.each(function(f) {
24218 ar.push(f.data[idField]);
24221 this.hiddenEl.dom.value = ar.join(',');
24227 this.items.clear();
24229 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
24233 this.el.dom.value = '';
24234 if (this.hiddenEl) {
24235 this.hiddenEl.dom.value = '';
24239 getValue: function()
24241 return this.hiddenEl ? this.hiddenEl.dom.value : '';
24243 setValue: function(v) // not a valid action - must use addItems..
24250 if (this.store.isLocal && (typeof(v) == 'string')) {
24251 // then we can use the store to find the values..
24252 // comma seperated at present.. this needs to allow JSON based encoding..
24253 this.hiddenEl.value = v;
24255 Roo.each(v.split(','), function(k) {
24256 Roo.log("CHECK " + this.valueField + ',' + k);
24257 var li = this.store.query(this.valueField, k);
24262 add[this.valueField] = k;
24263 add[this.displayField] = li.item(0).data[this.displayField];
24269 if (typeof(v) == 'object' ) {
24270 // then let's assume it's an array of objects..
24271 Roo.each(v, function(l) {
24279 setFromData: function(v)
24281 // this recieves an object, if setValues is called.
24283 this.el.dom.value = v[this.displayField];
24284 this.hiddenEl.dom.value = v[this.valueField];
24285 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24288 var kv = v[this.valueField];
24289 var dv = v[this.displayField];
24290 kv = typeof(kv) != 'string' ? '' : kv;
24291 dv = typeof(dv) != 'string' ? '' : dv;
24294 var keys = kv.split(',');
24295 var display = dv.split(',');
24296 for (var i = 0 ; i < keys.length; i++) {
24299 add[this.valueField] = keys[i];
24300 add[this.displayField] = display[i];
24308 * Validates the combox array value
24309 * @return {Boolean} True if the value is valid, else false
24311 validate : function(){
24312 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24313 this.clearInvalid();
24319 validateValue : function(value){
24320 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24328 isDirty : function() {
24329 if(this.disabled) {
24334 var d = Roo.decode(String(this.originalValue));
24336 return String(this.getValue()) !== String(this.originalValue);
24339 var originalValue = [];
24341 for (var i = 0; i < d.length; i++){
24342 originalValue.push(d[i][this.valueField]);
24345 return String(this.getValue()) !== String(originalValue.join(','));
24354 * @class Roo.form.ComboBoxArray.Item
24355 * @extends Roo.BoxComponent
24356 * A selected item in the list
24357 * Fred [x] Brian [x] [Pick another |v]
24360 * Create a new item.
24361 * @param {Object} config Configuration options
24364 Roo.form.ComboBoxArray.Item = function(config) {
24365 config.id = Roo.id();
24366 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24369 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24372 displayField : false,
24376 defaultAutoCreate : {
24378 cls: 'x-cbarray-item',
24385 src : Roo.BLANK_IMAGE_URL ,
24393 onRender : function(ct, position)
24395 Roo.form.Field.superclass.onRender.call(this, ct, position);
24398 var cfg = this.getAutoCreate();
24399 this.el = ct.createChild(cfg, position);
24402 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24404 this.el.child('div').dom.innerHTML = this.cb.renderer ?
24405 this.cb.renderer(this.data) :
24406 String.format('{0}',this.data[this.displayField]);
24409 this.el.child('div').dom.setAttribute('qtip',
24410 String.format('{0}',this.data[this.tipField])
24413 this.el.child('img').on('click', this.remove, this);
24417 remove : function()
24419 if(this.cb.disabled){
24423 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24424 this.cb.items.remove(this);
24425 this.el.child('img').un('click', this.remove, this);
24427 this.cb.updateHiddenEl();
24429 this.cb.fireEvent('remove', this.cb, this);
24435 * Ext JS Library 1.1.1
24436 * Copyright(c) 2006-2007, Ext JS, LLC.
24438 * Originally Released Under LGPL - original licence link has changed is not relivant.
24441 * <script type="text/javascript">
24444 * @class Roo.form.Checkbox
24445 * @extends Roo.form.Field
24446 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
24448 * Creates a new Checkbox
24449 * @param {Object} config Configuration options
24451 Roo.form.Checkbox = function(config){
24452 Roo.form.Checkbox.superclass.constructor.call(this, config);
24456 * Fires when the checkbox is checked or unchecked.
24457 * @param {Roo.form.Checkbox} this This checkbox
24458 * @param {Boolean} checked The new checked value
24464 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
24466 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24468 focusClass : undefined,
24470 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24472 fieldClass: "x-form-field",
24474 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24478 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24479 * {tag: "input", type: "checkbox", autocomplete: "off"})
24481 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24483 * @cfg {String} boxLabel The text that appears beside the checkbox
24487 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24491 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24493 valueOff: '0', // value when not checked..
24495 actionMode : 'viewEl',
24498 itemCls : 'x-menu-check-item x-form-item',
24499 groupClass : 'x-menu-group-item',
24500 inputType : 'hidden',
24503 inSetChecked: false, // check that we are not calling self...
24505 inputElement: false, // real input element?
24506 basedOn: false, // ????
24508 isFormField: true, // not sure where this is needed!!!!
24510 onResize : function(){
24511 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24512 if(!this.boxLabel){
24513 this.el.alignTo(this.wrap, 'c-c');
24517 initEvents : function(){
24518 Roo.form.Checkbox.superclass.initEvents.call(this);
24519 this.el.on("click", this.onClick, this);
24520 this.el.on("change", this.onClick, this);
24524 getResizeEl : function(){
24528 getPositionEl : function(){
24533 onRender : function(ct, position){
24534 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24536 if(this.inputValue !== undefined){
24537 this.el.dom.value = this.inputValue;
24540 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24541 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24542 var viewEl = this.wrap.createChild({
24543 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24544 this.viewEl = viewEl;
24545 this.wrap.on('click', this.onClick, this);
24547 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
24548 this.el.on('propertychange', this.setFromHidden, this); //ie
24553 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24554 // viewEl.on('click', this.onClick, this);
24556 //if(this.checked){
24557 this.setChecked(this.checked);
24559 //this.checked = this.el.dom;
24565 initValue : Roo.emptyFn,
24568 * Returns the checked state of the checkbox.
24569 * @return {Boolean} True if checked, else false
24571 getValue : function(){
24573 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24575 return this.valueOff;
24580 onClick : function(){
24581 if (this.disabled) {
24584 this.setChecked(!this.checked);
24586 //if(this.el.dom.checked != this.checked){
24587 // this.setValue(this.el.dom.checked);
24592 * Sets the checked state of the checkbox.
24593 * On is always based on a string comparison between inputValue and the param.
24594 * @param {Boolean/String} value - the value to set
24595 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24597 setValue : function(v,suppressEvent){
24600 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24601 //if(this.el && this.el.dom){
24602 // this.el.dom.checked = this.checked;
24603 // this.el.dom.defaultChecked = this.checked;
24605 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24606 //this.fireEvent("check", this, this.checked);
24609 setChecked : function(state,suppressEvent)
24611 if (this.inSetChecked) {
24612 this.checked = state;
24618 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24620 this.checked = state;
24621 if(suppressEvent !== true){
24622 this.fireEvent('check', this, state);
24624 this.inSetChecked = true;
24625 this.el.dom.value = state ? this.inputValue : this.valueOff;
24626 this.inSetChecked = false;
24629 // handle setting of hidden value by some other method!!?!?
24630 setFromHidden: function()
24635 //console.log("SET FROM HIDDEN");
24636 //alert('setFrom hidden');
24637 this.setValue(this.el.dom.value);
24640 onDestroy : function()
24643 Roo.get(this.viewEl).remove();
24646 Roo.form.Checkbox.superclass.onDestroy.call(this);
24649 setBoxLabel : function(str)
24651 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
24656 * Ext JS Library 1.1.1
24657 * Copyright(c) 2006-2007, Ext JS, LLC.
24659 * Originally Released Under LGPL - original licence link has changed is not relivant.
24662 * <script type="text/javascript">
24666 * @class Roo.form.Radio
24667 * @extends Roo.form.Checkbox
24668 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
24669 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24671 * Creates a new Radio
24672 * @param {Object} config Configuration options
24674 Roo.form.Radio = function(){
24675 Roo.form.Radio.superclass.constructor.apply(this, arguments);
24677 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24678 inputType: 'radio',
24681 * If this radio is part of a group, it will return the selected value
24684 getGroupValue : function(){
24685 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24689 onRender : function(ct, position){
24690 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24692 if(this.inputValue !== undefined){
24693 this.el.dom.value = this.inputValue;
24696 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24697 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24698 //var viewEl = this.wrap.createChild({
24699 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24700 //this.viewEl = viewEl;
24701 //this.wrap.on('click', this.onClick, this);
24703 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
24704 //this.el.on('propertychange', this.setFromHidden, this); //ie
24709 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24710 // viewEl.on('click', this.onClick, this);
24713 this.el.dom.checked = 'checked' ;
24719 });//<script type="text/javascript">
24722 * Based Ext JS Library 1.1.1
24723 * Copyright(c) 2006-2007, Ext JS, LLC.
24729 * @class Roo.HtmlEditorCore
24730 * @extends Roo.Component
24731 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24733 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24736 Roo.HtmlEditorCore = function(config){
24739 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24744 * @event initialize
24745 * Fires when the editor is fully initialized (including the iframe)
24746 * @param {Roo.HtmlEditorCore} this
24751 * Fires when the editor is first receives the focus. Any insertion must wait
24752 * until after this event.
24753 * @param {Roo.HtmlEditorCore} this
24757 * @event beforesync
24758 * Fires before the textarea is updated with content from the editor iframe. Return false
24759 * to cancel the sync.
24760 * @param {Roo.HtmlEditorCore} this
24761 * @param {String} html
24765 * @event beforepush
24766 * Fires before the iframe editor is updated with content from the textarea. Return false
24767 * to cancel the push.
24768 * @param {Roo.HtmlEditorCore} this
24769 * @param {String} html
24774 * Fires when the textarea is updated with content from the editor iframe.
24775 * @param {Roo.HtmlEditorCore} this
24776 * @param {String} html
24781 * Fires when the iframe editor is updated with content from the textarea.
24782 * @param {Roo.HtmlEditorCore} this
24783 * @param {String} html
24788 * @event editorevent
24789 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24790 * @param {Roo.HtmlEditorCore} this
24796 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24798 // defaults : white / black...
24799 this.applyBlacklists();
24806 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24810 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24816 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24821 * @cfg {Number} height (in pixels)
24825 * @cfg {Number} width (in pixels)
24830 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24833 stylesheets: false,
24838 // private properties
24839 validationEvent : false,
24841 initialized : false,
24843 sourceEditMode : false,
24844 onFocus : Roo.emptyFn,
24846 hideMode:'offsets',
24850 // blacklist + whitelisted elements..
24857 * Protected method that will not generally be called directly. It
24858 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24859 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24861 getDocMarkup : function(){
24865 // inherit styels from page...??
24866 if (this.stylesheets === false) {
24868 Roo.get(document.head).select('style').each(function(node) {
24869 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24872 Roo.get(document.head).select('link').each(function(node) {
24873 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24876 } else if (!this.stylesheets.length) {
24878 st = '<style type="text/css">' +
24879 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24885 st += '<style type="text/css">' +
24886 'IMG { cursor: pointer } ' +
24890 return '<html><head>' + st +
24891 //<style type="text/css">' +
24892 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24894 ' </head><body class="roo-htmleditor-body"></body></html>';
24898 onRender : function(ct, position)
24901 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24902 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24905 this.el.dom.style.border = '0 none';
24906 this.el.dom.setAttribute('tabIndex', -1);
24907 this.el.addClass('x-hidden hide');
24911 if(Roo.isIE){ // fix IE 1px bogus margin
24912 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24916 this.frameId = Roo.id();
24920 var iframe = this.owner.wrap.createChild({
24922 cls: 'form-control', // bootstrap..
24924 name: this.frameId,
24925 frameBorder : 'no',
24926 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24931 this.iframe = iframe.dom;
24933 this.assignDocWin();
24935 this.doc.designMode = 'on';
24938 this.doc.write(this.getDocMarkup());
24942 var task = { // must defer to wait for browser to be ready
24944 //console.log("run task?" + this.doc.readyState);
24945 this.assignDocWin();
24946 if(this.doc.body || this.doc.readyState == 'complete'){
24948 this.doc.designMode="on";
24952 Roo.TaskMgr.stop(task);
24953 this.initEditor.defer(10, this);
24960 Roo.TaskMgr.start(task);
24965 onResize : function(w, h)
24967 Roo.log('resize: ' +w + ',' + h );
24968 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24972 if(typeof w == 'number'){
24974 this.iframe.style.width = w + 'px';
24976 if(typeof h == 'number'){
24978 this.iframe.style.height = h + 'px';
24980 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24987 * Toggles the editor between standard and source edit mode.
24988 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24990 toggleSourceEdit : function(sourceEditMode){
24992 this.sourceEditMode = sourceEditMode === true;
24994 if(this.sourceEditMode){
24996 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24999 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25000 //this.iframe.className = '';
25003 //this.setSize(this.owner.wrap.getSize());
25004 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25011 * Protected method that will not generally be called directly. If you need/want
25012 * custom HTML cleanup, this is the method you should override.
25013 * @param {String} html The HTML to be cleaned
25014 * return {String} The cleaned HTML
25016 cleanHtml : function(html){
25017 html = String(html);
25018 if(html.length > 5){
25019 if(Roo.isSafari){ // strip safari nonsense
25020 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25023 if(html == ' '){
25030 * HTML Editor -> Textarea
25031 * Protected method that will not generally be called directly. Syncs the contents
25032 * of the editor iframe with the textarea.
25034 syncValue : function(){
25035 if(this.initialized){
25036 var bd = (this.doc.body || this.doc.documentElement);
25037 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25038 var html = bd.innerHTML;
25040 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25041 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25043 html = '<div style="'+m[0]+'">' + html + '</div>';
25046 html = this.cleanHtml(html);
25047 // fix up the special chars.. normaly like back quotes in word...
25048 // however we do not want to do this with chinese..
25049 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25050 var cc = b.charCodeAt();
25052 (cc >= 0x4E00 && cc < 0xA000 ) ||
25053 (cc >= 0x3400 && cc < 0x4E00 ) ||
25054 (cc >= 0xf900 && cc < 0xfb00 )
25060 if(this.owner.fireEvent('beforesync', this, html) !== false){
25061 this.el.dom.value = html;
25062 this.owner.fireEvent('sync', this, html);
25068 * Protected method that will not generally be called directly. Pushes the value of the textarea
25069 * into the iframe editor.
25071 pushValue : function(){
25072 if(this.initialized){
25073 var v = this.el.dom.value.trim();
25075 // if(v.length < 1){
25079 if(this.owner.fireEvent('beforepush', this, v) !== false){
25080 var d = (this.doc.body || this.doc.documentElement);
25082 this.cleanUpPaste();
25083 this.el.dom.value = d.innerHTML;
25084 this.owner.fireEvent('push', this, v);
25090 deferFocus : function(){
25091 this.focus.defer(10, this);
25095 focus : function(){
25096 if(this.win && !this.sourceEditMode){
25103 assignDocWin: function()
25105 var iframe = this.iframe;
25108 this.doc = iframe.contentWindow.document;
25109 this.win = iframe.contentWindow;
25111 // if (!Roo.get(this.frameId)) {
25114 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25115 // this.win = Roo.get(this.frameId).dom.contentWindow;
25117 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25121 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25122 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25127 initEditor : function(){
25128 //console.log("INIT EDITOR");
25129 this.assignDocWin();
25133 this.doc.designMode="on";
25135 this.doc.write(this.getDocMarkup());
25138 var dbody = (this.doc.body || this.doc.documentElement);
25139 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25140 // this copies styles from the containing element into thsi one..
25141 // not sure why we need all of this..
25142 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25144 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25145 //ss['background-attachment'] = 'fixed'; // w3c
25146 dbody.bgProperties = 'fixed'; // ie
25147 //Roo.DomHelper.applyStyles(dbody, ss);
25148 Roo.EventManager.on(this.doc, {
25149 //'mousedown': this.onEditorEvent,
25150 'mouseup': this.onEditorEvent,
25151 'dblclick': this.onEditorEvent,
25152 'click': this.onEditorEvent,
25153 'keyup': this.onEditorEvent,
25158 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25160 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25161 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25163 this.initialized = true;
25165 this.owner.fireEvent('initialize', this);
25170 onDestroy : function(){
25176 //for (var i =0; i < this.toolbars.length;i++) {
25177 // // fixme - ask toolbars for heights?
25178 // this.toolbars[i].onDestroy();
25181 //this.wrap.dom.innerHTML = '';
25182 //this.wrap.remove();
25187 onFirstFocus : function(){
25189 this.assignDocWin();
25192 this.activated = true;
25195 if(Roo.isGecko){ // prevent silly gecko errors
25197 var s = this.win.getSelection();
25198 if(!s.focusNode || s.focusNode.nodeType != 3){
25199 var r = s.getRangeAt(0);
25200 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25205 this.execCmd('useCSS', true);
25206 this.execCmd('styleWithCSS', false);
25209 this.owner.fireEvent('activate', this);
25213 adjustFont: function(btn){
25214 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25215 //if(Roo.isSafari){ // safari
25218 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25219 if(Roo.isSafari){ // safari
25220 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25221 v = (v < 10) ? 10 : v;
25222 v = (v > 48) ? 48 : v;
25223 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25228 v = Math.max(1, v+adjust);
25230 this.execCmd('FontSize', v );
25233 onEditorEvent : function(e)
25235 this.owner.fireEvent('editorevent', this, e);
25236 // this.updateToolbar();
25237 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25240 insertTag : function(tg)
25242 // could be a bit smarter... -> wrap the current selected tRoo..
25243 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25245 range = this.createRange(this.getSelection());
25246 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25247 wrappingNode.appendChild(range.extractContents());
25248 range.insertNode(wrappingNode);
25255 this.execCmd("formatblock", tg);
25259 insertText : function(txt)
25263 var range = this.createRange();
25264 range.deleteContents();
25265 //alert(Sender.getAttribute('label'));
25267 range.insertNode(this.doc.createTextNode(txt));
25273 * Executes a Midas editor command on the editor document and performs necessary focus and
25274 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25275 * @param {String} cmd The Midas command
25276 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25278 relayCmd : function(cmd, value){
25280 this.execCmd(cmd, value);
25281 this.owner.fireEvent('editorevent', this);
25282 //this.updateToolbar();
25283 this.owner.deferFocus();
25287 * Executes a Midas editor command directly on the editor document.
25288 * For visual commands, you should use {@link #relayCmd} instead.
25289 * <b>This should only be called after the editor is initialized.</b>
25290 * @param {String} cmd The Midas command
25291 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25293 execCmd : function(cmd, value){
25294 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25301 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25303 * @param {String} text | dom node..
25305 insertAtCursor : function(text)
25310 if(!this.activated){
25316 var r = this.doc.selection.createRange();
25327 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25331 // from jquery ui (MIT licenced)
25333 var win = this.win;
25335 if (win.getSelection && win.getSelection().getRangeAt) {
25336 range = win.getSelection().getRangeAt(0);
25337 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25338 range.insertNode(node);
25339 } else if (win.document.selection && win.document.selection.createRange) {
25340 // no firefox support
25341 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25342 win.document.selection.createRange().pasteHTML(txt);
25344 // no firefox support
25345 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25346 this.execCmd('InsertHTML', txt);
25355 mozKeyPress : function(e){
25357 var c = e.getCharCode(), cmd;
25360 c = String.fromCharCode(c).toLowerCase();
25374 this.cleanUpPaste.defer(100, this);
25382 e.preventDefault();
25390 fixKeys : function(){ // load time branching for fastest keydown performance
25392 return function(e){
25393 var k = e.getKey(), r;
25396 r = this.doc.selection.createRange();
25399 r.pasteHTML('    ');
25406 r = this.doc.selection.createRange();
25408 var target = r.parentElement();
25409 if(!target || target.tagName.toLowerCase() != 'li'){
25411 r.pasteHTML('<br />');
25417 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25418 this.cleanUpPaste.defer(100, this);
25424 }else if(Roo.isOpera){
25425 return function(e){
25426 var k = e.getKey();
25430 this.execCmd('InsertHTML','    ');
25433 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25434 this.cleanUpPaste.defer(100, this);
25439 }else if(Roo.isSafari){
25440 return function(e){
25441 var k = e.getKey();
25445 this.execCmd('InsertText','\t');
25449 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25450 this.cleanUpPaste.defer(100, this);
25458 getAllAncestors: function()
25460 var p = this.getSelectedNode();
25463 a.push(p); // push blank onto stack..
25464 p = this.getParentElement();
25468 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25472 a.push(this.doc.body);
25476 lastSelNode : false,
25479 getSelection : function()
25481 this.assignDocWin();
25482 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25485 getSelectedNode: function()
25487 // this may only work on Gecko!!!
25489 // should we cache this!!!!
25494 var range = this.createRange(this.getSelection()).cloneRange();
25497 var parent = range.parentElement();
25499 var testRange = range.duplicate();
25500 testRange.moveToElementText(parent);
25501 if (testRange.inRange(range)) {
25504 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25507 parent = parent.parentElement;
25512 // is ancestor a text element.
25513 var ac = range.commonAncestorContainer;
25514 if (ac.nodeType == 3) {
25515 ac = ac.parentNode;
25518 var ar = ac.childNodes;
25521 var other_nodes = [];
25522 var has_other_nodes = false;
25523 for (var i=0;i<ar.length;i++) {
25524 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25527 // fullly contained node.
25529 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25534 // probably selected..
25535 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25536 other_nodes.push(ar[i]);
25540 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25545 has_other_nodes = true;
25547 if (!nodes.length && other_nodes.length) {
25548 nodes= other_nodes;
25550 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25556 createRange: function(sel)
25558 // this has strange effects when using with
25559 // top toolbar - not sure if it's a great idea.
25560 //this.editor.contentWindow.focus();
25561 if (typeof sel != "undefined") {
25563 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25565 return this.doc.createRange();
25568 return this.doc.createRange();
25571 getParentElement: function()
25574 this.assignDocWin();
25575 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25577 var range = this.createRange(sel);
25580 var p = range.commonAncestorContainer;
25581 while (p.nodeType == 3) { // text node
25592 * Range intersection.. the hard stuff...
25596 * [ -- selected range --- ]
25600 * if end is before start or hits it. fail.
25601 * if start is after end or hits it fail.
25603 * if either hits (but other is outside. - then it's not
25609 // @see http://www.thismuchiknow.co.uk/?p=64.
25610 rangeIntersectsNode : function(range, node)
25612 var nodeRange = node.ownerDocument.createRange();
25614 nodeRange.selectNode(node);
25616 nodeRange.selectNodeContents(node);
25619 var rangeStartRange = range.cloneRange();
25620 rangeStartRange.collapse(true);
25622 var rangeEndRange = range.cloneRange();
25623 rangeEndRange.collapse(false);
25625 var nodeStartRange = nodeRange.cloneRange();
25626 nodeStartRange.collapse(true);
25628 var nodeEndRange = nodeRange.cloneRange();
25629 nodeEndRange.collapse(false);
25631 return rangeStartRange.compareBoundaryPoints(
25632 Range.START_TO_START, nodeEndRange) == -1 &&
25633 rangeEndRange.compareBoundaryPoints(
25634 Range.START_TO_START, nodeStartRange) == 1;
25638 rangeCompareNode : function(range, node)
25640 var nodeRange = node.ownerDocument.createRange();
25642 nodeRange.selectNode(node);
25644 nodeRange.selectNodeContents(node);
25648 range.collapse(true);
25650 nodeRange.collapse(true);
25652 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25653 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25655 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25657 var nodeIsBefore = ss == 1;
25658 var nodeIsAfter = ee == -1;
25660 if (nodeIsBefore && nodeIsAfter) {
25663 if (!nodeIsBefore && nodeIsAfter) {
25664 return 1; //right trailed.
25667 if (nodeIsBefore && !nodeIsAfter) {
25668 return 2; // left trailed.
25674 // private? - in a new class?
25675 cleanUpPaste : function()
25677 // cleans up the whole document..
25678 Roo.log('cleanuppaste');
25680 this.cleanUpChildren(this.doc.body);
25681 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25682 if (clean != this.doc.body.innerHTML) {
25683 this.doc.body.innerHTML = clean;
25688 cleanWordChars : function(input) {// change the chars to hex code
25689 var he = Roo.HtmlEditorCore;
25691 var output = input;
25692 Roo.each(he.swapCodes, function(sw) {
25693 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25695 output = output.replace(swapper, sw[1]);
25702 cleanUpChildren : function (n)
25704 if (!n.childNodes.length) {
25707 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25708 this.cleanUpChild(n.childNodes[i]);
25715 cleanUpChild : function (node)
25718 //console.log(node);
25719 if (node.nodeName == "#text") {
25720 // clean up silly Windows -- stuff?
25723 if (node.nodeName == "#comment") {
25724 node.parentNode.removeChild(node);
25725 // clean up silly Windows -- stuff?
25728 var lcname = node.tagName.toLowerCase();
25729 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25730 // whitelist of tags..
25732 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25734 node.parentNode.removeChild(node);
25739 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25741 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25742 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25744 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25745 // remove_keep_children = true;
25748 if (remove_keep_children) {
25749 this.cleanUpChildren(node);
25750 // inserts everything just before this node...
25751 while (node.childNodes.length) {
25752 var cn = node.childNodes[0];
25753 node.removeChild(cn);
25754 node.parentNode.insertBefore(cn, node);
25756 node.parentNode.removeChild(node);
25760 if (!node.attributes || !node.attributes.length) {
25761 this.cleanUpChildren(node);
25765 function cleanAttr(n,v)
25768 if (v.match(/^\./) || v.match(/^\//)) {
25771 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25774 if (v.match(/^#/)) {
25777 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25778 node.removeAttribute(n);
25782 var cwhite = this.cwhite;
25783 var cblack = this.cblack;
25785 function cleanStyle(n,v)
25787 if (v.match(/expression/)) { //XSS?? should we even bother..
25788 node.removeAttribute(n);
25792 var parts = v.split(/;/);
25795 Roo.each(parts, function(p) {
25796 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25800 var l = p.split(':').shift().replace(/\s+/g,'');
25801 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25803 if ( cwhite.length && cblack.indexOf(l) > -1) {
25804 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25805 //node.removeAttribute(n);
25809 // only allow 'c whitelisted system attributes'
25810 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25811 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25812 //node.removeAttribute(n);
25822 if (clean.length) {
25823 node.setAttribute(n, clean.join(';'));
25825 node.removeAttribute(n);
25831 for (var i = node.attributes.length-1; i > -1 ; i--) {
25832 var a = node.attributes[i];
25835 if (a.name.toLowerCase().substr(0,2)=='on') {
25836 node.removeAttribute(a.name);
25839 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25840 node.removeAttribute(a.name);
25843 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25844 cleanAttr(a.name,a.value); // fixme..
25847 if (a.name == 'style') {
25848 cleanStyle(a.name,a.value);
25851 /// clean up MS crap..
25852 // tecnically this should be a list of valid class'es..
25855 if (a.name == 'class') {
25856 if (a.value.match(/^Mso/)) {
25857 node.className = '';
25860 if (a.value.match(/body/)) {
25861 node.className = '';
25872 this.cleanUpChildren(node);
25878 * Clean up MS wordisms...
25880 cleanWord : function(node)
25885 this.cleanWord(this.doc.body);
25888 if (node.nodeName == "#text") {
25889 // clean up silly Windows -- stuff?
25892 if (node.nodeName == "#comment") {
25893 node.parentNode.removeChild(node);
25894 // clean up silly Windows -- stuff?
25898 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25899 node.parentNode.removeChild(node);
25903 // remove - but keep children..
25904 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25905 while (node.childNodes.length) {
25906 var cn = node.childNodes[0];
25907 node.removeChild(cn);
25908 node.parentNode.insertBefore(cn, node);
25910 node.parentNode.removeChild(node);
25911 this.iterateChildren(node, this.cleanWord);
25915 if (node.className.length) {
25917 var cn = node.className.split(/\W+/);
25919 Roo.each(cn, function(cls) {
25920 if (cls.match(/Mso[a-zA-Z]+/)) {
25925 node.className = cna.length ? cna.join(' ') : '';
25927 node.removeAttribute("class");
25931 if (node.hasAttribute("lang")) {
25932 node.removeAttribute("lang");
25935 if (node.hasAttribute("style")) {
25937 var styles = node.getAttribute("style").split(";");
25939 Roo.each(styles, function(s) {
25940 if (!s.match(/:/)) {
25943 var kv = s.split(":");
25944 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25947 // what ever is left... we allow.
25950 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25951 if (!nstyle.length) {
25952 node.removeAttribute('style');
25955 this.iterateChildren(node, this.cleanWord);
25961 * iterateChildren of a Node, calling fn each time, using this as the scole..
25962 * @param {DomNode} node node to iterate children of.
25963 * @param {Function} fn method of this class to call on each item.
25965 iterateChildren : function(node, fn)
25967 if (!node.childNodes.length) {
25970 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25971 fn.call(this, node.childNodes[i])
25977 * cleanTableWidths.
25979 * Quite often pasting from word etc.. results in tables with column and widths.
25980 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25983 cleanTableWidths : function(node)
25988 this.cleanTableWidths(this.doc.body);
25993 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25996 Roo.log(node.tagName);
25997 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25998 this.iterateChildren(node, this.cleanTableWidths);
26001 if (node.hasAttribute('width')) {
26002 node.removeAttribute('width');
26006 if (node.hasAttribute("style")) {
26009 var styles = node.getAttribute("style").split(";");
26011 Roo.each(styles, function(s) {
26012 if (!s.match(/:/)) {
26015 var kv = s.split(":");
26016 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26019 // what ever is left... we allow.
26022 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26023 if (!nstyle.length) {
26024 node.removeAttribute('style');
26028 this.iterateChildren(node, this.cleanTableWidths);
26036 domToHTML : function(currentElement, depth, nopadtext) {
26038 depth = depth || 0;
26039 nopadtext = nopadtext || false;
26041 if (!currentElement) {
26042 return this.domToHTML(this.doc.body);
26045 //Roo.log(currentElement);
26047 var allText = false;
26048 var nodeName = currentElement.nodeName;
26049 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26051 if (nodeName == '#text') {
26053 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26058 if (nodeName != 'BODY') {
26061 // Prints the node tagName, such as <A>, <IMG>, etc
26064 for(i = 0; i < currentElement.attributes.length;i++) {
26066 var aname = currentElement.attributes.item(i).name;
26067 if (!currentElement.attributes.item(i).value.length) {
26070 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26073 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26082 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26085 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26090 // Traverse the tree
26092 var currentElementChild = currentElement.childNodes.item(i);
26093 var allText = true;
26094 var innerHTML = '';
26096 while (currentElementChild) {
26097 // Formatting code (indent the tree so it looks nice on the screen)
26098 var nopad = nopadtext;
26099 if (lastnode == 'SPAN') {
26103 if (currentElementChild.nodeName == '#text') {
26104 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26105 toadd = nopadtext ? toadd : toadd.trim();
26106 if (!nopad && toadd.length > 80) {
26107 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26109 innerHTML += toadd;
26112 currentElementChild = currentElement.childNodes.item(i);
26118 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26120 // Recursively traverse the tree structure of the child node
26121 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26122 lastnode = currentElementChild.nodeName;
26124 currentElementChild=currentElement.childNodes.item(i);
26130 // The remaining code is mostly for formatting the tree
26131 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26136 ret+= "</"+tagName+">";
26142 applyBlacklists : function()
26144 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26145 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26149 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26150 if (b.indexOf(tag) > -1) {
26153 this.white.push(tag);
26157 Roo.each(w, function(tag) {
26158 if (b.indexOf(tag) > -1) {
26161 if (this.white.indexOf(tag) > -1) {
26164 this.white.push(tag);
26169 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26170 if (w.indexOf(tag) > -1) {
26173 this.black.push(tag);
26177 Roo.each(b, function(tag) {
26178 if (w.indexOf(tag) > -1) {
26181 if (this.black.indexOf(tag) > -1) {
26184 this.black.push(tag);
26189 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26190 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26194 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26195 if (b.indexOf(tag) > -1) {
26198 this.cwhite.push(tag);
26202 Roo.each(w, function(tag) {
26203 if (b.indexOf(tag) > -1) {
26206 if (this.cwhite.indexOf(tag) > -1) {
26209 this.cwhite.push(tag);
26214 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26215 if (w.indexOf(tag) > -1) {
26218 this.cblack.push(tag);
26222 Roo.each(b, function(tag) {
26223 if (w.indexOf(tag) > -1) {
26226 if (this.cblack.indexOf(tag) > -1) {
26229 this.cblack.push(tag);
26234 setStylesheets : function(stylesheets)
26236 if(typeof(stylesheets) == 'string'){
26237 Roo.get(this.iframe.contentDocument.head).createChild({
26239 rel : 'stylesheet',
26248 Roo.each(stylesheets, function(s) {
26253 Roo.get(_this.iframe.contentDocument.head).createChild({
26255 rel : 'stylesheet',
26264 removeStylesheets : function()
26268 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26273 // hide stuff that is not compatible
26287 * @event specialkey
26291 * @cfg {String} fieldClass @hide
26294 * @cfg {String} focusClass @hide
26297 * @cfg {String} autoCreate @hide
26300 * @cfg {String} inputType @hide
26303 * @cfg {String} invalidClass @hide
26306 * @cfg {String} invalidText @hide
26309 * @cfg {String} msgFx @hide
26312 * @cfg {String} validateOnBlur @hide
26316 Roo.HtmlEditorCore.white = [
26317 'area', 'br', 'img', 'input', 'hr', 'wbr',
26319 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26320 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26321 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26322 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26323 'table', 'ul', 'xmp',
26325 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26328 'dir', 'menu', 'ol', 'ul', 'dl',
26334 Roo.HtmlEditorCore.black = [
26335 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26337 'base', 'basefont', 'bgsound', 'blink', 'body',
26338 'frame', 'frameset', 'head', 'html', 'ilayer',
26339 'iframe', 'layer', 'link', 'meta', 'object',
26340 'script', 'style' ,'title', 'xml' // clean later..
26342 Roo.HtmlEditorCore.clean = [
26343 'script', 'style', 'title', 'xml'
26345 Roo.HtmlEditorCore.remove = [
26350 Roo.HtmlEditorCore.ablack = [
26354 Roo.HtmlEditorCore.aclean = [
26355 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26359 Roo.HtmlEditorCore.pwhite= [
26360 'http', 'https', 'mailto'
26363 // white listed style attributes.
26364 Roo.HtmlEditorCore.cwhite= [
26365 // 'text-align', /// default is to allow most things..
26371 // black listed style attributes.
26372 Roo.HtmlEditorCore.cblack= [
26373 // 'font-size' -- this can be set by the project
26377 Roo.HtmlEditorCore.swapCodes =[
26388 //<script type="text/javascript">
26391 * Ext JS Library 1.1.1
26392 * Copyright(c) 2006-2007, Ext JS, LLC.
26398 Roo.form.HtmlEditor = function(config){
26402 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26404 if (!this.toolbars) {
26405 this.toolbars = [];
26407 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26413 * @class Roo.form.HtmlEditor
26414 * @extends Roo.form.Field
26415 * Provides a lightweight HTML Editor component.
26417 * This has been tested on Fireforx / Chrome.. IE may not be so great..
26419 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26420 * supported by this editor.</b><br/><br/>
26421 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26422 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26424 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26426 * @cfg {Boolean} clearUp
26430 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26435 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26440 * @cfg {Number} height (in pixels)
26444 * @cfg {Number} width (in pixels)
26449 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26452 stylesheets: false,
26456 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26461 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26467 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26472 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26480 // private properties
26481 validationEvent : false,
26483 initialized : false,
26486 onFocus : Roo.emptyFn,
26488 hideMode:'offsets',
26490 actionMode : 'container', // defaults to hiding it...
26492 defaultAutoCreate : { // modified by initCompnoent..
26494 style:"width:500px;height:300px;",
26495 autocomplete: "new-password"
26499 initComponent : function(){
26502 * @event initialize
26503 * Fires when the editor is fully initialized (including the iframe)
26504 * @param {HtmlEditor} this
26509 * Fires when the editor is first receives the focus. Any insertion must wait
26510 * until after this event.
26511 * @param {HtmlEditor} this
26515 * @event beforesync
26516 * Fires before the textarea is updated with content from the editor iframe. Return false
26517 * to cancel the sync.
26518 * @param {HtmlEditor} this
26519 * @param {String} html
26523 * @event beforepush
26524 * Fires before the iframe editor is updated with content from the textarea. Return false
26525 * to cancel the push.
26526 * @param {HtmlEditor} this
26527 * @param {String} html
26532 * Fires when the textarea is updated with content from the editor iframe.
26533 * @param {HtmlEditor} this
26534 * @param {String} html
26539 * Fires when the iframe editor is updated with content from the textarea.
26540 * @param {HtmlEditor} this
26541 * @param {String} html
26545 * @event editmodechange
26546 * Fires when the editor switches edit modes
26547 * @param {HtmlEditor} this
26548 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26550 editmodechange: true,
26552 * @event editorevent
26553 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26554 * @param {HtmlEditor} this
26558 * @event firstfocus
26559 * Fires when on first focus - needed by toolbars..
26560 * @param {HtmlEditor} this
26565 * Auto save the htmlEditor value as a file into Events
26566 * @param {HtmlEditor} this
26570 * @event savedpreview
26571 * preview the saved version of htmlEditor
26572 * @param {HtmlEditor} this
26574 savedpreview: true,
26577 * @event stylesheetsclick
26578 * Fires when press the Sytlesheets button
26579 * @param {Roo.HtmlEditorCore} this
26581 stylesheetsclick: true
26583 this.defaultAutoCreate = {
26585 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26586 autocomplete: "new-password"
26591 * Protected method that will not generally be called directly. It
26592 * is called when the editor creates its toolbar. Override this method if you need to
26593 * add custom toolbar buttons.
26594 * @param {HtmlEditor} editor
26596 createToolbar : function(editor){
26597 Roo.log("create toolbars");
26598 if (!editor.toolbars || !editor.toolbars.length) {
26599 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26602 for (var i =0 ; i < editor.toolbars.length;i++) {
26603 editor.toolbars[i] = Roo.factory(
26604 typeof(editor.toolbars[i]) == 'string' ?
26605 { xtype: editor.toolbars[i]} : editor.toolbars[i],
26606 Roo.form.HtmlEditor);
26607 editor.toolbars[i].init(editor);
26615 onRender : function(ct, position)
26618 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26620 this.wrap = this.el.wrap({
26621 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26624 this.editorcore.onRender(ct, position);
26626 if (this.resizable) {
26627 this.resizeEl = new Roo.Resizable(this.wrap, {
26631 minHeight : this.height,
26632 height: this.height,
26633 handles : this.resizable,
26636 resize : function(r, w, h) {
26637 _t.onResize(w,h); // -something
26643 this.createToolbar(this);
26647 this.setSize(this.wrap.getSize());
26649 if (this.resizeEl) {
26650 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26651 // should trigger onReize..
26654 this.keyNav = new Roo.KeyNav(this.el, {
26656 "tab" : function(e){
26657 e.preventDefault();
26659 var value = this.getValue();
26661 var start = this.el.dom.selectionStart;
26662 var end = this.el.dom.selectionEnd;
26666 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26667 this.el.dom.setSelectionRange(end + 1, end + 1);
26671 var f = value.substring(0, start).split("\t");
26673 if(f.pop().length != 0){
26677 this.setValue(f.join("\t") + value.substring(end));
26678 this.el.dom.setSelectionRange(start - 1, start - 1);
26682 "home" : function(e){
26683 e.preventDefault();
26685 var curr = this.el.dom.selectionStart;
26686 var lines = this.getValue().split("\n");
26693 this.el.dom.setSelectionRange(0, 0);
26699 for (var i = 0; i < lines.length;i++) {
26700 pos += lines[i].length;
26710 pos -= lines[i].length;
26716 this.el.dom.setSelectionRange(pos, pos);
26720 this.el.dom.selectionStart = pos;
26721 this.el.dom.selectionEnd = curr;
26724 "end" : function(e){
26725 e.preventDefault();
26727 var curr = this.el.dom.selectionStart;
26728 var lines = this.getValue().split("\n");
26735 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26741 for (var i = 0; i < lines.length;i++) {
26743 pos += lines[i].length;
26757 this.el.dom.setSelectionRange(pos, pos);
26761 this.el.dom.selectionStart = curr;
26762 this.el.dom.selectionEnd = pos;
26767 doRelay : function(foo, bar, hname){
26768 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26774 // if(this.autosave && this.w){
26775 // this.autoSaveFn = setInterval(this.autosave, 1000);
26780 onResize : function(w, h)
26782 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26787 if(typeof w == 'number'){
26788 var aw = w - this.wrap.getFrameWidth('lr');
26789 this.el.setWidth(this.adjustWidth('textarea', aw));
26792 if(typeof h == 'number'){
26794 for (var i =0; i < this.toolbars.length;i++) {
26795 // fixme - ask toolbars for heights?
26796 tbh += this.toolbars[i].tb.el.getHeight();
26797 if (this.toolbars[i].footer) {
26798 tbh += this.toolbars[i].footer.el.getHeight();
26805 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26806 ah -= 5; // knock a few pixes off for look..
26808 this.el.setHeight(this.adjustWidth('textarea', ah));
26812 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26813 this.editorcore.onResize(ew,eh);
26818 * Toggles the editor between standard and source edit mode.
26819 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26821 toggleSourceEdit : function(sourceEditMode)
26823 this.editorcore.toggleSourceEdit(sourceEditMode);
26825 if(this.editorcore.sourceEditMode){
26826 Roo.log('editor - showing textarea');
26829 // Roo.log(this.syncValue());
26830 this.editorcore.syncValue();
26831 this.el.removeClass('x-hidden');
26832 this.el.dom.removeAttribute('tabIndex');
26835 for (var i = 0; i < this.toolbars.length; i++) {
26836 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26837 this.toolbars[i].tb.hide();
26838 this.toolbars[i].footer.hide();
26843 Roo.log('editor - hiding textarea');
26845 // Roo.log(this.pushValue());
26846 this.editorcore.pushValue();
26848 this.el.addClass('x-hidden');
26849 this.el.dom.setAttribute('tabIndex', -1);
26851 for (var i = 0; i < this.toolbars.length; i++) {
26852 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26853 this.toolbars[i].tb.show();
26854 this.toolbars[i].footer.show();
26858 //this.deferFocus();
26861 this.setSize(this.wrap.getSize());
26862 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26864 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26867 // private (for BoxComponent)
26868 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26870 // private (for BoxComponent)
26871 getResizeEl : function(){
26875 // private (for BoxComponent)
26876 getPositionEl : function(){
26881 initEvents : function(){
26882 this.originalValue = this.getValue();
26886 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26889 markInvalid : Roo.emptyFn,
26891 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26894 clearInvalid : Roo.emptyFn,
26896 setValue : function(v){
26897 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26898 this.editorcore.pushValue();
26903 deferFocus : function(){
26904 this.focus.defer(10, this);
26908 focus : function(){
26909 this.editorcore.focus();
26915 onDestroy : function(){
26921 for (var i =0; i < this.toolbars.length;i++) {
26922 // fixme - ask toolbars for heights?
26923 this.toolbars[i].onDestroy();
26926 this.wrap.dom.innerHTML = '';
26927 this.wrap.remove();
26932 onFirstFocus : function(){
26933 //Roo.log("onFirstFocus");
26934 this.editorcore.onFirstFocus();
26935 for (var i =0; i < this.toolbars.length;i++) {
26936 this.toolbars[i].onFirstFocus();
26942 syncValue : function()
26944 this.editorcore.syncValue();
26947 pushValue : function()
26949 this.editorcore.pushValue();
26952 setStylesheets : function(stylesheets)
26954 this.editorcore.setStylesheets(stylesheets);
26957 removeStylesheets : function()
26959 this.editorcore.removeStylesheets();
26963 // hide stuff that is not compatible
26977 * @event specialkey
26981 * @cfg {String} fieldClass @hide
26984 * @cfg {String} focusClass @hide
26987 * @cfg {String} autoCreate @hide
26990 * @cfg {String} inputType @hide
26993 * @cfg {String} invalidClass @hide
26996 * @cfg {String} invalidText @hide
26999 * @cfg {String} msgFx @hide
27002 * @cfg {String} validateOnBlur @hide
27006 // <script type="text/javascript">
27009 * Ext JS Library 1.1.1
27010 * Copyright(c) 2006-2007, Ext JS, LLC.
27016 * @class Roo.form.HtmlEditorToolbar1
27021 new Roo.form.HtmlEditor({
27024 new Roo.form.HtmlEditorToolbar1({
27025 disable : { fonts: 1 , format: 1, ..., ... , ...],
27031 * @cfg {Object} disable List of elements to disable..
27032 * @cfg {Array} btns List of additional buttons.
27036 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27039 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27042 Roo.apply(this, config);
27044 // default disabled, based on 'good practice'..
27045 this.disable = this.disable || {};
27046 Roo.applyIf(this.disable, {
27049 specialElements : true
27053 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27054 // dont call parent... till later.
27057 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
27064 editorcore : false,
27066 * @cfg {Object} disable List of toolbar elements to disable
27073 * @cfg {String} createLinkText The default text for the create link prompt
27075 createLinkText : 'Please enter the URL for the link:',
27077 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27079 defaultLinkValue : 'http:/'+'/',
27083 * @cfg {Array} fontFamilies An array of available font families
27101 // "á" , ?? a acute?
27106 "°" // , // degrees
27108 // "é" , // e ecute
27109 // "ú" , // u ecute?
27112 specialElements : [
27114 text: "Insert Table",
27117 ihtml : '<table><tr><td>Cell</td></tr></table>'
27121 text: "Insert Image",
27124 ihtml : '<img src="about:blank"/>'
27133 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
27134 "input:submit", "input:button", "select", "textarea", "label" ],
27137 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
27139 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27147 * @cfg {String} defaultFont default font to use.
27149 defaultFont: 'tahoma',
27151 fontSelect : false,
27154 formatCombo : false,
27156 init : function(editor)
27158 this.editor = editor;
27159 this.editorcore = editor.editorcore ? editor.editorcore : editor;
27160 var editorcore = this.editorcore;
27164 var fid = editorcore.frameId;
27166 function btn(id, toggle, handler){
27167 var xid = fid + '-'+ id ;
27171 cls : 'x-btn-icon x-edit-'+id,
27172 enableToggle:toggle !== false,
27173 scope: _t, // was editor...
27174 handler:handler||_t.relayBtnCmd,
27175 clickEvent:'mousedown',
27176 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27183 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27185 // stop form submits
27186 tb.el.on('click', function(e){
27187 e.preventDefault(); // what does this do?
27190 if(!this.disable.font) { // && !Roo.isSafari){
27191 /* why no safari for fonts
27192 editor.fontSelect = tb.el.createChild({
27195 cls:'x-font-select',
27196 html: this.createFontOptions()
27199 editor.fontSelect.on('change', function(){
27200 var font = editor.fontSelect.dom.value;
27201 editor.relayCmd('fontname', font);
27202 editor.deferFocus();
27206 editor.fontSelect.dom,
27212 if(!this.disable.formats){
27213 this.formatCombo = new Roo.form.ComboBox({
27214 store: new Roo.data.SimpleStore({
27217 data : this.formats // from states.js
27221 //autoCreate : {tag: "div", size: "20"},
27222 displayField:'tag',
27226 triggerAction: 'all',
27227 emptyText:'Add tag',
27228 selectOnFocus:true,
27231 'select': function(c, r, i) {
27232 editorcore.insertTag(r.get('tag'));
27238 tb.addField(this.formatCombo);
27242 if(!this.disable.format){
27247 btn('strikethrough')
27250 if(!this.disable.fontSize){
27255 btn('increasefontsize', false, editorcore.adjustFont),
27256 btn('decreasefontsize', false, editorcore.adjustFont)
27261 if(!this.disable.colors){
27264 id:editorcore.frameId +'-forecolor',
27265 cls:'x-btn-icon x-edit-forecolor',
27266 clickEvent:'mousedown',
27267 tooltip: this.buttonTips['forecolor'] || undefined,
27269 menu : new Roo.menu.ColorMenu({
27270 allowReselect: true,
27271 focus: Roo.emptyFn,
27274 selectHandler: function(cp, color){
27275 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27276 editor.deferFocus();
27279 clickEvent:'mousedown'
27282 id:editorcore.frameId +'backcolor',
27283 cls:'x-btn-icon x-edit-backcolor',
27284 clickEvent:'mousedown',
27285 tooltip: this.buttonTips['backcolor'] || undefined,
27287 menu : new Roo.menu.ColorMenu({
27288 focus: Roo.emptyFn,
27291 allowReselect: true,
27292 selectHandler: function(cp, color){
27294 editorcore.execCmd('useCSS', false);
27295 editorcore.execCmd('hilitecolor', color);
27296 editorcore.execCmd('useCSS', true);
27297 editor.deferFocus();
27299 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
27300 Roo.isSafari || Roo.isIE ? '#'+color : color);
27301 editor.deferFocus();
27305 clickEvent:'mousedown'
27310 // now add all the items...
27313 if(!this.disable.alignments){
27316 btn('justifyleft'),
27317 btn('justifycenter'),
27318 btn('justifyright')
27322 //if(!Roo.isSafari){
27323 if(!this.disable.links){
27326 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
27330 if(!this.disable.lists){
27333 btn('insertorderedlist'),
27334 btn('insertunorderedlist')
27337 if(!this.disable.sourceEdit){
27340 btn('sourceedit', true, function(btn){
27341 this.toggleSourceEdit(btn.pressed);
27348 // special menu.. - needs to be tidied up..
27349 if (!this.disable.special) {
27352 cls: 'x-edit-none',
27358 for (var i =0; i < this.specialChars.length; i++) {
27359 smenu.menu.items.push({
27361 html: this.specialChars[i],
27362 handler: function(a,b) {
27363 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27364 //editor.insertAtCursor(a.html);
27378 if (!this.disable.cleanStyles) {
27380 cls: 'x-btn-icon x-btn-clear',
27386 for (var i =0; i < this.cleanStyles.length; i++) {
27387 cmenu.menu.items.push({
27388 actiontype : this.cleanStyles[i],
27389 html: 'Remove ' + this.cleanStyles[i],
27390 handler: function(a,b) {
27393 var c = Roo.get(editorcore.doc.body);
27394 c.select('[style]').each(function(s) {
27395 s.dom.style.removeProperty(a.actiontype);
27397 editorcore.syncValue();
27402 cmenu.menu.items.push({
27403 actiontype : 'tablewidths',
27404 html: 'Remove Table Widths',
27405 handler: function(a,b) {
27406 editorcore.cleanTableWidths();
27407 editorcore.syncValue();
27411 cmenu.menu.items.push({
27412 actiontype : 'word',
27413 html: 'Remove MS Word Formating',
27414 handler: function(a,b) {
27415 editorcore.cleanWord();
27416 editorcore.syncValue();
27421 cmenu.menu.items.push({
27422 actiontype : 'all',
27423 html: 'Remove All Styles',
27424 handler: function(a,b) {
27426 var c = Roo.get(editorcore.doc.body);
27427 c.select('[style]').each(function(s) {
27428 s.dom.removeAttribute('style');
27430 editorcore.syncValue();
27435 cmenu.menu.items.push({
27436 actiontype : 'all',
27437 html: 'Remove All CSS Classes',
27438 handler: function(a,b) {
27440 var c = Roo.get(editorcore.doc.body);
27441 c.select('[class]').each(function(s) {
27442 s.dom.className = '';
27444 editorcore.syncValue();
27449 cmenu.menu.items.push({
27450 actiontype : 'tidy',
27451 html: 'Tidy HTML Source',
27452 handler: function(a,b) {
27453 editorcore.doc.body.innerHTML = editorcore.domToHTML();
27454 editorcore.syncValue();
27463 if (!this.disable.specialElements) {
27466 cls: 'x-edit-none',
27471 for (var i =0; i < this.specialElements.length; i++) {
27472 semenu.menu.items.push(
27474 handler: function(a,b) {
27475 editor.insertAtCursor(this.ihtml);
27477 }, this.specialElements[i])
27489 for(var i =0; i< this.btns.length;i++) {
27490 var b = Roo.factory(this.btns[i],Roo.form);
27491 b.cls = 'x-edit-none';
27493 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27494 b.cls += ' x-init-enable';
27497 b.scope = editorcore;
27505 // disable everything...
27507 this.tb.items.each(function(item){
27510 item.id != editorcore.frameId+ '-sourceedit' &&
27511 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27517 this.rendered = true;
27519 // the all the btns;
27520 editor.on('editorevent', this.updateToolbar, this);
27521 // other toolbars need to implement this..
27522 //editor.on('editmodechange', this.updateToolbar, this);
27526 relayBtnCmd : function(btn) {
27527 this.editorcore.relayCmd(btn.cmd);
27529 // private used internally
27530 createLink : function(){
27531 Roo.log("create link?");
27532 var url = prompt(this.createLinkText, this.defaultLinkValue);
27533 if(url && url != 'http:/'+'/'){
27534 this.editorcore.relayCmd('createlink', url);
27540 * Protected method that will not generally be called directly. It triggers
27541 * a toolbar update by reading the markup state of the current selection in the editor.
27543 updateToolbar: function(){
27545 if(!this.editorcore.activated){
27546 this.editor.onFirstFocus();
27550 var btns = this.tb.items.map,
27551 doc = this.editorcore.doc,
27552 frameId = this.editorcore.frameId;
27554 if(!this.disable.font && !Roo.isSafari){
27556 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27557 if(name != this.fontSelect.dom.value){
27558 this.fontSelect.dom.value = name;
27562 if(!this.disable.format){
27563 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27564 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27565 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27566 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27568 if(!this.disable.alignments){
27569 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27570 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27571 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27573 if(!Roo.isSafari && !this.disable.lists){
27574 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27575 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27578 var ans = this.editorcore.getAllAncestors();
27579 if (this.formatCombo) {
27582 var store = this.formatCombo.store;
27583 this.formatCombo.setValue("");
27584 for (var i =0; i < ans.length;i++) {
27585 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27587 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27595 // hides menus... - so this cant be on a menu...
27596 Roo.menu.MenuMgr.hideAll();
27598 //this.editorsyncValue();
27602 createFontOptions : function(){
27603 var buf = [], fs = this.fontFamilies, ff, lc;
27607 for(var i = 0, len = fs.length; i< len; i++){
27609 lc = ff.toLowerCase();
27611 '<option value="',lc,'" style="font-family:',ff,';"',
27612 (this.defaultFont == lc ? ' selected="true">' : '>'),
27617 return buf.join('');
27620 toggleSourceEdit : function(sourceEditMode){
27622 Roo.log("toolbar toogle");
27623 if(sourceEditMode === undefined){
27624 sourceEditMode = !this.sourceEditMode;
27626 this.sourceEditMode = sourceEditMode === true;
27627 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27628 // just toggle the button?
27629 if(btn.pressed !== this.sourceEditMode){
27630 btn.toggle(this.sourceEditMode);
27634 if(sourceEditMode){
27635 Roo.log("disabling buttons");
27636 this.tb.items.each(function(item){
27637 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27643 Roo.log("enabling buttons");
27644 if(this.editorcore.initialized){
27645 this.tb.items.each(function(item){
27651 Roo.log("calling toggole on editor");
27652 // tell the editor that it's been pressed..
27653 this.editor.toggleSourceEdit(sourceEditMode);
27657 * Object collection of toolbar tooltips for the buttons in the editor. The key
27658 * is the command id associated with that button and the value is a valid QuickTips object.
27663 title: 'Bold (Ctrl+B)',
27664 text: 'Make the selected text bold.',
27665 cls: 'x-html-editor-tip'
27668 title: 'Italic (Ctrl+I)',
27669 text: 'Make the selected text italic.',
27670 cls: 'x-html-editor-tip'
27678 title: 'Bold (Ctrl+B)',
27679 text: 'Make the selected text bold.',
27680 cls: 'x-html-editor-tip'
27683 title: 'Italic (Ctrl+I)',
27684 text: 'Make the selected text italic.',
27685 cls: 'x-html-editor-tip'
27688 title: 'Underline (Ctrl+U)',
27689 text: 'Underline the selected text.',
27690 cls: 'x-html-editor-tip'
27693 title: 'Strikethrough',
27694 text: 'Strikethrough the selected text.',
27695 cls: 'x-html-editor-tip'
27697 increasefontsize : {
27698 title: 'Grow Text',
27699 text: 'Increase the font size.',
27700 cls: 'x-html-editor-tip'
27702 decreasefontsize : {
27703 title: 'Shrink Text',
27704 text: 'Decrease the font size.',
27705 cls: 'x-html-editor-tip'
27708 title: 'Text Highlight Color',
27709 text: 'Change the background color of the selected text.',
27710 cls: 'x-html-editor-tip'
27713 title: 'Font Color',
27714 text: 'Change the color of the selected text.',
27715 cls: 'x-html-editor-tip'
27718 title: 'Align Text Left',
27719 text: 'Align text to the left.',
27720 cls: 'x-html-editor-tip'
27723 title: 'Center Text',
27724 text: 'Center text in the editor.',
27725 cls: 'x-html-editor-tip'
27728 title: 'Align Text Right',
27729 text: 'Align text to the right.',
27730 cls: 'x-html-editor-tip'
27732 insertunorderedlist : {
27733 title: 'Bullet List',
27734 text: 'Start a bulleted list.',
27735 cls: 'x-html-editor-tip'
27737 insertorderedlist : {
27738 title: 'Numbered List',
27739 text: 'Start a numbered list.',
27740 cls: 'x-html-editor-tip'
27743 title: 'Hyperlink',
27744 text: 'Make the selected text a hyperlink.',
27745 cls: 'x-html-editor-tip'
27748 title: 'Source Edit',
27749 text: 'Switch to source editing mode.',
27750 cls: 'x-html-editor-tip'
27754 onDestroy : function(){
27757 this.tb.items.each(function(item){
27759 item.menu.removeAll();
27761 item.menu.el.destroy();
27769 onFirstFocus: function() {
27770 this.tb.items.each(function(item){
27779 // <script type="text/javascript">
27782 * Ext JS Library 1.1.1
27783 * Copyright(c) 2006-2007, Ext JS, LLC.
27790 * @class Roo.form.HtmlEditor.ToolbarContext
27795 new Roo.form.HtmlEditor({
27798 { xtype: 'ToolbarStandard', styles : {} }
27799 { xtype: 'ToolbarContext', disable : {} }
27805 * @config : {Object} disable List of elements to disable.. (not done yet.)
27806 * @config : {Object} styles Map of styles available.
27810 Roo.form.HtmlEditor.ToolbarContext = function(config)
27813 Roo.apply(this, config);
27814 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27815 // dont call parent... till later.
27816 this.styles = this.styles || {};
27821 Roo.form.HtmlEditor.ToolbarContext.types = {
27833 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27899 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27904 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27914 style : 'fontFamily',
27915 displayField: 'display',
27916 optname : 'font-family',
27965 // should we really allow this??
27966 // should this just be
27977 style : 'fontFamily',
27978 displayField: 'display',
27979 optname : 'font-family',
27986 style : 'fontFamily',
27987 displayField: 'display',
27988 optname : 'font-family',
27995 style : 'fontFamily',
27996 displayField: 'display',
27997 optname : 'font-family',
28008 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28009 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28011 Roo.form.HtmlEditor.ToolbarContext.options = {
28013 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28014 [ 'Courier New', 'Courier New'],
28015 [ 'Tahoma', 'Tahoma'],
28016 [ 'Times New Roman,serif', 'Times'],
28017 [ 'Verdana','Verdana' ]
28021 // fixme - these need to be configurable..
28024 //Roo.form.HtmlEditor.ToolbarContext.types
28027 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
28034 editorcore : false,
28036 * @cfg {Object} disable List of toolbar elements to disable
28041 * @cfg {Object} styles List of styles
28042 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
28044 * These must be defined in the page, so they get rendered correctly..
28055 init : function(editor)
28057 this.editor = editor;
28058 this.editorcore = editor.editorcore ? editor.editorcore : editor;
28059 var editorcore = this.editorcore;
28061 var fid = editorcore.frameId;
28063 function btn(id, toggle, handler){
28064 var xid = fid + '-'+ id ;
28068 cls : 'x-btn-icon x-edit-'+id,
28069 enableToggle:toggle !== false,
28070 scope: editorcore, // was editor...
28071 handler:handler||editorcore.relayBtnCmd,
28072 clickEvent:'mousedown',
28073 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28077 // create a new element.
28078 var wdiv = editor.wrap.createChild({
28080 }, editor.wrap.dom.firstChild.nextSibling, true);
28082 // can we do this more than once??
28084 // stop form submits
28087 // disable everything...
28088 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28089 this.toolbars = {};
28091 for (var i in ty) {
28093 this.toolbars[i] = this.buildToolbar(ty[i],i);
28095 this.tb = this.toolbars.BODY;
28097 this.buildFooter();
28098 this.footer.show();
28099 editor.on('hide', function( ) { this.footer.hide() }, this);
28100 editor.on('show', function( ) { this.footer.show() }, this);
28103 this.rendered = true;
28105 // the all the btns;
28106 editor.on('editorevent', this.updateToolbar, this);
28107 // other toolbars need to implement this..
28108 //editor.on('editmodechange', this.updateToolbar, this);
28114 * Protected method that will not generally be called directly. It triggers
28115 * a toolbar update by reading the markup state of the current selection in the editor.
28117 * Note you can force an update by calling on('editorevent', scope, false)
28119 updateToolbar: function(editor,ev,sel){
28122 // capture mouse up - this is handy for selecting images..
28123 // perhaps should go somewhere else...
28124 if(!this.editorcore.activated){
28125 this.editor.onFirstFocus();
28131 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28132 // selectNode - might want to handle IE?
28134 (ev.type == 'mouseup' || ev.type == 'click' ) &&
28135 ev.target && ev.target.tagName == 'IMG') {
28136 // they have click on an image...
28137 // let's see if we can change the selection...
28140 var nodeRange = sel.ownerDocument.createRange();
28142 nodeRange.selectNode(sel);
28144 nodeRange.selectNodeContents(sel);
28146 //nodeRange.collapse(true);
28147 var s = this.editorcore.win.getSelection();
28148 s.removeAllRanges();
28149 s.addRange(nodeRange);
28153 var updateFooter = sel ? false : true;
28156 var ans = this.editorcore.getAllAncestors();
28159 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28162 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
28163 sel = sel ? sel : this.editorcore.doc.body;
28164 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28167 // pick a menu that exists..
28168 var tn = sel.tagName.toUpperCase();
28169 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28171 tn = sel.tagName.toUpperCase();
28173 var lastSel = this.tb.selectedNode;
28175 this.tb.selectedNode = sel;
28177 // if current menu does not match..
28179 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28182 ///console.log("show: " + tn);
28183 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28186 this.tb.items.first().el.innerHTML = tn + ': ';
28189 // update attributes
28190 if (this.tb.fields) {
28191 this.tb.fields.each(function(e) {
28193 e.setValue(sel.style[e.stylename]);
28196 e.setValue(sel.getAttribute(e.attrname));
28200 var hasStyles = false;
28201 for(var i in this.styles) {
28208 var st = this.tb.fields.item(0);
28210 st.store.removeAll();
28213 var cn = sel.className.split(/\s+/);
28216 if (this.styles['*']) {
28218 Roo.each(this.styles['*'], function(v) {
28219 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
28222 if (this.styles[tn]) {
28223 Roo.each(this.styles[tn], function(v) {
28224 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
28228 st.store.loadData(avs);
28232 // flag our selected Node.
28233 this.tb.selectedNode = sel;
28236 Roo.menu.MenuMgr.hideAll();
28240 if (!updateFooter) {
28241 //this.footDisp.dom.innerHTML = '';
28244 // update the footer
28248 this.footerEls = ans.reverse();
28249 Roo.each(this.footerEls, function(a,i) {
28250 if (!a) { return; }
28251 html += html.length ? ' > ' : '';
28253 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28258 var sz = this.footDisp.up('td').getSize();
28259 this.footDisp.dom.style.width = (sz.width -10) + 'px';
28260 this.footDisp.dom.style.marginLeft = '5px';
28262 this.footDisp.dom.style.overflow = 'hidden';
28264 this.footDisp.dom.innerHTML = html;
28266 //this.editorsyncValue();
28273 onDestroy : function(){
28276 this.tb.items.each(function(item){
28278 item.menu.removeAll();
28280 item.menu.el.destroy();
28288 onFirstFocus: function() {
28289 // need to do this for all the toolbars..
28290 this.tb.items.each(function(item){
28294 buildToolbar: function(tlist, nm)
28296 var editor = this.editor;
28297 var editorcore = this.editorcore;
28298 // create a new element.
28299 var wdiv = editor.wrap.createChild({
28301 }, editor.wrap.dom.firstChild.nextSibling, true);
28304 var tb = new Roo.Toolbar(wdiv);
28307 tb.add(nm+ ": ");
28310 for(var i in this.styles) {
28315 if (styles && styles.length) {
28317 // this needs a multi-select checkbox...
28318 tb.addField( new Roo.form.ComboBox({
28319 store: new Roo.data.SimpleStore({
28321 fields: ['val', 'selected'],
28324 name : '-roo-edit-className',
28325 attrname : 'className',
28326 displayField: 'val',
28330 triggerAction: 'all',
28331 emptyText:'Select Style',
28332 selectOnFocus:true,
28335 'select': function(c, r, i) {
28336 // initial support only for on class per el..
28337 tb.selectedNode.className = r ? r.get('val') : '';
28338 editorcore.syncValue();
28345 var tbc = Roo.form.HtmlEditor.ToolbarContext;
28346 var tbops = tbc.options;
28348 for (var i in tlist) {
28350 var item = tlist[i];
28351 tb.add(item.title + ": ");
28354 //optname == used so you can configure the options available..
28355 var opts = item.opts ? item.opts : false;
28356 if (item.optname) {
28357 opts = tbops[item.optname];
28362 // opts == pulldown..
28363 tb.addField( new Roo.form.ComboBox({
28364 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28366 fields: ['val', 'display'],
28369 name : '-roo-edit-' + i,
28371 stylename : item.style ? item.style : false,
28372 displayField: item.displayField ? item.displayField : 'val',
28373 valueField : 'val',
28375 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
28377 triggerAction: 'all',
28378 emptyText:'Select',
28379 selectOnFocus:true,
28380 width: item.width ? item.width : 130,
28382 'select': function(c, r, i) {
28384 tb.selectedNode.style[c.stylename] = r.get('val');
28387 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28396 tb.addField( new Roo.form.TextField({
28399 //allowBlank:false,
28404 tb.addField( new Roo.form.TextField({
28405 name: '-roo-edit-' + i,
28412 'change' : function(f, nv, ov) {
28413 tb.selectedNode.setAttribute(f.attrname, nv);
28414 editorcore.syncValue();
28427 text: 'Stylesheets',
28430 click : function ()
28432 _this.editor.fireEvent('stylesheetsclick', _this.editor);
28440 text: 'Remove Tag',
28443 click : function ()
28446 // undo does not work.
28448 var sn = tb.selectedNode;
28450 var pn = sn.parentNode;
28452 var stn = sn.childNodes[0];
28453 var en = sn.childNodes[sn.childNodes.length - 1 ];
28454 while (sn.childNodes.length) {
28455 var node = sn.childNodes[0];
28456 sn.removeChild(node);
28458 pn.insertBefore(node, sn);
28461 pn.removeChild(sn);
28462 var range = editorcore.createRange();
28464 range.setStart(stn,0);
28465 range.setEnd(en,0); //????
28466 //range.selectNode(sel);
28469 var selection = editorcore.getSelection();
28470 selection.removeAllRanges();
28471 selection.addRange(range);
28475 //_this.updateToolbar(null, null, pn);
28476 _this.updateToolbar(null, null, null);
28477 _this.footDisp.dom.innerHTML = '';
28487 tb.el.on('click', function(e){
28488 e.preventDefault(); // what does this do?
28490 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28493 // dont need to disable them... as they will get hidden
28498 buildFooter : function()
28501 var fel = this.editor.wrap.createChild();
28502 this.footer = new Roo.Toolbar(fel);
28503 // toolbar has scrolly on left / right?
28504 var footDisp= new Roo.Toolbar.Fill();
28510 handler : function() {
28511 _t.footDisp.scrollTo('left',0,true)
28515 this.footer.add( footDisp );
28520 handler : function() {
28522 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28526 var fel = Roo.get(footDisp.el);
28527 fel.addClass('x-editor-context');
28528 this.footDispWrap = fel;
28529 this.footDispWrap.overflow = 'hidden';
28531 this.footDisp = fel.createChild();
28532 this.footDispWrap.on('click', this.onContextClick, this)
28536 onContextClick : function (ev,dom)
28538 ev.preventDefault();
28539 var cn = dom.className;
28541 if (!cn.match(/x-ed-loc-/)) {
28544 var n = cn.split('-').pop();
28545 var ans = this.footerEls;
28549 var range = this.editorcore.createRange();
28551 range.selectNodeContents(sel);
28552 //range.selectNode(sel);
28555 var selection = this.editorcore.getSelection();
28556 selection.removeAllRanges();
28557 selection.addRange(range);
28561 this.updateToolbar(null, null, sel);
28578 * Ext JS Library 1.1.1
28579 * Copyright(c) 2006-2007, Ext JS, LLC.
28581 * Originally Released Under LGPL - original licence link has changed is not relivant.
28584 * <script type="text/javascript">
28588 * @class Roo.form.BasicForm
28589 * @extends Roo.util.Observable
28590 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28592 * @param {String/HTMLElement/Roo.Element} el The form element or its id
28593 * @param {Object} config Configuration options
28595 Roo.form.BasicForm = function(el, config){
28596 this.allItems = [];
28597 this.childForms = [];
28598 Roo.apply(this, config);
28600 * The Roo.form.Field items in this form.
28601 * @type MixedCollection
28605 this.items = new Roo.util.MixedCollection(false, function(o){
28606 return o.id || (o.id = Roo.id());
28610 * @event beforeaction
28611 * Fires before any action is performed. Return false to cancel the action.
28612 * @param {Form} this
28613 * @param {Action} action The action to be performed
28615 beforeaction: true,
28617 * @event actionfailed
28618 * Fires when an action fails.
28619 * @param {Form} this
28620 * @param {Action} action The action that failed
28622 actionfailed : true,
28624 * @event actioncomplete
28625 * Fires when an action is completed.
28626 * @param {Form} this
28627 * @param {Action} action The action that completed
28629 actioncomplete : true
28634 Roo.form.BasicForm.superclass.constructor.call(this);
28637 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28639 * @cfg {String} method
28640 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28643 * @cfg {DataReader} reader
28644 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28645 * This is optional as there is built-in support for processing JSON.
28648 * @cfg {DataReader} errorReader
28649 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28650 * This is completely optional as there is built-in support for processing JSON.
28653 * @cfg {String} url
28654 * The URL to use for form actions if one isn't supplied in the action options.
28657 * @cfg {Boolean} fileUpload
28658 * Set to true if this form is a file upload.
28662 * @cfg {Object} baseParams
28663 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28668 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28673 activeAction : null,
28676 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28677 * or setValues() data instead of when the form was first created.
28679 trackResetOnLoad : false,
28683 * childForms - used for multi-tab forms
28686 childForms : false,
28689 * allItems - full list of fields.
28695 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28696 * element by passing it or its id or mask the form itself by passing in true.
28699 waitMsgTarget : false,
28702 initEl : function(el){
28703 this.el = Roo.get(el);
28704 this.id = this.el.id || Roo.id();
28705 this.el.on('submit', this.onSubmit, this);
28706 this.el.addClass('x-form');
28710 onSubmit : function(e){
28715 * Returns true if client-side validation on the form is successful.
28718 isValid : function(){
28720 this.items.each(function(f){
28729 * DEPRICATED Returns true if any fields in this form have changed since their original load.
28732 isDirty : function(){
28734 this.items.each(function(f){
28744 * Returns true if any fields in this form have changed since their original load. (New version)
28748 hasChanged : function()
28751 this.items.each(function(f){
28752 if(f.hasChanged()){
28761 * Resets all hasChanged to 'false' -
28762 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
28763 * So hasChanged storage is only to be used for this purpose
28766 resetHasChanged : function()
28768 this.items.each(function(f){
28769 f.resetHasChanged();
28776 * Performs a predefined action (submit or load) or custom actions you define on this form.
28777 * @param {String} actionName The name of the action type
28778 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
28779 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28780 * accept other config options):
28782 Property Type Description
28783 ---------------- --------------- ----------------------------------------------------------------------------------
28784 url String The url for the action (defaults to the form's url)
28785 method String The form method to use (defaults to the form's method, or POST if not defined)
28786 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
28787 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
28788 validate the form on the client (defaults to false)
28790 * @return {BasicForm} this
28792 doAction : function(action, options){
28793 if(typeof action == 'string'){
28794 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28796 if(this.fireEvent('beforeaction', this, action) !== false){
28797 this.beforeAction(action);
28798 action.run.defer(100, action);
28804 * Shortcut to do a submit action.
28805 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28806 * @return {BasicForm} this
28808 submit : function(options){
28809 this.doAction('submit', options);
28814 * Shortcut to do a load action.
28815 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28816 * @return {BasicForm} this
28818 load : function(options){
28819 this.doAction('load', options);
28824 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28825 * @param {Record} record The record to edit
28826 * @return {BasicForm} this
28828 updateRecord : function(record){
28829 record.beginEdit();
28830 var fs = record.fields;
28831 fs.each(function(f){
28832 var field = this.findField(f.name);
28834 record.set(f.name, field.getValue());
28842 * Loads an Roo.data.Record into this form.
28843 * @param {Record} record The record to load
28844 * @return {BasicForm} this
28846 loadRecord : function(record){
28847 this.setValues(record.data);
28852 beforeAction : function(action){
28853 var o = action.options;
28856 if(this.waitMsgTarget === true){
28857 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28858 }else if(this.waitMsgTarget){
28859 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28860 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28862 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28868 afterAction : function(action, success){
28869 this.activeAction = null;
28870 var o = action.options;
28872 if(this.waitMsgTarget === true){
28874 }else if(this.waitMsgTarget){
28875 this.waitMsgTarget.unmask();
28877 Roo.MessageBox.updateProgress(1);
28878 Roo.MessageBox.hide();
28885 Roo.callback(o.success, o.scope, [this, action]);
28886 this.fireEvent('actioncomplete', this, action);
28890 // failure condition..
28891 // we have a scenario where updates need confirming.
28892 // eg. if a locking scenario exists..
28893 // we look for { errors : { needs_confirm : true }} in the response.
28895 (typeof(action.result) != 'undefined') &&
28896 (typeof(action.result.errors) != 'undefined') &&
28897 (typeof(action.result.errors.needs_confirm) != 'undefined')
28900 Roo.MessageBox.confirm(
28901 "Change requires confirmation",
28902 action.result.errorMsg,
28907 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
28917 Roo.callback(o.failure, o.scope, [this, action]);
28918 // show an error message if no failed handler is set..
28919 if (!this.hasListener('actionfailed')) {
28920 Roo.MessageBox.alert("Error",
28921 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28922 action.result.errorMsg :
28923 "Saving Failed, please check your entries or try again"
28927 this.fireEvent('actionfailed', this, action);
28933 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28934 * @param {String} id The value to search for
28937 findField : function(id){
28938 var field = this.items.get(id);
28940 this.items.each(function(f){
28941 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28947 return field || null;
28951 * Add a secondary form to this one,
28952 * Used to provide tabbed forms. One form is primary, with hidden values
28953 * which mirror the elements from the other forms.
28955 * @param {Roo.form.Form} form to add.
28958 addForm : function(form)
28961 if (this.childForms.indexOf(form) > -1) {
28965 this.childForms.push(form);
28967 Roo.each(form.allItems, function (fe) {
28969 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28970 if (this.findField(n)) { // already added..
28973 var add = new Roo.form.Hidden({
28976 add.render(this.el);
28983 * Mark fields in this form invalid in bulk.
28984 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28985 * @return {BasicForm} this
28987 markInvalid : function(errors){
28988 if(errors instanceof Array){
28989 for(var i = 0, len = errors.length; i < len; i++){
28990 var fieldError = errors[i];
28991 var f = this.findField(fieldError.id);
28993 f.markInvalid(fieldError.msg);
28999 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29000 field.markInvalid(errors[id]);
29004 Roo.each(this.childForms || [], function (f) {
29005 f.markInvalid(errors);
29012 * Set values for fields in this form in bulk.
29013 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29014 * @return {BasicForm} this
29016 setValues : function(values){
29017 if(values instanceof Array){ // array of objects
29018 for(var i = 0, len = values.length; i < len; i++){
29020 var f = this.findField(v.id);
29022 f.setValue(v.value);
29023 if(this.trackResetOnLoad){
29024 f.originalValue = f.getValue();
29028 }else{ // object hash
29031 if(typeof values[id] != 'function' && (field = this.findField(id))){
29033 if (field.setFromData &&
29034 field.valueField &&
29035 field.displayField &&
29036 // combos' with local stores can
29037 // be queried via setValue()
29038 // to set their value..
29039 (field.store && !field.store.isLocal)
29043 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29044 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29045 field.setFromData(sd);
29048 field.setValue(values[id]);
29052 if(this.trackResetOnLoad){
29053 field.originalValue = field.getValue();
29058 this.resetHasChanged();
29061 Roo.each(this.childForms || [], function (f) {
29062 f.setValues(values);
29063 f.resetHasChanged();
29070 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29071 * they are returned as an array.
29072 * @param {Boolean} asString
29075 getValues : function(asString){
29076 if (this.childForms) {
29077 // copy values from the child forms
29078 Roo.each(this.childForms, function (f) {
29079 this.setValues(f.getValues());
29085 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29086 if(asString === true){
29089 return Roo.urlDecode(fs);
29093 * Returns the fields in this form as an object with key/value pairs.
29094 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29097 getFieldValues : function(with_hidden)
29099 if (this.childForms) {
29100 // copy values from the child forms
29101 // should this call getFieldValues - probably not as we do not currently copy
29102 // hidden fields when we generate..
29103 Roo.each(this.childForms, function (f) {
29104 this.setValues(f.getValues());
29109 this.items.each(function(f){
29110 if (!f.getName()) {
29113 var v = f.getValue();
29114 if (f.inputType =='radio') {
29115 if (typeof(ret[f.getName()]) == 'undefined') {
29116 ret[f.getName()] = ''; // empty..
29119 if (!f.el.dom.checked) {
29123 v = f.el.dom.value;
29127 // not sure if this supported any more..
29128 if ((typeof(v) == 'object') && f.getRawValue) {
29129 v = f.getRawValue() ; // dates..
29131 // combo boxes where name != hiddenName...
29132 if (f.name != f.getName()) {
29133 ret[f.name] = f.getRawValue();
29135 ret[f.getName()] = v;
29142 * Clears all invalid messages in this form.
29143 * @return {BasicForm} this
29145 clearInvalid : function(){
29146 this.items.each(function(f){
29150 Roo.each(this.childForms || [], function (f) {
29159 * Resets this form.
29160 * @return {BasicForm} this
29162 reset : function(){
29163 this.items.each(function(f){
29167 Roo.each(this.childForms || [], function (f) {
29170 this.resetHasChanged();
29176 * Add Roo.form components to this form.
29177 * @param {Field} field1
29178 * @param {Field} field2 (optional)
29179 * @param {Field} etc (optional)
29180 * @return {BasicForm} this
29183 this.items.addAll(Array.prototype.slice.call(arguments, 0));
29189 * Removes a field from the items collection (does NOT remove its markup).
29190 * @param {Field} field
29191 * @return {BasicForm} this
29193 remove : function(field){
29194 this.items.remove(field);
29199 * Looks at the fields in this form, checks them for an id attribute,
29200 * and calls applyTo on the existing dom element with that id.
29201 * @return {BasicForm} this
29203 render : function(){
29204 this.items.each(function(f){
29205 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29213 * Calls {@link Ext#apply} for all fields in this form with the passed object.
29214 * @param {Object} values
29215 * @return {BasicForm} this
29217 applyToFields : function(o){
29218 this.items.each(function(f){
29225 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29226 * @param {Object} values
29227 * @return {BasicForm} this
29229 applyIfToFields : function(o){
29230 this.items.each(function(f){
29238 Roo.BasicForm = Roo.form.BasicForm;/*
29240 * Ext JS Library 1.1.1
29241 * Copyright(c) 2006-2007, Ext JS, LLC.
29243 * Originally Released Under LGPL - original licence link has changed is not relivant.
29246 * <script type="text/javascript">
29250 * @class Roo.form.Form
29251 * @extends Roo.form.BasicForm
29252 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29254 * @param {Object} config Configuration options
29256 Roo.form.Form = function(config){
29258 if (config.items) {
29259 xitems = config.items;
29260 delete config.items;
29264 Roo.form.Form.superclass.constructor.call(this, null, config);
29265 this.url = this.url || this.action;
29267 this.root = new Roo.form.Layout(Roo.applyIf({
29271 this.active = this.root;
29273 * Array of all the buttons that have been added to this form via {@link addButton}
29277 this.allItems = [];
29280 * @event clientvalidation
29281 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29282 * @param {Form} this
29283 * @param {Boolean} valid true if the form has passed client-side validation
29285 clientvalidation: true,
29288 * Fires when the form is rendered
29289 * @param {Roo.form.Form} form
29294 if (this.progressUrl) {
29295 // push a hidden field onto the list of fields..
29299 name : 'UPLOAD_IDENTIFIER'
29304 Roo.each(xitems, this.addxtype, this);
29310 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29312 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29315 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29318 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29320 buttonAlign:'center',
29323 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29328 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29329 * This property cascades to child containers if not set.
29334 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29335 * fires a looping event with that state. This is required to bind buttons to the valid
29336 * state using the config value formBind:true on the button.
29338 monitorValid : false,
29341 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29346 * @cfg {String} progressUrl - Url to return progress data
29349 progressUrl : false,
29352 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29353 * fields are added and the column is closed. If no fields are passed the column remains open
29354 * until end() is called.
29355 * @param {Object} config The config to pass to the column
29356 * @param {Field} field1 (optional)
29357 * @param {Field} field2 (optional)
29358 * @param {Field} etc (optional)
29359 * @return Column The column container object
29361 column : function(c){
29362 var col = new Roo.form.Column(c);
29364 if(arguments.length > 1){ // duplicate code required because of Opera
29365 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29372 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29373 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29374 * until end() is called.
29375 * @param {Object} config The config to pass to the fieldset
29376 * @param {Field} field1 (optional)
29377 * @param {Field} field2 (optional)
29378 * @param {Field} etc (optional)
29379 * @return FieldSet The fieldset container object
29381 fieldset : function(c){
29382 var fs = new Roo.form.FieldSet(c);
29384 if(arguments.length > 1){ // duplicate code required because of Opera
29385 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29392 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29393 * fields are added and the container is closed. If no fields are passed the container remains open
29394 * until end() is called.
29395 * @param {Object} config The config to pass to the Layout
29396 * @param {Field} field1 (optional)
29397 * @param {Field} field2 (optional)
29398 * @param {Field} etc (optional)
29399 * @return Layout The container object
29401 container : function(c){
29402 var l = new Roo.form.Layout(c);
29404 if(arguments.length > 1){ // duplicate code required because of Opera
29405 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29412 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29413 * @param {Object} container A Roo.form.Layout or subclass of Layout
29414 * @return {Form} this
29416 start : function(c){
29417 // cascade label info
29418 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29419 this.active.stack.push(c);
29420 c.ownerCt = this.active;
29426 * Closes the current open container
29427 * @return {Form} this
29430 if(this.active == this.root){
29433 this.active = this.active.ownerCt;
29438 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
29439 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29440 * as the label of the field.
29441 * @param {Field} field1
29442 * @param {Field} field2 (optional)
29443 * @param {Field} etc. (optional)
29444 * @return {Form} this
29447 this.active.stack.push.apply(this.active.stack, arguments);
29448 this.allItems.push.apply(this.allItems,arguments);
29450 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29451 if(a[i].isFormField){
29456 Roo.form.Form.superclass.add.apply(this, r);
29466 * Find any element that has been added to a form, using it's ID or name
29467 * This can include framesets, columns etc. along with regular fields..
29468 * @param {String} id - id or name to find.
29470 * @return {Element} e - or false if nothing found.
29472 findbyId : function(id)
29478 Roo.each(this.allItems, function(f){
29479 if (f.id == id || f.name == id ){
29490 * Render this form into the passed container. This should only be called once!
29491 * @param {String/HTMLElement/Element} container The element this component should be rendered into
29492 * @return {Form} this
29494 render : function(ct)
29500 var o = this.autoCreate || {
29502 method : this.method || 'POST',
29503 id : this.id || Roo.id()
29505 this.initEl(ct.createChild(o));
29507 this.root.render(this.el);
29511 this.items.each(function(f){
29512 f.render('x-form-el-'+f.id);
29515 if(this.buttons.length > 0){
29516 // tables are required to maintain order and for correct IE layout
29517 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29518 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29519 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29521 var tr = tb.getElementsByTagName('tr')[0];
29522 for(var i = 0, len = this.buttons.length; i < len; i++) {
29523 var b = this.buttons[i];
29524 var td = document.createElement('td');
29525 td.className = 'x-form-btn-td';
29526 b.render(tr.appendChild(td));
29529 if(this.monitorValid){ // initialize after render
29530 this.startMonitoring();
29532 this.fireEvent('rendered', this);
29537 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29538 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29539 * object or a valid Roo.DomHelper element config
29540 * @param {Function} handler The function called when the button is clicked
29541 * @param {Object} scope (optional) The scope of the handler function
29542 * @return {Roo.Button}
29544 addButton : function(config, handler, scope){
29548 minWidth: this.minButtonWidth,
29551 if(typeof config == "string"){
29554 Roo.apply(bc, config);
29556 var btn = new Roo.Button(null, bc);
29557 this.buttons.push(btn);
29562 * Adds a series of form elements (using the xtype property as the factory method.
29563 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29564 * @param {Object} config
29567 addxtype : function()
29569 var ar = Array.prototype.slice.call(arguments, 0);
29571 for(var i = 0; i < ar.length; i++) {
29573 continue; // skip -- if this happends something invalid got sent, we
29574 // should ignore it, as basically that interface element will not show up
29575 // and that should be pretty obvious!!
29578 if (Roo.form[ar[i].xtype]) {
29580 var fe = Roo.factory(ar[i], Roo.form);
29586 fe.store.form = this;
29591 this.allItems.push(fe);
29592 if (fe.items && fe.addxtype) {
29593 fe.addxtype.apply(fe, fe.items);
29603 // console.log('adding ' + ar[i].xtype);
29605 if (ar[i].xtype == 'Button') {
29606 //console.log('adding button');
29607 //console.log(ar[i]);
29608 this.addButton(ar[i]);
29609 this.allItems.push(fe);
29613 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29614 alert('end is not supported on xtype any more, use items');
29616 // //console.log('adding end');
29624 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29625 * option "monitorValid"
29627 startMonitoring : function(){
29630 Roo.TaskMgr.start({
29631 run : this.bindHandler,
29632 interval : this.monitorPoll || 200,
29639 * Stops monitoring of the valid state of this form
29641 stopMonitoring : function(){
29642 this.bound = false;
29646 bindHandler : function(){
29648 return false; // stops binding
29651 this.items.each(function(f){
29652 if(!f.isValid(true)){
29657 for(var i = 0, len = this.buttons.length; i < len; i++){
29658 var btn = this.buttons[i];
29659 if(btn.formBind === true && btn.disabled === valid){
29660 btn.setDisabled(!valid);
29663 this.fireEvent('clientvalidation', this, valid);
29677 Roo.Form = Roo.form.Form;
29680 * Ext JS Library 1.1.1
29681 * Copyright(c) 2006-2007, Ext JS, LLC.
29683 * Originally Released Under LGPL - original licence link has changed is not relivant.
29686 * <script type="text/javascript">
29689 // as we use this in bootstrap.
29690 Roo.namespace('Roo.form');
29692 * @class Roo.form.Action
29693 * Internal Class used to handle form actions
29695 * @param {Roo.form.BasicForm} el The form element or its id
29696 * @param {Object} config Configuration options
29701 // define the action interface
29702 Roo.form.Action = function(form, options){
29704 this.options = options || {};
29707 * Client Validation Failed
29710 Roo.form.Action.CLIENT_INVALID = 'client';
29712 * Server Validation Failed
29715 Roo.form.Action.SERVER_INVALID = 'server';
29717 * Connect to Server Failed
29720 Roo.form.Action.CONNECT_FAILURE = 'connect';
29722 * Reading Data from Server Failed
29725 Roo.form.Action.LOAD_FAILURE = 'load';
29727 Roo.form.Action.prototype = {
29729 failureType : undefined,
29730 response : undefined,
29731 result : undefined,
29733 // interface method
29734 run : function(options){
29738 // interface method
29739 success : function(response){
29743 // interface method
29744 handleResponse : function(response){
29748 // default connection failure
29749 failure : function(response){
29751 this.response = response;
29752 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29753 this.form.afterAction(this, false);
29756 processResponse : function(response){
29757 this.response = response;
29758 if(!response.responseText){
29761 this.result = this.handleResponse(response);
29762 return this.result;
29765 // utility functions used internally
29766 getUrl : function(appendParams){
29767 var url = this.options.url || this.form.url || this.form.el.dom.action;
29769 var p = this.getParams();
29771 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29777 getMethod : function(){
29778 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29781 getParams : function(){
29782 var bp = this.form.baseParams;
29783 var p = this.options.params;
29785 if(typeof p == "object"){
29786 p = Roo.urlEncode(Roo.applyIf(p, bp));
29787 }else if(typeof p == 'string' && bp){
29788 p += '&' + Roo.urlEncode(bp);
29791 p = Roo.urlEncode(bp);
29796 createCallback : function(){
29798 success: this.success,
29799 failure: this.failure,
29801 timeout: (this.form.timeout*1000),
29802 upload: this.form.fileUpload ? this.success : undefined
29807 Roo.form.Action.Submit = function(form, options){
29808 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29811 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29814 haveProgress : false,
29815 uploadComplete : false,
29817 // uploadProgress indicator.
29818 uploadProgress : function()
29820 if (!this.form.progressUrl) {
29824 if (!this.haveProgress) {
29825 Roo.MessageBox.progress("Uploading", "Uploading");
29827 if (this.uploadComplete) {
29828 Roo.MessageBox.hide();
29832 this.haveProgress = true;
29834 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29836 var c = new Roo.data.Connection();
29838 url : this.form.progressUrl,
29843 success : function(req){
29844 //console.log(data);
29848 rdata = Roo.decode(req.responseText)
29850 Roo.log("Invalid data from server..");
29854 if (!rdata || !rdata.success) {
29856 Roo.MessageBox.alert(Roo.encode(rdata));
29859 var data = rdata.data;
29861 if (this.uploadComplete) {
29862 Roo.MessageBox.hide();
29867 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29868 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29871 this.uploadProgress.defer(2000,this);
29874 failure: function(data) {
29875 Roo.log('progress url failed ');
29886 // run get Values on the form, so it syncs any secondary forms.
29887 this.form.getValues();
29889 var o = this.options;
29890 var method = this.getMethod();
29891 var isPost = method == 'POST';
29892 if(o.clientValidation === false || this.form.isValid()){
29894 if (this.form.progressUrl) {
29895 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29896 (new Date() * 1) + '' + Math.random());
29901 Roo.Ajax.request(Roo.apply(this.createCallback(), {
29902 form:this.form.el.dom,
29903 url:this.getUrl(!isPost),
29905 params:isPost ? this.getParams() : null,
29906 isUpload: this.form.fileUpload
29909 this.uploadProgress();
29911 }else if (o.clientValidation !== false){ // client validation failed
29912 this.failureType = Roo.form.Action.CLIENT_INVALID;
29913 this.form.afterAction(this, false);
29917 success : function(response)
29919 this.uploadComplete= true;
29920 if (this.haveProgress) {
29921 Roo.MessageBox.hide();
29925 var result = this.processResponse(response);
29926 if(result === true || result.success){
29927 this.form.afterAction(this, true);
29931 this.form.markInvalid(result.errors);
29932 this.failureType = Roo.form.Action.SERVER_INVALID;
29934 this.form.afterAction(this, false);
29936 failure : function(response)
29938 this.uploadComplete= true;
29939 if (this.haveProgress) {
29940 Roo.MessageBox.hide();
29943 this.response = response;
29944 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29945 this.form.afterAction(this, false);
29948 handleResponse : function(response){
29949 if(this.form.errorReader){
29950 var rs = this.form.errorReader.read(response);
29953 for(var i = 0, len = rs.records.length; i < len; i++) {
29954 var r = rs.records[i];
29955 errors[i] = r.data;
29958 if(errors.length < 1){
29962 success : rs.success,
29968 ret = Roo.decode(response.responseText);
29972 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29982 Roo.form.Action.Load = function(form, options){
29983 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29984 this.reader = this.form.reader;
29987 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29992 Roo.Ajax.request(Roo.apply(
29993 this.createCallback(), {
29994 method:this.getMethod(),
29995 url:this.getUrl(false),
29996 params:this.getParams()
30000 success : function(response){
30002 var result = this.processResponse(response);
30003 if(result === true || !result.success || !result.data){
30004 this.failureType = Roo.form.Action.LOAD_FAILURE;
30005 this.form.afterAction(this, false);
30008 this.form.clearInvalid();
30009 this.form.setValues(result.data);
30010 this.form.afterAction(this, true);
30013 handleResponse : function(response){
30014 if(this.form.reader){
30015 var rs = this.form.reader.read(response);
30016 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30018 success : rs.success,
30022 return Roo.decode(response.responseText);
30026 Roo.form.Action.ACTION_TYPES = {
30027 'load' : Roo.form.Action.Load,
30028 'submit' : Roo.form.Action.Submit
30031 * Ext JS Library 1.1.1
30032 * Copyright(c) 2006-2007, Ext JS, LLC.
30034 * Originally Released Under LGPL - original licence link has changed is not relivant.
30037 * <script type="text/javascript">
30041 * @class Roo.form.Layout
30042 * @extends Roo.Component
30043 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30045 * @param {Object} config Configuration options
30047 Roo.form.Layout = function(config){
30049 if (config.items) {
30050 xitems = config.items;
30051 delete config.items;
30053 Roo.form.Layout.superclass.constructor.call(this, config);
30055 Roo.each(xitems, this.addxtype, this);
30059 Roo.extend(Roo.form.Layout, Roo.Component, {
30061 * @cfg {String/Object} autoCreate
30062 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30065 * @cfg {String/Object/Function} style
30066 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30067 * a function which returns such a specification.
30070 * @cfg {String} labelAlign
30071 * Valid values are "left," "top" and "right" (defaults to "left")
30074 * @cfg {Number} labelWidth
30075 * Fixed width in pixels of all field labels (defaults to undefined)
30078 * @cfg {Boolean} clear
30079 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30083 * @cfg {String} labelSeparator
30084 * The separator to use after field labels (defaults to ':')
30086 labelSeparator : ':',
30088 * @cfg {Boolean} hideLabels
30089 * True to suppress the display of field labels in this layout (defaults to false)
30091 hideLabels : false,
30094 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30099 onRender : function(ct, position){
30100 if(this.el){ // from markup
30101 this.el = Roo.get(this.el);
30102 }else { // generate
30103 var cfg = this.getAutoCreate();
30104 this.el = ct.createChild(cfg, position);
30107 this.el.applyStyles(this.style);
30109 if(this.labelAlign){
30110 this.el.addClass('x-form-label-'+this.labelAlign);
30112 if(this.hideLabels){
30113 this.labelStyle = "display:none";
30114 this.elementStyle = "padding-left:0;";
30116 if(typeof this.labelWidth == 'number'){
30117 this.labelStyle = "width:"+this.labelWidth+"px;";
30118 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30120 if(this.labelAlign == 'top'){
30121 this.labelStyle = "width:auto;";
30122 this.elementStyle = "padding-left:0;";
30125 var stack = this.stack;
30126 var slen = stack.length;
30128 if(!this.fieldTpl){
30129 var t = new Roo.Template(
30130 '<div class="x-form-item {5}">',
30131 '<label for="{0}" style="{2}">{1}{4}</label>',
30132 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30134 '</div><div class="x-form-clear-left"></div>'
30136 t.disableFormats = true;
30138 Roo.form.Layout.prototype.fieldTpl = t;
30140 for(var i = 0; i < slen; i++) {
30141 if(stack[i].isFormField){
30142 this.renderField(stack[i]);
30144 this.renderComponent(stack[i]);
30149 this.el.createChild({cls:'x-form-clear'});
30154 renderField : function(f){
30155 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30158 f.labelStyle||this.labelStyle||'', //2
30159 this.elementStyle||'', //3
30160 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30161 f.itemCls||this.itemCls||'' //5
30162 ], true).getPrevSibling());
30166 renderComponent : function(c){
30167 c.render(c.isLayout ? this.el : this.el.createChild());
30170 * Adds a object form elements (using the xtype property as the factory method.)
30171 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
30172 * @param {Object} config
30174 addxtype : function(o)
30176 // create the lement.
30177 o.form = this.form;
30178 var fe = Roo.factory(o, Roo.form);
30179 this.form.allItems.push(fe);
30180 this.stack.push(fe);
30182 if (fe.isFormField) {
30183 this.form.items.add(fe);
30191 * @class Roo.form.Column
30192 * @extends Roo.form.Layout
30193 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30195 * @param {Object} config Configuration options
30197 Roo.form.Column = function(config){
30198 Roo.form.Column.superclass.constructor.call(this, config);
30201 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30203 * @cfg {Number/String} width
30204 * The fixed width of the column in pixels or CSS value (defaults to "auto")
30207 * @cfg {String/Object} autoCreate
30208 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30212 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30215 onRender : function(ct, position){
30216 Roo.form.Column.superclass.onRender.call(this, ct, position);
30218 this.el.setWidth(this.width);
30225 * @class Roo.form.Row
30226 * @extends Roo.form.Layout
30227 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30229 * @param {Object} config Configuration options
30233 Roo.form.Row = function(config){
30234 Roo.form.Row.superclass.constructor.call(this, config);
30237 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30239 * @cfg {Number/String} width
30240 * The fixed width of the column in pixels or CSS value (defaults to "auto")
30243 * @cfg {Number/String} height
30244 * The fixed height of the column in pixels or CSS value (defaults to "auto")
30246 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30250 onRender : function(ct, position){
30251 //console.log('row render');
30253 var t = new Roo.Template(
30254 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30255 '<label for="{0}" style="{2}">{1}{4}</label>',
30256 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30260 t.disableFormats = true;
30262 Roo.form.Layout.prototype.rowTpl = t;
30264 this.fieldTpl = this.rowTpl;
30266 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30267 var labelWidth = 100;
30269 if ((this.labelAlign != 'top')) {
30270 if (typeof this.labelWidth == 'number') {
30271 labelWidth = this.labelWidth
30273 this.padWidth = 20 + labelWidth;
30277 Roo.form.Column.superclass.onRender.call(this, ct, position);
30279 this.el.setWidth(this.width);
30282 this.el.setHeight(this.height);
30287 renderField : function(f){
30288 f.fieldEl = this.fieldTpl.append(this.el, [
30289 f.id, f.fieldLabel,
30290 f.labelStyle||this.labelStyle||'',
30291 this.elementStyle||'',
30292 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30293 f.itemCls||this.itemCls||'',
30294 f.width ? f.width + this.padWidth : 160 + this.padWidth
30301 * @class Roo.form.FieldSet
30302 * @extends Roo.form.Layout
30303 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30305 * @param {Object} config Configuration options
30307 Roo.form.FieldSet = function(config){
30308 Roo.form.FieldSet.superclass.constructor.call(this, config);
30311 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30313 * @cfg {String} legend
30314 * The text to display as the legend for the FieldSet (defaults to '')
30317 * @cfg {String/Object} autoCreate
30318 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30322 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30325 onRender : function(ct, position){
30326 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30328 this.setLegend(this.legend);
30333 setLegend : function(text){
30335 this.el.child('legend').update(text);
30340 * Ext JS Library 1.1.1
30341 * Copyright(c) 2006-2007, Ext JS, LLC.
30343 * Originally Released Under LGPL - original licence link has changed is not relivant.
30346 * <script type="text/javascript">
30349 * @class Roo.form.VTypes
30350 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30353 Roo.form.VTypes = function(){
30354 // closure these in so they are only created once.
30355 var alpha = /^[a-zA-Z_]+$/;
30356 var alphanum = /^[a-zA-Z0-9_]+$/;
30357 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30358 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30360 // All these messages and functions are configurable
30363 * The function used to validate email addresses
30364 * @param {String} value The email address
30366 'email' : function(v){
30367 return email.test(v);
30370 * The error text to display when the email validation function returns false
30373 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30375 * The keystroke filter mask to be applied on email input
30378 'emailMask' : /[a-z0-9_\.\-@]/i,
30381 * The function used to validate URLs
30382 * @param {String} value The URL
30384 'url' : function(v){
30385 return url.test(v);
30388 * The error text to display when the url validation function returns false
30391 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30394 * The function used to validate alpha values
30395 * @param {String} value The value
30397 'alpha' : function(v){
30398 return alpha.test(v);
30401 * The error text to display when the alpha validation function returns false
30404 'alphaText' : 'This field should only contain letters and _',
30406 * The keystroke filter mask to be applied on alpha input
30409 'alphaMask' : /[a-z_]/i,
30412 * The function used to validate alphanumeric values
30413 * @param {String} value The value
30415 'alphanum' : function(v){
30416 return alphanum.test(v);
30419 * The error text to display when the alphanumeric validation function returns false
30422 'alphanumText' : 'This field should only contain letters, numbers and _',
30424 * The keystroke filter mask to be applied on alphanumeric input
30427 'alphanumMask' : /[a-z0-9_]/i
30429 }();//<script type="text/javascript">
30432 * @class Roo.form.FCKeditor
30433 * @extends Roo.form.TextArea
30434 * Wrapper around the FCKEditor http://www.fckeditor.net
30436 * Creates a new FCKeditor
30437 * @param {Object} config Configuration options
30439 Roo.form.FCKeditor = function(config){
30440 Roo.form.FCKeditor.superclass.constructor.call(this, config);
30443 * @event editorinit
30444 * Fired when the editor is initialized - you can add extra handlers here..
30445 * @param {FCKeditor} this
30446 * @param {Object} the FCK object.
30453 Roo.form.FCKeditor.editors = { };
30454 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30456 //defaultAutoCreate : {
30457 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
30461 * @cfg {Object} fck options - see fck manual for details.
30466 * @cfg {Object} fck toolbar set (Basic or Default)
30468 toolbarSet : 'Basic',
30470 * @cfg {Object} fck BasePath
30472 basePath : '/fckeditor/',
30480 onRender : function(ct, position)
30483 this.defaultAutoCreate = {
30485 style:"width:300px;height:60px;",
30486 autocomplete: "new-password"
30489 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30492 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30493 if(this.preventScrollbars){
30494 this.el.setStyle("overflow", "hidden");
30496 this.el.setHeight(this.growMin);
30499 //console.log('onrender' + this.getId() );
30500 Roo.form.FCKeditor.editors[this.getId()] = this;
30503 this.replaceTextarea() ;
30507 getEditor : function() {
30508 return this.fckEditor;
30511 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
30512 * @param {Mixed} value The value to set
30516 setValue : function(value)
30518 //console.log('setValue: ' + value);
30520 if(typeof(value) == 'undefined') { // not sure why this is happending...
30523 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30525 //if(!this.el || !this.getEditor()) {
30526 // this.value = value;
30527 //this.setValue.defer(100,this,[value]);
30531 if(!this.getEditor()) {
30535 this.getEditor().SetData(value);
30542 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
30543 * @return {Mixed} value The field value
30545 getValue : function()
30548 if (this.frame && this.frame.dom.style.display == 'none') {
30549 return Roo.form.FCKeditor.superclass.getValue.call(this);
30552 if(!this.el || !this.getEditor()) {
30554 // this.getValue.defer(100,this);
30559 var value=this.getEditor().GetData();
30560 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30561 return Roo.form.FCKeditor.superclass.getValue.call(this);
30567 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
30568 * @return {Mixed} value The field value
30570 getRawValue : function()
30572 if (this.frame && this.frame.dom.style.display == 'none') {
30573 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30576 if(!this.el || !this.getEditor()) {
30577 //this.getRawValue.defer(100,this);
30584 var value=this.getEditor().GetData();
30585 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30586 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30590 setSize : function(w,h) {
30594 //if (this.frame && this.frame.dom.style.display == 'none') {
30595 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30598 //if(!this.el || !this.getEditor()) {
30599 // this.setSize.defer(100,this, [w,h]);
30605 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30607 this.frame.dom.setAttribute('width', w);
30608 this.frame.dom.setAttribute('height', h);
30609 this.frame.setSize(w,h);
30613 toggleSourceEdit : function(value) {
30617 this.el.dom.style.display = value ? '' : 'none';
30618 this.frame.dom.style.display = value ? 'none' : '';
30623 focus: function(tag)
30625 if (this.frame.dom.style.display == 'none') {
30626 return Roo.form.FCKeditor.superclass.focus.call(this);
30628 if(!this.el || !this.getEditor()) {
30629 this.focus.defer(100,this, [tag]);
30636 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30637 this.getEditor().Focus();
30639 if (!this.getEditor().Selection.GetSelection()) {
30640 this.focus.defer(100,this, [tag]);
30645 var r = this.getEditor().EditorDocument.createRange();
30646 r.setStart(tgs[0],0);
30647 r.setEnd(tgs[0],0);
30648 this.getEditor().Selection.GetSelection().removeAllRanges();
30649 this.getEditor().Selection.GetSelection().addRange(r);
30650 this.getEditor().Focus();
30657 replaceTextarea : function()
30659 if ( document.getElementById( this.getId() + '___Frame' ) ) {
30662 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30664 // We must check the elements firstly using the Id and then the name.
30665 var oTextarea = document.getElementById( this.getId() );
30667 var colElementsByName = document.getElementsByName( this.getId() ) ;
30669 oTextarea.style.display = 'none' ;
30671 if ( oTextarea.tabIndex ) {
30672 this.TabIndex = oTextarea.tabIndex ;
30675 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30676 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30677 this.frame = Roo.get(this.getId() + '___Frame')
30680 _getConfigHtml : function()
30684 for ( var o in this.fckconfig ) {
30685 sConfig += sConfig.length > 0 ? '&' : '';
30686 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30689 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30693 _getIFrameHtml : function()
30695 var sFile = 'fckeditor.html' ;
30696 /* no idea what this is about..
30699 if ( (/fcksource=true/i).test( window.top.location.search ) )
30700 sFile = 'fckeditor.original.html' ;
30705 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30706 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
30709 var html = '<iframe id="' + this.getId() +
30710 '___Frame" src="' + sLink +
30711 '" width="' + this.width +
30712 '" height="' + this.height + '"' +
30713 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
30714 ' frameborder="0" scrolling="no"></iframe>' ;
30719 _insertHtmlBefore : function( html, element )
30721 if ( element.insertAdjacentHTML ) {
30723 element.insertAdjacentHTML( 'beforeBegin', html ) ;
30725 var oRange = document.createRange() ;
30726 oRange.setStartBefore( element ) ;
30727 var oFragment = oRange.createContextualFragment( html );
30728 element.parentNode.insertBefore( oFragment, element ) ;
30741 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30743 function FCKeditor_OnComplete(editorInstance){
30744 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30745 f.fckEditor = editorInstance;
30746 //console.log("loaded");
30747 f.fireEvent('editorinit', f, editorInstance);
30767 //<script type="text/javascript">
30769 * @class Roo.form.GridField
30770 * @extends Roo.form.Field
30771 * Embed a grid (or editable grid into a form)
30774 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30776 * xgrid.store = Roo.data.Store
30777 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30778 * xgrid.store.reader = Roo.data.JsonReader
30782 * Creates a new GridField
30783 * @param {Object} config Configuration options
30785 Roo.form.GridField = function(config){
30786 Roo.form.GridField.superclass.constructor.call(this, config);
30790 Roo.extend(Roo.form.GridField, Roo.form.Field, {
30792 * @cfg {Number} width - used to restrict width of grid..
30796 * @cfg {Number} height - used to restrict height of grid..
30800 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30806 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30807 * {tag: "input", type: "checkbox", autocomplete: "off"})
30809 // defaultAutoCreate : { tag: 'div' },
30810 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30812 * @cfg {String} addTitle Text to include for adding a title.
30816 onResize : function(){
30817 Roo.form.Field.superclass.onResize.apply(this, arguments);
30820 initEvents : function(){
30821 // Roo.form.Checkbox.superclass.initEvents.call(this);
30822 // has no events...
30827 getResizeEl : function(){
30831 getPositionEl : function(){
30836 onRender : function(ct, position){
30838 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30839 var style = this.style;
30842 Roo.form.GridField.superclass.onRender.call(this, ct, position);
30843 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30844 this.viewEl = this.wrap.createChild({ tag: 'div' });
30846 this.viewEl.applyStyles(style);
30849 this.viewEl.setWidth(this.width);
30852 this.viewEl.setHeight(this.height);
30854 //if(this.inputValue !== undefined){
30855 //this.setValue(this.value);
30858 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30861 this.grid.render();
30862 this.grid.getDataSource().on('remove', this.refreshValue, this);
30863 this.grid.getDataSource().on('update', this.refreshValue, this);
30864 this.grid.on('afteredit', this.refreshValue, this);
30870 * Sets the value of the item.
30871 * @param {String} either an object or a string..
30873 setValue : function(v){
30875 v = v || []; // empty set..
30876 // this does not seem smart - it really only affects memoryproxy grids..
30877 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30878 var ds = this.grid.getDataSource();
30879 // assumes a json reader..
30881 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
30882 ds.loadData( data);
30884 // clear selection so it does not get stale.
30885 if (this.grid.sm) {
30886 this.grid.sm.clearSelections();
30889 Roo.form.GridField.superclass.setValue.call(this, v);
30890 this.refreshValue();
30891 // should load data in the grid really....
30895 refreshValue: function() {
30897 this.grid.getDataSource().each(function(r) {
30900 this.el.dom.value = Roo.encode(val);
30908 * Ext JS Library 1.1.1
30909 * Copyright(c) 2006-2007, Ext JS, LLC.
30911 * Originally Released Under LGPL - original licence link has changed is not relivant.
30914 * <script type="text/javascript">
30917 * @class Roo.form.DisplayField
30918 * @extends Roo.form.Field
30919 * A generic Field to display non-editable data.
30920 * @cfg {Boolean} closable (true|false) default false
30922 * Creates a new Display Field item.
30923 * @param {Object} config Configuration options
30925 Roo.form.DisplayField = function(config){
30926 Roo.form.DisplayField.superclass.constructor.call(this, config);
30931 * Fires after the click the close btn
30932 * @param {Roo.form.DisplayField} this
30938 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
30939 inputType: 'hidden',
30945 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30947 focusClass : undefined,
30949 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30951 fieldClass: 'x-form-field',
30954 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30956 valueRenderer: undefined,
30960 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30961 * {tag: "input", type: "checkbox", autocomplete: "off"})
30964 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30968 onResize : function(){
30969 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30973 initEvents : function(){
30974 // Roo.form.Checkbox.superclass.initEvents.call(this);
30975 // has no events...
30978 this.closeEl.on('click', this.onClose, this);
30984 getResizeEl : function(){
30988 getPositionEl : function(){
30993 onRender : function(ct, position){
30995 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30996 //if(this.inputValue !== undefined){
30997 this.wrap = this.el.wrap();
30999 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31002 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31005 if (this.bodyStyle) {
31006 this.viewEl.applyStyles(this.bodyStyle);
31008 //this.viewEl.setStyle('padding', '2px');
31010 this.setValue(this.value);
31015 initValue : Roo.emptyFn,
31020 onClick : function(){
31025 * Sets the checked state of the checkbox.
31026 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31028 setValue : function(v){
31030 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
31031 // this might be called before we have a dom element..
31032 if (!this.viewEl) {
31035 this.viewEl.dom.innerHTML = html;
31036 Roo.form.DisplayField.superclass.setValue.call(this, v);
31040 onClose : function(e)
31042 e.preventDefault();
31044 this.fireEvent('close', this);
31053 * @class Roo.form.DayPicker
31054 * @extends Roo.form.Field
31055 * A Day picker show [M] [T] [W] ....
31057 * Creates a new Day Picker
31058 * @param {Object} config Configuration options
31060 Roo.form.DayPicker= function(config){
31061 Roo.form.DayPicker.superclass.constructor.call(this, config);
31065 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
31067 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31069 focusClass : undefined,
31071 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31073 fieldClass: "x-form-field",
31076 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31077 * {tag: "input", type: "checkbox", autocomplete: "off"})
31079 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31082 actionMode : 'viewEl',
31086 inputType : 'hidden',
31089 inputElement: false, // real input element?
31090 basedOn: false, // ????
31092 isFormField: true, // not sure where this is needed!!!!
31094 onResize : function(){
31095 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31096 if(!this.boxLabel){
31097 this.el.alignTo(this.wrap, 'c-c');
31101 initEvents : function(){
31102 Roo.form.Checkbox.superclass.initEvents.call(this);
31103 this.el.on("click", this.onClick, this);
31104 this.el.on("change", this.onClick, this);
31108 getResizeEl : function(){
31112 getPositionEl : function(){
31118 onRender : function(ct, position){
31119 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31121 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31123 var r1 = '<table><tr>';
31124 var r2 = '<tr class="x-form-daypick-icons">';
31125 for (var i=0; i < 7; i++) {
31126 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31127 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
31130 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31131 viewEl.select('img').on('click', this.onClick, this);
31132 this.viewEl = viewEl;
31135 // this will not work on Chrome!!!
31136 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
31137 this.el.on('propertychange', this.setFromHidden, this); //ie
31145 initValue : Roo.emptyFn,
31148 * Returns the checked state of the checkbox.
31149 * @return {Boolean} True if checked, else false
31151 getValue : function(){
31152 return this.el.dom.value;
31157 onClick : function(e){
31158 //this.setChecked(!this.checked);
31159 Roo.get(e.target).toggleClass('x-menu-item-checked');
31160 this.refreshValue();
31161 //if(this.el.dom.checked != this.checked){
31162 // this.setValue(this.el.dom.checked);
31167 refreshValue : function()
31170 this.viewEl.select('img',true).each(function(e,i,n) {
31171 val += e.is(".x-menu-item-checked") ? String(n) : '';
31173 this.setValue(val, true);
31177 * Sets the checked state of the checkbox.
31178 * On is always based on a string comparison between inputValue and the param.
31179 * @param {Boolean/String} value - the value to set
31180 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31182 setValue : function(v,suppressEvent){
31183 if (!this.el.dom) {
31186 var old = this.el.dom.value ;
31187 this.el.dom.value = v;
31188 if (suppressEvent) {
31192 // update display..
31193 this.viewEl.select('img',true).each(function(e,i,n) {
31195 var on = e.is(".x-menu-item-checked");
31196 var newv = v.indexOf(String(n)) > -1;
31198 e.toggleClass('x-menu-item-checked');
31204 this.fireEvent('change', this, v, old);
31209 // handle setting of hidden value by some other method!!?!?
31210 setFromHidden: function()
31215 //console.log("SET FROM HIDDEN");
31216 //alert('setFrom hidden');
31217 this.setValue(this.el.dom.value);
31220 onDestroy : function()
31223 Roo.get(this.viewEl).remove();
31226 Roo.form.DayPicker.superclass.onDestroy.call(this);
31230 * RooJS Library 1.1.1
31231 * Copyright(c) 2008-2011 Alan Knowles
31238 * @class Roo.form.ComboCheck
31239 * @extends Roo.form.ComboBox
31240 * A combobox for multiple select items.
31242 * FIXME - could do with a reset button..
31245 * Create a new ComboCheck
31246 * @param {Object} config Configuration options
31248 Roo.form.ComboCheck = function(config){
31249 Roo.form.ComboCheck.superclass.constructor.call(this, config);
31250 // should verify some data...
31252 // hiddenName = required..
31253 // displayField = required
31254 // valudField == required
31255 var req= [ 'hiddenName', 'displayField', 'valueField' ];
31257 Roo.each(req, function(e) {
31258 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31259 throw "Roo.form.ComboCheck : missing value for: " + e;
31266 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31271 selectedClass: 'x-menu-item-checked',
31274 onRender : function(ct, position){
31280 var cls = 'x-combo-list';
31283 this.tpl = new Roo.Template({
31284 html : '<div class="'+cls+'-item x-menu-check-item">' +
31285 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
31286 '<span>{' + this.displayField + '}</span>' +
31293 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31294 this.view.singleSelect = false;
31295 this.view.multiSelect = true;
31296 this.view.toggleSelect = true;
31297 this.pageTb.add(new Roo.Toolbar.Fill(), {
31300 handler: function()
31307 onViewOver : function(e, t){
31313 onViewClick : function(doFocus,index){
31317 select: function () {
31318 //Roo.log("SELECT CALLED");
31321 selectByValue : function(xv, scrollIntoView){
31322 var ar = this.getValueArray();
31325 Roo.each(ar, function(v) {
31326 if(v === undefined || v === null){
31329 var r = this.findRecord(this.valueField, v);
31331 sels.push(this.store.indexOf(r))
31335 this.view.select(sels);
31341 onSelect : function(record, index){
31342 // Roo.log("onselect Called");
31343 // this is only called by the clear button now..
31344 this.view.clearSelections();
31345 this.setValue('[]');
31346 if (this.value != this.valueBefore) {
31347 this.fireEvent('change', this, this.value, this.valueBefore);
31348 this.valueBefore = this.value;
31351 getValueArray : function()
31356 //Roo.log(this.value);
31357 if (typeof(this.value) == 'undefined') {
31360 var ar = Roo.decode(this.value);
31361 return ar instanceof Array ? ar : []; //?? valid?
31364 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
31369 expand : function ()
31372 Roo.form.ComboCheck.superclass.expand.call(this);
31373 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31374 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31379 collapse : function(){
31380 Roo.form.ComboCheck.superclass.collapse.call(this);
31381 var sl = this.view.getSelectedIndexes();
31382 var st = this.store;
31386 Roo.each(sl, function(i) {
31388 nv.push(r.get(this.valueField));
31390 this.setValue(Roo.encode(nv));
31391 if (this.value != this.valueBefore) {
31393 this.fireEvent('change', this, this.value, this.valueBefore);
31394 this.valueBefore = this.value;
31399 setValue : function(v){
31403 var vals = this.getValueArray();
31405 Roo.each(vals, function(k) {
31406 var r = this.findRecord(this.valueField, k);
31408 tv.push(r.data[this.displayField]);
31409 }else if(this.valueNotFoundText !== undefined){
31410 tv.push( this.valueNotFoundText );
31415 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31416 this.hiddenField.value = v;
31422 * Ext JS Library 1.1.1
31423 * Copyright(c) 2006-2007, Ext JS, LLC.
31425 * Originally Released Under LGPL - original licence link has changed is not relivant.
31428 * <script type="text/javascript">
31432 * @class Roo.form.Signature
31433 * @extends Roo.form.Field
31437 * @param {Object} config Configuration options
31440 Roo.form.Signature = function(config){
31441 Roo.form.Signature.superclass.constructor.call(this, config);
31443 this.addEvents({// not in used??
31446 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31447 * @param {Roo.form.Signature} combo This combo box
31452 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31453 * @param {Roo.form.ComboBox} combo This combo box
31454 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31460 Roo.extend(Roo.form.Signature, Roo.form.Field, {
31462 * @cfg {Object} labels Label to use when rendering a form.
31466 * confirm : "Confirm"
31471 confirm : "Confirm"
31474 * @cfg {Number} width The signature panel width (defaults to 300)
31478 * @cfg {Number} height The signature panel height (defaults to 100)
31482 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31484 allowBlank : false,
31487 // {Object} signPanel The signature SVG panel element (defaults to {})
31489 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31490 isMouseDown : false,
31491 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31492 isConfirmed : false,
31493 // {String} signatureTmp SVG mapping string (defaults to empty string)
31497 defaultAutoCreate : { // modified by initCompnoent..
31503 onRender : function(ct, position){
31505 Roo.form.Signature.superclass.onRender.call(this, ct, position);
31507 this.wrap = this.el.wrap({
31508 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31511 this.createToolbar(this);
31512 this.signPanel = this.wrap.createChild({
31514 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31518 this.svgID = Roo.id();
31519 this.svgEl = this.signPanel.createChild({
31520 xmlns : 'http://www.w3.org/2000/svg',
31522 id : this.svgID + "-svg",
31524 height: this.height,
31525 viewBox: '0 0 '+this.width+' '+this.height,
31529 id: this.svgID + "-svg-r",
31531 height: this.height,
31536 id: this.svgID + "-svg-l",
31538 y1: (this.height*0.8), // start set the line in 80% of height
31539 x2: this.width, // end
31540 y2: (this.height*0.8), // end set the line in 80% of height
31542 'stroke-width': "1",
31543 'stroke-dasharray': "3",
31544 'shape-rendering': "crispEdges",
31545 'pointer-events': "none"
31549 id: this.svgID + "-svg-p",
31551 'stroke-width': "3",
31553 'pointer-events': 'none'
31558 this.svgBox = this.svgEl.dom.getScreenCTM();
31560 createSVG : function(){
31561 var svg = this.signPanel;
31562 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31565 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31566 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31567 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31568 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31569 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31570 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31571 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31574 isTouchEvent : function(e){
31575 return e.type.match(/^touch/);
31577 getCoords : function (e) {
31578 var pt = this.svgEl.dom.createSVGPoint();
31581 if (this.isTouchEvent(e)) {
31582 pt.x = e.targetTouches[0].clientX;
31583 pt.y = e.targetTouches[0].clientY;
31585 var a = this.svgEl.dom.getScreenCTM();
31586 var b = a.inverse();
31587 var mx = pt.matrixTransform(b);
31588 return mx.x + ',' + mx.y;
31590 //mouse event headler
31591 down : function (e) {
31592 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31593 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31595 this.isMouseDown = true;
31597 e.preventDefault();
31599 move : function (e) {
31600 if (this.isMouseDown) {
31601 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31602 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31605 e.preventDefault();
31607 up : function (e) {
31608 this.isMouseDown = false;
31609 var sp = this.signatureTmp.split(' ');
31612 if(!sp[sp.length-2].match(/^L/)){
31616 this.signatureTmp = sp.join(" ");
31619 if(this.getValue() != this.signatureTmp){
31620 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31621 this.isConfirmed = false;
31623 e.preventDefault();
31627 * Protected method that will not generally be called directly. It
31628 * is called when the editor creates its toolbar. Override this method if you need to
31629 * add custom toolbar buttons.
31630 * @param {HtmlEditor} editor
31632 createToolbar : function(editor){
31633 function btn(id, toggle, handler){
31634 var xid = fid + '-'+ id ;
31638 cls : 'x-btn-icon x-edit-'+id,
31639 enableToggle:toggle !== false,
31640 scope: editor, // was editor...
31641 handler:handler||editor.relayBtnCmd,
31642 clickEvent:'mousedown',
31643 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31649 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31653 cls : ' x-signature-btn x-signature-'+id,
31654 scope: editor, // was editor...
31655 handler: this.reset,
31656 clickEvent:'mousedown',
31657 text: this.labels.clear
31664 cls : ' x-signature-btn x-signature-'+id,
31665 scope: editor, // was editor...
31666 handler: this.confirmHandler,
31667 clickEvent:'mousedown',
31668 text: this.labels.confirm
31675 * when user is clicked confirm then show this image.....
31677 * @return {String} Image Data URI
31679 getImageDataURI : function(){
31680 var svg = this.svgEl.dom.parentNode.innerHTML;
31681 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31686 * @return {Boolean} this.isConfirmed
31688 getConfirmed : function(){
31689 return this.isConfirmed;
31693 * @return {Number} this.width
31695 getWidth : function(){
31700 * @return {Number} this.height
31702 getHeight : function(){
31703 return this.height;
31706 getSignature : function(){
31707 return this.signatureTmp;
31710 reset : function(){
31711 this.signatureTmp = '';
31712 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31713 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31714 this.isConfirmed = false;
31715 Roo.form.Signature.superclass.reset.call(this);
31717 setSignature : function(s){
31718 this.signatureTmp = s;
31719 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31720 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31722 this.isConfirmed = false;
31723 Roo.form.Signature.superclass.reset.call(this);
31726 // Roo.log(this.signPanel.dom.contentWindow.up())
31729 setConfirmed : function(){
31733 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31736 confirmHandler : function(){
31737 if(!this.getSignature()){
31741 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31742 this.setValue(this.getSignature());
31743 this.isConfirmed = true;
31745 this.fireEvent('confirm', this);
31748 // Subclasses should provide the validation implementation by overriding this
31749 validateValue : function(value){
31750 if(this.allowBlank){
31754 if(this.isConfirmed){
31761 * Ext JS Library 1.1.1
31762 * Copyright(c) 2006-2007, Ext JS, LLC.
31764 * Originally Released Under LGPL - original licence link has changed is not relivant.
31767 * <script type="text/javascript">
31772 * @class Roo.form.ComboBox
31773 * @extends Roo.form.TriggerField
31774 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31776 * Create a new ComboBox.
31777 * @param {Object} config Configuration options
31779 Roo.form.Select = function(config){
31780 Roo.form.Select.superclass.constructor.call(this, config);
31784 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31786 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31789 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31790 * rendering into an Roo.Editor, defaults to false)
31793 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31794 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31797 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31800 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31801 * the dropdown list (defaults to undefined, with no header element)
31805 * @cfg {String/Roo.Template} tpl The template to use to render the output
31809 defaultAutoCreate : {tag: "select" },
31811 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31813 listWidth: undefined,
31815 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31816 * mode = 'remote' or 'text' if mode = 'local')
31818 displayField: undefined,
31820 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31821 * mode = 'remote' or 'value' if mode = 'local').
31822 * Note: use of a valueField requires the user make a selection
31823 * in order for a value to be mapped.
31825 valueField: undefined,
31829 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31830 * field's data value (defaults to the underlying DOM element's name)
31832 hiddenName: undefined,
31834 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31838 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31840 selectedClass: 'x-combo-selected',
31842 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
31843 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31844 * which displays a downward arrow icon).
31846 triggerClass : 'x-form-arrow-trigger',
31848 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31852 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31853 * anchor positions (defaults to 'tl-bl')
31855 listAlign: 'tl-bl?',
31857 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31861 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
31862 * query specified by the allQuery config option (defaults to 'query')
31864 triggerAction: 'query',
31866 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31867 * (defaults to 4, does not apply if editable = false)
31871 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31872 * delay (typeAheadDelay) if it matches a known value (defaults to false)
31876 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31877 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31881 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31882 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
31886 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
31887 * when editable = true (defaults to false)
31889 selectOnFocus:false,
31891 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31893 queryParam: 'query',
31895 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
31896 * when mode = 'remote' (defaults to 'Loading...')
31898 loadingText: 'Loading...',
31900 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31904 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31908 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31909 * traditional select (defaults to true)
31913 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31917 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31921 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31922 * listWidth has a higher value)
31926 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31927 * allow the user to set arbitrary text into the field (defaults to false)
31929 forceSelection:false,
31931 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31932 * if typeAhead = true (defaults to 250)
31934 typeAheadDelay : 250,
31936 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31937 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31939 valueNotFoundText : undefined,
31942 * @cfg {String} defaultValue The value displayed after loading the store.
31947 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31949 blockFocus : false,
31952 * @cfg {Boolean} disableClear Disable showing of clear button.
31954 disableClear : false,
31956 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
31958 alwaysQuery : false,
31964 // element that contains real text value.. (when hidden is used..)
31967 onRender : function(ct, position){
31968 Roo.form.Field.prototype.onRender.call(this, ct, position);
31971 this.store.on('beforeload', this.onBeforeLoad, this);
31972 this.store.on('load', this.onLoad, this);
31973 this.store.on('loadexception', this.onLoadException, this);
31974 this.store.load({});
31982 initEvents : function(){
31983 //Roo.form.ComboBox.superclass.initEvents.call(this);
31987 onDestroy : function(){
31990 this.store.un('beforeload', this.onBeforeLoad, this);
31991 this.store.un('load', this.onLoad, this);
31992 this.store.un('loadexception', this.onLoadException, this);
31994 //Roo.form.ComboBox.superclass.onDestroy.call(this);
31998 fireKey : function(e){
31999 if(e.isNavKeyPress() && !this.list.isVisible()){
32000 this.fireEvent("specialkey", this, e);
32005 onResize: function(w, h){
32013 * Allow or prevent the user from directly editing the field text. If false is passed,
32014 * the user will only be able to select from the items defined in the dropdown list. This method
32015 * is the runtime equivalent of setting the 'editable' config option at config time.
32016 * @param {Boolean} value True to allow the user to directly edit the field text
32018 setEditable : function(value){
32023 onBeforeLoad : function(){
32025 Roo.log("Select before load");
32028 this.innerList.update(this.loadingText ?
32029 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32030 //this.restrictHeight();
32031 this.selectedIndex = -1;
32035 onLoad : function(){
32038 var dom = this.el.dom;
32039 dom.innerHTML = '';
32040 var od = dom.ownerDocument;
32042 if (this.emptyText) {
32043 var op = od.createElement('option');
32044 op.setAttribute('value', '');
32045 op.innerHTML = String.format('{0}', this.emptyText);
32046 dom.appendChild(op);
32048 if(this.store.getCount() > 0){
32050 var vf = this.valueField;
32051 var df = this.displayField;
32052 this.store.data.each(function(r) {
32053 // which colmsn to use... testing - cdoe / title..
32054 var op = od.createElement('option');
32055 op.setAttribute('value', r.data[vf]);
32056 op.innerHTML = String.format('{0}', r.data[df]);
32057 dom.appendChild(op);
32059 if (typeof(this.defaultValue != 'undefined')) {
32060 this.setValue(this.defaultValue);
32065 //this.onEmptyResults();
32070 onLoadException : function()
32072 dom.innerHTML = '';
32074 Roo.log("Select on load exception");
32078 Roo.log(this.store.reader.jsonData);
32079 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32080 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32086 onTypeAhead : function(){
32091 onSelect : function(record, index){
32092 Roo.log('on select?');
32094 if(this.fireEvent('beforeselect', this, record, index) !== false){
32095 this.setFromData(index > -1 ? record.data : false);
32097 this.fireEvent('select', this, record, index);
32102 * Returns the currently selected field value or empty string if no value is set.
32103 * @return {String} value The selected value
32105 getValue : function(){
32106 var dom = this.el.dom;
32107 this.value = dom.options[dom.selectedIndex].value;
32113 * Clears any text/value currently set in the field
32115 clearValue : function(){
32117 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32122 * Sets the specified value into the field. If the value finds a match, the corresponding record text
32123 * will be displayed in the field. If the value does not match the data value of an existing item,
32124 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32125 * Otherwise the field will be blank (although the value will still be set).
32126 * @param {String} value The value to match
32128 setValue : function(v){
32129 var d = this.el.dom;
32130 for (var i =0; i < d.options.length;i++) {
32131 if (v == d.options[i].value) {
32132 d.selectedIndex = i;
32140 * @property {Object} the last set data for the element
32145 * Sets the value of the field based on a object which is related to the record format for the store.
32146 * @param {Object} value the value to set as. or false on reset?
32148 setFromData : function(o){
32149 Roo.log('setfrom data?');
32155 reset : function(){
32159 findRecord : function(prop, value){
32164 if(this.store.getCount() > 0){
32165 this.store.each(function(r){
32166 if(r.data[prop] == value){
32176 getName: function()
32178 // returns hidden if it's set..
32179 if (!this.rendered) {return ''};
32180 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
32188 onEmptyResults : function(){
32189 Roo.log('empty results');
32194 * Returns true if the dropdown list is expanded, else false.
32196 isExpanded : function(){
32201 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32202 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32203 * @param {String} value The data value of the item to select
32204 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32205 * selected item if it is not currently in view (defaults to true)
32206 * @return {Boolean} True if the value matched an item in the list, else false
32208 selectByValue : function(v, scrollIntoView){
32209 Roo.log('select By Value');
32212 if(v !== undefined && v !== null){
32213 var r = this.findRecord(this.valueField || this.displayField, v);
32215 this.select(this.store.indexOf(r), scrollIntoView);
32223 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32224 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32225 * @param {Number} index The zero-based index of the list item to select
32226 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32227 * selected item if it is not currently in view (defaults to true)
32229 select : function(index, scrollIntoView){
32230 Roo.log('select ');
32233 this.selectedIndex = index;
32234 this.view.select(index);
32235 if(scrollIntoView !== false){
32236 var el = this.view.getNode(index);
32238 this.innerList.scrollChildIntoView(el, false);
32246 validateBlur : function(){
32253 initQuery : function(){
32254 this.doQuery(this.getRawValue());
32258 doForce : function(){
32259 if(this.el.dom.value.length > 0){
32260 this.el.dom.value =
32261 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32267 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
32268 * query allowing the query action to be canceled if needed.
32269 * @param {String} query The SQL query to execute
32270 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32271 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
32272 * saved in the current store (defaults to false)
32274 doQuery : function(q, forceAll){
32276 Roo.log('doQuery?');
32277 if(q === undefined || q === null){
32282 forceAll: forceAll,
32286 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32290 forceAll = qe.forceAll;
32291 if(forceAll === true || (q.length >= this.minChars)){
32292 if(this.lastQuery != q || this.alwaysQuery){
32293 this.lastQuery = q;
32294 if(this.mode == 'local'){
32295 this.selectedIndex = -1;
32297 this.store.clearFilter();
32299 this.store.filter(this.displayField, q);
32303 this.store.baseParams[this.queryParam] = q;
32305 params: this.getParams(q)
32310 this.selectedIndex = -1;
32317 getParams : function(q){
32319 //p[this.queryParam] = q;
32322 p.limit = this.pageSize;
32328 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32330 collapse : function(){
32335 collapseIf : function(e){
32340 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32342 expand : function(){
32350 * @cfg {Boolean} grow
32354 * @cfg {Number} growMin
32358 * @cfg {Number} growMax
32366 setWidth : function()
32370 getResizeEl : function(){
32373 });//<script type="text/javasscript">
32377 * @class Roo.DDView
32378 * A DnD enabled version of Roo.View.
32379 * @param {Element/String} container The Element in which to create the View.
32380 * @param {String} tpl The template string used to create the markup for each element of the View
32381 * @param {Object} config The configuration properties. These include all the config options of
32382 * {@link Roo.View} plus some specific to this class.<br>
32384 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32385 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32387 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32388 .x-view-drag-insert-above {
32389 border-top:1px dotted #3366cc;
32391 .x-view-drag-insert-below {
32392 border-bottom:1px dotted #3366cc;
32398 Roo.DDView = function(container, tpl, config) {
32399 Roo.DDView.superclass.constructor.apply(this, arguments);
32400 this.getEl().setStyle("outline", "0px none");
32401 this.getEl().unselectable();
32402 if (this.dragGroup) {
32403 this.setDraggable(this.dragGroup.split(","));
32405 if (this.dropGroup) {
32406 this.setDroppable(this.dropGroup.split(","));
32408 if (this.deletable) {
32409 this.setDeletable();
32411 this.isDirtyFlag = false;
32417 Roo.extend(Roo.DDView, Roo.View, {
32418 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32419 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32420 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32421 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32425 reset: Roo.emptyFn,
32427 clearInvalid: Roo.form.Field.prototype.clearInvalid,
32429 validate: function() {
32433 destroy: function() {
32434 this.purgeListeners();
32435 this.getEl.removeAllListeners();
32436 this.getEl().remove();
32437 if (this.dragZone) {
32438 if (this.dragZone.destroy) {
32439 this.dragZone.destroy();
32442 if (this.dropZone) {
32443 if (this.dropZone.destroy) {
32444 this.dropZone.destroy();
32449 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32450 getName: function() {
32454 /** Loads the View from a JSON string representing the Records to put into the Store. */
32455 setValue: function(v) {
32457 throw "DDView.setValue(). DDView must be constructed with a valid Store";
32460 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32461 this.store.proxy = new Roo.data.MemoryProxy(data);
32465 /** @return {String} a parenthesised list of the ids of the Records in the View. */
32466 getValue: function() {
32468 this.store.each(function(rec) {
32469 result += rec.id + ',';
32471 return result.substr(0, result.length - 1) + ')';
32474 getIds: function() {
32475 var i = 0, result = new Array(this.store.getCount());
32476 this.store.each(function(rec) {
32477 result[i++] = rec.id;
32482 isDirty: function() {
32483 return this.isDirtyFlag;
32487 * Part of the Roo.dd.DropZone interface. If no target node is found, the
32488 * whole Element becomes the target, and this causes the drop gesture to append.
32490 getTargetFromEvent : function(e) {
32491 var target = e.getTarget();
32492 while ((target !== null) && (target.parentNode != this.el.dom)) {
32493 target = target.parentNode;
32496 target = this.el.dom.lastChild || this.el.dom;
32502 * Create the drag data which consists of an object which has the property "ddel" as
32503 * the drag proxy element.
32505 getDragData : function(e) {
32506 var target = this.findItemFromChild(e.getTarget());
32508 this.handleSelection(e);
32509 var selNodes = this.getSelectedNodes();
32512 copy: this.copy || (this.allowCopy && e.ctrlKey),
32516 var selectedIndices = this.getSelectedIndexes();
32517 for (var i = 0; i < selectedIndices.length; i++) {
32518 dragData.records.push(this.store.getAt(selectedIndices[i]));
32520 if (selNodes.length == 1) {
32521 dragData.ddel = target.cloneNode(true); // the div element
32523 var div = document.createElement('div'); // create the multi element drag "ghost"
32524 div.className = 'multi-proxy';
32525 for (var i = 0, len = selNodes.length; i < len; i++) {
32526 div.appendChild(selNodes[i].cloneNode(true));
32528 dragData.ddel = div;
32530 //console.log(dragData)
32531 //console.log(dragData.ddel.innerHTML)
32534 //console.log('nodragData')
32538 /** Specify to which ddGroup items in this DDView may be dragged. */
32539 setDraggable: function(ddGroup) {
32540 if (ddGroup instanceof Array) {
32541 Roo.each(ddGroup, this.setDraggable, this);
32544 if (this.dragZone) {
32545 this.dragZone.addToGroup(ddGroup);
32547 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32548 containerScroll: true,
32552 // Draggability implies selection. DragZone's mousedown selects the element.
32553 if (!this.multiSelect) { this.singleSelect = true; }
32555 // Wire the DragZone's handlers up to methods in *this*
32556 this.dragZone.getDragData = this.getDragData.createDelegate(this);
32560 /** Specify from which ddGroup this DDView accepts drops. */
32561 setDroppable: function(ddGroup) {
32562 if (ddGroup instanceof Array) {
32563 Roo.each(ddGroup, this.setDroppable, this);
32566 if (this.dropZone) {
32567 this.dropZone.addToGroup(ddGroup);
32569 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32570 containerScroll: true,
32574 // Wire the DropZone's handlers up to methods in *this*
32575 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32576 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32577 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32578 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32579 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32583 /** Decide whether to drop above or below a View node. */
32584 getDropPoint : function(e, n, dd){
32585 if (n == this.el.dom) { return "above"; }
32586 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32587 var c = t + (b - t) / 2;
32588 var y = Roo.lib.Event.getPageY(e);
32596 onNodeEnter : function(n, dd, e, data){
32600 onNodeOver : function(n, dd, e, data){
32601 var pt = this.getDropPoint(e, n, dd);
32602 // set the insert point style on the target node
32603 var dragElClass = this.dropNotAllowed;
32606 if (pt == "above"){
32607 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32608 targetElClass = "x-view-drag-insert-above";
32610 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32611 targetElClass = "x-view-drag-insert-below";
32613 if (this.lastInsertClass != targetElClass){
32614 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32615 this.lastInsertClass = targetElClass;
32618 return dragElClass;
32621 onNodeOut : function(n, dd, e, data){
32622 this.removeDropIndicators(n);
32625 onNodeDrop : function(n, dd, e, data){
32626 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32629 var pt = this.getDropPoint(e, n, dd);
32630 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32631 if (pt == "below") { insertAt++; }
32632 for (var i = 0; i < data.records.length; i++) {
32633 var r = data.records[i];
32634 var dup = this.store.getById(r.id);
32635 if (dup && (dd != this.dragZone)) {
32636 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32639 this.store.insert(insertAt++, r.copy());
32641 data.source.isDirtyFlag = true;
32643 this.store.insert(insertAt++, r);
32645 this.isDirtyFlag = true;
32648 this.dragZone.cachedTarget = null;
32652 removeDropIndicators : function(n){
32654 Roo.fly(n).removeClass([
32655 "x-view-drag-insert-above",
32656 "x-view-drag-insert-below"]);
32657 this.lastInsertClass = "_noclass";
32662 * Utility method. Add a delete option to the DDView's context menu.
32663 * @param {String} imageUrl The URL of the "delete" icon image.
32665 setDeletable: function(imageUrl) {
32666 if (!this.singleSelect && !this.multiSelect) {
32667 this.singleSelect = true;
32669 var c = this.getContextMenu();
32670 this.contextMenu.on("itemclick", function(item) {
32673 this.remove(this.getSelectedIndexes());
32677 this.contextMenu.add({
32684 /** Return the context menu for this DDView. */
32685 getContextMenu: function() {
32686 if (!this.contextMenu) {
32687 // Create the View's context menu
32688 this.contextMenu = new Roo.menu.Menu({
32689 id: this.id + "-contextmenu"
32691 this.el.on("contextmenu", this.showContextMenu, this);
32693 return this.contextMenu;
32696 disableContextMenu: function() {
32697 if (this.contextMenu) {
32698 this.el.un("contextmenu", this.showContextMenu, this);
32702 showContextMenu: function(e, item) {
32703 item = this.findItemFromChild(e.getTarget());
32706 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32707 this.contextMenu.showAt(e.getXY());
32712 * Remove {@link Roo.data.Record}s at the specified indices.
32713 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32715 remove: function(selectedIndices) {
32716 selectedIndices = [].concat(selectedIndices);
32717 for (var i = 0; i < selectedIndices.length; i++) {
32718 var rec = this.store.getAt(selectedIndices[i]);
32719 this.store.remove(rec);
32724 * Double click fires the event, but also, if this is draggable, and there is only one other
32725 * related DropZone, it transfers the selected node.
32727 onDblClick : function(e){
32728 var item = this.findItemFromChild(e.getTarget());
32730 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32733 if (this.dragGroup) {
32734 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32735 while (targets.indexOf(this.dropZone) > -1) {
32736 targets.remove(this.dropZone);
32738 if (targets.length == 1) {
32739 this.dragZone.cachedTarget = null;
32740 var el = Roo.get(targets[0].getEl());
32741 var box = el.getBox(true);
32742 targets[0].onNodeDrop(el.dom, {
32744 xy: [box.x, box.y + box.height - 1]
32745 }, null, this.getDragData(e));
32751 handleSelection: function(e) {
32752 this.dragZone.cachedTarget = null;
32753 var item = this.findItemFromChild(e.getTarget());
32755 this.clearSelections(true);
32758 if (item && (this.multiSelect || this.singleSelect)){
32759 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32760 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32761 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32762 this.unselect(item);
32764 this.select(item, this.multiSelect && e.ctrlKey);
32765 this.lastSelection = item;
32770 onItemClick : function(item, index, e){
32771 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32777 unselect : function(nodeInfo, suppressEvent){
32778 var node = this.getNode(nodeInfo);
32779 if(node && this.isSelected(node)){
32780 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32781 Roo.fly(node).removeClass(this.selectedClass);
32782 this.selections.remove(node);
32783 if(!suppressEvent){
32784 this.fireEvent("selectionchange", this, this.selections);
32792 * Ext JS Library 1.1.1
32793 * Copyright(c) 2006-2007, Ext JS, LLC.
32795 * Originally Released Under LGPL - original licence link has changed is not relivant.
32798 * <script type="text/javascript">
32802 * @class Roo.LayoutManager
32803 * @extends Roo.util.Observable
32804 * Base class for layout managers.
32806 Roo.LayoutManager = function(container, config){
32807 Roo.LayoutManager.superclass.constructor.call(this);
32808 this.el = Roo.get(container);
32809 // ie scrollbar fix
32810 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32811 document.body.scroll = "no";
32812 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32813 this.el.position('relative');
32815 this.id = this.el.id;
32816 this.el.addClass("x-layout-container");
32817 /** false to disable window resize monitoring @type Boolean */
32818 this.monitorWindowResize = true;
32823 * Fires when a layout is performed.
32824 * @param {Roo.LayoutManager} this
32828 * @event regionresized
32829 * Fires when the user resizes a region.
32830 * @param {Roo.LayoutRegion} region The resized region
32831 * @param {Number} newSize The new size (width for east/west, height for north/south)
32833 "regionresized" : true,
32835 * @event regioncollapsed
32836 * Fires when a region is collapsed.
32837 * @param {Roo.LayoutRegion} region The collapsed region
32839 "regioncollapsed" : true,
32841 * @event regionexpanded
32842 * Fires when a region is expanded.
32843 * @param {Roo.LayoutRegion} region The expanded region
32845 "regionexpanded" : true
32847 this.updating = false;
32848 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32851 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32853 * Returns true if this layout is currently being updated
32854 * @return {Boolean}
32856 isUpdating : function(){
32857 return this.updating;
32861 * Suspend the LayoutManager from doing auto-layouts while
32862 * making multiple add or remove calls
32864 beginUpdate : function(){
32865 this.updating = true;
32869 * Restore auto-layouts and optionally disable the manager from performing a layout
32870 * @param {Boolean} noLayout true to disable a layout update
32872 endUpdate : function(noLayout){
32873 this.updating = false;
32879 layout: function(){
32883 onRegionResized : function(region, newSize){
32884 this.fireEvent("regionresized", region, newSize);
32888 onRegionCollapsed : function(region){
32889 this.fireEvent("regioncollapsed", region);
32892 onRegionExpanded : function(region){
32893 this.fireEvent("regionexpanded", region);
32897 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32898 * performs box-model adjustments.
32899 * @return {Object} The size as an object {width: (the width), height: (the height)}
32901 getViewSize : function(){
32903 if(this.el.dom != document.body){
32904 size = this.el.getSize();
32906 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32908 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32909 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32914 * Returns the Element this layout is bound to.
32915 * @return {Roo.Element}
32917 getEl : function(){
32922 * Returns the specified region.
32923 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32924 * @return {Roo.LayoutRegion}
32926 getRegion : function(target){
32927 return this.regions[target.toLowerCase()];
32930 onWindowResize : function(){
32931 if(this.monitorWindowResize){
32937 * Ext JS Library 1.1.1
32938 * Copyright(c) 2006-2007, Ext JS, LLC.
32940 * Originally Released Under LGPL - original licence link has changed is not relivant.
32943 * <script type="text/javascript">
32946 * @class Roo.BorderLayout
32947 * @extends Roo.LayoutManager
32948 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32949 * please see: <br><br>
32950 * <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>
32951 * <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>
32954 var layout = new Roo.BorderLayout(document.body, {
32988 preferredTabWidth: 150
32993 var CP = Roo.ContentPanel;
32995 layout.beginUpdate();
32996 layout.add("north", new CP("north", "North"));
32997 layout.add("south", new CP("south", {title: "South", closable: true}));
32998 layout.add("west", new CP("west", {title: "West"}));
32999 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33000 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33001 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33002 layout.getRegion("center").showPanel("center1");
33003 layout.endUpdate();
33006 <b>The container the layout is rendered into can be either the body element or any other element.
33007 If it is not the body element, the container needs to either be an absolute positioned element,
33008 or you will need to add "position:relative" to the css of the container. You will also need to specify
33009 the container size if it is not the body element.</b>
33012 * Create a new BorderLayout
33013 * @param {String/HTMLElement/Element} container The container this layout is bound to
33014 * @param {Object} config Configuration options
33016 Roo.BorderLayout = function(container, config){
33017 config = config || {};
33018 Roo.BorderLayout.superclass.constructor.call(this, container, config);
33019 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33020 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33021 var target = this.factory.validRegions[i];
33022 if(config[target]){
33023 this.addRegion(target, config[target]);
33028 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33030 * Creates and adds a new region if it doesn't already exist.
33031 * @param {String} target The target region key (north, south, east, west or center).
33032 * @param {Object} config The regions config object
33033 * @return {BorderLayoutRegion} The new region
33035 addRegion : function(target, config){
33036 if(!this.regions[target]){
33037 var r = this.factory.create(target, this, config);
33038 this.bindRegion(target, r);
33040 return this.regions[target];
33044 bindRegion : function(name, r){
33045 this.regions[name] = r;
33046 r.on("visibilitychange", this.layout, this);
33047 r.on("paneladded", this.layout, this);
33048 r.on("panelremoved", this.layout, this);
33049 r.on("invalidated", this.layout, this);
33050 r.on("resized", this.onRegionResized, this);
33051 r.on("collapsed", this.onRegionCollapsed, this);
33052 r.on("expanded", this.onRegionExpanded, this);
33056 * Performs a layout update.
33058 layout : function(){
33059 if(this.updating) {
33062 var size = this.getViewSize();
33063 var w = size.width;
33064 var h = size.height;
33069 //var x = 0, y = 0;
33071 var rs = this.regions;
33072 var north = rs["north"];
33073 var south = rs["south"];
33074 var west = rs["west"];
33075 var east = rs["east"];
33076 var center = rs["center"];
33077 //if(this.hideOnLayout){ // not supported anymore
33078 //c.el.setStyle("display", "none");
33080 if(north && north.isVisible()){
33081 var b = north.getBox();
33082 var m = north.getMargins();
33083 b.width = w - (m.left+m.right);
33086 centerY = b.height + b.y + m.bottom;
33087 centerH -= centerY;
33088 north.updateBox(this.safeBox(b));
33090 if(south && south.isVisible()){
33091 var b = south.getBox();
33092 var m = south.getMargins();
33093 b.width = w - (m.left+m.right);
33095 var totalHeight = (b.height + m.top + m.bottom);
33096 b.y = h - totalHeight + m.top;
33097 centerH -= totalHeight;
33098 south.updateBox(this.safeBox(b));
33100 if(west && west.isVisible()){
33101 var b = west.getBox();
33102 var m = west.getMargins();
33103 b.height = centerH - (m.top+m.bottom);
33105 b.y = centerY + m.top;
33106 var totalWidth = (b.width + m.left + m.right);
33107 centerX += totalWidth;
33108 centerW -= totalWidth;
33109 west.updateBox(this.safeBox(b));
33111 if(east && east.isVisible()){
33112 var b = east.getBox();
33113 var m = east.getMargins();
33114 b.height = centerH - (m.top+m.bottom);
33115 var totalWidth = (b.width + m.left + m.right);
33116 b.x = w - totalWidth + m.left;
33117 b.y = centerY + m.top;
33118 centerW -= totalWidth;
33119 east.updateBox(this.safeBox(b));
33122 var m = center.getMargins();
33124 x: centerX + m.left,
33125 y: centerY + m.top,
33126 width: centerW - (m.left+m.right),
33127 height: centerH - (m.top+m.bottom)
33129 //if(this.hideOnLayout){
33130 //center.el.setStyle("display", "block");
33132 center.updateBox(this.safeBox(centerBox));
33135 this.fireEvent("layout", this);
33139 safeBox : function(box){
33140 box.width = Math.max(0, box.width);
33141 box.height = Math.max(0, box.height);
33146 * Adds a ContentPanel (or subclass) to this layout.
33147 * @param {String} target The target region key (north, south, east, west or center).
33148 * @param {Roo.ContentPanel} panel The panel to add
33149 * @return {Roo.ContentPanel} The added panel
33151 add : function(target, panel){
33153 target = target.toLowerCase();
33154 return this.regions[target].add(panel);
33158 * Remove a ContentPanel (or subclass) to this layout.
33159 * @param {String} target The target region key (north, south, east, west or center).
33160 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33161 * @return {Roo.ContentPanel} The removed panel
33163 remove : function(target, panel){
33164 target = target.toLowerCase();
33165 return this.regions[target].remove(panel);
33169 * Searches all regions for a panel with the specified id
33170 * @param {String} panelId
33171 * @return {Roo.ContentPanel} The panel or null if it wasn't found
33173 findPanel : function(panelId){
33174 var rs = this.regions;
33175 for(var target in rs){
33176 if(typeof rs[target] != "function"){
33177 var p = rs[target].getPanel(panelId);
33187 * Searches all regions for a panel with the specified id and activates (shows) it.
33188 * @param {String/ContentPanel} panelId The panels id or the panel itself
33189 * @return {Roo.ContentPanel} The shown panel or null
33191 showPanel : function(panelId) {
33192 var rs = this.regions;
33193 for(var target in rs){
33194 var r = rs[target];
33195 if(typeof r != "function"){
33196 if(r.hasPanel(panelId)){
33197 return r.showPanel(panelId);
33205 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33206 * @param {Roo.state.Provider} provider (optional) An alternate state provider
33208 restoreState : function(provider){
33210 provider = Roo.state.Manager;
33212 var sm = new Roo.LayoutStateManager();
33213 sm.init(this, provider);
33217 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
33218 * object should contain properties for each region to add ContentPanels to, and each property's value should be
33219 * a valid ContentPanel config object. Example:
33221 // Create the main layout
33222 var layout = new Roo.BorderLayout('main-ct', {
33233 // Create and add multiple ContentPanels at once via configs
33236 id: 'source-files',
33238 title:'Ext Source Files',
33251 * @param {Object} regions An object containing ContentPanel configs by region name
33253 batchAdd : function(regions){
33254 this.beginUpdate();
33255 for(var rname in regions){
33256 var lr = this.regions[rname];
33258 this.addTypedPanels(lr, regions[rname]);
33265 addTypedPanels : function(lr, ps){
33266 if(typeof ps == 'string'){
33267 lr.add(new Roo.ContentPanel(ps));
33269 else if(ps instanceof Array){
33270 for(var i =0, len = ps.length; i < len; i++){
33271 this.addTypedPanels(lr, ps[i]);
33274 else if(!ps.events){ // raw config?
33276 delete ps.el; // prevent conflict
33277 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33279 else { // panel object assumed!
33284 * Adds a xtype elements to the layout.
33288 xtype : 'ContentPanel',
33295 xtype : 'NestedLayoutPanel',
33301 items : [ ... list of content panels or nested layout panels.. ]
33305 * @param {Object} cfg Xtype definition of item to add.
33307 addxtype : function(cfg)
33309 // basically accepts a pannel...
33310 // can accept a layout region..!?!?
33311 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33313 if (!cfg.xtype.match(/Panel$/)) {
33318 if (typeof(cfg.region) == 'undefined') {
33319 Roo.log("Failed to add Panel, region was not set");
33323 var region = cfg.region;
33329 xitems = cfg.items;
33336 case 'ContentPanel': // ContentPanel (el, cfg)
33337 case 'ScrollPanel': // ContentPanel (el, cfg)
33339 if(cfg.autoCreate) {
33340 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33342 var el = this.el.createChild();
33343 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33346 this.add(region, ret);
33350 case 'TreePanel': // our new panel!
33351 cfg.el = this.el.createChild();
33352 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33353 this.add(region, ret);
33356 case 'NestedLayoutPanel':
33357 // create a new Layout (which is a Border Layout...
33358 var el = this.el.createChild();
33359 var clayout = cfg.layout;
33361 clayout.items = clayout.items || [];
33362 // replace this exitems with the clayout ones..
33363 xitems = clayout.items;
33366 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33367 cfg.background = false;
33369 var layout = new Roo.BorderLayout(el, clayout);
33371 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33372 //console.log('adding nested layout panel ' + cfg.toSource());
33373 this.add(region, ret);
33374 nb = {}; /// find first...
33379 // needs grid and region
33381 //var el = this.getRegion(region).el.createChild();
33382 var el = this.el.createChild();
33383 // create the grid first...
33385 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33387 if (region == 'center' && this.active ) {
33388 cfg.background = false;
33390 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33392 this.add(region, ret);
33393 if (cfg.background) {
33394 ret.on('activate', function(gp) {
33395 if (!gp.grid.rendered) {
33410 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33412 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33413 this.add(region, ret);
33416 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33420 // GridPanel (grid, cfg)
33423 this.beginUpdate();
33427 Roo.each(xitems, function(i) {
33428 region = nb && i.region ? i.region : false;
33430 var add = ret.addxtype(i);
33433 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33434 if (!i.background) {
33435 abn[region] = nb[region] ;
33442 // make the last non-background panel active..
33443 //if (nb) { Roo.log(abn); }
33446 for(var r in abn) {
33447 region = this.getRegion(r);
33449 // tried using nb[r], but it does not work..
33451 region.showPanel(abn[r]);
33462 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33463 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
33464 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33465 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
33468 var CP = Roo.ContentPanel;
33470 var layout = Roo.BorderLayout.create({
33474 panels: [new CP("north", "North")]
33483 panels: [new CP("west", {title: "West"})]
33492 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33501 panels: [new CP("south", {title: "South", closable: true})]
33508 preferredTabWidth: 150,
33510 new CP("center1", {title: "Close Me", closable: true}),
33511 new CP("center2", {title: "Center Panel", closable: false})
33516 layout.getRegion("center").showPanel("center1");
33521 Roo.BorderLayout.create = function(config, targetEl){
33522 var layout = new Roo.BorderLayout(targetEl || document.body, config);
33523 layout.beginUpdate();
33524 var regions = Roo.BorderLayout.RegionFactory.validRegions;
33525 for(var j = 0, jlen = regions.length; j < jlen; j++){
33526 var lr = regions[j];
33527 if(layout.regions[lr] && config[lr].panels){
33528 var r = layout.regions[lr];
33529 var ps = config[lr].panels;
33530 layout.addTypedPanels(r, ps);
33533 layout.endUpdate();
33538 Roo.BorderLayout.RegionFactory = {
33540 validRegions : ["north","south","east","west","center"],
33543 create : function(target, mgr, config){
33544 target = target.toLowerCase();
33545 if(config.lightweight || config.basic){
33546 return new Roo.BasicLayoutRegion(mgr, config, target);
33550 return new Roo.NorthLayoutRegion(mgr, config);
33552 return new Roo.SouthLayoutRegion(mgr, config);
33554 return new Roo.EastLayoutRegion(mgr, config);
33556 return new Roo.WestLayoutRegion(mgr, config);
33558 return new Roo.CenterLayoutRegion(mgr, config);
33560 throw 'Layout region "'+target+'" not supported.';
33564 * Ext JS Library 1.1.1
33565 * Copyright(c) 2006-2007, Ext JS, LLC.
33567 * Originally Released Under LGPL - original licence link has changed is not relivant.
33570 * <script type="text/javascript">
33574 * @class Roo.BasicLayoutRegion
33575 * @extends Roo.util.Observable
33576 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33577 * and does not have a titlebar, tabs or any other features. All it does is size and position
33578 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33580 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33582 this.position = pos;
33585 * @scope Roo.BasicLayoutRegion
33589 * @event beforeremove
33590 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33591 * @param {Roo.LayoutRegion} this
33592 * @param {Roo.ContentPanel} panel The panel
33593 * @param {Object} e The cancel event object
33595 "beforeremove" : true,
33597 * @event invalidated
33598 * Fires when the layout for this region is changed.
33599 * @param {Roo.LayoutRegion} this
33601 "invalidated" : true,
33603 * @event visibilitychange
33604 * Fires when this region is shown or hidden
33605 * @param {Roo.LayoutRegion} this
33606 * @param {Boolean} visibility true or false
33608 "visibilitychange" : true,
33610 * @event paneladded
33611 * Fires when a panel is added.
33612 * @param {Roo.LayoutRegion} this
33613 * @param {Roo.ContentPanel} panel The panel
33615 "paneladded" : true,
33617 * @event panelremoved
33618 * Fires when a panel is removed.
33619 * @param {Roo.LayoutRegion} this
33620 * @param {Roo.ContentPanel} panel The panel
33622 "panelremoved" : true,
33624 * @event beforecollapse
33625 * Fires when this region before collapse.
33626 * @param {Roo.LayoutRegion} this
33628 "beforecollapse" : true,
33631 * Fires when this region is collapsed.
33632 * @param {Roo.LayoutRegion} this
33634 "collapsed" : true,
33637 * Fires when this region is expanded.
33638 * @param {Roo.LayoutRegion} this
33643 * Fires when this region is slid into view.
33644 * @param {Roo.LayoutRegion} this
33646 "slideshow" : true,
33649 * Fires when this region slides out of view.
33650 * @param {Roo.LayoutRegion} this
33652 "slidehide" : true,
33654 * @event panelactivated
33655 * Fires when a panel is activated.
33656 * @param {Roo.LayoutRegion} this
33657 * @param {Roo.ContentPanel} panel The activated panel
33659 "panelactivated" : true,
33662 * Fires when the user resizes this region.
33663 * @param {Roo.LayoutRegion} this
33664 * @param {Number} newSize The new size (width for east/west, height for north/south)
33668 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33669 this.panels = new Roo.util.MixedCollection();
33670 this.panels.getKey = this.getPanelId.createDelegate(this);
33672 this.activePanel = null;
33673 // ensure listeners are added...
33675 if (config.listeners || config.events) {
33676 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33677 listeners : config.listeners || {},
33678 events : config.events || {}
33682 if(skipConfig !== true){
33683 this.applyConfig(config);
33687 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33688 getPanelId : function(p){
33692 applyConfig : function(config){
33693 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33694 this.config = config;
33699 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33700 * the width, for horizontal (north, south) the height.
33701 * @param {Number} newSize The new width or height
33703 resizeTo : function(newSize){
33704 var el = this.el ? this.el :
33705 (this.activePanel ? this.activePanel.getEl() : null);
33707 switch(this.position){
33710 el.setWidth(newSize);
33711 this.fireEvent("resized", this, newSize);
33715 el.setHeight(newSize);
33716 this.fireEvent("resized", this, newSize);
33722 getBox : function(){
33723 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33726 getMargins : function(){
33727 return this.margins;
33730 updateBox : function(box){
33732 var el = this.activePanel.getEl();
33733 el.dom.style.left = box.x + "px";
33734 el.dom.style.top = box.y + "px";
33735 this.activePanel.setSize(box.width, box.height);
33739 * Returns the container element for this region.
33740 * @return {Roo.Element}
33742 getEl : function(){
33743 return this.activePanel;
33747 * Returns true if this region is currently visible.
33748 * @return {Boolean}
33750 isVisible : function(){
33751 return this.activePanel ? true : false;
33754 setActivePanel : function(panel){
33755 panel = this.getPanel(panel);
33756 if(this.activePanel && this.activePanel != panel){
33757 this.activePanel.setActiveState(false);
33758 this.activePanel.getEl().setLeftTop(-10000,-10000);
33760 this.activePanel = panel;
33761 panel.setActiveState(true);
33763 panel.setSize(this.box.width, this.box.height);
33765 this.fireEvent("panelactivated", this, panel);
33766 this.fireEvent("invalidated");
33770 * Show the specified panel.
33771 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33772 * @return {Roo.ContentPanel} The shown panel or null
33774 showPanel : function(panel){
33775 if(panel = this.getPanel(panel)){
33776 this.setActivePanel(panel);
33782 * Get the active panel for this region.
33783 * @return {Roo.ContentPanel} The active panel or null
33785 getActivePanel : function(){
33786 return this.activePanel;
33790 * Add the passed ContentPanel(s)
33791 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33792 * @return {Roo.ContentPanel} The panel added (if only one was added)
33794 add : function(panel){
33795 if(arguments.length > 1){
33796 for(var i = 0, len = arguments.length; i < len; i++) {
33797 this.add(arguments[i]);
33801 if(this.hasPanel(panel)){
33802 this.showPanel(panel);
33805 var el = panel.getEl();
33806 if(el.dom.parentNode != this.mgr.el.dom){
33807 this.mgr.el.dom.appendChild(el.dom);
33809 if(panel.setRegion){
33810 panel.setRegion(this);
33812 this.panels.add(panel);
33813 el.setStyle("position", "absolute");
33814 if(!panel.background){
33815 this.setActivePanel(panel);
33816 if(this.config.initialSize && this.panels.getCount()==1){
33817 this.resizeTo(this.config.initialSize);
33820 this.fireEvent("paneladded", this, panel);
33825 * Returns true if the panel is in this region.
33826 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33827 * @return {Boolean}
33829 hasPanel : function(panel){
33830 if(typeof panel == "object"){ // must be panel obj
33831 panel = panel.getId();
33833 return this.getPanel(panel) ? true : false;
33837 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33838 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33839 * @param {Boolean} preservePanel Overrides the config preservePanel option
33840 * @return {Roo.ContentPanel} The panel that was removed
33842 remove : function(panel, preservePanel){
33843 panel = this.getPanel(panel);
33848 this.fireEvent("beforeremove", this, panel, e);
33849 if(e.cancel === true){
33852 var panelId = panel.getId();
33853 this.panels.removeKey(panelId);
33858 * Returns the panel specified or null if it's not in this region.
33859 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33860 * @return {Roo.ContentPanel}
33862 getPanel : function(id){
33863 if(typeof id == "object"){ // must be panel obj
33866 return this.panels.get(id);
33870 * Returns this regions position (north/south/east/west/center).
33873 getPosition: function(){
33874 return this.position;
33878 * Ext JS Library 1.1.1
33879 * Copyright(c) 2006-2007, Ext JS, LLC.
33881 * Originally Released Under LGPL - original licence link has changed is not relivant.
33884 * <script type="text/javascript">
33888 * @class Roo.LayoutRegion
33889 * @extends Roo.BasicLayoutRegion
33890 * This class represents a region in a layout manager.
33891 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
33892 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
33893 * @cfg {Boolean} floatable False to disable floating (defaults to true)
33894 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33895 * @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})
33896 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33897 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
33898 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33899 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33900 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33901 * @cfg {String} title The title for the region (overrides panel titles)
33902 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33903 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33904 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33905 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33906 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33907 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33908 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33909 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33910 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33911 * @cfg {Boolean} showPin True to show a pin button
33912 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33913 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33914 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33915 * @cfg {Number} width For East/West panels
33916 * @cfg {Number} height For North/South panels
33917 * @cfg {Boolean} split To show the splitter
33918 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33920 Roo.LayoutRegion = function(mgr, config, pos){
33921 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33922 var dh = Roo.DomHelper;
33923 /** This region's container element
33924 * @type Roo.Element */
33925 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33926 /** This region's title element
33927 * @type Roo.Element */
33929 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33930 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
33931 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33933 this.titleEl.enableDisplayMode();
33934 /** This region's title text element
33935 * @type HTMLElement */
33936 this.titleTextEl = this.titleEl.dom.firstChild;
33937 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33938 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33939 this.closeBtn.enableDisplayMode();
33940 this.closeBtn.on("click", this.closeClicked, this);
33941 this.closeBtn.hide();
33943 this.createBody(config);
33944 this.visible = true;
33945 this.collapsed = false;
33947 if(config.hideWhenEmpty){
33949 this.on("paneladded", this.validateVisibility, this);
33950 this.on("panelremoved", this.validateVisibility, this);
33952 this.applyConfig(config);
33955 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33957 createBody : function(){
33958 /** This region's body element
33959 * @type Roo.Element */
33960 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33963 applyConfig : function(c){
33964 if(c.collapsible && this.position != "center" && !this.collapsedEl){
33965 var dh = Roo.DomHelper;
33966 if(c.titlebar !== false){
33967 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33968 this.collapseBtn.on("click", this.collapse, this);
33969 this.collapseBtn.enableDisplayMode();
33971 if(c.showPin === true || this.showPin){
33972 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33973 this.stickBtn.enableDisplayMode();
33974 this.stickBtn.on("click", this.expand, this);
33975 this.stickBtn.hide();
33978 /** This region's collapsed element
33979 * @type Roo.Element */
33980 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33981 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33983 if(c.floatable !== false){
33984 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33985 this.collapsedEl.on("click", this.collapseClick, this);
33988 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33989 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33990 id: "message", unselectable: "on", style:{"float":"left"}});
33991 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33993 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33994 this.expandBtn.on("click", this.expand, this);
33996 if(this.collapseBtn){
33997 this.collapseBtn.setVisible(c.collapsible == true);
33999 this.cmargins = c.cmargins || this.cmargins ||
34000 (this.position == "west" || this.position == "east" ?
34001 {top: 0, left: 2, right:2, bottom: 0} :
34002 {top: 2, left: 0, right:0, bottom: 2});
34003 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34004 this.bottomTabs = c.tabPosition != "top";
34005 this.autoScroll = c.autoScroll || false;
34006 if(this.autoScroll){
34007 this.bodyEl.setStyle("overflow", "auto");
34009 this.bodyEl.setStyle("overflow", "hidden");
34011 //if(c.titlebar !== false){
34012 if((!c.titlebar && !c.title) || c.titlebar === false){
34013 this.titleEl.hide();
34015 this.titleEl.show();
34017 this.titleTextEl.innerHTML = c.title;
34021 this.duration = c.duration || .30;
34022 this.slideDuration = c.slideDuration || .45;
34025 this.collapse(true);
34032 * Returns true if this region is currently visible.
34033 * @return {Boolean}
34035 isVisible : function(){
34036 return this.visible;
34040 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34041 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
34043 setCollapsedTitle : function(title){
34044 title = title || " ";
34045 if(this.collapsedTitleTextEl){
34046 this.collapsedTitleTextEl.innerHTML = title;
34050 getBox : function(){
34052 if(!this.collapsed){
34053 b = this.el.getBox(false, true);
34055 b = this.collapsedEl.getBox(false, true);
34060 getMargins : function(){
34061 return this.collapsed ? this.cmargins : this.margins;
34064 highlight : function(){
34065 this.el.addClass("x-layout-panel-dragover");
34068 unhighlight : function(){
34069 this.el.removeClass("x-layout-panel-dragover");
34072 updateBox : function(box){
34074 if(!this.collapsed){
34075 this.el.dom.style.left = box.x + "px";
34076 this.el.dom.style.top = box.y + "px";
34077 this.updateBody(box.width, box.height);
34079 this.collapsedEl.dom.style.left = box.x + "px";
34080 this.collapsedEl.dom.style.top = box.y + "px";
34081 this.collapsedEl.setSize(box.width, box.height);
34084 this.tabs.autoSizeTabs();
34088 updateBody : function(w, h){
34090 this.el.setWidth(w);
34091 w -= this.el.getBorderWidth("rl");
34092 if(this.config.adjustments){
34093 w += this.config.adjustments[0];
34097 this.el.setHeight(h);
34098 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34099 h -= this.el.getBorderWidth("tb");
34100 if(this.config.adjustments){
34101 h += this.config.adjustments[1];
34103 this.bodyEl.setHeight(h);
34105 h = this.tabs.syncHeight(h);
34108 if(this.panelSize){
34109 w = w !== null ? w : this.panelSize.width;
34110 h = h !== null ? h : this.panelSize.height;
34112 if(this.activePanel){
34113 var el = this.activePanel.getEl();
34114 w = w !== null ? w : el.getWidth();
34115 h = h !== null ? h : el.getHeight();
34116 this.panelSize = {width: w, height: h};
34117 this.activePanel.setSize(w, h);
34119 if(Roo.isIE && this.tabs){
34120 this.tabs.el.repaint();
34125 * Returns the container element for this region.
34126 * @return {Roo.Element}
34128 getEl : function(){
34133 * Hides this region.
34136 if(!this.collapsed){
34137 this.el.dom.style.left = "-2000px";
34140 this.collapsedEl.dom.style.left = "-2000px";
34141 this.collapsedEl.hide();
34143 this.visible = false;
34144 this.fireEvent("visibilitychange", this, false);
34148 * Shows this region if it was previously hidden.
34151 if(!this.collapsed){
34154 this.collapsedEl.show();
34156 this.visible = true;
34157 this.fireEvent("visibilitychange", this, true);
34160 closeClicked : function(){
34161 if(this.activePanel){
34162 this.remove(this.activePanel);
34166 collapseClick : function(e){
34168 e.stopPropagation();
34171 e.stopPropagation();
34177 * Collapses this region.
34178 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34180 collapse : function(skipAnim, skipCheck = false){
34181 if(this.collapsed) {
34185 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34187 this.collapsed = true;
34189 this.split.el.hide();
34191 if(this.config.animate && skipAnim !== true){
34192 this.fireEvent("invalidated", this);
34193 this.animateCollapse();
34195 this.el.setLocation(-20000,-20000);
34197 this.collapsedEl.show();
34198 this.fireEvent("collapsed", this);
34199 this.fireEvent("invalidated", this);
34205 animateCollapse : function(){
34210 * Expands this region if it was previously collapsed.
34211 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34212 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34214 expand : function(e, skipAnim){
34216 e.stopPropagation();
34218 if(!this.collapsed || this.el.hasActiveFx()) {
34222 this.afterSlideIn();
34225 this.collapsed = false;
34226 if(this.config.animate && skipAnim !== true){
34227 this.animateExpand();
34231 this.split.el.show();
34233 this.collapsedEl.setLocation(-2000,-2000);
34234 this.collapsedEl.hide();
34235 this.fireEvent("invalidated", this);
34236 this.fireEvent("expanded", this);
34240 animateExpand : function(){
34244 initTabs : function()
34246 this.bodyEl.setStyle("overflow", "hidden");
34247 var ts = new Roo.TabPanel(
34250 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34251 disableTooltips: this.config.disableTabTips,
34252 toolbar : this.config.toolbar
34255 if(this.config.hideTabs){
34256 ts.stripWrap.setDisplayed(false);
34259 ts.resizeTabs = this.config.resizeTabs === true;
34260 ts.minTabWidth = this.config.minTabWidth || 40;
34261 ts.maxTabWidth = this.config.maxTabWidth || 250;
34262 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34263 ts.monitorResize = false;
34264 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34265 ts.bodyEl.addClass('x-layout-tabs-body');
34266 this.panels.each(this.initPanelAsTab, this);
34269 initPanelAsTab : function(panel){
34270 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34271 this.config.closeOnTab && panel.isClosable());
34272 if(panel.tabTip !== undefined){
34273 ti.setTooltip(panel.tabTip);
34275 ti.on("activate", function(){
34276 this.setActivePanel(panel);
34278 if(this.config.closeOnTab){
34279 ti.on("beforeclose", function(t, e){
34281 this.remove(panel);
34287 updatePanelTitle : function(panel, title){
34288 if(this.activePanel == panel){
34289 this.updateTitle(title);
34292 var ti = this.tabs.getTab(panel.getEl().id);
34294 if(panel.tabTip !== undefined){
34295 ti.setTooltip(panel.tabTip);
34300 updateTitle : function(title){
34301 if(this.titleTextEl && !this.config.title){
34302 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
34306 setActivePanel : function(panel){
34307 panel = this.getPanel(panel);
34308 if(this.activePanel && this.activePanel != panel){
34309 this.activePanel.setActiveState(false);
34311 this.activePanel = panel;
34312 panel.setActiveState(true);
34313 if(this.panelSize){
34314 panel.setSize(this.panelSize.width, this.panelSize.height);
34317 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34319 this.updateTitle(panel.getTitle());
34321 this.fireEvent("invalidated", this);
34323 this.fireEvent("panelactivated", this, panel);
34327 * Shows the specified panel.
34328 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34329 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34331 showPanel : function(panel)
34333 panel = this.getPanel(panel);
34336 var tab = this.tabs.getTab(panel.getEl().id);
34337 if(tab.isHidden()){
34338 this.tabs.unhideTab(tab.id);
34342 this.setActivePanel(panel);
34349 * Get the active panel for this region.
34350 * @return {Roo.ContentPanel} The active panel or null
34352 getActivePanel : function(){
34353 return this.activePanel;
34356 validateVisibility : function(){
34357 if(this.panels.getCount() < 1){
34358 this.updateTitle(" ");
34359 this.closeBtn.hide();
34362 if(!this.isVisible()){
34369 * Adds the passed ContentPanel(s) to this region.
34370 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34371 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34373 add : function(panel){
34374 if(arguments.length > 1){
34375 for(var i = 0, len = arguments.length; i < len; i++) {
34376 this.add(arguments[i]);
34380 if(this.hasPanel(panel)){
34381 this.showPanel(panel);
34384 panel.setRegion(this);
34385 this.panels.add(panel);
34386 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34387 this.bodyEl.dom.appendChild(panel.getEl().dom);
34388 if(panel.background !== true){
34389 this.setActivePanel(panel);
34391 this.fireEvent("paneladded", this, panel);
34397 this.initPanelAsTab(panel);
34399 if(panel.background !== true){
34400 this.tabs.activate(panel.getEl().id);
34402 this.fireEvent("paneladded", this, panel);
34407 * Hides the tab for the specified panel.
34408 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34410 hidePanel : function(panel){
34411 if(this.tabs && (panel = this.getPanel(panel))){
34412 this.tabs.hideTab(panel.getEl().id);
34417 * Unhides the tab for a previously hidden panel.
34418 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34420 unhidePanel : function(panel){
34421 if(this.tabs && (panel = this.getPanel(panel))){
34422 this.tabs.unhideTab(panel.getEl().id);
34426 clearPanels : function(){
34427 while(this.panels.getCount() > 0){
34428 this.remove(this.panels.first());
34433 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34434 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34435 * @param {Boolean} preservePanel Overrides the config preservePanel option
34436 * @return {Roo.ContentPanel} The panel that was removed
34438 remove : function(panel, preservePanel){
34439 panel = this.getPanel(panel);
34444 this.fireEvent("beforeremove", this, panel, e);
34445 if(e.cancel === true){
34448 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34449 var panelId = panel.getId();
34450 this.panels.removeKey(panelId);
34452 document.body.appendChild(panel.getEl().dom);
34455 this.tabs.removeTab(panel.getEl().id);
34456 }else if (!preservePanel){
34457 this.bodyEl.dom.removeChild(panel.getEl().dom);
34459 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34460 var p = this.panels.first();
34461 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34462 tempEl.appendChild(p.getEl().dom);
34463 this.bodyEl.update("");
34464 this.bodyEl.dom.appendChild(p.getEl().dom);
34466 this.updateTitle(p.getTitle());
34468 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34469 this.setActivePanel(p);
34471 panel.setRegion(null);
34472 if(this.activePanel == panel){
34473 this.activePanel = null;
34475 if(this.config.autoDestroy !== false && preservePanel !== true){
34476 try{panel.destroy();}catch(e){}
34478 this.fireEvent("panelremoved", this, panel);
34483 * Returns the TabPanel component used by this region
34484 * @return {Roo.TabPanel}
34486 getTabs : function(){
34490 createTool : function(parentEl, className){
34491 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34492 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
34493 btn.addClassOnOver("x-layout-tools-button-over");
34498 * Ext JS Library 1.1.1
34499 * Copyright(c) 2006-2007, Ext JS, LLC.
34501 * Originally Released Under LGPL - original licence link has changed is not relivant.
34504 * <script type="text/javascript">
34510 * @class Roo.SplitLayoutRegion
34511 * @extends Roo.LayoutRegion
34512 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34514 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34515 this.cursor = cursor;
34516 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34519 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34520 splitTip : "Drag to resize.",
34521 collapsibleSplitTip : "Drag to resize. Double click to hide.",
34522 useSplitTips : false,
34524 applyConfig : function(config){
34525 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34528 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
34529 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
34530 /** The SplitBar for this region
34531 * @type Roo.SplitBar */
34532 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34533 this.split.on("moved", this.onSplitMove, this);
34534 this.split.useShim = config.useShim === true;
34535 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34536 if(this.useSplitTips){
34537 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34539 if(config.collapsible){
34540 this.split.el.on("dblclick", this.collapse, this);
34543 if(typeof config.minSize != "undefined"){
34544 this.split.minSize = config.minSize;
34546 if(typeof config.maxSize != "undefined"){
34547 this.split.maxSize = config.maxSize;
34549 if(config.hideWhenEmpty || config.hidden || config.collapsed){
34550 this.hideSplitter();
34555 getHMaxSize : function(){
34556 var cmax = this.config.maxSize || 10000;
34557 var center = this.mgr.getRegion("center");
34558 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34561 getVMaxSize : function(){
34562 var cmax = this.config.maxSize || 10000;
34563 var center = this.mgr.getRegion("center");
34564 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34567 onSplitMove : function(split, newSize){
34568 this.fireEvent("resized", this, newSize);
34572 * Returns the {@link Roo.SplitBar} for this region.
34573 * @return {Roo.SplitBar}
34575 getSplitBar : function(){
34580 this.hideSplitter();
34581 Roo.SplitLayoutRegion.superclass.hide.call(this);
34584 hideSplitter : function(){
34586 this.split.el.setLocation(-2000,-2000);
34587 this.split.el.hide();
34593 this.split.el.show();
34595 Roo.SplitLayoutRegion.superclass.show.call(this);
34598 beforeSlide: function(){
34599 if(Roo.isGecko){// firefox overflow auto bug workaround
34600 this.bodyEl.clip();
34602 this.tabs.bodyEl.clip();
34604 if(this.activePanel){
34605 this.activePanel.getEl().clip();
34607 if(this.activePanel.beforeSlide){
34608 this.activePanel.beforeSlide();
34614 afterSlide : function(){
34615 if(Roo.isGecko){// firefox overflow auto bug workaround
34616 this.bodyEl.unclip();
34618 this.tabs.bodyEl.unclip();
34620 if(this.activePanel){
34621 this.activePanel.getEl().unclip();
34622 if(this.activePanel.afterSlide){
34623 this.activePanel.afterSlide();
34629 initAutoHide : function(){
34630 if(this.autoHide !== false){
34631 if(!this.autoHideHd){
34632 var st = new Roo.util.DelayedTask(this.slideIn, this);
34633 this.autoHideHd = {
34634 "mouseout": function(e){
34635 if(!e.within(this.el, true)){
34639 "mouseover" : function(e){
34645 this.el.on(this.autoHideHd);
34649 clearAutoHide : function(){
34650 if(this.autoHide !== false){
34651 this.el.un("mouseout", this.autoHideHd.mouseout);
34652 this.el.un("mouseover", this.autoHideHd.mouseover);
34656 clearMonitor : function(){
34657 Roo.get(document).un("click", this.slideInIf, this);
34660 // these names are backwards but not changed for compat
34661 slideOut : function(){
34662 if(this.isSlid || this.el.hasActiveFx()){
34665 this.isSlid = true;
34666 if(this.collapseBtn){
34667 this.collapseBtn.hide();
34669 this.closeBtnState = this.closeBtn.getStyle('display');
34670 this.closeBtn.hide();
34672 this.stickBtn.show();
34675 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34676 this.beforeSlide();
34677 this.el.setStyle("z-index", 10001);
34678 this.el.slideIn(this.getSlideAnchor(), {
34679 callback: function(){
34681 this.initAutoHide();
34682 Roo.get(document).on("click", this.slideInIf, this);
34683 this.fireEvent("slideshow", this);
34690 afterSlideIn : function(){
34691 this.clearAutoHide();
34692 this.isSlid = false;
34693 this.clearMonitor();
34694 this.el.setStyle("z-index", "");
34695 if(this.collapseBtn){
34696 this.collapseBtn.show();
34698 this.closeBtn.setStyle('display', this.closeBtnState);
34700 this.stickBtn.hide();
34702 this.fireEvent("slidehide", this);
34705 slideIn : function(cb){
34706 if(!this.isSlid || this.el.hasActiveFx()){
34710 this.isSlid = false;
34711 this.beforeSlide();
34712 this.el.slideOut(this.getSlideAnchor(), {
34713 callback: function(){
34714 this.el.setLeftTop(-10000, -10000);
34716 this.afterSlideIn();
34724 slideInIf : function(e){
34725 if(!e.within(this.el)){
34730 animateCollapse : function(){
34731 this.beforeSlide();
34732 this.el.setStyle("z-index", 20000);
34733 var anchor = this.getSlideAnchor();
34734 this.el.slideOut(anchor, {
34735 callback : function(){
34736 this.el.setStyle("z-index", "");
34737 this.collapsedEl.slideIn(anchor, {duration:.3});
34739 this.el.setLocation(-10000,-10000);
34741 this.fireEvent("collapsed", this);
34748 animateExpand : function(){
34749 this.beforeSlide();
34750 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34751 this.el.setStyle("z-index", 20000);
34752 this.collapsedEl.hide({
34755 this.el.slideIn(this.getSlideAnchor(), {
34756 callback : function(){
34757 this.el.setStyle("z-index", "");
34760 this.split.el.show();
34762 this.fireEvent("invalidated", this);
34763 this.fireEvent("expanded", this);
34791 getAnchor : function(){
34792 return this.anchors[this.position];
34795 getCollapseAnchor : function(){
34796 return this.canchors[this.position];
34799 getSlideAnchor : function(){
34800 return this.sanchors[this.position];
34803 getAlignAdj : function(){
34804 var cm = this.cmargins;
34805 switch(this.position){
34821 getExpandAdj : function(){
34822 var c = this.collapsedEl, cm = this.cmargins;
34823 switch(this.position){
34825 return [-(cm.right+c.getWidth()+cm.left), 0];
34828 return [cm.right+c.getWidth()+cm.left, 0];
34831 return [0, -(cm.top+cm.bottom+c.getHeight())];
34834 return [0, cm.top+cm.bottom+c.getHeight()];
34840 * Ext JS Library 1.1.1
34841 * Copyright(c) 2006-2007, Ext JS, LLC.
34843 * Originally Released Under LGPL - original licence link has changed is not relivant.
34846 * <script type="text/javascript">
34849 * These classes are private internal classes
34851 Roo.CenterLayoutRegion = function(mgr, config){
34852 Roo.LayoutRegion.call(this, mgr, config, "center");
34853 this.visible = true;
34854 this.minWidth = config.minWidth || 20;
34855 this.minHeight = config.minHeight || 20;
34858 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34860 // center panel can't be hidden
34864 // center panel can't be hidden
34867 getMinWidth: function(){
34868 return this.minWidth;
34871 getMinHeight: function(){
34872 return this.minHeight;
34877 Roo.NorthLayoutRegion = function(mgr, config){
34878 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34880 this.split.placement = Roo.SplitBar.TOP;
34881 this.split.orientation = Roo.SplitBar.VERTICAL;
34882 this.split.el.addClass("x-layout-split-v");
34884 var size = config.initialSize || config.height;
34885 if(typeof size != "undefined"){
34886 this.el.setHeight(size);
34889 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34890 orientation: Roo.SplitBar.VERTICAL,
34891 getBox : function(){
34892 if(this.collapsed){
34893 return this.collapsedEl.getBox();
34895 var box = this.el.getBox();
34897 box.height += this.split.el.getHeight();
34902 updateBox : function(box){
34903 if(this.split && !this.collapsed){
34904 box.height -= this.split.el.getHeight();
34905 this.split.el.setLeft(box.x);
34906 this.split.el.setTop(box.y+box.height);
34907 this.split.el.setWidth(box.width);
34909 if(this.collapsed){
34910 this.updateBody(box.width, null);
34912 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34916 Roo.SouthLayoutRegion = function(mgr, config){
34917 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34919 this.split.placement = Roo.SplitBar.BOTTOM;
34920 this.split.orientation = Roo.SplitBar.VERTICAL;
34921 this.split.el.addClass("x-layout-split-v");
34923 var size = config.initialSize || config.height;
34924 if(typeof size != "undefined"){
34925 this.el.setHeight(size);
34928 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34929 orientation: Roo.SplitBar.VERTICAL,
34930 getBox : function(){
34931 if(this.collapsed){
34932 return this.collapsedEl.getBox();
34934 var box = this.el.getBox();
34936 var sh = this.split.el.getHeight();
34943 updateBox : function(box){
34944 if(this.split && !this.collapsed){
34945 var sh = this.split.el.getHeight();
34948 this.split.el.setLeft(box.x);
34949 this.split.el.setTop(box.y-sh);
34950 this.split.el.setWidth(box.width);
34952 if(this.collapsed){
34953 this.updateBody(box.width, null);
34955 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34959 Roo.EastLayoutRegion = function(mgr, config){
34960 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34962 this.split.placement = Roo.SplitBar.RIGHT;
34963 this.split.orientation = Roo.SplitBar.HORIZONTAL;
34964 this.split.el.addClass("x-layout-split-h");
34966 var size = config.initialSize || config.width;
34967 if(typeof size != "undefined"){
34968 this.el.setWidth(size);
34971 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34972 orientation: Roo.SplitBar.HORIZONTAL,
34973 getBox : function(){
34974 if(this.collapsed){
34975 return this.collapsedEl.getBox();
34977 var box = this.el.getBox();
34979 var sw = this.split.el.getWidth();
34986 updateBox : function(box){
34987 if(this.split && !this.collapsed){
34988 var sw = this.split.el.getWidth();
34990 this.split.el.setLeft(box.x);
34991 this.split.el.setTop(box.y);
34992 this.split.el.setHeight(box.height);
34995 if(this.collapsed){
34996 this.updateBody(null, box.height);
34998 Roo.LayoutRegion.prototype.updateBox.call(this, box);
35002 Roo.WestLayoutRegion = function(mgr, config){
35003 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35005 this.split.placement = Roo.SplitBar.LEFT;
35006 this.split.orientation = Roo.SplitBar.HORIZONTAL;
35007 this.split.el.addClass("x-layout-split-h");
35009 var size = config.initialSize || config.width;
35010 if(typeof size != "undefined"){
35011 this.el.setWidth(size);
35014 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35015 orientation: Roo.SplitBar.HORIZONTAL,
35016 getBox : function(){
35017 if(this.collapsed){
35018 return this.collapsedEl.getBox();
35020 var box = this.el.getBox();
35022 box.width += this.split.el.getWidth();
35027 updateBox : function(box){
35028 if(this.split && !this.collapsed){
35029 var sw = this.split.el.getWidth();
35031 this.split.el.setLeft(box.x+box.width);
35032 this.split.el.setTop(box.y);
35033 this.split.el.setHeight(box.height);
35035 if(this.collapsed){
35036 this.updateBody(null, box.height);
35038 Roo.LayoutRegion.prototype.updateBox.call(this, box);
35043 * Ext JS Library 1.1.1
35044 * Copyright(c) 2006-2007, Ext JS, LLC.
35046 * Originally Released Under LGPL - original licence link has changed is not relivant.
35049 * <script type="text/javascript">
35054 * Private internal class for reading and applying state
35056 Roo.LayoutStateManager = function(layout){
35057 // default empty state
35066 Roo.LayoutStateManager.prototype = {
35067 init : function(layout, provider){
35068 this.provider = provider;
35069 var state = provider.get(layout.id+"-layout-state");
35071 var wasUpdating = layout.isUpdating();
35073 layout.beginUpdate();
35075 for(var key in state){
35076 if(typeof state[key] != "function"){
35077 var rstate = state[key];
35078 var r = layout.getRegion(key);
35081 r.resizeTo(rstate.size);
35083 if(rstate.collapsed == true){
35086 r.expand(null, true);
35092 layout.endUpdate();
35094 this.state = state;
35096 this.layout = layout;
35097 layout.on("regionresized", this.onRegionResized, this);
35098 layout.on("regioncollapsed", this.onRegionCollapsed, this);
35099 layout.on("regionexpanded", this.onRegionExpanded, this);
35102 storeState : function(){
35103 this.provider.set(this.layout.id+"-layout-state", this.state);
35106 onRegionResized : function(region, newSize){
35107 this.state[region.getPosition()].size = newSize;
35111 onRegionCollapsed : function(region){
35112 this.state[region.getPosition()].collapsed = true;
35116 onRegionExpanded : function(region){
35117 this.state[region.getPosition()].collapsed = false;
35122 * Ext JS Library 1.1.1
35123 * Copyright(c) 2006-2007, Ext JS, LLC.
35125 * Originally Released Under LGPL - original licence link has changed is not relivant.
35128 * <script type="text/javascript">
35131 * @class Roo.ContentPanel
35132 * @extends Roo.util.Observable
35133 * A basic ContentPanel element.
35134 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
35135 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
35136 * @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
35137 * @cfg {Boolean} closable True if the panel can be closed/removed
35138 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
35139 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35140 * @cfg {Toolbar} toolbar A toolbar for this panel
35141 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
35142 * @cfg {String} title The title for this panel
35143 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35144 * @cfg {String} url Calls {@link #setUrl} with this value
35145 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35146 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
35147 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
35148 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
35151 * Create a new ContentPanel.
35152 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35153 * @param {String/Object} config A string to set only the title or a config object
35154 * @param {String} content (optional) Set the HTML content for this panel
35155 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35157 Roo.ContentPanel = function(el, config, content){
35161 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35165 if (config && config.parentLayout) {
35166 el = config.parentLayout.el.createChild();
35169 if(el.autoCreate){ // xtype is available if this is called from factory
35173 this.el = Roo.get(el);
35174 if(!this.el && config && config.autoCreate){
35175 if(typeof config.autoCreate == "object"){
35176 if(!config.autoCreate.id){
35177 config.autoCreate.id = config.id||el;
35179 this.el = Roo.DomHelper.append(document.body,
35180 config.autoCreate, true);
35182 this.el = Roo.DomHelper.append(document.body,
35183 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35186 this.closable = false;
35187 this.loaded = false;
35188 this.active = false;
35189 if(typeof config == "string"){
35190 this.title = config;
35192 Roo.apply(this, config);
35195 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35196 this.wrapEl = this.el.wrap();
35197 this.toolbar.container = this.el.insertSibling(false, 'before');
35198 this.toolbar = new Roo.Toolbar(this.toolbar);
35201 // xtype created footer. - not sure if will work as we normally have to render first..
35202 if (this.footer && !this.footer.el && this.footer.xtype) {
35203 if (!this.wrapEl) {
35204 this.wrapEl = this.el.wrap();
35207 this.footer.container = this.wrapEl.createChild();
35209 this.footer = Roo.factory(this.footer, Roo);
35214 this.resizeEl = Roo.get(this.resizeEl, true);
35216 this.resizeEl = this.el;
35218 // handle view.xtype
35226 * Fires when this panel is activated.
35227 * @param {Roo.ContentPanel} this
35231 * @event deactivate
35232 * Fires when this panel is activated.
35233 * @param {Roo.ContentPanel} this
35235 "deactivate" : true,
35239 * Fires when this panel is resized if fitToFrame is true.
35240 * @param {Roo.ContentPanel} this
35241 * @param {Number} width The width after any component adjustments
35242 * @param {Number} height The height after any component adjustments
35248 * Fires when this tab is created
35249 * @param {Roo.ContentPanel} this
35260 if(this.autoScroll){
35261 this.resizeEl.setStyle("overflow", "auto");
35263 // fix randome scrolling
35264 this.el.on('scroll', function() {
35265 Roo.log('fix random scolling');
35266 this.scrollTo('top',0);
35269 content = content || this.content;
35271 this.setContent(content);
35273 if(config && config.url){
35274 this.setUrl(this.url, this.params, this.loadOnce);
35279 Roo.ContentPanel.superclass.constructor.call(this);
35281 if (this.view && typeof(this.view.xtype) != 'undefined') {
35282 this.view.el = this.el.appendChild(document.createElement("div"));
35283 this.view = Roo.factory(this.view);
35284 this.view.render && this.view.render(false, '');
35288 this.fireEvent('render', this);
35291 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35293 setRegion : function(region){
35294 this.region = region;
35296 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35298 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35303 * Returns the toolbar for this Panel if one was configured.
35304 * @return {Roo.Toolbar}
35306 getToolbar : function(){
35307 return this.toolbar;
35310 setActiveState : function(active){
35311 this.active = active;
35313 this.fireEvent("deactivate", this);
35315 this.fireEvent("activate", this);
35319 * Updates this panel's element
35320 * @param {String} content The new content
35321 * @param {Boolean} loadScripts (optional) true to look for and process scripts
35323 setContent : function(content, loadScripts){
35324 this.el.update(content, loadScripts);
35327 ignoreResize : function(w, h){
35328 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35331 this.lastSize = {width: w, height: h};
35336 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35337 * @return {Roo.UpdateManager} The UpdateManager
35339 getUpdateManager : function(){
35340 return this.el.getUpdateManager();
35343 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35344 * @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:
35347 url: "your-url.php",
35348 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35349 callback: yourFunction,
35350 scope: yourObject, //(optional scope)
35353 text: "Loading...",
35358 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35359 * 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.
35360 * @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}
35361 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35362 * @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.
35363 * @return {Roo.ContentPanel} this
35366 var um = this.el.getUpdateManager();
35367 um.update.apply(um, arguments);
35373 * 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.
35374 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35375 * @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)
35376 * @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)
35377 * @return {Roo.UpdateManager} The UpdateManager
35379 setUrl : function(url, params, loadOnce){
35380 if(this.refreshDelegate){
35381 this.removeListener("activate", this.refreshDelegate);
35383 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35384 this.on("activate", this.refreshDelegate);
35385 return this.el.getUpdateManager();
35388 _handleRefresh : function(url, params, loadOnce){
35389 if(!loadOnce || !this.loaded){
35390 var updater = this.el.getUpdateManager();
35391 updater.update(url, params, this._setLoaded.createDelegate(this));
35395 _setLoaded : function(){
35396 this.loaded = true;
35400 * Returns this panel's id
35403 getId : function(){
35408 * Returns this panel's element - used by regiosn to add.
35409 * @return {Roo.Element}
35411 getEl : function(){
35412 return this.wrapEl || this.el;
35415 adjustForComponents : function(width, height)
35417 //Roo.log('adjustForComponents ');
35418 if(this.resizeEl != this.el){
35419 width -= this.el.getFrameWidth('lr');
35420 height -= this.el.getFrameWidth('tb');
35423 var te = this.toolbar.getEl();
35424 height -= te.getHeight();
35425 te.setWidth(width);
35428 var te = this.footer.getEl();
35429 Roo.log("footer:" + te.getHeight());
35431 height -= te.getHeight();
35432 te.setWidth(width);
35436 if(this.adjustments){
35437 width += this.adjustments[0];
35438 height += this.adjustments[1];
35440 return {"width": width, "height": height};
35443 setSize : function(width, height){
35444 if(this.fitToFrame && !this.ignoreResize(width, height)){
35445 if(this.fitContainer && this.resizeEl != this.el){
35446 this.el.setSize(width, height);
35448 var size = this.adjustForComponents(width, height);
35449 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35450 this.fireEvent('resize', this, size.width, size.height);
35455 * Returns this panel's title
35458 getTitle : function(){
35463 * Set this panel's title
35464 * @param {String} title
35466 setTitle : function(title){
35467 this.title = title;
35469 this.region.updatePanelTitle(this, title);
35474 * Returns true is this panel was configured to be closable
35475 * @return {Boolean}
35477 isClosable : function(){
35478 return this.closable;
35481 beforeSlide : function(){
35483 this.resizeEl.clip();
35486 afterSlide : function(){
35488 this.resizeEl.unclip();
35492 * Force a content refresh from the URL specified in the {@link #setUrl} method.
35493 * Will fail silently if the {@link #setUrl} method has not been called.
35494 * This does not activate the panel, just updates its content.
35496 refresh : function(){
35497 if(this.refreshDelegate){
35498 this.loaded = false;
35499 this.refreshDelegate();
35504 * Destroys this panel
35506 destroy : function(){
35507 this.el.removeAllListeners();
35508 var tempEl = document.createElement("span");
35509 tempEl.appendChild(this.el.dom);
35510 tempEl.innerHTML = "";
35516 * form - if the content panel contains a form - this is a reference to it.
35517 * @type {Roo.form.Form}
35521 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35522 * This contains a reference to it.
35528 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35538 * @param {Object} cfg Xtype definition of item to add.
35541 addxtype : function(cfg) {
35543 if (cfg.xtype.match(/^Form$/)) {
35546 //if (this.footer) {
35547 // el = this.footer.container.insertSibling(false, 'before');
35549 el = this.el.createChild();
35552 this.form = new Roo.form.Form(cfg);
35555 if ( this.form.allItems.length) {
35556 this.form.render(el.dom);
35560 // should only have one of theses..
35561 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35562 // views.. should not be just added - used named prop 'view''
35564 cfg.el = this.el.appendChild(document.createElement("div"));
35567 var ret = new Roo.factory(cfg);
35569 ret.render && ret.render(false, ''); // render blank..
35578 * @class Roo.GridPanel
35579 * @extends Roo.ContentPanel
35581 * Create a new GridPanel.
35582 * @param {Roo.grid.Grid} grid The grid for this panel
35583 * @param {String/Object} config A string to set only the panel's title, or a config object
35585 Roo.GridPanel = function(grid, config){
35588 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35589 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35591 this.wrapper.dom.appendChild(grid.getGridEl().dom);
35593 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35596 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35598 // xtype created footer. - not sure if will work as we normally have to render first..
35599 if (this.footer && !this.footer.el && this.footer.xtype) {
35601 this.footer.container = this.grid.getView().getFooterPanel(true);
35602 this.footer.dataSource = this.grid.dataSource;
35603 this.footer = Roo.factory(this.footer, Roo);
35607 grid.monitorWindowResize = false; // turn off autosizing
35608 grid.autoHeight = false;
35609 grid.autoWidth = false;
35611 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35614 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35615 getId : function(){
35616 return this.grid.id;
35620 * Returns the grid for this panel
35621 * @return {Roo.grid.Grid}
35623 getGrid : function(){
35627 setSize : function(width, height){
35628 if(!this.ignoreResize(width, height)){
35629 var grid = this.grid;
35630 var size = this.adjustForComponents(width, height);
35631 grid.getGridEl().setSize(size.width, size.height);
35636 beforeSlide : function(){
35637 this.grid.getView().scroller.clip();
35640 afterSlide : function(){
35641 this.grid.getView().scroller.unclip();
35644 destroy : function(){
35645 this.grid.destroy();
35647 Roo.GridPanel.superclass.destroy.call(this);
35653 * @class Roo.NestedLayoutPanel
35654 * @extends Roo.ContentPanel
35656 * Create a new NestedLayoutPanel.
35659 * @param {Roo.BorderLayout} layout The layout for this panel
35660 * @param {String/Object} config A string to set only the title or a config object
35662 Roo.NestedLayoutPanel = function(layout, config)
35664 // construct with only one argument..
35665 /* FIXME - implement nicer consturctors
35666 if (layout.layout) {
35668 layout = config.layout;
35669 delete config.layout;
35671 if (layout.xtype && !layout.getEl) {
35672 // then layout needs constructing..
35673 layout = Roo.factory(layout, Roo);
35678 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35680 layout.monitorWindowResize = false; // turn off autosizing
35681 this.layout = layout;
35682 this.layout.getEl().addClass("x-layout-nested-layout");
35689 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35691 setSize : function(width, height){
35692 if(!this.ignoreResize(width, height)){
35693 var size = this.adjustForComponents(width, height);
35694 var el = this.layout.getEl();
35695 el.setSize(size.width, size.height);
35696 var touch = el.dom.offsetWidth;
35697 this.layout.layout();
35698 // ie requires a double layout on the first pass
35699 if(Roo.isIE && !this.initialized){
35700 this.initialized = true;
35701 this.layout.layout();
35706 // activate all subpanels if not currently active..
35708 setActiveState : function(active){
35709 this.active = active;
35711 this.fireEvent("deactivate", this);
35715 this.fireEvent("activate", this);
35716 // not sure if this should happen before or after..
35717 if (!this.layout) {
35718 return; // should not happen..
35721 for (var r in this.layout.regions) {
35722 reg = this.layout.getRegion(r);
35723 if (reg.getActivePanel()) {
35724 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35725 reg.setActivePanel(reg.getActivePanel());
35728 if (!reg.panels.length) {
35731 reg.showPanel(reg.getPanel(0));
35740 * Returns the nested BorderLayout for this panel
35741 * @return {Roo.BorderLayout}
35743 getLayout : function(){
35744 return this.layout;
35748 * Adds a xtype elements to the layout of the nested panel
35752 xtype : 'ContentPanel',
35759 xtype : 'NestedLayoutPanel',
35765 items : [ ... list of content panels or nested layout panels.. ]
35769 * @param {Object} cfg Xtype definition of item to add.
35771 addxtype : function(cfg) {
35772 return this.layout.addxtype(cfg);
35777 Roo.ScrollPanel = function(el, config, content){
35778 config = config || {};
35779 config.fitToFrame = true;
35780 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35782 this.el.dom.style.overflow = "hidden";
35783 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35784 this.el.removeClass("x-layout-inactive-content");
35785 this.el.on("mousewheel", this.onWheel, this);
35787 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
35788 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
35789 up.unselectable(); down.unselectable();
35790 up.on("click", this.scrollUp, this);
35791 down.on("click", this.scrollDown, this);
35792 up.addClassOnOver("x-scroller-btn-over");
35793 down.addClassOnOver("x-scroller-btn-over");
35794 up.addClassOnClick("x-scroller-btn-click");
35795 down.addClassOnClick("x-scroller-btn-click");
35796 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35798 this.resizeEl = this.el;
35799 this.el = wrap; this.up = up; this.down = down;
35802 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35804 wheelIncrement : 5,
35805 scrollUp : function(){
35806 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35809 scrollDown : function(){
35810 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35813 afterScroll : function(){
35814 var el = this.resizeEl;
35815 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35816 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35817 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35820 setSize : function(){
35821 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35822 this.afterScroll();
35825 onWheel : function(e){
35826 var d = e.getWheelDelta();
35827 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35828 this.afterScroll();
35832 setContent : function(content, loadScripts){
35833 this.resizeEl.update(content, loadScripts);
35847 * @class Roo.TreePanel
35848 * @extends Roo.ContentPanel
35850 * Create a new TreePanel. - defaults to fit/scoll contents.
35851 * @param {String/Object} config A string to set only the panel's title, or a config object
35852 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35854 Roo.TreePanel = function(config){
35855 var el = config.el;
35856 var tree = config.tree;
35857 delete config.tree;
35858 delete config.el; // hopefull!
35860 // wrapper for IE7 strict & safari scroll issue
35862 var treeEl = el.createChild();
35863 config.resizeEl = treeEl;
35867 Roo.TreePanel.superclass.constructor.call(this, el, config);
35870 this.tree = new Roo.tree.TreePanel(treeEl , tree);
35871 //console.log(tree);
35872 this.on('activate', function()
35874 if (this.tree.rendered) {
35877 //console.log('render tree');
35878 this.tree.render();
35880 // this should not be needed.. - it's actually the 'el' that resizes?
35881 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35883 //this.on('resize', function (cp, w, h) {
35884 // this.tree.innerCt.setWidth(w);
35885 // this.tree.innerCt.setHeight(h);
35886 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
35893 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
35910 * Ext JS Library 1.1.1
35911 * Copyright(c) 2006-2007, Ext JS, LLC.
35913 * Originally Released Under LGPL - original licence link has changed is not relivant.
35916 * <script type="text/javascript">
35921 * @class Roo.ReaderLayout
35922 * @extends Roo.BorderLayout
35923 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
35924 * center region containing two nested regions (a top one for a list view and one for item preview below),
35925 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35926 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35927 * expedites the setup of the overall layout and regions for this common application style.
35930 var reader = new Roo.ReaderLayout();
35931 var CP = Roo.ContentPanel; // shortcut for adding
35933 reader.beginUpdate();
35934 reader.add("north", new CP("north", "North"));
35935 reader.add("west", new CP("west", {title: "West"}));
35936 reader.add("east", new CP("east", {title: "East"}));
35938 reader.regions.listView.add(new CP("listView", "List"));
35939 reader.regions.preview.add(new CP("preview", "Preview"));
35940 reader.endUpdate();
35943 * Create a new ReaderLayout
35944 * @param {Object} config Configuration options
35945 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35946 * document.body if omitted)
35948 Roo.ReaderLayout = function(config, renderTo){
35949 var c = config || {size:{}};
35950 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35951 north: c.north !== false ? Roo.apply({
35955 }, c.north) : false,
35956 west: c.west !== false ? Roo.apply({
35964 margins:{left:5,right:0,bottom:5,top:5},
35965 cmargins:{left:5,right:5,bottom:5,top:5}
35966 }, c.west) : false,
35967 east: c.east !== false ? Roo.apply({
35975 margins:{left:0,right:5,bottom:5,top:5},
35976 cmargins:{left:5,right:5,bottom:5,top:5}
35977 }, c.east) : false,
35978 center: Roo.apply({
35979 tabPosition: 'top',
35983 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35987 this.el.addClass('x-reader');
35989 this.beginUpdate();
35991 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35992 south: c.preview !== false ? Roo.apply({
35999 cmargins:{top:5,left:0, right:0, bottom:0}
36000 }, c.preview) : false,
36001 center: Roo.apply({
36007 this.add('center', new Roo.NestedLayoutPanel(inner,
36008 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36012 this.regions.preview = inner.getRegion('south');
36013 this.regions.listView = inner.getRegion('center');
36016 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36018 * Ext JS Library 1.1.1
36019 * Copyright(c) 2006-2007, Ext JS, LLC.
36021 * Originally Released Under LGPL - original licence link has changed is not relivant.
36024 * <script type="text/javascript">
36028 * @class Roo.grid.Grid
36029 * @extends Roo.util.Observable
36030 * This class represents the primary interface of a component based grid control.
36031 * <br><br>Usage:<pre><code>
36032 var grid = new Roo.grid.Grid("my-container-id", {
36035 selModel: mySelectionModel,
36036 autoSizeColumns: true,
36037 monitorWindowResize: false,
36038 trackMouseOver: true
36043 * <b>Common Problems:</b><br/>
36044 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36045 * element will correct this<br/>
36046 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36047 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36048 * are unpredictable.<br/>
36049 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36050 * grid to calculate dimensions/offsets.<br/>
36052 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36053 * The container MUST have some type of size defined for the grid to fill. The container will be
36054 * automatically set to position relative if it isn't already.
36055 * @param {Object} config A config object that sets properties on this grid.
36057 Roo.grid.Grid = function(container, config){
36058 // initialize the container
36059 this.container = Roo.get(container);
36060 this.container.update("");
36061 this.container.setStyle("overflow", "hidden");
36062 this.container.addClass('x-grid-container');
36064 this.id = this.container.id;
36066 Roo.apply(this, config);
36067 // check and correct shorthanded configs
36069 this.dataSource = this.ds;
36073 this.colModel = this.cm;
36077 this.selModel = this.sm;
36081 if (this.selModel) {
36082 this.selModel = Roo.factory(this.selModel, Roo.grid);
36083 this.sm = this.selModel;
36084 this.sm.xmodule = this.xmodule || false;
36086 if (typeof(this.colModel.config) == 'undefined') {
36087 this.colModel = new Roo.grid.ColumnModel(this.colModel);
36088 this.cm = this.colModel;
36089 this.cm.xmodule = this.xmodule || false;
36091 if (this.dataSource) {
36092 this.dataSource= Roo.factory(this.dataSource, Roo.data);
36093 this.ds = this.dataSource;
36094 this.ds.xmodule = this.xmodule || false;
36101 this.container.setWidth(this.width);
36105 this.container.setHeight(this.height);
36112 * The raw click event for the entire grid.
36113 * @param {Roo.EventObject} e
36118 * The raw dblclick event for the entire grid.
36119 * @param {Roo.EventObject} e
36123 * @event contextmenu
36124 * The raw contextmenu event for the entire grid.
36125 * @param {Roo.EventObject} e
36127 "contextmenu" : true,
36130 * The raw mousedown event for the entire grid.
36131 * @param {Roo.EventObject} e
36133 "mousedown" : true,
36136 * The raw mouseup event for the entire grid.
36137 * @param {Roo.EventObject} e
36142 * The raw mouseover event for the entire grid.
36143 * @param {Roo.EventObject} e
36145 "mouseover" : true,
36148 * The raw mouseout event for the entire grid.
36149 * @param {Roo.EventObject} e
36154 * The raw keypress event for the entire grid.
36155 * @param {Roo.EventObject} e
36160 * The raw keydown event for the entire grid.
36161 * @param {Roo.EventObject} e
36169 * Fires when a cell is clicked
36170 * @param {Grid} this
36171 * @param {Number} rowIndex
36172 * @param {Number} columnIndex
36173 * @param {Roo.EventObject} e
36175 "cellclick" : true,
36177 * @event celldblclick
36178 * Fires when a cell is double clicked
36179 * @param {Grid} this
36180 * @param {Number} rowIndex
36181 * @param {Number} columnIndex
36182 * @param {Roo.EventObject} e
36184 "celldblclick" : true,
36187 * Fires when a row is clicked
36188 * @param {Grid} this
36189 * @param {Number} rowIndex
36190 * @param {Roo.EventObject} e
36194 * @event rowdblclick
36195 * Fires when a row is double clicked
36196 * @param {Grid} this
36197 * @param {Number} rowIndex
36198 * @param {Roo.EventObject} e
36200 "rowdblclick" : true,
36202 * @event headerclick
36203 * Fires when a header is clicked
36204 * @param {Grid} this
36205 * @param {Number} columnIndex
36206 * @param {Roo.EventObject} e
36208 "headerclick" : true,
36210 * @event headerdblclick
36211 * Fires when a header cell is double clicked
36212 * @param {Grid} this
36213 * @param {Number} columnIndex
36214 * @param {Roo.EventObject} e
36216 "headerdblclick" : true,
36218 * @event rowcontextmenu
36219 * Fires when a row is right clicked
36220 * @param {Grid} this
36221 * @param {Number} rowIndex
36222 * @param {Roo.EventObject} e
36224 "rowcontextmenu" : true,
36226 * @event cellcontextmenu
36227 * Fires when a cell is right clicked
36228 * @param {Grid} this
36229 * @param {Number} rowIndex
36230 * @param {Number} cellIndex
36231 * @param {Roo.EventObject} e
36233 "cellcontextmenu" : true,
36235 * @event headercontextmenu
36236 * Fires when a header is right clicked
36237 * @param {Grid} this
36238 * @param {Number} columnIndex
36239 * @param {Roo.EventObject} e
36241 "headercontextmenu" : true,
36243 * @event bodyscroll
36244 * Fires when the body element is scrolled
36245 * @param {Number} scrollLeft
36246 * @param {Number} scrollTop
36248 "bodyscroll" : true,
36250 * @event columnresize
36251 * Fires when the user resizes a column
36252 * @param {Number} columnIndex
36253 * @param {Number} newSize
36255 "columnresize" : true,
36257 * @event columnmove
36258 * Fires when the user moves a column
36259 * @param {Number} oldIndex
36260 * @param {Number} newIndex
36262 "columnmove" : true,
36265 * Fires when row(s) start being dragged
36266 * @param {Grid} this
36267 * @param {Roo.GridDD} dd The drag drop object
36268 * @param {event} e The raw browser event
36270 "startdrag" : true,
36273 * Fires when a drag operation is complete
36274 * @param {Grid} this
36275 * @param {Roo.GridDD} dd The drag drop object
36276 * @param {event} e The raw browser event
36281 * Fires when dragged row(s) are dropped on a valid DD target
36282 * @param {Grid} this
36283 * @param {Roo.GridDD} dd The drag drop object
36284 * @param {String} targetId The target drag drop object
36285 * @param {event} e The raw browser event
36290 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36291 * @param {Grid} this
36292 * @param {Roo.GridDD} dd The drag drop object
36293 * @param {String} targetId The target drag drop object
36294 * @param {event} e The raw browser event
36299 * Fires when the dragged row(s) first cross another DD target while being dragged
36300 * @param {Grid} this
36301 * @param {Roo.GridDD} dd The drag drop object
36302 * @param {String} targetId The target drag drop object
36303 * @param {event} e The raw browser event
36305 "dragenter" : true,
36308 * Fires when the dragged row(s) leave another DD target while being dragged
36309 * @param {Grid} this
36310 * @param {Roo.GridDD} dd The drag drop object
36311 * @param {String} targetId The target drag drop object
36312 * @param {event} e The raw browser event
36317 * Fires when a row is rendered, so you can change add a style to it.
36318 * @param {GridView} gridview The grid view
36319 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
36325 * Fires when the grid is rendered
36326 * @param {Grid} grid
36331 Roo.grid.Grid.superclass.constructor.call(this);
36333 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36336 * @cfg {String} ddGroup - drag drop group.
36340 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36342 minColumnWidth : 25,
36345 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36346 * <b>on initial render.</b> It is more efficient to explicitly size the columns
36347 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
36349 autoSizeColumns : false,
36352 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36354 autoSizeHeaders : true,
36357 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36359 monitorWindowResize : true,
36362 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36363 * rows measured to get a columns size. Default is 0 (all rows).
36365 maxRowsToMeasure : 0,
36368 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36370 trackMouseOver : true,
36373 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
36377 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36379 enableDragDrop : false,
36382 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36384 enableColumnMove : true,
36387 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36389 enableColumnHide : true,
36392 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36394 enableRowHeightSync : false,
36397 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
36402 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36404 autoHeight : false,
36407 * @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.
36409 autoExpandColumn : false,
36412 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36415 autoExpandMin : 50,
36418 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36420 autoExpandMax : 1000,
36423 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36428 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36432 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36442 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36443 * of a fixed width. Default is false.
36446 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36449 * Called once after all setup has been completed and the grid is ready to be rendered.
36450 * @return {Roo.grid.Grid} this
36452 render : function()
36454 var c = this.container;
36455 // try to detect autoHeight/width mode
36456 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36457 this.autoHeight = true;
36459 var view = this.getView();
36462 c.on("click", this.onClick, this);
36463 c.on("dblclick", this.onDblClick, this);
36464 c.on("contextmenu", this.onContextMenu, this);
36465 c.on("keydown", this.onKeyDown, this);
36467 c.on("touchstart", this.onTouchStart, this);
36470 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36472 this.getSelectionModel().init(this);
36477 this.loadMask = new Roo.LoadMask(this.container,
36478 Roo.apply({store:this.dataSource}, this.loadMask));
36482 if (this.toolbar && this.toolbar.xtype) {
36483 this.toolbar.container = this.getView().getHeaderPanel(true);
36484 this.toolbar = new Roo.Toolbar(this.toolbar);
36486 if (this.footer && this.footer.xtype) {
36487 this.footer.dataSource = this.getDataSource();
36488 this.footer.container = this.getView().getFooterPanel(true);
36489 this.footer = Roo.factory(this.footer, Roo);
36491 if (this.dropTarget && this.dropTarget.xtype) {
36492 delete this.dropTarget.xtype;
36493 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36497 this.rendered = true;
36498 this.fireEvent('render', this);
36503 * Reconfigures the grid to use a different Store and Column Model.
36504 * The View will be bound to the new objects and refreshed.
36505 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36506 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36508 reconfigure : function(dataSource, colModel){
36510 this.loadMask.destroy();
36511 this.loadMask = new Roo.LoadMask(this.container,
36512 Roo.apply({store:dataSource}, this.loadMask));
36514 this.view.bind(dataSource, colModel);
36515 this.dataSource = dataSource;
36516 this.colModel = colModel;
36517 this.view.refresh(true);
36521 onKeyDown : function(e){
36522 this.fireEvent("keydown", e);
36526 * Destroy this grid.
36527 * @param {Boolean} removeEl True to remove the element
36529 destroy : function(removeEl, keepListeners){
36531 this.loadMask.destroy();
36533 var c = this.container;
36534 c.removeAllListeners();
36535 this.view.destroy();
36536 this.colModel.purgeListeners();
36537 if(!keepListeners){
36538 this.purgeListeners();
36541 if(removeEl === true){
36547 processEvent : function(name, e){
36548 // does this fire select???
36549 //Roo.log('grid:processEvent ' + name);
36551 if (name != 'touchstart' ) {
36552 this.fireEvent(name, e);
36555 var t = e.getTarget();
36557 var header = v.findHeaderIndex(t);
36558 if(header !== false){
36559 var ename = name == 'touchstart' ? 'click' : name;
36561 this.fireEvent("header" + ename, this, header, e);
36563 var row = v.findRowIndex(t);
36564 var cell = v.findCellIndex(t);
36565 if (name == 'touchstart') {
36566 // first touch is always a click.
36567 // hopefull this happens after selection is updated.?
36570 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36571 var cs = this.selModel.getSelectedCell();
36572 if (row == cs[0] && cell == cs[1]){
36576 if (typeof(this.selModel.getSelections) != 'undefined') {
36577 var cs = this.selModel.getSelections();
36578 var ds = this.dataSource;
36579 if (cs.length == 1 && ds.getAt(row) == cs[0]){
36590 this.fireEvent("row" + name, this, row, e);
36591 if(cell !== false){
36592 this.fireEvent("cell" + name, this, row, cell, e);
36599 onClick : function(e){
36600 this.processEvent("click", e);
36603 onTouchStart : function(e){
36604 this.processEvent("touchstart", e);
36608 onContextMenu : function(e, t){
36609 this.processEvent("contextmenu", e);
36613 onDblClick : function(e){
36614 this.processEvent("dblclick", e);
36618 walkCells : function(row, col, step, fn, scope){
36619 var cm = this.colModel, clen = cm.getColumnCount();
36620 var ds = this.dataSource, rlen = ds.getCount(), first = true;
36632 if(fn.call(scope || this, row, col, cm) === true){
36650 if(fn.call(scope || this, row, col, cm) === true){
36662 getSelections : function(){
36663 return this.selModel.getSelections();
36667 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36668 * but if manual update is required this method will initiate it.
36670 autoSize : function(){
36672 this.view.layout();
36673 if(this.view.adjustForScroll){
36674 this.view.adjustForScroll();
36680 * Returns the grid's underlying element.
36681 * @return {Element} The element
36683 getGridEl : function(){
36684 return this.container;
36687 // private for compatibility, overridden by editor grid
36688 stopEditing : function(){},
36691 * Returns the grid's SelectionModel.
36692 * @return {SelectionModel}
36694 getSelectionModel : function(){
36695 if(!this.selModel){
36696 this.selModel = new Roo.grid.RowSelectionModel();
36698 return this.selModel;
36702 * Returns the grid's DataSource.
36703 * @return {DataSource}
36705 getDataSource : function(){
36706 return this.dataSource;
36710 * Returns the grid's ColumnModel.
36711 * @return {ColumnModel}
36713 getColumnModel : function(){
36714 return this.colModel;
36718 * Returns the grid's GridView object.
36719 * @return {GridView}
36721 getView : function(){
36723 this.view = new Roo.grid.GridView(this.viewConfig);
36728 * Called to get grid's drag proxy text, by default returns this.ddText.
36731 getDragDropText : function(){
36732 var count = this.selModel.getCount();
36733 return String.format(this.ddText, count, count == 1 ? '' : 's');
36737 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36738 * %0 is replaced with the number of selected rows.
36741 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36743 * Ext JS Library 1.1.1
36744 * Copyright(c) 2006-2007, Ext JS, LLC.
36746 * Originally Released Under LGPL - original licence link has changed is not relivant.
36749 * <script type="text/javascript">
36752 Roo.grid.AbstractGridView = function(){
36756 "beforerowremoved" : true,
36757 "beforerowsinserted" : true,
36758 "beforerefresh" : true,
36759 "rowremoved" : true,
36760 "rowsinserted" : true,
36761 "rowupdated" : true,
36764 Roo.grid.AbstractGridView.superclass.constructor.call(this);
36767 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36768 rowClass : "x-grid-row",
36769 cellClass : "x-grid-cell",
36770 tdClass : "x-grid-td",
36771 hdClass : "x-grid-hd",
36772 splitClass : "x-grid-hd-split",
36774 init: function(grid){
36776 var cid = this.grid.getGridEl().id;
36777 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36778 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36779 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36780 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36783 getColumnRenderers : function(){
36784 var renderers = [];
36785 var cm = this.grid.colModel;
36786 var colCount = cm.getColumnCount();
36787 for(var i = 0; i < colCount; i++){
36788 renderers[i] = cm.getRenderer(i);
36793 getColumnIds : function(){
36795 var cm = this.grid.colModel;
36796 var colCount = cm.getColumnCount();
36797 for(var i = 0; i < colCount; i++){
36798 ids[i] = cm.getColumnId(i);
36803 getDataIndexes : function(){
36804 if(!this.indexMap){
36805 this.indexMap = this.buildIndexMap();
36807 return this.indexMap.colToData;
36810 getColumnIndexByDataIndex : function(dataIndex){
36811 if(!this.indexMap){
36812 this.indexMap = this.buildIndexMap();
36814 return this.indexMap.dataToCol[dataIndex];
36818 * Set a css style for a column dynamically.
36819 * @param {Number} colIndex The index of the column
36820 * @param {String} name The css property name
36821 * @param {String} value The css value
36823 setCSSStyle : function(colIndex, name, value){
36824 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36825 Roo.util.CSS.updateRule(selector, name, value);
36828 generateRules : function(cm){
36829 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36830 Roo.util.CSS.removeStyleSheet(rulesId);
36831 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36832 var cid = cm.getColumnId(i);
36833 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36834 this.tdSelector, cid, " {\n}\n",
36835 this.hdSelector, cid, " {\n}\n",
36836 this.splitSelector, cid, " {\n}\n");
36838 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36842 * Ext JS Library 1.1.1
36843 * Copyright(c) 2006-2007, Ext JS, LLC.
36845 * Originally Released Under LGPL - original licence link has changed is not relivant.
36848 * <script type="text/javascript">
36852 // This is a support class used internally by the Grid components
36853 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36855 this.view = grid.getView();
36856 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36857 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36859 this.setHandleElId(Roo.id(hd));
36860 this.setOuterHandleElId(Roo.id(hd2));
36862 this.scroll = false;
36864 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36866 getDragData : function(e){
36867 var t = Roo.lib.Event.getTarget(e);
36868 var h = this.view.findHeaderCell(t);
36870 return {ddel: h.firstChild, header:h};
36875 onInitDrag : function(e){
36876 this.view.headersDisabled = true;
36877 var clone = this.dragData.ddel.cloneNode(true);
36878 clone.id = Roo.id();
36879 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36880 this.proxy.update(clone);
36884 afterValidDrop : function(){
36886 setTimeout(function(){
36887 v.headersDisabled = false;
36891 afterInvalidDrop : function(){
36893 setTimeout(function(){
36894 v.headersDisabled = false;
36900 * Ext JS Library 1.1.1
36901 * Copyright(c) 2006-2007, Ext JS, LLC.
36903 * Originally Released Under LGPL - original licence link has changed is not relivant.
36906 * <script type="text/javascript">
36909 // This is a support class used internally by the Grid components
36910 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36912 this.view = grid.getView();
36913 // split the proxies so they don't interfere with mouse events
36914 this.proxyTop = Roo.DomHelper.append(document.body, {
36915 cls:"col-move-top", html:" "
36917 this.proxyBottom = Roo.DomHelper.append(document.body, {
36918 cls:"col-move-bottom", html:" "
36920 this.proxyTop.hide = this.proxyBottom.hide = function(){
36921 this.setLeftTop(-100,-100);
36922 this.setStyle("visibility", "hidden");
36924 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36925 // temporarily disabled
36926 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36927 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36929 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36930 proxyOffsets : [-4, -9],
36931 fly: Roo.Element.fly,
36933 getTargetFromEvent : function(e){
36934 var t = Roo.lib.Event.getTarget(e);
36935 var cindex = this.view.findCellIndex(t);
36936 if(cindex !== false){
36937 return this.view.getHeaderCell(cindex);
36942 nextVisible : function(h){
36943 var v = this.view, cm = this.grid.colModel;
36946 if(!cm.isHidden(v.getCellIndex(h))){
36954 prevVisible : function(h){
36955 var v = this.view, cm = this.grid.colModel;
36958 if(!cm.isHidden(v.getCellIndex(h))){
36966 positionIndicator : function(h, n, e){
36967 var x = Roo.lib.Event.getPageX(e);
36968 var r = Roo.lib.Dom.getRegion(n.firstChild);
36969 var px, pt, py = r.top + this.proxyOffsets[1];
36970 if((r.right - x) <= (r.right-r.left)/2){
36971 px = r.right+this.view.borderWidth;
36977 var oldIndex = this.view.getCellIndex(h);
36978 var newIndex = this.view.getCellIndex(n);
36980 if(this.grid.colModel.isFixed(newIndex)){
36984 var locked = this.grid.colModel.isLocked(newIndex);
36989 if(oldIndex < newIndex){
36992 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36995 px += this.proxyOffsets[0];
36996 this.proxyTop.setLeftTop(px, py);
36997 this.proxyTop.show();
36998 if(!this.bottomOffset){
36999 this.bottomOffset = this.view.mainHd.getHeight();
37001 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37002 this.proxyBottom.show();
37006 onNodeEnter : function(n, dd, e, data){
37007 if(data.header != n){
37008 this.positionIndicator(data.header, n, e);
37012 onNodeOver : function(n, dd, e, data){
37013 var result = false;
37014 if(data.header != n){
37015 result = this.positionIndicator(data.header, n, e);
37018 this.proxyTop.hide();
37019 this.proxyBottom.hide();
37021 return result ? this.dropAllowed : this.dropNotAllowed;
37024 onNodeOut : function(n, dd, e, data){
37025 this.proxyTop.hide();
37026 this.proxyBottom.hide();
37029 onNodeDrop : function(n, dd, e, data){
37030 var h = data.header;
37032 var cm = this.grid.colModel;
37033 var x = Roo.lib.Event.getPageX(e);
37034 var r = Roo.lib.Dom.getRegion(n.firstChild);
37035 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37036 var oldIndex = this.view.getCellIndex(h);
37037 var newIndex = this.view.getCellIndex(n);
37038 var locked = cm.isLocked(newIndex);
37042 if(oldIndex < newIndex){
37045 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37048 cm.setLocked(oldIndex, locked, true);
37049 cm.moveColumn(oldIndex, newIndex);
37050 this.grid.fireEvent("columnmove", oldIndex, newIndex);
37058 * Ext JS Library 1.1.1
37059 * Copyright(c) 2006-2007, Ext JS, LLC.
37061 * Originally Released Under LGPL - original licence link has changed is not relivant.
37064 * <script type="text/javascript">
37068 * @class Roo.grid.GridView
37069 * @extends Roo.util.Observable
37072 * @param {Object} config
37074 Roo.grid.GridView = function(config){
37075 Roo.grid.GridView.superclass.constructor.call(this);
37078 Roo.apply(this, config);
37081 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37083 unselectable : 'unselectable="on"',
37084 unselectableCls : 'x-unselectable',
37087 rowClass : "x-grid-row",
37089 cellClass : "x-grid-col",
37091 tdClass : "x-grid-td",
37093 hdClass : "x-grid-hd",
37095 splitClass : "x-grid-split",
37097 sortClasses : ["sort-asc", "sort-desc"],
37099 enableMoveAnim : false,
37103 dh : Roo.DomHelper,
37105 fly : Roo.Element.fly,
37107 css : Roo.util.CSS,
37113 scrollIncrement : 22,
37115 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37117 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37119 bind : function(ds, cm){
37121 this.ds.un("load", this.onLoad, this);
37122 this.ds.un("datachanged", this.onDataChange, this);
37123 this.ds.un("add", this.onAdd, this);
37124 this.ds.un("remove", this.onRemove, this);
37125 this.ds.un("update", this.onUpdate, this);
37126 this.ds.un("clear", this.onClear, this);
37129 ds.on("load", this.onLoad, this);
37130 ds.on("datachanged", this.onDataChange, this);
37131 ds.on("add", this.onAdd, this);
37132 ds.on("remove", this.onRemove, this);
37133 ds.on("update", this.onUpdate, this);
37134 ds.on("clear", this.onClear, this);
37139 this.cm.un("widthchange", this.onColWidthChange, this);
37140 this.cm.un("headerchange", this.onHeaderChange, this);
37141 this.cm.un("hiddenchange", this.onHiddenChange, this);
37142 this.cm.un("columnmoved", this.onColumnMove, this);
37143 this.cm.un("columnlockchange", this.onColumnLock, this);
37146 this.generateRules(cm);
37147 cm.on("widthchange", this.onColWidthChange, this);
37148 cm.on("headerchange", this.onHeaderChange, this);
37149 cm.on("hiddenchange", this.onHiddenChange, this);
37150 cm.on("columnmoved", this.onColumnMove, this);
37151 cm.on("columnlockchange", this.onColumnLock, this);
37156 init: function(grid){
37157 Roo.grid.GridView.superclass.init.call(this, grid);
37159 this.bind(grid.dataSource, grid.colModel);
37161 grid.on("headerclick", this.handleHeaderClick, this);
37163 if(grid.trackMouseOver){
37164 grid.on("mouseover", this.onRowOver, this);
37165 grid.on("mouseout", this.onRowOut, this);
37167 grid.cancelTextSelection = function(){};
37168 this.gridId = grid.id;
37170 var tpls = this.templates || {};
37173 tpls.master = new Roo.Template(
37174 '<div class="x-grid" hidefocus="true">',
37175 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37176 '<div class="x-grid-topbar"></div>',
37177 '<div class="x-grid-scroller"><div></div></div>',
37178 '<div class="x-grid-locked">',
37179 '<div class="x-grid-header">{lockedHeader}</div>',
37180 '<div class="x-grid-body">{lockedBody}</div>',
37182 '<div class="x-grid-viewport">',
37183 '<div class="x-grid-header">{header}</div>',
37184 '<div class="x-grid-body">{body}</div>',
37186 '<div class="x-grid-bottombar"></div>',
37188 '<div class="x-grid-resize-proxy"> </div>',
37191 tpls.master.disableformats = true;
37195 tpls.header = new Roo.Template(
37196 '<table border="0" cellspacing="0" cellpadding="0">',
37197 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37200 tpls.header.disableformats = true;
37202 tpls.header.compile();
37205 tpls.hcell = new Roo.Template(
37206 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37207 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37210 tpls.hcell.disableFormats = true;
37212 tpls.hcell.compile();
37215 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37216 this.unselectableCls + '" ' + this.unselectable +'> </div>');
37217 tpls.hsplit.disableFormats = true;
37219 tpls.hsplit.compile();
37222 tpls.body = new Roo.Template(
37223 '<table border="0" cellspacing="0" cellpadding="0">',
37224 "<tbody>{rows}</tbody>",
37227 tpls.body.disableFormats = true;
37229 tpls.body.compile();
37232 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37233 tpls.row.disableFormats = true;
37235 tpls.row.compile();
37238 tpls.cell = new Roo.Template(
37239 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37240 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37241 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37244 tpls.cell.disableFormats = true;
37246 tpls.cell.compile();
37248 this.templates = tpls;
37251 // remap these for backwards compat
37252 onColWidthChange : function(){
37253 this.updateColumns.apply(this, arguments);
37255 onHeaderChange : function(){
37256 this.updateHeaders.apply(this, arguments);
37258 onHiddenChange : function(){
37259 this.handleHiddenChange.apply(this, arguments);
37261 onColumnMove : function(){
37262 this.handleColumnMove.apply(this, arguments);
37264 onColumnLock : function(){
37265 this.handleLockChange.apply(this, arguments);
37268 onDataChange : function(){
37270 this.updateHeaderSortState();
37273 onClear : function(){
37277 onUpdate : function(ds, record){
37278 this.refreshRow(record);
37281 refreshRow : function(record){
37282 var ds = this.ds, index;
37283 if(typeof record == 'number'){
37285 record = ds.getAt(index);
37287 index = ds.indexOf(record);
37289 this.insertRows(ds, index, index, true);
37290 this.onRemove(ds, record, index+1, true);
37291 this.syncRowHeights(index, index);
37293 this.fireEvent("rowupdated", this, index, record);
37296 onAdd : function(ds, records, index){
37297 this.insertRows(ds, index, index + (records.length-1));
37300 onRemove : function(ds, record, index, isUpdate){
37301 if(isUpdate !== true){
37302 this.fireEvent("beforerowremoved", this, index, record);
37304 var bt = this.getBodyTable(), lt = this.getLockedTable();
37305 if(bt.rows[index]){
37306 bt.firstChild.removeChild(bt.rows[index]);
37308 if(lt.rows[index]){
37309 lt.firstChild.removeChild(lt.rows[index]);
37311 if(isUpdate !== true){
37312 this.stripeRows(index);
37313 this.syncRowHeights(index, index);
37315 this.fireEvent("rowremoved", this, index, record);
37319 onLoad : function(){
37320 this.scrollToTop();
37324 * Scrolls the grid to the top
37326 scrollToTop : function(){
37328 this.scroller.dom.scrollTop = 0;
37334 * Gets a panel in the header of the grid that can be used for toolbars etc.
37335 * After modifying the contents of this panel a call to grid.autoSize() may be
37336 * required to register any changes in size.
37337 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37338 * @return Roo.Element
37340 getHeaderPanel : function(doShow){
37342 this.headerPanel.show();
37344 return this.headerPanel;
37348 * Gets a panel in the footer of the grid that can be used for toolbars etc.
37349 * After modifying the contents of this panel a call to grid.autoSize() may be
37350 * required to register any changes in size.
37351 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37352 * @return Roo.Element
37354 getFooterPanel : function(doShow){
37356 this.footerPanel.show();
37358 return this.footerPanel;
37361 initElements : function(){
37362 var E = Roo.Element;
37363 var el = this.grid.getGridEl().dom.firstChild;
37364 var cs = el.childNodes;
37366 this.el = new E(el);
37368 this.focusEl = new E(el.firstChild);
37369 this.focusEl.swallowEvent("click", true);
37371 this.headerPanel = new E(cs[1]);
37372 this.headerPanel.enableDisplayMode("block");
37374 this.scroller = new E(cs[2]);
37375 this.scrollSizer = new E(this.scroller.dom.firstChild);
37377 this.lockedWrap = new E(cs[3]);
37378 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37379 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37381 this.mainWrap = new E(cs[4]);
37382 this.mainHd = new E(this.mainWrap.dom.firstChild);
37383 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37385 this.footerPanel = new E(cs[5]);
37386 this.footerPanel.enableDisplayMode("block");
37388 this.resizeProxy = new E(cs[6]);
37390 this.headerSelector = String.format(
37391 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37392 this.lockedHd.id, this.mainHd.id
37395 this.splitterSelector = String.format(
37396 '#{0} div.x-grid-split, #{1} div.x-grid-split',
37397 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37400 idToCssName : function(s)
37402 return s.replace(/[^a-z0-9]+/ig, '-');
37405 getHeaderCell : function(index){
37406 return Roo.DomQuery.select(this.headerSelector)[index];
37409 getHeaderCellMeasure : function(index){
37410 return this.getHeaderCell(index).firstChild;
37413 getHeaderCellText : function(index){
37414 return this.getHeaderCell(index).firstChild.firstChild;
37417 getLockedTable : function(){
37418 return this.lockedBody.dom.firstChild;
37421 getBodyTable : function(){
37422 return this.mainBody.dom.firstChild;
37425 getLockedRow : function(index){
37426 return this.getLockedTable().rows[index];
37429 getRow : function(index){
37430 return this.getBodyTable().rows[index];
37433 getRowComposite : function(index){
37435 this.rowEl = new Roo.CompositeElementLite();
37437 var els = [], lrow, mrow;
37438 if(lrow = this.getLockedRow(index)){
37441 if(mrow = this.getRow(index)){
37444 this.rowEl.elements = els;
37448 * Gets the 'td' of the cell
37450 * @param {Integer} rowIndex row to select
37451 * @param {Integer} colIndex column to select
37455 getCell : function(rowIndex, colIndex){
37456 var locked = this.cm.getLockedCount();
37458 if(colIndex < locked){
37459 source = this.lockedBody.dom.firstChild;
37461 source = this.mainBody.dom.firstChild;
37462 colIndex -= locked;
37464 return source.rows[rowIndex].childNodes[colIndex];
37467 getCellText : function(rowIndex, colIndex){
37468 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37471 getCellBox : function(cell){
37472 var b = this.fly(cell).getBox();
37473 if(Roo.isOpera){ // opera fails to report the Y
37474 b.y = cell.offsetTop + this.mainBody.getY();
37479 getCellIndex : function(cell){
37480 var id = String(cell.className).match(this.cellRE);
37482 return parseInt(id[1], 10);
37487 findHeaderIndex : function(n){
37488 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37489 return r ? this.getCellIndex(r) : false;
37492 findHeaderCell : function(n){
37493 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37494 return r ? r : false;
37497 findRowIndex : function(n){
37501 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37502 return r ? r.rowIndex : false;
37505 findCellIndex : function(node){
37506 var stop = this.el.dom;
37507 while(node && node != stop){
37508 if(this.findRE.test(node.className)){
37509 return this.getCellIndex(node);
37511 node = node.parentNode;
37516 getColumnId : function(index){
37517 return this.cm.getColumnId(index);
37520 getSplitters : function()
37522 if(this.splitterSelector){
37523 return Roo.DomQuery.select(this.splitterSelector);
37529 getSplitter : function(index){
37530 return this.getSplitters()[index];
37533 onRowOver : function(e, t){
37535 if((row = this.findRowIndex(t)) !== false){
37536 this.getRowComposite(row).addClass("x-grid-row-over");
37540 onRowOut : function(e, t){
37542 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37543 this.getRowComposite(row).removeClass("x-grid-row-over");
37547 renderHeaders : function(){
37549 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37550 var cb = [], lb = [], sb = [], lsb = [], p = {};
37551 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37552 p.cellId = "x-grid-hd-0-" + i;
37553 p.splitId = "x-grid-csplit-0-" + i;
37554 p.id = cm.getColumnId(i);
37555 p.value = cm.getColumnHeader(i) || "";
37556 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
37557 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37558 if(!cm.isLocked(i)){
37559 cb[cb.length] = ct.apply(p);
37560 sb[sb.length] = st.apply(p);
37562 lb[lb.length] = ct.apply(p);
37563 lsb[lsb.length] = st.apply(p);
37566 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37567 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37570 updateHeaders : function(){
37571 var html = this.renderHeaders();
37572 this.lockedHd.update(html[0]);
37573 this.mainHd.update(html[1]);
37577 * Focuses the specified row.
37578 * @param {Number} row The row index
37580 focusRow : function(row)
37582 //Roo.log('GridView.focusRow');
37583 var x = this.scroller.dom.scrollLeft;
37584 this.focusCell(row, 0, false);
37585 this.scroller.dom.scrollLeft = x;
37589 * Focuses the specified cell.
37590 * @param {Number} row The row index
37591 * @param {Number} col The column index
37592 * @param {Boolean} hscroll false to disable horizontal scrolling
37594 focusCell : function(row, col, hscroll)
37596 //Roo.log('GridView.focusCell');
37597 var el = this.ensureVisible(row, col, hscroll);
37598 this.focusEl.alignTo(el, "tl-tl");
37600 this.focusEl.focus();
37602 this.focusEl.focus.defer(1, this.focusEl);
37607 * Scrolls the specified cell into view
37608 * @param {Number} row The row index
37609 * @param {Number} col The column index
37610 * @param {Boolean} hscroll false to disable horizontal scrolling
37612 ensureVisible : function(row, col, hscroll)
37614 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37615 //return null; //disable for testing.
37616 if(typeof row != "number"){
37617 row = row.rowIndex;
37619 if(row < 0 && row >= this.ds.getCount()){
37622 col = (col !== undefined ? col : 0);
37623 var cm = this.grid.colModel;
37624 while(cm.isHidden(col)){
37628 var el = this.getCell(row, col);
37632 var c = this.scroller.dom;
37634 var ctop = parseInt(el.offsetTop, 10);
37635 var cleft = parseInt(el.offsetLeft, 10);
37636 var cbot = ctop + el.offsetHeight;
37637 var cright = cleft + el.offsetWidth;
37639 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37640 var stop = parseInt(c.scrollTop, 10);
37641 var sleft = parseInt(c.scrollLeft, 10);
37642 var sbot = stop + ch;
37643 var sright = sleft + c.clientWidth;
37645 Roo.log('GridView.ensureVisible:' +
37647 ' c.clientHeight:' + c.clientHeight +
37648 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37656 c.scrollTop = ctop;
37657 //Roo.log("set scrolltop to ctop DISABLE?");
37658 }else if(cbot > sbot){
37659 //Roo.log("set scrolltop to cbot-ch");
37660 c.scrollTop = cbot-ch;
37663 if(hscroll !== false){
37665 c.scrollLeft = cleft;
37666 }else if(cright > sright){
37667 c.scrollLeft = cright-c.clientWidth;
37674 updateColumns : function(){
37675 this.grid.stopEditing();
37676 var cm = this.grid.colModel, colIds = this.getColumnIds();
37677 //var totalWidth = cm.getTotalWidth();
37679 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37680 //if(cm.isHidden(i)) continue;
37681 var w = cm.getColumnWidth(i);
37682 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37683 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37685 this.updateSplitters();
37688 generateRules : function(cm){
37689 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37690 Roo.util.CSS.removeStyleSheet(rulesId);
37691 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37692 var cid = cm.getColumnId(i);
37694 if(cm.config[i].align){
37695 align = 'text-align:'+cm.config[i].align+';';
37698 if(cm.isHidden(i)){
37699 hidden = 'display:none;';
37701 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37703 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37704 this.hdSelector, cid, " {\n", align, width, "}\n",
37705 this.tdSelector, cid, " {\n",hidden,"\n}\n",
37706 this.splitSelector, cid, " {\n", hidden , "\n}\n");
37708 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37711 updateSplitters : function(){
37712 var cm = this.cm, s = this.getSplitters();
37713 if(s){ // splitters not created yet
37714 var pos = 0, locked = true;
37715 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37716 if(cm.isHidden(i)) {
37719 var w = cm.getColumnWidth(i); // make sure it's a number
37720 if(!cm.isLocked(i) && locked){
37725 s[i].style.left = (pos-this.splitOffset) + "px";
37730 handleHiddenChange : function(colModel, colIndex, hidden){
37732 this.hideColumn(colIndex);
37734 this.unhideColumn(colIndex);
37738 hideColumn : function(colIndex){
37739 var cid = this.getColumnId(colIndex);
37740 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37741 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37743 this.updateHeaders();
37745 this.updateSplitters();
37749 unhideColumn : function(colIndex){
37750 var cid = this.getColumnId(colIndex);
37751 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37752 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37755 this.updateHeaders();
37757 this.updateSplitters();
37761 insertRows : function(dm, firstRow, lastRow, isUpdate){
37762 if(firstRow == 0 && lastRow == dm.getCount()-1){
37766 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37768 var s = this.getScrollState();
37769 var markup = this.renderRows(firstRow, lastRow);
37770 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37771 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37772 this.restoreScroll(s);
37774 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37775 this.syncRowHeights(firstRow, lastRow);
37776 this.stripeRows(firstRow);
37782 bufferRows : function(markup, target, index){
37783 var before = null, trows = target.rows, tbody = target.tBodies[0];
37784 if(index < trows.length){
37785 before = trows[index];
37787 var b = document.createElement("div");
37788 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37789 var rows = b.firstChild.rows;
37790 for(var i = 0, len = rows.length; i < len; i++){
37792 tbody.insertBefore(rows[0], before);
37794 tbody.appendChild(rows[0]);
37801 deleteRows : function(dm, firstRow, lastRow){
37802 if(dm.getRowCount()<1){
37803 this.fireEvent("beforerefresh", this);
37804 this.mainBody.update("");
37805 this.lockedBody.update("");
37806 this.fireEvent("refresh", this);
37808 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37809 var bt = this.getBodyTable();
37810 var tbody = bt.firstChild;
37811 var rows = bt.rows;
37812 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37813 tbody.removeChild(rows[firstRow]);
37815 this.stripeRows(firstRow);
37816 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37820 updateRows : function(dataSource, firstRow, lastRow){
37821 var s = this.getScrollState();
37823 this.restoreScroll(s);
37826 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37830 this.updateHeaderSortState();
37833 getScrollState : function(){
37835 var sb = this.scroller.dom;
37836 return {left: sb.scrollLeft, top: sb.scrollTop};
37839 stripeRows : function(startRow){
37840 if(!this.grid.stripeRows || this.ds.getCount() < 1){
37843 startRow = startRow || 0;
37844 var rows = this.getBodyTable().rows;
37845 var lrows = this.getLockedTable().rows;
37846 var cls = ' x-grid-row-alt ';
37847 for(var i = startRow, len = rows.length; i < len; i++){
37848 var row = rows[i], lrow = lrows[i];
37849 var isAlt = ((i+1) % 2 == 0);
37850 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37851 if(isAlt == hasAlt){
37855 row.className += " x-grid-row-alt";
37857 row.className = row.className.replace("x-grid-row-alt", "");
37860 lrow.className = row.className;
37865 restoreScroll : function(state){
37866 //Roo.log('GridView.restoreScroll');
37867 var sb = this.scroller.dom;
37868 sb.scrollLeft = state.left;
37869 sb.scrollTop = state.top;
37873 syncScroll : function(){
37874 //Roo.log('GridView.syncScroll');
37875 var sb = this.scroller.dom;
37876 var sh = this.mainHd.dom;
37877 var bs = this.mainBody.dom;
37878 var lv = this.lockedBody.dom;
37879 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37880 lv.scrollTop = bs.scrollTop = sb.scrollTop;
37883 handleScroll : function(e){
37885 var sb = this.scroller.dom;
37886 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37890 handleWheel : function(e){
37891 var d = e.getWheelDelta();
37892 this.scroller.dom.scrollTop -= d*22;
37893 // set this here to prevent jumpy scrolling on large tables
37894 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37898 renderRows : function(startRow, endRow){
37899 // pull in all the crap needed to render rows
37900 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37901 var colCount = cm.getColumnCount();
37903 if(ds.getCount() < 1){
37907 // build a map for all the columns
37909 for(var i = 0; i < colCount; i++){
37910 var name = cm.getDataIndex(i);
37912 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37913 renderer : cm.getRenderer(i),
37914 id : cm.getColumnId(i),
37915 locked : cm.isLocked(i),
37916 has_editor : cm.isCellEditable(i)
37920 startRow = startRow || 0;
37921 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37923 // records to render
37924 var rs = ds.getRange(startRow, endRow);
37926 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37929 // As much as I hate to duplicate code, this was branched because FireFox really hates
37930 // [].join("") on strings. The performance difference was substantial enough to
37931 // branch this function
37932 doRender : Roo.isGecko ?
37933 function(cs, rs, ds, startRow, colCount, stripe){
37934 var ts = this.templates, ct = ts.cell, rt = ts.row;
37936 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37938 var hasListener = this.grid.hasListener('rowclass');
37940 for(var j = 0, len = rs.length; j < len; j++){
37941 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37942 for(var i = 0; i < colCount; i++){
37944 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37946 p.css = p.attr = "";
37947 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37948 if(p.value == undefined || p.value === "") {
37949 p.value = " ";
37952 p.css += ' x-grid-editable-cell';
37954 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
37955 p.css += ' x-grid-dirty-cell';
37957 var markup = ct.apply(p);
37965 if(stripe && ((rowIndex+1) % 2 == 0)){
37966 alt.push("x-grid-row-alt")
37969 alt.push( " x-grid-dirty-row");
37972 if(this.getRowClass){
37973 alt.push(this.getRowClass(r, rowIndex));
37979 rowIndex : rowIndex,
37982 this.grid.fireEvent('rowclass', this, rowcfg);
37983 alt.push(rowcfg.rowClass);
37985 rp.alt = alt.join(" ");
37986 lbuf+= rt.apply(rp);
37988 buf+= rt.apply(rp);
37990 return [lbuf, buf];
37992 function(cs, rs, ds, startRow, colCount, stripe){
37993 var ts = this.templates, ct = ts.cell, rt = ts.row;
37995 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37996 var hasListener = this.grid.hasListener('rowclass');
37999 for(var j = 0, len = rs.length; j < len; j++){
38000 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38001 for(var i = 0; i < colCount; i++){
38003 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38005 p.css = p.attr = "";
38006 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38007 if(p.value == undefined || p.value === "") {
38008 p.value = " ";
38012 p.css += ' x-grid-editable-cell';
38014 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38015 p.css += ' x-grid-dirty-cell'
38018 var markup = ct.apply(p);
38020 cb[cb.length] = markup;
38022 lcb[lcb.length] = markup;
38026 if(stripe && ((rowIndex+1) % 2 == 0)){
38027 alt.push( "x-grid-row-alt");
38030 alt.push(" x-grid-dirty-row");
38033 if(this.getRowClass){
38034 alt.push( this.getRowClass(r, rowIndex));
38040 rowIndex : rowIndex,
38043 this.grid.fireEvent('rowclass', this, rowcfg);
38044 alt.push(rowcfg.rowClass);
38047 rp.alt = alt.join(" ");
38048 rp.cells = lcb.join("");
38049 lbuf[lbuf.length] = rt.apply(rp);
38050 rp.cells = cb.join("");
38051 buf[buf.length] = rt.apply(rp);
38053 return [lbuf.join(""), buf.join("")];
38056 renderBody : function(){
38057 var markup = this.renderRows();
38058 var bt = this.templates.body;
38059 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38063 * Refreshes the grid
38064 * @param {Boolean} headersToo
38066 refresh : function(headersToo){
38067 this.fireEvent("beforerefresh", this);
38068 this.grid.stopEditing();
38069 var result = this.renderBody();
38070 this.lockedBody.update(result[0]);
38071 this.mainBody.update(result[1]);
38072 if(headersToo === true){
38073 this.updateHeaders();
38074 this.updateColumns();
38075 this.updateSplitters();
38076 this.updateHeaderSortState();
38078 this.syncRowHeights();
38080 this.fireEvent("refresh", this);
38083 handleColumnMove : function(cm, oldIndex, newIndex){
38084 this.indexMap = null;
38085 var s = this.getScrollState();
38086 this.refresh(true);
38087 this.restoreScroll(s);
38088 this.afterMove(newIndex);
38091 afterMove : function(colIndex){
38092 if(this.enableMoveAnim && Roo.enableFx){
38093 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38095 // if multisort - fix sortOrder, and reload..
38096 if (this.grid.dataSource.multiSort) {
38097 // the we can call sort again..
38098 var dm = this.grid.dataSource;
38099 var cm = this.grid.colModel;
38101 for(var i = 0; i < cm.config.length; i++ ) {
38103 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38104 continue; // dont' bother, it's not in sort list or being set.
38107 so.push(cm.config[i].dataIndex);
38110 dm.load(dm.lastOptions);
38117 updateCell : function(dm, rowIndex, dataIndex){
38118 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38119 if(typeof colIndex == "undefined"){ // not present in grid
38122 var cm = this.grid.colModel;
38123 var cell = this.getCell(rowIndex, colIndex);
38124 var cellText = this.getCellText(rowIndex, colIndex);
38127 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38128 id : cm.getColumnId(colIndex),
38129 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38131 var renderer = cm.getRenderer(colIndex);
38132 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38133 if(typeof val == "undefined" || val === "") {
38136 cellText.innerHTML = val;
38137 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38138 this.syncRowHeights(rowIndex, rowIndex);
38141 calcColumnWidth : function(colIndex, maxRowsToMeasure){
38143 if(this.grid.autoSizeHeaders){
38144 var h = this.getHeaderCellMeasure(colIndex);
38145 maxWidth = Math.max(maxWidth, h.scrollWidth);
38148 if(this.cm.isLocked(colIndex)){
38149 tb = this.getLockedTable();
38152 tb = this.getBodyTable();
38153 index = colIndex - this.cm.getLockedCount();
38156 var rows = tb.rows;
38157 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38158 for(var i = 0; i < stopIndex; i++){
38159 var cell = rows[i].childNodes[index].firstChild;
38160 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38163 return maxWidth + /*margin for error in IE*/ 5;
38166 * Autofit a column to its content.
38167 * @param {Number} colIndex
38168 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38170 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38171 if(this.cm.isHidden(colIndex)){
38172 return; // can't calc a hidden column
38175 var cid = this.cm.getColumnId(colIndex);
38176 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38177 if(this.grid.autoSizeHeaders){
38178 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38181 var newWidth = this.calcColumnWidth(colIndex);
38182 this.cm.setColumnWidth(colIndex,
38183 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38184 if(!suppressEvent){
38185 this.grid.fireEvent("columnresize", colIndex, newWidth);
38190 * Autofits all columns to their content and then expands to fit any extra space in the grid
38192 autoSizeColumns : function(){
38193 var cm = this.grid.colModel;
38194 var colCount = cm.getColumnCount();
38195 for(var i = 0; i < colCount; i++){
38196 this.autoSizeColumn(i, true, true);
38198 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38201 this.updateColumns();
38207 * Autofits all columns to the grid's width proportionate with their current size
38208 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38210 fitColumns : function(reserveScrollSpace){
38211 var cm = this.grid.colModel;
38212 var colCount = cm.getColumnCount();
38216 for (i = 0; i < colCount; i++){
38217 if(!cm.isHidden(i) && !cm.isFixed(i)){
38218 w = cm.getColumnWidth(i);
38224 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38225 if(reserveScrollSpace){
38228 var frac = (avail - cm.getTotalWidth())/width;
38229 while (cols.length){
38232 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38234 this.updateColumns();
38238 onRowSelect : function(rowIndex){
38239 var row = this.getRowComposite(rowIndex);
38240 row.addClass("x-grid-row-selected");
38243 onRowDeselect : function(rowIndex){
38244 var row = this.getRowComposite(rowIndex);
38245 row.removeClass("x-grid-row-selected");
38248 onCellSelect : function(row, col){
38249 var cell = this.getCell(row, col);
38251 Roo.fly(cell).addClass("x-grid-cell-selected");
38255 onCellDeselect : function(row, col){
38256 var cell = this.getCell(row, col);
38258 Roo.fly(cell).removeClass("x-grid-cell-selected");
38262 updateHeaderSortState : function(){
38264 // sort state can be single { field: xxx, direction : yyy}
38265 // or { xxx=>ASC , yyy : DESC ..... }
38268 if (!this.ds.multiSort) {
38269 var state = this.ds.getSortState();
38273 mstate[state.field] = state.direction;
38274 // FIXME... - this is not used here.. but might be elsewhere..
38275 this.sortState = state;
38278 mstate = this.ds.sortToggle;
38280 //remove existing sort classes..
38282 var sc = this.sortClasses;
38283 var hds = this.el.select(this.headerSelector).removeClass(sc);
38285 for(var f in mstate) {
38287 var sortColumn = this.cm.findColumnIndex(f);
38289 if(sortColumn != -1){
38290 var sortDir = mstate[f];
38291 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38300 handleHeaderClick : function(g, index,e){
38302 Roo.log("header click");
38305 // touch events on header are handled by context
38306 this.handleHdCtx(g,index,e);
38311 if(this.headersDisabled){
38314 var dm = g.dataSource, cm = g.colModel;
38315 if(!cm.isSortable(index)){
38320 if (dm.multiSort) {
38321 // update the sortOrder
38323 for(var i = 0; i < cm.config.length; i++ ) {
38325 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38326 continue; // dont' bother, it's not in sort list or being set.
38329 so.push(cm.config[i].dataIndex);
38335 dm.sort(cm.getDataIndex(index));
38339 destroy : function(){
38341 this.colMenu.removeAll();
38342 Roo.menu.MenuMgr.unregister(this.colMenu);
38343 this.colMenu.getEl().remove();
38344 delete this.colMenu;
38347 this.hmenu.removeAll();
38348 Roo.menu.MenuMgr.unregister(this.hmenu);
38349 this.hmenu.getEl().remove();
38352 if(this.grid.enableColumnMove){
38353 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38355 for(var dd in dds){
38356 if(!dds[dd].config.isTarget && dds[dd].dragElId){
38357 var elid = dds[dd].dragElId;
38359 Roo.get(elid).remove();
38360 } else if(dds[dd].config.isTarget){
38361 dds[dd].proxyTop.remove();
38362 dds[dd].proxyBottom.remove();
38365 if(Roo.dd.DDM.locationCache[dd]){
38366 delete Roo.dd.DDM.locationCache[dd];
38369 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38372 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38373 this.bind(null, null);
38374 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38377 handleLockChange : function(){
38378 this.refresh(true);
38381 onDenyColumnLock : function(){
38385 onDenyColumnHide : function(){
38389 handleHdMenuClick : function(item){
38390 var index = this.hdCtxIndex;
38391 var cm = this.cm, ds = this.ds;
38394 ds.sort(cm.getDataIndex(index), "ASC");
38397 ds.sort(cm.getDataIndex(index), "DESC");
38400 var lc = cm.getLockedCount();
38401 if(cm.getColumnCount(true) <= lc+1){
38402 this.onDenyColumnLock();
38406 cm.setLocked(index, true, true);
38407 cm.moveColumn(index, lc);
38408 this.grid.fireEvent("columnmove", index, lc);
38410 cm.setLocked(index, true);
38414 var lc = cm.getLockedCount();
38415 if((lc-1) != index){
38416 cm.setLocked(index, false, true);
38417 cm.moveColumn(index, lc-1);
38418 this.grid.fireEvent("columnmove", index, lc-1);
38420 cm.setLocked(index, false);
38423 case 'wider': // used to expand cols on touch..
38425 var cw = cm.getColumnWidth(index);
38426 cw += (item.id == 'wider' ? 1 : -1) * 50;
38427 cw = Math.max(0, cw);
38428 cw = Math.min(cw,4000);
38429 cm.setColumnWidth(index, cw);
38433 index = cm.getIndexById(item.id.substr(4));
38435 if(item.checked && cm.getColumnCount(true) <= 1){
38436 this.onDenyColumnHide();
38439 cm.setHidden(index, item.checked);
38445 beforeColMenuShow : function(){
38446 var cm = this.cm, colCount = cm.getColumnCount();
38447 this.colMenu.removeAll();
38448 for(var i = 0; i < colCount; i++){
38449 this.colMenu.add(new Roo.menu.CheckItem({
38450 id: "col-"+cm.getColumnId(i),
38451 text: cm.getColumnHeader(i),
38452 checked: !cm.isHidden(i),
38458 handleHdCtx : function(g, index, e){
38460 var hd = this.getHeaderCell(index);
38461 this.hdCtxIndex = index;
38462 var ms = this.hmenu.items, cm = this.cm;
38463 ms.get("asc").setDisabled(!cm.isSortable(index));
38464 ms.get("desc").setDisabled(!cm.isSortable(index));
38465 if(this.grid.enableColLock !== false){
38466 ms.get("lock").setDisabled(cm.isLocked(index));
38467 ms.get("unlock").setDisabled(!cm.isLocked(index));
38469 this.hmenu.show(hd, "tl-bl");
38472 handleHdOver : function(e){
38473 var hd = this.findHeaderCell(e.getTarget());
38474 if(hd && !this.headersDisabled){
38475 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38476 this.fly(hd).addClass("x-grid-hd-over");
38481 handleHdOut : function(e){
38482 var hd = this.findHeaderCell(e.getTarget());
38484 this.fly(hd).removeClass("x-grid-hd-over");
38488 handleSplitDblClick : function(e, t){
38489 var i = this.getCellIndex(t);
38490 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38491 this.autoSizeColumn(i, true);
38496 render : function(){
38499 var colCount = cm.getColumnCount();
38501 if(this.grid.monitorWindowResize === true){
38502 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38504 var header = this.renderHeaders();
38505 var body = this.templates.body.apply({rows:""});
38506 var html = this.templates.master.apply({
38509 lockedHeader: header[0],
38513 //this.updateColumns();
38515 this.grid.getGridEl().dom.innerHTML = html;
38517 this.initElements();
38519 // a kludge to fix the random scolling effect in webkit
38520 this.el.on("scroll", function() {
38521 this.el.dom.scrollTop=0; // hopefully not recursive..
38524 this.scroller.on("scroll", this.handleScroll, this);
38525 this.lockedBody.on("mousewheel", this.handleWheel, this);
38526 this.mainBody.on("mousewheel", this.handleWheel, this);
38528 this.mainHd.on("mouseover", this.handleHdOver, this);
38529 this.mainHd.on("mouseout", this.handleHdOut, this);
38530 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38531 {delegate: "."+this.splitClass});
38533 this.lockedHd.on("mouseover", this.handleHdOver, this);
38534 this.lockedHd.on("mouseout", this.handleHdOut, this);
38535 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38536 {delegate: "."+this.splitClass});
38538 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38539 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38542 this.updateSplitters();
38544 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38545 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38546 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38549 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38550 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38552 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38553 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38555 if(this.grid.enableColLock !== false){
38556 this.hmenu.add('-',
38557 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38558 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38562 this.hmenu.add('-',
38563 {id:"wider", text: this.columnsWiderText},
38564 {id:"narrow", text: this.columnsNarrowText }
38570 if(this.grid.enableColumnHide !== false){
38572 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38573 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38574 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38576 this.hmenu.add('-',
38577 {id:"columns", text: this.columnsText, menu: this.colMenu}
38580 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38582 this.grid.on("headercontextmenu", this.handleHdCtx, this);
38585 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38586 this.dd = new Roo.grid.GridDragZone(this.grid, {
38587 ddGroup : this.grid.ddGroup || 'GridDD'
38593 for(var i = 0; i < colCount; i++){
38594 if(cm.isHidden(i)){
38595 this.hideColumn(i);
38597 if(cm.config[i].align){
38598 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38599 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38603 this.updateHeaderSortState();
38605 this.beforeInitialResize();
38608 // two part rendering gives faster view to the user
38609 this.renderPhase2.defer(1, this);
38612 renderPhase2 : function(){
38613 // render the rows now
38615 if(this.grid.autoSizeColumns){
38616 this.autoSizeColumns();
38620 beforeInitialResize : function(){
38624 onColumnSplitterMoved : function(i, w){
38625 this.userResized = true;
38626 var cm = this.grid.colModel;
38627 cm.setColumnWidth(i, w, true);
38628 var cid = cm.getColumnId(i);
38629 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38630 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38631 this.updateSplitters();
38633 this.grid.fireEvent("columnresize", i, w);
38636 syncRowHeights : function(startIndex, endIndex){
38637 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38638 startIndex = startIndex || 0;
38639 var mrows = this.getBodyTable().rows;
38640 var lrows = this.getLockedTable().rows;
38641 var len = mrows.length-1;
38642 endIndex = Math.min(endIndex || len, len);
38643 for(var i = startIndex; i <= endIndex; i++){
38644 var m = mrows[i], l = lrows[i];
38645 var h = Math.max(m.offsetHeight, l.offsetHeight);
38646 m.style.height = l.style.height = h + "px";
38651 layout : function(initialRender, is2ndPass){
38653 var auto = g.autoHeight;
38654 var scrollOffset = 16;
38655 var c = g.getGridEl(), cm = this.cm,
38656 expandCol = g.autoExpandColumn,
38658 //c.beginMeasure();
38660 if(!c.dom.offsetWidth){ // display:none?
38662 this.lockedWrap.show();
38663 this.mainWrap.show();
38668 var hasLock = this.cm.isLocked(0);
38670 var tbh = this.headerPanel.getHeight();
38671 var bbh = this.footerPanel.getHeight();
38674 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38675 var newHeight = ch + c.getBorderWidth("tb");
38677 newHeight = Math.min(g.maxHeight, newHeight);
38679 c.setHeight(newHeight);
38683 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38686 var s = this.scroller;
38688 var csize = c.getSize(true);
38690 this.el.setSize(csize.width, csize.height);
38692 this.headerPanel.setWidth(csize.width);
38693 this.footerPanel.setWidth(csize.width);
38695 var hdHeight = this.mainHd.getHeight();
38696 var vw = csize.width;
38697 var vh = csize.height - (tbh + bbh);
38701 var bt = this.getBodyTable();
38703 if(cm.getLockedCount() == cm.config.length){
38704 bt = this.getLockedTable();
38707 var ltWidth = hasLock ?
38708 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38710 var scrollHeight = bt.offsetHeight;
38711 var scrollWidth = ltWidth + bt.offsetWidth;
38712 var vscroll = false, hscroll = false;
38714 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38716 var lw = this.lockedWrap, mw = this.mainWrap;
38717 var lb = this.lockedBody, mb = this.mainBody;
38719 setTimeout(function(){
38720 var t = s.dom.offsetTop;
38721 var w = s.dom.clientWidth,
38722 h = s.dom.clientHeight;
38725 lw.setSize(ltWidth, h);
38727 mw.setLeftTop(ltWidth, t);
38728 mw.setSize(w-ltWidth, h);
38730 lb.setHeight(h-hdHeight);
38731 mb.setHeight(h-hdHeight);
38733 if(is2ndPass !== true && !gv.userResized && expandCol){
38734 // high speed resize without full column calculation
38736 var ci = cm.getIndexById(expandCol);
38738 ci = cm.findColumnIndex(expandCol);
38740 ci = Math.max(0, ci); // make sure it's got at least the first col.
38741 var expandId = cm.getColumnId(ci);
38742 var tw = cm.getTotalWidth(false);
38743 var currentWidth = cm.getColumnWidth(ci);
38744 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38745 if(currentWidth != cw){
38746 cm.setColumnWidth(ci, cw, true);
38747 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38748 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38749 gv.updateSplitters();
38750 gv.layout(false, true);
38762 onWindowResize : function(){
38763 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38769 appendFooter : function(parentEl){
38773 sortAscText : "Sort Ascending",
38774 sortDescText : "Sort Descending",
38775 lockText : "Lock Column",
38776 unlockText : "Unlock Column",
38777 columnsText : "Columns",
38779 columnsWiderText : "Wider",
38780 columnsNarrowText : "Thinner"
38784 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38785 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38786 this.proxy.el.addClass('x-grid3-col-dd');
38789 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38790 handleMouseDown : function(e){
38794 callHandleMouseDown : function(e){
38795 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38800 * Ext JS Library 1.1.1
38801 * Copyright(c) 2006-2007, Ext JS, LLC.
38803 * Originally Released Under LGPL - original licence link has changed is not relivant.
38806 * <script type="text/javascript">
38810 // This is a support class used internally by the Grid components
38811 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38813 this.view = grid.getView();
38814 this.proxy = this.view.resizeProxy;
38815 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38816 "gridSplitters" + this.grid.getGridEl().id, {
38817 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38819 this.setHandleElId(Roo.id(hd));
38820 this.setOuterHandleElId(Roo.id(hd2));
38821 this.scroll = false;
38823 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38824 fly: Roo.Element.fly,
38826 b4StartDrag : function(x, y){
38827 this.view.headersDisabled = true;
38828 this.proxy.setHeight(this.view.mainWrap.getHeight());
38829 var w = this.cm.getColumnWidth(this.cellIndex);
38830 var minw = Math.max(w-this.grid.minColumnWidth, 0);
38831 this.resetConstraints();
38832 this.setXConstraint(minw, 1000);
38833 this.setYConstraint(0, 0);
38834 this.minX = x - minw;
38835 this.maxX = x + 1000;
38837 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38841 handleMouseDown : function(e){
38842 ev = Roo.EventObject.setEvent(e);
38843 var t = this.fly(ev.getTarget());
38844 if(t.hasClass("x-grid-split")){
38845 this.cellIndex = this.view.getCellIndex(t.dom);
38846 this.split = t.dom;
38847 this.cm = this.grid.colModel;
38848 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38849 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38854 endDrag : function(e){
38855 this.view.headersDisabled = false;
38856 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38857 var diff = endX - this.startPos;
38858 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38861 autoOffset : function(){
38862 this.setDelta(0,0);
38866 * Ext JS Library 1.1.1
38867 * Copyright(c) 2006-2007, Ext JS, LLC.
38869 * Originally Released Under LGPL - original licence link has changed is not relivant.
38872 * <script type="text/javascript">
38876 // This is a support class used internally by the Grid components
38877 Roo.grid.GridDragZone = function(grid, config){
38878 this.view = grid.getView();
38879 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38880 if(this.view.lockedBody){
38881 this.setHandleElId(Roo.id(this.view.mainBody.dom));
38882 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38884 this.scroll = false;
38886 this.ddel = document.createElement('div');
38887 this.ddel.className = 'x-grid-dd-wrap';
38890 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38891 ddGroup : "GridDD",
38893 getDragData : function(e){
38894 var t = Roo.lib.Event.getTarget(e);
38895 var rowIndex = this.view.findRowIndex(t);
38896 var sm = this.grid.selModel;
38898 //Roo.log(rowIndex);
38900 if (sm.getSelectedCell) {
38901 // cell selection..
38902 if (!sm.getSelectedCell()) {
38905 if (rowIndex != sm.getSelectedCell()[0]) {
38911 if(rowIndex !== false){
38916 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38918 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38921 if (e.hasModifier()){
38922 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38925 Roo.log("getDragData");
38930 rowIndex: rowIndex,
38931 selections:sm.getSelections ? sm.getSelections() : (
38932 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38939 onInitDrag : function(e){
38940 var data = this.dragData;
38941 this.ddel.innerHTML = this.grid.getDragDropText();
38942 this.proxy.update(this.ddel);
38943 // fire start drag?
38946 afterRepair : function(){
38947 this.dragging = false;
38950 getRepairXY : function(e, data){
38954 onEndDrag : function(data, e){
38958 onValidDrop : function(dd, e, id){
38963 beforeInvalidDrop : function(e, id){
38968 * Ext JS Library 1.1.1
38969 * Copyright(c) 2006-2007, Ext JS, LLC.
38971 * Originally Released Under LGPL - original licence link has changed is not relivant.
38974 * <script type="text/javascript">
38979 * @class Roo.grid.ColumnModel
38980 * @extends Roo.util.Observable
38981 * This is the default implementation of a ColumnModel used by the Grid. It defines
38982 * the columns in the grid.
38985 var colModel = new Roo.grid.ColumnModel([
38986 {header: "Ticker", width: 60, sortable: true, locked: true},
38987 {header: "Company Name", width: 150, sortable: true},
38988 {header: "Market Cap.", width: 100, sortable: true},
38989 {header: "$ Sales", width: 100, sortable: true, renderer: money},
38990 {header: "Employees", width: 100, sortable: true, resizable: false}
38995 * The config options listed for this class are options which may appear in each
38996 * individual column definition.
38997 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38999 * @param {Object} config An Array of column config objects. See this class's
39000 * config objects for details.
39002 Roo.grid.ColumnModel = function(config){
39004 * The config passed into the constructor
39006 this.config = config;
39009 // if no id, create one
39010 // if the column does not have a dataIndex mapping,
39011 // map it to the order it is in the config
39012 for(var i = 0, len = config.length; i < len; i++){
39014 if(typeof c.dataIndex == "undefined"){
39017 if(typeof c.renderer == "string"){
39018 c.renderer = Roo.util.Format[c.renderer];
39020 if(typeof c.id == "undefined"){
39023 if(c.editor && c.editor.xtype){
39024 c.editor = Roo.factory(c.editor, Roo.grid);
39026 if(c.editor && c.editor.isFormField){
39027 c.editor = new Roo.grid.GridEditor(c.editor);
39029 this.lookup[c.id] = c;
39033 * The width of columns which have no width specified (defaults to 100)
39036 this.defaultWidth = 100;
39039 * Default sortable of columns which have no sortable specified (defaults to false)
39042 this.defaultSortable = false;
39046 * @event widthchange
39047 * Fires when the width of a column changes.
39048 * @param {ColumnModel} this
39049 * @param {Number} columnIndex The column index
39050 * @param {Number} newWidth The new width
39052 "widthchange": true,
39054 * @event headerchange
39055 * Fires when the text of a header changes.
39056 * @param {ColumnModel} this
39057 * @param {Number} columnIndex The column index
39058 * @param {Number} newText The new header text
39060 "headerchange": true,
39062 * @event hiddenchange
39063 * Fires when a column is hidden or "unhidden".
39064 * @param {ColumnModel} this
39065 * @param {Number} columnIndex The column index
39066 * @param {Boolean} hidden true if hidden, false otherwise
39068 "hiddenchange": true,
39070 * @event columnmoved
39071 * Fires when a column is moved.
39072 * @param {ColumnModel} this
39073 * @param {Number} oldIndex
39074 * @param {Number} newIndex
39076 "columnmoved" : true,
39078 * @event columlockchange
39079 * Fires when a column's locked state is changed
39080 * @param {ColumnModel} this
39081 * @param {Number} colIndex
39082 * @param {Boolean} locked true if locked
39084 "columnlockchange" : true
39086 Roo.grid.ColumnModel.superclass.constructor.call(this);
39088 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39090 * @cfg {String} header The header text to display in the Grid view.
39093 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39094 * {@link Roo.data.Record} definition from which to draw the column's value. If not
39095 * specified, the column's index is used as an index into the Record's data Array.
39098 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39099 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39102 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39103 * Defaults to the value of the {@link #defaultSortable} property.
39104 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39107 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
39110 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
39113 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39116 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39119 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39120 * given the cell's data value. See {@link #setRenderer}. If not specified, the
39121 * default renderer uses the raw data value. If an object is returned (bootstrap only)
39122 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39125 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
39128 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
39131 * @cfg {String} cursor (Optional)
39134 * @cfg {String} tooltip (Optional)
39137 * @cfg {Number} xs (Optional)
39140 * @cfg {Number} sm (Optional)
39143 * @cfg {Number} md (Optional)
39146 * @cfg {Number} lg (Optional)
39149 * Returns the id of the column at the specified index.
39150 * @param {Number} index The column index
39151 * @return {String} the id
39153 getColumnId : function(index){
39154 return this.config[index].id;
39158 * Returns the column for a specified id.
39159 * @param {String} id The column id
39160 * @return {Object} the column
39162 getColumnById : function(id){
39163 return this.lookup[id];
39168 * Returns the column for a specified dataIndex.
39169 * @param {String} dataIndex The column dataIndex
39170 * @return {Object|Boolean} the column or false if not found
39172 getColumnByDataIndex: function(dataIndex){
39173 var index = this.findColumnIndex(dataIndex);
39174 return index > -1 ? this.config[index] : false;
39178 * Returns the index for a specified column id.
39179 * @param {String} id The column id
39180 * @return {Number} the index, or -1 if not found
39182 getIndexById : function(id){
39183 for(var i = 0, len = this.config.length; i < len; i++){
39184 if(this.config[i].id == id){
39192 * Returns the index for a specified column dataIndex.
39193 * @param {String} dataIndex The column dataIndex
39194 * @return {Number} the index, or -1 if not found
39197 findColumnIndex : function(dataIndex){
39198 for(var i = 0, len = this.config.length; i < len; i++){
39199 if(this.config[i].dataIndex == dataIndex){
39207 moveColumn : function(oldIndex, newIndex){
39208 var c = this.config[oldIndex];
39209 this.config.splice(oldIndex, 1);
39210 this.config.splice(newIndex, 0, c);
39211 this.dataMap = null;
39212 this.fireEvent("columnmoved", this, oldIndex, newIndex);
39215 isLocked : function(colIndex){
39216 return this.config[colIndex].locked === true;
39219 setLocked : function(colIndex, value, suppressEvent){
39220 if(this.isLocked(colIndex) == value){
39223 this.config[colIndex].locked = value;
39224 if(!suppressEvent){
39225 this.fireEvent("columnlockchange", this, colIndex, value);
39229 getTotalLockedWidth : function(){
39230 var totalWidth = 0;
39231 for(var i = 0; i < this.config.length; i++){
39232 if(this.isLocked(i) && !this.isHidden(i)){
39233 this.totalWidth += this.getColumnWidth(i);
39239 getLockedCount : function(){
39240 for(var i = 0, len = this.config.length; i < len; i++){
39241 if(!this.isLocked(i)){
39246 return this.config.length;
39250 * Returns the number of columns.
39253 getColumnCount : function(visibleOnly){
39254 if(visibleOnly === true){
39256 for(var i = 0, len = this.config.length; i < len; i++){
39257 if(!this.isHidden(i)){
39263 return this.config.length;
39267 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39268 * @param {Function} fn
39269 * @param {Object} scope (optional)
39270 * @return {Array} result
39272 getColumnsBy : function(fn, scope){
39274 for(var i = 0, len = this.config.length; i < len; i++){
39275 var c = this.config[i];
39276 if(fn.call(scope||this, c, i) === true){
39284 * Returns true if the specified column is sortable.
39285 * @param {Number} col The column index
39286 * @return {Boolean}
39288 isSortable : function(col){
39289 if(typeof this.config[col].sortable == "undefined"){
39290 return this.defaultSortable;
39292 return this.config[col].sortable;
39296 * Returns the rendering (formatting) function defined for the column.
39297 * @param {Number} col The column index.
39298 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39300 getRenderer : function(col){
39301 if(!this.config[col].renderer){
39302 return Roo.grid.ColumnModel.defaultRenderer;
39304 return this.config[col].renderer;
39308 * Sets the rendering (formatting) function for a column.
39309 * @param {Number} col The column index
39310 * @param {Function} fn The function to use to process the cell's raw data
39311 * to return HTML markup for the grid view. The render function is called with
39312 * the following parameters:<ul>
39313 * <li>Data value.</li>
39314 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39315 * <li>css A CSS style string to apply to the table cell.</li>
39316 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39317 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39318 * <li>Row index</li>
39319 * <li>Column index</li>
39320 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39322 setRenderer : function(col, fn){
39323 this.config[col].renderer = fn;
39327 * Returns the width for the specified column.
39328 * @param {Number} col The column index
39331 getColumnWidth : function(col){
39332 return this.config[col].width * 1 || this.defaultWidth;
39336 * Sets the width for a column.
39337 * @param {Number} col The column index
39338 * @param {Number} width The new width
39340 setColumnWidth : function(col, width, suppressEvent){
39341 this.config[col].width = width;
39342 this.totalWidth = null;
39343 if(!suppressEvent){
39344 this.fireEvent("widthchange", this, col, width);
39349 * Returns the total width of all columns.
39350 * @param {Boolean} includeHidden True to include hidden column widths
39353 getTotalWidth : function(includeHidden){
39354 if(!this.totalWidth){
39355 this.totalWidth = 0;
39356 for(var i = 0, len = this.config.length; i < len; i++){
39357 if(includeHidden || !this.isHidden(i)){
39358 this.totalWidth += this.getColumnWidth(i);
39362 return this.totalWidth;
39366 * Returns the header for the specified column.
39367 * @param {Number} col The column index
39370 getColumnHeader : function(col){
39371 return this.config[col].header;
39375 * Sets the header for a column.
39376 * @param {Number} col The column index
39377 * @param {String} header The new header
39379 setColumnHeader : function(col, header){
39380 this.config[col].header = header;
39381 this.fireEvent("headerchange", this, col, header);
39385 * Returns the tooltip for the specified column.
39386 * @param {Number} col The column index
39389 getColumnTooltip : function(col){
39390 return this.config[col].tooltip;
39393 * Sets the tooltip for a column.
39394 * @param {Number} col The column index
39395 * @param {String} tooltip The new tooltip
39397 setColumnTooltip : function(col, tooltip){
39398 this.config[col].tooltip = tooltip;
39402 * Returns the dataIndex for the specified column.
39403 * @param {Number} col The column index
39406 getDataIndex : function(col){
39407 return this.config[col].dataIndex;
39411 * Sets the dataIndex for a column.
39412 * @param {Number} col The column index
39413 * @param {Number} dataIndex The new dataIndex
39415 setDataIndex : function(col, dataIndex){
39416 this.config[col].dataIndex = dataIndex;
39422 * Returns true if the cell is editable.
39423 * @param {Number} colIndex The column index
39424 * @param {Number} rowIndex The row index - this is nto actually used..?
39425 * @return {Boolean}
39427 isCellEditable : function(colIndex, rowIndex){
39428 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39432 * Returns the editor defined for the cell/column.
39433 * return false or null to disable editing.
39434 * @param {Number} colIndex The column index
39435 * @param {Number} rowIndex The row index
39438 getCellEditor : function(colIndex, rowIndex){
39439 return this.config[colIndex].editor;
39443 * Sets if a column is editable.
39444 * @param {Number} col The column index
39445 * @param {Boolean} editable True if the column is editable
39447 setEditable : function(col, editable){
39448 this.config[col].editable = editable;
39453 * Returns true if the column is hidden.
39454 * @param {Number} colIndex The column index
39455 * @return {Boolean}
39457 isHidden : function(colIndex){
39458 return this.config[colIndex].hidden;
39463 * Returns true if the column width cannot be changed
39465 isFixed : function(colIndex){
39466 return this.config[colIndex].fixed;
39470 * Returns true if the column can be resized
39471 * @return {Boolean}
39473 isResizable : function(colIndex){
39474 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39477 * Sets if a column is hidden.
39478 * @param {Number} colIndex The column index
39479 * @param {Boolean} hidden True if the column is hidden
39481 setHidden : function(colIndex, hidden){
39482 this.config[colIndex].hidden = hidden;
39483 this.totalWidth = null;
39484 this.fireEvent("hiddenchange", this, colIndex, hidden);
39488 * Sets the editor for a column.
39489 * @param {Number} col The column index
39490 * @param {Object} editor The editor object
39492 setEditor : function(col, editor){
39493 this.config[col].editor = editor;
39497 Roo.grid.ColumnModel.defaultRenderer = function(value){
39498 if(typeof value == "string" && value.length < 1){
39504 // Alias for backwards compatibility
39505 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39508 * Ext JS Library 1.1.1
39509 * Copyright(c) 2006-2007, Ext JS, LLC.
39511 * Originally Released Under LGPL - original licence link has changed is not relivant.
39514 * <script type="text/javascript">
39518 * @class Roo.grid.AbstractSelectionModel
39519 * @extends Roo.util.Observable
39520 * Abstract base class for grid SelectionModels. It provides the interface that should be
39521 * implemented by descendant classes. This class should not be directly instantiated.
39524 Roo.grid.AbstractSelectionModel = function(){
39525 this.locked = false;
39526 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39529 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
39530 /** @ignore Called by the grid automatically. Do not call directly. */
39531 init : function(grid){
39537 * Locks the selections.
39540 this.locked = true;
39544 * Unlocks the selections.
39546 unlock : function(){
39547 this.locked = false;
39551 * Returns true if the selections are locked.
39552 * @return {Boolean}
39554 isLocked : function(){
39555 return this.locked;
39559 * Ext JS Library 1.1.1
39560 * Copyright(c) 2006-2007, Ext JS, LLC.
39562 * Originally Released Under LGPL - original licence link has changed is not relivant.
39565 * <script type="text/javascript">
39568 * @extends Roo.grid.AbstractSelectionModel
39569 * @class Roo.grid.RowSelectionModel
39570 * The default SelectionModel used by {@link Roo.grid.Grid}.
39571 * It supports multiple selections and keyboard selection/navigation.
39573 * @param {Object} config
39575 Roo.grid.RowSelectionModel = function(config){
39576 Roo.apply(this, config);
39577 this.selections = new Roo.util.MixedCollection(false, function(o){
39582 this.lastActive = false;
39586 * @event selectionchange
39587 * Fires when the selection changes
39588 * @param {SelectionModel} this
39590 "selectionchange" : true,
39592 * @event afterselectionchange
39593 * Fires after the selection changes (eg. by key press or clicking)
39594 * @param {SelectionModel} this
39596 "afterselectionchange" : true,
39598 * @event beforerowselect
39599 * Fires when a row is selected being selected, return false to cancel.
39600 * @param {SelectionModel} this
39601 * @param {Number} rowIndex The selected index
39602 * @param {Boolean} keepExisting False if other selections will be cleared
39604 "beforerowselect" : true,
39607 * Fires when a row is selected.
39608 * @param {SelectionModel} this
39609 * @param {Number} rowIndex The selected index
39610 * @param {Roo.data.Record} r The record
39612 "rowselect" : true,
39614 * @event rowdeselect
39615 * Fires when a row is deselected.
39616 * @param {SelectionModel} this
39617 * @param {Number} rowIndex The selected index
39619 "rowdeselect" : true
39621 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39622 this.locked = false;
39625 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
39627 * @cfg {Boolean} singleSelect
39628 * True to allow selection of only one row at a time (defaults to false)
39630 singleSelect : false,
39633 initEvents : function(){
39635 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39636 this.grid.on("mousedown", this.handleMouseDown, this);
39637 }else{ // allow click to work like normal
39638 this.grid.on("rowclick", this.handleDragableRowClick, this);
39641 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39642 "up" : function(e){
39644 this.selectPrevious(e.shiftKey);
39645 }else if(this.last !== false && this.lastActive !== false){
39646 var last = this.last;
39647 this.selectRange(this.last, this.lastActive-1);
39648 this.grid.getView().focusRow(this.lastActive);
39649 if(last !== false){
39653 this.selectFirstRow();
39655 this.fireEvent("afterselectionchange", this);
39657 "down" : function(e){
39659 this.selectNext(e.shiftKey);
39660 }else if(this.last !== false && this.lastActive !== false){
39661 var last = this.last;
39662 this.selectRange(this.last, this.lastActive+1);
39663 this.grid.getView().focusRow(this.lastActive);
39664 if(last !== false){
39668 this.selectFirstRow();
39670 this.fireEvent("afterselectionchange", this);
39675 var view = this.grid.view;
39676 view.on("refresh", this.onRefresh, this);
39677 view.on("rowupdated", this.onRowUpdated, this);
39678 view.on("rowremoved", this.onRemove, this);
39682 onRefresh : function(){
39683 var ds = this.grid.dataSource, i, v = this.grid.view;
39684 var s = this.selections;
39685 s.each(function(r){
39686 if((i = ds.indexOfId(r.id)) != -1){
39688 s.add(ds.getAt(i)); // updating the selection relate data
39696 onRemove : function(v, index, r){
39697 this.selections.remove(r);
39701 onRowUpdated : function(v, index, r){
39702 if(this.isSelected(r)){
39703 v.onRowSelect(index);
39709 * @param {Array} records The records to select
39710 * @param {Boolean} keepExisting (optional) True to keep existing selections
39712 selectRecords : function(records, keepExisting){
39714 this.clearSelections();
39716 var ds = this.grid.dataSource;
39717 for(var i = 0, len = records.length; i < len; i++){
39718 this.selectRow(ds.indexOf(records[i]), true);
39723 * Gets the number of selected rows.
39726 getCount : function(){
39727 return this.selections.length;
39731 * Selects the first row in the grid.
39733 selectFirstRow : function(){
39738 * Select the last row.
39739 * @param {Boolean} keepExisting (optional) True to keep existing selections
39741 selectLastRow : function(keepExisting){
39742 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39746 * Selects the row immediately following the last selected row.
39747 * @param {Boolean} keepExisting (optional) True to keep existing selections
39749 selectNext : function(keepExisting){
39750 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39751 this.selectRow(this.last+1, keepExisting);
39752 this.grid.getView().focusRow(this.last);
39757 * Selects the row that precedes the last selected row.
39758 * @param {Boolean} keepExisting (optional) True to keep existing selections
39760 selectPrevious : function(keepExisting){
39762 this.selectRow(this.last-1, keepExisting);
39763 this.grid.getView().focusRow(this.last);
39768 * Returns the selected records
39769 * @return {Array} Array of selected records
39771 getSelections : function(){
39772 return [].concat(this.selections.items);
39776 * Returns the first selected record.
39779 getSelected : function(){
39780 return this.selections.itemAt(0);
39785 * Clears all selections.
39787 clearSelections : function(fast){
39792 var ds = this.grid.dataSource;
39793 var s = this.selections;
39794 s.each(function(r){
39795 this.deselectRow(ds.indexOfId(r.id));
39799 this.selections.clear();
39806 * Selects all rows.
39808 selectAll : function(){
39812 this.selections.clear();
39813 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39814 this.selectRow(i, true);
39819 * Returns True if there is a selection.
39820 * @return {Boolean}
39822 hasSelection : function(){
39823 return this.selections.length > 0;
39827 * Returns True if the specified row is selected.
39828 * @param {Number/Record} record The record or index of the record to check
39829 * @return {Boolean}
39831 isSelected : function(index){
39832 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39833 return (r && this.selections.key(r.id) ? true : false);
39837 * Returns True if the specified record id is selected.
39838 * @param {String} id The id of record to check
39839 * @return {Boolean}
39841 isIdSelected : function(id){
39842 return (this.selections.key(id) ? true : false);
39846 handleMouseDown : function(e, t){
39847 var view = this.grid.getView(), rowIndex;
39848 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39851 if(e.shiftKey && this.last !== false){
39852 var last = this.last;
39853 this.selectRange(last, rowIndex, e.ctrlKey);
39854 this.last = last; // reset the last
39855 view.focusRow(rowIndex);
39857 var isSelected = this.isSelected(rowIndex);
39858 if(e.button !== 0 && isSelected){
39859 view.focusRow(rowIndex);
39860 }else if(e.ctrlKey && isSelected){
39861 this.deselectRow(rowIndex);
39862 }else if(!isSelected){
39863 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39864 view.focusRow(rowIndex);
39867 this.fireEvent("afterselectionchange", this);
39870 handleDragableRowClick : function(grid, rowIndex, e)
39872 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39873 this.selectRow(rowIndex, false);
39874 grid.view.focusRow(rowIndex);
39875 this.fireEvent("afterselectionchange", this);
39880 * Selects multiple rows.
39881 * @param {Array} rows Array of the indexes of the row to select
39882 * @param {Boolean} keepExisting (optional) True to keep existing selections
39884 selectRows : function(rows, keepExisting){
39886 this.clearSelections();
39888 for(var i = 0, len = rows.length; i < len; i++){
39889 this.selectRow(rows[i], true);
39894 * Selects a range of rows. All rows in between startRow and endRow are also selected.
39895 * @param {Number} startRow The index of the first row in the range
39896 * @param {Number} endRow The index of the last row in the range
39897 * @param {Boolean} keepExisting (optional) True to retain existing selections
39899 selectRange : function(startRow, endRow, keepExisting){
39904 this.clearSelections();
39906 if(startRow <= endRow){
39907 for(var i = startRow; i <= endRow; i++){
39908 this.selectRow(i, true);
39911 for(var i = startRow; i >= endRow; i--){
39912 this.selectRow(i, true);
39918 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39919 * @param {Number} startRow The index of the first row in the range
39920 * @param {Number} endRow The index of the last row in the range
39922 deselectRange : function(startRow, endRow, preventViewNotify){
39926 for(var i = startRow; i <= endRow; i++){
39927 this.deselectRow(i, preventViewNotify);
39933 * @param {Number} row The index of the row to select
39934 * @param {Boolean} keepExisting (optional) True to keep existing selections
39936 selectRow : function(index, keepExisting, preventViewNotify){
39937 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
39940 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39941 if(!keepExisting || this.singleSelect){
39942 this.clearSelections();
39944 var r = this.grid.dataSource.getAt(index);
39945 this.selections.add(r);
39946 this.last = this.lastActive = index;
39947 if(!preventViewNotify){
39948 this.grid.getView().onRowSelect(index);
39950 this.fireEvent("rowselect", this, index, r);
39951 this.fireEvent("selectionchange", this);
39957 * @param {Number} row The index of the row to deselect
39959 deselectRow : function(index, preventViewNotify){
39963 if(this.last == index){
39966 if(this.lastActive == index){
39967 this.lastActive = false;
39969 var r = this.grid.dataSource.getAt(index);
39970 this.selections.remove(r);
39971 if(!preventViewNotify){
39972 this.grid.getView().onRowDeselect(index);
39974 this.fireEvent("rowdeselect", this, index);
39975 this.fireEvent("selectionchange", this);
39979 restoreLast : function(){
39981 this.last = this._last;
39986 acceptsNav : function(row, col, cm){
39987 return !cm.isHidden(col) && cm.isCellEditable(col, row);
39991 onEditorKey : function(field, e){
39992 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39997 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39999 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40001 }else if(k == e.ENTER && !e.ctrlKey){
40005 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40007 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40009 }else if(k == e.ESC){
40013 g.startEditing(newCell[0], newCell[1]);
40018 * Ext JS Library 1.1.1
40019 * Copyright(c) 2006-2007, Ext JS, LLC.
40021 * Originally Released Under LGPL - original licence link has changed is not relivant.
40024 * <script type="text/javascript">
40027 * @class Roo.grid.CellSelectionModel
40028 * @extends Roo.grid.AbstractSelectionModel
40029 * This class provides the basic implementation for cell selection in a grid.
40031 * @param {Object} config The object containing the configuration of this model.
40032 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40034 Roo.grid.CellSelectionModel = function(config){
40035 Roo.apply(this, config);
40037 this.selection = null;
40041 * @event beforerowselect
40042 * Fires before a cell is selected.
40043 * @param {SelectionModel} this
40044 * @param {Number} rowIndex The selected row index
40045 * @param {Number} colIndex The selected cell index
40047 "beforecellselect" : true,
40049 * @event cellselect
40050 * Fires when a cell is selected.
40051 * @param {SelectionModel} this
40052 * @param {Number} rowIndex The selected row index
40053 * @param {Number} colIndex The selected cell index
40055 "cellselect" : true,
40057 * @event selectionchange
40058 * Fires when the active selection changes.
40059 * @param {SelectionModel} this
40060 * @param {Object} selection null for no selection or an object (o) with two properties
40062 <li>o.record: the record object for the row the selection is in</li>
40063 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40066 "selectionchange" : true,
40069 * Fires when the tab (or enter) was pressed on the last editable cell
40070 * You can use this to trigger add new row.
40071 * @param {SelectionModel} this
40075 * @event beforeeditnext
40076 * Fires before the next editable sell is made active
40077 * You can use this to skip to another cell or fire the tabend
40078 * if you set cell to false
40079 * @param {Object} eventdata object : { cell : [ row, col ] }
40081 "beforeeditnext" : true
40083 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40086 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
40088 enter_is_tab: false,
40091 initEvents : function(){
40092 this.grid.on("mousedown", this.handleMouseDown, this);
40093 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40094 var view = this.grid.view;
40095 view.on("refresh", this.onViewChange, this);
40096 view.on("rowupdated", this.onRowUpdated, this);
40097 view.on("beforerowremoved", this.clearSelections, this);
40098 view.on("beforerowsinserted", this.clearSelections, this);
40099 if(this.grid.isEditor){
40100 this.grid.on("beforeedit", this.beforeEdit, this);
40105 beforeEdit : function(e){
40106 this.select(e.row, e.column, false, true, e.record);
40110 onRowUpdated : function(v, index, r){
40111 if(this.selection && this.selection.record == r){
40112 v.onCellSelect(index, this.selection.cell[1]);
40117 onViewChange : function(){
40118 this.clearSelections(true);
40122 * Returns the currently selected cell,.
40123 * @return {Array} The selected cell (row, column) or null if none selected.
40125 getSelectedCell : function(){
40126 return this.selection ? this.selection.cell : null;
40130 * Clears all selections.
40131 * @param {Boolean} true to prevent the gridview from being notified about the change.
40133 clearSelections : function(preventNotify){
40134 var s = this.selection;
40136 if(preventNotify !== true){
40137 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40139 this.selection = null;
40140 this.fireEvent("selectionchange", this, null);
40145 * Returns true if there is a selection.
40146 * @return {Boolean}
40148 hasSelection : function(){
40149 return this.selection ? true : false;
40153 handleMouseDown : function(e, t){
40154 var v = this.grid.getView();
40155 if(this.isLocked()){
40158 var row = v.findRowIndex(t);
40159 var cell = v.findCellIndex(t);
40160 if(row !== false && cell !== false){
40161 this.select(row, cell);
40167 * @param {Number} rowIndex
40168 * @param {Number} collIndex
40170 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40171 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40172 this.clearSelections();
40173 r = r || this.grid.dataSource.getAt(rowIndex);
40176 cell : [rowIndex, colIndex]
40178 if(!preventViewNotify){
40179 var v = this.grid.getView();
40180 v.onCellSelect(rowIndex, colIndex);
40181 if(preventFocus !== true){
40182 v.focusCell(rowIndex, colIndex);
40185 this.fireEvent("cellselect", this, rowIndex, colIndex);
40186 this.fireEvent("selectionchange", this, this.selection);
40191 isSelectable : function(rowIndex, colIndex, cm){
40192 return !cm.isHidden(colIndex);
40196 handleKeyDown : function(e){
40197 //Roo.log('Cell Sel Model handleKeyDown');
40198 if(!e.isNavKeyPress()){
40201 var g = this.grid, s = this.selection;
40204 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
40206 this.select(cell[0], cell[1]);
40211 var walk = function(row, col, step){
40212 return g.walkCells(row, col, step, sm.isSelectable, sm);
40214 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40221 // handled by onEditorKey
40222 if (g.isEditor && g.editing) {
40226 newCell = walk(r, c-1, -1);
40228 newCell = walk(r, c+1, 1);
40233 newCell = walk(r+1, c, 1);
40237 newCell = walk(r-1, c, -1);
40241 newCell = walk(r, c+1, 1);
40245 newCell = walk(r, c-1, -1);
40250 if(g.isEditor && !g.editing){
40251 g.startEditing(r, c);
40260 this.select(newCell[0], newCell[1]);
40266 acceptsNav : function(row, col, cm){
40267 return !cm.isHidden(col) && cm.isCellEditable(col, row);
40271 * @param {Number} field (not used) - as it's normally used as a listener
40272 * @param {Number} e - event - fake it by using
40274 * var e = Roo.EventObjectImpl.prototype;
40275 * e.keyCode = e.TAB
40279 onEditorKey : function(field, e){
40281 var k = e.getKey(),
40284 ed = g.activeEditor,
40286 ///Roo.log('onEditorKey' + k);
40289 if (this.enter_is_tab && k == e.ENTER) {
40295 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40297 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40303 } else if(k == e.ENTER && !e.ctrlKey){
40306 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40308 } else if(k == e.ESC){
40313 var ecall = { cell : newCell, forward : forward };
40314 this.fireEvent('beforeeditnext', ecall );
40315 newCell = ecall.cell;
40316 forward = ecall.forward;
40320 //Roo.log('next cell after edit');
40321 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40322 } else if (forward) {
40323 // tabbed past last
40324 this.fireEvent.defer(100, this, ['tabend',this]);
40329 * Ext JS Library 1.1.1
40330 * Copyright(c) 2006-2007, Ext JS, LLC.
40332 * Originally Released Under LGPL - original licence link has changed is not relivant.
40335 * <script type="text/javascript">
40339 * @class Roo.grid.EditorGrid
40340 * @extends Roo.grid.Grid
40341 * Class for creating and editable grid.
40342 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40343 * The container MUST have some type of size defined for the grid to fill. The container will be
40344 * automatically set to position relative if it isn't already.
40345 * @param {Object} dataSource The data model to bind to
40346 * @param {Object} colModel The column model with info about this grid's columns
40348 Roo.grid.EditorGrid = function(container, config){
40349 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40350 this.getGridEl().addClass("xedit-grid");
40352 if(!this.selModel){
40353 this.selModel = new Roo.grid.CellSelectionModel();
40356 this.activeEditor = null;
40360 * @event beforeedit
40361 * Fires before cell editing is triggered. The edit event object has the following properties <br />
40362 * <ul style="padding:5px;padding-left:16px;">
40363 * <li>grid - This grid</li>
40364 * <li>record - The record being edited</li>
40365 * <li>field - The field name being edited</li>
40366 * <li>value - The value for the field being edited.</li>
40367 * <li>row - The grid row index</li>
40368 * <li>column - The grid column index</li>
40369 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40371 * @param {Object} e An edit event (see above for description)
40373 "beforeedit" : true,
40376 * Fires after a cell is edited. <br />
40377 * <ul style="padding:5px;padding-left:16px;">
40378 * <li>grid - This grid</li>
40379 * <li>record - The record being edited</li>
40380 * <li>field - The field name being edited</li>
40381 * <li>value - The value being set</li>
40382 * <li>originalValue - The original value for the field, before the edit.</li>
40383 * <li>row - The grid row index</li>
40384 * <li>column - The grid column index</li>
40386 * @param {Object} e An edit event (see above for description)
40388 "afteredit" : true,
40390 * @event validateedit
40391 * Fires after a cell is edited, but before the value is set in the record.
40392 * You can use this to modify the value being set in the field, Return false
40393 * to cancel the change. The edit event object has the following properties <br />
40394 * <ul style="padding:5px;padding-left:16px;">
40395 * <li>editor - This editor</li>
40396 * <li>grid - This grid</li>
40397 * <li>record - The record being edited</li>
40398 * <li>field - The field name being edited</li>
40399 * <li>value - The value being set</li>
40400 * <li>originalValue - The original value for the field, before the edit.</li>
40401 * <li>row - The grid row index</li>
40402 * <li>column - The grid column index</li>
40403 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40405 * @param {Object} e An edit event (see above for description)
40407 "validateedit" : true
40409 this.on("bodyscroll", this.stopEditing, this);
40410 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
40413 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40415 * @cfg {Number} clicksToEdit
40416 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40423 trackMouseOver: false, // causes very odd FF errors
40425 onCellDblClick : function(g, row, col){
40426 this.startEditing(row, col);
40429 onEditComplete : function(ed, value, startValue){
40430 this.editing = false;
40431 this.activeEditor = null;
40432 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40434 var field = this.colModel.getDataIndex(ed.col);
40439 originalValue: startValue,
40446 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
40449 if(String(value) !== String(startValue)){
40451 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40452 r.set(field, e.value);
40453 // if we are dealing with a combo box..
40454 // then we also set the 'name' colum to be the displayField
40455 if (ed.field.displayField && ed.field.name) {
40456 r.set(ed.field.name, ed.field.el.dom.value);
40459 delete e.cancel; //?? why!!!
40460 this.fireEvent("afteredit", e);
40463 this.fireEvent("afteredit", e); // always fire it!
40465 this.view.focusCell(ed.row, ed.col);
40469 * Starts editing the specified for the specified row/column
40470 * @param {Number} rowIndex
40471 * @param {Number} colIndex
40473 startEditing : function(row, col){
40474 this.stopEditing();
40475 if(this.colModel.isCellEditable(col, row)){
40476 this.view.ensureVisible(row, col, true);
40478 var r = this.dataSource.getAt(row);
40479 var field = this.colModel.getDataIndex(col);
40480 var cell = Roo.get(this.view.getCell(row,col));
40485 value: r.data[field],
40490 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40491 this.editing = true;
40492 var ed = this.colModel.getCellEditor(col, row);
40498 ed.render(ed.parentEl || document.body);
40504 (function(){ // complex but required for focus issues in safari, ie and opera
40508 ed.on("complete", this.onEditComplete, this, {single: true});
40509 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
40510 this.activeEditor = ed;
40511 var v = r.data[field];
40512 ed.startEdit(this.view.getCell(row, col), v);
40513 // combo's with 'displayField and name set
40514 if (ed.field.displayField && ed.field.name) {
40515 ed.field.el.dom.value = r.data[ed.field.name];
40519 }).defer(50, this);
40525 * Stops any active editing
40527 stopEditing : function(){
40528 if(this.activeEditor){
40529 this.activeEditor.completeEdit();
40531 this.activeEditor = null;
40535 * Called to get grid's drag proxy text, by default returns this.ddText.
40538 getDragDropText : function(){
40539 var count = this.selModel.getSelectedCell() ? 1 : 0;
40540 return String.format(this.ddText, count, count == 1 ? '' : 's');
40545 * Ext JS Library 1.1.1
40546 * Copyright(c) 2006-2007, Ext JS, LLC.
40548 * Originally Released Under LGPL - original licence link has changed is not relivant.
40551 * <script type="text/javascript">
40554 // private - not really -- you end up using it !
40555 // This is a support class used internally by the Grid components
40558 * @class Roo.grid.GridEditor
40559 * @extends Roo.Editor
40560 * Class for creating and editable grid elements.
40561 * @param {Object} config any settings (must include field)
40563 Roo.grid.GridEditor = function(field, config){
40564 if (!config && field.field) {
40566 field = Roo.factory(config.field, Roo.form);
40568 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40569 field.monitorTab = false;
40572 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40575 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40578 alignment: "tl-tl",
40581 cls: "x-small-editor x-grid-editor",
40586 * Ext JS Library 1.1.1
40587 * Copyright(c) 2006-2007, Ext JS, LLC.
40589 * Originally Released Under LGPL - original licence link has changed is not relivant.
40592 * <script type="text/javascript">
40597 Roo.grid.PropertyRecord = Roo.data.Record.create([
40598 {name:'name',type:'string'}, 'value'
40602 Roo.grid.PropertyStore = function(grid, source){
40604 this.store = new Roo.data.Store({
40605 recordType : Roo.grid.PropertyRecord
40607 this.store.on('update', this.onUpdate, this);
40609 this.setSource(source);
40611 Roo.grid.PropertyStore.superclass.constructor.call(this);
40616 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40617 setSource : function(o){
40619 this.store.removeAll();
40622 if(this.isEditableValue(o[k])){
40623 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40626 this.store.loadRecords({records: data}, {}, true);
40629 onUpdate : function(ds, record, type){
40630 if(type == Roo.data.Record.EDIT){
40631 var v = record.data['value'];
40632 var oldValue = record.modified['value'];
40633 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40634 this.source[record.id] = v;
40636 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40643 getProperty : function(row){
40644 return this.store.getAt(row);
40647 isEditableValue: function(val){
40648 if(val && val instanceof Date){
40650 }else if(typeof val == 'object' || typeof val == 'function'){
40656 setValue : function(prop, value){
40657 this.source[prop] = value;
40658 this.store.getById(prop).set('value', value);
40661 getSource : function(){
40662 return this.source;
40666 Roo.grid.PropertyColumnModel = function(grid, store){
40669 g.PropertyColumnModel.superclass.constructor.call(this, [
40670 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40671 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40673 this.store = store;
40674 this.bselect = Roo.DomHelper.append(document.body, {
40675 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40676 {tag: 'option', value: 'true', html: 'true'},
40677 {tag: 'option', value: 'false', html: 'false'}
40680 Roo.id(this.bselect);
40683 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40684 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40685 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40686 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40687 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40689 this.renderCellDelegate = this.renderCell.createDelegate(this);
40690 this.renderPropDelegate = this.renderProp.createDelegate(this);
40693 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40697 valueText : 'Value',
40699 dateFormat : 'm/j/Y',
40702 renderDate : function(dateVal){
40703 return dateVal.dateFormat(this.dateFormat);
40706 renderBool : function(bVal){
40707 return bVal ? 'true' : 'false';
40710 isCellEditable : function(colIndex, rowIndex){
40711 return colIndex == 1;
40714 getRenderer : function(col){
40716 this.renderCellDelegate : this.renderPropDelegate;
40719 renderProp : function(v){
40720 return this.getPropertyName(v);
40723 renderCell : function(val){
40725 if(val instanceof Date){
40726 rv = this.renderDate(val);
40727 }else if(typeof val == 'boolean'){
40728 rv = this.renderBool(val);
40730 return Roo.util.Format.htmlEncode(rv);
40733 getPropertyName : function(name){
40734 var pn = this.grid.propertyNames;
40735 return pn && pn[name] ? pn[name] : name;
40738 getCellEditor : function(colIndex, rowIndex){
40739 var p = this.store.getProperty(rowIndex);
40740 var n = p.data['name'], val = p.data['value'];
40742 if(typeof(this.grid.customEditors[n]) == 'string'){
40743 return this.editors[this.grid.customEditors[n]];
40745 if(typeof(this.grid.customEditors[n]) != 'undefined'){
40746 return this.grid.customEditors[n];
40748 if(val instanceof Date){
40749 return this.editors['date'];
40750 }else if(typeof val == 'number'){
40751 return this.editors['number'];
40752 }else if(typeof val == 'boolean'){
40753 return this.editors['boolean'];
40755 return this.editors['string'];
40761 * @class Roo.grid.PropertyGrid
40762 * @extends Roo.grid.EditorGrid
40763 * This class represents the interface of a component based property grid control.
40764 * <br><br>Usage:<pre><code>
40765 var grid = new Roo.grid.PropertyGrid("my-container-id", {
40773 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40774 * The container MUST have some type of size defined for the grid to fill. The container will be
40775 * automatically set to position relative if it isn't already.
40776 * @param {Object} config A config object that sets properties on this grid.
40778 Roo.grid.PropertyGrid = function(container, config){
40779 config = config || {};
40780 var store = new Roo.grid.PropertyStore(this);
40781 this.store = store;
40782 var cm = new Roo.grid.PropertyColumnModel(this, store);
40783 store.store.sort('name', 'ASC');
40784 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40787 enableColLock:false,
40788 enableColumnMove:false,
40790 trackMouseOver: false,
40793 this.getGridEl().addClass('x-props-grid');
40794 this.lastEditRow = null;
40795 this.on('columnresize', this.onColumnResize, this);
40798 * @event beforepropertychange
40799 * Fires before a property changes (return false to stop?)
40800 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40801 * @param {String} id Record Id
40802 * @param {String} newval New Value
40803 * @param {String} oldval Old Value
40805 "beforepropertychange": true,
40807 * @event propertychange
40808 * Fires after a property changes
40809 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40810 * @param {String} id Record Id
40811 * @param {String} newval New Value
40812 * @param {String} oldval Old Value
40814 "propertychange": true
40816 this.customEditors = this.customEditors || {};
40818 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40821 * @cfg {Object} customEditors map of colnames=> custom editors.
40822 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40823 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40824 * false disables editing of the field.
40828 * @cfg {Object} propertyNames map of property Names to their displayed value
40831 render : function(){
40832 Roo.grid.PropertyGrid.superclass.render.call(this);
40833 this.autoSize.defer(100, this);
40836 autoSize : function(){
40837 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40839 this.view.fitColumns();
40843 onColumnResize : function(){
40844 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40848 * Sets the data for the Grid
40849 * accepts a Key => Value object of all the elements avaiable.
40850 * @param {Object} data to appear in grid.
40852 setSource : function(source){
40853 this.store.setSource(source);
40857 * Gets all the data from the grid.
40858 * @return {Object} data data stored in grid
40860 getSource : function(){
40861 return this.store.getSource();
40870 * @class Roo.grid.Calendar
40871 * @extends Roo.util.Grid
40872 * This class extends the Grid to provide a calendar widget
40873 * <br><br>Usage:<pre><code>
40874 var grid = new Roo.grid.Calendar("my-container-id", {
40877 selModel: mySelectionModel,
40878 autoSizeColumns: true,
40879 monitorWindowResize: false,
40880 trackMouseOver: true
40881 eventstore : real data store..
40887 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40888 * The container MUST have some type of size defined for the grid to fill. The container will be
40889 * automatically set to position relative if it isn't already.
40890 * @param {Object} config A config object that sets properties on this grid.
40892 Roo.grid.Calendar = function(container, config){
40893 // initialize the container
40894 this.container = Roo.get(container);
40895 this.container.update("");
40896 this.container.setStyle("overflow", "hidden");
40897 this.container.addClass('x-grid-container');
40899 this.id = this.container.id;
40901 Roo.apply(this, config);
40902 // check and correct shorthanded configs
40906 for (var r = 0;r < 6;r++) {
40909 for (var c =0;c < 7;c++) {
40913 if (this.eventStore) {
40914 this.eventStore= Roo.factory(this.eventStore, Roo.data);
40915 this.eventStore.on('load',this.onLoad, this);
40916 this.eventStore.on('beforeload',this.clearEvents, this);
40920 this.dataSource = new Roo.data.Store({
40921 proxy: new Roo.data.MemoryProxy(rows),
40922 reader: new Roo.data.ArrayReader({}, [
40923 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40926 this.dataSource.load();
40927 this.ds = this.dataSource;
40928 this.ds.xmodule = this.xmodule || false;
40931 var cellRender = function(v,x,r)
40933 return String.format(
40934 '<div class="fc-day fc-widget-content"><div>' +
40935 '<div class="fc-event-container"></div>' +
40936 '<div class="fc-day-number">{0}</div>'+
40938 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40939 '</div></div>', v);
40944 this.colModel = new Roo.grid.ColumnModel( [
40946 xtype: 'ColumnModel',
40948 dataIndex : 'weekday0',
40950 renderer : cellRender
40953 xtype: 'ColumnModel',
40955 dataIndex : 'weekday1',
40957 renderer : cellRender
40960 xtype: 'ColumnModel',
40962 dataIndex : 'weekday2',
40963 header : 'Tuesday',
40964 renderer : cellRender
40967 xtype: 'ColumnModel',
40969 dataIndex : 'weekday3',
40970 header : 'Wednesday',
40971 renderer : cellRender
40974 xtype: 'ColumnModel',
40976 dataIndex : 'weekday4',
40977 header : 'Thursday',
40978 renderer : cellRender
40981 xtype: 'ColumnModel',
40983 dataIndex : 'weekday5',
40985 renderer : cellRender
40988 xtype: 'ColumnModel',
40990 dataIndex : 'weekday6',
40991 header : 'Saturday',
40992 renderer : cellRender
40995 this.cm = this.colModel;
40996 this.cm.xmodule = this.xmodule || false;
41000 //this.selModel = new Roo.grid.CellSelectionModel();
41001 //this.sm = this.selModel;
41002 //this.selModel.init(this);
41006 this.container.setWidth(this.width);
41010 this.container.setHeight(this.height);
41017 * The raw click event for the entire grid.
41018 * @param {Roo.EventObject} e
41023 * The raw dblclick event for the entire grid.
41024 * @param {Roo.EventObject} e
41028 * @event contextmenu
41029 * The raw contextmenu event for the entire grid.
41030 * @param {Roo.EventObject} e
41032 "contextmenu" : true,
41035 * The raw mousedown event for the entire grid.
41036 * @param {Roo.EventObject} e
41038 "mousedown" : true,
41041 * The raw mouseup event for the entire grid.
41042 * @param {Roo.EventObject} e
41047 * The raw mouseover event for the entire grid.
41048 * @param {Roo.EventObject} e
41050 "mouseover" : true,
41053 * The raw mouseout event for the entire grid.
41054 * @param {Roo.EventObject} e
41059 * The raw keypress event for the entire grid.
41060 * @param {Roo.EventObject} e
41065 * The raw keydown event for the entire grid.
41066 * @param {Roo.EventObject} e
41074 * Fires when a cell is clicked
41075 * @param {Grid} this
41076 * @param {Number} rowIndex
41077 * @param {Number} columnIndex
41078 * @param {Roo.EventObject} e
41080 "cellclick" : true,
41082 * @event celldblclick
41083 * Fires when a cell is double clicked
41084 * @param {Grid} this
41085 * @param {Number} rowIndex
41086 * @param {Number} columnIndex
41087 * @param {Roo.EventObject} e
41089 "celldblclick" : true,
41092 * Fires when a row is clicked
41093 * @param {Grid} this
41094 * @param {Number} rowIndex
41095 * @param {Roo.EventObject} e
41099 * @event rowdblclick
41100 * Fires when a row is double clicked
41101 * @param {Grid} this
41102 * @param {Number} rowIndex
41103 * @param {Roo.EventObject} e
41105 "rowdblclick" : true,
41107 * @event headerclick
41108 * Fires when a header is clicked
41109 * @param {Grid} this
41110 * @param {Number} columnIndex
41111 * @param {Roo.EventObject} e
41113 "headerclick" : true,
41115 * @event headerdblclick
41116 * Fires when a header cell is double clicked
41117 * @param {Grid} this
41118 * @param {Number} columnIndex
41119 * @param {Roo.EventObject} e
41121 "headerdblclick" : true,
41123 * @event rowcontextmenu
41124 * Fires when a row is right clicked
41125 * @param {Grid} this
41126 * @param {Number} rowIndex
41127 * @param {Roo.EventObject} e
41129 "rowcontextmenu" : true,
41131 * @event cellcontextmenu
41132 * Fires when a cell is right clicked
41133 * @param {Grid} this
41134 * @param {Number} rowIndex
41135 * @param {Number} cellIndex
41136 * @param {Roo.EventObject} e
41138 "cellcontextmenu" : true,
41140 * @event headercontextmenu
41141 * Fires when a header is right clicked
41142 * @param {Grid} this
41143 * @param {Number} columnIndex
41144 * @param {Roo.EventObject} e
41146 "headercontextmenu" : true,
41148 * @event bodyscroll
41149 * Fires when the body element is scrolled
41150 * @param {Number} scrollLeft
41151 * @param {Number} scrollTop
41153 "bodyscroll" : true,
41155 * @event columnresize
41156 * Fires when the user resizes a column
41157 * @param {Number} columnIndex
41158 * @param {Number} newSize
41160 "columnresize" : true,
41162 * @event columnmove
41163 * Fires when the user moves a column
41164 * @param {Number} oldIndex
41165 * @param {Number} newIndex
41167 "columnmove" : true,
41170 * Fires when row(s) start being dragged
41171 * @param {Grid} this
41172 * @param {Roo.GridDD} dd The drag drop object
41173 * @param {event} e The raw browser event
41175 "startdrag" : true,
41178 * Fires when a drag operation is complete
41179 * @param {Grid} this
41180 * @param {Roo.GridDD} dd The drag drop object
41181 * @param {event} e The raw browser event
41186 * Fires when dragged row(s) are dropped on a valid DD target
41187 * @param {Grid} this
41188 * @param {Roo.GridDD} dd The drag drop object
41189 * @param {String} targetId The target drag drop object
41190 * @param {event} e The raw browser event
41195 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41196 * @param {Grid} this
41197 * @param {Roo.GridDD} dd The drag drop object
41198 * @param {String} targetId The target drag drop object
41199 * @param {event} e The raw browser event
41204 * Fires when the dragged row(s) first cross another DD target while being dragged
41205 * @param {Grid} this
41206 * @param {Roo.GridDD} dd The drag drop object
41207 * @param {String} targetId The target drag drop object
41208 * @param {event} e The raw browser event
41210 "dragenter" : true,
41213 * Fires when the dragged row(s) leave another DD target while being dragged
41214 * @param {Grid} this
41215 * @param {Roo.GridDD} dd The drag drop object
41216 * @param {String} targetId The target drag drop object
41217 * @param {event} e The raw browser event
41222 * Fires when a row is rendered, so you can change add a style to it.
41223 * @param {GridView} gridview The grid view
41224 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
41230 * Fires when the grid is rendered
41231 * @param {Grid} grid
41236 * Fires when a date is selected
41237 * @param {DatePicker} this
41238 * @param {Date} date The selected date
41242 * @event monthchange
41243 * Fires when the displayed month changes
41244 * @param {DatePicker} this
41245 * @param {Date} date The selected month
41247 'monthchange': true,
41249 * @event evententer
41250 * Fires when mouse over an event
41251 * @param {Calendar} this
41252 * @param {event} Event
41254 'evententer': true,
41256 * @event eventleave
41257 * Fires when the mouse leaves an
41258 * @param {Calendar} this
41261 'eventleave': true,
41263 * @event eventclick
41264 * Fires when the mouse click an
41265 * @param {Calendar} this
41268 'eventclick': true,
41270 * @event eventrender
41271 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41272 * @param {Calendar} this
41273 * @param {data} data to be modified
41275 'eventrender': true
41279 Roo.grid.Grid.superclass.constructor.call(this);
41280 this.on('render', function() {
41281 this.view.el.addClass('x-grid-cal');
41283 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41287 if (!Roo.grid.Calendar.style) {
41288 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41291 '.x-grid-cal .x-grid-col' : {
41292 height: 'auto !important',
41293 'vertical-align': 'top'
41295 '.x-grid-cal .fc-event-hori' : {
41306 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41308 * @cfg {Store} eventStore The store that loads events.
41313 activeDate : false,
41316 monitorWindowResize : false,
41319 resizeColumns : function() {
41320 var col = (this.view.el.getWidth() / 7) - 3;
41321 // loop through cols, and setWidth
41322 for(var i =0 ; i < 7 ; i++){
41323 this.cm.setColumnWidth(i, col);
41326 setDate :function(date) {
41328 Roo.log('setDate?');
41330 this.resizeColumns();
41331 var vd = this.activeDate;
41332 this.activeDate = date;
41333 // if(vd && this.el){
41334 // var t = date.getTime();
41335 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41336 // Roo.log('using add remove');
41338 // this.fireEvent('monthchange', this, date);
41340 // this.cells.removeClass("fc-state-highlight");
41341 // this.cells.each(function(c){
41342 // if(c.dateValue == t){
41343 // c.addClass("fc-state-highlight");
41344 // setTimeout(function(){
41345 // try{c.dom.firstChild.focus();}catch(e){}
41355 var days = date.getDaysInMonth();
41357 var firstOfMonth = date.getFirstDateOfMonth();
41358 var startingPos = firstOfMonth.getDay()-this.startDay;
41360 if(startingPos < this.startDay){
41364 var pm = date.add(Date.MONTH, -1);
41365 var prevStart = pm.getDaysInMonth()-startingPos;
41369 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41371 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41372 //this.cells.addClassOnOver('fc-state-hover');
41374 var cells = this.cells.elements;
41375 var textEls = this.textNodes;
41377 //Roo.each(cells, function(cell){
41378 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41381 days += startingPos;
41383 // convert everything to numbers so it's fast
41384 var day = 86400000;
41385 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41388 //Roo.log(prevStart);
41390 var today = new Date().clearTime().getTime();
41391 var sel = date.clearTime().getTime();
41392 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41393 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41394 var ddMatch = this.disabledDatesRE;
41395 var ddText = this.disabledDatesText;
41396 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41397 var ddaysText = this.disabledDaysText;
41398 var format = this.format;
41400 var setCellClass = function(cal, cell){
41402 //Roo.log('set Cell Class');
41404 var t = d.getTime();
41409 cell.dateValue = t;
41411 cell.className += " fc-today";
41412 cell.className += " fc-state-highlight";
41413 cell.title = cal.todayText;
41416 // disable highlight in other month..
41417 cell.className += " fc-state-highlight";
41422 //cell.className = " fc-state-disabled";
41423 cell.title = cal.minText;
41427 //cell.className = " fc-state-disabled";
41428 cell.title = cal.maxText;
41432 if(ddays.indexOf(d.getDay()) != -1){
41433 // cell.title = ddaysText;
41434 // cell.className = " fc-state-disabled";
41437 if(ddMatch && format){
41438 var fvalue = d.dateFormat(format);
41439 if(ddMatch.test(fvalue)){
41440 cell.title = ddText.replace("%0", fvalue);
41441 cell.className = " fc-state-disabled";
41445 if (!cell.initialClassName) {
41446 cell.initialClassName = cell.dom.className;
41449 cell.dom.className = cell.initialClassName + ' ' + cell.className;
41454 for(; i < startingPos; i++) {
41455 cells[i].dayName = (++prevStart);
41456 Roo.log(textEls[i]);
41457 d.setDate(d.getDate()+1);
41459 //cells[i].className = "fc-past fc-other-month";
41460 setCellClass(this, cells[i]);
41465 for(; i < days; i++){
41466 intDay = i - startingPos + 1;
41467 cells[i].dayName = (intDay);
41468 d.setDate(d.getDate()+1);
41470 cells[i].className = ''; // "x-date-active";
41471 setCellClass(this, cells[i]);
41475 for(; i < 42; i++) {
41476 //textEls[i].innerHTML = (++extraDays);
41478 d.setDate(d.getDate()+1);
41479 cells[i].dayName = (++extraDays);
41480 cells[i].className = "fc-future fc-other-month";
41481 setCellClass(this, cells[i]);
41484 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41486 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41488 // this will cause all the cells to mis
41491 for (var r = 0;r < 6;r++) {
41492 for (var c =0;c < 7;c++) {
41493 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41497 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41498 for(i=0;i<cells.length;i++) {
41500 this.cells.elements[i].dayName = cells[i].dayName ;
41501 this.cells.elements[i].className = cells[i].className;
41502 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41503 this.cells.elements[i].title = cells[i].title ;
41504 this.cells.elements[i].dateValue = cells[i].dateValue ;
41510 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41511 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41513 ////if(totalRows != 6){
41514 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41515 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41518 this.fireEvent('monthchange', this, date);
41523 * Returns the grid's SelectionModel.
41524 * @return {SelectionModel}
41526 getSelectionModel : function(){
41527 if(!this.selModel){
41528 this.selModel = new Roo.grid.CellSelectionModel();
41530 return this.selModel;
41534 this.eventStore.load()
41540 findCell : function(dt) {
41541 dt = dt.clearTime().getTime();
41543 this.cells.each(function(c){
41544 //Roo.log("check " +c.dateValue + '?=' + dt);
41545 if(c.dateValue == dt){
41555 findCells : function(rec) {
41556 var s = rec.data.start_dt.clone().clearTime().getTime();
41558 var e= rec.data.end_dt.clone().clearTime().getTime();
41561 this.cells.each(function(c){
41562 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41564 if(c.dateValue > e){
41567 if(c.dateValue < s){
41576 findBestRow: function(cells)
41580 for (var i =0 ; i < cells.length;i++) {
41581 ret = Math.max(cells[i].rows || 0,ret);
41588 addItem : function(rec)
41590 // look for vertical location slot in
41591 var cells = this.findCells(rec);
41593 rec.row = this.findBestRow(cells);
41595 // work out the location.
41599 for(var i =0; i < cells.length; i++) {
41607 if (crow.start.getY() == cells[i].getY()) {
41609 crow.end = cells[i];
41625 for (var i = 0; i < cells.length;i++) {
41626 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41633 clearEvents: function() {
41635 if (!this.eventStore.getCount()) {
41638 // reset number of rows in cells.
41639 Roo.each(this.cells.elements, function(c){
41643 this.eventStore.each(function(e) {
41644 this.clearEvent(e);
41649 clearEvent : function(ev)
41652 Roo.each(ev.els, function(el) {
41653 el.un('mouseenter' ,this.onEventEnter, this);
41654 el.un('mouseleave' ,this.onEventLeave, this);
41662 renderEvent : function(ev,ctr) {
41664 ctr = this.view.el.select('.fc-event-container',true).first();
41668 this.clearEvent(ev);
41674 var cells = ev.cells;
41675 var rows = ev.rows;
41676 this.fireEvent('eventrender', this, ev);
41678 for(var i =0; i < rows.length; i++) {
41682 cls += ' fc-event-start';
41684 if ((i+1) == rows.length) {
41685 cls += ' fc-event-end';
41688 //Roo.log(ev.data);
41689 // how many rows should it span..
41690 var cg = this.eventTmpl.append(ctr,Roo.apply({
41693 }, ev.data) , true);
41696 cg.on('mouseenter' ,this.onEventEnter, this, ev);
41697 cg.on('mouseleave' ,this.onEventLeave, this, ev);
41698 cg.on('click', this.onEventClick, this, ev);
41702 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41703 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41706 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
41707 cg.setWidth(ebox.right - sbox.x -2);
41711 renderEvents: function()
41713 // first make sure there is enough space..
41715 if (!this.eventTmpl) {
41716 this.eventTmpl = new Roo.Template(
41717 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
41718 '<div class="fc-event-inner">' +
41719 '<span class="fc-event-time">{time}</span>' +
41720 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41722 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
41730 this.cells.each(function(c) {
41731 //Roo.log(c.select('.fc-day-content div',true).first());
41732 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41735 var ctr = this.view.el.select('.fc-event-container',true).first();
41738 this.eventStore.each(function(ev){
41740 this.renderEvent(ev);
41744 this.view.layout();
41748 onEventEnter: function (e, el,event,d) {
41749 this.fireEvent('evententer', this, el, event);
41752 onEventLeave: function (e, el,event,d) {
41753 this.fireEvent('eventleave', this, el, event);
41756 onEventClick: function (e, el,event,d) {
41757 this.fireEvent('eventclick', this, el, event);
41760 onMonthChange: function () {
41764 onLoad: function () {
41766 //Roo.log('calendar onload');
41768 if(this.eventStore.getCount() > 0){
41772 this.eventStore.each(function(d){
41777 if (typeof(add.end_dt) == 'undefined') {
41778 Roo.log("Missing End time in calendar data: ");
41782 if (typeof(add.start_dt) == 'undefined') {
41783 Roo.log("Missing Start time in calendar data: ");
41787 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41788 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41789 add.id = add.id || d.id;
41790 add.title = add.title || '??';
41798 this.renderEvents();
41808 render : function ()
41812 if (!this.view.el.hasClass('course-timesheet')) {
41813 this.view.el.addClass('course-timesheet');
41815 if (this.tsStyle) {
41820 Roo.log(_this.grid.view.el.getWidth());
41823 this.tsStyle = Roo.util.CSS.createStyleSheet({
41824 '.course-timesheet .x-grid-row' : {
41827 '.x-grid-row td' : {
41828 'vertical-align' : 0
41830 '.course-edit-link' : {
41832 'text-overflow' : 'ellipsis',
41833 'overflow' : 'hidden',
41834 'white-space' : 'nowrap',
41835 'cursor' : 'pointer'
41840 '.de-act-sup-link' : {
41841 'color' : 'purple',
41842 'text-decoration' : 'line-through'
41846 'text-decoration' : 'line-through'
41848 '.course-timesheet .course-highlight' : {
41849 'border-top-style': 'dashed !important',
41850 'border-bottom-bottom': 'dashed !important'
41852 '.course-timesheet .course-item' : {
41853 'font-family' : 'tahoma, arial, helvetica',
41854 'font-size' : '11px',
41855 'overflow' : 'hidden',
41856 'padding-left' : '10px',
41857 'padding-right' : '10px',
41858 'padding-top' : '10px'
41866 monitorWindowResize : false,
41867 cellrenderer : function(v,x,r)
41872 xtype: 'CellSelectionModel',
41879 beforeload : function (_self, options)
41881 options.params = options.params || {};
41882 options.params._month = _this.monthField.getValue();
41883 options.params.limit = 9999;
41884 options.params['sort'] = 'when_dt';
41885 options.params['dir'] = 'ASC';
41886 this.proxy.loadResponse = this.loadResponse;
41888 //this.addColumns();
41890 load : function (_self, records, options)
41892 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41893 // if you click on the translation.. you can edit it...
41894 var el = Roo.get(this);
41895 var id = el.dom.getAttribute('data-id');
41896 var d = el.dom.getAttribute('data-date');
41897 var t = el.dom.getAttribute('data-time');
41898 //var id = this.child('span').dom.textContent;
41901 Pman.Dialog.CourseCalendar.show({
41905 productitem_active : id ? 1 : 0
41907 _this.grid.ds.load({});
41912 _this.panel.fireEvent('resize', [ '', '' ]);
41915 loadResponse : function(o, success, response){
41916 // this is overridden on before load..
41918 Roo.log("our code?");
41919 //Roo.log(success);
41920 //Roo.log(response)
41921 delete this.activeRequest;
41923 this.fireEvent("loadexception", this, o, response);
41924 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41929 result = o.reader.read(response);
41931 Roo.log("load exception?");
41932 this.fireEvent("loadexception", this, o, response, e);
41933 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41936 Roo.log("ready...");
41937 // loop through result.records;
41938 // and set this.tdate[date] = [] << array of records..
41940 Roo.each(result.records, function(r){
41942 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41943 _this.tdata[r.data.when_dt.format('j')] = [];
41945 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41948 //Roo.log(_this.tdata);
41950 result.records = [];
41951 result.totalRecords = 6;
41953 // let's generate some duumy records for the rows.
41954 //var st = _this.dateField.getValue();
41956 // work out monday..
41957 //st = st.add(Date.DAY, -1 * st.format('w'));
41959 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41961 var firstOfMonth = date.getFirstDayOfMonth();
41962 var days = date.getDaysInMonth();
41964 var firstAdded = false;
41965 for (var i = 0; i < result.totalRecords ; i++) {
41966 //var d= st.add(Date.DAY, i);
41969 for(var w = 0 ; w < 7 ; w++){
41970 if(!firstAdded && firstOfMonth != w){
41977 var dd = (d > 0 && d < 10) ? "0"+d : d;
41978 row['weekday'+w] = String.format(
41979 '<span style="font-size: 16px;"><b>{0}</b></span>'+
41980 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41982 date.format('Y-m-')+dd
41985 if(typeof(_this.tdata[d]) != 'undefined'){
41986 Roo.each(_this.tdata[d], function(r){
41990 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41991 if(r.parent_id*1>0){
41992 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41995 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41996 deactive = 'de-act-link';
41999 row['weekday'+w] += String.format(
42000 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42002 r.product_id_name, //1
42003 r.when_dt.format('h:ia'), //2
42013 // only do this if something added..
42015 result.records.push(_this.grid.dataSource.reader.newRow(row));
42019 // push it twice. (second one with an hour..
42023 this.fireEvent("load", this, o, o.request.arg);
42024 o.request.callback.call(o.request.scope, result, o.request.arg, true);
42026 sortInfo : {field: 'when_dt', direction : 'ASC' },
42028 xtype: 'HttpProxy',
42031 url : baseURL + '/Roo/Shop_course.php'
42034 xtype: 'JsonReader',
42051 'name': 'parent_id',
42055 'name': 'product_id',
42059 'name': 'productitem_id',
42077 click : function (_self, e)
42079 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42080 sd.setMonth(sd.getMonth()-1);
42081 _this.monthField.setValue(sd.format('Y-m-d'));
42082 _this.grid.ds.load({});
42088 xtype: 'Separator',
42092 xtype: 'MonthField',
42095 render : function (_self)
42097 _this.monthField = _self;
42098 // _this.monthField.set today
42100 select : function (combo, date)
42102 _this.grid.ds.load({});
42105 value : (function() { return new Date(); })()
42108 xtype: 'Separator',
42114 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42124 click : function (_self, e)
42126 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42127 sd.setMonth(sd.getMonth()+1);
42128 _this.monthField.setValue(sd.format('Y-m-d'));
42129 _this.grid.ds.load({});
42142 * Ext JS Library 1.1.1
42143 * Copyright(c) 2006-2007, Ext JS, LLC.
42145 * Originally Released Under LGPL - original licence link has changed is not relivant.
42148 * <script type="text/javascript">
42152 * @class Roo.LoadMask
42153 * A simple utility class for generically masking elements while loading data. If the element being masked has
42154 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42155 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
42156 * element's UpdateManager load indicator and will be destroyed after the initial load.
42158 * Create a new LoadMask
42159 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42160 * @param {Object} config The config object
42162 Roo.LoadMask = function(el, config){
42163 this.el = Roo.get(el);
42164 Roo.apply(this, config);
42166 this.store.on('beforeload', this.onBeforeLoad, this);
42167 this.store.on('load', this.onLoad, this);
42168 this.store.on('loadexception', this.onLoadException, this);
42169 this.removeMask = false;
42171 var um = this.el.getUpdateManager();
42172 um.showLoadIndicator = false; // disable the default indicator
42173 um.on('beforeupdate', this.onBeforeLoad, this);
42174 um.on('update', this.onLoad, this);
42175 um.on('failure', this.onLoad, this);
42176 this.removeMask = true;
42180 Roo.LoadMask.prototype = {
42182 * @cfg {Boolean} removeMask
42183 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42184 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
42187 * @cfg {String} msg
42188 * The text to display in a centered loading message box (defaults to 'Loading...')
42190 msg : 'Loading...',
42192 * @cfg {String} msgCls
42193 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42195 msgCls : 'x-mask-loading',
42198 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42204 * Disables the mask to prevent it from being displayed
42206 disable : function(){
42207 this.disabled = true;
42211 * Enables the mask so that it can be displayed
42213 enable : function(){
42214 this.disabled = false;
42217 onLoadException : function()
42219 Roo.log(arguments);
42221 if (typeof(arguments[3]) != 'undefined') {
42222 Roo.MessageBox.alert("Error loading",arguments[3]);
42226 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42227 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42236 this.el.unmask(this.removeMask);
42239 onLoad : function()
42241 this.el.unmask(this.removeMask);
42245 onBeforeLoad : function(){
42246 if(!this.disabled){
42247 this.el.mask(this.msg, this.msgCls);
42252 destroy : function(){
42254 this.store.un('beforeload', this.onBeforeLoad, this);
42255 this.store.un('load', this.onLoad, this);
42256 this.store.un('loadexception', this.onLoadException, this);
42258 var um = this.el.getUpdateManager();
42259 um.un('beforeupdate', this.onBeforeLoad, this);
42260 um.un('update', this.onLoad, this);
42261 um.un('failure', this.onLoad, this);
42266 * Ext JS Library 1.1.1
42267 * Copyright(c) 2006-2007, Ext JS, LLC.
42269 * Originally Released Under LGPL - original licence link has changed is not relivant.
42272 * <script type="text/javascript">
42277 * @class Roo.XTemplate
42278 * @extends Roo.Template
42279 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42281 var t = new Roo.XTemplate(
42282 '<select name="{name}">',
42283 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
42287 // then append, applying the master template values
42290 * Supported features:
42295 {a_variable} - output encoded.
42296 {a_variable.format:("Y-m-d")} - call a method on the variable
42297 {a_variable:raw} - unencoded output
42298 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42299 {a_variable:this.method_on_template(...)} - call a method on the template object.
42304 <tpl for="a_variable or condition.."></tpl>
42305 <tpl if="a_variable or condition"></tpl>
42306 <tpl exec="some javascript"></tpl>
42307 <tpl name="named_template"></tpl> (experimental)
42309 <tpl for="."></tpl> - just iterate the property..
42310 <tpl for=".."></tpl> - iterates with the parent (probably the template)
42314 Roo.XTemplate = function()
42316 Roo.XTemplate.superclass.constructor.apply(this, arguments);
42323 Roo.extend(Roo.XTemplate, Roo.Template, {
42326 * The various sub templates
42331 * basic tag replacing syntax
42334 * // you can fake an object call by doing this
42338 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42341 * compile the template
42343 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42346 compile: function()
42350 s = ['<tpl>', s, '</tpl>'].join('');
42352 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42353 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42354 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
42355 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42356 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
42361 while(true == !!(m = s.match(re))){
42362 var forMatch = m[0].match(nameRe),
42363 ifMatch = m[0].match(ifRe),
42364 execMatch = m[0].match(execRe),
42365 namedMatch = m[0].match(namedRe),
42370 name = forMatch && forMatch[1] ? forMatch[1] : '';
42373 // if - puts fn into test..
42374 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42376 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42381 // exec - calls a function... returns empty if true is returned.
42382 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42384 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42392 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42393 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42394 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42397 var uid = namedMatch ? namedMatch[1] : id;
42401 id: namedMatch ? namedMatch[1] : id,
42408 s = s.replace(m[0], '');
42410 s = s.replace(m[0], '{xtpl'+ id + '}');
42415 for(var i = tpls.length-1; i >= 0; --i){
42416 this.compileTpl(tpls[i]);
42417 this.tpls[tpls[i].id] = tpls[i];
42419 this.master = tpls[tpls.length-1];
42423 * same as applyTemplate, except it's done to one of the subTemplates
42424 * when using named templates, you can do:
42426 * var str = pl.applySubTemplate('your-name', values);
42429 * @param {Number} id of the template
42430 * @param {Object} values to apply to template
42431 * @param {Object} parent (normaly the instance of this object)
42433 applySubTemplate : function(id, values, parent)
42437 var t = this.tpls[id];
42441 if(t.test && !t.test.call(this, values, parent)){
42445 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42446 Roo.log(e.toString());
42452 if(t.exec && t.exec.call(this, values, parent)){
42456 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42457 Roo.log(e.toString());
42462 var vs = t.target ? t.target.call(this, values, parent) : values;
42463 parent = t.target ? values : parent;
42464 if(t.target && vs instanceof Array){
42466 for(var i = 0, len = vs.length; i < len; i++){
42467 buf[buf.length] = t.compiled.call(this, vs[i], parent);
42469 return buf.join('');
42471 return t.compiled.call(this, vs, parent);
42473 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42474 Roo.log(e.toString());
42475 Roo.log(t.compiled);
42480 compileTpl : function(tpl)
42482 var fm = Roo.util.Format;
42483 var useF = this.disableFormats !== true;
42484 var sep = Roo.isGecko ? "+" : ",";
42485 var undef = function(str) {
42486 Roo.log("Property not found :" + str);
42490 var fn = function(m, name, format, args)
42492 //Roo.log(arguments);
42493 args = args ? args.replace(/\\'/g,"'") : args;
42494 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42495 if (typeof(format) == 'undefined') {
42496 format= 'htmlEncode';
42498 if (format == 'raw' ) {
42502 if(name.substr(0, 4) == 'xtpl'){
42503 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42506 // build an array of options to determine if value is undefined..
42508 // basically get 'xxxx.yyyy' then do
42509 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42510 // (function () { Roo.log("Property not found"); return ''; })() :
42515 Roo.each(name.split('.'), function(st) {
42516 lookfor += (lookfor.length ? '.': '') + st;
42517 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
42520 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42523 if(format && useF){
42525 args = args ? ',' + args : "";
42527 if(format.substr(0, 5) != "this."){
42528 format = "fm." + format + '(';
42530 format = 'this.call("'+ format.substr(5) + '", ';
42534 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
42538 // called with xxyx.yuu:(test,test)
42540 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
42542 // raw.. - :raw modifier..
42543 return "'"+ sep + udef_st + name + ")"+sep+"'";
42547 // branched to use + in gecko and [].join() in others
42549 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
42550 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42553 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
42554 body.push(tpl.body.replace(/(\r\n|\n)/g,
42555 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42556 body.push("'].join('');};};");
42557 body = body.join('');
42560 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42562 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
42568 applyTemplate : function(values){
42569 return this.master.compiled.call(this, values, {});
42570 //var s = this.subs;
42573 apply : function(){
42574 return this.applyTemplate.apply(this, arguments);
42579 Roo.XTemplate.from = function(el){
42580 el = Roo.getDom(el);
42581 return new Roo.XTemplate(el.value || el.innerHTML);