3 * Copyright(c) 2006-2009 Ext JS, LLC
5 * http://www.extjs.com/license
10 * @class Ext.ux.grid.BufferView
11 * @extends Ext.grid.GridView
12 * A custom GridView which renders rows on an as-needed basis.
14 Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, {
16 * @cfg {Number} rowHeight
17 * The height of a row in the grid.
22 * @cfg {Number} borderHeight
23 * The combined height of border-top and border-bottom of a row.
28 * @cfg {Boolean/Number} scrollDelay
29 * The number of milliseconds before rendering rows out of the visible
30 * viewing area. Defaults to 100. Rows will render immediately with a config
36 * @cfg {Number} cacheSize
37 * The number of rows to look forward and backwards from the currently viewable
38 * area. The cache applies only to rows that have been rendered already.
43 * @cfg {Number} cleanDelay
44 * The number of milliseconds to buffer cleaning of extra rows not in the
49 initTemplates : function(){
50 Ext.ux.grid.BufferView.superclass.initTemplates.call(this);
51 var ts = this.templates;
52 // empty div to act as a place holder for a row
53 ts.rowHolder = new Ext.Template(
54 '<div class="x-grid3-row {alt}" style="{tstyle}"></div>'
56 ts.rowHolder.disableFormats = true;
57 ts.rowHolder.compile();
59 ts.rowBody = new Ext.Template(
60 '<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
61 '<tbody><tr>{cells}</tr>',
62 (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
65 ts.rowBody.disableFormats = true;
69 getStyleRowHeight : function(){
70 return Ext.isBorderBox ? (this.rowHeight + this.borderHeight) : this.rowHeight;
73 getCalculatedRowHeight : function(){
74 return this.rowHeight + this.borderHeight;
77 getVisibleRowCount : function(){
78 var rh = this.getCalculatedRowHeight();
79 var visibleHeight = this.scroller.dom.clientHeight;
80 return (visibleHeight < 1) ? 0 : Math.ceil(visibleHeight / rh);
83 getVisibleRows: function(){
84 var count = this.getVisibleRowCount();
85 var sc = this.scroller.dom.scrollTop;
86 var start = (sc == 0 ? 0 : Math.floor(sc/this.getCalculatedRowHeight())-1);
88 first: Math.max(start, 0),
89 last: Math.min(start + count + 2, this.ds.getCount()-1)
93 doRender : function(cs, rs, ds, startRow, colCount, stripe, onlyBody){
94 var ts = this.templates, ct = ts.cell, rt = ts.row, rb = ts.rowBody, last = colCount-1;
95 var rh = this.getStyleRowHeight();
96 var vr = this.getVisibleRows();
97 var tstyle = 'width:'+this.getTotalWidth()+';height:'+rh+'px;';
99 var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
100 for (var j = 0, len = rs.length; j < len; j++) {
102 var rowIndex = (j+startRow);
103 var visible = rowIndex >= vr.first && rowIndex <= vr.last;
105 for (var i = 0; i < colCount; i++) {
108 p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
109 p.attr = p.cellAttr = "";
110 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
112 if (p.value == undefined || p.value === "") {
115 if (r.dirty && typeof r.modified[c.name] !== 'undefined') {
116 p.css += ' x-grid3-dirty-cell';
118 cb[cb.length] = ct.apply(p);
122 if(stripe && ((rowIndex+1) % 2 == 0)){
123 alt[0] = "x-grid3-row-alt";
126 alt[1] = " x-grid3-dirty-row";
129 if(this.getRowClass){
130 alt[2] = this.getRowClass(r, rowIndex, rp, ds);
132 rp.alt = alt.join(" ");
133 rp.cells = cb.join("");
134 buf[buf.length] = !visible ? ts.rowHolder.apply(rp) : (onlyBody ? rb.apply(rp) : rt.apply(rp));
139 isRowRendered: function(index){
140 var row = this.getRow(index);
141 return row && row.childNodes.length > 0;
144 syncScroll: function(){
145 Ext.ux.grid.BufferView.superclass.syncScroll.apply(this, arguments);
149 // a (optionally) buffered method to update contents of gridview
151 if (this.scrollDelay) {
152 if (!this.renderTask) {
153 this.renderTask = new Ext.util.DelayedTask(this.doUpdate, this);
155 this.renderTask.delay(this.scrollDelay);
161 doUpdate: function(){
162 if (this.getVisibleRowCount() > 0) {
163 var g = this.grid, cm = g.colModel, ds = g.store;
164 var cs = this.getColumnData();
166 var vr = this.getVisibleRows();
167 for (var i = vr.first; i <= vr.last; i++) {
168 // if row is NOT rendered and is visible, render it
169 if(!this.isRowRendered(i)){
170 var html = this.doRender(cs, [ds.getAt(i)], ds, i, cm.getColumnCount(), g.stripeRows, true);
171 this.getRow(i).innerHTML = html;
178 // a buffered method to clean rows
181 this.cleanTask = new Ext.util.DelayedTask(this.doClean, this);
183 this.cleanTask.delay(this.cleanDelay);
187 if (this.getVisibleRowCount() > 0) {
188 var vr = this.getVisibleRows();
189 vr.first -= this.cacheSize;
190 vr.last += this.cacheSize;
192 var i = 0, rows = this.getRows();
193 // if first is less than 0, all rows have been rendered
194 // so lets clean the end...
198 for(var len = this.ds.getCount(); i < len; i++){
199 // if current row is outside of first and last and
200 // has content, update the innerHTML to nothing
201 if ((i < vr.first || i > vr.last) && rows[i].innerHTML) {
202 rows[i].innerHTML = '';
209 Ext.ux.grid.BufferView.superclass.layout.call(this);
213 // We are adding these custom layouts to a namespace that does not
214 // exist by default in Ext, so we have to add the namespace first:
215 Ext.ns('Ext.ux.layout');
218 * @class Ext.ux.layout.CenterLayout
219 * @extends Ext.layout.FitLayout
220 * <p>This is a very simple layout style used to center contents within a container. This layout works within
221 * nested containers and can also be used as expected as a Viewport layout to center the page layout.</p>
222 * <p>As a subclass of FitLayout, CenterLayout expects to have a single child panel of the container that uses
223 * the layout. The layout does not require any config options, although the child panel contained within the
224 * layout must provide a fixed or percentage width. The child panel's height will fit to the container by
225 * default, but you can specify <tt>autoHeight:true</tt> to allow it to autosize based on its content height.
228 // The content panel is centered in the container
229 var p = new Ext.Panel({
230 title: 'Center Layout',
233 title: 'Centered Content',
239 // If you leave the title blank and specify no border
240 // you'll create a non-visual, structural panel just
241 // for centering the contents in the main container.
242 var p = new Ext.Panel({
246 title: 'Centered Content',
254 Ext.ux.layout.CenterLayout = Ext.extend(Ext.layout.FitLayout, {
256 setItemSize : function(item, size){
257 this.container.addClass('ux-layout-center');
258 item.addClass('ux-layout-center-item');
259 if(item && size.height > 0){
261 size.width = item.width;
268 Ext.Container.LAYOUTS['ux.center'] = Ext.ux.layout.CenterLayout;
269 Ext.ns('Ext.ux.grid');
\r
272 * @class Ext.ux.grid.CheckColumn
\r
274 * GridPanel plugin to add a column with check boxes to a grid.
\r
275 * <p>Example usage:</p>
\r
277 // create the column
\r
278 var checkColumn = new Ext.grid.CheckColumn({
\r
280 dataIndex: 'indoor',
\r
285 // add the column to the column model
\r
286 var cm = new Ext.grid.ColumnModel([{
\r
294 var grid = new Ext.grid.EditorGridPanel({
\r
297 plugins: [checkColumn], // include plugin
\r
301 * In addition to storing a Boolean value within the record data, this
\r
302 * class toggles a css class between <tt>'x-grid3-check-col'</tt> and
\r
303 * <tt>'x-grid3-check-col-on'</tt> to alter the background image used for
\r
306 Ext.ux.grid.CheckColumn = function(config){
\r
307 Ext.apply(this, config);
\r
309 this.id = Ext.id();
\r
311 this.renderer = this.renderer.createDelegate(this);
\r
314 Ext.ux.grid.CheckColumn.prototype ={
\r
315 init : function(grid){
\r
317 this.grid.on('render', function(){
\r
318 var view = this.grid.getView();
\r
319 view.mainBody.on('mousedown', this.onMouseDown, this);
\r
323 onMouseDown : function(e, t){
\r
324 if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){
\r
326 var index = this.grid.getView().findRowIndex(t);
\r
327 var record = this.grid.store.getAt(index);
\r
328 record.set(this.dataIndex, !record.data[this.dataIndex]);
\r
332 renderer : function(v, p, record){
\r
333 p.css += ' x-grid3-check-col-td';
\r
334 return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'"> </div>';
\r
339 Ext.preg('checkcolumn', Ext.ux.grid.CheckColumn);
\r
341 // backwards compat
\r
342 Ext.grid.CheckColumn = Ext.ux.grid.CheckColumn;Ext.ns('Ext.ux.tree');
\r
345 * @class Ext.ux.tree.ColumnTree
\r
346 * @extends Ext.tree.TreePanel
\r
348 * @xtype columntree
\r
350 Ext.ux.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, {
\r
352 borderWidth : Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
\r
353 cls : 'x-column-tree',
\r
355 onRender : function(){
\r
356 Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments);
\r
357 this.headers = this.header.createChild({cls:'x-tree-headers'});
\r
359 var cols = this.columns, c;
\r
360 var totalWidth = 0;
\r
361 var scrollOffset = 19; // similar to Ext.grid.GridView default
\r
363 for(var i = 0, len = cols.length; i < len; i++){
\r
365 totalWidth += c.width;
\r
366 this.headers.createChild({
\r
367 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
\r
369 cls:'x-tree-hd-text',
\r
372 style:'width:'+(c.width-this.borderWidth)+'px;'
\r
375 this.headers.createChild({cls:'x-clear'});
\r
376 // prevent floats from wrapping when clipped
\r
377 this.headers.setWidth(totalWidth+scrollOffset);
\r
378 this.innerCt.setWidth(totalWidth);
\r
382 Ext.reg('columntree', Ext.ux.tree.ColumnTree);
\r
385 Ext.tree.ColumnTree = Ext.ux.tree.ColumnTree;
\r
389 * @class Ext.ux.tree.ColumnNodeUI
\r
390 * @extends Ext.tree.TreeNodeUI
\r
392 Ext.ux.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
\r
393 focus: Ext.emptyFn, // prevent odd scrolling behavior
\r
395 renderElements : function(n, a, targetNode, bulkRender){
\r
396 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
\r
398 var t = n.getOwnerTree();
\r
399 var cols = t.columns;
\r
400 var bw = t.borderWidth;
\r
404 '<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf ', a.cls,'">',
\r
405 '<div class="x-tree-col" style="width:',c.width-bw,'px;">',
\r
406 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
\r
407 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">',
\r
408 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on">',
\r
409 '<a hidefocus="on" class="x-tree-node-anchor" href="',a.href ? a.href : "#",'" tabIndex="1" ',
\r
410 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>',
\r
411 '<span unselectable="on">', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</span></a>",
\r
413 for(var i = 1, len = cols.length; i < len; i++){
\r
416 buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
\r
417 '<div class="x-tree-col-text">',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</div>",
\r
421 '<div class="x-clear"></div></div>',
\r
422 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
\r
425 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
\r
426 this.wrap = Ext.DomHelper.insertHtml("beforeBegin",
\r
427 n.nextSibling.ui.getEl(), buf.join(""));
\r
429 this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
\r
432 this.elNode = this.wrap.childNodes[0];
\r
433 this.ctNode = this.wrap.childNodes[1];
\r
434 var cs = this.elNode.firstChild.childNodes;
\r
435 this.indentNode = cs[0];
\r
436 this.ecNode = cs[1];
\r
437 this.iconNode = cs[2];
\r
438 this.anchor = cs[3];
\r
439 this.textNode = cs[3].firstChild;
\r
444 Ext.tree.ColumnNodeUI = Ext.ux.tree.ColumnNodeUI;
\r
446 * @class Ext.DataView.LabelEditor
\r
447 * @extends Ext.Editor
\r
450 Ext.DataView.LabelEditor = Ext.extend(Ext.Editor, {
\r
451 alignment: "tl-tl",
\r
453 cls: "x-small-editor",
\r
455 completeOnEnter: true,
\r
457 labelSelector: 'span.x-editable',
\r
459 constructor: function(cfg, field){
\r
460 Ext.DataView.LabelEditor.superclass.constructor.call(this,
\r
461 field || new Ext.form.TextField({
\r
471 init : function(view){
\r
473 view.on('render', this.initEditor, this);
\r
474 this.on('complete', this.onSave, this);
\r
477 initEditor : function(){
\r
480 containerclick: this.doBlur,
\r
483 this.view.getEl().on('mousedown', this.onMouseDown, this, {delegate: this.labelSelector});
\r
486 doBlur: function(){
\r
492 onMouseDown : function(e, target){
\r
493 if(!e.ctrlKey && !e.shiftKey){
\r
494 var item = this.view.findItemFromChild(target);
\r
496 var record = this.view.store.getAt(this.view.indexOf(item));
\r
497 this.startEdit(target, record.data[this.dataIndex]);
\r
498 this.activeRecord = record;
\r
500 e.preventDefault();
\r
504 onSave : function(ed, value){
\r
505 this.activeRecord.set(this.dataIndex, value);
\r
510 Ext.DataView.DragSelector = function(cfg){
\r
512 var view, proxy, tracker;
\r
513 var rs, bodyRegion, dragRegion = new Ext.lib.Region(0,0,0,0);
\r
514 var dragSafe = cfg.dragSafe === true;
\r
516 this.init = function(dataView){
\r
518 view.on('render', onRender);
\r
521 function fillRegions(){
\r
523 view.all.each(function(el){
\r
524 rs[rs.length] = el.getRegion();
\r
526 bodyRegion = view.el.getRegion();
\r
529 function cancelClick(){
\r
533 function onBeforeStart(e){
\r
534 return !dragSafe || e.target == view.el.dom;
\r
537 function onStart(e){
\r
538 view.on('containerclick', cancelClick, view, {single:true});
\r
540 proxy = view.el.createChild({cls:'x-view-selector'});
\r
542 proxy.setDisplayed('block');
\r
545 view.clearSelections();
\r
548 function onDrag(e){
\r
549 var startXY = tracker.startXY;
\r
550 var xy = tracker.getXY();
\r
552 var x = Math.min(startXY[0], xy[0]);
\r
553 var y = Math.min(startXY[1], xy[1]);
\r
554 var w = Math.abs(startXY[0] - xy[0]);
\r
555 var h = Math.abs(startXY[1] - xy[1]);
\r
557 dragRegion.left = x;
\r
558 dragRegion.top = y;
\r
559 dragRegion.right = x+w;
\r
560 dragRegion.bottom = y+h;
\r
562 dragRegion.constrainTo(bodyRegion);
\r
563 proxy.setRegion(dragRegion);
\r
565 for(var i = 0, len = rs.length; i < len; i++){
\r
566 var r = rs[i], sel = dragRegion.intersect(r);
\r
567 if(sel && !r.selected){
\r
569 view.select(i, true);
\r
570 }else if(!sel && r.selected){
\r
571 r.selected = false;
\r
579 view.un('containerclick', cancelClick, view);
\r
582 proxy.setDisplayed(false);
\r
586 function onRender(view){
\r
587 tracker = new Ext.dd.DragTracker({
\r
588 onBeforeStart: onBeforeStart,
\r
593 tracker.initEl(view.el);
\r
595 };Ext.ns('Ext.ux.form');
598 * @class Ext.ux.form.FileUploadField
599 * @extends Ext.form.TextField
600 * Creates a file upload field.
601 * @xtype fileuploadfield
603 Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, {
605 * @cfg {String} buttonText The button text to display on the upload button (defaults to
606 * 'Browse...'). Note that if you supply a value for {@link #buttonCfg}, the buttonCfg.text
607 * value will be used instead if available.
609 buttonText: 'Browse...',
611 * @cfg {Boolean} buttonOnly True to display the file upload field as a button with no visible
612 * text field (defaults to false). If true, all inherited TextField members will still be available.
616 * @cfg {Number} buttonOffset The number of pixels of space reserved between the button and the text field
617 * (defaults to 3). Note that this only applies if {@link #buttonOnly} = false.
621 * @cfg {Object} buttonCfg A standard {@link Ext.Button} config object.
631 autoSize: Ext.emptyFn,
634 initComponent: function(){
635 Ext.ux.form.FileUploadField.superclass.initComponent.call(this);
639 * @event fileselected
640 * Fires when the underlying file input field's value has changed from the user
641 * selecting a new file from the system file selection dialog.
642 * @param {Ext.ux.form.FileUploadField} this
643 * @param {String} value The file value returned by the underlying file input field
650 onRender : function(ct, position){
651 Ext.ux.form.FileUploadField.superclass.onRender.call(this, ct, position);
653 this.wrap = this.el.wrap({cls:'x-form-field-wrap x-form-file-wrap'});
654 this.el.addClass('x-form-file-text');
655 this.el.dom.removeAttribute('name');
657 this.fileInput = this.wrap.createChild({
658 id: this.getFileInputId(),
659 name: this.name||this.getId(),
666 var btnCfg = Ext.applyIf(this.buttonCfg || {}, {
667 text: this.buttonText
669 this.button = new Ext.Button(Ext.apply(btnCfg, {
671 cls: 'x-form-file-btn' + (btnCfg.iconCls ? ' x-btn-icon' : '')
676 this.wrap.setWidth(this.button.getEl().getWidth());
679 this.fileInput.on('change', function(){
680 var v = this.fileInput.dom.value;
682 this.fireEvent('fileselected', this, v);
687 getFileInputId: function(){
688 return this.id + '-file';
692 onResize : function(w, h){
693 Ext.ux.form.FileUploadField.superclass.onResize.call(this, w, h);
695 this.wrap.setWidth(w);
697 if(!this.buttonOnly){
698 var w = this.wrap.getWidth() - this.button.getEl().getWidth() - this.buttonOffset;
704 onDestroy: function(){
705 Ext.ux.form.FileUploadField.superclass.onDestroy.call(this);
706 Ext.destroy(this.fileInput, this.button, this.wrap);
711 preFocus : Ext.emptyFn,
714 getResizeEl : function(){
719 getPositionEl : function(){
724 alignErrorIcon : function(){
725 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
730 Ext.reg('fileuploadfield', Ext.ux.form.FileUploadField);
733 Ext.form.FileUploadField = Ext.ux.form.FileUploadField;
735 Ext.ns('Ext.a11y');
\r
737 Ext.a11y.Frame = Ext.extend(Object, {
\r
738 initialized: false,
\r
740 constructor: function(size, color){
\r
741 this.setSize(size || 1);
\r
742 this.setColor(color || '15428B');
\r
746 if (!this.initialized) {
\r
751 this.ct = Ext.DomHelper.append(document.body, {
\r
752 cls: 'x-a11y-focusframe'
\r
755 for (i = 0; i < 4; i++) {
\r
756 s = Ext.DomHelper.append(this.ct, {
\r
757 cls: 'x-a11y-focusframe-side',
\r
758 style: 'background-color: #' + this.color
\r
760 s.visibilityMode = Ext.Element.DISPLAY;
\r
761 this.sides.push(s);
\r
764 this.frameTask = new Ext.util.DelayedTask(function(el){
\r
765 var newEl = Ext.get(el);
\r
766 if (newEl != this.curEl) {
\r
767 var w = newEl.getWidth();
\r
768 var h = newEl.getHeight();
\r
769 this.sides[0].show().setSize(w, this.size).anchorTo(el, 'tl', [0, -1]);
\r
770 this.sides[2].show().setSize(w, this.size).anchorTo(el, 'bl', [0, -1]);
\r
771 this.sides[1].show().setSize(this.size, h).anchorTo(el, 'tr', [-1, 0]);
\r
772 this.sides[3].show().setSize(this.size, h).anchorTo(el, 'tl', [-1, 0]);
\r
773 this.curEl = newEl;
\r
777 this.unframeTask = new Ext.util.DelayedTask(function(){
\r
778 if (this.initialized) {
\r
779 this.sides[0].hide();
\r
780 this.sides[1].hide();
\r
781 this.sides[2].hide();
\r
782 this.sides[3].hide();
\r
786 this.initialized = true;
\r
790 frame: function(el){
\r
792 this.unframeTask.cancel();
\r
793 this.frameTask.delay(2, false, false, [el]);
\r
796 unframe: function(){
\r
798 this.unframeTask.delay(2);
\r
801 setSize: function(size){
\r
805 setColor: function(color){
\r
806 this.color = color;
\r
810 Ext.a11y.FocusFrame = new Ext.a11y.Frame(2, '15428B');
\r
811 Ext.a11y.RelayFrame = new Ext.a11y.Frame(1, '6B8CBF');
\r
813 Ext.a11y.Focusable = Ext.extend(Ext.util.Observable, {
\r
814 constructor: function(el, relayTo, noFrame, frameEl){
\r
815 Ext.a11y.Focusable.superclass.constructor.call(this);
\r
817 this.addEvents('focus', 'blur', 'left', 'right', 'up', 'down', 'esc', 'enter', 'space');
\r
819 if (el instanceof Ext.Component) {
\r
821 this.setComponent(el);
\r
824 this.el = Ext.get(el);
\r
825 this.setComponent(null);
\r
828 this.setRelayTo(relayTo)
\r
829 this.setNoFrame(noFrame);
\r
830 this.setFrameEl(frameEl);
\r
834 Ext.a11y.FocusMgr.register(this);
\r
838 this.el.dom.tabIndex = '1';
\r
839 this.el.addClass('x-a11y-focusable');
\r
841 focus: this.onFocus,
\r
843 keydown: this.onKeyDown,
\r
848 setRelayTo: function(relayTo){
\r
849 this.relayTo = relayTo ? Ext.a11y.FocusMgr.get(relayTo) : null;
\r
852 setNoFrame: function(noFrame){
\r
853 this.noFrame = (noFrame === true) ? true : false;
\r
856 setFrameEl: function(frameEl){
\r
857 this.frameEl = frameEl && Ext.get(frameEl) || this.el;
\r
860 setComponent: function(cmp){
\r
861 this.component = cmp || null;
\r
864 onKeyDown: function(e, t){
\r
865 var k = e.getKey(), SK = Ext.a11y.Focusable.SpecialKeys, ret, tf;
\r
867 tf = (t !== this.el.dom) ? Ext.a11y.FocusMgr.get(t, true) : this;
\r
869 // this can happen when you are on a focused item within a panel body
\r
870 // that is not a Ext.a11y.Focusable
\r
871 tf = Ext.a11y.FocusMgr.get(Ext.fly(t).parent('.x-a11y-focusable'));
\r
874 if (SK[k] !== undefined) {
\r
875 ret = this.fireEvent(SK[k], e, t, tf, this);
\r
877 if (ret === false || this.fireEvent('keydown', e, t, tf, this) === false) {
\r
883 this.el.dom.focus();
\r
887 this.el.dom.blur();
\r
890 onFocus: function(e, t){
\r
891 this.el.addClass('x-a11y-focused');
\r
892 if (this.relayTo) {
\r
893 this.relayTo.el.addClass('x-a11y-focused-relay');
\r
894 if (!this.relayTo.noFrame) {
\r
895 Ext.a11y.FocusFrame.frame(this.relayTo.frameEl);
\r
897 if (!this.noFrame) {
\r
898 Ext.a11y.RelayFrame.frame(this.frameEl);
\r
902 if (!this.noFrame) {
\r
903 Ext.a11y.FocusFrame.frame(this.frameEl);
\r
907 this.fireEvent('focus', e, t, this);
\r
910 onBlur: function(e, t){
\r
911 if (this.relayTo) {
\r
912 this.relayTo.el.removeClass('x-a11y-focused-relay');
\r
913 Ext.a11y.RelayFrame.unframe();
\r
915 this.el.removeClass('x-a11y-focused');
\r
916 Ext.a11y.FocusFrame.unframe();
\r
917 this.fireEvent('blur', e, t, this);
\r
920 destroy: function(){
\r
921 this.el.un('keydown', this.onKeyDown);
\r
922 this.el.un('focus', this.onFocus);
\r
923 this.el.un('blur', this.onBlur);
\r
924 this.el.removeClass('x-a11y-focusable');
\r
925 this.el.removeClass('x-a11y-focused');
\r
926 if (this.relayTo) {
\r
927 this.relayTo.el.removeClass('x-a11y-focused-relay');
\r
932 Ext.a11y.FocusItem = Ext.extend(Object, {
\r
933 constructor: function(el, enableTabbing){
\r
934 Ext.a11y.FocusItem.superclass.constructor.call(this);
\r
936 this.el = Ext.get(el);
\r
937 this.fi = new Ext.a11y.Focusable(el);
\r
938 this.fi.setComponent(this);
\r
940 this.fi.on('tab', this.onTab, this);
\r
942 this.enableTabbing = enableTabbing === true ? true : false;
\r
945 getEnterItem: function(){
\r
946 if (this.enableTabbing) {
\r
947 var items = this.getFocusItems();
\r
948 if (items && items.length) {
\r
954 getFocusItems: function(){
\r
955 if (this.enableTabbing) {
\r
956 return this.el.query('a, button, input, select');
\r
961 onTab: function(e, t){
\r
962 var items = this.getFocusItems(), i;
\r
964 if (items && items.length && (i = items.indexOf(t)) !== -1) {
\r
965 if (e.shiftKey && i > 0) {
\r
967 items[i - 1].focus();
\r
968 Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);
\r
972 if (!e.shiftKey && i < items.length - 1) {
\r
974 items[i + 1].focus();
\r
975 Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);
\r
982 if (this.enableTabbing) {
\r
983 var items = this.getFocusItems();
\r
984 if (items && items.length) {
\r
986 Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);
\r
998 Ext.a11y.FocusMgr = function(){
\r
999 var all = new Ext.util.MixedCollection();
\r
1002 register: function(f){
\r
1003 all.add(f.el && Ext.id(f.el), f);
\r
1006 unregister: function(f){
\r
1010 get: function(el, noCreate){
\r
1011 return all.get(Ext.id(el)) || (noCreate ? false : new Ext.a11y.Focusable(el));
\r
1018 Ext.a11y.Focusable.SpecialKeys = {};
\r
1019 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.LEFT] = 'left';
\r
1020 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.RIGHT] = 'right';
\r
1021 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.DOWN] = 'down';
\r
1022 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.UP] = 'up';
\r
1023 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ESC] = 'esc';
\r
1024 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ENTER] = 'enter';
\r
1025 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.SPACE] = 'space';
\r
1026 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.TAB] = 'tab';
\r
1028 // we use the new observeClass method to fire our new initFocus method on components
\r
1029 Ext.util.Observable.observeClass(Ext.Component);
\r
1030 Ext.Component.on('render', function(cmp){
\r
1034 Ext.override(Ext.Component, {
\r
1035 initFocus: Ext.emptyFn,
\r
1036 initARIA: Ext.emptyFn
\r
1039 Ext.override(Ext.Container, {
\r
1040 isFocusable: true,
\r
1044 initFocus: function(){
\r
1045 if (!this.fi && !this.noFocus) {
\r
1046 this.fi = new Ext.a11y.Focusable(this);
\r
1048 this.mon(this.fi, {
\r
1049 focus: this.onFocus,
\r
1050 blur: this.onBlur,
\r
1052 enter: this.onEnter,
\r
1057 if (this.hidden) {
\r
1058 this.isFocusable = false;
\r
1061 this.on('show', function(){
\r
1062 this.isFocusable = true;
\r
1064 this.on('hide', function(){
\r
1065 this.isFocusable = false;
\r
1069 focus: function(){
\r
1077 enter: function(){
\r
1078 var eitem = this.getEnterItem();
\r
1084 onFocus: Ext.emptyFn,
\r
1085 onBlur: Ext.emptyFn,
\r
1087 onTab: function(e, t, tf){
\r
1088 var rf = tf.relayTo || tf;
\r
1089 if (rf.component && rf.component !== this) {
\r
1091 var item = e.shiftKey ? this.getPreviousFocus(rf.component) : this.getNextFocus(rf.component);
\r
1096 onEnter: function(e, t, tf){
\r
1097 // check to see if enter is pressed while "on" the panel
\r
1098 if (tf.component && tf.component === this) {
\r
1102 e.stopPropagation();
\r
1105 onEsc: function(e, t){
\r
1106 e.preventDefault();
\r
1108 // check to see if esc is pressed while "inside" the panel
\r
1109 // or while "on" the panel
\r
1110 if (t === this.el.dom) {
\r
1111 // "on" the panel, check if this panel has an owner panel and focus that
\r
1112 // we dont stop the event in this case so that this same check will be
\r
1113 // done for this ownerCt
\r
1114 if (this.ownerCt) {
\r
1115 this.ownerCt.focus();
\r
1119 // we were inside the panel when esc was pressed,
\r
1120 // so go back "on" the panel
\r
1121 if (this.ownerCt && this.ownerCt.isFocusable) {
\r
1122 var si = this.ownerCt.getFocusItems();
\r
1124 if (si && si.getCount() > 1) {
\r
1132 getFocusItems: function(){
\r
1133 return this.items &&
\r
1134 this.items.filterBy(function(o){
\r
1135 return o.isFocusable;
\r
1140 getEnterItem: function(){
\r
1141 var ci = this.getFocusItems(), length = ci ? ci.getCount() : 0;
\r
1143 if (length === 1) {
\r
1144 return ci.first().getEnterItem && ci.first().getEnterItem() || ci.first();
\r
1148 return ci.first();
\r
1152 getNextFocus: function(current){
\r
1153 var items = this.getFocusItems(), next = current, i = items.indexOf(current), length = items.getCount();
\r
1155 if (i === length - 1) {
\r
1156 next = items.first();
\r
1159 next = items.get(i + 1);
\r
1164 getPreviousFocus: function(current){
\r
1165 var items = this.getFocusItems(), prev = current, i = items.indexOf(current), length = items.getCount();
\r
1168 prev = items.last();
\r
1171 prev = items.get(i - 1);
\r
1176 getFocusable : function() {
\r
1181 Ext.override(Ext.Panel, {
\r
1183 * @cfg {Boolean} enableTabbing <tt>true</tt> to enable tabbing. Default is <tt>false</tt>.
\r
1185 getFocusItems: function(){
\r
1186 // items gets all the items inside the body
\r
1187 var items = Ext.Panel.superclass.getFocusItems.call(this), bodyFocus = null;
\r
1190 items = new Ext.util.MixedCollection();
\r
1191 this.bodyFocus = this.bodyFocus || new Ext.a11y.FocusItem(this.body, this.enableTabbing);
\r
1192 items.add('body', this.bodyFocus);
\r
1194 // but panels can also have tbar, bbar, fbar
\r
1195 if (this.tbar && this.topToolbar) {
\r
1196 items.insert(0, this.topToolbar);
\r
1198 if (this.bbar && this.bottomToolbar) {
\r
1199 items.add(this.bottomToolbar);
\r
1202 items.add(this.fbar);
\r
1209 Ext.override(Ext.TabPanel, {
\r
1211 initFocus: function(){
\r
1212 Ext.TabPanel.superclass.initFocus.call(this);
\r
1213 this.mon(this.fi, {
\r
1214 left: this.onLeft,
\r
1215 right: this.onRight,
\r
1220 onLeft: function(e){
\r
1221 if (!this.activeTab) {
\r
1225 var prev = this.items.itemAt(this.items.indexOf(this.activeTab) - 1);
\r
1227 this.setActiveTab(prev);
\r
1232 onRight: function(e){
\r
1233 if (!this.activeTab) {
\r
1237 var next = this.items.itemAt(this.items.indexOf(this.activeTab) + 1);
\r
1239 this.setActiveTab(next);
\r
1245 Ext.override(Ext.tree.TreeNodeUI, {
\r
1247 focus: function(){
\r
1248 this.node.getOwnerTree().bodyFocus.focus();
\r
1252 Ext.override(Ext.tree.TreePanel, {
\r
1254 afterRender : function(){
\r
1255 Ext.tree.TreePanel.superclass.afterRender.call(this);
\r
1256 this.root.render();
\r
1257 if(!this.rootVisible){
\r
1258 this.root.renderChildren();
\r
1260 this.bodyFocus = new Ext.a11y.FocusItem(this.body.down('.x-tree-root-ct'));
\r
1261 this.bodyFocus.fi.setFrameEl(this.body);
\r
1265 Ext.override(Ext.grid.GridPanel, {
\r
1266 initFocus: function(){
\r
1267 Ext.grid.GridPanel.superclass.initFocus.call(this);
\r
1268 this.bodyFocus = new Ext.a11y.FocusItem(this.view.focusEl);
\r
1269 this.bodyFocus.fi.setFrameEl(this.body);
\r
1273 Ext.override(Ext.Button, {
\r
1274 isFocusable: true,
\r
1277 initFocus: function(){
\r
1278 Ext.Button.superclass.initFocus.call(this);
\r
1279 this.fi = this.fi || new Ext.a11y.Focusable(this.btnEl, null, null, this.el);
\r
1280 this.fi.setComponent(this);
\r
1282 this.mon(this.fi, {
\r
1283 focus: this.onFocus,
\r
1284 blur: this.onBlur,
\r
1289 this.mon(this.fi, 'down', this.showMenu, this);
\r
1290 this.on('menuhide', this.focus, this);
\r
1293 if (this.hidden) {
\r
1294 this.isFocusable = false;
\r
1297 this.on('show', function(){
\r
1298 this.isFocusable = true;
\r
1300 this.on('hide', function(){
\r
1301 this.isFocusable = false;
\r
1305 focus: function(){
\r
1313 onFocus: function(){
\r
1314 if (!this.disabled) {
\r
1315 this.el.addClass("x-btn-focus");
\r
1319 onBlur: function(){
\r
1320 this.el.removeClass("x-btn-focus");
\r
1324 Ext.override(Ext.Toolbar, {
\r
1325 initFocus: function(){
\r
1326 Ext.Toolbar.superclass.initFocus.call(this);
\r
1327 this.mon(this.fi, {
\r
1328 left: this.onLeft,
\r
1329 right: this.onRight,
\r
1333 this.on('focus', this.onButtonFocus, this, {
\r
1338 addItem: function(item){
\r
1339 Ext.Toolbar.superclass.add.apply(this, arguments);
\r
1340 if (item.rendered && item.fi !== undefined) {
\r
1341 item.fi.setRelayTo(this.el);
\r
1342 this.relayEvents(item.fi, ['focus']);
\r
1345 item.on('render', function(){
\r
1346 if (item.fi !== undefined) {
\r
1347 item.fi.setRelayTo(this.el);
\r
1348 this.relayEvents(item.fi, ['focus']);
\r
1357 onFocus: function(){
\r
1358 var items = this.getFocusItems();
\r
1359 if (items && items.getCount() > 0) {
\r
1360 if (this.lastFocus && items.indexOf(this.lastFocus) !== -1) {
\r
1361 this.lastFocus.focus();
\r
1364 items.first().focus();
\r
1369 onButtonFocus: function(e, t, tf){
\r
1370 this.lastFocus = tf.component || null;
\r
1373 onLeft: function(e, t, tf){
\r
1375 this.getPreviousFocus(tf.component).focus();
\r
1378 onRight: function(e, t, tf){
\r
1380 this.getNextFocus(tf.component).focus();
\r
1383 getEnterItem: Ext.emptyFn,
\r
1384 onTab: Ext.emptyFn,
\r
1385 onEsc: Ext.emptyFn
\r
1388 Ext.override(Ext.menu.BaseItem, {
\r
1389 initFocus: function(){
\r
1390 this.fi = new Ext.a11y.Focusable(this, this.parentMenu && this.parentMenu.el || null, true);
\r
1394 Ext.override(Ext.menu.Menu, {
\r
1395 initFocus: function(){
\r
1396 this.fi = new Ext.a11y.Focusable(this);
\r
1397 this.focusEl = this.fi;
\r
1401 Ext.a11y.WindowMgr = new Ext.WindowGroup();
\r
1403 Ext.apply(Ext.WindowMgr, {
\r
1404 bringToFront: function(win){
\r
1405 Ext.a11y.WindowMgr.bringToFront.call(this, win);
\r
1415 Ext.override(Ext.Window, {
\r
1416 initFocus: function(){
\r
1417 Ext.Window.superclass.initFocus.call(this);
\r
1418 this.on('beforehide', function(){
\r
1419 Ext.a11y.RelayFrame.unframe();
\r
1420 Ext.a11y.FocusFrame.unframe();
\r
1425 Ext.override(Ext.form.Field, {
\r
1426 isFocusable: true,
\r
1429 initFocus: function(){
\r
1430 this.fi = this.fi || new Ext.a11y.Focusable(this, null, true);
\r
1432 Ext.form.Field.superclass.initFocus.call(this);
\r
1434 if (this.hidden) {
\r
1435 this.isFocusable = false;
\r
1438 this.on('show', function(){
\r
1439 this.isFocusable = true;
\r
1441 this.on('hide', function(){
\r
1442 this.isFocusable = false;
\r
1447 Ext.override(Ext.FormPanel, {
\r
1448 initFocus: function(){
\r
1449 Ext.FormPanel.superclass.initFocus.call(this);
\r
1450 this.on('focus', this.onFieldFocus, this, {
\r
1456 createForm: function(){
\r
1457 delete this.initialConfig.listeners;
\r
1458 var form = new Ext.form.BasicForm(null, this.initialConfig);
\r
1459 form.afterMethod('add', this.formItemAdd, this);
\r
1463 formItemAdd: function(item){
\r
1464 item.on('render', function(field){
\r
1465 field.fi.setRelayTo(this.el);
\r
1466 this.relayEvents(field.fi, ['focus']);
\r
1472 onFocus: function(){
\r
1473 var items = this.getFocusItems();
\r
1474 if (items && items.getCount() > 0) {
\r
1475 if (this.lastFocus && items.indexOf(this.lastFocus) !== -1) {
\r
1476 this.lastFocus.focus();
\r
1479 items.first().focus();
\r
1484 onFieldFocus: function(e, t, tf){
\r
1485 this.lastFocus = tf.component || null;
\r
1488 onTab: function(e, t, tf){
\r
1489 if (tf.relayTo.component === this) {
\r
1490 var item = e.shiftKey ? this.getPreviousFocus(tf.component) : this.getNextFocus(tf.component);
\r
1498 Ext.FormPanel.superclass.onTab.apply(this, arguments);
\r
1501 getNextFocus: function(current){
\r
1502 var items = this.getFocusItems(), i = items.indexOf(current), length = items.getCount();
\r
1504 return (i < length - 1) ? items.get(i + 1) : false;
\r
1507 getPreviousFocus: function(current){
\r
1508 var items = this.getFocusItems(), i = items.indexOf(current), length = items.getCount();
\r
1510 return (i > 0) ? items.get(i - 1) : false;
\r
1514 Ext.override(Ext.Viewport, {
\r
1515 initFocus: function(){
\r
1516 Ext.Viewport.superclass.initFocus.apply(this);
\r
1517 this.mon(Ext.get(document), 'focus', this.focus, this);
\r
1518 this.mon(Ext.get(document), 'blur', this.blur, this);
\r
1519 this.fi.setNoFrame(true);
\r
1522 onTab: function(e, t, tf, f){
\r
1526 items = this.getFocusItems();
\r
1527 if (items && items.getCount() > 0) {
\r
1528 items.first().focus();
\r
1532 var rf = tf.relayTo || tf;
\r
1533 var item = e.shiftKey ? this.getPreviousFocus(rf.component) : this.getNextFocus(rf.component);
\r
1540 * @class Ext.ux.GMapPanel
\r
1541 * @extends Ext.Panel
\r
1542 * @author Shea Frederick
\r
1544 Ext.ux.GMapPanel = Ext.extend(Ext.Panel, {
\r
1545 initComponent : function(){
\r
1557 Ext.applyIf(this,defConfig);
\r
1559 Ext.ux.GMapPanel.superclass.initComponent.call(this);
\r
1562 afterRender : function(){
\r
1564 var wh = this.ownerCt.getSize();
\r
1565 Ext.applyIf(this, wh);
\r
1567 Ext.ux.GMapPanel.superclass.afterRender.call(this);
\r
1569 if (this.gmapType === 'map'){
\r
1570 this.gmap = new GMap2(this.body.dom);
\r
1573 if (this.gmapType === 'panorama'){
\r
1574 this.gmap = new GStreetviewPanorama(this.body.dom);
\r
1577 if (typeof this.addControl == 'object' && this.gmapType === 'map') {
\r
1578 this.gmap.addControl(this.addControl);
\r
1581 if (typeof this.setCenter === 'object') {
\r
1582 if (typeof this.setCenter.geoCodeAddr === 'string'){
\r
1583 this.geoCodeLookup(this.setCenter.geoCodeAddr);
\r
1585 if (this.gmapType === 'map'){
\r
1586 var point = new GLatLng(this.setCenter.lat,this.setCenter.lng);
\r
1587 this.gmap.setCenter(point, this.zoomLevel);
\r
1589 if (typeof this.setCenter.marker === 'object' && typeof point === 'object'){
\r
1590 this.addMarker(point,this.setCenter.marker,this.setCenter.marker.clear);
\r
1593 if (this.gmapType === 'panorama'){
\r
1594 this.gmap.setLocationAndPOV(new GLatLng(this.setCenter.lat,this.setCenter.lng), {yaw: this.yaw, pitch: this.pitch, zoom: this.zoom});
\r
1598 GEvent.bind(this.gmap, 'load', this, function(){
\r
1599 this.onMapReady();
\r
1603 onMapReady : function(){
\r
1604 this.addMarkers(this.markers);
\r
1605 this.addMapControls();
\r
1606 this.addOptions();
\r
1608 onResize : function(w, h){
\r
1610 if (typeof this.getMap() == 'object') {
\r
1611 this.gmap.checkResize();
\r
1614 Ext.ux.GMapPanel.superclass.onResize.call(this, w, h);
\r
1617 setSize : function(width, height, animate){
\r
1619 if (typeof this.getMap() == 'object') {
\r
1620 this.gmap.checkResize();
\r
1623 Ext.ux.GMapPanel.superclass.setSize.call(this, width, height, animate);
\r
1626 getMap : function(){
\r
1631 getCenter : function(){
\r
1633 return this.getMap().getCenter();
\r
1636 getCenterLatLng : function(){
\r
1638 var ll = this.getCenter();
\r
1639 return {lat: ll.lat(), lng: ll.lng()};
\r
1642 addMarkers : function(markers) {
\r
1644 if (Ext.isArray(markers)){
\r
1645 for (var i = 0; i < markers.length; i++) {
\r
1646 var mkr_point = new GLatLng(markers[i].lat,markers[i].lng);
\r
1647 this.addMarker(mkr_point,markers[i].marker,false,markers[i].setCenter, markers[i].listeners);
\r
1652 addMarker : function(point, marker, clear, center, listeners){
\r
1654 Ext.applyIf(marker,G_DEFAULT_ICON);
\r
1656 if (clear === true){
\r
1657 this.getMap().clearOverlays();
\r
1659 if (center === true) {
\r
1660 this.getMap().setCenter(point, this.zoomLevel);
\r
1663 var mark = new GMarker(point,marker);
\r
1664 if (typeof listeners === 'object'){
\r
1665 for (evt in listeners) {
\r
1666 GEvent.bind(mark, evt, this, listeners[evt]);
\r
1669 this.getMap().addOverlay(mark);
\r
1672 addMapControls : function(){
\r
1674 if (this.gmapType === 'map') {
\r
1675 if (Ext.isArray(this.mapControls)) {
\r
1676 for(i=0;i<this.mapControls.length;i++){
\r
1677 this.addMapControl(this.mapControls[i]);
\r
1679 }else if(typeof this.mapControls === 'string'){
\r
1680 this.addMapControl(this.mapControls);
\r
1681 }else if(typeof this.mapControls === 'object'){
\r
1682 this.getMap().addControl(this.mapControls);
\r
1687 addMapControl : function(mc){
\r
1689 var mcf = window[mc];
\r
1690 if (typeof mcf === 'function') {
\r
1691 this.getMap().addControl(new mcf());
\r
1695 addOptions : function(){
\r
1697 if (Ext.isArray(this.mapConfOpts)) {
\r
1699 for(i=0;i<this.mapConfOpts.length;i++){
\r
1700 this.addOption(this.mapConfOpts[i]);
\r
1702 }else if(typeof this.mapConfOpts === 'string'){
\r
1703 this.addOption(this.mapConfOpts);
\r
1707 addOption : function(mc){
\r
1709 var mcf = this.getMap()[mc];
\r
1710 if (typeof mcf === 'function') {
\r
1711 this.getMap()[mc]();
\r
1715 geoCodeLookup : function(addr) {
\r
1717 this.geocoder = new GClientGeocoder();
\r
1718 this.geocoder.getLocations(addr, this.addAddressToMap.createDelegate(this));
\r
1721 addAddressToMap : function(response) {
\r
1723 if (!response || response.Status.code != 200) {
\r
1724 Ext.MessageBox.alert('Error', 'Code '+response.Status.code+' Error Returned');
\r
1726 place = response.Placemark[0];
\r
1727 addressinfo = place.AddressDetails;
\r
1728 accuracy = addressinfo.Accuracy;
\r
1729 if (accuracy === 0) {
\r
1730 Ext.MessageBox.alert('Unable to Locate Address', 'Unable to Locate the Address you provided');
\r
1732 if (accuracy < 7) {
\r
1733 Ext.MessageBox.alert('Address Accuracy', 'The address provided has a low accuracy.<br><br>Level '+accuracy+' Accuracy (8 = Exact Match, 1 = Vague Match)');
\r
1735 point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
\r
1736 if (typeof this.setCenter.marker === 'object' && typeof point === 'object'){
\r
1737 this.addMarker(point,this.setCenter.marker,this.setCenter.marker.clear,true, this.setCenter.listeners);
\r
1747 Ext.reg('gmappanel', Ext.ux.GMapPanel); Ext.ns('Ext.ux.grid');
\r
1750 * @class Ext.ux.grid.GroupSummary
\r
1751 * @extends Ext.util.Observable
\r
1752 * A GridPanel plugin that enables dynamic column calculations and a dynamically
\r
1753 * updated grouped summary row.
\r
1755 Ext.ux.grid.GroupSummary = Ext.extend(Ext.util.Observable, {
\r
1757 * @cfg {Function} summaryRenderer Renderer example:<pre><code>
\r
1758 summaryRenderer: function(v, params, data){
\r
1759 return ((v === 0 || v > 1) ? '(' + v +' Tasks)' : '(1 Task)');
\r
1764 * @cfg {String} summaryType (Optional) The type of
\r
1765 * calculation to be used for the column. For options available see
\r
1766 * {@link #Calculations}.
\r
1769 constructor : function(config){
\r
1770 Ext.apply(this, config);
\r
1771 Ext.ux.grid.GroupSummary.superclass.constructor.call(this);
\r
1773 init : function(grid){
\r
1775 this.cm = grid.getColumnModel();
\r
1776 this.view = grid.getView();
\r
1778 var v = this.view;
\r
1779 v.doGroupEnd = this.doGroupEnd.createDelegate(this);
\r
1781 v.afterMethod('onColumnWidthUpdated', this.doWidth, this);
\r
1782 v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this);
\r
1783 v.afterMethod('onColumnHiddenUpdated', this.doHidden, this);
\r
1784 v.afterMethod('onUpdate', this.doUpdate, this);
\r
1785 v.afterMethod('onRemove', this.doRemove, this);
\r
1788 this.rowTpl = new Ext.Template(
\r
1789 '<div class="x-grid3-summary-row" style="{tstyle}">',
\r
1790 '<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
\r
1791 '<tbody><tr>{cells}</tr></tbody>',
\r
1794 this.rowTpl.disableFormats = true;
\r
1796 this.rowTpl.compile();
\r
1798 if(!this.cellTpl){
\r
1799 this.cellTpl = new Ext.Template(
\r
1800 '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
\r
1801 '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on">{value}</div>',
\r
1804 this.cellTpl.disableFormats = true;
\r
1806 this.cellTpl.compile();
\r
1810 * Toggle the display of the summary row on/off
\r
1811 * @param {Boolean} visible <tt>true</tt> to show the summary, <tt>false</tt> to hide the summary.
\r
1813 toggleSummaries : function(visible){
\r
1814 var el = this.grid.getGridEl();
\r
1816 if(visible === undefined){
\r
1817 visible = el.hasClass('x-grid-hide-summary');
\r
1819 el[visible ? 'removeClass' : 'addClass']('x-grid-hide-summary');
\r
1823 renderSummary : function(o, cs){
\r
1824 cs = cs || this.view.getColumnData();
\r
1825 var cfg = this.cm.config;
\r
1827 var buf = [], c, p = {}, cf, last = cs.length-1;
\r
1828 for(var i = 0, len = cs.length; i < len; i++){
\r
1832 p.style = c.style;
\r
1833 p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
\r
1834 if(cf.summaryType || cf.summaryRenderer){
\r
1835 p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o);
\r
1839 if(p.value == undefined || p.value === "") p.value = " ";
\r
1840 buf[buf.length] = this.cellTpl.apply(p);
\r
1843 return this.rowTpl.apply({
\r
1844 tstyle: 'width:'+this.view.getTotalWidth()+';',
\r
1845 cells: buf.join('')
\r
1851 * @param {Object} rs
\r
1852 * @param {Object} cs
\r
1854 calculate : function(rs, cs){
\r
1855 var data = {}, r, c, cfg = this.cm.config, cf;
\r
1856 for(var j = 0, jlen = rs.length; j < jlen; j++){
\r
1858 for(var i = 0, len = cs.length; i < len; i++){
\r
1861 if(cf.summaryType){
\r
1862 data[c.name] = Ext.ux.grid.GroupSummary.Calculations[cf.summaryType](data[c.name] || 0, r, c.name, data);
\r
1869 doGroupEnd : function(buf, g, cs, ds, colCount){
\r
1870 var data = this.calculate(g.rs, cs);
\r
1871 buf.push('</div>', this.renderSummary({data: data}, cs), '</div>');
\r
1874 doWidth : function(col, w, tw){
\r
1875 var gs = this.view.getGroups(), s;
\r
1876 for(var i = 0, len = gs.length; i < len; i++){
\r
1877 s = gs[i].childNodes[2];
\r
1878 s.style.width = tw;
\r
1879 s.firstChild.style.width = tw;
\r
1880 s.firstChild.rows[0].childNodes[col].style.width = w;
\r
1884 doAllWidths : function(ws, tw){
\r
1885 var gs = this.view.getGroups(), s, cells, wlen = ws.length;
\r
1886 for(var i = 0, len = gs.length; i < len; i++){
\r
1887 s = gs[i].childNodes[2];
\r
1888 s.style.width = tw;
\r
1889 s.firstChild.style.width = tw;
\r
1890 cells = s.firstChild.rows[0].childNodes;
\r
1891 for(var j = 0; j < wlen; j++){
\r
1892 cells[j].style.width = ws[j];
\r
1897 doHidden : function(col, hidden, tw){
\r
1898 var gs = this.view.getGroups(), s, display = hidden ? 'none' : '';
\r
1899 for(var i = 0, len = gs.length; i < len; i++){
\r
1900 s = gs[i].childNodes[2];
\r
1901 s.style.width = tw;
\r
1902 s.firstChild.style.width = tw;
\r
1903 s.firstChild.rows[0].childNodes[col].style.display = display;
\r
1907 // Note: requires that all (or the first) record in the
\r
1908 // group share the same group value. Returns false if the group
\r
1909 // could not be found.
\r
1910 refreshSummary : function(groupValue){
\r
1911 return this.refreshSummaryById(this.view.getGroupId(groupValue));
\r
1914 getSummaryNode : function(gid){
\r
1915 var g = Ext.fly(gid, '_gsummary');
\r
1917 return g.down('.x-grid3-summary-row', true);
\r
1922 refreshSummaryById : function(gid){
\r
1923 var g = document.getElementById(gid);
\r
1928 this.grid.store.each(function(r){
\r
1929 if(r._groupId == gid){
\r
1930 rs[rs.length] = r;
\r
1933 var cs = this.view.getColumnData();
\r
1934 var data = this.calculate(rs, cs);
\r
1935 var markup = this.renderSummary({data: data}, cs);
\r
1937 var existing = this.getSummaryNode(gid);
\r
1939 g.removeChild(existing);
\r
1941 Ext.DomHelper.append(g, markup);
\r
1945 doUpdate : function(ds, record){
\r
1946 this.refreshSummaryById(record._groupId);
\r
1949 doRemove : function(ds, record, index, isUpdate){
\r
1951 this.refreshSummaryById(record._groupId);
\r
1956 * Show a message in the summary row.
\r
1958 grid.on('afteredit', function(){
\r
1959 var groupValue = 'Ext Forms: Field Anchoring';
\r
1960 summary.showSummaryMsg(groupValue, 'Updating Summary...');
\r
1963 * @param {String} groupValue
\r
1964 * @param {String} msg Text to use as innerHTML for the summary row.
\r
1966 showSummaryMsg : function(groupValue, msg){
\r
1967 var gid = this.view.getGroupId(groupValue);
\r
1968 var node = this.getSummaryNode(gid);
\r
1970 node.innerHTML = '<div class="x-grid3-summary-msg">' + msg + '</div>';
\r
1975 //backwards compat
\r
1976 Ext.grid.GroupSummary = Ext.ux.grid.GroupSummary;
\r
1980 * Calculation types for summary row:</p><div class="mdetail-params"><ul>
\r
1981 * <li><b><tt>sum</tt></b> : <div class="sub-desc"></div></li>
\r
1982 * <li><b><tt>count</tt></b> : <div class="sub-desc"></div></li>
\r
1983 * <li><b><tt>max</tt></b> : <div class="sub-desc"></div></li>
\r
1984 * <li><b><tt>min</tt></b> : <div class="sub-desc"></div></li>
\r
1985 * <li><b><tt>average</tt></b> : <div class="sub-desc"></div></li>
\r
1987 * <p>Custom calculations may be implemented. An example of
\r
1988 * custom <code>summaryType=totalCost</code>:</p><pre><code>
\r
1989 // define a custom summary function
\r
1990 Ext.ux.grid.GroupSummary.Calculations['totalCost'] = function(v, record, field){
\r
1991 return v + (record.data.estimate * record.data.rate);
\r
1994 * @property Calculations
\r
1997 Ext.ux.grid.GroupSummary.Calculations = {
\r
1998 'sum' : function(v, record, field){
\r
1999 return v + (record.data[field]||0);
\r
2002 'count' : function(v, record, field, data){
\r
2003 return data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);
\r
2006 'max' : function(v, record, field, data){
\r
2007 var v = record.data[field];
\r
2008 var max = data[field+'max'] === undefined ? (data[field+'max'] = v) : data[field+'max'];
\r
2009 return v > max ? (data[field+'max'] = v) : max;
\r
2012 'min' : function(v, record, field, data){
\r
2013 var v = record.data[field];
\r
2014 var min = data[field+'min'] === undefined ? (data[field+'min'] = v) : data[field+'min'];
\r
2015 return v < min ? (data[field+'min'] = v) : min;
\r
2018 'average' : function(v, record, field, data){
\r
2019 var c = data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);
\r
2020 var t = (data[field+'total'] = ((data[field+'total']||0) + (record.data[field]||0)));
\r
2021 return t === 0 ? 0 : t / c;
\r
2024 Ext.grid.GroupSummary.Calculations = Ext.ux.grid.GroupSummary.Calculations;
\r
2027 * @class Ext.ux.grid.HybridSummary
\r
2028 * @extends Ext.ux.grid.GroupSummary
\r
2029 * Adds capability to specify the summary data for the group via json as illustrated here:
\r
2034 projectId: 100, project: 'House',
\r
2035 taskId: 112, description: 'Paint',
\r
2036 estimate: 6, rate: 150,
\r
2044 description: 14, estimate: 9,
\r
2045 rate: 99, due: new Date(2009, 6, 29),
\r
2053 Ext.ux.grid.HybridSummary = Ext.extend(Ext.ux.grid.GroupSummary, {
\r
2056 * @param {Object} rs
\r
2057 * @param {Object} cs
\r
2059 calculate : function(rs, cs){
\r
2060 var gcol = this.view.getGroupField();
\r
2061 var gvalue = rs[0].data[gcol];
\r
2062 var gdata = this.getSummaryData(gvalue);
\r
2063 return gdata || Ext.ux.grid.HybridSummary.superclass.calculate.call(this, rs, cs);
\r
2068 grid.on('afteredit', function(){
\r
2069 var groupValue = 'Ext Forms: Field Anchoring';
\r
2070 summary.showSummaryMsg(groupValue, 'Updating Summary...');
\r
2071 setTimeout(function(){ // simulate server call
\r
2072 // HybridSummary class implements updateSummaryData
\r
2073 summary.updateSummaryData(groupValue,
\r
2074 // create data object based on configured dataIndex
\r
2075 {description: 22, estimate: 888, rate: 888, due: new Date(), cost: 8});
\r
2079 * @param {String} groupValue
\r
2080 * @param {Object} data data object
\r
2081 * @param {Boolean} skipRefresh (Optional) Defaults to false
\r
2083 updateSummaryData : function(groupValue, data, skipRefresh){
\r
2084 var json = this.grid.store.reader.jsonData;
\r
2085 if(!json.summaryData){
\r
2086 json.summaryData = {};
\r
2088 json.summaryData[groupValue] = data;
\r
2090 this.refreshSummary(groupValue);
\r
2095 * Returns the summaryData for the specified groupValue or null.
\r
2096 * @param {String} groupValue
\r
2097 * @return {Object} summaryData
\r
2099 getSummaryData : function(groupValue){
\r
2100 var json = this.grid.store.reader.jsonData;
\r
2101 if(json && json.summaryData){
\r
2102 return json.summaryData[groupValue];
\r
2108 //backwards compat
\r
2109 Ext.grid.HybridSummary = Ext.ux.grid.HybridSummary;
\r
2110 Ext.ux.GroupTab = Ext.extend(Ext.Container, {
\r
2115 deferredRender: true,
\r
2119 idDelimiter: '__',
\r
2121 headerAsText: false,
\r
2125 hideBorders: true,
\r
2127 initComponent: function(config){
\r
2128 Ext.apply(this, config);
\r
2129 this.frame = false;
\r
2131 Ext.ux.GroupTab.superclass.initComponent.call(this);
\r
2133 this.addEvents('activate', 'deactivate', 'changemainitem', 'beforetabchange', 'tabchange');
\r
2135 this.setLayout(new Ext.layout.CardLayout({
\r
2136 deferredRender: this.deferredRender
\r
2139 if (!this.stack) {
\r
2140 this.stack = Ext.TabPanel.AccessStack();
\r
2145 this.on('beforerender', function(){
\r
2146 this.groupEl = this.ownerCt.getGroupEl(this);
\r
2149 this.on('add', this.onAdd, this, {
\r
2152 this.on('remove', this.onRemove, this, {
\r
2156 if (this.mainItem !== undefined) {
\r
2157 var item = (typeof this.mainItem == 'object') ? this.mainItem : this.items.get(this.mainItem);
\r
2158 delete this.mainItem;
\r
2159 this.setMainItem(item);
\r
2164 * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
\r
2165 * can return false to cancel the tab change.
\r
2166 * @param {String/Panel} tab The id or tab Panel to activate
\r
2168 setActiveTab : function(item){
\r
2169 item = this.getComponent(item);
\r
2170 if(!item || this.fireEvent('beforetabchange', this, item, this.activeTab) === false){
\r
2173 if(!this.rendered){
\r
2174 this.activeTab = item;
\r
2177 if(this.activeTab != item){
\r
2178 if(this.activeTab && this.activeTab != this.mainItem){
\r
2179 var oldEl = this.getTabEl(this.activeTab);
\r
2181 Ext.fly(oldEl).removeClass('x-grouptabs-strip-active');
\r
2183 this.activeTab.fireEvent('deactivate', this.activeTab);
\r
2185 var el = this.getTabEl(item);
\r
2186 Ext.fly(el).addClass('x-grouptabs-strip-active');
\r
2187 this.activeTab = item;
\r
2188 this.stack.add(item);
\r
2190 this.layout.setActiveItem(item);
\r
2191 if(this.layoutOnTabChange && item.doLayout){
\r
2194 if(this.scrolling){
\r
2195 this.scrollToTab(item, this.animScroll);
\r
2198 item.fireEvent('activate', item);
\r
2199 this.fireEvent('tabchange', this, item);
\r
2203 getTabEl: function(item){
\r
2204 if (item == this.mainItem) {
\r
2205 return this.groupEl;
\r
2207 return Ext.TabPanel.prototype.getTabEl.call(this, item);
\r
2210 onRender: function(ct, position){
\r
2211 Ext.ux.GroupTab.superclass.onRender.call(this, ct, position);
\r
2213 this.strip = Ext.fly(this.groupEl).createChild({
\r
2215 cls: 'x-grouptabs-sub'
\r
2218 this.tooltip = new Ext.ToolTip({
\r
2219 target: this.groupEl,
\r
2220 delegate: 'a.x-grouptabs-text',
\r
2222 renderTo: document.body,
\r
2224 beforeshow: function(tip) {
\r
2225 var item = (tip.triggerElement.parentNode === this.mainItem.tabEl)
\r
2227 : this.findById(tip.triggerElement.parentNode.id.split(this.idDelimiter)[1]);
\r
2229 if(!item.tabTip) {
\r
2232 tip.body.dom.innerHTML = item.tabTip;
\r
2238 if (!this.itemTpl) {
\r
2239 var tt = new Ext.Template('<li class="{cls}" id="{id}">', '<a onclick="return false;" class="x-grouptabs-text {iconCls}">{text}</a>', '</li>');
\r
2240 tt.disableFormats = true;
\r
2242 Ext.ux.GroupTab.prototype.itemTpl = tt;
\r
2245 this.items.each(this.initTab, this);
\r
2248 afterRender: function(){
\r
2249 Ext.ux.GroupTab.superclass.afterRender.call(this);
\r
2251 if (this.activeTab !== undefined) {
\r
2252 var item = (typeof this.activeTab == 'object') ? this.activeTab : this.items.get(this.activeTab);
\r
2253 delete this.activeTab;
\r
2254 this.setActiveTab(item);
\r
2259 initTab: function(item, index){
\r
2260 var before = this.strip.dom.childNodes[index];
\r
2261 var p = Ext.TabPanel.prototype.getTemplateArgs.call(this, item);
\r
2263 if (item === this.mainItem) {
\r
2264 item.tabEl = this.groupEl;
\r
2265 p.cls += ' x-grouptabs-main-item';
\r
2268 var el = before ? this.itemTpl.insertBefore(before, p) : this.itemTpl.append(this.strip, p);
\r
2270 item.tabEl = item.tabEl || el;
\r
2272 item.on('disable', this.onItemDisabled, this);
\r
2273 item.on('enable', this.onItemEnabled, this);
\r
2274 item.on('titlechange', this.onItemTitleChanged, this);
\r
2275 item.on('iconchange', this.onItemIconChanged, this);
\r
2276 item.on('beforeshow', this.onBeforeShowItem, this);
\r
2279 setMainItem: function(item){
\r
2280 item = this.getComponent(item);
\r
2281 if (!item || this.fireEvent('changemainitem', this, item, this.mainItem) === false) {
\r
2285 this.mainItem = item;
\r
2288 getMainItem: function(){
\r
2289 return this.mainItem || null;
\r
2293 onBeforeShowItem: function(item){
\r
2294 if (item != this.activeTab) {
\r
2295 this.setActiveTab(item);
\r
2301 onAdd: function(gt, item, index){
\r
2302 if (this.rendered) {
\r
2303 this.initTab.call(this, item, index);
\r
2308 onRemove: function(tp, item){
\r
2309 Ext.destroy(Ext.get(this.getTabEl(item)));
\r
2310 this.stack.remove(item);
\r
2311 item.un('disable', this.onItemDisabled, this);
\r
2312 item.un('enable', this.onItemEnabled, this);
\r
2313 item.un('titlechange', this.onItemTitleChanged, this);
\r
2314 item.un('iconchange', this.onItemIconChanged, this);
\r
2315 item.un('beforeshow', this.onBeforeShowItem, this);
\r
2316 if (item == this.activeTab) {
\r
2317 var next = this.stack.next();
\r
2319 this.setActiveTab(next);
\r
2321 else if (this.items.getCount() > 0) {
\r
2322 this.setActiveTab(0);
\r
2325 this.activeTab = null;
\r
2331 onBeforeAdd: function(item){
\r
2332 var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);
\r
2334 this.setActiveTab(item);
\r
2337 Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);
\r
2338 var es = item.elements;
\r
2339 item.elements = es ? es.replace(',header', '') : es;
\r
2340 item.border = (item.border === true);
\r
2344 onItemDisabled: Ext.TabPanel.prototype.onItemDisabled,
\r
2345 onItemEnabled: Ext.TabPanel.prototype.onItemEnabled,
\r
2348 onItemTitleChanged: function(item){
\r
2349 var el = this.getTabEl(item);
\r
2351 Ext.fly(el).child('a.x-grouptabs-text', true).innerHTML = item.title;
\r
2356 onItemIconChanged: function(item, iconCls, oldCls){
\r
2357 var el = this.getTabEl(item);
\r
2359 Ext.fly(el).child('a.x-grouptabs-text').replaceClass(oldCls, iconCls);
\r
2363 beforeDestroy: function(){
\r
2364 Ext.TabPanel.prototype.beforeDestroy.call(this);
\r
2365 this.tooltip.destroy();
\r
2369 Ext.reg('grouptab', Ext.ux.GroupTab);
\r
2372 Ext.ux.GroupTabPanel = Ext.extend(Ext.TabPanel, {
\r
2373 tabPosition: 'left',
\r
2375 alternateColor: false,
\r
2377 alternateCls: 'x-grouptabs-panel-alt',
\r
2379 defaultType: 'grouptab',
\r
2381 deferredRender: false,
\r
2383 activeGroup : null,
\r
2385 initComponent: function(){
\r
2386 Ext.ux.GroupTabPanel.superclass.initComponent.call(this);
\r
2389 'beforegroupchange',
\r
2392 this.elements = 'body,header';
\r
2393 this.stripTarget = 'header';
\r
2395 this.tabPosition = this.tabPosition == 'right' ? 'right' : 'left';
\r
2397 this.addClass('x-grouptabs-panel');
\r
2399 if (this.tabStyle && this.tabStyle != '') {
\r
2400 this.addClass('x-grouptabs-panel-' + this.tabStyle);
\r
2403 if (this.alternateColor) {
\r
2404 this.addClass(this.alternateCls);
\r
2407 this.on('beforeadd', function(gtp, item, index){
\r
2408 this.initGroup(item, index);
\r
2412 initEvents : function() {
\r
2413 this.mon(this.strip, 'mousedown', this.onStripMouseDown, this);
\r
2416 onRender: function(ct, position){
\r
2417 Ext.TabPanel.superclass.onRender.call(this, ct, position);
\r
2420 var pos = this.tabPosition == 'top' ? 'header' : 'footer';
\r
2421 this[pos].addClass('x-tab-panel-'+pos+'-plain');
\r
2424 var st = this[this.stripTarget];
\r
2426 this.stripWrap = st.createChild({cls:'x-tab-strip-wrap ', cn:{
\r
2427 tag:'ul', cls:'x-grouptabs-strip x-grouptabs-tab-strip-'+this.tabPosition}});
\r
2429 var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
\r
2430 this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
\r
2432 this.header.addClass('x-grouptabs-panel-header');
\r
2433 this.bwrap.addClass('x-grouptabs-bwrap');
\r
2434 this.body.addClass('x-tab-panel-body-'+this.tabPosition + ' x-grouptabs-panel-body');
\r
2436 if (!this.itemTpl) {
\r
2437 var tt = new Ext.Template(
\r
2438 '<li class="{cls}" id="{id}">',
\r
2439 '<a class="x-grouptabs-expand" onclick="return false;"></a>',
\r
2440 '<a class="x-grouptabs-text {iconCls}" href="#" onclick="return false;">',
\r
2441 '<span>{text}</span></a>',
\r
2444 tt.disableFormats = true;
\r
2446 Ext.ux.GroupTabPanel.prototype.itemTpl = tt;
\r
2449 this.items.each(this.initGroup, this);
\r
2452 afterRender: function(){
\r
2453 Ext.ux.GroupTabPanel.superclass.afterRender.call(this);
\r
2455 this.tabJoint = Ext.fly(this.body.dom.parentNode).createChild({
\r
2456 cls: 'x-tab-joint'
\r
2459 this.addClass('x-tab-panel-' + this.tabPosition);
\r
2460 this.header.setWidth(this.tabWidth);
\r
2462 if (this.activeGroup !== undefined) {
\r
2463 var group = (typeof this.activeGroup == 'object') ? this.activeGroup : this.items.get(this.activeGroup);
\r
2464 delete this.activeGroup;
\r
2465 this.setActiveGroup(group);
\r
2466 group.setActiveTab(group.getMainItem());
\r
2470 getGroupEl : Ext.TabPanel.prototype.getTabEl,
\r
2473 findTargets: function(e){
\r
2475 var itemEl = e.getTarget('li', this.strip);
\r
2477 item = this.findById(itemEl.id.split(this.idDelimiter)[1]);
\r
2478 if (item.disabled) {
\r
2487 expand: e.getTarget('.x-grouptabs-expand', this.strip),
\r
2488 isGroup: !e.getTarget('ul.x-grouptabs-sub', this.strip),
\r
2495 onStripMouseDown: function(e){
\r
2496 if (e.button != 0) {
\r
2499 e.preventDefault();
\r
2500 var t = this.findTargets(e);
\r
2502 this.toggleGroup(t.el);
\r
2504 else if (t.item) {
\r
2506 t.item.setActiveTab(t.item.getMainItem());
\r
2509 t.item.ownerCt.setActiveTab(t.item);
\r
2514 expandGroup: function(groupEl){
\r
2515 if(groupEl.isXType) {
\r
2516 groupEl = this.getGroupEl(groupEl);
\r
2518 Ext.fly(groupEl).addClass('x-grouptabs-expanded');
\r
2521 toggleGroup: function(groupEl){
\r
2522 if(groupEl.isXType) {
\r
2523 groupEl = this.getGroupEl(groupEl);
\r
2525 Ext.fly(groupEl).toggleClass('x-grouptabs-expanded');
\r
2526 this.syncTabJoint();
\r
2529 syncTabJoint: function(groupEl){
\r
2530 if (!this.tabJoint) {
\r
2534 groupEl = groupEl || this.getGroupEl(this.activeGroup);
\r
2536 this.tabJoint.setHeight(Ext.fly(groupEl).getHeight() - 2);
\r
2538 var y = Ext.isGecko2 ? 0 : 1;
\r
2539 if (this.tabPosition == 'left'){
\r
2540 this.tabJoint.alignTo(groupEl, 'tl-tr', [-2,y]);
\r
2543 this.tabJoint.alignTo(groupEl, 'tr-tl', [1,y]);
\r
2547 this.tabJoint.hide();
\r
2551 getActiveTab : function() {
\r
2552 if(!this.activeGroup) return null;
\r
2553 return this.activeGroup.getTabEl(this.activeGroup.activeTab) || null;
\r
2556 onResize: function(){
\r
2557 Ext.ux.GroupTabPanel.superclass.onResize.apply(this, arguments);
\r
2558 this.syncTabJoint();
\r
2561 createCorner: function(el, pos){
\r
2562 return Ext.fly(el).createChild({
\r
2563 cls: 'x-grouptabs-corner x-grouptabs-corner-' + pos
\r
2567 initGroup: function(group, index){
\r
2568 var before = this.strip.dom.childNodes[index];
\r
2569 var p = this.getTemplateArgs(group);
\r
2570 if (index === 0) {
\r
2571 p.cls += ' x-tab-first';
\r
2573 p.cls += ' x-grouptabs-main';
\r
2574 p.text = group.getMainItem().title;
\r
2576 var el = before ? this.itemTpl.insertBefore(before, p) : this.itemTpl.append(this.strip, p);
\r
2578 var tl = this.createCorner(el, 'top-' + this.tabPosition);
\r
2579 var bl = this.createCorner(el, 'bottom-' + this.tabPosition);
\r
2581 if (group.expanded) {
\r
2582 this.expandGroup(el);
\r
2585 if (Ext.isIE6 || (Ext.isIE && !Ext.isStrict)){
\r
2586 bl.setLeft('-10px');
\r
2587 bl.setBottom('-5px');
\r
2588 tl.setLeft('-10px');
\r
2589 tl.setTop('-5px');
\r
2592 this.mon(group, 'changemainitem', this.onGroupChangeMainItem, this);
\r
2593 this.mon(group, 'beforetabchange', this.onGroupBeforeTabChange, this);
\r
2596 setActiveGroup : function(group) {
\r
2597 group = this.getComponent(group);
\r
2598 if(!group || this.fireEvent('beforegroupchange', this, group, this.activeGroup) === false){
\r
2601 if(!this.rendered){
\r
2602 this.activeGroup = group;
\r
2605 if(this.activeGroup != group){
\r
2606 if(this.activeGroup){
\r
2607 var oldEl = this.getGroupEl(this.activeGroup);
\r
2609 Ext.fly(oldEl).removeClass('x-grouptabs-strip-active');
\r
2611 this.activeGroup.fireEvent('deactivate', this.activeTab);
\r
2614 var groupEl = this.getGroupEl(group);
\r
2615 Ext.fly(groupEl).addClass('x-grouptabs-strip-active');
\r
2617 this.activeGroup = group;
\r
2618 this.stack.add(group);
\r
2620 this.layout.setActiveItem(group);
\r
2621 this.syncTabJoint(groupEl);
\r
2623 group.fireEvent('activate', group);
\r
2624 this.fireEvent('groupchange', this, group);
\r
2628 onGroupBeforeTabChange: function(group, newTab, oldTab){
\r
2629 if(group !== this.activeGroup || newTab !== oldTab) {
\r
2630 this.strip.select('.x-grouptabs-sub > li.x-grouptabs-strip-active', true).removeClass('x-grouptabs-strip-active');
\r
2633 this.expandGroup(this.getGroupEl(group));
\r
2634 this.setActiveGroup(group);
\r
2637 getFrameHeight: function(){
\r
2638 var h = this.el.getFrameWidth('tb');
\r
2639 h += (this.tbar ? this.tbar.getHeight() : 0) +
\r
2640 (this.bbar ? this.bbar.getHeight() : 0);
\r
2645 adjustBodyWidth: function(w){
\r
2646 return w - this.tabWidth;
\r
2650 Ext.reg('grouptabpanel', Ext.ux.GroupTabPanel);/*
\r
2651 * Note that this control will most likely remain as an example, and not as a core Ext form
\r
2652 * control. However, the API will be changing in a future release and so should not yet be
\r
2653 * treated as a final, stable API at this time.
\r
2657 * @class Ext.ux.form.ItemSelector
\r
2658 * @extends Ext.form.Field
\r
2659 * A control that allows selection of between two Ext.ux.form.MultiSelect controls.
\r
2662 * 2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams)
\r
2665 * Create a new ItemSelector
\r
2666 * @param {Object} config Configuration options
\r
2667 * @xtype itemselector
\r
2669 Ext.ux.form.ItemSelector = Ext.extend(Ext.form.Field, {
\r
2670 hideNavIcons:false,
\r
2673 iconDown:"down2.gif",
\r
2674 iconLeft:"left2.gif",
\r
2675 iconRight:"right2.gif",
\r
2676 iconTop:"top2.gif",
\r
2677 iconBottom:"bottom2.gif",
\r
2679 drawDownIcon:true,
\r
2680 drawLeftIcon:true,
\r
2681 drawRightIcon:true,
\r
2687 defaultAutoCreate:{tag: "div"},
\r
2689 * @cfg {Array} multiselects An array of {@link Ext.ux.form.MultiSelect} config objects, with at least all required parameters (e.g., store)
\r
2691 multiselects:null,
\r
2693 initComponent: function(){
\r
2694 Ext.ux.form.ItemSelector.superclass.initComponent.call(this);
\r
2696 'rowdblclick' : true,
\r
2701 onRender: function(ct, position){
\r
2702 Ext.ux.form.ItemSelector.superclass.onRender.call(this, ct, position);
\r
2704 // Internal default configuration for both multiselects
\r
2706 legend: 'Available',
\r
2712 legend: 'Selected',
\r
2719 this.fromMultiselect = new Ext.ux.form.MultiSelect(Ext.applyIf(this.multiselects[0], msConfig[0]));
\r
2720 this.fromMultiselect.on('dblclick', this.onRowDblClick, this);
\r
2722 this.toMultiselect = new Ext.ux.form.MultiSelect(Ext.applyIf(this.multiselects[1], msConfig[1]));
\r
2723 this.toMultiselect.on('dblclick', this.onRowDblClick, this);
\r
2725 var p = new Ext.Panel({
\r
2726 bodyStyle:this.bodyStyle,
\r
2727 border:this.border,
\r
2729 layoutConfig:{columns:3}
\r
2732 p.add(this.fromMultiselect);
\r
2733 var icons = new Ext.Panel({header:false});
\r
2735 p.add(this.toMultiselect);
\r
2736 p.render(this.el);
\r
2737 icons.el.down('.'+icons.bwrapCls).remove();
\r
2740 if (this.imagePath!="" && this.imagePath.charAt(this.imagePath.length-1)!="/")
\r
2741 this.imagePath+="/";
\r
2742 this.iconUp = this.imagePath + (this.iconUp || 'up2.gif');
\r
2743 this.iconDown = this.imagePath + (this.iconDown || 'down2.gif');
\r
2744 this.iconLeft = this.imagePath + (this.iconLeft || 'left2.gif');
\r
2745 this.iconRight = this.imagePath + (this.iconRight || 'right2.gif');
\r
2746 this.iconTop = this.imagePath + (this.iconTop || 'top2.gif');
\r
2747 this.iconBottom = this.imagePath + (this.iconBottom || 'bottom2.gif');
\r
2748 var el=icons.getEl();
\r
2749 this.toTopIcon = el.createChild({tag:'img', src:this.iconTop, style:{cursor:'pointer', margin:'2px'}});
\r
2750 el.createChild({tag: 'br'});
\r
2751 this.upIcon = el.createChild({tag:'img', src:this.iconUp, style:{cursor:'pointer', margin:'2px'}});
\r
2752 el.createChild({tag: 'br'});
\r
2753 this.addIcon = el.createChild({tag:'img', src:this.iconRight, style:{cursor:'pointer', margin:'2px'}});
\r
2754 el.createChild({tag: 'br'});
\r
2755 this.removeIcon = el.createChild({tag:'img', src:this.iconLeft, style:{cursor:'pointer', margin:'2px'}});
\r
2756 el.createChild({tag: 'br'});
\r
2757 this.downIcon = el.createChild({tag:'img', src:this.iconDown, style:{cursor:'pointer', margin:'2px'}});
\r
2758 el.createChild({tag: 'br'});
\r
2759 this.toBottomIcon = el.createChild({tag:'img', src:this.iconBottom, style:{cursor:'pointer', margin:'2px'}});
\r
2760 this.toTopIcon.on('click', this.toTop, this);
\r
2761 this.upIcon.on('click', this.up, this);
\r
2762 this.downIcon.on('click', this.down, this);
\r
2763 this.toBottomIcon.on('click', this.toBottom, this);
\r
2764 this.addIcon.on('click', this.fromTo, this);
\r
2765 this.removeIcon.on('click', this.toFrom, this);
\r
2766 if (!this.drawUpIcon || this.hideNavIcons) { this.upIcon.dom.style.display='none'; }
\r
2767 if (!this.drawDownIcon || this.hideNavIcons) { this.downIcon.dom.style.display='none'; }
\r
2768 if (!this.drawLeftIcon || this.hideNavIcons) { this.addIcon.dom.style.display='none'; }
\r
2769 if (!this.drawRightIcon || this.hideNavIcons) { this.removeIcon.dom.style.display='none'; }
\r
2770 if (!this.drawTopIcon || this.hideNavIcons) { this.toTopIcon.dom.style.display='none'; }
\r
2771 if (!this.drawBotIcon || this.hideNavIcons) { this.toBottomIcon.dom.style.display='none'; }
\r
2773 var tb = p.body.first();
\r
2774 this.el.setWidth(p.body.first().getWidth());
\r
2775 p.body.removeClass();
\r
2777 this.hiddenName = this.name;
\r
2778 var hiddenTag = {tag: "input", type: "hidden", value: "", name: this.name};
\r
2779 this.hiddenField = this.el.createChild(hiddenTag);
\r
2782 doLayout: function(){
\r
2783 if(this.rendered){
\r
2784 this.fromMultiselect.fs.doLayout();
\r
2785 this.toMultiselect.fs.doLayout();
\r
2789 afterRender: function(){
\r
2790 Ext.ux.form.ItemSelector.superclass.afterRender.call(this);
\r
2792 this.toStore = this.toMultiselect.store;
\r
2793 this.toStore.on('add', this.valueChanged, this);
\r
2794 this.toStore.on('remove', this.valueChanged, this);
\r
2795 this.toStore.on('load', this.valueChanged, this);
\r
2796 this.valueChanged(this.toStore);
\r
2799 toTop : function() {
\r
2800 var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
\r
2802 if (selectionsArray.length > 0) {
\r
2803 selectionsArray.sort();
\r
2804 for (var i=0; i<selectionsArray.length; i++) {
\r
2805 record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
\r
2806 records.push(record);
\r
2808 selectionsArray = [];
\r
2809 for (var i=records.length-1; i>-1; i--) {
\r
2810 record = records[i];
\r
2811 this.toMultiselect.view.store.remove(record);
\r
2812 this.toMultiselect.view.store.insert(0, record);
\r
2813 selectionsArray.push(((records.length - 1) - i));
\r
2816 this.toMultiselect.view.refresh();
\r
2817 this.toMultiselect.view.select(selectionsArray);
\r
2820 toBottom : function() {
\r
2821 var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
\r
2823 if (selectionsArray.length > 0) {
\r
2824 selectionsArray.sort();
\r
2825 for (var i=0; i<selectionsArray.length; i++) {
\r
2826 record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
\r
2827 records.push(record);
\r
2829 selectionsArray = [];
\r
2830 for (var i=0; i<records.length; i++) {
\r
2831 record = records[i];
\r
2832 this.toMultiselect.view.store.remove(record);
\r
2833 this.toMultiselect.view.store.add(record);
\r
2834 selectionsArray.push((this.toMultiselect.view.store.getCount()) - (records.length - i));
\r
2837 this.toMultiselect.view.refresh();
\r
2838 this.toMultiselect.view.select(selectionsArray);
\r
2842 var record = null;
\r
2843 var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
\r
2844 selectionsArray.sort();
\r
2845 var newSelectionsArray = [];
\r
2846 if (selectionsArray.length > 0) {
\r
2847 for (var i=0; i<selectionsArray.length; i++) {
\r
2848 record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
\r
2849 if ((selectionsArray[i] - 1) >= 0) {
\r
2850 this.toMultiselect.view.store.remove(record);
\r
2851 this.toMultiselect.view.store.insert(selectionsArray[i] - 1, record);
\r
2852 newSelectionsArray.push(selectionsArray[i] - 1);
\r
2855 this.toMultiselect.view.refresh();
\r
2856 this.toMultiselect.view.select(newSelectionsArray);
\r
2860 down : function() {
\r
2861 var record = null;
\r
2862 var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
\r
2863 selectionsArray.sort();
\r
2864 selectionsArray.reverse();
\r
2865 var newSelectionsArray = [];
\r
2866 if (selectionsArray.length > 0) {
\r
2867 for (var i=0; i<selectionsArray.length; i++) {
\r
2868 record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
\r
2869 if ((selectionsArray[i] + 1) < this.toMultiselect.view.store.getCount()) {
\r
2870 this.toMultiselect.view.store.remove(record);
\r
2871 this.toMultiselect.view.store.insert(selectionsArray[i] + 1, record);
\r
2872 newSelectionsArray.push(selectionsArray[i] + 1);
\r
2875 this.toMultiselect.view.refresh();
\r
2876 this.toMultiselect.view.select(newSelectionsArray);
\r
2880 fromTo : function() {
\r
2881 var selectionsArray = this.fromMultiselect.view.getSelectedIndexes();
\r
2883 if (selectionsArray.length > 0) {
\r
2884 for (var i=0; i<selectionsArray.length; i++) {
\r
2885 record = this.fromMultiselect.view.store.getAt(selectionsArray[i]);
\r
2886 records.push(record);
\r
2888 if(!this.allowDup)selectionsArray = [];
\r
2889 for (var i=0; i<records.length; i++) {
\r
2890 record = records[i];
\r
2891 if(this.allowDup){
\r
2892 var x=new Ext.data.Record();
\r
2895 this.toMultiselect.view.store.add(record);
\r
2897 this.fromMultiselect.view.store.remove(record);
\r
2898 this.toMultiselect.view.store.add(record);
\r
2899 selectionsArray.push((this.toMultiselect.view.store.getCount() - 1));
\r
2903 this.toMultiselect.view.refresh();
\r
2904 this.fromMultiselect.view.refresh();
\r
2905 var si = this.toMultiselect.store.sortInfo;
\r
2907 this.toMultiselect.store.sort(si.field, si.direction);
\r
2909 this.toMultiselect.view.select(selectionsArray);
\r
2912 toFrom : function() {
\r
2913 var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
\r
2915 if (selectionsArray.length > 0) {
\r
2916 for (var i=0; i<selectionsArray.length; i++) {
\r
2917 record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
\r
2918 records.push(record);
\r
2920 selectionsArray = [];
\r
2921 for (var i=0; i<records.length; i++) {
\r
2922 record = records[i];
\r
2923 this.toMultiselect.view.store.remove(record);
\r
2924 if(!this.allowDup){
\r
2925 this.fromMultiselect.view.store.add(record);
\r
2926 selectionsArray.push((this.fromMultiselect.view.store.getCount() - 1));
\r
2930 this.fromMultiselect.view.refresh();
\r
2931 this.toMultiselect.view.refresh();
\r
2932 var si = this.fromMultiselect.store.sortInfo;
\r
2934 this.fromMultiselect.store.sort(si.field, si.direction);
\r
2936 this.fromMultiselect.view.select(selectionsArray);
\r
2939 valueChanged: function(store) {
\r
2940 var record = null;
\r
2942 for (var i=0; i<store.getCount(); i++) {
\r
2943 record = store.getAt(i);
\r
2944 values.push(record.get(this.toMultiselect.valueField));
\r
2946 this.hiddenField.dom.value = values.join(this.delimiter);
\r
2947 this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value);
\r
2950 getValue : function() {
\r
2951 return this.hiddenField.dom.value;
\r
2954 onRowDblClick : function(vw, index, node, e) {
\r
2955 if (vw == this.toMultiselect.view){
\r
2957 } else if (vw == this.fromMultiselect.view) {
\r
2960 return this.fireEvent('rowdblclick', vw, index, node, e);
\r
2963 reset: function(){
\r
2964 range = this.toMultiselect.store.getRange();
\r
2965 this.toMultiselect.store.removeAll();
\r
2966 this.fromMultiselect.store.add(range);
\r
2967 var si = this.fromMultiselect.store.sortInfo;
\r
2969 this.fromMultiselect.store.sort(si.field, si.direction);
\r
2971 this.valueChanged(this.toMultiselect.store);
\r
2975 Ext.reg('itemselector', Ext.ux.form.ItemSelector);
\r
2977 //backwards compat
\r
2978 Ext.ux.ItemSelector = Ext.ux.form.ItemSelector;
\r
2979 Ext.ns('Ext.ux.form');
\r
2982 * @class Ext.ux.form.MultiSelect
\r
2983 * @extends Ext.form.Field
\r
2984 * A control that allows selection and form submission of multiple list items.
\r
2987 * 2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams)
\r
2988 * 2008-06-19 bpm Docs and demo code clean up
\r
2991 * Create a new MultiSelect
\r
2992 * @param {Object} config Configuration options
\r
2993 * @xtype multiselect
\r
2995 Ext.ux.form.MultiSelect = Ext.extend(Ext.form.Field, {
\r
2997 * @cfg {String} legend Wraps the object with a fieldset and specified legend.
\r
3000 * @cfg {Ext.ListView} view The {@link Ext.ListView} used to render the multiselect list.
\r
3003 * @cfg {String/Array} dragGroup The ddgroup name(s) for the MultiSelect DragZone (defaults to undefined).
\r
3006 * @cfg {String/Array} dropGroup The ddgroup name(s) for the MultiSelect DropZone (defaults to undefined).
\r
3009 * @cfg {Boolean} ddReorder Whether the items in the MultiSelect list are drag/drop reorderable (defaults to false).
\r
3013 * @cfg {Object/Array} tbar The top toolbar of the control. This can be a {@link Ext.Toolbar} object, a
\r
3014 * toolbar config, or an array of buttons/button configs to be added to the toolbar.
\r
3017 * @cfg {String} appendOnly True if the list should only allow append drops when drag/drop is enabled
\r
3018 * (use for lists which are sorted, defaults to false).
\r
3022 * @cfg {Number} width Width in pixels of the control (defaults to 100).
\r
3026 * @cfg {Number} height Height in pixels of the control (defaults to 100).
\r
3030 * @cfg {String/Number} displayField Name/Index of the desired display field in the dataset (defaults to 0).
\r
3034 * @cfg {String/Number} valueField Name/Index of the desired value field in the dataset (defaults to 1).
\r
3038 * @cfg {Boolean} allowBlank False to require at least one item in the list to be selected, true to allow no
\r
3039 * selection (defaults to true).
\r
3043 * @cfg {Number} minSelections Minimum number of selections allowed (defaults to 0).
\r
3047 * @cfg {Number} maxSelections Maximum number of selections allowed (defaults to Number.MAX_VALUE).
\r
3049 maxSelections:Number.MAX_VALUE,
\r
3051 * @cfg {String} blankText Default text displayed when the control contains no items (defaults to the same value as
\r
3052 * {@link Ext.form.TextField#blankText}.
\r
3054 blankText:Ext.form.TextField.prototype.blankText,
\r
3056 * @cfg {String} minSelectionsText Validation message displayed when {@link #minSelections} is not met (defaults to 'Minimum {0}
\r
3057 * item(s) required'). The {0} token will be replaced by the value of {@link #minSelections}.
\r
3059 minSelectionsText:'Minimum {0} item(s) required',
\r
3061 * @cfg {String} maxSelectionsText Validation message displayed when {@link #maxSelections} is not met (defaults to 'Maximum {0}
\r
3062 * item(s) allowed'). The {0} token will be replaced by the value of {@link #maxSelections}.
\r
3064 maxSelectionsText:'Maximum {0} item(s) allowed',
\r
3066 * @cfg {String} delimiter The string used to delimit between items when set or returned as a string of values
\r
3067 * (defaults to ',').
\r
3071 * @cfg {Ext.data.Store/Array} store The data source to which this MultiSelect is bound (defaults to <tt>undefined</tt>).
\r
3072 * Acceptable values for this property are:
\r
3073 * <div class="mdetail-params"><ul>
\r
3074 * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
\r
3075 * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally.
\r
3076 * <div class="mdetail-params"><ul>
\r
3077 * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
\r
3078 * A 1-dimensional array will automatically be expanded (each array item will be the combo
\r
3079 * {@link #valueField value} and {@link #displayField text})</div></li>
\r
3080 * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
\r
3081 * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
\r
3082 * {@link #valueField value}, while the value at index 1 is assumed to be the combo {@link #displayField text}.
\r
3083 * </div></li></ul></div></li></ul></div>
\r
3087 defaultAutoCreate : {tag: "div"},
\r
3090 initComponent: function(){
\r
3091 Ext.ux.form.MultiSelect.superclass.initComponent.call(this);
\r
3093 if(Ext.isArray(this.store)){
\r
3094 if (Ext.isArray(this.store[0])){
\r
3095 this.store = new Ext.data.ArrayStore({
\r
3096 fields: ['value','text'],
\r
3099 this.valueField = 'value';
\r
3101 this.store = new Ext.data.ArrayStore({
\r
3106 this.valueField = 'text';
\r
3108 this.displayField = 'text';
\r
3110 this.store = Ext.StoreMgr.lookup(this.store);
\r
3114 'dblclick' : true,
\r
3122 onRender: function(ct, position){
\r
3123 Ext.ux.form.MultiSelect.superclass.onRender.call(this, ct, position);
\r
3125 var fs = this.fs = new Ext.form.FieldSet({
\r
3126 renderTo: this.el,
\r
3127 title: this.legend,
\r
3128 height: this.height,
\r
3129 width: this.width,
\r
3130 style: "padding:0;",
\r
3132 bodyStyle: 'overflow: auto;'
\r
3135 this.view = new Ext.ListView({
\r
3136 multiSelect: true,
\r
3137 store: this.store,
\r
3138 columns: [{ header: 'Value', width: 1, dataIndex: this.displayField }],
\r
3142 fs.add(this.view);
\r
3144 this.view.on('click', this.onViewClick, this);
\r
3145 this.view.on('beforeclick', this.onViewBeforeClick, this);
\r
3146 this.view.on('dblclick', this.onViewDblClick, this);
\r
3148 this.hiddenName = this.name || Ext.id();
\r
3149 var hiddenTag = { tag: "input", type: "hidden", value: "", name: this.hiddenName };
\r
3150 this.hiddenField = this.el.createChild(hiddenTag);
\r
3151 this.hiddenField.dom.disabled = this.hiddenName != this.name;
\r
3156 afterRender: function(){
\r
3157 Ext.ux.form.MultiSelect.superclass.afterRender.call(this);
\r
3159 if (this.ddReorder && !this.dragGroup && !this.dropGroup){
\r
3160 this.dragGroup = this.dropGroup = 'MultiselectDD-' + Ext.id();
\r
3163 if (this.draggable || this.dragGroup){
\r
3164 this.dragZone = new Ext.ux.form.MultiSelect.DragZone(this, {
\r
3165 ddGroup: this.dragGroup
\r
3168 if (this.droppable || this.dropGroup){
\r
3169 this.dropZone = new Ext.ux.form.MultiSelect.DropZone(this, {
\r
3170 ddGroup: this.dropGroup
\r
3176 onViewClick: function(vw, index, node, e) {
\r
3177 this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value);
\r
3178 this.hiddenField.dom.value = this.getValue();
\r
3179 this.fireEvent('click', this, e);
\r
3184 onViewBeforeClick: function(vw, index, node, e) {
\r
3185 if (this.disabled) {return false;}
\r
3189 onViewDblClick : function(vw, index, node, e) {
\r
3190 return this.fireEvent('dblclick', vw, index, node, e);
\r
3194 * Returns an array of data values for the selected items in the list. The values will be separated
\r
3195 * by {@link #delimiter}.
\r
3196 * @return {Array} value An array of string data values
\r
3198 getValue: function(valueField){
\r
3199 var returnArray = [];
\r
3200 var selectionsArray = this.view.getSelectedIndexes();
\r
3201 if (selectionsArray.length == 0) {return '';}
\r
3202 for (var i=0; i<selectionsArray.length; i++) {
\r
3203 returnArray.push(this.store.getAt(selectionsArray[i]).get((valueField != null) ? valueField : this.valueField));
\r
3205 return returnArray.join(this.delimiter);
\r
3209 * Sets a delimited string (using {@link #delimiter}) or array of data values into the list.
\r
3210 * @param {String/Array} values The values to set
\r
3212 setValue: function(values) {
\r
3214 var selections = [];
\r
3215 this.view.clearSelections();
\r
3216 this.hiddenField.dom.value = '';
\r
3218 if (!values || (values == '')) { return; }
\r
3220 if (!Ext.isArray(values)) { values = values.split(this.delimiter); }
\r
3221 for (var i=0; i<values.length; i++) {
\r
3222 index = this.view.store.indexOf(this.view.store.query(this.valueField,
\r
3223 new RegExp('^' + values[i] + '$', "i")).itemAt(0));
\r
3224 selections.push(index);
\r
3226 this.view.select(selections);
\r
3227 this.hiddenField.dom.value = this.getValue();
\r
3232 reset : function() {
\r
3233 this.setValue('');
\r
3237 getRawValue: function(valueField) {
\r
3238 var tmp = this.getValue(valueField);
\r
3240 tmp = tmp.split(this.delimiter);
\r
3249 setRawValue: function(values){
\r
3254 validateValue : function(value){
\r
3255 if (value.length < 1) { // if it has no value
\r
3256 if (this.allowBlank) {
\r
3257 this.clearInvalid();
\r
3260 this.markInvalid(this.blankText);
\r
3264 if (value.length < this.minSelections) {
\r
3265 this.markInvalid(String.format(this.minSelectionsText, this.minSelections));
\r
3268 if (value.length > this.maxSelections) {
\r
3269 this.markInvalid(String.format(this.maxSelectionsText, this.maxSelections));
\r
3276 disable: function(){
\r
3277 this.disabled = true;
\r
3278 this.hiddenField.dom.disabled = true;
\r
3279 this.fs.disable();
\r
3283 enable: function(){
\r
3284 this.disabled = false;
\r
3285 this.hiddenField.dom.disabled = false;
\r
3290 destroy: function(){
\r
3291 Ext.destroy(this.fs, this.dragZone, this.dropZone);
\r
3292 Ext.ux.form.MultiSelect.superclass.destroy.call(this);
\r
3297 Ext.reg('multiselect', Ext.ux.form.MultiSelect);
\r
3299 //backwards compat
\r
3300 Ext.ux.Multiselect = Ext.ux.form.MultiSelect;
\r
3303 Ext.ux.form.MultiSelect.DragZone = function(ms, config){
\r
3305 this.view = ms.view;
\r
3306 var ddGroup = config.ddGroup || 'MultiselectDD';
\r
3308 if (Ext.isArray(ddGroup)){
\r
3309 dd = ddGroup.shift();
\r
3314 Ext.ux.form.MultiSelect.DragZone.superclass.constructor.call(this, this.ms.fs.body, { containerScroll: true, ddGroup: dd });
\r
3315 this.setDraggable(ddGroup);
\r
3318 Ext.extend(Ext.ux.form.MultiSelect.DragZone, Ext.dd.DragZone, {
\r
3319 onInitDrag : function(x, y){
\r
3320 var el = Ext.get(this.dragData.ddel.cloneNode(true));
\r
3321 this.proxy.update(el.dom);
\r
3322 el.setWidth(el.child('em').getWidth());
\r
3323 this.onStartDrag(x, y);
\r
3328 collectSelection: function(data) {
\r
3329 data.repairXY = Ext.fly(this.view.getSelectedNodes()[0]).getXY();
\r
3331 this.view.store.each(function(rec){
\r
3332 if (this.view.isSelected(i)) {
\r
3333 var n = this.view.getNode(i);
\r
3334 var dragNode = n.cloneNode(true);
\r
3335 dragNode.id = Ext.id();
\r
3336 data.ddel.appendChild(dragNode);
\r
3337 data.records.push(this.view.store.getAt(i));
\r
3338 data.viewNodes.push(n);
\r
3345 onEndDrag: function(data, e) {
\r
3346 var d = Ext.get(this.dragData.ddel);
\r
3347 if (d && d.hasClass("multi-proxy")) {
\r
3353 getDragData: function(e){
\r
3354 var target = this.view.findItemFromChild(e.getTarget());
\r
3356 if (!this.view.isSelected(target) && !e.ctrlKey && !e.shiftKey) {
\r
3357 this.view.select(target);
\r
3358 this.ms.setValue(this.ms.getValue());
\r
3360 if (this.view.getSelectionCount() == 0 || e.ctrlKey || e.shiftKey) return false;
\r
3362 sourceView: this.view,
\r
3366 if (this.view.getSelectionCount() == 1) {
\r
3367 var i = this.view.getSelectedIndexes()[0];
\r
3368 var n = this.view.getNode(i);
\r
3369 dragData.viewNodes.push(dragData.ddel = n);
\r
3370 dragData.records.push(this.view.store.getAt(i));
\r
3371 dragData.repairXY = Ext.fly(n).getXY();
\r
3373 dragData.ddel = document.createElement('div');
\r
3374 dragData.ddel.className = 'multi-proxy';
\r
3375 this.collectSelection(dragData);
\r
3382 // override the default repairXY.
\r
3383 getRepairXY : function(e){
\r
3384 return this.dragData.repairXY;
\r
3388 setDraggable: function(ddGroup){
\r
3389 if (!ddGroup) return;
\r
3390 if (Ext.isArray(ddGroup)) {
\r
3391 Ext.each(ddGroup, this.setDraggable, this);
\r
3394 this.addToGroup(ddGroup);
\r
3398 Ext.ux.form.MultiSelect.DropZone = function(ms, config){
\r
3400 this.view = ms.view;
\r
3401 var ddGroup = config.ddGroup || 'MultiselectDD';
\r
3403 if (Ext.isArray(ddGroup)){
\r
3404 dd = ddGroup.shift();
\r
3409 Ext.ux.form.MultiSelect.DropZone.superclass.constructor.call(this, this.ms.fs.body, { containerScroll: true, ddGroup: dd });
\r
3410 this.setDroppable(ddGroup);
\r
3413 Ext.extend(Ext.ux.form.MultiSelect.DropZone, Ext.dd.DropZone, {
\r
3415 * Part of the Ext.dd.DropZone interface. If no target node is found, the
\r
3416 * whole Element becomes the target, and this causes the drop gesture to append.
\r
3418 getTargetFromEvent : function(e) {
\r
3419 var target = e.getTarget();
\r
3424 getDropPoint : function(e, n, dd){
\r
3425 if (n == this.ms.fs.body.dom) { return "below"; }
\r
3426 var t = Ext.lib.Dom.getY(n), b = t + n.offsetHeight;
\r
3427 var c = t + (b - t) / 2;
\r
3428 var y = Ext.lib.Event.getPageY(e);
\r
3437 isValidDropPoint: function(pt, n, data) {
\r
3438 if (!data.viewNodes || (data.viewNodes.length != 1)) {
\r
3441 var d = data.viewNodes[0];
\r
3445 if ((pt == "below") && (n.nextSibling == d)) {
\r
3448 if ((pt == "above") && (n.previousSibling == d)) {
\r
3455 onNodeEnter : function(n, dd, e, data){
\r
3460 onNodeOver : function(n, dd, e, data){
\r
3461 var dragElClass = this.dropNotAllowed;
\r
3462 var pt = this.getDropPoint(e, n, dd);
\r
3463 if (this.isValidDropPoint(pt, n, data)) {
\r
3464 if (this.ms.appendOnly) {
\r
3465 return "x-tree-drop-ok-below";
\r
3468 // set the insert point style on the target node
\r
3470 var targetElClass;
\r
3471 if (pt == "above"){
\r
3472 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
\r
3473 targetElClass = "x-view-drag-insert-above";
\r
3475 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
\r
3476 targetElClass = "x-view-drag-insert-below";
\r
3478 if (this.lastInsertClass != targetElClass){
\r
3479 Ext.fly(n).replaceClass(this.lastInsertClass, targetElClass);
\r
3480 this.lastInsertClass = targetElClass;
\r
3484 return dragElClass;
\r
3488 onNodeOut : function(n, dd, e, data){
\r
3489 this.removeDropIndicators(n);
\r
3493 onNodeDrop : function(n, dd, e, data){
\r
3494 if (this.ms.fireEvent("drop", this, n, dd, e, data) === false) {
\r
3497 var pt = this.getDropPoint(e, n, dd);
\r
3498 if (n != this.ms.fs.body.dom)
\r
3499 n = this.view.findItemFromChild(n);
\r
3500 var insertAt = (this.ms.appendOnly || (n == this.ms.fs.body.dom)) ? this.view.store.getCount() : this.view.indexOf(n);
\r
3501 if (pt == "below") {
\r
3507 // Validate if dragging within the same MultiSelect
\r
3508 if (data.sourceView == this.view) {
\r
3509 // If the first element to be inserted below is the target node, remove it
\r
3510 if (pt == "below") {
\r
3511 if (data.viewNodes[0] == n) {
\r
3512 data.viewNodes.shift();
\r
3514 } else { // If the last element to be inserted above is the target node, remove it
\r
3515 if (data.viewNodes[data.viewNodes.length - 1] == n) {
\r
3516 data.viewNodes.pop();
\r
3520 // Nothing to drop...
\r
3521 if (!data.viewNodes.length) {
\r
3525 // If we are moving DOWN, then because a store.remove() takes place first,
\r
3526 // the insertAt must be decremented.
\r
3527 if (insertAt > this.view.store.indexOf(data.records[0])) {
\r
3533 for (var i = 0; i < data.records.length; i++) {
\r
3534 var r = data.records[i];
\r
3535 if (data.sourceView) {
\r
3536 data.sourceView.store.remove(r);
\r
3538 this.view.store.insert(dir == 'down' ? insertAt : insertAt++, r);
\r
3539 var si = this.view.store.sortInfo;
\r
3541 this.view.store.sort(si.field, si.direction);
\r
3548 removeDropIndicators : function(n){
\r
3550 Ext.fly(n).removeClass([
\r
3551 "x-view-drag-insert-above",
\r
3552 "x-view-drag-insert-left",
\r
3553 "x-view-drag-insert-right",
\r
3554 "x-view-drag-insert-below"]);
\r
3555 this.lastInsertClass = "_noclass";
\r
3560 setDroppable: function(ddGroup){
\r
3561 if (!ddGroup) return;
\r
3562 if (Ext.isArray(ddGroup)) {
\r
3563 Ext.each(ddGroup, this.setDroppable, this);
\r
3566 this.addToGroup(ddGroup);
\r
3570 /* Fix for Opera, which does not seem to include the map function on Array's */
3571 if (!Array.prototype.map) {
3572 Array.prototype.map = function(fun){
3573 var len = this.length;
3574 if (typeof fun != 'function') {
3575 throw new TypeError();
3577 var res = new Array(len);
3578 var thisp = arguments[1];
3579 for (var i = 0; i < len; i++) {
3581 res[i] = fun.call(thisp, this[i], i, this);
3588 Ext.ns('Ext.ux.data');
3591 * @class Ext.ux.data.PagingMemoryProxy
3592 * @extends Ext.data.MemoryProxy
3593 * <p>Paging Memory Proxy, allows to use paging grid with in memory dataset</p>
3595 Ext.ux.data.PagingMemoryProxy = Ext.extend(Ext.data.MemoryProxy, {
3596 constructor : function(data){
3597 Ext.ux.data.PagingMemoryProxy.superclass.constructor.call(this);
3600 doRequest : function(action, rs, params, reader, callback, scope, options){
3605 result = reader.readRecords(this.data);
3608 this.fireEvent('loadexception', this, options, null, e);
3609 callback.call(scope, null, options, false);
3614 if (params.filter !== undefined) {
3615 result.records = result.records.filter(function(el){
3616 if (typeof(el) == 'object') {
3617 var att = params.filterCol || 0;
3618 return String(el.data[att]).match(params.filter) ? true : false;
3621 return String(el).match(params.filter) ? true : false;
3624 result.totalRecords = result.records.length;
3628 if (params.sort !== undefined) {
3629 // use integer as params.sort to specify column, since arrays are not named
3630 // params.sort=0; would also match a array without columns
3631 var dir = String(params.dir).toUpperCase() == 'DESC' ? -1 : 1;
3632 var fn = function(r1, r2){
3635 result.records.sort(function(a, b){
3637 if (typeof(a) == 'object') {
3638 v = fn(a.data[params.sort], b.data[params.sort]) * dir;
3644 v = (a.index < b.index ? -1 : 1);
3649 // paging (use undefined cause start can also be 0 (thus false))
3650 if (params.start !== undefined && params.limit !== undefined) {
3651 result.records = result.records.slice(params.start, params.start + params.limit);
3653 callback.call(scope, result, options, true);
3658 Ext.data.PagingMemoryProxy = Ext.ux.data.PagingMemoryProxy;
3659 Ext.ux.PanelResizer = Ext.extend(Ext.util.Observable, {
\r
3661 maxHeight:10000000,
\r
3663 constructor: function(config){
\r
3664 Ext.apply(this, config);
\r
3666 Ext.ux.PanelResizer.superclass.constructor.call(this, config);
\r
3669 init : function(p){
\r
3672 if(this.panel.elements.indexOf('footer')==-1){
\r
3673 p.elements += ',footer';
\r
3675 p.on('render', this.onRender, this);
\r
3678 onRender : function(p){
\r
3679 this.handle = p.footer.createChild({cls:'x-panel-resize'});
\r
3681 this.tracker = new Ext.dd.DragTracker({
\r
3682 onStart: this.onDragStart.createDelegate(this),
\r
3683 onDrag: this.onDrag.createDelegate(this),
\r
3684 onEnd: this.onDragEnd.createDelegate(this),
\r
3688 this.tracker.initEl(this.handle);
\r
3689 p.on('beforedestroy', this.tracker.destroy, this.tracker);
\r
3693 onDragStart: function(e){
\r
3694 this.dragging = true;
\r
3695 this.startHeight = this.panel.el.getHeight();
\r
3696 this.fireEvent('dragstart', this, e);
\r
3700 onDrag: function(e){
\r
3701 this.panel.setHeight((this.startHeight-this.tracker.getOffset()[1]).constrain(this.minHeight, this.maxHeight));
\r
3702 this.fireEvent('drag', this, e);
\r
3706 onDragEnd: function(e){
\r
3707 this.dragging = false;
\r
3708 this.fireEvent('dragend', this, e);
\r
3711 Ext.preg('panelresizer', Ext.ux.PanelResizer);Ext.ux.Portal = Ext.extend(Ext.Panel, {
\r
3712 layout : 'column',
\r
3713 autoScroll : true,
\r
3715 defaultType : 'portalcolumn',
\r
3717 initComponent : function(){
\r
3718 Ext.ux.Portal.superclass.initComponent.call(this);
\r
3720 validatedrop:true,
\r
3721 beforedragover:true,
\r
3728 initEvents : function(){
\r
3729 Ext.ux.Portal.superclass.initEvents.call(this);
\r
3730 this.dd = new Ext.ux.Portal.DropZone(this, this.dropConfig);
\r
3733 beforeDestroy : function() {
\r
3737 Ext.ux.Portal.superclass.beforeDestroy.call(this);
\r
3741 Ext.reg('portal', Ext.ux.Portal);
\r
3744 Ext.ux.Portal.DropZone = function(portal, cfg){
\r
3745 this.portal = portal;
\r
3746 Ext.dd.ScrollManager.register(portal.body);
\r
3747 Ext.ux.Portal.DropZone.superclass.constructor.call(this, portal.bwrap.dom, cfg);
\r
3748 portal.body.ddScrollConfig = this.ddScrollConfig;
\r
3751 Ext.extend(Ext.ux.Portal.DropZone, Ext.dd.DropTarget, {
\r
3752 ddScrollConfig : {
\r
3759 createEvent : function(dd, e, data, col, c, pos){
\r
3761 portal: this.portal,
\r
3762 panel: data.panel,
\r
3769 status: this.dropAllowed
\r
3773 notifyOver : function(dd, e, data){
\r
3774 var xy = e.getXY(), portal = this.portal, px = dd.proxy;
\r
3776 // case column widths
\r
3778 this.grid = this.getGrid();
\r
3781 // handle case scroll where scrollbars appear during drag
\r
3782 var cw = portal.body.dom.clientWidth;
\r
3785 }else if(this.lastCW != cw){
\r
3787 portal.doLayout();
\r
3788 this.grid = this.getGrid();
\r
3791 // determine column
\r
3792 var col = 0, xs = this.grid.columnX, cmatch = false;
\r
3793 for(var len = xs.length; col < len; col++){
\r
3794 if(xy[0] < (xs[col].x + xs[col].w)){
\r
3799 // no match, fix last index
\r
3804 // find insert position
\r
3805 var p, match = false, pos = 0,
\r
3806 c = portal.items.itemAt(col),
\r
3807 items = c.items.items, overSelf = false;
\r
3809 for(var len = items.length; pos < len; pos++){
\r
3811 var h = p.el.getHeight();
\r
3815 else if((p.el.getY()+(h/2)) > xy[1]){
\r
3821 pos = (match && p ? pos : c.items.getCount()) + (overSelf ? -1 : 0);
\r
3822 var overEvent = this.createEvent(dd, e, data, col, c, pos);
\r
3824 if(portal.fireEvent('validatedrop', overEvent) !== false &&
\r
3825 portal.fireEvent('beforedragover', overEvent) !== false){
\r
3827 // make sure proxy width is fluid
\r
3828 px.getProxy().setWidth('auto');
\r
3831 px.moveProxy(p.el.dom.parentNode, match ? p.el.dom : null);
\r
3833 px.moveProxy(c.el.dom, null);
\r
3836 this.lastPos = {c: c, col: col, p: overSelf || (match && p) ? pos : false};
\r
3837 this.scrollPos = portal.body.getScroll();
\r
3839 portal.fireEvent('dragover', overEvent);
\r
3841 return overEvent.status;
\r
3843 return overEvent.status;
\r
3848 notifyOut : function(){
\r
3852 notifyDrop : function(dd, e, data){
\r
3854 if(!this.lastPos){
\r
3857 var c = this.lastPos.c, col = this.lastPos.col, pos = this.lastPos.p;
\r
3859 var dropEvent = this.createEvent(dd, e, data, col, c,
\r
3860 pos !== false ? pos : c.items.getCount());
\r
3862 if(this.portal.fireEvent('validatedrop', dropEvent) !== false &&
\r
3863 this.portal.fireEvent('beforedrop', dropEvent) !== false){
\r
3865 dd.proxy.getProxy().remove();
\r
3866 dd.panel.el.dom.parentNode.removeChild(dd.panel.el.dom);
\r
3868 if(pos !== false){
\r
3869 if(c == dd.panel.ownerCt && (c.items.items.indexOf(dd.panel) <= pos)){
\r
3872 c.insert(pos, dd.panel);
\r
3879 this.portal.fireEvent('drop', dropEvent);
\r
3881 // scroll position is lost on drop, fix it
\r
3882 var st = this.scrollPos.top;
\r
3884 var d = this.portal.body.dom;
\r
3885 setTimeout(function(){
\r
3891 delete this.lastPos;
\r
3894 // internal cache of body and column coords
\r
3895 getGrid : function(){
\r
3896 var box = this.portal.bwrap.getBox();
\r
3898 this.portal.items.each(function(c){
\r
3899 box.columnX.push({x: c.el.getX(), w: c.el.getWidth()});
\r
3904 // unregister the dropzone from ScrollManager
\r
3905 unreg: function() {
\r
3906 //Ext.dd.ScrollManager.unregister(this.portal.body);
\r
3907 Ext.ux.Portal.DropZone.superclass.unreg.call(this);
\r
3910 Ext.ux.PortalColumn = Ext.extend(Ext.Container, {
\r
3911 layout : 'anchor',
\r
3912 //autoEl : 'div',//already defined by Ext.Component
\r
3913 defaultType : 'portlet',
\r
3914 cls : 'x-portal-column'
\r
3917 Ext.reg('portalcolumn', Ext.ux.PortalColumn);
\r
3918 Ext.ux.Portlet = Ext.extend(Ext.Panel, {
\r
3921 collapsible : true,
\r
3926 Ext.reg('portlet', Ext.ux.Portlet);
\r
3928 * @class Ext.ux.ProgressBarPager
3930 * Plugin (ptype = 'tabclosemenu') for displaying a progressbar inside of a paging toolbar instead of plain text
3932 * @ptype progressbarpager
3934 * Create a new ItemSelector
3935 * @param {Object} config Configuration options
3936 * @xtype itemselector
3938 Ext.ux.ProgressBarPager = Ext.extend(Object, {
3940 * @cfg {Integer} progBarWidth
3941 * <p>The default progress bar width. Default is 225.</p>
3945 * @cfg {String} defaultText
3946 * <p>The text to display while the store is loading. Default is 'Loading...'</p>
3948 defaultText : 'Loading...',
3950 * @cfg {Object} defaultAnimCfg
3951 * <p>A {@link Ext.Fx Ext.Fx} configuration object. Default is { duration : 1, easing : 'bounceOut' }.</p>
3955 easing : 'bounceOut'
3957 constructor : function(config) {
3959 Ext.apply(this, config);
3963 init : function (parent) {
3965 if(parent.displayInfo){
3966 this.parent = parent;
3967 var ind = parent.items.indexOf(parent.displayItem);
3968 parent.remove(parent.displayItem, true);
3969 this.progressBar = new Ext.ProgressBar({
3970 text : this.defaultText,
3971 width : this.progBarWidth,
3972 animate : this.defaultAnimCfg
3975 parent.displayItem = this.progressBar;
3977 parent.add(parent.displayItem);
3979 Ext.apply(parent, this.parentOverrides);
3981 this.progressBar.on('render', function(pb) {
3982 pb.el.applyStyles('cursor:pointer');
3984 pb.el.on('click', this.handleProgressBarClick, this);
3988 // Remove the click handler from the
3989 this.progressBar.on({
3991 beforeDestroy : function() {
3992 this.progressBar.el.un('click', this.handleProgressBarClick, this);
4000 // This method handles the click for the progress bar
4001 handleProgressBarClick : function(e){
4002 var parent = this.parent;
4003 var displayItem = parent.displayItem;
4005 var box = this.progressBar.getBox();
4007 var position = xy[0]-box.x;
4008 var pages = Math.ceil(parent.store.getTotalCount()/parent.pageSize);
4010 var newpage = Math.ceil(position/(displayItem.width/pages));
4011 parent.changePage(newpage);
4014 // private, overriddes
4017 // This method updates the information via the progress bar.
4018 updateInfo : function(){
4019 if(this.displayItem){
4020 var count = this.store.getCount();
4021 var pgData = this.getPageData();
4022 var pageNum = this.readPage(pgData);
4024 var msg = count == 0 ?
4028 this.cursor+1, this.cursor+count, this.store.getTotalCount()
4031 pageNum = pgData.activePage; ;
4033 var pct = pageNum / pgData.pages;
4035 this.displayItem.updateProgress(pct, msg, this.animate || this.defaultAnimConfig);
4040 Ext.preg('progressbarpager', Ext.ux.ProgressBarPager);
4042 Ext.ns('Ext.ux.grid');
4045 * @class Ext.ux.grid.RowEditor
4046 * @extends Ext.Panel
4047 * Plugin (ptype = 'roweditor') that adds the ability to rapidly edit full rows in a grid.
4048 * A validation mode may be enabled which uses AnchorTips to notify the user of all
4049 * validation errors at once.
4053 Ext.ux.grid.RowEditor = Ext.extend(Ext.Panel, {
4057 cls: 'x-small-editor',
4058 buttonAlign: 'center',
4059 baseCls: 'x-row-editor',
4060 elements: 'header,footer,body',
4063 clicksToEdit: 'auto',
4072 initComponent: function(){
4073 Ext.ux.grid.RowEditor.superclass.initComponent.call(this);
4077 * Fired before the row editor is activated.
4078 * If the listener returns <tt>false</tt> the editor will not be activated.
4079 * @param {Ext.ux.grid.RowEditor} roweditor This object
4080 * @param {Number} rowIndex The rowIndex of the row just edited
4084 * @event validateedit
4085 * Fired after a row is edited and passes validation.
4086 * If the listener returns <tt>false</tt> changes to the record will not be set.
4087 * @param {Ext.ux.grid.RowEditor} roweditor This object
4088 * @param {Object} changes Object with changes made to the record.
4089 * @param {Ext.data.Record} r The Record that was edited.
4090 * @param {Number} rowIndex The rowIndex of the row just edited
4095 * Fired after a row is edited and passes validation. This event is fired
4096 * after the store's update event is fired with this edit.
4097 * @param {Ext.ux.grid.RowEditor} roweditor This object
4098 * @param {Object} changes Object with changes made to the record.
4099 * @param {Ext.data.Record} r The Record that was edited.
4100 * @param {Number} rowIndex The rowIndex of the row just edited
4106 init: function(grid){
4108 this.ownerCt = grid;
4109 if(this.clicksToEdit === 2){
4110 grid.on('rowdblclick', this.onRowDblClick, this);
4112 grid.on('rowclick', this.onRowClick, this);
4114 grid.on('rowdblclick', this.onRowDblClick, this);
4118 // stopEditing without saving when a record is removed from Store.
4119 grid.getStore().on('remove', function() {
4120 this.stopEditing(false);
4125 keydown: this.onGridKey,
4126 columnresize: this.verifyLayout,
4127 columnmove: this.refreshFields,
4128 reconfigure: this.refreshFields,
4129 destroy : this.destroy,
4132 fn: this.positionButtons
4135 grid.getColumnModel().on('hiddenchange', this.verifyLayout, this, {delay:1});
4136 grid.getView().on('refresh', this.stopEditing.createDelegate(this, []));
4139 refreshFields: function(){
4141 this.verifyLayout();
4144 isDirty: function(){
4146 this.items.each(function(f){
4147 if(String(this.values[f.id]) !== String(f.getValue())){
4155 startEditing: function(rowIndex, doFocus){
4156 if(this.editing && this.isDirty()){
4157 this.showTooltip('You need to commit or cancel your changes');
4160 this.editing = true;
4161 if(typeof rowIndex == 'object'){
4162 rowIndex = this.grid.getStore().indexOf(rowIndex);
4164 if(this.fireEvent('beforeedit', this, rowIndex) !== false){
4165 var g = this.grid, view = g.getView();
4166 var row = view.getRow(rowIndex);
4167 var record = g.store.getAt(rowIndex);
4168 this.record = record;
4169 this.rowIndex = rowIndex;
4172 this.render(view.getEditorParent());
4174 var w = Ext.fly(row).getWidth();
4176 if(!this.initialized){
4179 var cm = g.getColumnModel(), fields = this.items.items, f, val;
4180 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4181 val = this.preEditValue(record, cm.getDataIndex(i));
4184 this.values[f.id] = val || '';
4186 this.verifyLayout(true);
4187 if(!this.isVisible()){
4188 this.setPagePosition(Ext.fly(row).getXY());
4190 this.el.setXY(Ext.fly(row).getXY(), {duration:0.15});
4192 if(!this.isVisible()){
4193 this.show().doLayout();
4195 if(doFocus !== false){
4196 this.doFocus.defer(this.focusDelay, this);
4201 stopEditing : function(saveChanges){
4202 this.editing = false;
4203 if(!this.isVisible()){
4206 if(saveChanges === false || !this.isValid()){
4210 var changes = {}, r = this.record, hasChange = false;
4211 var cm = this.grid.colModel, fields = this.items.items;
4212 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4213 if(!cm.isHidden(i)){
4214 var dindex = cm.getDataIndex(i);
4215 if(!Ext.isEmpty(dindex)){
4216 var oldValue = r.data[dindex];
4217 var value = this.postEditValue(fields[i].getValue(), oldValue, r, dindex);
4218 if(String(oldValue) !== String(value)){
4219 changes[dindex] = value;
4225 if(hasChange && this.fireEvent('validateedit', this, changes, r, this.rowIndex) !== false){
4227 for(var k in changes){
4228 if(changes.hasOwnProperty(k)){
4229 r.set(k, changes[k]);
4233 this.fireEvent('afteredit', this, changes, r, this.rowIndex);
4238 verifyLayout: function(force){
4239 if(this.el && (this.isVisible() || force === true)){
4240 var row = this.grid.getView().getRow(this.rowIndex);
4241 this.setSize(Ext.fly(row).getWidth(), Ext.isIE ? Ext.fly(row).getHeight() + (Ext.isBorderBox ? 9 : 0) : undefined);
4242 var cm = this.grid.colModel, fields = this.items.items;
4243 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4244 if(!cm.isHidden(i)){
4247 adjust += 0; // outer padding
4249 if(i === (len - 1)){
4250 adjust += 3; // outer padding
4255 fields[i].setWidth(cm.getColumnWidth(i) - adjust);
4261 this.positionButtons();
4265 slideHide : function(){
4269 initFields: function(){
4270 var cm = this.grid.getColumnModel(), pm = Ext.layout.ContainerLayout.prototype.parseMargins;
4271 this.removeAll(false);
4272 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4273 var c = cm.getColumnAt(i);
4274 var ed = c.getEditor();
4276 ed = c.displayEditor || new Ext.form.DisplayField();
4279 ed.margins = pm('0 1 2 1');
4280 } else if(i == len - 1){
4281 ed.margins = pm('0 0 2 1');
4283 ed.margins = pm('0 1 2');
4285 ed.setWidth(cm.getColumnWidth(i));
4287 if(ed.ownerCt !== this){
4288 ed.on('focus', this.ensureVisible, this);
4289 ed.on('specialkey', this.onKey, this);
4293 this.initialized = true;
4296 onKey: function(f, e){
4297 if(e.getKey() === e.ENTER){
4298 this.stopEditing(true);
4299 e.stopPropagation();
4303 onGridKey: function(e){
4304 if(e.getKey() === e.ENTER && !this.isVisible()){
4305 var r = this.grid.getSelectionModel().getSelected();
4307 var index = this.grid.store.indexOf(r);
4308 this.startEditing(index);
4309 e.stopPropagation();
4314 ensureVisible: function(editor){
4315 if(this.isVisible()){
4316 this.grid.getView().ensureVisible(this.rowIndex, this.grid.colModel.getIndexById(editor.column.id), true);
4320 onRowClick: function(g, rowIndex, e){
4321 if(this.clicksToEdit == 'auto'){
4322 var li = this.lastClickIndex;
4323 this.lastClickIndex = rowIndex;
4324 if(li != rowIndex && !this.isVisible()){
4328 this.startEditing(rowIndex, false);
4329 this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
4332 onRowDblClick: function(g, rowIndex, e){
4333 this.startEditing(rowIndex, false);
4334 this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
4337 onRender: function(){
4338 Ext.ux.grid.RowEditor.superclass.onRender.apply(this, arguments);
4339 this.el.swallowEvent(['keydown', 'keyup', 'keypress']);
4340 this.btns = new Ext.Panel({
4345 width: (this.minButtonWidth * 2) + (this.frameWidth * 2) + (this.buttonPad * 4), // width must be specified for IE
4350 text: this.saveText || 'Save',
4351 width: this.minButtonWidth,
4352 handler: this.stopEditing.createDelegate(this, [true])
4355 text: this.cancelText || 'Cancel',
4356 width: this.minButtonWidth,
4357 handler: this.stopEditing.createDelegate(this, [false])
4360 this.btns.render(this.bwrap);
4363 afterRender: function(){
4364 Ext.ux.grid.RowEditor.superclass.afterRender.apply(this, arguments);
4365 this.positionButtons();
4366 if(this.monitorValid){
4367 this.startMonitoring();
4372 if(this.monitorValid){
4373 this.startMonitoring();
4375 Ext.ux.grid.RowEditor.superclass.onShow.apply(this, arguments);
4379 Ext.ux.grid.RowEditor.superclass.onHide.apply(this, arguments);
4380 this.stopMonitoring();
4381 this.grid.getView().focusRow(this.rowIndex);
4384 positionButtons: function(){
4386 var h = this.el.dom.clientHeight;
4387 var view = this.grid.getView();
4388 var scroll = view.scroller.dom.scrollLeft;
4389 var width = view.mainBody.getWidth();
4390 var bw = this.btns.getWidth();
4391 this.btns.el.shift({left: (width/2)-(bw/2)+scroll, top: h - 2, stopFx: true, duration:0.2});
4396 preEditValue : function(r, field){
4397 var value = r.data[field];
4398 return this.autoEncode && typeof value === 'string' ? Ext.util.Format.htmlDecode(value) : value;
4402 postEditValue : function(value, originalValue, r, field){
4403 return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlEncode(value) : value;
4406 doFocus: function(pt){
4407 if(this.isVisible()){
4410 index = this.getTargetColumnIndex(pt);
4412 var cm = this.grid.getColumnModel();
4413 for(var i = index||0, len = cm.getColumnCount(); i < len; i++){
4414 var c = cm.getColumnAt(i);
4415 if(!c.hidden && c.getEditor()){
4416 c.getEditor().focus();
4423 getTargetColumnIndex: function(pt){
4424 var grid = this.grid, v = grid.view;
4426 var cms = grid.colModel.config;
4427 var i = 0, match = false;
4428 for(var len = cms.length, c; c = cms[i]; i++){
4430 if(Ext.fly(v.getHeaderCell(i)).getRegion().right >= x){
4439 startMonitoring : function(){
4440 if(!this.bound && this.monitorValid){
4443 run : this.bindHandler,
4444 interval : this.monitorPoll || 200,
4450 stopMonitoring : function(){
4453 this.tooltip.hide();
4457 isValid: function(){
4459 this.items.each(function(f){
4460 if(!f.isValid(true)){
4469 bindHandler : function(){
4471 return false; // stops binding
4473 var valid = this.isValid();
4474 if(!valid && this.errorSummary){
4475 this.showTooltip(this.getErrorText().join(''));
4477 this.btns.saveBtn.setDisabled(!valid);
4478 this.fireEvent('validation', this, valid);
4481 showTooltip: function(msg){
4482 var t = this.tooltip;
4484 t = this.tooltip = new Ext.ToolTip({
4491 anchorToTarget: true,
4495 t.initTarget(this.items.last().getEl());
4505 getErrorText: function(){
4506 var data = ['<ul>'];
4507 this.items.each(function(f){
4508 if(!f.isValid(true)){
4509 data.push('<li>', f.activeError, '</li>');
4516 Ext.preg('roweditor', Ext.ux.grid.RowEditor);
4518 Ext.override(Ext.form.Field, {
4519 markInvalid : function(msg){
4520 if(!this.rendered || this.preventMark){ // not rendered
4523 msg = msg || this.invalidText;
4525 var mt = this.getMessageHandler();
4528 }else if(this.msgTarget){
4529 this.el.addClass(this.invalidClass);
4530 var t = Ext.getDom(this.msgTarget);
4533 t.style.display = this.msgDisplay;
4536 this.activeError = msg;
4537 this.fireEvent('invalid', this, msg);
4541 Ext.override(Ext.ToolTip, {
4542 doAutoWidth : function(){
4543 var bw = this.body.getTextWidth();
4545 bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));
4547 bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + 20;
4548 this.setWidth(bw.constrain(this.minWidth, this.maxWidth));
4550 // IE7 repaint bug on initial show
4551 if(Ext.isIE7 && !this.repainted){
4553 this.repainted = true;
4557 Ext.ns('Ext.ux.grid');
\r
4560 * @class Ext.ux.grid.RowExpander
\r
4561 * @extends Ext.util.Observable
\r
4562 * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables
\r
4563 * a second row body which expands/contracts. The expand/contract behavior is configurable to react
\r
4564 * on clicking of the column, double click of the row, and/or hitting enter while a row is selected.
\r
4566 * @ptype rowexpander
\r
4568 Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, {
\r
4570 * @cfg {Boolean} expandOnEnter
\r
4571 * <tt>true</tt> to toggle selected row(s) between expanded/collapsed when the enter
\r
4572 * key is pressed (defaults to <tt>true</tt>).
\r
4574 expandOnEnter : true,
\r
4576 * @cfg {Boolean} expandOnDblClick
\r
4577 * <tt>true</tt> to toggle a row between expanded/collapsed when double clicked
\r
4578 * (defaults to <tt>true</tt>).
\r
4580 expandOnDblClick : true,
\r
4586 menuDisabled : true,
\r
4589 lazyRender : true,
\r
4590 enableCaching : true,
\r
4592 constructor: function(config){
\r
4593 Ext.apply(this, config);
\r
4597 * @event beforeexpand
\r
4598 * Fires before the row expands. Have the listener return false to prevent the row from expanding.
\r
4599 * @param {Object} this RowExpander object.
\r
4600 * @param {Object} Ext.data.Record Record for the selected row.
\r
4601 * @param {Object} body body element for the secondary row.
\r
4602 * @param {Number} rowIndex The current row index.
\r
4604 beforeexpand: true,
\r
4607 * Fires after the row expands.
\r
4608 * @param {Object} this RowExpander object.
\r
4609 * @param {Object} Ext.data.Record Record for the selected row.
\r
4610 * @param {Object} body body element for the secondary row.
\r
4611 * @param {Number} rowIndex The current row index.
\r
4615 * @event beforecollapse
\r
4616 * Fires before the row collapses. Have the listener return false to prevent the row from collapsing.
\r
4617 * @param {Object} this RowExpander object.
\r
4618 * @param {Object} Ext.data.Record Record for the selected row.
\r
4619 * @param {Object} body body element for the secondary row.
\r
4620 * @param {Number} rowIndex The current row index.
\r
4622 beforecollapse: true,
\r
4625 * Fires after the row collapses.
\r
4626 * @param {Object} this RowExpander object.
\r
4627 * @param {Object} Ext.data.Record Record for the selected row.
\r
4628 * @param {Object} body body element for the secondary row.
\r
4629 * @param {Number} rowIndex The current row index.
\r
4634 Ext.ux.grid.RowExpander.superclass.constructor.call(this);
\r
4637 if(typeof this.tpl == 'string'){
\r
4638 this.tpl = new Ext.Template(this.tpl);
\r
4640 this.tpl.compile();
\r
4644 this.bodyContent = {};
\r
4647 getRowClass : function(record, rowIndex, p, ds){
\r
4648 p.cols = p.cols-1;
\r
4649 var content = this.bodyContent[record.id];
\r
4650 if(!content && !this.lazyRender){
\r
4651 content = this.getBodyContent(record, rowIndex);
\r
4656 return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
\r
4659 init : function(grid){
\r
4662 var view = grid.getView();
\r
4663 view.getRowClass = this.getRowClass.createDelegate(this);
\r
4665 view.enableRowBody = true;
\r
4668 grid.on('render', this.onRender, this);
\r
4669 grid.on('destroy', this.onDestroy, this);
\r
4673 onRender: function() {
\r
4674 var grid = this.grid;
\r
4675 var mainBody = grid.getView().mainBody;
\r
4676 mainBody.on('mousedown', this.onMouseDown, this, {delegate: '.x-grid3-row-expander'});
\r
4677 if (this.expandOnEnter) {
\r
4678 this.keyNav = new Ext.KeyNav(this.grid.getGridEl(), {
\r
4679 'enter' : this.onEnter,
\r
4683 if (this.expandOnDblClick) {
\r
4684 grid.on('rowdblclick', this.onRowDblClick, this);
\r
4689 onDestroy: function() {
\r
4690 this.keyNav.disable();
\r
4691 delete this.keyNav;
\r
4692 var mainBody = this.grid.getView().mainBody;
\r
4693 mainBody.un('mousedown', this.onMouseDown, this);
\r
4696 onRowDblClick: function(grid, rowIdx, e) {
\r
4697 this.toggleRow(rowIdx);
\r
4700 onEnter: function(e) {
\r
4701 var g = this.grid;
\r
4702 var sm = g.getSelectionModel();
\r
4703 var sels = sm.getSelections();
\r
4704 for (var i = 0, len = sels.length; i < len; i++) {
\r
4705 var rowIdx = g.getStore().indexOf(sels[i]);
\r
4706 this.toggleRow(rowIdx);
\r
4710 getBodyContent : function(record, index){
\r
4711 if(!this.enableCaching){
\r
4712 return this.tpl.apply(record.data);
\r
4714 var content = this.bodyContent[record.id];
\r
4716 content = this.tpl.apply(record.data);
\r
4717 this.bodyContent[record.id] = content;
\r
4722 onMouseDown : function(e, t){
\r
4724 var row = e.getTarget('.x-grid3-row');
\r
4725 this.toggleRow(row);
\r
4728 renderer : function(v, p, record){
\r
4729 p.cellAttr = 'rowspan="2"';
\r
4730 return '<div class="x-grid3-row-expander"> </div>';
\r
4733 beforeExpand : function(record, body, rowIndex){
\r
4734 if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){
\r
4735 if(this.tpl && this.lazyRender){
\r
4736 body.innerHTML = this.getBodyContent(record, rowIndex);
\r
4744 toggleRow : function(row){
\r
4745 if(typeof row == 'number'){
\r
4746 row = this.grid.view.getRow(row);
\r
4748 this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
\r
4751 expandRow : function(row){
\r
4752 if(typeof row == 'number'){
\r
4753 row = this.grid.view.getRow(row);
\r
4755 var record = this.grid.store.getAt(row.rowIndex);
\r
4756 var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
\r
4757 if(this.beforeExpand(record, body, row.rowIndex)){
\r
4758 this.state[record.id] = true;
\r
4759 Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
\r
4760 this.fireEvent('expand', this, record, body, row.rowIndex);
\r
4764 collapseRow : function(row){
\r
4765 if(typeof row == 'number'){
\r
4766 row = this.grid.view.getRow(row);
\r
4768 var record = this.grid.store.getAt(row.rowIndex);
\r
4769 var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
\r
4770 if(this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false){
\r
4771 this.state[record.id] = false;
\r
4772 Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
\r
4773 this.fireEvent('collapse', this, record, body, row.rowIndex);
\r
4778 Ext.preg('rowexpander', Ext.ux.grid.RowExpander);
\r
4780 //backwards compat
\r
4781 Ext.grid.RowExpander = Ext.ux.grid.RowExpander;// We are adding these custom layouts to a namespace that does not
4782 // exist by default in Ext, so we have to add the namespace first:
4783 Ext.ns('Ext.ux.layout');
4786 * @class Ext.ux.layout.RowLayout
4787 * @extends Ext.layout.ContainerLayout
4788 * <p>This is the layout style of choice for creating structural layouts in a multi-row format where the height of
4789 * each row can be specified as a percentage or fixed height. Row widths can also be fixed, percentage or auto.
4790 * This class is intended to be extended or created via the layout:'ux.row' {@link Ext.Container#layout} config,
4791 * and should generally not need to be created directly via the new keyword.</p>
4792 * <p>RowLayout does not have any direct config options (other than inherited ones), but it does support a
4793 * specific config property of <b><tt>rowHeight</tt></b> that can be included in the config of any panel added to it. The
4794 * layout will use the rowHeight (if present) or height of each panel during layout to determine how to size each panel.
4795 * If height or rowHeight is not specified for a given panel, its height will default to the panel's height (or auto).</p>
4796 * <p>The height property is always evaluated as pixels, and must be a number greater than or equal to 1.
4797 * The rowHeight property is always evaluated as a percentage, and must be a decimal value greater than 0 and
4798 * less than 1 (e.g., .25).</p>
4799 * <p>The basic rules for specifying row heights are pretty simple. The logic makes two passes through the
4800 * set of contained panels. During the first layout pass, all panels that either have a fixed height or none
4801 * specified (auto) are skipped, but their heights are subtracted from the overall container height. During the second
4802 * pass, all panels with rowHeights are assigned pixel heights in proportion to their percentages based on
4803 * the total <b>remaining</b> container height. In other words, percentage height panels are designed to fill the space
4804 * left over by all the fixed-height and/or auto-height panels. Because of this, while you can specify any number of rows
4805 * with different percentages, the rowHeights must always add up to 1 (or 100%) when added together, otherwise your
4806 * layout may not render as expected. Example usage:</p>
4808 // All rows are percentages -- they must add up to 1
4809 var p = new Ext.Panel({
4810 title: 'Row Layout - Percentage Only',
4824 // Mix of height and rowHeight -- all rowHeight values must add
4825 // up to 1. The first row will take up exactly 120px, and the last two
4826 // rows will fill the remaining container height.
4827 var p = new Ext.Panel({
4828 title: 'Row Layout - Mixed',
4833 // standard panel widths are still supported too:
4834 width: '50%' // or 200
4846 Ext.ux.layout.RowLayout = Ext.extend(Ext.layout.ContainerLayout, {
4851 isValidParent : function(c, target){
4852 return c.getEl().dom.parentNode == this.innerCt.dom;
4856 onLayout : function(ct, target){
4857 var rs = ct.items.items, len = rs.length, r, i;
4860 target.addClass('ux-row-layout-ct');
4861 this.innerCt = target.createChild({cls:'x-row-inner'});
4863 this.renderAll(ct, this.innerCt);
4865 var size = target.getViewSize();
4867 if(size.width < 1 && size.height < 1){ // display none?
4871 var h = size.height - target.getPadding('tb'),
4874 this.innerCt.setSize({height:h});
4876 // some rows can be percentages while others are fixed
4877 // so we need to make 2 passes
4879 for(i = 0; i < len; i++){
4882 ph -= (r.getSize().height + r.getEl().getMargins('tb'));
4886 ph = ph < 0 ? 0 : ph;
4888 for(i = 0; i < len; i++){
4891 r.setSize({height: Math.floor(r.rowHeight*ph) - r.getEl().getMargins('tb')});
4897 * @property activeItem
4902 Ext.Container.LAYOUTS['ux.row'] = Ext.ux.layout.RowLayout;
4903 Ext.ns('Ext.ux.form');
\r
4905 Ext.ux.form.SearchField = Ext.extend(Ext.form.TwinTriggerField, {
\r
4906 initComponent : function(){
\r
4907 Ext.ux.form.SearchField.superclass.initComponent.call(this);
\r
4908 this.on('specialkey', function(f, e){
\r
4909 if(e.getKey() == e.ENTER){
\r
4910 this.onTrigger2Click();
\r
4915 validationEvent:false,
\r
4916 validateOnBlur:false,
\r
4917 trigger1Class:'x-form-clear-trigger',
\r
4918 trigger2Class:'x-form-search-trigger',
\r
4919 hideTrigger1:true,
\r
4921 hasSearch : false,
\r
4922 paramName : 'query',
\r
4924 onTrigger1Click : function(){
\r
4925 if(this.hasSearch){
\r
4926 this.el.dom.value = '';
\r
4927 var o = {start: 0};
\r
4928 this.store.baseParams = this.store.baseParams || {};
\r
4929 this.store.baseParams[this.paramName] = '';
\r
4930 this.store.reload({params:o});
\r
4931 this.triggers[0].hide();
\r
4932 this.hasSearch = false;
\r
4936 onTrigger2Click : function(){
\r
4937 var v = this.getRawValue();
\r
4939 this.onTrigger1Click();
\r
4942 var o = {start: 0};
\r
4943 this.store.baseParams = this.store.baseParams || {};
\r
4944 this.store.baseParams[this.paramName] = v;
\r
4945 this.store.reload({params:o});
\r
4946 this.hasSearch = true;
\r
4947 this.triggers[0].show();
\r
4949 });Ext.ns('Ext.ux.form');
\r
4952 * @class Ext.ux.form.SelectBox
\r
4953 * @extends Ext.form.ComboBox
\r
4954 * <p>Makes a ComboBox more closely mimic an HTML SELECT. Supports clicking and dragging
\r
4955 * through the list, with item selection occurring when the mouse button is released.
\r
4956 * When used will automatically set {@link #editable} to false and call {@link Ext.Element#unselectable}
\r
4957 * on inner elements. Re-enabling editable after calling this will NOT work.</p>
\r
4958 * @author Corey Gilmore http://extjs.com/forum/showthread.php?t=6392
\r
4959 * @history 2007-07-08 jvs
\r
4960 * Slight mods for Ext 2.0
\r
4961 * @xtype selectbox
\r
4963 Ext.ux.form.SelectBox = Ext.extend(Ext.form.ComboBox, {
\r
4964 constructor: function(config){
\r
4965 this.searchResetDelay = 1000;
\r
4966 config = config || {};
\r
4967 config = Ext.apply(config || {}, {
\r
4969 forceSelection: true,
\r
4971 lastSearchTerm: false,
\r
4972 triggerAction: 'all',
\r
4976 Ext.ux.form.SelectBox.superclass.constructor.apply(this, arguments);
\r
4978 this.lastSelectedIndex = this.selectedIndex || 0;
\r
4981 initEvents : function(){
\r
4982 Ext.ux.form.SelectBox.superclass.initEvents.apply(this, arguments);
\r
4983 // you need to use keypress to capture upper/lower case and shift+key, but it doesn't work in IE
\r
4984 this.el.on('keydown', this.keySearch, this, true);
\r
4985 this.cshTask = new Ext.util.DelayedTask(this.clearSearchHistory, this);
\r
4988 keySearch : function(e, target, options) {
\r
4989 var raw = e.getKey();
\r
4990 var key = String.fromCharCode(raw);
\r
4991 var startIndex = 0;
\r
4993 if( !this.store.getCount() ) {
\r
4998 case Ext.EventObject.HOME:
\r
5000 this.selectFirst();
\r
5003 case Ext.EventObject.END:
\r
5005 this.selectLast();
\r
5008 case Ext.EventObject.PAGEDOWN:
\r
5009 this.selectNextPage();
\r
5013 case Ext.EventObject.PAGEUP:
\r
5014 this.selectPrevPage();
\r
5019 // skip special keys other than the shift key
\r
5020 if( (e.hasModifier() && !e.shiftKey) || e.isNavKeyPress() || e.isSpecialKey() ) {
\r
5023 if( this.lastSearchTerm == key ) {
\r
5024 startIndex = this.lastSelectedIndex;
\r
5026 this.search(this.displayField, key, startIndex);
\r
5027 this.cshTask.delay(this.searchResetDelay);
\r
5030 onRender : function(ct, position) {
\r
5031 this.store.on('load', this.calcRowsPerPage, this);
\r
5032 Ext.ux.form.SelectBox.superclass.onRender.apply(this, arguments);
\r
5033 if( this.mode == 'local' ) {
\r
5034 this.calcRowsPerPage();
\r
5038 onSelect : function(record, index, skipCollapse){
\r
5039 if(this.fireEvent('beforeselect', this, record, index) !== false){
\r
5040 this.setValue(record.data[this.valueField || this.displayField]);
\r
5041 if( !skipCollapse ) {
\r
5044 this.lastSelectedIndex = index + 1;
\r
5045 this.fireEvent('select', this, record, index);
\r
5049 render : function(ct) {
\r
5050 Ext.ux.form.SelectBox.superclass.render.apply(this, arguments);
\r
5051 if( Ext.isSafari ) {
\r
5052 this.el.swallowEvent('mousedown', true);
\r
5054 this.el.unselectable();
\r
5055 this.innerList.unselectable();
\r
5056 this.trigger.unselectable();
\r
5057 this.innerList.on('mouseup', function(e, target, options) {
\r
5058 if( target.id && target.id == this.innerList.id ) {
\r
5061 this.onViewClick();
\r
5064 this.innerList.on('mouseover', function(e, target, options) {
\r
5065 if( target.id && target.id == this.innerList.id ) {
\r
5068 this.lastSelectedIndex = this.view.getSelectedIndexes()[0] + 1;
\r
5069 this.cshTask.delay(this.searchResetDelay);
\r
5072 this.trigger.un('click', this.onTriggerClick, this);
\r
5073 this.trigger.on('mousedown', function(e, target, options) {
\r
5074 e.preventDefault();
\r
5075 this.onTriggerClick();
\r
5078 this.on('collapse', function(e, target, options) {
\r
5079 Ext.getDoc().un('mouseup', this.collapseIf, this);
\r
5082 this.on('expand', function(e, target, options) {
\r
5083 Ext.getDoc().on('mouseup', this.collapseIf, this);
\r
5087 clearSearchHistory : function() {
\r
5088 this.lastSelectedIndex = 0;
\r
5089 this.lastSearchTerm = false;
\r
5092 selectFirst : function() {
\r
5093 this.focusAndSelect(this.store.data.first());
\r
5096 selectLast : function() {
\r
5097 this.focusAndSelect(this.store.data.last());
\r
5100 selectPrevPage : function() {
\r
5101 if( !this.rowHeight ) {
\r
5104 var index = Math.max(this.selectedIndex-this.rowsPerPage, 0);
\r
5105 this.focusAndSelect(this.store.getAt(index));
\r
5108 selectNextPage : function() {
\r
5109 if( !this.rowHeight ) {
\r
5112 var index = Math.min(this.selectedIndex+this.rowsPerPage, this.store.getCount() - 1);
\r
5113 this.focusAndSelect(this.store.getAt(index));
\r
5116 search : function(field, value, startIndex) {
\r
5117 field = field || this.displayField;
\r
5118 this.lastSearchTerm = value;
\r
5119 var index = this.store.find.apply(this.store, arguments);
\r
5120 if( index !== -1 ) {
\r
5121 this.focusAndSelect(index);
\r
5125 focusAndSelect : function(record) {
\r
5126 var index = typeof record === 'number' ? record : this.store.indexOf(record);
\r
5127 this.select(index, this.isExpanded());
\r
5128 this.onSelect(this.store.getAt(record), index, this.isExpanded());
\r
5131 calcRowsPerPage : function() {
\r
5132 if( this.store.getCount() ) {
\r
5133 this.rowHeight = Ext.fly(this.view.getNode(0)).getHeight();
\r
5134 this.rowsPerPage = this.maxHeight / this.rowHeight;
\r
5136 this.rowHeight = false;
\r
5142 Ext.reg('selectbox', Ext.ux.form.SelectBox);
\r
5144 //backwards compat
\r
5145 Ext.ux.SelectBox = Ext.ux.form.SelectBox;
\r
5147 * @class Ext.ux.SliderTip
\r
5148 * @extends Ext.Tip
\r
5149 * Simple plugin for using an Ext.Tip with a slider to show the slider value
\r
5151 Ext.ux.SliderTip = Ext.extend(Ext.Tip, {
\r
5153 offsets : [0, -10],
\r
5154 init : function(slider){
\r
5155 slider.on('dragstart', this.onSlide, this);
\r
5156 slider.on('drag', this.onSlide, this);
\r
5157 slider.on('dragend', this.hide, this);
\r
5158 slider.on('destroy', this.destroy, this);
\r
5161 onSlide : function(slider){
\r
5163 this.body.update(this.getText(slider));
\r
5164 this.doAutoWidth();
\r
5165 this.el.alignTo(slider.thumb, 'b-t?', this.offsets);
\r
5168 getText : function(slider){
\r
5169 return String(slider.getValue());
\r
5172 Ext.ux.SlidingPager = Ext.extend(Object, {
\r
5173 init : function(pbar){
\r
5174 Ext.each(pbar.items.getRange(2,6), function(c){
\r
5177 var slider = new Ext.Slider({
\r
5181 plugins: new Ext.ux.SliderTip({
\r
5182 getText : function(s){
\r
5183 return String.format('Page <b>{0}</b> of <b>{1}</b>', s.value, s.maxValue);
\r
5187 changecomplete: function(s, v){
\r
5188 pbar.changePage(v);
\r
5192 pbar.insert(5, slider);
\r
5194 change: function(pb, data){
\r
5195 slider.maxValue = data.pages;
\r
5196 slider.setValue(data.activePage);
\r
5198 beforedestroy: function(){
\r
5203 });Ext.ns('Ext.ux.form');
\r
5206 * @class Ext.ux.form.SpinnerField
\r
5207 * @extends Ext.form.NumberField
\r
5208 * Creates a field utilizing Ext.ux.Spinner
\r
5209 * @xtype spinnerfield
\r
5211 Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, {
\r
5212 deferHeight: true,
\r
5213 autoSize: Ext.emptyFn,
\r
5214 onBlur: Ext.emptyFn,
\r
5215 adjustSize: Ext.BoxComponent.prototype.adjustSize,
\r
5217 constructor: function(config) {
\r
5218 var spinnerConfig = Ext.copyTo({}, config, 'incrementValue,alternateIncrementValue,accelerate,defaultValue,triggerClass,splitterClass');
\r
5220 var spl = this.spinner = new Ext.ux.Spinner(spinnerConfig);
\r
5222 var plugins = config.plugins
\r
5223 ? (Ext.isArray(config.plugins)
\r
5224 ? config.plugins.push(spl)
\r
5225 : [config.plugins, spl])
\r
5228 Ext.ux.form.SpinnerField.superclass.constructor.call(this, Ext.apply(config, {plugins: plugins}));
\r
5231 onShow: function(){
\r
5233 this.wrap.dom.style.display = '';
\r
5234 this.wrap.dom.style.visibility = 'visible';
\r
5238 onHide: function(){
\r
5239 this.wrap.dom.style.display = 'none';
\r
5243 getResizeEl: function(){
\r
5248 getPositionEl: function(){
\r
5253 alignErrorIcon: function(){
\r
5255 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
\r
5259 validateBlur: function(){
\r
5264 Ext.reg('spinnerfield', Ext.ux.form.SpinnerField);
\r
5266 //backwards compat
\r
5267 Ext.form.SpinnerField = Ext.ux.form.SpinnerField;
\r
5269 * @class Ext.ux.Spinner
\r
5270 * @extends Ext.util.Observable
\r
5271 * Creates a Spinner control utilized by Ext.ux.form.SpinnerField
\r
5273 Ext.ux.Spinner = Ext.extend(Ext.util.Observable, {
\r
5274 incrementValue: 1,
\r
5275 alternateIncrementValue: 5,
\r
5276 triggerClass: 'x-form-spinner-trigger',
\r
5277 splitterClass: 'x-form-spinner-splitter',
\r
5278 alternateKey: Ext.EventObject.shiftKey,
\r
5280 accelerate: false,
\r
5282 constructor: function(config){
\r
5283 Ext.ux.Spinner.superclass.constructor.call(this, config);
\r
5284 Ext.apply(this, config);
\r
5285 this.mimicing = false;
\r
5288 init: function(field){
\r
5289 this.field = field;
\r
5291 field.afterMethod('onRender', this.doRender, this);
\r
5292 field.afterMethod('onEnable', this.doEnable, this);
\r
5293 field.afterMethod('onDisable', this.doDisable, this);
\r
5294 field.afterMethod('afterRender', this.doAfterRender, this);
\r
5295 field.afterMethod('onResize', this.doResize, this);
\r
5296 field.afterMethod('onFocus', this.doFocus, this);
\r
5297 field.beforeMethod('onDestroy', this.doDestroy, this);
\r
5300 doRender: function(ct, position){
\r
5301 var el = this.el = this.field.getEl();
\r
5302 var f = this.field;
\r
5305 f.wrap = this.wrap = el.wrap({
\r
5306 cls: "x-form-field-wrap"
\r
5310 this.wrap = f.wrap.addClass('x-form-field-wrap');
\r
5313 this.trigger = this.wrap.createChild({
\r
5315 src: Ext.BLANK_IMAGE_URL,
\r
5316 cls: "x-form-trigger " + this.triggerClass
\r
5320 this.wrap.setWidth(el.getWidth() + this.trigger.getWidth());
\r
5323 this.splitter = this.wrap.createChild({
\r
5325 cls: this.splitterClass,
\r
5326 style: 'width:13px; height:2px;'
\r
5328 this.splitter.setRight((Ext.isIE) ? 1 : 2).setTop(10).show();
\r
5330 this.proxy = this.trigger.createProxy('', this.splitter, true);
\r
5331 this.proxy.addClass("x-form-spinner-proxy");
\r
5332 this.proxy.setStyle('left', '0px');
\r
5333 this.proxy.setSize(14, 1);
\r
5334 this.proxy.hide();
\r
5335 this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", {
\r
5336 dragElId: this.proxy.id
\r
5339 this.initTrigger();
\r
5340 this.initSpinner();
\r
5343 doAfterRender: function(){
\r
5345 if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) {
\r
5346 this.el.position();
\r
5351 doEnable: function(){
\r
5353 this.wrap.removeClass(this.field.disabledClass);
\r
5357 doDisable: function(){
\r
5359 this.wrap.addClass(this.field.disabledClass);
\r
5360 this.el.removeClass(this.field.disabledClass);
\r
5364 doResize: function(w, h){
\r
5365 if (typeof w == 'number') {
\r
5366 this.el.setWidth(this.field.adjustWidth('input', w - this.trigger.getWidth()));
\r
5368 this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth());
\r
5371 doFocus: function(){
\r
5372 if (!this.mimicing) {
\r
5373 this.wrap.addClass('x-trigger-wrap-focus');
\r
5374 this.mimicing = true;
\r
5375 Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, {
\r
5378 this.el.on('keydown', this.checkTab, this);
\r
5383 checkTab: function(e){
\r
5384 if (e.getKey() == e.TAB) {
\r
5385 this.triggerBlur();
\r
5390 mimicBlur: function(e){
\r
5391 if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) {
\r
5392 this.triggerBlur();
\r
5397 triggerBlur: function(){
\r
5398 this.mimicing = false;
\r
5399 Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);
\r
5400 this.el.un("keydown", this.checkTab, this);
\r
5401 this.field.beforeBlur();
\r
5402 this.wrap.removeClass('x-trigger-wrap-focus');
\r
5403 this.field.onBlur.call(this.field);
\r
5406 initTrigger: function(){
\r
5407 this.trigger.addClassOnOver('x-form-trigger-over');
\r
5408 this.trigger.addClassOnClick('x-form-trigger-click');
\r
5411 initSpinner: function(){
\r
5412 this.field.addEvents({
\r
5418 this.keyNav = new Ext.KeyNav(this.el, {
\r
5419 "up": function(e){
\r
5420 e.preventDefault();
\r
5424 "down": function(e){
\r
5425 e.preventDefault();
\r
5426 this.onSpinDown();
\r
5429 "pageUp": function(e){
\r
5430 e.preventDefault();
\r
5431 this.onSpinUpAlternate();
\r
5434 "pageDown": function(e){
\r
5435 e.preventDefault();
\r
5436 this.onSpinDownAlternate();
\r
5442 this.repeater = new Ext.util.ClickRepeater(this.trigger, {
\r
5443 accelerate: this.accelerate
\r
5445 this.field.mon(this.repeater, "click", this.onTriggerClick, this, {
\r
5446 preventDefault: true
\r
5449 this.field.mon(this.trigger, {
\r
5450 mouseover: this.onMouseOver,
\r
5451 mouseout: this.onMouseOut,
\r
5452 mousemove: this.onMouseMove,
\r
5453 mousedown: this.onMouseDown,
\r
5454 mouseup: this.onMouseUp,
\r
5456 preventDefault: true
\r
5459 this.field.mon(this.wrap, "mousewheel", this.handleMouseWheel, this);
\r
5461 this.dd.setXConstraint(0, 0, 10)
\r
5462 this.dd.setYConstraint(1500, 1500, 10);
\r
5463 this.dd.endDrag = this.endDrag.createDelegate(this);
\r
5464 this.dd.startDrag = this.startDrag.createDelegate(this);
\r
5465 this.dd.onDrag = this.onDrag.createDelegate(this);
\r
5468 onMouseOver: function(){
\r
5469 if (this.disabled) {
\r
5472 var middle = this.getMiddle();
\r
5473 this.tmpHoverClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown';
\r
5474 this.trigger.addClass(this.tmpHoverClass);
\r
5478 onMouseOut: function(){
\r
5479 this.trigger.removeClass(this.tmpHoverClass);
\r
5483 onMouseMove: function(){
\r
5484 if (this.disabled) {
\r
5487 var middle = this.getMiddle();
\r
5488 if (((Ext.EventObject.getPageY() > middle) && this.tmpHoverClass == "x-form-spinner-overup") ||
\r
5489 ((Ext.EventObject.getPageY() < middle) && this.tmpHoverClass == "x-form-spinner-overdown")) {
\r
5494 onMouseDown: function(){
\r
5495 if (this.disabled) {
\r
5498 var middle = this.getMiddle();
\r
5499 this.tmpClickClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown';
\r
5500 this.trigger.addClass(this.tmpClickClass);
\r
5504 onMouseUp: function(){
\r
5505 this.trigger.removeClass(this.tmpClickClass);
\r
5509 onTriggerClick: function(){
\r
5510 if (this.disabled || this.el.dom.readOnly) {
\r
5513 var middle = this.getMiddle();
\r
5514 var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down';
\r
5515 this['onSpin' + ud]();
\r
5519 getMiddle: function(){
\r
5520 var t = this.trigger.getTop();
\r
5521 var h = this.trigger.getHeight();
\r
5522 var middle = t + (h / 2);
\r
5527 //checks if control is allowed to spin
\r
5528 isSpinnable: function(){
\r
5529 if (this.disabled || this.el.dom.readOnly) {
\r
5530 Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly
\r
5536 handleMouseWheel: function(e){
\r
5537 //disable scrolling when not focused
\r
5538 if (this.wrap.hasClass('x-trigger-wrap-focus') == false) {
\r
5542 var delta = e.getWheelDelta();
\r
5549 this.onSpinDown();
\r
5555 startDrag: function(){
\r
5556 this.proxy.show();
\r
5557 this._previousY = Ext.fly(this.dd.getDragEl()).getTop();
\r
5561 endDrag: function(){
\r
5562 this.proxy.hide();
\r
5566 onDrag: function(){
\r
5567 if (this.disabled) {
\r
5570 var y = Ext.fly(this.dd.getDragEl()).getTop();
\r
5573 if (this._previousY > y) {
\r
5576 if (this._previousY < y) {
\r
5580 this['onSpin' + ud]();
\r
5583 this._previousY = y;
\r
5587 onSpinUp: function(){
\r
5588 if (this.isSpinnable() == false) {
\r
5591 if (Ext.EventObject.shiftKey == true) {
\r
5592 this.onSpinUpAlternate();
\r
5596 this.spin(false, false);
\r
5598 this.field.fireEvent("spin", this);
\r
5599 this.field.fireEvent("spinup", this);
\r
5603 onSpinDown: function(){
\r
5604 if (this.isSpinnable() == false) {
\r
5607 if (Ext.EventObject.shiftKey == true) {
\r
5608 this.onSpinDownAlternate();
\r
5612 this.spin(true, false);
\r
5614 this.field.fireEvent("spin", this);
\r
5615 this.field.fireEvent("spindown", this);
\r
5619 onSpinUpAlternate: function(){
\r
5620 if (this.isSpinnable() == false) {
\r
5623 this.spin(false, true);
\r
5624 this.field.fireEvent("spin", this);
\r
5625 this.field.fireEvent("spinup", this);
\r
5629 onSpinDownAlternate: function(){
\r
5630 if (this.isSpinnable() == false) {
\r
5633 this.spin(true, true);
\r
5634 this.field.fireEvent("spin", this);
\r
5635 this.field.fireEvent("spindown", this);
\r
5638 spin: function(down, alternate){
\r
5639 var v = parseFloat(this.field.getValue());
\r
5640 var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue;
\r
5641 (down == true) ? v -= incr : v += incr;
\r
5643 v = (isNaN(v)) ? this.defaultValue : v;
\r
5644 v = this.fixBoundries(v);
\r
5645 this.field.setRawValue(v);
\r
5648 fixBoundries: function(value){
\r
5651 if (this.field.minValue != undefined && v < this.field.minValue) {
\r
5652 v = this.field.minValue;
\r
5654 if (this.field.maxValue != undefined && v > this.field.maxValue) {
\r
5655 v = this.field.maxValue;
\r
5658 return this.fixPrecision(v);
\r
5662 fixPrecision: function(value){
\r
5663 var nan = isNaN(value);
\r
5664 if (!this.field.allowDecimals || this.field.decimalPrecision == -1 || nan || !value) {
\r
5665 return nan ? '' : value;
\r
5667 return parseFloat(parseFloat(value).toFixed(this.field.decimalPrecision));
\r
5670 doDestroy: function(){
\r
5671 if (this.trigger) {
\r
5672 this.trigger.remove();
\r
5675 this.wrap.remove();
\r
5676 delete this.field.wrap;
\r
5679 if (this.splitter) {
\r
5680 this.splitter.remove();
\r
5689 this.proxy.remove();
\r
5692 if (this.repeater) {
\r
5693 this.repeater.purgeListeners();
\r
5698 //backwards compat
\r
5699 Ext.form.Spinner = Ext.ux.Spinner;Ext.ux.Spotlight = function(config){
\r
5700 Ext.apply(this, config);
\r
5702 Ext.ux.Spotlight.prototype = {
\r
5706 easing:'easeNone',
\r
5711 createElements : function(){
\r
5712 var bd = Ext.getBody();
\r
5714 this.right = bd.createChild({cls:'x-spotlight'});
\r
5715 this.left = bd.createChild({cls:'x-spotlight'});
\r
5716 this.top = bd.createChild({cls:'x-spotlight'});
\r
5717 this.bottom = bd.createChild({cls:'x-spotlight'});
\r
5719 this.all = new Ext.CompositeElement([this.right, this.left, this.top, this.bottom]);
\r
5722 show : function(el, callback, scope){
\r
5723 if(this.animated){
\r
5724 this.show.defer(50, this, [el, callback, scope]);
\r
5727 this.el = Ext.get(el);
\r
5729 this.createElements();
\r
5732 this.all.setDisplayed('');
\r
5733 this.applyBounds(true, false);
\r
5734 this.active = true;
\r
5735 Ext.EventManager.onWindowResize(this.syncSize, this);
\r
5736 this.applyBounds(false, this.animate, false, callback, scope);
\r
5738 this.applyBounds(false, false, false, callback, scope); // all these booleans look hideous
\r
5742 hide : function(callback, scope){
\r
5743 if(this.animated){
\r
5744 this.hide.defer(50, this, [callback, scope]);
\r
5747 Ext.EventManager.removeResizeListener(this.syncSize, this);
\r
5748 this.applyBounds(true, this.animate, true, callback, scope);
\r
5751 doHide : function(){
\r
5752 this.active = false;
\r
5753 this.all.setDisplayed(false);
\r
5756 syncSize : function(){
\r
5757 this.applyBounds(false, false);
\r
5760 applyBounds : function(basePts, anim, doHide, callback, scope){
\r
5762 var rg = this.el.getRegion();
\r
5764 var dw = Ext.lib.Dom.getViewWidth(true);
\r
5765 var dh = Ext.lib.Dom.getViewHeight(true);
\r
5767 var c = 0, cb = false;
\r
5770 callback: function(){
\r
5773 this.animated = false;
\r
5777 Ext.callback(callback, scope, [this]);
\r
5781 duration: this.duration,
\r
5782 easing: this.easing
\r
5784 this.animated = true;
\r
5787 this.right.setBounds(
\r
5789 basePts ? dh : rg.top,
\r
5791 basePts ? 0 : (dh - rg.top),
\r
5794 this.left.setBounds(
\r
5798 basePts ? 0 : rg.bottom,
\r
5801 this.top.setBounds(
\r
5802 basePts ? dw : rg.left,
\r
5804 basePts ? 0 : dw - rg.left,
\r
5808 this.bottom.setBounds(
\r
5811 basePts ? 0 : rg.right,
\r
5820 Ext.callback(callback, scope, [this]);
\r
5825 destroy : function(){
\r
5837 //backwards compat
\r
5838 Ext.Spotlight = Ext.ux.Spotlight;/**
\r
5839 * @class Ext.ux.TabCloseMenu
\r
5840 * @extends Object
\r
5841 * Plugin (ptype = 'tabclosemenu') for adding a close context menu to tabs.
\r
5843 * @ptype tabclosemenu
\r
5845 Ext.ux.TabCloseMenu = function(){
\r
5846 var tabs, menu, ctxItem;
\r
5847 this.init = function(tp){
\r
5849 tabs.on('contextmenu', onContextMenu);
\r
5852 function onContextMenu(ts, item, e){
\r
5853 if(!menu){ // create context menu on first right click
\r
5854 menu = new Ext.menu.Menu({
\r
5856 id: tabs.id + '-close',
\r
5857 text: 'Close Tab',
\r
5858 handler : function(){
\r
5859 tabs.remove(ctxItem);
\r
5862 id: tabs.id + '-close-others',
\r
5863 text: 'Close Other Tabs',
\r
5864 handler : function(){
\r
5865 tabs.items.each(function(item){
\r
5866 if(item.closable && item != ctxItem){
\r
5867 tabs.remove(item);
\r
5874 var items = menu.items;
\r
5875 items.get(tabs.id + '-close').setDisabled(!item.closable);
\r
5876 var disableOthers = true;
\r
5877 tabs.items.each(function(){
\r
5878 if(this != item && this.closable){
\r
5879 disableOthers = false;
\r
5883 items.get(tabs.id + '-close-others').setDisabled(disableOthers);
\r
5885 menu.showAt(e.getPoint());
\r
5889 Ext.preg('tabclosemenu', Ext.ux.TabCloseMenu);
\r
5890 Ext.ns('Ext.ux.grid');
5893 * @class Ext.ux.grid.TableGrid
5894 * @extends Ext.grid.GridPanel
5895 * A Grid which creates itself from an existing HTML table element.
5897 * 2007-03-01 Original version by Nige "Animal" White
5898 * 2007-03-10 jvs Slightly refactored to reuse existing classes * @constructor
5899 * @param {String/HTMLElement/Ext.Element} table The table element from which this grid will be created -
5900 * The table MUST have some type of size defined for the grid to fill. The container will be
5901 * automatically set to position relative if it isn't already.
5902 * @param {Object} config A config object that sets properties on this grid and has two additional (optional)
5903 * properties: fields and columns which allow for customizing data fields and columns for this grid.
5905 Ext.ux.grid.TableGrid = function(table, config){
5908 Ext.apply(this, config);
5909 var cf = config.fields || [], ch = config.columns || [];
5910 table = Ext.get(table);
5912 var ct = table.insertSibling();
5914 var fields = [], cols = [];
5915 var headers = table.query("thead th");
5916 for (var i = 0, h; h = headers[i]; i++) {
5917 var text = h.innerHTML;
5918 var name = 'tcol-' + i;
5920 fields.push(Ext.applyIf(cf[i] ||
5923 mapping: 'td:nth(' + (i + 1) + ')/@innerHTML'
5926 cols.push(Ext.applyIf(ch[i] ||
5930 'width': h.offsetWidth,
5936 var ds = new Ext.data.Store({
5937 reader: new Ext.data.XmlReader({
5942 ds.loadData(table.dom);
5944 var cm = new Ext.grid.ColumnModel(cols);
5946 if (config.width || config.height) {
5947 ct.setSize(config.width || 'auto', config.height || 'auto');
5950 ct.setWidth(table.getWidth());
5953 if (config.remove !== false) {
5960 'sm': new Ext.grid.RowSelectionModel(),
5964 Ext.ux.grid.TableGrid.superclass.constructor.call(this, ct, {});
5967 Ext.extend(Ext.ux.grid.TableGrid, Ext.grid.GridPanel);
5970 Ext.grid.TableGrid = Ext.ux.grid.TableGrid;
5973 Ext.ux.TabScrollerMenu = Ext.extend(Object, {
5976 menuPrefixText : 'Items',
5977 constructor : function(config) {
5978 config = config || {};
5979 Ext.apply(this, config);
5981 init : function(tabPanel) {
5982 Ext.apply(tabPanel, this.tabPanelMethods);
5984 tabPanel.tabScrollerMenu = this;
5992 var newFn = tabPanel.createScrollers.createSequence(thisRef.createPanelsMenu, this);
5993 tabPanel.createScrollers = newFn;
5998 // private && sequeneced
5999 createPanelsMenu : function() {
6000 var h = this.stripWrap.dom.offsetHeight;
6002 //move the right menu item to the left 18px
6003 var rtScrBtn = this.header.dom.firstChild;
6004 Ext.fly(rtScrBtn).applyStyles({
6008 var stripWrap = Ext.get(this.strip.dom.parentNode);
6009 stripWrap.applyStyles({
6010 'margin-right' : '36px'
6013 // Add the new righthand menu
6014 var scrollMenu = this.header.insertFirst({
6015 cls:'x-tab-tabmenu-right'
6017 scrollMenu.setHeight(h);
6018 scrollMenu.addClassOnOver('x-tab-tabmenu-over');
6019 scrollMenu.on('click', this.showTabsMenu, this);
6021 this.scrollLeft.show = this.scrollLeft.show.createSequence(function() {
6025 this.scrollLeft.hide = this.scrollLeft.hide.createSequence(function() {
6031 getPageSize : function() {
6032 return this.pageSize;
6035 setPageSize : function(pageSize) {
6036 this.pageSize = pageSize;
6039 getMaxText : function() {
6040 return this.maxText;
6043 setMaxText : function(t) {
6046 getMenuPrefixText : function() {
6047 return this.menuPrefixText;
6049 setMenuPrefixText : function(t) {
6050 this.menuPrefixText = t;
6052 // private && applied to the tab panel itself.
6054 // all execute within the scope of the tab panel
6056 showTabsMenu : function(e) {
6057 if (! this.tabsMenu) {
6058 this.tabsMenu = new Ext.menu.Menu();
6059 this.on('beforedestroy', this.tabsMenu.destroy, this.tabsMenu);
6062 this.tabsMenu.removeAll();
6064 this.generateTabMenuItems();
6066 var target = Ext.get(e.getTarget());
6067 var xy = target.getXY();
6069 //Y param + 24 pixels
6072 this.tabsMenu.showAt(xy);
6075 generateTabMenuItems : function() {
6076 var curActive = this.getActiveTab();
6077 var totalItems = this.items.getCount();
6078 var pageSize = this.tabScrollerMenu.getPageSize();
6081 if (totalItems > pageSize) {
6082 var numSubMenus = Math.floor(totalItems / pageSize);
6083 var remainder = totalItems % pageSize;
6085 // Loop through all of the items and create submenus in chunks of 10
6086 for (var i = 0 ; i < numSubMenus; i++) {
6087 var curPage = (i + 1) * pageSize;
6091 for (var x = 0; x < pageSize; x++) {
6092 index = x + curPage - pageSize;
6093 var item = this.items.get(index);
6094 menuItems.push(this.autoGenMenuItem(item));
6098 text : this.tabScrollerMenu.getMenuPrefixText() + ' ' + (curPage - pageSize + 1) + ' - ' + curPage,
6104 if (remainder > 0) {
6105 var start = numSubMenus * pageSize;
6107 for (var i = start ; i < totalItems; i ++ ) {
6108 var item = this.items.get(i);
6109 menuItems.push(this.autoGenMenuItem(item));
6114 text : this.tabScrollerMenu.menuPrefixText + ' ' + (start + 1) + ' - ' + (start + menuItems.length),
6122 this.items.each(function(item) {
6123 if (item.id != curActive.id && ! item.hidden) {
6124 menuItems.push(this.autoGenMenuItem(item));
6130 autoGenMenuItem : function(item) {
6131 var maxText = this.tabScrollerMenu.getMaxText();
6132 var text = Ext.util.Format.ellipsis(item.title, maxText);
6136 handler : this.showTabFromMenu,
6138 disabled : item.disabled,
6140 iconCls : item.iconCls
6145 showTabFromMenu : function(menuItem) {
6146 this.setActiveTab(menuItem.tabToShow);
6150 Ext.ns('Ext.ux.tree');
6153 * @class Ext.ux.tree.XmlTreeLoader
6154 * @extends Ext.tree.TreeLoader
6155 * <p>A TreeLoader that can convert an XML document into a hierarchy of {@link Ext.tree.TreeNode}s.
6156 * Any text value included as a text node in the XML will be added to the parent node as an attribute
6157 * called <tt>innerText</tt>. Also, the tag name of each XML node will be added to the tree node as
6158 * an attribute called <tt>tagName</tt>.</p>
6159 * <p>By default, this class expects that your source XML will provide the necessary attributes on each
6160 * node as expected by the {@link Ext.tree.TreePanel} to display and load properly. However, you can
6161 * provide your own custom processing of node attributes by overriding the {@link #processNode} method
6162 * and modifying the attributes as needed before they are used to create the associated TreeNode.</p>
6164 * Creates a new XmlTreeloader.
6165 * @param {Object} config A config object containing config properties.
6167 Ext.ux.tree.XmlTreeLoader = Ext.extend(Ext.tree.TreeLoader, {
6169 * @property XML_NODE_ELEMENT
6170 * XML element node (value 1, read-only)
6173 XML_NODE_ELEMENT : 1,
6175 * @property XML_NODE_TEXT
6176 * XML text node (value 3, read-only)
6182 processResponse : function(response, node, callback){
6183 var xmlData = response.responseXML;
6184 var root = xmlData.documentElement || xmlData;
6188 node.appendChild(this.parseXml(root));
6191 if(typeof callback == "function"){
6192 callback(this, node);
6195 this.handleFailure(response);
6200 parseXml : function(node) {
6202 Ext.each(node.childNodes, function(n){
6203 if(n.nodeType == this.XML_NODE_ELEMENT){
6204 var treeNode = this.createNode(n);
6205 if(n.childNodes.length > 0){
6206 var child = this.parseXml(n);
6207 if(typeof child == 'string'){
6208 treeNode.attributes.innerText = child;
6210 treeNode.appendChild(child);
6213 nodes.push(treeNode);
6215 else if(n.nodeType == this.XML_NODE_TEXT){
6216 var text = n.nodeValue.trim();
6217 if(text.length > 0){
6218 return nodes = text;
6227 createNode : function(node){
6229 tagName: node.tagName
6232 Ext.each(node.attributes, function(a){
6233 attr[a.nodeName] = a.nodeValue;
6236 this.processAttributes(attr);
6238 return Ext.ux.tree.XmlTreeLoader.superclass.createNode.call(this, attr);
6242 * Template method intended to be overridden by subclasses that need to provide
6243 * custom attribute processing prior to the creation of each TreeNode. This method
6244 * will be passed a config object containing existing TreeNode attribute name/value
6245 * pairs which can be modified as needed directly (no need to return the object).
6247 processAttributes: Ext.emptyFn
6251 Ext.ux.XmlTreeLoader = Ext.ux.tree.XmlTreeLoader;