<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>One damn thing after another</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/" />
    <link rel="self" type="application/atom+xml" href="http://hutten.org/bill/extjs/atom.xml" />
    <id>tag:hutten.org,2008-07-03:/bill/extjs//2</id>
    <updated>2012-05-06T13:08:41Z</updated>
    <subtitle>Trying to understand stuff that never sits still.</subtitle>
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type Pro 4.34-en</generator>

<entry>
    <title>Debugging PHP with MAMP 2, PHPStorm 4, OS X 10.7</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2012/05/debugging-php-with-mamp-2-phps.html" />
    <id>tag:hutten.org,2012:/bill/extjs//2.37</id>

    <published>2012-05-06T13:08:38Z</published>
    <updated>2012-05-06T13:08:41Z</updated>

    <summary>Getting this configured is simple enough, once you read 10,000 web pages. MAMP 2.0.5 ships with xdebug installed, but in my experience it does not work - at least, it does not work for me, on my 2.66 Ghz MacBook...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="Other" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p>Getting this configured is simple enough, once you read 10,000 web pages. MAMP 2.0.5 ships with xdebug installed, but in my experience it does not work - at least, it does not work for me, on my 2.66 Ghz MacBook Pro 17&#8221;, running OS X 10.7.3.  Internet research seems to indicate that this may be a 32bit vs 64bit problem.</p>

<p>So, here&#8217;s what I did to make it work:</p>

<p><strong>First</strong> - Don&#8217;t use the MAMP built-in xdebug.so file.  In your MAMP php.ini file there&#8217;s this line:</p>

<pre><code>zend_extension="/Applications/MAMP/bin/php5.3/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so"
</code></pre>

<p>Replace it with this line:</p>

<pre><code>zend_extension="/usr/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so"
</code></pre>

<p>OS X ships with a working version of xdebug - that&#8217;s what we&#8217;re using here.</p>

<p><strong>Second</strong> - don&#8217;t use Chrome.  At least for me, Chrome (I&#8217;m using the beta channel, so that may be part of the issue), does not work with the PHPStorm debugger - breakpoints are never triggered.  Firefox (beta as well) works fine.</p>

<p>With the above two constraints, you can then follow the PHPStorm documentation on creating a zero-configuration debugging environment (basically, create a &#8220;PHP Web Application&#8221; debugging configuration), and everything will work correctly.  In my case, I&#8217;m calling PHP code via XHR from an ExtJS front-end - PHP breakpoints work perfectly, I can step through code, examine variables, etc etc.  </p>
]]>
        

    </content>
</entry>

<entry>
    <title>Add &quot;setLabel&quot; to ExtJS 4 fields...</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2012/01/add-setlabel-to-extjs-4-fields.html" />
    <id>tag:hutten.org,2012:/bill/extjs//2.36</id>

    <published>2012-01-27T18:26:36Z</published>
    <updated>2012-01-27T18:26:38Z</updated>

    <summary>A trivial override: Ext.form.field.Base.override({ setLabel: function (text) { if (this.rendered) { Ext.get(this.labelEl.id).update(text); } this.fieldLabel = text; } });...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="ExtJS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p>A trivial override:</p>

<pre><code>Ext.form.field.Base.override({
    setLabel: function (text) {
        if (this.rendered) {
            Ext.get(this.labelEl.id).update(text);
        }
        this.fieldLabel = text;
    }
});
</code></pre>
]]>
        

    </content>
</entry>

<entry>
    <title>Make ExtJS 4 disabled fields darker</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2011/09/make-ext4-disabled-fields-dark.html" />
    <id>tag:hutten.org,2011:/bill/extjs//2.35</id>

    <published>2011-09-24T21:10:28Z</published>
    <updated>2011-09-24T21:12:12Z</updated>

    <summary>I find the disabled fields in ExtJS 4 (and 3) too light, especially in Safari/Chrome. Just add this to your application&#8217;s CSS file to make them darker: .x-item-disabled { color: #888888 !important; -moz-opacity: 100; opacity: 1; } .x-form-item-label .x-item-disabled {...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="ExtJS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p>I find the disabled fields in ExtJS 4 (and 3) too light, especially in Safari/Chrome.  Just add this to your application&#8217;s CSS file to make them darker:</p>

<pre><code>.x-item-disabled {
    color: #888888 !important;
    -moz-opacity: 100;
    opacity: 1;
}

.x-form-item-label .x-item-disabled {
    color: #888888 !important;
    -moz-opacity: 100;
    opacity: 1;
}
</code></pre>
]]>
        

    </content>
</entry>

<entry>
    <title>Enabling Unity on Ubuntu 11 under VMWare Fusion 3 on a MacBook Pro</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2011/07/enabling-unity-un-ubuntu-11-un.html" />
    <id>tag:hutten.org,2011:/bill/extjs//2.28</id>

    <published>2011-07-19T12:49:33Z</published>
    <updated>2011-07-19T12:51:00Z</updated>

    <summary>When installing Ubuntu 11 under VMWare Fusion it&#8217;s likely that the new Ubuntu &#8220;Unity&#8221; interface will not be enabled - you&#8217;ll just get some brief message during the install process or boot that the hardware doesn&#8217;t support it. I&#8217;m not...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="Other" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p>When installing Ubuntu 11 under VMWare Fusion it&#8217;s likely that the new Ubuntu &#8220;Unity&#8221; interface will not be enabled - you&#8217;ll just get some brief message during the install process or boot that the hardware doesn&#8217;t support it. I&#8217;m not 100% certain that this is a universal fix, but in my case using the &#8220;Additional Drivers&#8221; option under the &#8220;Administration&#8221; menu fixed the problem:</p>

<p><img style="display:block; margin-left:auto; margin-right:auto;" src="http://hutten.org/bill/extjs//additional_drivers_ubuntu.jpg" alt="Additional drivers ubuntu" title="additional_drivers_ubuntu.jpg" border="0" width="473" height="594" /></p>

<p>In the above image the &#8220;VMWare Client Tools&#8221; driver has been installed.  When the &#8220;Additional Drivers&#8221; was initially displayed it was not - once it was installed I just restarted the Ubuntu VM and Unity started working.</p>
]]>
        

    </content>
</entry>

<entry>
    <title>ExtJS 3 - How to make a draggable Panel</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2011/03/extjs-3---how-to-make-a-dragga.html" />
    <id>tag:hutten.org,2011:/bill/extjs//2.27</id>

    <published>2011-04-01T01:07:17Z</published>
    <updated>2011-04-01T01:07:21Z</updated>

    <summary>Just creating an Ext.Panel with &#8216;draggable&#8217; set to true will give you a panel that you can drag around, but you need to override the &#8216;onDrag&#8217; and &#8216;endDrag&#8217; methods to track the position of the panel when it&#8217;s being dragged...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="ExtJS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p>Just creating an Ext.Panel with &#8216;draggable&#8217; set to true will give you a panel that you can drag around, but you need to override the &#8216;onDrag&#8217; and &#8216;endDrag&#8217; methods to track the position of the panel when it&#8217;s being dragged and set it to that position when the drag ends.</p>

<p> </p>

<pre>var panel = new Ext.Panel({
    draggable: {</pre>
<pre>      insertProxy: false,</pre>
<pre>      onDrag: function(e) {
        var el = this.proxy.getEl();
        this.x = el.getLeft(true);
        this.y = el.getTop(true);
      },
</pre>

<pre>      endDrag: function(e) {
        this.panel.setPosition(this.x, this.y);
      }
    },
</pre>

<pre>    title: 'Panel',
    width: 400,
    height: 400,
    x: 20,
    y: 20
  });</pre>
]]>
        

    </content>
</entry>

<entry>
    <title>ExtJS 3 - How to have a draggable &amp; resizable image</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2011/03/extjs-3---how-to-have-a-dragga.html" />
    <id>tag:hutten.org,2011:/bill/extjs//2.26</id>

    <published>2011-03-28T22:55:21Z</published>
    <updated>2011-03-28T22:55:51Z</updated>

    <summary><![CDATA[How to have an image that&#8217;s both draggable &amp; resizable:   Ext.onReady(function() { var el = Ext.get('image1'); el.dd = new Ext.dd.DDProxy('image1', 'group'); var r = new Ext.Resizable('image1', { preserveRatio: true, width: 400, height: 300, resizeChild: true }); });   The...]]></summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="ExtJS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p>How to have an image that&#8217;s both draggable &amp; resizable:</p>

<p> </p>

<blockquote>
<pre>Ext.onReady(function() {</pre>
<pre>var el = Ext.get('image1');
el.dd = new Ext.dd.DDProxy('image1', 'group');</pre>
<pre>var r = new Ext.Resizable('image1', {
    preserveRatio: true,
    width: 400,
    height: 300,
    resizeChild: true
  });
});</pre>
</blockquote>

<p> </p>

<p>The above assumes you have your image wrapped in a &lt;div&gt; with an id of &#8220;image1&#8221;, ie:</p>

<blockquote>
<p>&lt;div id=&#8221;image1&#8221;&gt;&lt;img src=&#8221;images/test1.jpg&#8221;/&gt;&lt;/div&gt;</p>
</blockquote>
]]>
        

    </content>
</entry>

<entry>
    <title>ExtJS 4 PR3 &amp; PR4 - How to get double-click events on a GridPanel</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2011/03/extjs-4-pr3---how-to-get-doubl.html" />
    <id>tag:hutten.org,2011:/bill/extjs//2.25</id>

    <published>2011-03-15T21:20:43Z</published>
    <updated>2011-03-16T12:47:44Z</updated>

    <summary>  In ExtJS 3.x to listen for double-click events on a GridPanel you attach a listener to the GridPanel like this: this.on(&apos;rowdblclick&apos;, function(eventGrid, rowIndex, e) { console.log(&apos;double click&apos;); }, this);   In ExtJS 4.0 you need to listen for events...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="ExtJS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p> </p>

<p>In ExtJS 3.x to listen for double-click events on a GridPanel you attach a listener to the GridPanel like this:</p>

<blockquote>
<pre><code>this.on('rowdblclick', function(eventGrid, rowIndex, e) {
  console.log('double click');
}, this);</code></pre>
</blockquote>

<p> </p>

<p>In ExtJS 4.0 you need to listen for events on the GridPanel&#8217;s view, via the &#8220;viewConfig&#8221;:</p>

<blockquote>
<pre>viewConfig: {<br /><span style="white-space: pre;">    </span>listeners: {<br /><span style="white-space: pre;">       </span>dblclick: function(dataview, index, item, e) {
        <span style="white-space: pre;">        </span>console.log('dblclick');
      <span style="white-space: pre;">      </span>}
    <span style="white-space: pre;">    </span>}
  }</pre>
</blockquote>

<p> </p>

<p>In my case, I wanted the GridPanel to fire a &#8216;rowdblclick&#8217; event just like it did in Ext 3.x, so I defined my Ext 4.x GridPanel class like this:</p>

<blockquote>
<pre>Ext.define('MyGridPanel', {
  extend: 'Ext.grid.GridPanel',
</pre>
<pre>  viewConfig: {<br />  listeners: {<br />      'dblclick': {<br />        fn: function(dataview, index, item, e) {<br />          var gridPanel = this.ownerCt.ownerCt;<br />          gridPanel.fireEvent('rowdblclick', this, index, e);<br />        }<br />      }<br />    }<br />  },</pre>
<pre>initComponent: function() {
    this.addEvents(
      'rowdblclick'
      );
    this.callParent(arguments);
  }
});</pre>
</blockquote>

<p> </p>

<p>The &#8220;this.ownerCt.ownerCt&#8221; in the handler seems like hackery to me, but for now at least, in PR3, it works.  Anyone have a better solution?</p>

<p> </p>

<p><strong>UPDATE FOR PR4:</strong></p>

<p>The line &#8220;var gridPanel = this.ownerCt.ownerCt;&#8221; should be &#8220;var gridPanel = this.ownerCt;&#8221;</p>

<p> </p>
]]>
        

    </content>
</entry>

<entry>
    <title>ExtJS cursor position in a TextArea or TextField</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2010/11/extjs-cursor-position-in-a-tex.html" />
    <id>tag:hutten.org,2010:/bill/extjs//2.24</id>

    <published>2010-11-12T13:50:59Z</published>
    <updated>2010-11-12T15:05:24Z</updated>

    <summary>Say you have a standard ExtJS TextArea with a &#8216;keyup&#8217; event listener, ie: var commentsField = new Ext.form.TextArea({ fieldLabel: &apos;Comments&apos;, name: &apos;Comments&apos;, enableKeyEvents: true }); commentsField.on(&apos;keyup&apos;, function(field, e) { // Here&apos;s how you get the current cursor position/selection range: var...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="ExtJS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p><br />Say you have a standard ExtJS TextArea with a &#8216;keyup&#8217; event listener, ie:<br /><br /></p>

<pre><code>var commentsField = new Ext.form.TextArea({
    fieldLabel: 'Comments',
    name: 'Comments',
    enableKeyEvents: true
});

commentsField.on('keyup', function(field, e) {

    // Here's how you get the current cursor position/selection range:

    var s, e;

    if (Ext.isIE) {
        var bookmark = document.selection.createRange().getBookmark();
        var selection = field.el.dom.createTextRange();
        selection.moveToBookmark(bookmark);

        var before = field.el.dom.createTextRange();
        before.collapse(true);
        before.setEndPoint("EndToStart", selection);

        var selLength = selection.text.length;

        s = before.text.length;
        e = s + selLength;

    } else {
        s = field.el.dom.selectionStart;
        e = field.el.dom.selectionEnd;
    }
}, this);
</code></pre>

<p><br /><br />Just in case anyone would like to save the hour or so of Googling it took me to figure this out.</p>
]]>
        

    </content>
</entry>

<entry>
    <title>RESTful store example as a pre-configured class</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2010/05/restful-store-example-as-a-pre.html" />
    <id>tag:hutten.org,2010:/bill/extjs//2.23</id>

    <published>2010-05-20T18:45:31Z</published>
    <updated>2010-05-20T18:45:31Z</updated>

    <summary>The ExtJS 3.2+ examples include a &#8220;RESTful store&#8221; which is a very interesting no-code way to get full CRUD behaviour from a GridPanel. Unfortunately the example is not written with a reusable structure - here is the same example, but...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="ExtJS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p>The ExtJS 3.2+ examples include a &#8220;RESTful store&#8221; which is a very interesting no-code way to get full CRUD behaviour from a GridPanel. Unfortunately the example is not written with a reusable structure - here is the same example, but as a pre-configured class: <br /><br /></p>

<pre><code>
Ext.onReady(function() {
            Ext.QuickTips.init();

            var TheGrid = Ext.extend(Ext.grid.GridPanel, {
                        title: 'Users',
                        frame: true,
                        height: 300,
                        width: 500,
                        viewConfig: {
                            forceFit: true
                        },
                        editor: new Ext.ux.grid.RowEditor({
                                    saveText: 'Update'
                                }),


                        onAdd: function(btn, ev) {
                            var u = new this.store.recordType({
                                        first: '',
                                        last: '',
                                        email: ''
                                    });
                            this.editor.stopEditing();
                            this.store.insert(0, u);
                            this.editor.startEditing(0);
                        },


                        onDelete: function() {
                            var rec = this.getSelectionModel().getSelected();
                            if (rec) {
                                this.store.remove(rec);
                            }
                        },


                        initComponent: function() {
                            var proxy = new Ext.data.HttpProxy({
                                        url: 'app.php/users'
                                    });

                            var reader = new Ext.data.JsonReader({
                                        totalProperty: 'total',
                                        successProperty: 'success',
                                        idProperty: 'id',
                                        root: 'data',
                                        messageProperty: 'message' // attribute in server response for user message...
                                    }, [{
                                                name: 'id'
                                            }, {
                                                name: 'email'
                                            }, {
                                                name: 'first',
                                                allowBlank: false
                                            }, {
                                                name: 'last'
                                            }]);

                            var writer = new Ext.data.JsonWriter({
                                        encode: false
                                    });

                            var store = new Ext.data.Store({
                                        restful: true,
                                        proxy: proxy,
                                        reader: reader,
                                        writer: writer
                                    });

                            var config = {
                                store: store,
                                plugins: [this.editor],
                                columns: [{
                                            header: "ID",
                                            width: 40,
                                            sortable: true,
                                            dataIndex: 'id'
                                        }, {
                                            header: "Email",
                                            width: 100,
                                            sortable: true,
                                            dataIndex: 'email',
                                            editor: new Ext.form.TextField({})
                                        }, {
                                            header: "First",
                                            width: 50,
                                            sortable: true,
                                            dataIndex: 'first',
                                            editor: new Ext.form.TextField({})
                                        }, {
                                            header: "Last",
                                            width: 50,
                                            sortable: true,
                                            dataIndex: 'last',
                                            editor: new Ext.form.TextField({})
                                        }],
                                tbar: [{
                                            text: 'Add',
                                            iconCls: 'silk-add',
                                            handler: this.onAdd,
                                            scope: this
                                        }, '-', {
                                            text: 'Delete',
                                            iconCls: 'silk-delete',
                                            handler: this.onDelete,
                                            scope: this
                                        }, '-']
                            };

                            Ext.apply(this, Ext.apply(this.initialConfig, config));
                            TheGrid.superclass.initComponent.apply(this, arguments);

                        },


                        onRender: function() {
                            this.store.load();
                            TheGrid.superclass.onRender.apply(this, arguments);
                        }
                    });
            Ext.reg('my_grid', TheGrid);



            var w = new Ext.Window({
                        modal: true,
                        items: {
                            xtype: 'my_grid',
                            title: 'Panel 1'
                        }
                    });
            w.show();
        });
</code>﻿</pre>
]]>
        

    </content>
</entry>

<entry>
    <title>Using the &apos;ref&apos; option in ExtJS 3.x</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2010/05/using-the-ref-option-in-extjs.html" />
    <id>tag:hutten.org,2010:/bill/extjs//2.22</id>

    <published>2010-05-03T16:24:36Z</published>
    <updated>2010-05-03T16:24:36Z</updated>

    <summary>As with so many things in ExtJS, the &#8216;ref&#8217; option introduced in 3.0 is not very well documented, at least as far as I can tell. Which is unfortunate, because it&#8217;s extremely useful, and drastically reduces the need for ids....</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="ExtJS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p>As with so many things in ExtJS, the &#8216;ref&#8217; option introduced in 3.0 is not very well documented, at least as far as I can tell.  Which is unfortunate, because it&#8217;s <em>extremely</em> useful, and drastically reduces the need for ids.  Here&#8217;s an example of how to use it:<br /><br /></p>

<pre><code>Ext.onReady(function() {
            Ext.BLANK_IMAGE_URL = 'ext/resources/images/default/s.gif';
            Ext.QuickTips.init();


            // Define a simple component

            MyComponent = Ext.extend(Ext.form.FormPanel, {
                        frame: true,

                        initComponent: function() {
                            var config = {
                                items: [{
                                            xtype: 'textfield',
                                            fieldLabel: 'Name'
                                        }, {
                                            xtype: 'textfield',
                                            fieldLabel: 'Address'
                                        }],
                                bbar: ['-&gt;', {
                                            text: 'Cancel',
                                            minWidth: 100,
                                            ref: '../cancelButton' 
                                        }, {
                                            text: 'Save',
                                            minWidth: 100,
                                            ref: '../saveButton' 
                                        }]
                            };

                            Ext.apply(this, Ext.apply(this.initialConfig, config));
                            MyComponent.superclass.initComponent.apply(this, arguments);

                        }
                    });
            Ext.reg('my_component_xtype', MyComponent);


            // Create a display a window with the panel in it...

            var w = new Ext.Window({
                        modal: true,
                        items: {
                            xtype: 'my_component_xtype',
                            title: 'Panel 1',
                            ref: 'theFormPanel'
                        }
                    });
            w.show();


            // See how we can use the references...

            w.theFormPanel.saveButton.on('click', function() {
                        console.log('Save was clicked');
                    }, this);

            console.log(w.theFormPanel.title);
        });
</code></pre>
]]>
        

    </content>
</entry>

<entry>
    <title>Drupal - adding metadata to FileField</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2010/02/drupal-adding-metadata-to-file.html" />
    <id>tag:hutten.org,2010:/bill/extjs//2.21</id>

    <published>2010-02-08T14:28:13Z</published>
    <updated>2010-02-08T14:28:13Z</updated>

    <summary>While working with file uploads in Drupal, I had a situation where I needed to be able to attach metadata to FileFields. Specifically, the site needed to support video uploads, with a low-res Flash video that would be uploaded via...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="Drupal" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p>While working with file uploads in Drupal, I had a situation where I needed to be able to attach metadata to FileFields. Specifically, the site needed to support video uploads, with a low-res Flash video that would be uploaded via Drupal. For each of these Flash videos, there would be a corresponding high-res broadcast video that would be hosted on an external server, and that would be made available to the user via a URL.  So, I needed to be able to attach a &#8220;download URL&#8221; as metadata to each FileField. I wanted my upload form to look like this:</p>

<div style="text-align:center;"><img src="http://hutten.org/bill/extjs//filefield_metadata.jpg" alt="filefield_metadata.jpg" border="0" width="619" height="185" /></div>

<p><br />
A few hours of Google searching led me to think that this was a pretty complex task - <a href="http://www.poplarware.com/cckfieldmodule.html">this page</a> for instance is a detailed description of how to solve the problem, via the creation of a compound CCK field.</p>

<p>However, it turns out that there is a simpler solution, as described <a href="http://www.trellon.com/content/blog/adding-new-fields-to-file-uploads">on the Trellon blog.</a> Below is my description of how I solved the problem using the same technique.</p>

<p>The first thing you need to do is create your own Drupal module - this is very simple, and involves creating a folder in the &#8220;/sites/all/modules/&#8221; folder.  My module is called &#8220;mediacentre&#8221;, so I created &#8220;/sites/all/modules/mediacentre/&#8221;, and in that folder I created two files - &#8220;mediacentre.info&#8221; and &#8220;mediacentre.module&#8221;.<br /><br /></p>

<p>Here is the &#8220;mediacentre.info&#8221; file:</p>

<pre><code>    ; $Id: 
    name = Media Centre
    description = Media Centre
    core = 6.x
</code></pre>

<p><br /><br />
The &#8220;mediacentre.module&#8221; file is a little more complex:</p>

<pre><code>&lt;?php
function mediacentre_form_alter(&amp;$form, $form_state, $form_id) {
    if ($form_id == "story_node_form") {

        // each field can have multiple values, we need to add custom process function to every upload field
        foreach (element_children($form['field_video']) as $key) {
            $type = $form['field_video'][$key]['#type'];

            if ($type == 'filefield_widget') {
                $a = array('filefield_widget_process', 'download_url_widget_process');
                $form['field_video'][$key]['#process'] = $a;

                $va = array('filefield_widget_validate', 'download_url_widget_validate');
                $form['field_video'][$key]['#element_validate'] = $va;
            }
        }
    }
}

function download_url_widget_process($element, $edit, &amp;$form_state, $form) {
    $file = $element['#value'];

    $element['data']['download'] = array(
        '#type' =&gt; 'textfield',
        '#title' =&gt; t('Download URL'),
        '#value' =&gt; $file['fid'] ? $file['data']['download'] : ''
    );

    return $element;
}

function download_url_widget_validate($element, &amp;$form_state) {
}
</code></pre>

<p><br />In the above code the important method is &#8220;mediacentre_form_alter&#8221; - this is a Drupal <em>form hook</em> - the <a href="http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html">Drupal FAPI documentation</a> has much more information on this, most of it horribly confusing if you&#8217;re just trying - like I was - to learn the basics. With the above method in our module, every time Drupal displays a form it will call our form hook, so we can customize the form as needed. </p>

<p>In the form hook method we check for the ID of the form we want to modify - this can be discovered by using Firebug or the Webkit Inspector to examine your page.  In my case, I wanted to modify the Drupal form for editing nodes, and I was looking specifically for the forms that were used to upload my videos.  I was using CCK for these fields, and the field name was &#8220;field_video&#8221;. If you use a different field name, then you have to change the use of &#8216;field_video&#8217; in the method to the name of your own field.</p>

<p>For each &#8216;field_video&#8217;, we check for the &#8216;filefield_widget&#8217;, which is the actual upload form we want to modify, and we replace the existing &#8216;#process&#8217; key for that array with a new value that includes the name of our &#8220;process&#8221; or &#8220;validate&#8221; method. </p>

<p>So, when Drupal displays the &#8220;filefield_widget&#8221;, it will now call our &#8220;download_url_widget_process&#8221; method, and that method will insert a new element into the form with the name &#8216;download&#8217;, and the title &#8216;Download URL&#8217;.</p>

<p>Right now we&#8217;re not doing any particular validation, so we just leave the &#8220;download_url_widget_validate&#8221; method blank. Normally this method would contain code to make sure that the value entered is OK.</p>

<p>And that&#8217;s it!  When displaying your data in a tpl.php you have access to the new &#8220;download&#8221; value, in the same way you would access the &#8220;description&#8221; value, as a key in the item->data array.</p>
]]>
        

    </content>
</entry>

<entry>
    <title>How to manually customize Firebug 1.5.0 for widescreen use</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2010/01/how-to-manually-customize-fire.html" />
    <id>tag:hutten.org,2010:/bill/extjs//2.20</id>

    <published>2010-01-11T18:32:18Z</published>
    <updated>2010-01-11T18:32:18Z</updated>

    <summary>There&#8217;s a customized version of Firebug available called Widerbug that changes the Firebug layout to look like this: Unfortunately Widerbug is not updated as frequently as Firebug, so if you want to use the most recent versions of Firebug you&#8217;re...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="Other" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p><br />There&#8217;s a customized version of Firebug available called <a href="http://www.command-tab.com/2008/01/19/widerbug-widescreen-firebug/">Widerbug</a> that changes the Firebug layout to look like this:</p>

<div style="text-align:center;"><img src="http://hutten.org/bill/extjs//ScreenCapture001.jpg" alt="ScreenCapture001.jpg" border="0" width="617" height="391" /></div>

<p><br /><br />Unfortunately Widerbug is not updated as frequently as Firebug, so if you want to use the most recent versions of Firebug you&#8217;re out of luck.</p>

<p>To solve this issue I used FileMerge to compare Widerbug with a stock release of Firebug, and came up with the following list of required changes:  (<strong>UPDATE:</strong> It turns out that in the Widerbug xpi there&#8217;s a text file &#8220;instructions.txt&#8221; that details these steps as well&#8230;  ha!  Note that the steps described in the Widerbug &#8220;instructions.txt&#8221; are slightly more complex than mine, as they involve changing some Firebug artwork, the update URL, and signing the XPI.)</p>

<p>Anyway, here are my changes for Firebug 1.5.0. We edit the relevant files in-place, after Firebug has been installed. First you need to open the Firefox  &#8220;Profiles&#8221; directory, which on OS X is located at ~/Library/Application Support/Firefox/Profiles - open the appropriate profile, then look for the &#8220;extensions&#8221; directory, and inside that is the &#8220;firebug@software.joehewitt.com&#8221; directory.  Make the following edits:</p>

<p><br />1) <strong><em>content/firebug/browserOverlay.xul</em></strong></p>

<p><em>Replace this:</em></p>

<pre><code>    &lt;vbox id="appcontent"&gt;
        &lt;splitter id="fbContentSplitter" collapsed="true"/&gt;
        &lt;vbox id="fbContentBox" collapsed="true" persist="height"&gt;&lt;/vbox&gt;
    &lt;/vbox&gt;
</code></pre>

<p><em>With this:</em></p>

<pre><code>    &lt;hbox id="browser"&gt;
        &lt;splitter id="fbContentSplitter"/&gt;
        &lt;vbox id="fbContentBox" flex="1" collapsed="true" persist="width"/&gt;
    &lt;/hbox&gt;
</code></pre>

<p><br /><br />2) <strong><em>content/firebug/firebugOverlay.xul</em></strong></p>

<p>Change line 141 from </p>

<pre><code>    &lt;box id="fbPanelPane" flex="1" persist="orient"&gt;
</code></pre>

<p>to</p>

<pre><code>    &lt;vbox id="fbPanelPane" flex="1" persist="orient"&gt;
</code></pre>

<p><br /><br />and change line 276, the corresponding closing tag, from</p>

<pre><code>    &lt;/box&gt;
</code></pre>

<p>to</p>

<pre><code>    &lt;/vbox&gt;
</code></pre>

<p><br /><br />3) <strong><em>content/firebug/firebug.css</em></strong></p>

<p>Insert this:</p>

<pre><code>    #fbToolbox {
        border-top-width: 0px;
        border-top-style: none;
    }
</code></pre>

<p>Before this:</p>

<pre><code>    /************************************************************************************************/
    panelTabMenu {
        -moz-binding: url("chrome://firebug/content/bindings.xml#panelTabMenu");
    }
</code></pre>

<p><br /><br />4) <strong><em>skin/classic/mac/firebug.css</em></strong></p>

<p>Replace this:</p>

<pre><code>    #fbContentSplitter {
        border-top: 1px solid #BBB9BA;
        border-bottom: none;
        background: #f3f3f3 !important;
        min-height: 3px;
        max-height: 3px;
    }
</code></pre>

<p>With this:</p>

<pre><code>    #fbContentSplitter {
        border-left: 1px solid #BBB9BA;
        border-right: 1px solid #BBB9BA;
        background: #f3f3f3 !important;
        min-width: 5px;
        max-width: 5px;
    }
</code></pre>

<p><br /><br />4) <strong><em>skin/classic/win/firebug.css</em></strong></p>

<p>Replace this:</p>

<pre><code>    #fbContentSplitter {
        border-top: 1px solid !important;
        -moz-border-top-colors: threedShadow !important;
        border-bottom: 1px solid !important;
        -moz-border-bottom-colors: #EEEEEE !important;
        min-height: 3px;
        max-height: 3px;
        background-color: #FFFFFF;
    }
</code></pre>

<p>With this:</p>

<pre><code>    #fbContentSplitter {
        border-left: 1px solid !important;
        -moz-border-left-colors: threedShadow !important;
        border-right: 1px solid !important;
        -moz-border-right-colors: threedShadow !important;
        min-width: 3px;
        max-width: 3px;
        background-color: #d4d0c8;
    }
</code></pre>

<p><br /><br /><strong><em>NOTE:</em></strong>  The next time you update Firebug these changes will - of course - be overridden by the new version.  So it&#8217;s pretty ugly, but it works.</p>
]]>
        

    </content>
</entry>

<entry>
    <title>Enabling event bubbling for all items in a class</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2009/11/enabling-event-bubbling-for-al.html" />
    <id>tag:hutten.org,2009:/bill/extjs//2.19</id>

    <published>2009-11-03T13:14:25Z</published>
    <updated>2009-11-03T13:14:25Z</updated>

    <summary>There doesn&apos;t seem to be direct way in ExtJS to indicate that all items in a class should bubble events. Below is simple workaround - we iterate over all the items in the class in the &apos;afterRender&apos; event: Ext.BLANK_IMAGE_URL =...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="ExtJS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p>There doesn't seem to be direct way in ExtJS to indicate that all items in a class should bubble events.  Below is simple workaround - we iterate over all the items in the class in the 'afterRender' event:<br /><br /><br /></p>

<pre><code>Ext.BLANK_IMAGE_URL = 'lib/ext/resources/images/default/s.gif';

Ext.onReady(function() {
        Ext.QuickTips.init();


        FP = Ext.extend(Ext.form.FormPanel, {
                    frame: true,

                    initComponent: function() {
                        var config = {
                            items: [{
                                        xtype: 'textfield',
                                        name: 'First',
                                        fieldLabel: 'First'
                                    }, {
                                        xtype: 'textfield',
                                        name: 'Second',
                                        fieldLabel: 'Second'
                                    }]
                        };

                        Ext.apply(this, Ext.apply(this.initialConfig, config));
                        FP.superclass.initComponent.apply(this, arguments);

                        this.on('change', function(field, newVal, oldVal) {
                                    console.log('form change event on ' + field.name);
                                }, this);
                    },


                    // When the form has been rendered, we step though all the items on 
                    // the form and enable bubbling for each one. In a more complex form
                    // we would need to check the type of each item and enable the right
                    // kind of events, of course.

                    afterRender: function() {
                        FP.superclass.afterRender.apply(this, arguments);
                        Ext.each(this.items.items, function(item) {
                                    item.enableBubble('change');
                                }, this);
                    }
                });


        var fp = new FP();
        var win = new Ext.Window({
                    items: [fp]
                });
        win.show();
    });
</code></pre>
]]>
        

    </content>
</entry>

<entry>
    <title>Complex objects in classes (Ext3 &amp; Ext4 examples)</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2009/11/complex-objects-in-classes.html" />
    <id>tag:hutten.org,2009:/bill/extjs//2.18</id>

    <published>2009-11-03T02:10:31Z</published>
    <updated>2011-09-07T12:57:10Z</updated>

    <summary> When using Ext.extend (Ext3) or Ext.define (Ext4) to create your own classes, it&apos;s critical to understand that complex objects defined outside of a function definition are added to the prototype of the extended class, and as such are shared...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="ExtJS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p><br />
When using Ext.extend (Ext3) or Ext.define (Ext4) to create your own classes, it's critical to understand that complex objects defined outside of a function definition are added to the prototype of the extended class, and as such are <em>shared</em> across all instances of the class. This sharing is <em>not</em> the case with simple attributes.</p>

<p>The solution is to define complex objects inside the "initComponent" function, referencing them via "this".</p>

<p><strong>Ext4 Example:</strong></p>

<pre><code>Ext.onReady(function() {
  Ext.QuickTips.init();

  // This class definition is broken for most uses - the 'obj' complex object is
  // created when the class is defined, and as such is shared across all instances
  // of the class! Confusingly, 'simple' class attributes are /not/ shared.

  Ext.define('BrokenClass', {
    extend: 'Ext.Panel',
    obj: {
      colour: 'red'
    },
    simple: 'one', // this is not shared

    initComponent: function() {
      this.callParent();
    }
  });


  // This class definition is not broken - the 'obj' complex object is created when an
  // instance of the class is created, and thus is unique to each instance.

  Ext.define('WorkingClass', {
    extend: 'Ext.Panel',
    initComponent: function() {
      this.obj = {
        colour: 'red'
      };
      WorkingClass.superclass.initComponent.apply(this, arguments);
    }
  });


  // Note that b1 and b2 share the value of 'obj', but do NOT share the value
  // of 'simple'

  var b1 = Ext.create('BrokenClass');
  var b2 = Ext.create('BrokenClass');

  b2.obj.colour = 'blue';
  b2.simple = 'two';

  console.log(b1.obj.colour, b2.obj.colour, b1.simple, b2.simple);


  // Note that w1 and w2 each have their own values for 'obj'

  var w1 = Ext.create('WorkingClass');
  var w2 = Ext.create('WorkingClass');

  w1.obj.colour = 'blue';

  console.log(w1.obj.colour, w2.obj.colour);
});
</code></pre>

<p><br /><br /><hr><br /></p>

<p><strong>Ext3 Example:</strong></p>

<pre><code>Ext.BLANK_IMAGE_URL = 'lib/ext/resources/images/default/s.gif';

Ext.onReady(function() {
        Ext.QuickTips.init();

    // This class definition is broken for most uses - the 'obj' complex object is 
    // created when the class is defined, and as such is shared across all instances
    // of the class! Confusingly, 'simple' class attributes are /not/ shared.

        BrokenClass = Ext.extend(Ext.Panel, {
                    obj: {
                        colour: 'red'
                    },
                    simple: 'one', // this is not shared

                    initComponent: function() {
                        BrokenClass.superclass.initComponent.apply(this, arguments);
                    }
                });


    // This class definition is not broken - the 'obj' complex object is created when an 
    // instance of the class is created, and thus is unique to each instance.

        WorkingClass = Ext.extend(Ext.Panel, {
                    initComponent: function() {
                        this.obj = {
                            colour: 'red'
                        };
                        WorkingClass.superclass.initComponent.apply(this, arguments);
                    }
                });



    // Note that b1 and b2 share the value of 'obj', but do NOT share the value 
    // of 'simple'

        var b1 = new BrokenClass();
        var b2 = new BrokenClass();

        b2.obj.colour = 'blue';
        b2.simple = 'two';

        console.log(b1.obj.colour, b2.obj.colour, b1.simple, b2.simple);


        // Note that w1 and w2 each have their own values for 'obj'

        var w1 = new WorkingClass();
        var w2 = new WorkingClass();

        w1.obj.colour = 'blue';

        console.log(w1.obj.colour, w2.obj.colour);
    });
</code></pre>
]]>
        

    </content>
</entry>

<entry>
    <title>GridPanel from JSON data</title>
    <link rel="alternate" type="text/html" href="http://hutten.org/bill/extjs/2009/05/gridpanel-from-json-data.html" />
    <id>tag:hutten.org,2009:/bill/extjs//2.17</id>

    <published>2009-05-05T14:59:04Z</published>
    <updated>2009-05-05T14:59:04Z</updated>

    <summary> Given the following JSON data: {&quot;totalCount&quot;: 2,&quot;items&quot;: [{&quot;ID&quot;: 1,&quot;Name&quot;: &quot;First&quot;},{&quot;ID&quot;: 2,&quot;Name&quot;: &quot;Second&quot;}]} Note: ExtJS requires the above format for GridPanel data. The list of data must be wrapped in an object that contains a &quot;total count&quot; attribute, and the...</summary>
    <author>
        <name>Bill Hutten</name>
        
    </author>
    
        <category term="ExtJS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://hutten.org/bill/extjs/">
        <![CDATA[<p><br /><br /></p>

<p>Given the following JSON data:<br /><br />
{"totalCount": 2,"items": [{"ID": 1,"Name": "First"},{"ID": 2,"Name": "Second"}]}</p>

<p><br />
Note: ExtJS <em>requires</em> the above format for GridPanel data. The list of data must be wrapped in an object that contains a "total count" attribute, and the list itself is the data of an "items" attribute.  These names of these attributes must match the "totalProperty" and "root" values in the JsonReader of the Store used by the GridPanel. See below.</p>

<p><br /><br /><br />
Here's an ExtJS GridPanel that will load and display it:
<br /><br /></p>

<p>ExampleGrid = Ext.extend(Ext.grid.GridPanel, {
            title: 'Example',
            border: true,
            frame: true,
            closable: true,</p>

<pre><code>        initComponent: function() {

            var proxy = new Ext.data.HttpProxy({
                        url: 'http://host/url/to/get/JSON/data'
                    });

            var store = new Ext.data.Store({
                        remoteSort: true,
                        proxy: proxy,
                        baseParams: {
                            limit: 100
                        },
                        reader: new Ext.data.JsonReader({
                                    totalProperty: 'totalCount',
                                    root: 'items'
                                }, [{
                                            name: 'ID'
                                        }, {
                                            name: 'Name'
                                        }])
                    });

            Ext.apply(this, {
                        loadMask: true,
                        store: store,
                        colModel: new Ext.grid.ColumnModel([{
                                    header: 'ID',
                                    dataIndex: 'ID',
                                    sortable: true
                                }, {
                                    header: 'Name',
                                    dataIndex: 'Name',
                                    sortable: true
                                }])
                    });

            ExampleGrid.superclass.initComponent.apply(this, arguments);
        },

        onRender: function() {
            this.store.load();
            ExampleGrid.superclass.onRender.apply(this, arguments);
        }
    });
</code></pre>

<p>Ext.reg('ExampleGrid_panel', ExampleGrid);</p>
]]>
        

    </content>
</entry>

</feed>

