Page 1 of 2 12 LastLast
Results 1 to 10 of 17

Thread: My bugfix overrides for 3.4.0

  1. #1
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Antwerp, Belgium
    Posts
    563

    Default My bugfix overrides for 3.4.1.1 (and 3.4.0)

    In case it helps someone, this is the set of overrides I use to fix bugs in ExtJS 3.4.1.1. Many of these were taken from the forums but some weren't reported yet by anyone.

    You're welcome to post yours.

    For ExtJS 3.4.1.1:

    Code:
    // override 3.4.1.1 to be able to restore column state
    Ext.override(Ext.grid.ColumnModel, {
       // add properties on columns that are stateful here
       statefulColProps: ['width', 'hidden'],
       setState: function(col, state) {
          // filter properties on those that should actually be stateful
          // (prevents overwriting properties like renderer accidentally)
          var newState = {};
          if (state) {
             Ext.each(this.statefulColProps, function(prop) {
                if (state[prop]) newState[prop] = state[prop];
             });         
          };
          // apply to column configuration 
          if (this.columns && this.columns[col]) {
             Ext.apply(this.columns[col], newState);
          } else if (this.config && this.config[col]) {
             Ext.apply(this.config[col], newState);
          }
       }
    });
    
    // override 3.4.1.1 to fix layout bug with composite fields (field width too narrow)
    Ext.override(Ext.form.TriggerField, {
       onResize : function(w, h) {
          Ext.form.TriggerField.superclass.onResize.call(this, w, h);
          var tw = this.getTriggerWidth();
          if (Ext.isNumber(w)) {
             this.el.setWidth(w - tw);
          }
          if (this.rendered && !this.readOnly && this.editable && !this.el.getWidth()) this.wrap.setWidth(w);
          else this.wrap.setWidth(this.el.getWidth() + tw);
       }
    });
    
    // override 3.4.1.1 to fix issue with tooltip text wrapping in IE9 (tooltip 1 pixel too narrow)
    // JS: I suspect this issue is caused by subpixel rendering in IE9 causing bad measurements
    Ext.override(Ext.Tip, {
       doAutoWidth : function(adjust) {
          // next line added to allow beforeshow to cancel tooltip (see below)
          if (!this.body) return;
          adjust = adjust || 0;
          var bw = this.body.getTextWidth();
          if (this.title) {
              bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));
          }
          bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + adjust;
          // added this line:
          if (Ext.isIE9) bw += 1;
          this.setWidth(bw.constrain(this.minWidth, this.maxWidth));
          
          if (Ext.isIE7 && !this.repainted) {
              this.el.repaint();
              this.repainted = true;
          }
       }
    });
    
    // override 3.4.1.1 to allow beforeshow to cancel the tooltip
    // EP: override 3.4.1.1 onMouseMove - to forbid tooltip to be positioned outside of the parent container(if this.constrainPosition == true)
    Ext.override(Ext.ToolTip, {
       show: function() {
          if (this.anchor) {
              this.showAt([-1000,-1000]);
              this.origConstrainPosition = this.constrainPosition;
              this.constrainPosition = false;
              this.anchor = this.origAnchor;
          }
          this.showAt(this.getTargetXY());
          
          if (this.anchor) {
              this.anchorEl.show();
              this.syncAnchor();
              this.constrainPosition = this.origConstrainPosition;
          // added "if (this.anchorEl)"
          } else if (this.anchorEl) {
              this.anchorEl.hide();
          }
       },
       showAt : function(xy) {
          this.lastActive = new Date();
          this.clearTimers();
          Ext.ToolTip.superclass.showAt.call(this, xy);
          if (this.dismissDelay && this.autoHide !== false) {
              this.dismissTimer = this.hide.defer(this.dismissDelay, this);
          }
          if (this.anchor && !this.anchorEl.isVisible()) {
              this.syncAnchor();
              this.anchorEl.show();
          // added "if (this.anchorEl)"
          } else if (this.anchorEl) {
              this.anchorEl.hide();
          }
       },
       onMouseMove : function(e){
          var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;
          if (t) {
             this.targetXY = e.getXY();
             if (t === this.triggerElement) {
                if(!this.hidden && this.trackMouse){
                   var xy = this.getTargetXY();
                   //added adjust positioning
                   if(this.constrainPosition){
                      xy = this.el.adjustForConstraints(xy);
                   }
                   this.setPagePosition(xy);
                }
             } else {
                this.hide();
                this.lastActive = new Date(0);
                this.onTargetOver(e);
             }
          } else if (!this.closable && this.isVisible()) {
             this.hide();
          }
       }
    });
    
    // override 3.4.1.1 to fix false security warning in IE on component destroy
    Ext.apply(Ext, {
       removeNode : (Ext.isIE && !Ext.isIE9 && !Ext.isIE10p) ? function() {
          return function(n) {
             if (n && n.tagName != 'BODY') {
                (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n, true) : Ext.EventManager.removeAll(n);
                if (n.parentNode && n.tagName == 'TD') {
                   if (Ext.isIE7) {
                      n.parentNode.removeChild(n);
                   } else {
                      n.parentNode.deleteCell(n);
                   }
                } else if (n.parentNode && n.tagName == 'TR') {
                   n.parentNode.deleteRow(n);
                } else {
                   n.outerHTML = ' ';
                }
                delete Ext.elCache[n.id];
             }
          };
       }() : function(n) {
          if (n && n.parentNode && n.tagName != 'BODY') {
             (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n, true) : Ext.EventManager.removeAll(n);
             n.parentNode.removeChild(n);
             delete Ext.elCache[n.id];
          }
       }
    });
    
    // override 3.4.1.1 to ensure that the grid stops editing if the view is refreshed
    // actual bug: removing grid lines with active lookup editor didn't hide editor
    (function() {
       var originalProcessRows = Ext.grid.GridView.prototype.processRows;
       Ext.override(Ext.grid.GridView, {
          processRows: function() {
             if (this.grid) this.grid.stopEditing(true);
             originalProcessRows.apply(this, arguments);
          }
       });
    }());
    
    // override 3.4.1.1 to fix issue with chart labels losing their labelRenderer after hide/show
    Ext.override(Ext.chart.CartesianChart, {
       createAxis : function(axis, value) {
          var o = Ext.apply({}, value),
             ref,
             old;
    
          if (this[axis]) {
             old = this[axis].labelFunction;
             this.removeFnProxy(old);
             this.labelFn.remove(old);
          }
          if (o.labelRenderer) {
             ref = this.getFunctionRef(o.labelRenderer);
             o.labelFunction = this.createFnProxy(function(v) {
                return ref.fn.call(ref.scope, v);
             });
             // delete o.labelRenderer; // <-- commented out this line
             this.labelFn.push(o.labelFunction);
          }
          if (axis.indexOf('xAxis') > -1 && o.position == 'left') {
             o.position = 'bottom';
          }
          return o;
       }
    });
    
    // override 3.4.1.1 to allow tabbing between editable grid cells to work correctly
    Ext.override(Ext.grid.RowSelectionModel, {
       acceptsNav : function(row, col, cm) {
          if (!cm.isHidden(col) && cm.isCellEditable(col, row)) {
             // check that there is actually an editor
             if (cm.getCellEditor) return !!cm.getCellEditor(col, row);
             return true;
          }
          return false;
       }
    });
    
    // override ExtJS 3.4.1.1 to make sure that in IE the HTMLEditor
    // persists the cursor position across blur/focus events
    Ext.override(Ext.form.HtmlEditor, {
       onEditorEvent: function() {
          if (Ext.isIE) {
             this.currentRange = this.getDoc().selection.createRange();
          }
          this.updateToolbar();
       },
       insertAtCursor: function(text) {
          if (!this.activated) return;
          if (Ext.isIE) {
             this.win.focus();
             var r = this.currentRange || this.getDoc().selection.createRange();
             if (r) {
                r.collapse(true);
                r.pasteHTML(text);
                this.syncValue();
                this.deferFocus();
                r.moveEnd('character', 0);
                r.moveStart('character', 0);
                r.select();
             }
          } else if (Ext.isGecko || Ext.isOpera|| Ext.isChrome) {
             this.win.focus();
             this.execCmd('InsertHTML', text);
             this.deferFocus();
          } else if (Ext.isWebKit) {
             this.execCmd('InsertText', text);
             this.deferFocus();
          }
       }
    });
    
    // override ExtJS 3.4.1.1 to avoid "string is undefined" or "object is undefined" or
    // "cannot execute code from a freed script" errors in IE9 when using <iframe> tags
    // in the html property.
    // The real cause is explained here:
    // http://stackoverflow.com/questions/5514973/javascript-code-in-iframes-in-ie9-not-working
    if (Ext.isIE9) {
       Ext.Component.prototype.originalRender = Ext.Component.prototype.render;
       Ext.override(Ext.Component, {
          render : function(container, position){
             var hasIframe =
                ( this.html && Ext.isString(this.html) &&
                  (this.html.toLowerCase().indexOf('iframe') >= 0) );
             if (hasIframe) {
                var originalHtml = this.html;
                delete this.html;
             }
             var result = Ext.Component.prototype.originalRender.apply(this, arguments);
             if (hasIframe) {
                var contentTarget = this.getContentTarget();
                contentTarget.update.defer(100, contentTarget, [Ext.DomHelper.markup(originalHtml)]);
             }
             return result;
          }
       });
    };
    
    // override ExtJS 3.4.1.1 to encode HTML according to ESAPI security guidelines:
    // https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
    Ext.apply(Ext.util.Format, {
       htmlEncode : function(value) {
          return !value ? value :
             String(value).replace(
                /&/g, "&amp;").replace(
                />/g, "&gt;").replace(
                /</g, "&lt;").replace(
                /"/g, "&quot;").replace(
                /'/g, "&#x27;").replace(
                /\//g, "&#x2F;");
       }
    });
    
    //added styleSheet to fix the incorrectly rendered grid columns in Chrome 19+ and other recent webkit browsers
    //if this behaviour was changed in future versions of Chrome - the below code could be deleted
    //This bug was the cause of the incorrectly aligned time headers in Reservation Calendar (WO 16696)
    //
    //Box-sizing was changed beginning with Chrome v19.  For background information, see:
    //   http://code.google.com/p/chromium/issues/detail?id=124816
    //   https://bugs.webkit.org/show_bug.cgi?id=78412
    //   https://bugs.webkit.org/show_bug.cgi?id=87536
    //   http://www.sencha.com/forum/showthread.php?198124-Grids-are-rendered-differently-in-upcoming-versions-of-Google-Chrome&p=886395&viewfull=1#post886395
    //   http://www.useragentstring.com/pages/Chrome/
    if (!Ext.isDefined(Ext.webKitVersion)) {
       Ext.webKitVersion = Ext.isWebKit ? parseFloat(/AppleWebKit\/([\d.]+)/.exec(navigator.userAgent)[1]) : NaN;
    };
    // chrome 19+ or safari 6+ (or any other recent webkit browser)
    if(Ext.isWebKit && Ext.webKitVersion >= 535.2 && Ext.webKitVersion <= 537.1) {
       Ext.onReady(function(){
          Ext.util.CSS.createStyleSheet('.ext-chrome .x-grid3-cell, .ext-chrome .x-grid3-gcell{box-sizing: border-box !important;}', 'chrome-fixes-box-sizing');
       });
    };
    
    // make sure Ext.Component does not keep references to external components around
    // (this.plugins, this.initialConfig
    (function() {
       var originalDestroy = Ext.Component.prototype.destroy;
       Ext.override(Ext.Component, {
          destroy: function() {
             if (!this.isDestroyed) {
                originalDestroy.apply(this, arguments);
                this.plugins = this.initialConfig = null;
             };
          }
       });
    })();
    
    // make sure Ext.grid.GridView does not keep references to drag-and-drop components
    // this.columnDrag, this.columnDrop, this.ds, ...
    (function() {
       var originalDestroy = Ext.grid.GridView.prototype.destroy;
       Ext.override(Ext.grid.GridView, {
          destroy: function() {
             originalDestroy.apply(this, arguments);
             this.columnDrag = this.columnDrop = this.splitZone = this.hmenu = this.ds = null;
          }
       });
    })();
    
    // make sure Ext.PagingToolbar does not keep references to objects around
    // this.dsLoaded
    (function() {
       var originalDestroy = Ext.PagingToolbar.prototype.destroy;
       Ext.override(Ext.PagingToolbar, {
          destroy: function() {
             originalDestroy.apply(this, arguments);
             this.dsLoaded = null;
          }
       });
    })();
    
    // override 3.4.1.1 to avoid leaking memory in container layouts
    (function() {
       var originalDestroy = Ext.layout.ContainerLayout.prototype.destroy;
       Ext.override(Ext.layout.ContainerLayout, {
          destroy: function() {
             originalDestroy.apply(this, arguments);
             this.resizeTask = null;
          }
       });
    })();
    
    // override 3.4.1.1 to make the "readOnly" attribute work correctly on a CheckBox
    // (property had no effect on ability to check / uncheck)
    (function() {
       var originalOnClick = Ext.form.Checkbox.prototype.onClick;
       Ext.override(Ext.form.Checkbox, {
          onClick: function() {
             if (!this.readOnly) {
                return originalOnClick.apply(this, arguments);
             } else {
                this.el.dom.checked = this.checked;
             };
             return this;
          }
       });
    })();
    
    // override 3.4.1.1 to make AnchorLayout not render scrollbars for anchor: 100% when
    // zooming in firefox (ctrl-+)
    (function() {
       var originalParseAnchor = Ext.layout.AnchorLayout.prototype.parseAnchor;
       Ext.override(Ext.layout.AnchorLayout, {
          parseAnchor : function(a, start, cstart) {
             var anchorFn = originalParseAnchor.apply(this, arguments);
             if (anchorFn && Ext.isGecko && (a.indexOf('100%') != -1)) {
                var isZoomed = window['matchMedia'] && window.matchMedia('(max--moz-device-pixel-ratio:0.99), (min--moz-device-pixel-ratio:1.01)').matches;
                if (isZoomed) {
                   return function(v) {
                      var result = anchorFn.call(this, v);
                      result -= 2;
                      return result;
                   };
                };
             };
             return anchorFn;
          }
       });
    })();
    
    // override 3.4.1.1 so RadioGroup.getValue() works before the RadioGroup is rendered
    Ext.override(Ext.form.RadioGroup, {
       eachItem: function(fn, scope) {
          if (this.items && this.items.each){
             this.items.each(fn, scope || this);
          } else if (Ext.isArray(this.items)) {
             Ext.each(this.items, fn, scope || this);
          }
       }
    });
    
    // override 3.4.1.1 Ext.ButtonGroup onAfterLayout. Fix: header width, when header is longer than the body
    Ext.override(Ext.ButtonGroup, {
       onAfterLayout : function() {
          var bodyWidth = this.body.getFrameWidth('lr') + this.body.dom.firstChild.offsetWidth;
    
          // begin apply fix
          if (this.header) {
             if (bodyWidth < this.body.getFrameWidth('lr') + this.header.dom.firstChild.offsetWidth) {
                bodyWidth = this.body.getFrameWidth('lr') + this.header.dom.offsetWidth;
             } 
          } // end fix apply
          
          this.body.setWidth(bodyWidth);
          this.el.setWidth(bodyWidth + this.getFrameWidth());
       }
    });
    For ExtJS 3.4.0:

    Code:
    // override 3.4.0 to be able to restore column state
    Ext.override(Ext.grid.ColumnModel, {
       // add properties on columns that are stateful here
       statefulColProps: ['width', 'hidden'],
       setState: function(col, state) {
          // filter properties on those that should actually be stateful
          // (prevents overwriting properties like renderer accidentally)
          var newState = {};
          if (state) {
             Ext.each(this.statefulColProps, function(prop) {
                if (state[prop]) newState[prop] = state[prop];
             });         
          };
          // apply to column configuration 
          if (this.columns && this.columns[col]) {
             Ext.apply(this.columns[col], newState);
          } else if (this.config && this.config[col]) {
             Ext.apply(this.config[col], newState);
          }
       }
    });
    
    // override 3.4.0 to fix layout bug with composite fields (field width too narrow)
    Ext.override(Ext.form.TriggerField, {
       onResize : function(w, h) {
          Ext.form.TriggerField.superclass.onResize.call(this, w, h);
          var tw = this.getTriggerWidth();
          if (Ext.isNumber(w)) {
             this.el.setWidth(w - tw);
          }
          if (this.rendered && !this.readOnly && this.editable && !this.el.getWidth()) this.wrap.setWidth(w);
          else this.wrap.setWidth(this.el.getWidth() + tw);
       }
    });
    
    // override 3.4.0 to fix issue where drag to select didn't work in ext scheduler
    Ext.override(Ext.dd.DragTracker, {
        onMouseMove: function(e, target) {
            // !Ext.isIE9 check added
            if (this.active && Ext.isIE && !Ext.isIE9 && !e.browserEvent.button) {
                e.preventDefault();
                this.onMouseUp(e);
                return;
            }
            
            e.preventDefault();
            var xy = e.getXY(), s = this.startXY;
            this.lastXY = xy;
            if (!this.active) {
                if (Math.abs(s[0]-xy[0]) > this.tolerance || Math.abs(s[1]-xy[1]) > this.tolerance) {
                    this.triggerStart(e);
                }else{
                    return;
                }
            }
            this.fireEvent('mousemove', this, e);
            this.onDrag(e);
            this.fireEvent('drag', this, e);
        }   
    });
    
    // override 3.4.0 to fix issue with tooltip text wrapping in IE9 (tooltip 1 pixel too narrow)
    // JS: I suspect this issue is caused by subpixel rendering in IE9 causing bad measurements
    Ext.override(Ext.Tip, {
       doAutoWidth : function(adjust) {
          // next line added to allow beforeshow to cancel tooltip (see below)
          if (!this.body) return;
          adjust = adjust || 0;
          var bw = this.body.getTextWidth();
          if (this.title) {
              bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));
          }
          bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + adjust;
          // added this line:
          if (Ext.isIE9) bw += 1;
          this.setWidth(bw.constrain(this.minWidth, this.maxWidth));
          
          if (Ext.isIE7 && !this.repainted) {
              this.el.repaint();
              this.repainted = true;
          }
       }
    });
    
    // override 3.4.0 to allow beforeshow to cancel the tooltip
    // EP: override 3.4.0 onMouseMove - to forbid tooltip to be positioned outside of the parent container(if this.constrainPosition == true)
    Ext.override(Ext.ToolTip, {
       show: function() {
          if (this.anchor) {
              this.showAt([-1000,-1000]);
              this.origConstrainPosition = this.constrainPosition;
              this.constrainPosition = false;
              this.anchor = this.origAnchor;
          }
          this.showAt(this.getTargetXY());
          
          if (this.anchor) {
              this.anchorEl.show();
              this.syncAnchor();
              this.constrainPosition = this.origConstrainPosition;
          // added "if (this.anchorEl)"
          } else if (this.anchorEl) {
              this.anchorEl.hide();
          }
       },
       showAt : function(xy) {
          this.lastActive = new Date();
          this.clearTimers();
          Ext.ToolTip.superclass.showAt.call(this, xy);
          if (this.dismissDelay && this.autoHide !== false) {
              this.dismissTimer = this.hide.defer(this.dismissDelay, this);
          }
          if (this.anchor && !this.anchorEl.isVisible()) {
              this.syncAnchor();
              this.anchorEl.show();
          // added "if (this.anchorEl)"
          } else if (this.anchorEl) {
              this.anchorEl.hide();
          }
       },
       onMouseMove : function(e){
          var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;
          if (t) {
             this.targetXY = e.getXY();
             if (t === this.triggerElement) {
                if(!this.hidden && this.trackMouse){
                   var xy = this.getTargetXY();
                   //added adjust positioning
                   if(this.constrainPosition){
                      xy = this.el.adjustForConstraints(xy);
                   }
                   this.setPagePosition(xy);
                }
             } else {
                this.hide();
                this.lastActive = new Date(0);
                this.onTargetOver(e);
             }
          } else if (!this.closable && this.isVisible()) {
             this.hide();
          }
       }
    });
    
    // override 3.4.0 to fix issue where enableDragDrop + checkbox selection has issues
    // clicking on a selected checkbox does not unselect it + impossible to select multiple
    // rows via checkbox
    Ext.override( Ext.grid.GridDragZone, {
       getDragData : function( e ) {
           var t = Ext.lib.Event.getTarget(e);
           var rowIndex = this.view.findRowIndex(t);
           if ( rowIndex !== false ) {
               var sm = this.grid.selModel;
               
               if ( sm instanceof( Ext.grid.CheckboxSelectionModel ) ) {
                   sm.onMouseDown( e, t );
               }
               
               if ( t.className != 'x-grid3-row-checker' && ( !sm.isSelected( rowIndex ) || e.hasModifier() ) ) {
                   sm.handleMouseDown( this.grid, rowIndex, e );
               }
               return { grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections() };
           }
           return false;
       }
    } );
    
    // override 3.4.0 to fix false security warning in IE on component destroy
    Ext.apply(Ext, {
       removeNode : Ext.isIE && !Ext.isIE9 ? function() {
          return function(n) {
             if (n && n.tagName != 'BODY') {
                (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n, true) : Ext.EventManager.removeAll(n);
                if (n.parentNode && n.tagName == 'TD') {
                   if (Ext.isIE7) {
                      n.parentNode.removeChild(n);
                   } else {
                      n.parentNode.deleteCell(n);
                   }
                } else if (n.parentNode && n.tagName == 'TR') {
                   n.parentNode.deleteRow(n);
                } else {
                   n.outerHTML = ' ';
                }
                delete Ext.elCache[n.id];
             }
          };
       }() : function(n) {
          if (n && n.parentNode && n.tagName != 'BODY') {
             (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n, true) : Ext.EventManager.removeAll(n);
             n.parentNode.removeChild(n);
             delete Ext.elCache[n.id];
          }
       }
    });
    
    // override 3.4.0 to ensure that the grid stops editing if the view is refreshed
    // actual bug: removing grid lines with active lookup editor didn't hide editor
    (function() {
       var originalProcessRows = Ext.grid.GridView.prototype.processRows;
       Ext.override(Ext.grid.GridView, {
          processRows: function() {
             if (this.grid) this.grid.stopEditing(true);
             originalProcessRows.apply(this, arguments);
          }
       });
    }());
    
    // override 3.4.0 to fix issue with chart labels losing their labelRenderer after hide/show
    Ext.override(Ext.chart.CartesianChart, {
       createAxis : function(axis, value) {
          var o = Ext.apply({}, value),
             ref,
             old;
    
          if (this[axis]) {
             old = this[axis].labelFunction;
             this.removeFnProxy(old);
             this.labelFn.remove(old);
          }
          if (o.labelRenderer) {
             ref = this.getFunctionRef(o.labelRenderer);
             o.labelFunction = this.createFnProxy(function(v) {
                return ref.fn.call(ref.scope, v);
             });
             // delete o.labelRenderer; // <-- commented out this line
             this.labelFn.push(o.labelFunction);
          }
          if (axis.indexOf('xAxis') > -1 && o.position == 'left') {
             o.position = 'bottom';
          }
          return o;
       }
    });
    
    // override 3.4.0 to allow tabbing between editable grid cells to work correctly
    Ext.override(Ext.grid.RowSelectionModel, {
       acceptsNav : function(row, col, cm) {
          if (!cm.isHidden(col) && cm.isCellEditable(col, row)) {
             // check that there is actually an editor
             if (cm.getCellEditor) return !!cm.getCellEditor(col, row);
             return true;
          }
          return false;
       }
    });
    
    // override ExtJS 3.4.0 to make sure that in IE the HTMLEditor
    // persists the cursor position across blur/focus events
    Ext.override(Ext.form.HtmlEditor, {
       onEditorEvent: function() {
          if (Ext.isIE) {
             this.currentRange = this.getDoc().selection.createRange();
          }
          this.updateToolbar();
       },
       insertAtCursor: function(text) {
          if (!this.activated) return;
          if (Ext.isIE) {
             this.win.focus();
             var r = this.currentRange || this.getDoc().selection.createRange();
             if (r) {
                r.collapse(true);
                r.pasteHTML(text);
                this.syncValue();
                this.deferFocus();
                r.moveEnd('character', 0);
                r.moveStart('character', 0);
                r.select();
             }
          } else if (Ext.isGecko || Ext.isOpera|| Ext.isChrome) {
             this.win.focus();
             this.execCmd('InsertHTML', text);
             this.deferFocus();
          } else if (Ext.isWebKit) {
             this.execCmd('InsertText', text);
             this.deferFocus();
          }
       },
       // JB: Cross browser function to get the selected text in the HtmlEditor body.
       // TODO: find solution so that the normal selected text in the HtmlEditor in IE gets replaced by the final link.
       // SOL1 and current implement: Remove text on first getSelectedText call if IE and then if cancel for link insert
       //                             it back else insert link.
       // SOL2: remember the start position and the length of the text+html tags. When clip, remove first then insert.
       getSelectedText : function(clip, extract) {
          var doc = this.getDoc(), selDocFrag;
          var txt = '', hasHTML = false, selNodes = [], ret, html = '';
          if (this.win.getSelection || doc.getSelection) {
             // FF, Chrome, Safari
             var sel = this.win.getSelection();
             if (!sel) {
                sel = doc.getSelection();
             }
             if (clip) {
                // TODO: need patch for IE9. In IE9 selection gets lost when LinkWindow is focused.
                // TODO: if we do not fix this then the selected text will not be replaced.
                selDocFrag = sel.getRangeAt(0).extractContents();
             } else {
                if (Ext.isIE9 && extract) {
                   selDocFrag = this.win.getSelection().getRangeAt(0).extractContents();
                } else {
                   selDocFrag = this.win.getSelection().getRangeAt(0).cloneContents();
                }
             }
             Ext.each(selDocFrag.childNodes, function(n) {
                if (n.nodeType !== 3) {
                   hasHTML = true;
                }
             });
             if (hasHTML) {
                var div = document.createElement('div');
                div.appendChild(selDocFrag);
                html = div.innerHTML;
                txt = this.win.getSelection()+'';
             } else {
                html = txt = selDocFrag.textContent;
             }
             ret = {
                textContent: txt,
                hasHTML: hasHTML,
                html: html
             };
          } else if (doc.selection) {
             // IE
             this.win.focus();
             txt = doc.selection.createRange();
             if (txt.text !== txt.htmlText) {
                hasHTML = true;
             }
             ret = {
                textContent: txt.text,
                hasHTML: hasHTML,
                html: txt.htmlText
             };
          } else {
             return {
                textContent: ''
             };
          }
          return ret;
       }
    });
    
    // override ExtJS 3.4.0 to avoid "string is undefined" or "object is undefined" or
    // "cannot execute code from a freed script" errors in IE9 when using <iframe> tags
    // in the html property.
    // The real cause is explained here:
    // http://stackoverflow.com/questions/5514973/javascript-code-in-iframes-in-ie9-not-working
    if (Ext.isIE9) {
       Ext.Component.prototype.originalRender = Ext.Component.prototype.render;
       Ext.override(Ext.Component, {
          render : function(container, position){
             var hasIframe =
                ( this.html && Ext.isString(this.html) &&
                  (this.html.toLowerCase().indexOf('iframe') >= 0) );
             if (hasIframe) {
                var originalHtml = this.html;
                delete this.html;
             }
             var result = Ext.Component.prototype.originalRender.apply(this, arguments);
             if (hasIframe) {
                var contentTarget = this.getContentTarget();
                contentTarget.update.defer(100, contentTarget, [Ext.DomHelper.markup(originalHtml)]);
             }
             return result;
          }
       });
    };
    
    // override ExtJS 3.4.0 to encode HTML according to ESAPI security guidelines:
    // https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
    Ext.apply(Ext.util.Format, {
       htmlEncode : function(value) {
          return !value ? value :
             String(value).replace(
                /&/g, "&amp;").replace(
                />/g, "&gt;").replace(
                /</g, "&lt;").replace(
                /"/g, "&quot;").replace(
                /'/g, "&#x27;").replace(
                /\//g, "&#x2F;");
       }
    });
    
    //added styleSheet to fix the incorrectly rendered grid columns in Chrome 19+ and other recent webkit browsers
    //
    //Box-sizing was changed beginning with Chrome v19.  For background information, see:
    //   http://code.google.com/p/chromium/issues/detail?id=124816
    //   https://bugs.webkit.org/show_bug.cgi?id=78412
    //   https://bugs.webkit.org/show_bug.cgi?id=87536
    //   http://www.sencha.com/forum/showthread.php?198124-Grids-are-rendered-differently-in-upcoming-versions-of-Google-Chrome&p=891425&viewfull=1#post891425
    if (!Ext.isDefined(Ext.webKitVersion)) {
        Ext.webKitVersion = Ext.isWebKit ? parseFloat(/AppleWebKit\/([\d.]+)/.exec(navigator.userAgent)[1], 10) : NaN;
    };
    // chrome 19+ or safari 6+ (or any other recent webkit browser)
    if(Ext.isWebKit && Ext.webKitVersion >= 535.2) {
       Ext.onReady(function(){
          Ext.util.CSS.createStyleSheet('.ext-chrome .x-grid3-cell, .ext-chrome .x-grid3-gcell{box-sizing: border-box !important;}', 'chrome-fixes-box-sizing');
       });
    };
    
    // override 3.4.0 to remove memory leaks
    // make sure Ext.Component does not keep references to external components around
    // (this.plugins, this.initialConfig)
    (function() {
       var originalDestroy = Ext.Component.prototype.destroy;
       Ext.override(Ext.Component, {
          destroy: function() {
             if (!this.isDestroyed) {
                originalDestroy.apply(this, arguments);
                this.plugins = this.initialConfig = null;
             };
          }
       });
    })();
    
    // override 3.4.0 to remove memory leaks
    // make sure Ext.grid.GridView does not keep references to drag-and-drop components
    // this.columnDrag, this.columnDrop, this.ds, ...
    (function() {
       var originalDestroy = Ext.grid.GridView.prototype.destroy;
       Ext.override(Ext.grid.GridView, {
          destroy: function() {
             originalDestroy.apply(this, arguments);
             this.columnDrag = this.columnDrop = this.splitZone = this.hmenu = this.ds = null;
          }
       })
    })();
    
    // override 3.4.0 to remove memory leaks
    // make sure Ext.PagingToolbar does not keep references to objects around
    // this.dsLoaded
    (function() {
       var originalDestroy = Ext.PagingToolbar.prototype.destroy;
       Ext.override(Ext.PagingToolbar, {
          destroy: function() {
             originalDestroy.apply(this, arguments);
             this.dsLoaded = null;
          }
       });
    })();
    
    // override 3.4.0 to avoid leaking memory in container layouts
    (function() {
       var originalDestroy = Ext.layout.ContainerLayout.prototype.destroy;
       Ext.override(Ext.layout.ContainerLayout, {
          destroy: function() {
             originalDestroy.apply(this, arguments);
             this.resizeTask = null;
          }
       })
    })();
    
    // override 3.4.0 to make the "readOnly" attribute work correctly on a CheckBox
    // (property had no effect on clickability
    (function() {
       var originalOnClick = Ext.form.Checkbox.prototype.onClick;
       Ext.override(Ext.form.Checkbox, {
          onClick: function() {
             if (!this.readOnly) {
                return originalOnClick.apply(this, arguments);
             } else {
                this.el.dom.checked = this.checked;
             };
             return this;
          }
       })
    })();
    
    // override 3.4.0 to make AnchorLayout not render scrollbars for anchor: 100% when
    // zooming in firefox (ctrl-+)
    (function() {
       var originalParseAnchor = Ext.layout.AnchorLayout.prototype.parseAnchor;
       Ext.override(Ext.layout.AnchorLayout, {
          parseAnchor : function(a, start, cstart) {
             var anchorFn = originalParseAnchor.apply(this, arguments);
             if (anchorFn && Ext.isGecko && (a.indexOf('100%') != -1)) {
                var isZoomed = window['matchMedia'] && window.matchMedia('(max--moz-device-pixel-ratio:0.99), (min--moz-device-pixel-ratio:1.01)').matches;
                if (isZoomed) {
                   return function(v) {
                      var result = anchorFn.call(this, v);
                      result -= 2;
                      return result;
                   };
                };
             };
             return anchorFn;
          }
       });
    })();
    Last edited by joeri; 25 Nov 2013 at 12:21 AM. Reason: 3.4.1.1: fix layout issues with grid in recent chrome, 2 additional fixes at the end

  2. #2
    Sencha Premium User mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    40,448

    Default

    Great open source/community contribution!
    Mitchell Simoens @LikelyMitch

    Check out my GitHub:
    https://github.com/mitchellsimoens

    Posts are my own, not any current, past or future employer's.

  3. #3

  4. #4
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Antwerp, Belgium
    Posts
    563

    Default

    In IE9, you can get "object not found" or "string not found" errors when including <iframe> tags inside the html property of a component configured using xtype.

    To avoid, you can use this override:
    Code:
    // override ExtJS 3.4.0 to avoid "string is undefined" or "object is undefined" or
    // "cannot execute code from a freed script" errors in IE9 when using <iframe> tags
    // in the html property.
    // The real cause is explained here:
    // http://stackoverflow.com/questions/5514973/javascript-code-in-iframes-in-ie9-not-working
    if (Ext.isIE9) {
       Ext.Component.prototype.originalRender = Ext.Component.prototype.render;
       Ext.override(Ext.Component, {
          render : function(container, position){
             var hasIframe =
                ( this.html && Ext.isString(this.html) &&
                  (this.html.toLowerCase().indexOf('iframe') >= 0) );
             if (hasIframe) {
                var originalHtml = this.html;
                delete this.html;
             };
             var result = Ext.Component.prototype.originalRender.apply(this, arguments);
             if (hasIframe) {
                var contentTarget = this.getContentTarget();
                contentTarget.update.defer(100, contentTarget, [Ext.DomHelper.markup(originalHtml)]);
             };
             return result;
          }
       });
    };

  5. #5
    Ext JS Premium Member
    Join Date
    Apr 2008
    Posts
    395

    Default

    @joeri, Many thanks for your contributions! Do you have an update of these overrides for 3.4.1.1 ?


    Quote Originally Posted by joeri View Post
    In case it helps someone, this is the set of overrides I use to fix bugs in ExtJS 3.4.0. Many of these were taken from the forums but some weren't reported yet by anyone.

    I'm posting it now because it has stopped growing (I've more or less stopped discovering new bugs in 3.4.0). 3.4.0 is reliable and fast for me (using these bugfixes).

    You're welcome to post yours.

    Code:
    // override 3.4.0 to be able to restore column state
    Ext.override(Ext.grid.ColumnModel, { 
       setState: function(col, state){ 
          state = Ext.applyIf(state, this.defaults);
          if (this.columns && this.columns[col]) {
             Ext.apply(this.columns[col], state);
          } else if (this.config && this.config[col]) {
             Ext.apply(this.config[col], state);
          };
       }
    }); 
    
    // override 3.4.0 to fix layout bug with composite fields (field width too narrow)
    Ext.override(Ext.form.TriggerField, {
       onResize : function(w, h){
          Ext.form.TriggerField.superclass.onResize.call(this, w, h);
          var tw = this.getTriggerWidth();
          if(Ext.isNumber(w)){
             this.el.setWidth(w - tw);
          }
          if (this.rendered && !this.readOnly && this.editable && !this.el.getWidth()) this.wrap.setWidth(w);
          else this.wrap.setWidth(this.el.getWidth() + tw);
       }
    });
    
    // override 3.4.0 to fix issue where drag to select didn't work in ext scheduler
    Ext.override(Ext.dd.DragTracker, {
        onMouseMove: function(e, target){
            // !Ext.isIE9 check added
            if(this.active && Ext.isIE && !Ext.isIE9 && !e.browserEvent.button){
                e.preventDefault();
                this.onMouseUp(e);
                return;
            }
            
            e.preventDefault();
            var xy = e.getXY(), s = this.startXY;
            this.lastXY = xy;
            if(!this.active){
                if(Math.abs(s[0]-xy[0]) > this.tolerance || Math.abs(s[1]-xy[1]) > this.tolerance){
                    this.triggerStart(e);
                }else{
                    return;
                }
            }
            this.fireEvent('mousemove', this, e);
            this.onDrag(e);
            this.fireEvent('drag', this, e);
        }   
    });
    
    // override 3.4.0 to fix issue with tooltip text wrapping in IE9 (tooltip 1 pixel too narrow)
    // JS: I suspect this issue is caused by subpixel rendering in IE9 causing bad measurements
    Ext.override(Ext.Tip, {
       doAutoWidth : function(adjust){
          // next line added to allow beforeshow to cancel tooltip (see below)
          if (!this.body) return;
          adjust = adjust || 0;
          var bw = this.body.getTextWidth();
          if(this.title){
              bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));
          }
          bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + adjust;
          // added this line:
          if (Ext.isIE9) bw += 1;
          this.setWidth(bw.constrain(this.minWidth, this.maxWidth));
          
          
          if(Ext.isIE7 && !this.repainted){
              this.el.repaint();
              this.repainted = true;
          }
       }
    });
    
    // override 3.4.0 to allow beforeshow to cancel the tooltip
    Ext.override(Ext.ToolTip, {
       show: function(){
          if(this.anchor){
              this.showAt([-1000,-1000]);
              this.origConstrainPosition = this.constrainPosition;
              this.constrainPosition = false;
              this.anchor = this.origAnchor;
          }
          this.showAt(this.getTargetXY());
          
          if(this.anchor){
              this.anchorEl.show();
              this.syncAnchor();
              this.constrainPosition = this.origConstrainPosition;
          // added "if (this.anchorEl)"
          }else if (this.anchorEl){
              this.anchorEl.hide();
          }
       },
       showAt : function(xy){
          this.lastActive = new Date();
          this.clearTimers();
          Ext.ToolTip.superclass.showAt.call(this, xy);
          if(this.dismissDelay && this.autoHide !== false){
              this.dismissTimer = this.hide.defer(this.dismissDelay, this);
          }
          if(this.anchor && !this.anchorEl.isVisible()){
              this.syncAnchor();
              this.anchorEl.show();
          // added "if (this.anchorEl)"
          }else if (this.anchorEl){
              this.anchorEl.hide();
          }
       }
    });
    
    // override 3.4.0 to fix issue where enableDragDrop + checkbox selection has issues
    // clicking on a selected checkbox does not unselect it + impossible to select multiple
    // rows via checkbox
    Ext.override( Ext.grid.GridDragZone, {
       getDragData : function( e ) {
           var t = Ext.lib.Event.getTarget(e);
           var rowIndex = this.view.findRowIndex(t);
           if ( rowIndex !== false ) {
               var sm = this.grid.selModel;
               
               if ( sm instanceof( Ext.grid.CheckboxSelectionModel ) ) {
                   sm.onMouseDown( e, t );
               }
               
               if( t.className != 'x-grid3-row-checker' && ( !sm.isSelected( rowIndex ) || e.hasModifier() ) ) {
                   sm.handleMouseDown( this.grid, rowIndex, e );
               }
               return { grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections() };
           }
           return false;
       }
    } );
    
    // override 3.4.0 to fix false security warning in IE on component destroy
    Ext.apply(Ext, {
       removeNode : Ext.isIE && !Ext.isIE9 ? function(){
          return function(n){
             if(n && n.tagName != 'BODY'){
                (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n, true) : Ext.EventManager.removeAll(n);
                if (n.parentNode && n.tagName == 'TD') {
                   n.parentNode.deleteCell(n);
                } else if (n.parentNode && n.tagName == 'TR') {
                   n.parentNode.deleteRow(n);
                } else {
                   n.outerHTML = ' ';
                }
                delete Ext.elCache[n.id];
             }
          };
       }() : function(n){
          if(n && n.parentNode && n.tagName != 'BODY'){
             (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n, true) : Ext.EventManager.removeAll(n);
             n.parentNode.removeChild(n);
             delete Ext.elCache[n.id];
          }
       }
    });
    
    // override 3.4.0 to ensure that the grid stops editing if the view is refreshed
    // actual bug: removing grid lines with active lookup editor didn't hide editor
    Ext.grid.GridView.prototype.processRows =
       Ext.grid.GridView.prototype.processRows.createInterceptor(function() {
          if (this.grid) this.grid.stopEditing(true);
       });
    
    // override 3.4.0 to fix issue with chart labels losing their labelRenderer after hide/show
    Ext.override(Ext.chart.CartesianChart,{
        createAxis : function(axis, value){
            var o = Ext.apply({}, value),
                ref,
                old;
    
            if(this[axis]){
                old = this[axis].labelFunction;
                this.removeFnProxy(old);
                this.labelFn.remove(old);
            }
            if(o.labelRenderer){
                ref = this.getFunctionRef(o.labelRenderer);
                o.labelFunction = this.createFnProxy(function(v){
                    return ref.fn.call(ref.scope, v);
                });
                // delete o.labelRenderer; // <-- commented out this line
                this.labelFn.push(o.labelFunction);
            }
            if(axis.indexOf('xAxis') > -1 && o.position == 'left'){
                o.position = 'bottom';
            }
            return o;
        }
    });
    
    // override 3.4.0 to allow tabbing between editable grid cells to work correctly
    Ext.override(Ext.grid.RowSelectionModel, {
       acceptsNav : function(row, col, cm){
          if (!cm.isHidden(col) && cm.isCellEditable(col, row)) {
             // check that there is actually an editor
             if (cm.getCellEditor) return !!cm.getCellEditor(col, row);
             return true;
          };
          return false;
       }
    });

  6. #6
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Antwerp, Belgium
    Posts
    563

    Default

    I will be upgrading our codebase to 3.4.1.1 this month most likely (it's a bit of a challenge because there are 100.000 lines of code in that codebase). At that point I'll go through the fixes file, bring it up to date, and will post the resulting file here.

  7. #7
    Ext JS Premium Member
    Join Date
    Apr 2008
    Posts
    395

    Default

    Many thanks.

  8. #8
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Antwerp, Belgium
    Posts
    563

    Default

    I updated the first post with the most recent version of the overrides for 3.4.0. There's a bunch of new ones which I hadn't posted here yet.

    The one to be especially mindful of is the htmlEncode override, because that fixes potential XSS security issues.

  9. #9
    Sencha Premium User
    Join Date
    Oct 2007
    Location
    Berlin, Germany
    Posts
    891

    Default

    The fixes from post #1 (except 2) are still valid/needed for Ext 3.4.1.1 (unfortunately)

    Just two fixes already made it into Ext 3.4.1.1:

    - Ext.dd.DragTracker override to fix issue where drag to select didn't work in ext scheduler
    - Fix the incorrectly rendered grid columns in Chrome 19+ and other recent webkit browsers

  10. #10
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Antwerp, Belgium
    Posts
    563

    Default

    I ported our codebase to 3.4.1.1. The updated fixes are in the original post. As pointed out by @wm003 most of the fixes are still needed. Not that much changed between 3.4.0 and 3.4.1.1, so theoretically the fixes should still work fine, but I have not had much testing with them on 3.4.1.1.

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •