]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
webui: cosmetics cleanup of static/app 381/head
authorCarlo Landmeter <clandmeter@gmail.com>
Tue, 20 May 2014 16:41:35 +0000 (18:41 +0200)
committerCarlo Landmeter <clandmeter@gmail.com>
Wed, 21 May 2014 18:27:58 +0000 (20:27 +0200)
24 files changed:
src/webui/static/app/acleditor.js
src/webui/static/app/capmteditor.js
src/webui/static/app/chconf.js
src/webui/static/app/comet.js
src/webui/static/app/config.js
src/webui/static/app/cteditor.js
src/webui/static/app/cwceditor.js
src/webui/static/app/dvr.js
src/webui/static/app/epg.js [changed mode: 0755->0644]
src/webui/static/app/epggrab.js
src/webui/static/app/esfilter.js
src/webui/static/app/ext.css
src/webui/static/app/extensions.js
src/webui/static/app/idnode.js
src/webui/static/app/iptv.js
src/webui/static/app/mpegts.js
src/webui/static/app/servicemapper.js
src/webui/static/app/status.js
src/webui/static/app/tableeditor.js
src/webui/static/app/timeshift.js
src/webui/static/app/tvadapters.js
src/webui/static/app/tvheadend.js
src/webui/static/app/tvhlog.js
src/webui/static/app/v4l.js

index bdde91b359221232a84451fd75eb475e294d1f44..e3f7244da2d0004186ffce73cd0355a93f2a1b63 100644 (file)
@@ -1,78 +1,77 @@
 tvheadend.acleditor = function() {
-       var fm = Ext.form;
+    var fm = Ext.form;
 
-  var cm = new Ext.grid.ColumnModel({
-    defaultSortable: true,
+    var cm = new Ext.grid.ColumnModel({
+        defaultSortable: true,
+        columns: [{
+                xtype: 'checkcolumn',
+                header: "Enabled",
+                dataIndex: 'enabled',
+                width: 60
+            }, {
+                header: "Username",
+                dataIndex: 'username',
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            }, {
+                header: "Password",
+                dataIndex: 'password',
+                renderer: function(value, metadata, record, row, col, store) {
+                    return '<span class="tvh-grid-unset">Hidden</span>';
+                },
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            }, {
+                header: "Prefix",
+                dataIndex: 'prefix',
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            }, {
+                xtype: 'checkcolumn',
+                header: "Streaming",
+                dataIndex: 'streaming',
+                width: 100
+            }, {
+                xtype: 'checkcolumn',
+                header: "Video Recorder",
+                dataIndex: 'dvr',
+                width: 100
+            }, {
+                xtype: 'checkcolumn',
+                header: "All Configs (VR)",
+                dataIndex: 'dvrallcfg',
+                width: 100
+            }, {
+                xtype: 'checkcolumn',
+                header: "Web Interface",
+                dataIndex: 'webui',
+                width: 100
+            }, {
+                xtype: 'checkcolumn',
+                header: "Admin",
+                dataIndex: 'admin',
+                width: 100
+            }, {
+                xtype: 'checkcolumn',
+                header: "Channel Tag Only",
+                dataIndex: 'tag_only',
+                width: 200
+            }, {
+                header: "Comment",
+                dataIndex: 'comment',
+                width: 300,
+                editor: new fm.TextField({})
+            }]});
 
-    columns : [{
-      xtype: 'checkcolumn',
-      header : "Enabled",
-      dataIndex : 'enabled',
-      width : 60
-    }, {
-      header : "Username",
-      dataIndex : 'username',
-      editor : new fm.TextField({
-       allowBlank : false
-      })
-    }, {
-      header : "Password",
-      dataIndex : 'password',
-      renderer : function(value, metadata, record, row, col, store) {
-       return '<span class="tvh-grid-unset">Hidden</span>';
-      },
-      editor : new fm.TextField({
-       allowBlank : false
-      })
-    }, {
-      header : "Prefix",
-      dataIndex : 'prefix',
-      editor : new fm.TextField({
-       allowBlank : false
-      })
-    }, {
-      xtype: 'checkcolumn',
-      header : "Streaming",
-      dataIndex : 'streaming',
-      width : 100
-    }, {
-      xtype: 'checkcolumn',
-      header : "Video Recorder",
-      dataIndex : 'dvr',
-      width : 100
-    }, {
-      xtype: 'checkcolumn',
-      header : "All Configs (VR)",
-      dataIndex : 'dvrallcfg',
-      width : 100
-    }, {
-      xtype: 'checkcolumn',
-      header : "Web Interface",
-      dataIndex : 'webui',
-      width : 100
-    }, {
-      xtype: 'checkcolumn',
-      header : "Admin",
-      dataIndex : 'admin',
-      width : 100
-    }, {
-      xtype: 'checkcolumn',
-      header : "Channel Tag Only",
-      dataIndex : 'tag_only',
-      width : 200
-    }, {      
-      header : "Comment",
-      dataIndex : 'comment',
-      width : 300,
-      editor : new fm.TextField({})
-    }]});
+    var UserRecord = Ext.data.Record.create(
+            ['enabled', 'streaming', 'dvr', 'dvrallcfg', 'admin', 'webui', 'username', 'tag_only',
+                'prefix', 'password', 'comment'
+            ]);
 
-  var UserRecord = Ext.data.Record.create(
-    [ 'enabled', 'streaming', 'dvr', 'dvrallcfg', 'admin', 'webui', 'username', 'tag_only',
-      'prefix', 'password', 'comment'
-    ]);
-
-  return new tvheadend.tableEditor('Access control', 'accesscontrol', cm,
-                                  UserRecord, [], null, 'config_access.html',
-                                   'group');
-}
+    return new tvheadend.tableEditor('Access control', 'accesscontrol', cm,
+            UserRecord, [], null, 'config_access.html',
+            'group');
+};
index 37f55c5305e9120221b7809c6f1d7e146e79735d..21b85faf3bee5fd0deb30d9665c5ab37faf7a802 100644 (file)
 tvheadend.capmteditor = function() {
-       var fm = Ext.form;
+    var fm = Ext.form;
 
-       function setMetaAttr(meta, record) {
-               var enabled = record.get('enabled');
-               if (!enabled) return;
+    function setMetaAttr(meta, record) {
+        var enabled = record.get('enabled');
+        if (!enabled)
+            return;
 
-               var connected = record.get('connected');
-               if (connected == 2) {
-                       meta.attr = 'style="color:green;"';
-               }
-               else if (connected == 1) {
-                       meta.attr = 'style="color:orange;"';
-               }
-               else {
-                       meta.attr = 'style="color:red;"';
-               }
-       }
-       var selectMode = new Ext.form.ComboBox({
-               displayField:'name',
-               valueField: 'res',
-               value: 2,
-               mode: 'local',
-               editable: false,
-               triggerAction: 'all',
-               emptyText: 'Select mode...',
-               store: new Ext.data.SimpleStore({
-                       fields: ['res','name'],
-                       id: 0,
-                       data: [
-                               ['2','Recent OSCam (svn rev >= 9095)'],
-                               ['1','Older OSCam'],
-                               ['0','Wrapper (capmt_ca.so)']
-                       ]
-               })
-       });
+        var connected = record.get('connected');
+        if (connected === 2) {
+            meta.attr = 'style="color:green;"';
+        }
+        else if (connected === 1) {
+            meta.attr = 'style="color:orange;"';
+        }
+        else {
+            meta.attr = 'style="color:red;"';
+        }
+    }
+    var selectMode = new Ext.form.ComboBox({
+        displayField: 'name',
+        valueField: 'res',
+        value: 2,
+        mode: 'local',
+        editable: false,
+        triggerAction: 'all',
+        emptyText: 'Select mode...',
+        store: new Ext.data.SimpleStore({
+            fields: ['res', 'name'],
+            id: 0,
+            data: [
+                ['2', 'Recent OSCam (svn rev >= 9095)'],
+                ['1', 'Older OSCam'],
+                ['0', 'Wrapper (capmt_ca.so)']
+            ]
+        })
+    });
 
-       var cm = new Ext.grid.ColumnModel({
-  defaultSortable: true,
-          columns: [ {
-            xtype: 'checkcolumn',
-               header : "Enabled",
-               dataIndex : 'enabled',
-               width : 60
-       }, {
-               header: "Mode",
-               dataIndex: 'oscam',
-               width: 150,
-               editor: selectMode
-       }, {
-               header : "Camd.socket Filename",
-               dataIndex : 'camdfilename',
-               width : 200,
-               renderer : function(value, metadata, record, row, col, store) {
-                       setMetaAttr(metadata, record);
-                       return value;
-               },
-               editor : new fm.TextField({
-                       allowBlank : false
-               })
-       }, {
-               header : "Listenport",
-               dataIndex : 'port',
-               renderer : function(value, metadata, record, row, col, store) {
-                       setMetaAttr(metadata, record);
-                       return value;
-               },
-               editor : new fm.TextField({
-                       allowBlank : false
-               })
-       }, {
-               header : "Comment",
-               dataIndex : 'comment',
-               width : 400,
-               renderer : function(value, metadata, record, row, col, store) {
-                       setMetaAttr(metadata, record);
-                       return value;
-               },
-               editor : new fm.TextField()
-       ]});
+    var cm = new Ext.grid.ColumnModel({
+        defaultSortable: true,
+        columns: [{
+                xtype: 'checkcolumn',
+                header: "Enabled",
+                dataIndex: 'enabled',
+                width: 60
+            }, {
+                header: "Mode",
+                dataIndex: 'oscam',
+                width: 150,
+                editor: selectMode
+            }, {
+                header: "Camd.socket Filename",
+                dataIndex: 'camdfilename',
+                width: 200,
+                renderer: function(value, metadata, record, row, col, store) {
+                    setMetaAttr(metadata, record);
+                    return value;
+                },
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            }, {
+                header: "Listenport",
+                dataIndex: 'port',
+                renderer: function(value, metadata, record, row, col, store) {
+                    setMetaAttr(metadata, record);
+                    return value;
+                },
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            }, {
+                header: "Comment",
+                dataIndex: 'comment',
+                width: 400,
+                renderer: function(value, metadata, record, row, col, store) {
+                    setMetaAttr(metadata, record);
+                    return value;
+                },
+                editor: new fm.TextField()
+            }]});
 
-       var rec = Ext.data.Record.create([ 'enabled', 'connected', 'camdfilename',
-               'port', 'oscam', 'comment' ]);
+    var rec = Ext.data.Record.create(['enabled', 'connected', 'camdfilename',
+        'port', 'oscam', 'comment']);
 
-       store = new Ext.data.JsonStore({
-               root : 'entries',
-               fields : rec,
-               url : "tablemgr",
-               autoLoad : true,
-               id : 'id',
-               baseParams : {
-                       table : 'capmt',
-                       op : "get"
-               }
-       });
+    store = new Ext.data.JsonStore({
+        root: 'entries',
+        fields: rec,
+        url: "tablemgr",
+        autoLoad: true,
+        id: 'id',
+        baseParams: {
+            table: 'capmt',
+            op: "get"
+        }
+    });
 
-       tvheadend.comet.on('capmt', function(server) {
-               var rec = store.getById(server.id);
-               if (rec) {
-                       rec.set('connected', server.connected);
-               }
-       });
+    tvheadend.comet.on('capmt', function(server) {
+        var rec = store.getById(server.id);
+        if (rec) {
+            rec.set('connected', server.connected);
+        }
+    });
 
-       return new tvheadend.tableEditor('Capmt Connections', 'capmt', cm, rec,
-               [ ], store, 'config_capmt.html', 'key');
-}
+    return new tvheadend.tableEditor('Capmt Connections', 'capmt', cm, rec,
+            [], store, 'config_capmt.html', 'key');
+};
index 46bec4837a090cacaa9289d91d0b48ed8c65de99..deb0e9b43896501dc6c2af820c305cb3a46aad10 100644 (file)
  * Channel tags
  */
 tvheadend.channelTags = new Ext.data.JsonStore({
-       autoLoad : true,
-       root : 'entries',
-       fields : [ 'identifier', 'name' ],
-       id : 'identifier',
-       url : 'channeltags',
-       baseParams : {
-               op : 'listTags'
-       }
+    autoLoad: true,
+    root: 'entries',
+    fields: ['identifier', 'name'],
+    id: 'identifier',
+    url: 'channeltags',
+    baseParams: {
+        op: 'listTags'
+    }
 });
 
 tvheadend.channelTags.setDefaultSort('name', 'ASC');
 
 tvheadend.comet.on('channeltags', function(m) {
-       if (m.reload != null) tvheadend.channelTags.reload();
+    if (m.reload != null)
+        tvheadend.channelTags.reload();
 });
 
 /**
  * Channels
  */
 tvheadend.channelrec = new Ext.data.Record.create(
-       'name', 'chid', 'epggrabsrc', 'tags', 'ch_icon', 'epg_pre_start',
-               'epg_post_end', 'number' ]);
+        ['name', 'chid', 'epggrabsrc', 'tags', 'ch_icon', 'epg_pre_start',
+            'epg_post_end', 'number']);
 
 tvheadend.channels = new Ext.data.JsonStore({
-       url       : 'api/channel/list',
-       root      : 'entries',
-       fields    : [ 'key', 'val' ],
-       id        : 'key',
-       autoLoad  : true,
-  sortInfo : {
-    field : 'val',
-    direction : 'ASC'
-  }
+    url: 'api/channel/list',
+    root: 'entries',
+    fields: ['key', 'val'],
+    id: 'key',
+    autoLoad: true,
+    sortInfo: {
+        field: 'val',
+        direction: 'ASC'
+    }
 });
 
 tvheadend.comet.on('channels', function(m) {
-       if (m.reload != null) tvheadend.channels.reload();
+    if (m.reload != null)
+        tvheadend.channels.reload();
 });
 
 tvheadend.channel_tab = function(panel)
 {
-  function assign_low_number() {
-    var tab = panel.getActiveTab()
-    var sm = tab.getSelectionModel()
-    var store = tab.getStore()
-
-    if (sm.getCount() != 1)
-      return
-
-    var nums = []
-    store.each(function() {
-      if(this.data.number > 0)
-        nums.push(this.data.number)
-    })
-
-    if(nums.length == 0)
-    {
-      sm.getSelected().set('number', 1)
-      return
-    }
+    function assign_low_number() {
+        var tab = panel.getActiveTab();
+        var sm = tab.getSelectionModel();
+        var store = tab.getStore();
 
-    nums.sort(function(a,b) { return (a - b) })
+        if (sm.getCount() !== 1)
+            return;
 
-    var max = nums[nums.length - 1]
-    var low = max + 1
+        var nums = [];
+        store.each(function() {
+            if (this.data.number > 0)
+                nums.push(this.data.number);
+        });
 
-    for(var i = 1; i <= max; ++i)
-    {
-      var ct = false
-      for(var j = 0; j < nums.length; ++j)
-        if(nums[j] == i)
+        if (nums.length === 0)
         {
-          ct = true
-          break
+            sm.getSelected().set('number', 1);
+            return;
         }
 
-      if(!ct)
-      {
-        low = i
-        break;
-      }
-    }
+        nums.sort(function(a, b) {
+            return (a - b);
+        });
 
-    sm.getSelected().set('number', low)
-    sm.selectNext()
-  }
-
-  function move_number_up() {
-    var tab = panel.getActiveTab()
-    var sm = tab.getSelectionModel()
-    var store = tab.getStore()
-
-    if (sm.getCount() != 1)
-      return
-
-    var sel = sm.getSelected()
-    var num = sel.data.number
-
-    if(!num)
-      num = 0
-
-    store.each(function() {
-      if(this.data.number == num + 1)
-        this.set('number', num)
-    })
-
-    sel.set('number', num + 1)
-  }
-
-  function move_number_down() {
-    var tab = panel.getActiveTab()
-    var sm = tab.getSelectionModel()
-    var store = tab.getStore()
-
-    if(sm.getCount() != 1)
-      return
-
-    var sel = sm.getSelected()
-    var num = sel.data.number
-
-    if(!num)
-      num = 0
-
-    if(num <= 1)
-      return
-
-    store.each(function() {
-      if(this.data.number == num - 1)
-        this.set('number', num)
-    })
-
-    sel.set('number', num - 1)
-  }
-
-  function swap_numbers() {
-    var tab = panel.getActiveTab()
-    var sm = tab.getSelectionModel()
-    var store = tab.getStore()
-
-    if(sm.getCount() != 2)
-      return
-
-    var sel = sm.getSelections()
-    var tmp = sel[0].data.number
-
-    sel[0].set('number', sel[1].data.number)
-    sel[1].set('number', tmp)
-  }
-
-  var mapButton = new Ext.Toolbar.Button({
-    tooltip   : 'Map services to channels',
-    iconCls   : 'clone',
-    text      : 'Map Services',
-    handler   : tvheadend.service_mapper,
-    disabled  : false
-  });
-
-  var lowNoButton = new Ext.Toolbar.Button({
-    tooltip   : 'Assign lowest free channel number',
-    iconCls   : 'bullet_add',
-    text      : 'Assign Number',
-    handler   : assign_low_number,
-    disabled  : false
-  });
-
-  var noUpButton = new Ext.Toolbar.Button({
-    tooltip   : 'Move channel one number up',
-    iconCls   : 'arrow_up',
-    text      : 'Number Up',
-    handler   : move_number_up,
-    disabled  : false
-  });
-
-  var noDownButton = new Ext.Toolbar.Button({
-    tooltip   : 'Move channel one number down',
-    iconCls   : 'arrow_down',
-    text      : 'Number Down',
-    handler   : move_number_down,
-    disabled  : false
-  });
-
-  var noSwapButton = new Ext.Toolbar.Button({
-    tooltip   : 'Swap the two selected channels numbers',
-    iconCls   : 'arrow_switch',
-    text      : 'Swap Numbers',
-    handler   : swap_numbers,
-    disabled  : false
-  });
-
-  tvheadend.idnode_grid(panel, {
-    url     : 'api/channel',
-    comet   : 'channel',
-    titleS  : 'Channel',
-    titleP  : 'Channels',
-    tabIndex: 0,
-    add     :  {
-      url    : 'api/channel',
-      create : {}
-    },
-    del     : true,
-    tbar    : [ mapButton, lowNoButton, noUpButton, noDownButton, noSwapButton ],
-    lcol    : [
-      {
-        width    : 50,
-        header   : 'Play',
-        renderer : function (v, o, r) {
-          return "<a href='stream/channel/" + r.id + "'>Play</a>";
+        var max = nums[nums.length - 1];
+        var low = max + 1;
+
+        for (var i = 1; i <= max; ++i)
+        {
+            var ct = false;
+            for (var j = 0; j < nums.length; ++j)
+                if (nums[j] === i)
+                {
+                    ct = true;
+                    break
+                }
+            if (!ct)
+            {
+                low = i;
+                break;
+            }
         }
-      }
-    ],
-    sort    : {
-      field : 'number',
-      direction : 'ASC'
+
+        sm.getSelected().set('number', low);
+        sm.selectNext();
     }
-  });
-}
+
+    function move_number_up() {
+        var tab = panel.getActiveTab();
+        var sm = tab.getSelectionModel();
+        var store = tab.getStore();
+
+        if (sm.getCount() !== 1)
+            return;
+
+        var sel = sm.getSelected();
+        var num = sel.data.number;
+
+        if (!num)
+            num = 0;
+
+        store.each(function() {
+            if (this.data.number === num + 1)
+                this.set('number', num);
+        });
+
+        sel.set('number', num + 1);
+    }
+
+    function move_number_down() {
+        var tab = panel.getActiveTab();
+        var sm = tab.getSelectionModel();
+        var store = tab.getStore();
+
+        if (sm.getCount() !== 1)
+            return;
+
+        var sel = sm.getSelected();
+        var num = sel.data.number;
+
+        if (!num)
+            num = 0;
+
+        if (num <= 1)
+            return;
+
+        store.each(function() {
+            if (this.data.number === num - 1)
+                this.set('number', num);
+        });
+
+        sel.set('number', num - 1);
+    }
+
+    function swap_numbers() {
+        var tab = panel.getActiveTab();
+        var sm = tab.getSelectionModel();
+        var store = tab.getStore(); //store is unused
+
+        if (sm.getCount() !== 2)
+            return;
+
+        var sel = sm.getSelections();
+        var tmp = sel[0].data.number;
+
+        sel[0].set('number', sel[1].data.number);
+        sel[1].set('number', tmp);
+    }
+
+    var mapButton = new Ext.Toolbar.Button({
+        tooltip: 'Map services to channels',
+        iconCls: 'clone',
+        text: 'Map Services',
+        handler: tvheadend.service_mapper,
+        disabled: false
+    });
+
+    var lowNoButton = new Ext.Toolbar.Button({
+        tooltip: 'Assign lowest free channel number',
+        iconCls: 'bullet_add',
+        text: 'Assign Number',
+        handler: assign_low_number,
+        disabled: false
+    });
+
+    var noUpButton = new Ext.Toolbar.Button({
+        tooltip: 'Move channel one number up',
+        iconCls: 'arrow_up',
+        text: 'Number Up',
+        handler: move_number_up,
+        disabled: false
+    });
+
+    var noDownButton = new Ext.Toolbar.Button({
+        tooltip: 'Move channel one number down',
+        iconCls: 'arrow_down',
+        text: 'Number Down',
+        handler: move_number_down,
+        disabled: false
+    });
+
+    var noSwapButton = new Ext.Toolbar.Button({
+        tooltip: 'Swap the two selected channels numbers',
+        iconCls: 'arrow_switch',
+        text: 'Swap Numbers',
+        handler: swap_numbers,
+        disabled: false
+    });
+
+    tvheadend.idnode_grid(panel, {
+        url: 'api/channel',
+        comet: 'channel',
+        titleS: 'Channel',
+        titleP: 'Channels',
+        tabIndex: 0,
+        add: {
+            url: 'api/channel',
+            create: {}
+        },
+        del: true,
+        tbar: [mapButton, lowNoButton, noUpButton, noDownButton, noSwapButton],
+        lcol: [
+            {
+                width: 50,
+                header: 'Play',
+                renderer: function(v, o, r) {
+                    return "<a href='stream/channel/" + r.id + "'>Play</a>";
+                }
+            }
+        ],
+        sort: {
+            field: 'number',
+            direction: 'ASC'
+        }
+    });
+};
index 68455ef5b431d90acb39e03c4bfce91b9201f6a7..3b9493e60a65bb37f987595f9b6d63eaf337b06c 100644 (file)
@@ -2,19 +2,19 @@
  * Comet interfaces
  */
 Ext.extend(tvheadend.Comet = function() {
-       this.addEvents({
-               accessUpdate : true,
-               tvAdapter : true,
-               dvbMux : true,
-               dvbStore : true,
-               dvbSatConf : true,
-               logmessage : true,
-               channeltags : true,
-               autorec : true,
-               dvrdb : true,
-               dvrconfig : true,
-               channels : true
-       });
+    this.addEvents({
+        accessUpdate: true,
+        tvAdapter: true,
+        dvbMux: true,
+        dvbStore: true,
+        dvbSatConf: true,
+        logmessage: true,
+        channeltags: true,
+        autorec: true,
+        dvrdb: true,
+        dvrconfig: true,
+        channels: true
+    });
 }, Ext.util.Observable);
 
 tvheadend.comet = new tvheadend.Comet();
@@ -22,53 +22,53 @@ tvheadend.boxid = null;
 
 tvheadend.cometPoller = function() {
 
-       var failures = 0;
+    var failures = 0;
 
-       var cometRequest = new Ext.util.DelayedTask(function() {
+    var cometRequest = new Ext.util.DelayedTask(function() {
 
-               Ext.Ajax
-                       .request({
-                               url : 'comet/poll',
-                               params : {
-                                       boxid : (tvheadend.boxid ? tvheadend.boxid : null),
-                                       immediate : failures > 0 ? 1 : 0
-                               },
-                               success : function(result, request) {
-                                       parse_comet_response(result.responseText);
+        Ext.Ajax
+                .request({
+                    url: 'comet/poll',
+                    params: {
+                        boxid: (tvheadend.boxid ? tvheadend.boxid : null),
+                        immediate: failures > 0 ? 1 : 0
+                    },
+                    success: function(result, request) {
+                        parse_comet_response(result.responseText);
 
-                                       if (failures > 1) {
-                                               tvheadend.log('Reconnected to Tvheadend',
-                                                       'font-weight: bold; color: #080');
-                                       }
-                                       failures = 0;
-                               },
-                               failure : function(result, request) {
-                                       cometRequest.delay(failures ? 1000 : 1);
-                                       if (failures == 1) {
-                                               tvheadend.log('There seems to be a problem with the '
-                                                       + 'live update feed from Tvheadend. '
-                                                       + 'Trying to reconnect...',
-                                                       'font-weight: bold; color: #f00');
-                                       }
-                                       failures++;
-                               }
-                       });
-       });
+                        if (failures > 1) {
+                            tvheadend.log('Reconnected to Tvheadend',
+                                    'font-weight: bold; color: #080');
+                        }
+                        failures = 0;
+                    },
+                    failure: function(result, request) {
+                        cometRequest.delay(failures ? 1000 : 1);
+                        if (failures === 1) {
+                            tvheadend.log('There seems to be a problem with the '
+                                    + 'live update feed from Tvheadend. '
+                                    + 'Trying to reconnect...',
+                                    'font-weight: bold; color: #f00');
+                        }
+                        failures++;
+                    }
+                });
+    });
 
-       function parse_comet_response(responsetxt) {
-               response = Ext.util.JSON.decode(responsetxt);
-               tvheadend.boxid = response.boxid
-               for (x = 0; x < response.messages.length; x++) {
-                       m = response.messages[x];
-                       try {
-                         tvheadend.comet.fireEvent(m.notificationClass, m);
-                       } catch (e) {
-                               tvheadend.log('comet failure [e=' + e.message + ']');
-                       }
-               }
-               cometRequest.delay(100);
-       }
-       ;
+    function parse_comet_response(responsetxt) {
+        response = Ext.util.JSON.decode(responsetxt);
+        tvheadend.boxid = response.boxid;
+        for (x = 0; x < response.messages.length; x++) {
+            m = response.messages[x];
+            try {
+                tvheadend.comet.fireEvent(m.notificationClass, m);
+            } catch (e) {
+                tvheadend.log('comet failure [e=' + e.message + ']');
+            }
+        }
+        cometRequest.delay(100);
+    }
+    ;
 
-       cometRequest.delay(100);
-}
+    cometRequest.delay(100);
+};
index d1d99403e7f719c29cb50e5c2d543bb8f2e94731..8a87d25f28dae6e14b8bafccf57dbaa43af0058d 100644 (file)
@@ -1,31 +1,31 @@
 // Store: config languages
 tvheadend.languages = new Ext.data.JsonStore({
-    autoLoad:true,
-    root:'entries',
-    fields: ['identifier','name'],
+    autoLoad: true,
+    root: 'entries',
+    fields: ['identifier', 'name'],
     id: 'identifier',
-    url:'languages',
+    url: 'languages',
     baseParams: {
-       op: 'list'
+        op: 'list'
     }
 });
 
 // Store: all languages
 tvheadend.config_languages = new Ext.data.JsonStore({
-    autoLoad:true,
-    root:'entries',
-    fields: ['identifier','name'],
+    autoLoad: true,
+    root: 'entries',
+    fields: ['identifier', 'name'],
     id: 'identifier',
-    url:'languages',
+    url: 'languages',
     baseParams: {
-       op: 'config'
+        op: 'config'
     }
 });
 
 tvheadend.languages.setDefaultSort('name', 'ASC');
 
 tvheadend.comet.on('config', function(m) {
-    if(m.reload != null) {
+    if (m.reload != null) {
         tvheadend.languages.reload();
         tvheadend.config_languages.reload();
     }
@@ -33,240 +33,243 @@ tvheadend.comet.on('config', function(m) {
 
 tvheadend.miscconf = function() {
 
-       /*
-        * Basic Config
-        */
-       var confreader = new Ext.data.JsonReader({
-               root : 'config'
-       }, [ 'muxconfpath', 'language',
-       'tvhtime_update_enabled', 'tvhtime_ntp_enabled',
-       'tvhtime_tolerance', 'transcoding_enabled']);
-
-       /* ****************************************************************
-        * Form Fields
-        * ***************************************************************/
-
-  /*
-   * DVB path
-   */
-
-       var dvbscanPath = new Ext.form.TextField({
-               fieldLabel : 'DVB scan files path',
-               name : 'muxconfpath',
-               allowBlank : true,
-               width: 400
-       });
-
-  /*
-   * Language
-   */
-
-       var language = new Ext.ux.ItemSelector({
-               name: 'language',
-               fromStore: tvheadend.languages,
-               toStore: tvheadend.config_languages,
-               fieldLabel: 'Default Language(s)',
-               dataFields:['identifier', 'name'],
-               msWidth: 190,
-               msHeight: 150,
-               valueField: 'identifier',
-               displayField: 'name',
-               imagePath: 'static/multiselect/resources',
-               toLegend: 'Selected',
-               fromLegend: 'Available'
-       });
-
-  /*
-   * Time/Date
-   */
-  var tvhtimeUpdateEnabled = new Ext.form.Checkbox({
-    name: 'tvhtime_update_enabled',
-    fieldLabel: 'Update time'
-  });
-  
-  var tvhtimeNtpEnabled = new Ext.form.Checkbox({
-    name: 'tvhtime_ntp_enabled',
-    fieldLabel: 'Enable NTP driver'
-  });
-
-  var tvhtimeTolerance = new Ext.form.NumberField({
-    name: 'tvhtime_tolerance',
-    fieldLabel: 'Update tolerance (ms)'
-  });
-
-  var tvhtimePanel = new Ext.form.FieldSet({
-    title: 'Time Update',
-    width: 700,
-    autoHeight: true,
-    collapsible: true,
-    items : [ tvhtimeUpdateEnabled, tvhtimeNtpEnabled, tvhtimeTolerance ]
-  });
-
-  /*
-   * Image cache
-   */
-  if (tvheadend.capabilities.indexOf('imagecache') != -1) {
-    var imagecache_reader = new Ext.data.JsonReader({
-      root        : 'entries'
+    /*
+     * Basic Config
+     */
+    var confreader = new Ext.data.JsonReader({
+        root: 'config'
     },
     [
-       'enabled', 'ok_period', 'fail_period', 'ignore_sslcert',
+        'muxconfpath', 'language',
+        'tvhtime_update_enabled', 'tvhtime_ntp_enabled',
+        'tvhtime_tolerance', 'transcoding_enabled'
     ]);
 
-    var imagecacheEnabled = new Ext.ux.form.XCheckbox({
-      name: 'enabled',
-      fieldLabel: 'Enabled',
+    /* ****************************************************************
+     * Form Fields
+     * ***************************************************************/
+
+    /*
+     * DVB path
+     */
+
+    var dvbscanPath = new Ext.form.TextField({
+        fieldLabel: 'DVB scan files path',
+        name: 'muxconfpath',
+        allowBlank: true,
+        width: 400
     });
 
-    var imagecacheOkPeriod = new Ext.form.NumberField({
-      name: 'ok_period',
-      fieldLabel: 'Re-fetch period (hours)'
+    /*
+     * Language
+     */
+
+    var language = new Ext.ux.ItemSelector({
+        name: 'language',
+        fromStore: tvheadend.languages,
+        toStore: tvheadend.config_languages,
+        fieldLabel: 'Default Language(s)',
+        dataFields: ['identifier', 'name'],
+        msWidth: 190,
+        msHeight: 150,
+        valueField: 'identifier',
+        displayField: 'name',
+        imagePath: 'static/multiselect/resources',
+        toLegend: 'Selected',
+        fromLegend: 'Available'
     });
 
-    var imagecacheFailPeriod = new Ext.form.NumberField({
-      name: 'fail_period',
-      fieldLabel: 'Re-try period (hours)',
+    /*
+     * Time/Date
+     */
+    var tvhtimeUpdateEnabled = new Ext.form.Checkbox({
+        name: 'tvhtime_update_enabled',
+        fieldLabel: 'Update time'
     });
 
-    var imagecacheIgnoreSSLCert = new Ext.ux.form.XCheckbox({
-      name: 'ignore_sslcert',
-      fieldLabel: 'Ignore invalid SSL certificate'
+    var tvhtimeNtpEnabled = new Ext.form.Checkbox({
+        name: 'tvhtime_ntp_enabled',
+        fieldLabel: 'Enable NTP driver'
     });
 
-    var imagecachePanel = new Ext.form.FieldSet({
-      title: 'Image Caching',
-      width: 700,
-      autoHeight: true,
-      collapsible: true,
-      items : [ imagecacheEnabled, imagecacheOkPeriod, imagecacheFailPeriod,
-                imagecacheIgnoreSSLCert ]
+    var tvhtimeTolerance = new Ext.form.NumberField({
+        name: 'tvhtime_tolerance',
+        fieldLabel: 'Update tolerance (ms)'
     });
 
-    var imagecache_form = new Ext.form.FormPanel({
-      border : false,
-      labelAlign : 'left',
-      labelWidth : 200,
-      waitMsgTarget : true,
-      reader: imagecache_reader,
-      layout : 'form',
-      defaultType : 'textfield',
-      autoHeight : true,
-      items : [ imagecachePanel ]
+    var tvhtimePanel = new Ext.form.FieldSet({
+        title: 'Time Update',
+        width: 700,
+        autoHeight: true,
+        collapsible: true,
+        items: [tvhtimeUpdateEnabled, tvhtimeNtpEnabled, tvhtimeTolerance]
     });
-  } else {
-    var imagecache_form = null;
-  }
-
-  /*
-   * Transcoding
-   */
-  var transcodingEnabled = new Ext.form.Checkbox({
-    name: 'transcoding_enabled',
-    fieldLabel: 'Enabled',
-  });
-
-  var transcodingPanel = new Ext.form.FieldSet({
-    title: 'Transcoding',
-    width: 700,
-    autoHeight: true,
-    collapsible: true,
-    items : [ transcodingEnabled ]
-  });
-  if (tvheadend.capabilities.indexOf('transcoding') == -1)
-    transcodingPanel.hide();
-
-
-       /* ****************************************************************
-        * Form
-        * ***************************************************************/
-
-       var saveButton = new Ext.Button({
-               text : "Save configuration",
-               tooltip : 'Save changes made to configuration below',
-               iconCls : 'save',
-               handler : saveChanges
-       });
-
-       var helpButton = new Ext.Button({
-               text : 'Help',
-               handler : function() {
-                       new tvheadend.help('General Configuration', 'config_misc.html');
-               }
-       });
-
-       var confpanel = new Ext.form.FormPanel({
-               labelAlign : 'left',
-               labelWidth : 200,
-               border : false,
-               waitMsgTarget : true,
-               reader : confreader,
-               layout : 'form',
-               defaultType : 'textfield',
-               autoHeight : true,
-               items : [ language, dvbscanPath,
-                         tvhtimePanel,
-                         transcodingPanel]
-       });
-
-  var _items = [confpanel];
-  if (imagecache_form)
-    _items.push(imagecache_form);
-  var panel = new Ext.Panel({
-               title : 'General',
-               iconCls : 'wrench',
-               border : false,
-               bodyStyle : 'padding:15px',
-               layout : 'form',
-    items: _items,
-               tbar : [ saveButton, '->', helpButton ]
-  });
-
-       /* ****************************************************************
-        * Load/Save
-        * ***************************************************************/
-
-       confpanel.on('render', function() {
-               confpanel.getForm().load({
-                       url : 'config',
-                       params : {
-                               op : 'loadSettings'
-                       },
-                       success : function(form, action) {
-                               confpanel.enable();
-                       }
-               });
-               if (imagecache_form)
-      imagecache_form.getForm().load({
-        url     : 'api/imagecache/config/load',
-        success : function (form, action) {
-          imagecache_form.enable();
+
+    /*
+     * Image cache
+     */
+    if (tvheadend.capabilities.indexOf('imagecache') !== -1) {
+        var imagecache_reader = new Ext.data.JsonReader({
+            root: 'entries'
         },
-        failure : function (form, action) {
-          alert("FAILED");
-        }
-      });
-       });
-
-       function saveChanges() {
-               confpanel.getForm().submit({
-                       url : 'config',
-                       params : {
-                               op : 'saveSettings'
-                       },
-                       waitMsg : 'Saving Data...',
-                       failure : function(form, action) {
-                               Ext.Msg.alert('Save failed', action.result.errormsg);
-                       }
-               });
-               if (imagecache_form)
-      imagecache_form.getForm().submit({
-        url     : 'api/imagecache/config/save',
-        waitMsg : 'Saving data...',
-        failure : function(form, action) {
-          Ext.Msg.alert('Imagecache save failed', action.result.errormsg);
+        [
+            'enabled', 'ok_period', 'fail_period', 'ignore_sslcert'
+        ]);
+
+        var imagecacheEnabled = new Ext.ux.form.XCheckbox({
+            name: 'enabled',
+            fieldLabel: 'Enabled'
+        });
+
+        var imagecacheOkPeriod = new Ext.form.NumberField({
+            name: 'ok_period',
+            fieldLabel: 'Re-fetch period (hours)'
+        });
+
+        var imagecacheFailPeriod = new Ext.form.NumberField({
+            name: 'fail_period',
+            fieldLabel: 'Re-try period (hours)'
+        });
+
+        var imagecacheIgnoreSSLCert = new Ext.ux.form.XCheckbox({
+            name: 'ignore_sslcert',
+            fieldLabel: 'Ignore invalid SSL certificate'
+        });
+
+        var imagecachePanel = new Ext.form.FieldSet({
+            title: 'Image Caching',
+            width: 700,
+            autoHeight: true,
+            collapsible: true,
+            items: [imagecacheEnabled, imagecacheOkPeriod, imagecacheFailPeriod,
+                imagecacheIgnoreSSLCert]
+        });
+
+        var imagecache_form = new Ext.form.FormPanel({
+            border: false,
+            labelAlign: 'left',
+            labelWidth: 200,
+            waitMsgTarget: true,
+            reader: imagecache_reader,
+            layout: 'form',
+            defaultType: 'textfield',
+            autoHeight: true,
+            items: [imagecachePanel]
+        });
+    } else {
+        var imagecache_form = null;
+    }
+
+    /*
+     * Transcoding
+     */
+    var transcodingEnabled = new Ext.form.Checkbox({
+        name: 'transcoding_enabled',
+        fieldLabel: 'Enabled'
+    });
+
+    var transcodingPanel = new Ext.form.FieldSet({
+        title: 'Transcoding',
+        width: 700,
+        autoHeight: true,
+        collapsible: true,
+        items: [transcodingEnabled]
+    });
+    if (tvheadend.capabilities.indexOf('transcoding') === -1)
+        transcodingPanel.hide();
+
+
+    /* ****************************************************************
+     * Form
+     * ***************************************************************/
+
+    var saveButton = new Ext.Button({
+        text: "Save configuration",
+        tooltip: 'Save changes made to configuration below',
+        iconCls: 'save',
+        handler: saveChanges
+    });
+
+    var helpButton = new Ext.Button({
+        text: 'Help',
+        handler: function() {
+            new tvheadend.help('General Configuration', 'config_misc.html');
         }
-      });
-       }
+    });
+
+    var confpanel = new Ext.form.FormPanel({
+        labelAlign: 'left',
+        labelWidth: 200,
+        border: false,
+        waitMsgTarget: true,
+        reader: confreader,
+        layout: 'form',
+        defaultType: 'textfield',
+        autoHeight: true,
+        items: [language, dvbscanPath,
+            tvhtimePanel,
+            transcodingPanel]
+    });
+
+    var _items = [confpanel];
+    if (imagecache_form)
+        _items.push(imagecache_form);
+    var panel = new Ext.Panel({
+        title: 'General',
+        iconCls: 'wrench',
+        border: false,
+        bodyStyle: 'padding:15px',
+        layout: 'form',
+        items: _items,
+        tbar: [saveButton, '->', helpButton]
+    });
+
+    /* ****************************************************************
+     * Load/Save
+     * ***************************************************************/
+
+    confpanel.on('render', function() {
+        confpanel.getForm().load({
+            url: 'config',
+            params: {
+                op: 'loadSettings'
+            },
+            success: function(form, action) {
+                confpanel.enable();
+            }
+        });
+        if (imagecache_form)
+            imagecache_form.getForm().load({
+                url: 'api/imagecache/config/load',
+                success: function(form, action) {
+                    imagecache_form.enable();
+                },
+                failure: function(form, action) {
+                    alert("FAILED");
+                }
+            });
+    });
+
+    function saveChanges() {
+        confpanel.getForm().submit({
+            url: 'config',
+            params: {
+                op: 'saveSettings'
+            },
+            waitMsg: 'Saving Data...',
+            failure: function(form, action) {
+                Ext.Msg.alert('Save failed', action.result.errormsg);
+            }
+        });
+        if (imagecache_form)
+            imagecache_form.getForm().submit({
+                url: 'api/imagecache/config/save',
+                waitMsg: 'Saving data...',
+                failure: function(form, action) {
+                    Ext.Msg.alert('Imagecache save failed', action.result.errormsg);
+                }
+            });
+    }
 
-       return panel;
-}
+    return panel;
+};
index 7cbda3d41e756e68f73339f6dee69c8d44865e03..3fc3654940dc92cf24686c499fcf13acc01f9b2a 100644 (file)
@@ -1,49 +1,48 @@
 tvheadend.cteditor = function() {
-  var fm = Ext.form;
+    var fm = Ext.form;
 
+    var cm = new Ext.grid.ColumnModel({
+        defaultSortable: true,
+        columns: [{
+                xtype: 'checkcolumn',
+                header: "Enabled",
+                dataIndex: 'enabled',
+                width: 60
+            }, {
+                header: "Name",
+                dataIndex: 'name',
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            }, {
+                xtype: 'checkcolumn',
+                header: "Internal",
+                dataIndex: 'internal',
+                width: 100
+            }, {
+                header: "Icon (full URL)",
+                dataIndex: 'icon',
+                width: 400,
+                editor: new fm.TextField({})
+            }, {
+                xtype: 'checkcolumn',
+                header: "Icon has title",
+                dataIndex: 'titledIcon',
+                width: 100,
+                tooltip: 'Set this if the supplied icon has a title embedded. '
+                        + 'This will tell displaying application not to superimpose title '
+                        + 'on top of logo.'
+            }, {
+                header: "Comment",
+                dataIndex: 'comment',
+                width: 400,
+                editor: new fm.TextField({})
+            }]});
 
-  var cm = new Ext.grid.ColumnModel({
-    defaultSortable: true,
-    columns : [{
-      xtype: 'checkcolumn',
-      header : "Enabled",
-      dataIndex : 'enabled',
-      width : 60
-    } , {
-      header : "Name",
-      dataIndex : 'name',
-      editor : new fm.TextField({
-       allowBlank : false
-      })
-    }, {
-      xtype: 'checkcolumn',
-      header : "Internal",
-      dataIndex : 'internal',
-      width : 100
-    }, {
-      header : "Icon (full URL)",
-      dataIndex : 'icon',
-      width : 400,
-      editor : new fm.TextField({})
-    }, {
-      xtype: 'checkcolumn',
-      header : "Icon has title",
-      dataIndex : 'titledIcon',
-      width : 100,
-      tooltip : 'Set this if the supplied icon has a title embedded. '
-       + 'This will tell displaying application not to superimpose title '
-       + 'on top of logo.'
-    }, {
-      header : "Comment",
-      dataIndex : 'comment',
-      width : 400,
-      editor : new fm.TextField({})
-    } ]});
+    var ChannelTagRecord = Ext.data.Record.create([
+        'enabled', 'name', 'internal', 'icon', 'comment', 'titledIcon']);
 
-  var ChannelTagRecord = Ext.data.Record.create([
-    'enabled', 'name', 'internal', 'icon', 'comment', 'titledIcon' ]);
-
-  return new tvheadend.tableEditor('Channel Tags', 'channeltags', cm,
-                                  ChannelTagRecord, [],
-                                  null, 'config_tags.html', 'tags');
-}
+    return new tvheadend.tableEditor('Channel Tags', 'channeltags', cm,
+            ChannelTagRecord, [],
+            null, 'config_tags.html', 'tags');
+};
index 7ec176e7a2cacd5cd095bf79cf8f8d105dcf62fe..62b88749ab8f27ebd9545b3dbd5e0c173cabeae3 100644 (file)
 tvheadend.cwceditor = function() {
-       var fm = Ext.form;
+    var fm = Ext.form;
 
-       function setMetaAttr(meta, record) {
-               var enabled = record.get('enabled');
-               if (!enabled) return;
+    function setMetaAttr(meta, record) {
+        var enabled = record.get('enabled');
+        if (!enabled)
+            return;
 
-               var connected = record.get('connected');
-               if (connected == 1) {
-                       meta.attr = 'style="color:green;"';
-               }
-               else {
-                       meta.attr = 'style="color:red;"';
-               }
-       }
+        var connected = record.get('connected');
+        if (connected === 1) {
+            meta.attr = 'style="color:green;"';
+        }
+        else {
+            meta.attr = 'style="color:red;"';
+        }
+    }
 
-       var cm = new Ext.grid.ColumnModel({
-    defaultSortable: true,
-          columns : [ {
-            xtype: 'checkcolumn',
-           header : "Enabled",
-           dataIndex : 'enabled',
-           width : 60
-          }, {
-               header : "Hostname",
-               dataIndex : 'hostname',
-               width : 200,
-               renderer : function(value, metadata, record, row, col, store) {
-                       setMetaAttr(metadata, record);
-                       return value;
-               },
-               editor : new fm.TextField({
-                       allowBlank : false
-               })
-       }, {
-               header : "Port",
-               dataIndex : 'port',
-               renderer : function(value, metadata, record, row, col, store) {
-                       setMetaAttr(metadata, record);
-                       return value;
-               },
-               editor : new fm.TextField({
-                       allowBlank : false
-               })
-       }, {
-               header : "Username",
-               dataIndex : 'username',
-               renderer : function(value, metadata, record, row, col, store) {
-                       setMetaAttr(metadata, record);
-                       return value;
-               },
-               editor : new fm.TextField({
-                       allowBlank : false
-               })
-       }, {
-               header : "Password",
-               dataIndex : 'password',
-               renderer : function(value, metadata, record, row, col, store) {
-                       setMetaAttr(metadata, record);
-                       return '<span class="tvh-grid-unset">Hidden</span>';
-               },
-               editor : new fm.TextField({
-                       allowBlank : false
-               })
-       }, {
-               header : "DES Key",
-               dataIndex : 'deskey',
-               width : 300,
-               renderer : function(value, metadata, record, row, col, store) {
-                       setMetaAttr(metadata, record);
-                       return '<span class="tvh-grid-unset">Hidden</span>';
-               },
-               editor : new fm.TextField({
-                       allowBlank : false
-               })
-       }, {
-          xtype: 'checkcolumn',
-               header : "Update Card",
-               dataIndex : 'emm',
-               width : 100
-        }, {
-          xtype: 'checkcolumn',
-               header : "Update One",
-               dataIndex : 'emmex',
-               width : 100
-        }, {
-               header : "Comment",
-               dataIndex : 'comment',
-               width : 400,
-               renderer : function(value, metadata, record, row, col, store) {
-                       setMetaAttr(metadata, record);
-                       return value;
-               },
-               editor : new fm.TextField()
-       ]});
+    var cm = new Ext.grid.ColumnModel({
+        defaultSortable: true,
+        columns: [{
+                xtype: 'checkcolumn',
+                header: "Enabled",
+                dataIndex: 'enabled',
+                width: 60
+            }, {
+                header: "Hostname",
+                dataIndex: 'hostname',
+                width: 200,
+                renderer: function(value, metadata, record, row, col, store) {
+                    setMetaAttr(metadata, record);
+                    return value;
+                },
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            }, {
+                header: "Port",
+                dataIndex: 'port',
+                renderer: function(value, metadata, record, row, col, store) {
+                    setMetaAttr(metadata, record);
+                    return value;
+                },
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            }, {
+                header: "Username",
+                dataIndex: 'username',
+                renderer: function(value, metadata, record, row, col, store) {
+                    setMetaAttr(metadata, record);
+                    return value;
+                },
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            }, {
+                header: "Password",
+                dataIndex: 'password',
+                renderer: function(value, metadata, record, row, col, store) {
+                    setMetaAttr(metadata, record);
+                    return '<span class="tvh-grid-unset">Hidden</span>';
+                },
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            }, {
+                header: "DES Key",
+                dataIndex: 'deskey',
+                width: 300,
+                renderer: function(value, metadata, record, row, col, store) {
+                    setMetaAttr(metadata, record);
+                    return '<span class="tvh-grid-unset">Hidden</span>';
+                },
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            }, {
+                xtype: 'checkcolumn',
+                header: "Update Card",
+                dataIndex: 'emm',
+                width: 100
+            }, {
+                xtype: 'checkcolumn',
+                header: "Update One",
+                dataIndex: 'emmex',
+                width: 100
+            }, {
+                header: "Comment",
+                dataIndex: 'comment',
+                width: 400,
+                renderer: function(value, metadata, record, row, col, store) {
+                    setMetaAttr(metadata, record);
+                    return value;
+                },
+                editor: new fm.TextField()
+            }]});
 
-       var rec = Ext.data.Record.create([ 'enabled', 'connected', 'hostname',
-               'port', 'username', 'password', 'deskey', 'emm', 'emmex', 'comment' ]);
+    var rec = Ext.data.Record.create(['enabled', 'connected', 'hostname',
+        'port', 'username', 'password', 'deskey', 'emm', 'emmex', 'comment']);
 
-       var store = new Ext.data.JsonStore({
-               root : 'entries',
-               fields : rec,
-               url : "tablemgr",
-               autoLoad : true,
-               id : 'id',
-               baseParams : {
-                       table : 'cwc',
-                       op : "get"
-               },
-               sortInfo : {
-                        field : 'username',
-                        direction : 'ASC'
-                }
-       });
+    var store = new Ext.data.JsonStore({
+        root: 'entries',
+        fields: rec,
+        url: "tablemgr",
+        autoLoad: true,
+        id: 'id',
+        baseParams: {
+            table: 'cwc',
+            op: "get"
+        },
+        sortInfo: {
+            field: 'username',
+            direction: 'ASC'
+        }
+    });
 
-       var grid = new tvheadend.tableEditor('Code Word Client', 'cwc', cm, rec, [],
-                                             store, 'config_cwc.html', 'key');
+    var grid = new tvheadend.tableEditor('Code Word Client', 'cwc', cm, rec, [],
+            store, 'config_cwc.html', 'key');
 
-       tvheadend.comet.on('cwcStatus', function(msg) {
-               var rec = store.getById(msg.id);
-               if (rec) {
-                       rec.set('connected', msg.connected);
-                       grid.getView().refresh();
-               }
-       });
+    tvheadend.comet.on('cwcStatus', function(msg) {
+        var rec = store.getById(msg.id);
+        if (rec) {
+            rec.set('connected', msg.connected);
+            grid.getView().refresh();
+        }
+    });
 
-       return grid;
-}
+    return grid;
+};
index 9b9163bf996ef1c40aa8dc4e9c283570109049e8..eddf34b0398e43f41788474a4c01072692a12797 100644 (file)
@@ -1,42 +1,42 @@
 tvheadend.weekdays = new Ext.data.SimpleStore({
-       fields : [ 'identifier', 'name' ],
-       id : 0,
-       data : [ [ '1', 'Mon' ], [ '2', 'Tue' ], [ '3', 'Wed' ], [ '4', 'Thu' ],
-               [ '5', 'Fri' ], [ '6', 'Sat' ], [ '7', 'Sun' ] ]
+    fields: ['identifier', 'name'],
+    id: 0,
+    data: [['1', 'Mon'], ['2', 'Tue'], ['3', 'Wed'], ['4', 'Thu'],
+        ['5', 'Fri'], ['6', 'Sat'], ['7', 'Sun']]
 });
 
 //This should be loaded from tvheadend
 tvheadend.dvrprio = new Ext.data.SimpleStore({
-       fields : [ 'identifier', 'name' ],
-       id : 0,
-       data : [ [ 'important', 'Important' ], [ 'high', 'High' ],
-               [ 'normal', 'Normal' ], [ 'low', 'Low' ],
-               [ 'unimportant', 'Unimportant' ] ]
+    fields: ['identifier', 'name'],
+    id: 0,
+    data: [['important', 'Important'], ['high', 'High'],
+        ['normal', 'Normal'], ['low', 'Low'],
+        ['unimportant', 'Unimportant']]
 });
 
 
 //For the container configuration
 tvheadend.containers = new Ext.data.JsonStore({
-       autoLoad : true,
-       root : 'entries',
-       fields : [ 'name', 'description' ],
-       id : 'name',
-       url : 'dvr_containers',
-       baseParams : {
-               op : 'list'
-       }
+    autoLoad: true,
+    root: 'entries',
+    fields: ['name', 'description'],
+    id: 'name',
+    url: 'dvr_containers',
+    baseParams: {
+        op: 'list'
+    }
 });
 
 //For the cache configuration
 tvheadend.caches = new Ext.data.JsonStore({
-       autoLoad : true,
-       root : 'entries',
-       fields : [ 'index', 'description' ],
-       id : 'name',
-       url : 'dvr_caches',
-       baseParams : {
-               op : 'list'
-       }
+    autoLoad: true,
+    root: 'entries',
+    fields: ['index', 'description'],
+    id: 'name',
+    url: 'dvr_caches',
+    baseParams: {
+        op: 'list'
+    }
 });
 
 
@@ -44,20 +44,21 @@ tvheadend.caches = new Ext.data.JsonStore({
  * Configuration names
  */
 tvheadend.configNames = new Ext.data.JsonStore({
-       autoLoad : true,
-       root : 'entries',
-       fields : [ 'identifier', 'name' ],
-       id : 'identifier',
-       url : 'confignames',
-       baseParams : {
-               op : 'list'
-       }
+    autoLoad: true,
+    root: 'entries',
+    fields: ['identifier', 'name'],
+    id: 'identifier',
+    url: 'confignames',
+    baseParams: {
+        op: 'list'
+    }
 });
 
 tvheadend.configNames.setDefaultSort('name', 'ASC');
 
 tvheadend.comet.on('dvrconfig', function(m) {
-       if (m.reload != null) tvheadend.configNames.reload();
+    if (m.reload != null)
+        tvheadend.configNames.reload();
 });
 
 /**
@@ -65,368 +66,365 @@ tvheadend.comet.on('dvrconfig', function(m) {
  */
 tvheadend.dvrDetails = function(entry) {
 
-       var content = '';
-       var but;
-
-       if (entry.chicon != null && entry.chicon.length > 0) content += '<img class="x-epg-chicon" src="'
-               + entry.chicon + '">';
-
-       content += '<div class="x-epg-title">' + entry.title + '</div>';
-       content += '<div class="x-epg-desc">' + entry.description + '</div>';
-       content += '<hr>'
-       content += '<div class="x-epg-meta">Status: ' + entry.status + '</div>';
-
-       if (entry.url != null && entry.filesize > 0) {
-               content += '<div class="x-epg-meta">' + '<a href="' + entry.url
-                       + '" target="_blank">Download</a> '
-                       + parseInt(entry.filesize / 1000000) + ' MB</div>';
-
-       }
-
-       var win = new Ext.Window({
-               title : entry.title,
-               layout : 'fit',
-               width : 400,
-               height : 300,
-               constrainHeader : true,
-               buttonAlign : 'center',
-               html : content
-       });
-
-       switch (entry.schedstate) {
-               case 'scheduled':
-                       win.addButton({
-                               handler : cancelEvent,
-                               text : "Remove from schedule"
-                       });
-                       break;
-
-               case 'recording':
-               case 'recordingError':
-                       win.addButton({
-                               handler : cancelEvent,
-                               text : "Abort recording"
-                       });
-                       break;
-               case 'completedError':
-               case 'completed':
-                       win.addButton({
-                               handler : deleteEvent,
-                               text : "Delete recording"
-                       });
-                       break;
-       }
-
-       win.show();
-
-       function cancelEvent() {
-               Ext.Ajax.request({
-                       url : 'dvr',
-                       params : {
-                               entryId : entry.id,
-                               op : 'cancelEntry'
-                       },
-
-                       success : function(response, options) {
-                               win.close();
-                       },
-
-                       failure : function(response, options) {
-                               Ext.MessageBox.alert('DVR', response.statusText);
-                       }
-               });
-       }
-
-       function deleteEvent() {
-               Ext.Ajax.request({
-                       url : 'dvr',
-                       params : {
-                               entryId : entry.id,
-                               op : 'deleteEntry'
-                       },
-
-                       success : function(response, options) {
-                               win.close();
-                       },
-
-                       failure : function(response, options) {
-                               Ext.MessageBox.alert('DVR', response.statusText);
-                       }
-               });
-       }
-
-}
+    var content = '';
+    var but;
+
+    if (entry.chicon != null && entry.chicon.length > 0)
+        content += '<img class="x-epg-chicon" src="'
+                + entry.chicon + '">';
+
+    content += '<div class="x-epg-title">' + entry.title + '</div>';
+    content += '<div class="x-epg-desc">' + entry.description + '</div>';
+    content += '<hr>';
+    content += '<div class="x-epg-meta">Status: ' + entry.status + '</div>';
+
+    if (entry.url != null && entry.filesize > 0) {
+        content += '<div class="x-epg-meta">' + '<a href="' + entry.url
+                + '" target="_blank">Download</a> '
+                + parseInt(entry.filesize / 1000000) + ' MB</div>';
+
+    }
+
+    var win = new Ext.Window({
+        title: entry.title,
+        layout: 'fit',
+        width: 400,
+        height: 300,
+        constrainHeader: true,
+        buttonAlign: 'center',
+        html: content
+    });
+
+    switch (entry.schedstate) {
+        case 'scheduled':
+            win.addButton({
+                handler: cancelEvent,
+                text: "Remove from schedule"
+            });
+            break;
+
+        case 'recording':
+        case 'recordingError':
+            win.addButton({
+                handler: cancelEvent,
+                text: "Abort recording"
+            });
+            break;
+        case 'completedError':
+        case 'completed':
+            win.addButton({
+                handler: deleteEvent,
+                text: "Delete recording"
+            });
+            break;
+    }
+
+    win.show();
+
+    function cancelEvent() {
+        Ext.Ajax.request({
+            url: 'dvr',
+            params: {
+                entryId: entry.id,
+                op: 'cancelEntry'
+            },
+            success: function(response, options) {
+                win.close();
+            },
+            failure: function(response, options) {
+                Ext.MessageBox.alert('DVR', response.statusText);
+            }
+        });
+    }
+
+    function deleteEvent() {
+        Ext.Ajax.request({
+            url: 'dvr',
+            params: {
+                entryId: entry.id,
+                op: 'deleteEntry'
+            },
+            success: function(response, options) {
+                win.close();
+            },
+            failure: function(response, options) {
+                Ext.MessageBox.alert('DVR', response.statusText);
+            }
+        });
+    }
+
+};
 
 /**
  *
  */
 tvheadend.dvrschedule = function(title, iconCls, dvrStore) {
 
-       var actions = new Ext.ux.grid.RowActions({
-               header : '',
-               dataIndex : 'actions',
-               width : 45,
-               actions : [ {
-                       iconIndex : 'schedstate'
-               } ]
-       });
-
-       function renderDate(value) {
-               var dt = new Date(value);
-               return dt.format('D j M H:i');
-       }
-
-       function renderDuration(value) {
-               value = value / 60; /* Nevermind the seconds */
-
-               if (value >= 60) {
-                       var min = parseInt(value % 60);
-                       var hours = parseInt(value / 60);
-
-                       if (min == 0) {
-                               return hours + ' hrs';
-                       }
-                       return hours + ' hrs, ' + min + ' min';
-               }
-               else {
-                       return parseInt(value) + ' min';
-               }
-       }
-
-       function renderSize(value)
-       {
-               if (value == null)
-                       return '';
-               return parseInt(value / 1000000) + ' MB';
-       }
-
-       function renderPri(value) {
-               return tvheadend.dvrprio.getById(value).data.name;
-       }
-
-       var dvrCm = new Ext.grid.ColumnModel([ actions, {
-               width : 250,
-               id : 'title',
-               header : "Title",
-               dataIndex : 'title'
-       }, {
-               width : 100,
-               id : 'episode',
-               header : "Episode",
-               dataIndex : 'episode'
-       }, {
-               width : 100,
-               id : 'pri',
-               header : "Priority",
-               dataIndex : 'pri',
-               renderer : renderPri,
-               hidden : iconCls != 'clock',
-       }, {
-               width : 100,
-               id : 'start',
-               header : iconCls == 'clock' ? "Start" : "Date/Time",
-               dataIndex : 'start',
-               renderer : renderDate
-       }, {
-               width : 100,
-               hidden : true,
-               id : 'end',
-               header : "End",
-               dataIndex : 'end',
-               renderer : renderDate
-       }, {
-               width : 100,
-               id : 'duration',
-               header : "Duration",
-               dataIndex : 'duration',
-               renderer : renderDuration
-       }, {
-               width : 100,
-               id : 'filesize',
-               header : "Filesize",
-               dataIndex : 'filesize',
-               renderer : renderSize,
-               hidden : iconCls != 'television'
-       }, {
-               width : 250,
-               id : 'channel',
-               header : "Channel",
-               dataIndex : 'channel'
-       }, {
-               width : 200,
-               id : 'creator',
-               header : "Created by",
-               hidden : true,
-               dataIndex : 'creator'
-       }, {
-               width : 200,
-               id : 'config_name',
-               header : "DVR Configuration",
-               renderer : function(value, metadata, record, row, col, store) {
-                       if (!value) {
-                               return '<span class="tvh-grid-unset">(default)</span>';
-                       }
-                       else {
-                               return value;
-                       }
-               },
-               dataIndex : 'config_name',
-               hidden: iconCls != 'clock'
-       }, {
-               width : 200,
-               id : 'status',
-               header : "Status",
-               dataIndex : 'status',
-               hidden: iconCls != 'exclamation'
-       ]);
-
-       function addEntry() {
-
-               function createRecording() {
-                       panel.getForm().submit({
-                               params : {
-                                       'op' : 'createEntry'
-                               },
-                               url : 'dvr/addentry',
-                               waitMsg : 'Creating entry...',
-                               failure : function(response, options) {
-                                       Ext.MessageBox.alert('Server Error', 'Unable to create entry');
-                               },
-                               success : function() {
-                                       win.close();
-                               }
-                       });
-               }
-
-               var panel = new Ext.FormPanel({
-                       frame : true,
-                       border : true,
-                       bodyStyle : 'padding:5px',
-                       labelAlign : 'right',
-                       labelWidth : 110,
-                       defaultType : 'textfield',
-                       items : [ new Ext.form.ComboBox({
-                               fieldLabel : 'Channel',
-                               name : 'channel',
-                               hiddenName : 'channelid',
-                               editable : false,
-                               allowBlank : false,
-                               displayField : 'val',
-                               valueField : 'key',
-                               mode : 'remote',
-                               triggerAction : 'all',
-                               store : tvheadend.channels
-                       }), new Ext.form.DateField({
-                               allowBlank : false,
-                               fieldLabel : 'Date',
-                               name : 'date'
-                       }), new Ext.form.TimeField({
-                               allowBlank : false,
-                               fieldLabel : 'Start time',
-                               name : 'starttime',
-                               increment : 10,
-                               format : 'H:i'
-                       }), new Ext.form.TimeField({
-                               allowBlank : false,
-                               fieldLabel : 'Stop time',
-                               name : 'stoptime',
-                               increment : 10,
-                               format : 'H:i'
-                       }), new Ext.form.ComboBox({
-                               store : tvheadend.dvrprio,
-                               value : "normal",
-                               triggerAction : 'all',
-                               mode : 'local',
-                               fieldLabel : 'Priority',
-                               valueField : 'identifier',
-                               displayField : 'name',
-                               name : 'pri'
-                       }), {
-                               allowBlank : false,
-                               fieldLabel : 'Title',
-                               name : 'title'
-                       }, new Ext.form.ComboBox({
-                               store : tvheadend.configNames,
-                               triggerAction : 'all',
-                               mode : 'local',
-                               fieldLabel : 'DVR Configuration',
-                               valueField : 'identifier',
-                               displayField : 'name',
-                               name : 'config_name',
-                               emptyText : '(default)',
-                               value : '',
-                               editable : false
-                       }) ],
-                       buttons : [ {
-                               text : 'Create',
-                               handler : createRecording
-                       } ]
-
-               });
-
-               win = new Ext.Window({
-                       title : 'Add single recording',
-                       layout : 'fit',
-                       width : 500,
-                       height : 300,
-                       plain : true,
-                       items : panel
-               });
-               win.show();
-               new Ext.form.ComboBox({
-                       store : tvheadend.configNames,
-                       triggerAction : 'all',
-                       mode : 'local',
-                       fieldLabel : 'DVR Configuration',
-                       valueField : 'identifier',
-                       displayField : 'name',
-                       name : 'config_name',
-                       emptyText : '(default)',
-                       value : '',
-                       editable : false
-               })
-       }
-       ;
-
-       var panel = new Ext.grid.GridPanel({
-               loadMask : true,
-               stripeRows : true,
-               disableSelection : true,
-               title : title,
-               iconCls : iconCls,
-               store : dvrStore,
-               cm : dvrCm,
-               plugins : [ actions ],
-               viewConfig : {
-                       forceFit : true
-               },
-               tbar : [ {
-                       tooltip : 'Schedule a new recording session on the server.',
-                       iconCls : 'add',
-                       text : 'Add entry',
-                       handler : addEntry
-               }, '->', {
-                       text : 'Help',
-                       handler : function() {
-                               new tvheadend.help('Digital Video Recorder', 'dvrlog.html');
-                       }
-               } ],
-               bbar : new Ext.PagingToolbar({
-                       store : dvrStore,
-                       pageSize : 20,
-                       displayInfo : true,
-                       displayMsg : 'Programs {0} - {1} of {2}',
-                       emptyMsg : "No programs to display"
-               })
-
-       });
-
-       panel.on('rowclick', rowclicked);
-       function rowclicked(grid, index) {
-               new tvheadend.dvrDetails(grid.getStore().getAt(index).data);
-       }
-       return panel;
-}
+    var actions = new Ext.ux.grid.RowActions({
+        header: '',
+        dataIndex: 'actions',
+        width: 45,
+        actions: [{
+                iconIndex: 'schedstate'
+            }]
+    });
+
+    function renderDate(value) {
+        var dt = new Date(value);
+        return dt.format('D j M H:i');
+    }
+
+    function renderDuration(value) {
+        value = value / 60; /* Nevermind the seconds */
+
+        if (value >= 60) {
+            var min = parseInt(value % 60);
+            var hours = parseInt(value / 60);
+
+            if (min === 0) {
+                return hours + ' hrs';
+            }
+            return hours + ' hrs, ' + min + ' min';
+        }
+        else {
+            return parseInt(value) + ' min';
+        }
+    }
+
+    function renderSize(value)
+    {
+        if (value == null)
+            return '';
+        return parseInt(value / 1000000) + ' MB';
+    }
+
+    function renderPri(value) {
+        return tvheadend.dvrprio.getById(value).data.name;
+    }
+
+    var dvrCm = new Ext.grid.ColumnModel([actions, {
+            width: 250,
+            id: 'title',
+            header: "Title",
+            dataIndex: 'title'
+        }, {
+            width: 100,
+            id: 'episode',
+            header: "Episode",
+            dataIndex: 'episode'
+        }, {
+            width: 100,
+            id: 'pri',
+            header: "Priority",
+            dataIndex: 'pri',
+            renderer: renderPri,
+            hidden: iconCls !== 'clock'
+        }, {
+            width: 100,
+            id: 'start',
+            header: iconCls === 'clock' ? "Start" : "Date/Time",
+            dataIndex: 'start',
+            renderer: renderDate
+        }, {
+            width: 100,
+            hidden: true,
+            id: 'end',
+            header: "End",
+            dataIndex: 'end',
+            renderer: renderDate
+        }, {
+            width: 100,
+            id: 'duration',
+            header: "Duration",
+            dataIndex: 'duration',
+            renderer: renderDuration
+        }, {
+            width: 100,
+            id: 'filesize',
+            header: "Filesize",
+            dataIndex: 'filesize',
+            renderer: renderSize,
+            hidden: iconCls !== 'television'
+        }, {
+            width: 250,
+            id: 'channel',
+            header: "Channel",
+            dataIndex: 'channel'
+        }, {
+            width: 200,
+            id: 'creator',
+            header: "Created by",
+            hidden: true,
+            dataIndex: 'creator'
+        }, {
+            width: 200,
+            id: 'config_name',
+            header: "DVR Configuration",
+            renderer: function(value, metadata, record, row, col, store) {
+                if (!value) {
+                    return '<span class="tvh-grid-unset">(default)</span>';
+                }
+                else {
+                    return value;
+                }
+            },
+            dataIndex: 'config_name',
+            hidden: iconCls !== 'clock'
+        }, {
+            width: 200,
+            id: 'status',
+            header: "Status",
+            dataIndex: 'status',
+            hidden: iconCls !== 'exclamation'
+        }]);
+
+    function addEntry() {
+
+        function createRecording() {
+            panel.getForm().submit({
+                params: {
+                    'op': 'createEntry'
+                },
+                url: 'dvr/addentry',
+                waitMsg: 'Creating entry...',
+                failure: function(response, options) {
+                    Ext.MessageBox.alert('Server Error', 'Unable to create entry');
+                },
+                success: function() {
+                    win.close();
+                }
+            });
+        }
+
+        var panel = new Ext.FormPanel({
+            frame: true,
+            border: true,
+            bodyStyle: 'padding:5px',
+            labelAlign: 'right',
+            labelWidth: 110,
+            defaultType: 'textfield',
+            items: [new Ext.form.ComboBox({
+                    fieldLabel: 'Channel',
+                    name: 'channel',
+                    hiddenName: 'channelid',
+                    editable: false,
+                    allowBlank: false,
+                    displayField: 'val',
+                    valueField: 'key',
+                    mode: 'remote',
+                    triggerAction: 'all',
+                    store: tvheadend.channels
+                }), new Ext.form.DateField({
+                    allowBlank: false,
+                    fieldLabel: 'Date',
+                    name: 'date'
+                }), new Ext.form.TimeField({
+                    allowBlank: false,
+                    fieldLabel: 'Start time',
+                    name: 'starttime',
+                    increment: 10,
+                    format: 'H:i'
+                }), new Ext.form.TimeField({
+                    allowBlank: false,
+                    fieldLabel: 'Stop time',
+                    name: 'stoptime',
+                    increment: 10,
+                    format: 'H:i'
+                }), new Ext.form.ComboBox({
+                    store: tvheadend.dvrprio,
+                    value: "normal",
+                    triggerAction: 'all',
+                    mode: 'local',
+                    fieldLabel: 'Priority',
+                    valueField: 'identifier',
+                    displayField: 'name',
+                    name: 'pri'
+                }), {
+                    allowBlank: false,
+                    fieldLabel: 'Title',
+                    name: 'title'
+                }, new Ext.form.ComboBox({
+                    store: tvheadend.configNames,
+                    triggerAction: 'all',
+                    mode: 'local',
+                    fieldLabel: 'DVR Configuration',
+                    valueField: 'identifier',
+                    displayField: 'name',
+                    name: 'config_name',
+                    emptyText: '(default)',
+                    value: '',
+                    editable: false
+                })],
+            buttons: [{
+                    text: 'Create',
+                    handler: createRecording
+                }]
+
+        });
+
+        win = new Ext.Window({
+            title: 'Add single recording',
+            layout: 'fit',
+            width: 500,
+            height: 300,
+            plain: true,
+            items: panel
+        });
+        win.show();
+        new Ext.form.ComboBox({
+            store: tvheadend.configNames,
+            triggerAction: 'all',
+            mode: 'local',
+            fieldLabel: 'DVR Configuration',
+            valueField: 'identifier',
+            displayField: 'name',
+            name: 'config_name',
+            emptyText: '(default)',
+            value: '',
+            editable: false
+        });
+    }
+    ;
+
+    var panel = new Ext.grid.GridPanel({
+        loadMask: true,
+        stripeRows: true,
+        disableSelection: true,
+        title: title,
+        iconCls: iconCls,
+        store: dvrStore,
+        cm: dvrCm,
+        plugins: [actions],
+        viewConfig: {
+            forceFit: true
+        },
+        tbar: [{
+                tooltip: 'Schedule a new recording session on the server.',
+                iconCls: 'add',
+                text: 'Add entry',
+                handler: addEntry
+            }, '->', {
+                text: 'Help',
+                handler: function() {
+                    new tvheadend.help('Digital Video Recorder', 'dvrlog.html');
+                }
+            }],
+        bbar: new Ext.PagingToolbar({
+            store: dvrStore,
+            pageSize: 20,
+            displayInfo: true,
+            displayMsg: 'Programs {0} - {1} of {2}',
+            emptyMsg: "No programs to display"
+        })
+
+    });
+
+    panel.on('rowclick', rowclicked);
+    function rowclicked(grid, index) {
+        new tvheadend.dvrDetails(grid.getStore().getAt(index).data);
+    }
+    return panel;
+};
 
 /**
  *
@@ -436,644 +434,651 @@ tvheadend.dvrschedule = function(title, iconCls, dvrStore) {
  *
  */
 tvheadend.autoreceditor = function() {
-       var fm = Ext.form;
-
-
-       var cm = new Ext.grid.ColumnModel({
-  defaultSortable: true,
-  columns :
-         [
-            {
-              header: 'Enabled',
-              dataIndex: 'enabled',
-              width: 30,
-              xtype: 'checkcolumn'
-            },
-
-                       {
-                               header : "Title (Regexp)",
-                               dataIndex : 'title',
-                               editor : new fm.TextField({
-                                       allowBlank : true
-                               })
-                       },
-                       {
-                               header : "Channel",
-                               dataIndex : 'channel',
-                               editor : new Ext.form.ComboBox({
-                                       loadingText : 'Loading...',
-                                       displayField : 'val',
-                                        valueField: 'key',
-                                       store : tvheadend.channels,
-                                       mode : 'local',
-                                       editable : false,
-                                       triggerAction : 'all',
-                                       emptyText : 'Only include channel...'
-                               }),
-                                renderer : function (v, m, r) {
-                                  var i = tvheadend.channels.find('key', v);
-                                  if (i != -1)
-                                    v = tvheadend.channels.getAt(i).get('val')
-                                  return v
-                                }
-                       },
-      {
-        header    : "SeriesLink",
-        dataIndex : 'serieslink',
-        renderer  : function(v) {
-          return v ? 'yes' : 'no';
-        }
-                       },
-                       {
-                               header : "Channel tag",
-                               dataIndex : 'tag',
-                               editor : new Ext.form.ComboBox({
-                                       displayField : 'name',
-                                       store : tvheadend.channelTags,
-                                       mode : 'local',
-                                       editable : false,
-                                       triggerAction : 'all',
-                                       emptyText : 'Only include tag...'
-                               })
-                       },
-                       {
-                               header : "Genre",
-                               dataIndex : 'contenttype',
-                               renderer : function(v) {
-                                       return tvheadend.contentGroupLookupName(v);
-                               },
-                               editor : new Ext.form.ComboBox({
-                                       valueField : 'code',
-                                       displayField : 'name',
-                                       store : tvheadend.ContentGroupStore,
-                                       mode : 'local',
-                                       editable : false,
-                                       triggerAction : 'all',
-                                       emptyText : 'Only include content...'
-                               })
-                       },
-                       {
-                               header : "Weekdays",
-                               dataIndex : 'weekdays',
-                               renderer : function(value, metadata, record, row, col, store) {
-                                        if (value.split) value = value.split(',')
-                                        if (value.length == 7) return 'All days';
-                                        if (value.length == 0 || value[0] == "") return 'No days';
-                                       ret = [];
-                                       tags = value;
-                                       for ( var i = 0; i < tags.length; i++) {
-                                               var tag = tvheadend.weekdays.getById(tags[i]);
-                                               if (typeof tag !== 'undefined') ret.push(tag.data.name);
-                                       }
-                                       return ret.join(', ');
-                               },
-                               editor : new Ext.ux.form.LovCombo({
-                                       store : tvheadend.weekdays,
-                                       mode : 'local',
-                                       valueField : 'identifier',
-                                       displayField : 'name'
-                               })
-                       }, {
-                               header : "Starting Around",
-                               dataIndex : 'approx_time',
-                               renderer : function(value, metadata, record, row, col, store) {
-                                       if (typeof value === 'string') return value;
-
-                                       if (value === 0) return '';
-
-                                       var hours = Math.floor(value / 60);
-                                       var mins = value % 60;
-                                       var dt = new Date();
-                                       dt.setHours(hours);
-                                       dt.setMinutes(mins);
-                                       return dt.format('H:i');
-                               },
-                               editor : new Ext.form.TimeField({
-                                       allowBlank : true,
-                                       increment : 10,
-                                       format : 'H:i'
-                               })
-                       }, {
-                               header : "Priority",
-                               dataIndex : 'pri',
-                               width : 100,
-                               renderer : function(value, metadata, record, row, col, store) {
-                                       return tvheadend.dvrprio.getById(value).data.name;
-                               },
-                               editor : new fm.ComboBox({
-                                       store : tvheadend.dvrprio,
-                                       triggerAction : 'all',
-                                       mode : 'local',
-                                       valueField : 'identifier',
-                                       displayField : 'name'
-                               })
-                       }, {
-                               header : "DVR Configuration",
-                               dataIndex : 'config_name',
-                               renderer : function(value, metadata, record, row, col, store) {
-                                       if (!value) {
-                                               return '<span class="tvh-grid-unset">(default)</span>';
-                                       }
-                                       else {
-                                               return value;
-                                       }
-                               },
-                               editor : new Ext.form.ComboBox({
-                                       store : tvheadend.configNames,
-                                       triggerAction : 'all',
-                                       mode : 'local',
-                                       valueField : 'identifier',
-                                       displayField : 'name',
-                                       name : 'config_name',
-                                       emptyText : '(default)',
-                                       editable : false
-                               })
-                       }, {
-                               header : "Created by",
-                               dataIndex : 'creator',
-                               editor : new fm.TextField({
-                                       allowBlank : false
-                               })
-                       }, {
-                               header : "Comment",
-                               dataIndex : 'comment',
-                               editor : new fm.TextField({
-                                       allowBlank : false
-                               })
-                       } ]});
-
-       return new tvheadend.tableEditor('Automatic Recorder', 'autorec', cm,
-               tvheadend.autorecRecord, [], tvheadend.autorecStore,
-               'autorec.html', 'wand');
-}
+    var fm = Ext.form;
+
+
+    var cm = new Ext.grid.ColumnModel({
+        defaultSortable: true,
+        columns:
+                [
+                    {
+                        header: 'Enabled',
+                        dataIndex: 'enabled',
+                        width: 30,
+                        xtype: 'checkcolumn'
+                    },
+                    {
+                        header: "Title (Regexp)",
+                        dataIndex: 'title',
+                        editor: new fm.TextField({
+                            allowBlank: true
+                        })
+                    },
+                    {
+                        header: "Channel",
+                        dataIndex: 'channel',
+                        editor: new Ext.form.ComboBox({
+                            loadingText: 'Loading...',
+                            displayField: 'val',
+                            valueField: 'key',
+                            store: tvheadend.channels,
+                            mode: 'local',
+                            editable: false,
+                            triggerAction: 'all',
+                            emptyText: 'Only include channel...'
+                        }),
+                        renderer: function(v, m, r) {
+                            var i = tvheadend.channels.find('key', v);
+                            if (i !== -1)
+                                v = tvheadend.channels.getAt(i).get('val');
+                            return v;
+                        }
+                    },
+                    {
+                        header: "SeriesLink",
+                        dataIndex: 'serieslink',
+                        renderer: function(v) {
+                            return v ? 'yes' : 'no';
+                        }
+                    },
+                    {
+                        header: "Channel tag",
+                        dataIndex: 'tag',
+                        editor: new Ext.form.ComboBox({
+                            displayField: 'name',
+                            store: tvheadend.channelTags,
+                            mode: 'local',
+                            editable: false,
+                            triggerAction: 'all',
+                            emptyText: 'Only include tag...'
+                        })
+                    },
+                    {
+                        header: "Genre",
+                        dataIndex: 'contenttype',
+                        renderer: function(v) {
+                            return tvheadend.contentGroupLookupName(v);
+                        },
+                        editor: new Ext.form.ComboBox({
+                            valueField: 'code',
+                            displayField: 'name',
+                            store: tvheadend.ContentGroupStore,
+                            mode: 'local',
+                            editable: false,
+                            triggerAction: 'all',
+                            emptyText: 'Only include content...'
+                        })
+                    },
+                    {
+                        header: "Weekdays",
+                        dataIndex: 'weekdays',
+                        renderer: function(value, metadata, record, row, col, store) {
+                            if (value.split)
+                                value = value.split(',');
+                            if (value.length === 7)
+                                return 'All days';
+                            if (value.length === 0 || value[0] === "")
+                                return 'No days';
+                            ret = [];
+                            tags = value;
+                            for (var i = 0; i < tags.length; i++) {
+                                var tag = tvheadend.weekdays.getById(tags[i]);
+                                if (typeof tag !== 'undefined')
+                                    ret.push(tag.data.name);
+                            }
+                            return ret.join(', ');
+                        },
+                        editor: new Ext.ux.form.LovCombo({
+                            store: tvheadend.weekdays,
+                            mode: 'local',
+                            valueField: 'identifier',
+                            displayField: 'name'
+                        })
+                    }, {
+                        header: "Starting Around",
+                        dataIndex: 'approx_time',
+                        renderer: function(value, metadata, record, row, col, store) {
+                            if (typeof value === 'string')
+                                return value;
+
+                            if (value === 0)
+                                return '';
+
+                            var hours = Math.floor(value / 60);
+                            var mins = value % 60;
+                            var dt = new Date();
+                            dt.setHours(hours);
+                            dt.setMinutes(mins);
+                            return dt.format('H:i');
+                        },
+                        editor: new Ext.form.TimeField({
+                            allowBlank: true,
+                            increment: 10,
+                            format: 'H:i'
+                        })
+                    }, {
+                        header: "Priority",
+                        dataIndex: 'pri',
+                        width: 100,
+                        renderer: function(value, metadata, record, row, col, store) {
+                            return tvheadend.dvrprio.getById(value).data.name;
+                        },
+                        editor: new fm.ComboBox({
+                            store: tvheadend.dvrprio,
+                            triggerAction: 'all',
+                            mode: 'local',
+                            valueField: 'identifier',
+                            displayField: 'name'
+                        })
+                    }, {
+                        header: "DVR Configuration",
+                        dataIndex: 'config_name',
+                        renderer: function(value, metadata, record, row, col, store) {
+                            if (!value) {
+                                return '<span class="tvh-grid-unset">(default)</span>';
+                            }
+                            else {
+                                return value;
+                            }
+                        },
+                        editor: new Ext.form.ComboBox({
+                            store: tvheadend.configNames,
+                            triggerAction: 'all',
+                            mode: 'local',
+                            valueField: 'identifier',
+                            displayField: 'name',
+                            name: 'config_name',
+                            emptyText: '(default)',
+                            editable: false
+                        })
+                    }, {
+                        header: "Created by",
+                        dataIndex: 'creator',
+                        editor: new fm.TextField({
+                            allowBlank: false
+                        })
+                    }, {
+                        header: "Comment",
+                        dataIndex: 'comment',
+                        editor: new fm.TextField({
+                            allowBlank: false
+                        })
+                    }]});
+
+    return new tvheadend.tableEditor('Automatic Recorder', 'autorec', cm,
+            tvheadend.autorecRecord, [], tvheadend.autorecStore,
+            'autorec.html', 'wand');
+};
 /**
  *
  */
 tvheadend.dvr = function() {
 
-       function datastoreBuilder(url) {
-           return new Ext.data.JsonStore({
-               root : 'entries',
-               totalProperty : 'totalCount',
-               fields : [ {
-                       name : 'id'
-               }, {
-                       name : 'channel'
-               }, {
-                       name : 'title'
-               }, {
-                       name : 'episode'
-               }, {
-                       name : 'pri'
-               }, {
-                       name : 'description'
-               }, {
-                       name : 'chicon'
-               }, {
-                       name : 'start',
-                       type : 'date',
-                       dateFormat : 'U' /* unix time */
-               }, {
-                       name : 'end',
-                       type : 'date',
-                       dateFormat : 'U' /* unix time */
-               }, {
-                       name : 'config_name'
-               }, {
-                       name : 'status'
-               }, {
-                       name : 'schedstate'
-               }, {
-                       name : 'error'
-               }, {
-                       name : 'creator'
-               }, {
-                       name : 'duration'
-               }, {
-                       name : 'filesize'
-               }, {
-                       name : 'url'
-               } ],
-               url : url,
-               autoLoad : true,
-               id : 'id',
-               remoteSort : true
-           });
-       }
-       tvheadend.dvrStoreUpcoming = datastoreBuilder('dvrlist_upcoming');
-       tvheadend.dvrStoreFinished = datastoreBuilder('dvrlist_finished');
-       tvheadend.dvrStoreFailed = datastoreBuilder('dvrlist_failed');
-        tvheadend.dvrStores = [tvheadend.dvrStoreUpcoming,
-                              tvheadend.dvrStoreFinished,
-                              tvheadend.dvrStoreFailed];
-
-
-       function updateDvrStore(store, r, m) {
-               r.data.status = m.status;
-               r.data.schedstate = m.schedstate;
-
-               store.afterEdit(r);
-               store.fireEvent('updated', store, r,
-                       Ext.data.Record.COMMIT);
-       }
-
-       function reloadStores() {
-               for (var i = 0; i < tvheadend.dvrStores.length; i++) {
-                       tvheadend.dvrStores[i].reload();
-               }
-       }
-
-       tvheadend.comet.on('dvrdb', function(m) {
-
-               if (m.reload != null) {
-                      reloadStores();
-               }
-
-               if (m.updateEntry != null) {
-                       for (var i = 0; i < tvheadend.dvrStores.length; i++) {
-                               var store = tvheadend.dvrStores[i];
-                               r = tvheadend.dvrStoreUpcoming.getById(m.id);
-                               if (typeof r !== 'undefined') {
-                                       updateDvrStore(store, r, m);
-                                       return;
-                               }
-                       }
-                       reloadStores();
-               }
-       });
-
-       tvheadend.autorecRecord = Ext.data.Record.create([ 'enabled', 'title',
-               'serieslink', 'channel', 'tag', 'creator', 'contenttype', 'comment',
-               'weekdays', 'pri', 'approx_time', 'config_name' ]);
-
-       tvheadend.autorecStore = new Ext.data.JsonStore({
-               root : 'entries',
-               fields : tvheadend.autorecRecord,
-               url : "tablemgr",
-               autoLoad : true,
-               id : 'id',
-               baseParams : {
-                       table : "autorec",
-                       op : "get"
-               }
-       });
-
-       tvheadend.comet.on('autorec', function(m) {
-               if (m.reload != null) tvheadend.autorecStore.reload();
-       });
-
-       var panel = new Ext.TabPanel({
-               activeTab : 0,
-               autoScroll : true,
-               title : 'Digital Video Recorder',
-               iconCls : 'drive',
-               items : [ 
-                         new tvheadend.dvrschedule('Upcoming recordings', 'clock', tvheadend.dvrStoreUpcoming),
-                         new tvheadend.dvrschedule('Finished recordings', 'television', tvheadend.dvrStoreFinished),
-                         new tvheadend.dvrschedule('Failed recordings', 'exclamation', tvheadend.dvrStoreFailed),
-                         new tvheadend.autoreceditor
-                       ]
-       });
-       return panel;
-}
+    function datastoreBuilder(url) {
+        return new Ext.data.JsonStore({
+            root: 'entries',
+            totalProperty: 'totalCount',
+            fields: [{
+                    name: 'id'
+                }, {
+                    name: 'channel'
+                }, {
+                    name: 'title'
+                }, {
+                    name: 'episode'
+                }, {
+                    name: 'pri'
+                }, {
+                    name: 'description'
+                }, {
+                    name: 'chicon'
+                }, {
+                    name: 'start',
+                    type: 'date',
+                    dateFormat: 'U' /* unix time */
+                }, {
+                    name: 'end',
+                    type: 'date',
+                    dateFormat: 'U' /* unix time */
+                }, {
+                    name: 'config_name'
+                }, {
+                    name: 'status'
+                }, {
+                    name: 'schedstate'
+                }, {
+                    name: 'error'
+                }, {
+                    name: 'creator'
+                }, {
+                    name: 'duration'
+                }, {
+                    name: 'filesize'
+                }, {
+                    name: 'url'
+                }],
+            url: url,
+            autoLoad: true,
+            id: 'id',
+            remoteSort: true
+        });
+    }
+    tvheadend.dvrStoreUpcoming = datastoreBuilder('dvrlist_upcoming');
+    tvheadend.dvrStoreFinished = datastoreBuilder('dvrlist_finished');
+    tvheadend.dvrStoreFailed = datastoreBuilder('dvrlist_failed');
+    tvheadend.dvrStores = [tvheadend.dvrStoreUpcoming,
+        tvheadend.dvrStoreFinished,
+        tvheadend.dvrStoreFailed];
+
+
+    function updateDvrStore(store, r, m) {
+        r.data.status = m.status;
+        r.data.schedstate = m.schedstate;
+
+        store.afterEdit(r);
+        store.fireEvent('updated', store, r,
+                Ext.data.Record.COMMIT);
+    }
+
+    function reloadStores() {
+        for (var i = 0; i < tvheadend.dvrStores.length; i++) {
+            tvheadend.dvrStores[i].reload();
+        }
+    }
+
+    tvheadend.comet.on('dvrdb', function(m) {
+
+        if (m.reload != null) {
+            reloadStores();
+        }
+
+        if (m.updateEntry != null) {
+            for (var i = 0; i < tvheadend.dvrStores.length; i++) {
+                var store = tvheadend.dvrStores[i];
+                r = tvheadend.dvrStoreUpcoming.getById(m.id);
+                if (typeof r !== 'undefined') {
+                    updateDvrStore(store, r, m);
+                    return;
+                }
+            }
+            reloadStores();
+        }
+    });
+
+    tvheadend.autorecRecord = Ext.data.Record.create(['enabled', 'title',
+        'serieslink', 'channel', 'tag', 'creator', 'contenttype', 'comment',
+        'weekdays', 'pri', 'approx_time', 'config_name']);
+
+    tvheadend.autorecStore = new Ext.data.JsonStore({
+        root: 'entries',
+        fields: tvheadend.autorecRecord,
+        url: "tablemgr",
+        autoLoad: true,
+        id: 'id',
+        baseParams: {
+            table: "autorec",
+            op: "get"
+        }
+    });
+
+    tvheadend.comet.on('autorec', function(m) {
+        if (m.reload != null)
+            tvheadend.autorecStore.reload();
+    });
+
+    var panel = new Ext.TabPanel({
+        activeTab: 0,
+        autoScroll: true,
+        title: 'Digital Video Recorder',
+        iconCls: 'drive',
+        items: [
+            new tvheadend.dvrschedule('Upcoming recordings', 'clock', tvheadend.dvrStoreUpcoming),
+            new tvheadend.dvrschedule('Finished recordings', 'television', tvheadend.dvrStoreFinished),
+            new tvheadend.dvrschedule('Failed recordings', 'exclamation', tvheadend.dvrStoreFailed),
+            new tvheadend.autoreceditor
+        ]
+    });
+    return panel;
+};
 
 /**
  * Configuration panel (located under configuration)
  */
 tvheadend.dvrsettings = function() {
 
-       var confreader = new Ext.data.JsonReader({
-               root : 'dvrSettings'
-       }, [ 'storage', 'filePermissions', 'dirPermissions', 'postproc', 'retention', 'dayDirs', 'channelDirs',
-               'channelInTitle', 'container', 'cache', 'dateInTitle', 'timeInTitle',
-               'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 'titleDirs',
-               'episodeInTitle', 'cleanTitle', 'tagFiles', 'commSkip', 'subtitleInTitle', 
-               'episodeBeforeDate', 'rewritePAT', 'rewritePMT' ]);
-
-       var confcombo = new Ext.form.ComboBox({
-               store : tvheadend.configNames,
-               triggerAction : 'all',
-               mode : 'local',
-               displayField : 'name',
-               name : 'config_name',
-               emptyText : '(default)',
-               value : '',
-               editable : true
-       });
-
-       var delButton = new Ext.Toolbar.Button({
-               tooltip : 'Delete named configuration',
-               iconCls : 'remove',
-               text : "Delete configuration",
-               handler : deleteConfiguration,
-               disabled : true
-       });
-
-/* Config panel variables */
-
-/* DVR Behaviour */
-
-       var recordingContainer = new Ext.form.ComboBox({
-               store : tvheadend.containers,
-               fieldLabel : 'Media container',
-               triggerAction : 'all',
-               displayField : 'description',
-               valueField : 'name',
-               editable : false,
-               width : 200,
-               hiddenName : 'container' 
-       });
-       
-       var cacheScheme = new Ext.form.ComboBox({
-               store : tvheadend.caches,
-               fieldLabel : 'Cache scheme',
-               triggerAction : 'all',
-               displayField : 'description',
-               valueField : 'index',
-               editable : false,
-               width : 200,
-               hiddenName : 'cache'
-       });
-
-       var logRetention = new Ext.form.NumberField({
-               allowNegative : false,
-               allowDecimals : false,
-               minValue : 1,
-               fieldLabel : 'DVR Log retention time (days)',
-               name : 'retention'
-       });
-
-       var timeBefore = new Ext.form.NumberField({
-               allowDecimals : false,
-               fieldLabel : 'Extra time before recordings (minutes)',
-               name : 'preExtraTime'
+    var confreader = new Ext.data.JsonReader({
+        root: 'dvrSettings'
+    }, ['storage', 'filePermissions', 'dirPermissions', 'postproc', 'retention', 'dayDirs', 'channelDirs',
+        'channelInTitle', 'container', 'cache', 'dateInTitle', 'timeInTitle',
+        'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 'titleDirs',
+        'episodeInTitle', 'cleanTitle', 'tagFiles', 'commSkip', 'subtitleInTitle',
+        'episodeBeforeDate', 'rewritePAT', 'rewritePMT']);
+
+    var confcombo = new Ext.form.ComboBox({
+        store: tvheadend.configNames,
+        triggerAction: 'all',
+        mode: 'local',
+        displayField: 'name',
+        name: 'config_name',
+        emptyText: '(default)',
+        value: '',
+        editable: true
+    });
+
+    var delButton = new Ext.Toolbar.Button({
+        tooltip: 'Delete named configuration',
+        iconCls: 'remove',
+        text: "Delete configuration",
+        handler: deleteConfiguration,
+        disabled: true
+    });
+
+    /* Config panel variables */
+
+    /* DVR Behaviour */
+
+    var recordingContainer = new Ext.form.ComboBox({
+        store: tvheadend.containers,
+        fieldLabel: 'Media container',
+        triggerAction: 'all',
+        displayField: 'description',
+        valueField: 'name',
+        editable: false,
+        width: 200,
+        hiddenName: 'container'
+    });
+
+    var cacheScheme = new Ext.form.ComboBox({
+        store: tvheadend.caches,
+        fieldLabel: 'Cache scheme',
+        triggerAction: 'all',
+        displayField: 'description',
+        valueField: 'index',
+        editable: false,
+        width: 200,
+        hiddenName: 'cache'
+    });
+
+    var logRetention = new Ext.form.NumberField({
+        allowNegative: false,
+        allowDecimals: false,
+        minValue: 1,
+        fieldLabel: 'DVR Log retention time (days)',
+        name: 'retention'
+    });
+
+    var timeBefore = new Ext.form.NumberField({
+        allowDecimals: false,
+        fieldLabel: 'Extra time before recordings (minutes)',
+        name: 'preExtraTime'
     });
-        
+
     var timeAfter = new Ext.form.NumberField({
-        allowDecimals : false,
-        fieldLabel : 'Extra time after recordings (minutes)',
-        name : 'postExtraTime'
-       }); 
-       
-       var postProcessing = new Ext.form.TextField({
-               width : 300,
-               fieldLabel : 'Post-processor command',
-               name : 'postproc'
-       });
-
-/* Recording File Options */
-
-       var recordingPath = new Ext.form.TextField({
-               width : 300,
-               fieldLabel : 'Recording system path',
-               name : 'storage'
-       });
-
-/* NB: recordingPermissions is defined as a TextField for validation purposes (leading zeros), but is ultimately a number */
-       
-       var recordingPermissions = new Ext.form.TextField({
-               regex : /^[0][0-7]{3}$/,
-           maskRe : /[0-7]/,
-               width : 100,
-               allowBlank : false,
-               blankText : 'You must provide a value - use octal chmod notation, e.g. 0664',
-               fieldLabel : 'File permissions (octal, e.g. 0664)',
-               name : 'filePermissions'
-       });
-
-/* TO DO - Add 'override user umask?' option, then trigger fchmod in mkmux.c, muxer_pass.c after file created */
-               
-       var PATrewrite = new Ext.form.Checkbox({
-               fieldLabel : 'Rewrite PAT in passthrough mode',
-               name : 'rewritePAT'
-       });
-               
-       var PMTrewrite = new Ext.form.Checkbox({
-               fieldLabel : 'Rewrite PMT in passthrough mode',
-               name : 'rewritePMT'
-       }); 
-               
-       var tagMetadata = new Ext.form.Checkbox({
-               fieldLabel : 'Tag files with metadata',
-               name : 'tagFiles'
-       });
-                
-       var skipCommercials = new Ext.form.Checkbox({
-               fieldLabel : 'Skip commercials',
-               name : 'commSkip'
-       }); 
-               
-/* Subdirectories and filename handling */
-
-/* NB: directoryPermissions is defined as a TextField for validation purposes (leading zeros), but is ultimately a number */
-
-       var directoryPermissions = new Ext.form.TextField({
-               regex : /^[0][0-7]{3}$/,
-           maskRe : /[0-7]/,
-               width : 100,
-               allowBlank : false,
-               blankText : 'You must provide a value - use octal chmod notation, e.g. 0775',
-               fieldLabel : 'Directory permissions (octal, e.g. 0775)',
-               name : 'dirPermissions'
-       });
-       
-/* TO DO - Add 'override user umask?' option, then trigger fchmod in utils.c after directory created */
-       
-       var dirsPerDay = new Ext.form.Checkbox({
-               fieldLabel : 'Make subdirectories per day',
-               name : 'dayDirs'
-       }); 
-               
-       var dirsPerChannel = new Ext.form.Checkbox({
-               fieldLabel : 'Make subdirectories per channel',
-               name : 'channelDirs'
-       }); 
-               
-       var dirsPerTitle = new Ext.form.Checkbox({
-               fieldLabel : 'Make subdirectories per title',
-               name : 'titleDirs'
-       }); 
-               
-       var incChannelInTitle = new Ext.form.Checkbox({
-               fieldLabel : 'Include channel name in filename',
-               name : 'channelInTitle'
-       });
-               
-       var incDateInTitle = new Ext.form.Checkbox({
-               fieldLabel : 'Include date in filename',
-               name : 'dateInTitle'
-       });
-               
-       var incTimeInTitle = new Ext.form.Checkbox({
-               fieldLabel : 'Include time in filename',
-               name : 'timeInTitle'
-       });
-                
-       var incEpisodeInTitle = new Ext.form.Checkbox({
-               fieldLabel : 'Include episode in filename',
-               name : 'episodeInTitle'
-       });
-       
-       var incSubtitleInTitle = new Ext.form.Checkbox({
-               fieldLabel : 'Include subtitle in filename',
-               name : 'subtitleInTitle'
-       }); 
-       
-       var episodeFirst = new Ext.form.Checkbox({
-               fieldLabel : 'Put episode in filename before date and time',
-               name : 'episodeBeforeDate'
-       }); 
-       
-       var stripUnsafeChars = new Ext.form.Checkbox({
-               fieldLabel : 'Remove all unsafe characters from filename',
-               name : 'cleanTitle'
-       });
-                
-       var stripWhitespace = new Ext.form.Checkbox({
-               fieldLabel : 'Replace whitespace in title with \'-\'',
-               name : 'whitespaceInTitle'
-       }); 
-       
-
-/* Sub-Panel - DVR behaviour */
-               
-       var DVRBehaviour = new Ext.form.FieldSet({
-               title: 'DVR Behaviour',
-               width: 700,
-               autoHeight: true,
-               collapsible: true,
-               items : [ recordingContainer, cacheScheme, logRetention, timeBefore, timeAfter, postProcessing ]
-       });
-
-/* Sub-Panel - File Output */
-
-       var FileOutputPanel = new Ext.form.FieldSet({
-               title: 'Recording File Options',
-               width: 700,
-               autoHeight: true,
-               collapsible: true,
-               items : [ recordingPath, recordingPermissions, PATrewrite, PMTrewrite, tagMetadata, skipCommercials ]
-       });
-
-/* Sub-Panel - Directory operations */
-
-       var DirHandlingPanel = new Ext.form.FieldSet({
-               title: 'Subdirectory Options',
-               width: 700,
-               autoHeight: true,
-               collapsible: true,
-               items : [ directoryPermissions, dirsPerDay, dirsPerChannel, dirsPerTitle ]
-       });
-               
-/* Sub-Panel - File operations */
-
-       var FileHandlingPanel = new Ext.form.FieldSet({
-               title: 'Filename Options',
-               width: 700,
-               autoHeight: true,
-               collapsible: true,
-               items : [ incChannelInTitle, incDateInTitle, incTimeInTitle, incEpisodeInTitle,
-                       incSubtitleInTitle, episodeFirst, stripUnsafeChars, stripWhitespace ]
-       });
-
-/* Main (form) panel */
-                               
-       var confpanel = new Ext.FormPanel({
-               title : 'Digital Video Recorder',
-               iconCls : 'drive',
-               border : false,
-               bodyStyle : 'padding:15px',
-               anchor : '100% 50%',
-               labelAlign : 'right',
-               labelWidth : 250,
-               waitMsgTarget : true,
-               reader : confreader,
-               defaultType : 'textfield',
-               layout : 'form',
-               items : [ DVRBehaviour, FileOutputPanel, DirHandlingPanel, FileHandlingPanel ],
-               
-               tbar : [ confcombo, {
-                       tooltip : 'Save changes made to dvr configuration below',
-                       iconCls : 'save',
-                       text : "Save configuration",
-                       handler : saveChanges
-               }, delButton, '->', {
-                       text : 'Help',
-                       handler : function() {
-                               new tvheadend.help('DVR configuration', 'config_dvr.html');
-                       }
-               } ]
-       });
-
-       function loadConfig() {
-               confpanel.getForm().load({
-                       url : 'dvr',
-                       params : {
-                               'op' : 'loadSettings',
-                               'config_name' : confcombo.getValue()
-                       },
-                       success : function(form, action) {
-                               confpanel.enable();
-                       }
-               });
-       }
-
-       confcombo.on('select', function() {
-               if (confcombo.getValue() == '') delButton.disable();
-               else delButton.enable();
-               loadConfig();
-       });
-
-       confpanel.on('render', function() {
-               loadConfig();
-       });
-
-       function saveChanges() {
-               var config_name = confcombo.getValue();
-               confpanel.getForm().submit({
-                       url : 'dvr',
-                       params : {
-                               'op' : 'saveSettings',
-                               'config_name' : config_name
-                       },
-                       waitMsg : 'Saving Data...',
-                       success : function(form, action) {
-                               confcombo.setValue(config_name);
-                               confcombo.fireEvent('select');
-                       },
-                       failure : function(form, action) {
-                               Ext.Msg.alert('Save failed', action.result.errormsg);
-                       }
-               });
-       }
-
-       function deleteConfiguration() {
-               if (confcombo.getValue() != "") {
-                       Ext.MessageBox.confirm('Message',
-                               'Do you really want to delete DVR configuration \''
-                                       + confcombo.getValue() + '\'?', deleteAction);
-               }
-       }
-
-       function deleteAction(btn) {
-               if (btn == 'yes') {
-                       confpanel.getForm().submit({
-                               url : 'dvr',
-                               params : {
-                                       'op' : 'deleteSettings',
-                                       'config_name' : confcombo.getValue()
-                               },
-                               waitMsg : 'Deleting Data...',
-                               success : function(form, action) {
-                                       confcombo.setValue('');
-                                       confcombo.fireEvent('select');
-                               },
-                               failure : function(form, action) {
-                                       Ext.Msg.alert('Delete failed', action.result.errormsg);
-                               }
-                       });
-               }
-       }
-
-       return confpanel;
-}
+        allowDecimals: false,
+        fieldLabel: 'Extra time after recordings (minutes)',
+        name: 'postExtraTime'
+    });
+
+    var postProcessing = new Ext.form.TextField({
+        width: 300,
+        fieldLabel: 'Post-processor command',
+        name: 'postproc'
+    });
+
+    /* Recording File Options */
+
+    var recordingPath = new Ext.form.TextField({
+        width: 300,
+        fieldLabel: 'Recording system path',
+        name: 'storage'
+    });
+
+    /* NB: recordingPermissions is defined as a TextField for validation purposes (leading zeros), but is ultimately a number */
+
+    var recordingPermissions = new Ext.form.TextField({
+        regex: /^[0][0-7]{3}$/,
+        maskRe: /[0-7]/,
+        width: 100,
+        allowBlank: false,
+        blankText: 'You must provide a value - use octal chmod notation, e.g. 0664',
+        fieldLabel: 'File permissions (octal, e.g. 0664)',
+        name: 'filePermissions'
+    });
+
+    /* TO DO - Add 'override user umask?' option, then trigger fchmod in mkmux.c, muxer_pass.c after file created */
+
+    var PATrewrite = new Ext.form.Checkbox({
+        fieldLabel: 'Rewrite PAT in passthrough mode',
+        name: 'rewritePAT'
+    });
+
+    var PMTrewrite = new Ext.form.Checkbox({
+        fieldLabel: 'Rewrite PMT in passthrough mode',
+        name: 'rewritePMT'
+    });
+
+    var tagMetadata = new Ext.form.Checkbox({
+        fieldLabel: 'Tag files with metadata',
+        name: 'tagFiles'
+    });
+
+    var skipCommercials = new Ext.form.Checkbox({
+        fieldLabel: 'Skip commercials',
+        name: 'commSkip'
+    });
+
+    /* Subdirectories and filename handling */
+
+    /* NB: directoryPermissions is defined as a TextField for validation purposes (leading zeros), but is ultimately a number */
+
+    var directoryPermissions = new Ext.form.TextField({
+        regex: /^[0][0-7]{3}$/,
+        maskRe: /[0-7]/,
+        width: 100,
+        allowBlank: false,
+        blankText: 'You must provide a value - use octal chmod notation, e.g. 0775',
+        fieldLabel: 'Directory permissions (octal, e.g. 0775)',
+        name: 'dirPermissions'
+    });
+
+    /* TO DO - Add 'override user umask?' option, then trigger fchmod in utils.c after directory created */
+
+    var dirsPerDay = new Ext.form.Checkbox({
+        fieldLabel: 'Make subdirectories per day',
+        name: 'dayDirs'
+    });
+
+    var dirsPerChannel = new Ext.form.Checkbox({
+        fieldLabel: 'Make subdirectories per channel',
+        name: 'channelDirs'
+    });
+
+    var dirsPerTitle = new Ext.form.Checkbox({
+        fieldLabel: 'Make subdirectories per title',
+        name: 'titleDirs'
+    });
+
+    var incChannelInTitle = new Ext.form.Checkbox({
+        fieldLabel: 'Include channel name in filename',
+        name: 'channelInTitle'
+    });
+
+    var incDateInTitle = new Ext.form.Checkbox({
+        fieldLabel: 'Include date in filename',
+        name: 'dateInTitle'
+    });
+
+    var incTimeInTitle = new Ext.form.Checkbox({
+        fieldLabel: 'Include time in filename',
+        name: 'timeInTitle'
+    });
+
+    var incEpisodeInTitle = new Ext.form.Checkbox({
+        fieldLabel: 'Include episode in filename',
+        name: 'episodeInTitle'
+    });
+
+    var incSubtitleInTitle = new Ext.form.Checkbox({
+        fieldLabel: 'Include subtitle in filename',
+        name: 'subtitleInTitle'
+    });
+
+    var episodeFirst = new Ext.form.Checkbox({
+        fieldLabel: 'Put episode in filename before date and time',
+        name: 'episodeBeforeDate'
+    });
+
+    var stripUnsafeChars = new Ext.form.Checkbox({
+        fieldLabel: 'Remove all unsafe characters from filename',
+        name: 'cleanTitle'
+    });
+
+    var stripWhitespace = new Ext.form.Checkbox({
+        fieldLabel: 'Replace whitespace in title with \'-\'',
+        name: 'whitespaceInTitle'
+    });
+
+
+    /* Sub-Panel - DVR behaviour */
+
+    var DVRBehaviour = new Ext.form.FieldSet({
+        title: 'DVR Behaviour',
+        width: 700,
+        autoHeight: true,
+        collapsible: true,
+        items: [recordingContainer, cacheScheme, logRetention, timeBefore, timeAfter, postProcessing]
+    });
+
+    /* Sub-Panel - File Output */
+
+    var FileOutputPanel = new Ext.form.FieldSet({
+        title: 'Recording File Options',
+        width: 700,
+        autoHeight: true,
+        collapsible: true,
+        items: [recordingPath, recordingPermissions, PATrewrite, PMTrewrite, tagMetadata, skipCommercials]
+    });
+
+    /* Sub-Panel - Directory operations */
+
+    var DirHandlingPanel = new Ext.form.FieldSet({
+        title: 'Subdirectory Options',
+        width: 700,
+        autoHeight: true,
+        collapsible: true,
+        items: [directoryPermissions, dirsPerDay, dirsPerChannel, dirsPerTitle]
+    });
+
+    /* Sub-Panel - File operations */
+
+    var FileHandlingPanel = new Ext.form.FieldSet({
+        title: 'Filename Options',
+        width: 700,
+        autoHeight: true,
+        collapsible: true,
+        items: [incChannelInTitle, incDateInTitle, incTimeInTitle, incEpisodeInTitle,
+            incSubtitleInTitle, episodeFirst, stripUnsafeChars, stripWhitespace]
+    });
+
+    /* Main (form) panel */
+
+    var confpanel = new Ext.FormPanel({
+        title: 'Digital Video Recorder',
+        iconCls: 'drive',
+        border: false,
+        bodyStyle: 'padding:15px',
+        anchor: '100% 50%',
+        labelAlign: 'right',
+        labelWidth: 250,
+        waitMsgTarget: true,
+        reader: confreader,
+        defaultType: 'textfield',
+        layout: 'form',
+        items: [DVRBehaviour, FileOutputPanel, DirHandlingPanel, FileHandlingPanel],
+        tbar: [confcombo, {
+                tooltip: 'Save changes made to dvr configuration below',
+                iconCls: 'save',
+                text: "Save configuration",
+                handler: saveChanges
+            }, delButton, '->', {
+                text: 'Help',
+                handler: function() {
+                    new tvheadend.help('DVR configuration', 'config_dvr.html');
+                }
+            }]
+    });
+
+    function loadConfig() {
+        confpanel.getForm().load({
+            url: 'dvr',
+            params: {
+                'op': 'loadSettings',
+                'config_name': confcombo.getValue()
+            },
+            success: function(form, action) {
+                confpanel.enable();
+            }
+        });
+    }
+
+    confcombo.on('select', function() {
+        if (confcombo.getValue() === '')
+            delButton.disable();
+        else
+            delButton.enable();
+        loadConfig();
+    });
+
+    confpanel.on('render', function() {
+        loadConfig();
+    });
+
+    function saveChanges() {
+        var config_name = confcombo.getValue();
+        confpanel.getForm().submit({
+            url: 'dvr',
+            params: {
+                'op': 'saveSettings',
+                'config_name': config_name
+            },
+            waitMsg: 'Saving Data...',
+            success: function(form, action) {
+                confcombo.setValue(config_name);
+                confcombo.fireEvent('select');
+            },
+            failure: function(form, action) {
+                Ext.Msg.alert('Save failed', action.result.errormsg);
+            }
+        });
+    }
+
+    function deleteConfiguration() {
+        if (confcombo.getValue() !== "") {
+            Ext.MessageBox.confirm('Message',
+                    'Do you really want to delete DVR configuration \''
+                    + confcombo.getValue() + '\'?', deleteAction);
+        }
+    }
+
+    function deleteAction(btn) {
+        if (btn === 'yes') {
+            confpanel.getForm().submit({
+                url: 'dvr',
+                params: {
+                    'op': 'deleteSettings',
+                    'config_name': confcombo.getValue()
+                },
+                waitMsg: 'Deleting Data...',
+                success: function(form, action) {
+                    confcombo.setValue('');
+                    confcombo.fireEvent('select');
+                },
+                failure: function(form, action) {
+                    Ext.Msg.alert('Delete failed', action.result.errormsg);
+                }
+            });
+        }
+    }
+
+    return confpanel;
+};
old mode 100755 (executable)
new mode 100644 (file)
index 4bf4c26..c4175e6
 tvheadend.brands = new Ext.data.JsonStore({
-       root : 'entries',
-       fields : [ 'uri', 'title' ],
-       autoLoad : true,
-       url : 'epgobject',
-       baseParams : {
-               op : 'brandList'
-       }
+    root: 'entries',
+    fields: ['uri', 'title'],
+    autoLoad: true,
+    url: 'epgobject',
+    baseParams: {
+        op: 'brandList'
+    }
 });
 //WIBNI: might want this store to periodically update
 
 tvheadend.ContentGroupStore = new Ext.data.JsonStore({
-       root : 'entries',
-       fields : [ 'name', 'code' ],
-       autoLoad : true,
-       url : 'ecglist'
+    root: 'entries',
+    fields: ['name', 'code'],
+    autoLoad: true,
+    url: 'ecglist'
 });
 
 tvheadend.contentGroupLookupName = function(code) {
-       ret = "";
-       tvheadend.ContentGroupStore.each(function(r) {
-               if (r.data.code == code) ret = r.data.name;
-               else if (ret == "" && r.data.code == (code & 0xF0)) ret = r.data.name;
-       });
-       return ret;
-}
+    ret = "";
+    tvheadend.ContentGroupStore.each(function(r) {
+        if (r.data.code === code)
+            ret = r.data.name;
+        else if (ret === "" && r.data.code === (code & 0xF0))
+            ret = r.data.name;
+    });
+    return ret;
+};
 
 tvheadend.ContentGroupStore.setDefaultSort('code', 'ASC');
 
 tvheadend.epgDetails = function(event) {
 
-       var content = '';
-       
-       if (event.chicon != null && event.chicon.length > 0) 
-               content += '<img class="x-epg-chicon" src="'+ event.chicon + '">';
-       
-       content += '<div class="x-epg-title">' + event.title;
-       if (event.subtitle) 
-               content += "&nbsp;:&nbsp;" + event.subtitle;
-       content += '</div>';
-       content += '<div class="x-epg-desc">' + event.episode + '</div>';
-       content += '<div class="x-epg-desc">' + event.description + '</div>';
-       content += '<div class="x-epg-meta">' + event.starrating + '</div>';
-       content += '<div class="x-epg-meta">' + event.agerating + '</div>';
-       content += '<div class="x-epg-meta">' + tvheadend.contentGroupLookupName(event.contenttype) + '</div>';
-
-       if (event.ext_desc != null) 
-               content += '<div class="x-epg-meta">' + event.ext_desc + '</div>';
-
-       if (event.ext_item != null) 
-               content += '<div class="x-epg-meta">' + event.ext_item + '</div>';
-
-       if (event.ext_text != null) 
-               content += '<div class="x-epg-meta">' + event.ext_text + '</div>';
-
-       content += '<div class="x-epg-meta"><a target="_blank" href="http://akas.imdb.com/find?q=' + event.title + '">Search IMDB</a></div>'
-       content += '<div id="related"></div>';
-       content += '<div id="altbcast"></div>';
-
-       var confcombo = new Ext.form.ComboBox({
-               store : tvheadend.configNames,
-               triggerAction : 'all',
-               mode : 'local',
-               valueField : 'identifier',
-               displayField : 'name',
-               name : 'config_name',
-               emptyText : '(default)',
-               value : '',
-               editable : false
-       });
-
-       var win = new Ext.Window({
-               title : event.title,
-               layout : 'fit',
-               width : 500,
-               height : 300,
-               constrainHeader : true,
-               buttons : [ confcombo, new Ext.Button({
-                       handler : recordEvent,
-                       text : "Record program"
-               }), new Ext.Button({
-                       handler : recordSeries,
-                       text : event.serieslink ? "Record series" : "Autorec"
-               }) ],
-               buttonAlign : 'center',
-               html : content
-       });
-       win.show();
-
-       function recordEvent() {
-               record('recordEvent');
-       }
-
-       function recordSeries() {
-               record('recordSeries');
-       }
-
-       function record(op) {
-               Ext.Ajax.request({
-                       url : 'dvr',
-                       params : {
-                               eventId : event.id,
-                               op : op,
-                               config_name : confcombo.getValue()
-                       },
-
-                       success : function(response, options) {
-                               win.close();
-                       },
-
-                       failure : function(response, options) {
-                               Ext.MessageBox.alert('DVR', response.statusText);
-                       }
-               });
-       }
-
-       function showAlternatives(s) {
-               var e = Ext.get('altbcast')
-               html = '';
-               if (s.getTotalCount() > 0) {
-                       html += '<div class="x-epg-subtitle">Alternative Broadcasts</div>';
-                       for (i = 0; i < s.getTotalCount(); i++) {
-                               var ab = s.getAt(i).data;
-                               var dt = Date.parseDate(ab.start, 'U');
-                               html += '<div class="x-epg-desc">' + dt.format('l H:i')
-                                       + '&nbsp;&nbsp;&nbsp;' + ab.channel + '</div>';
-                       }
-               }
-               e.dom.innerHTML = html;
-       }
-       function showRelated(s) {
-               var e = Ext.get('related')
-               html = '';
-               if (s.getTotalCount() > 0) {
-                       html += '<div class="x-epg-subtitle">Related Episodes</div>';
-                       for (i = 0; i < s.getTotalCount(); i++) {
-                               var ee = s.getAt(i).data;
-                               html += '<div class="x-epg-desc">';
-                               if (ee.episode) html += ee.episode + '&nbsp;&nbsp;&nbsp;';
-                               html += ee.title;
-                               if (ee.subtitle) html += ' : ' + ee.subtitle
-                               html += '</div>';
-                       }
-               }
-               e.dom.innerHTML = html;
-       }
-
-       var ab = new Ext.data.JsonStore({
-               root : 'entries',
-               url : 'epgrelated',
-               autoLoad : false,
-               id : 'id',
-               baseParams : {
-                       op : 'get',
-                       id : event.id,
-                       type : 'alternative'
-               },
-               fields : Ext.data.Record.create([ 'id', 'channel', 'start' ]),
-               listeners : {
-                       'datachanged' : showAlternatives
-               }
-       });
-       var re = new Ext.data.JsonStore({
-               root : 'entries',
-               url : 'epgrelated',
-               autoLoad : false,
-               id : 'uri',
-               baseParams : {
-                       op : 'get',
-                       id : event.id,
-                       type : 'related'
-               },
-               fields : Ext.data.Record
-                       .create([ 'uri', 'title', 'subtitle', 'episode' ]),
-               listeners : {
-                       'datachanged' : showRelated
-               }
-       });
-}
+    var content = '';
+
+    if (event.chicon != null && event.chicon.length > 0)
+        content += '<img class="x-epg-chicon" src="' + event.chicon + '">';
+
+    content += '<div class="x-epg-title">' + event.title;
+    if (event.subtitle)
+        content += "&nbsp;:&nbsp;" + event.subtitle;
+    content += '</div>';
+    content += '<div class="x-epg-desc">' + event.episode + '</div>';
+    content += '<div class="x-epg-desc">' + event.description + '</div>';
+    content += '<div class="x-epg-meta">' + event.starrating + '</div>';
+    content += '<div class="x-epg-meta">' + event.agerating + '</div>';
+    content += '<div class="x-epg-meta">' + tvheadend.contentGroupLookupName(event.contenttype) + '</div>';
+
+    if (event.ext_desc != null)
+        content += '<div class="x-epg-meta">' + event.ext_desc + '</div>';
+
+    if (event.ext_item != null)
+        content += '<div class="x-epg-meta">' + event.ext_item + '</div>';
+
+    if (event.ext_text != null)
+        content += '<div class="x-epg-meta">' + event.ext_text + '</div>';
+
+    content += '<div class="x-epg-meta"><a target="_blank" href="http://akas.imdb.com/find?q=' + event.title + '">Search IMDB</a></div>';
+    content += '<div id="related"></div>';
+    content += '<div id="altbcast"></div>';
+
+    var confcombo = new Ext.form.ComboBox({
+        store: tvheadend.configNames,
+        triggerAction: 'all',
+        mode: 'local',
+        valueField: 'identifier',
+        displayField: 'name',
+        name: 'config_name',
+        emptyText: '(default)',
+        value: '',
+        editable: false
+    });
+
+    var win = new Ext.Window({
+        title: event.title,
+        layout: 'fit',
+        width: 500,
+        height: 300,
+        constrainHeader: true,
+        buttons: [confcombo, new Ext.Button({
+                handler: recordEvent,
+                text: "Record program"
+            }), new Ext.Button({
+                handler: recordSeries,
+                text: event.serieslink ? "Record series" : "Autorec"
+            })],
+        buttonAlign: 'center',
+        html: content
+    });
+    win.show();
+
+    function recordEvent() {
+        record('recordEvent');
+    }
+
+    function recordSeries() {
+        record('recordSeries');
+    }
+
+    function record(op) {
+        Ext.Ajax.request({
+            url: 'dvr',
+            params: {
+                eventId: event.id,
+                op: op,
+                config_name: confcombo.getValue()
+            },
+            success: function(response, options) {
+                win.close();
+            },
+            failure: function(response, options) {
+                Ext.MessageBox.alert('DVR', response.statusText);
+            }
+        });
+    }
+
+    function showAlternatives(s) {
+        var e = Ext.get('altbcast');
+        html = '';
+        if (s.getTotalCount() > 0) {
+            html += '<div class="x-epg-subtitle">Alternative Broadcasts</div>';
+            for (i = 0; i < s.getTotalCount(); i++) {
+                var ab = s.getAt(i).data;
+                var dt = Date.parseDate(ab.start, 'U');
+                html += '<div class="x-epg-desc">' + dt.format('l H:i')
+                        + '&nbsp;&nbsp;&nbsp;' + ab.channel + '</div>';
+            }
+        }
+        e.dom.innerHTML = html;
+    }
+    function showRelated(s) {
+        var e = Ext.get('related');
+        html = '';
+        if (s.getTotalCount() > 0) {
+            html += '<div class="x-epg-subtitle">Related Episodes</div>';
+            for (i = 0; i < s.getTotalCount(); i++) {
+                var ee = s.getAt(i).data;
+                html += '<div class="x-epg-desc">';
+                if (ee.episode)
+                    html += ee.episode + '&nbsp;&nbsp;&nbsp;';
+                html += ee.title;
+                if (ee.subtitle)
+                    html += ' : ' + ee.subtitle;
+                html += '</div>';
+            }
+        }
+        e.dom.innerHTML = html;
+    }
+
+    var ab = new Ext.data.JsonStore({
+        root: 'entries',
+        url: 'epgrelated',
+        autoLoad: false,
+        id: 'id',
+        baseParams: {
+            op: 'get',
+            id: event.id,
+            type: 'alternative'
+        },
+        fields: Ext.data.Record.create(['id', 'channel', 'start']),
+        listeners: {
+            'datachanged': showAlternatives
+        }
+    });
+    var re = new Ext.data.JsonStore({
+        root: 'entries',
+        url: 'epgrelated',
+        autoLoad: false,
+        id: 'uri',
+        baseParams: {
+            op: 'get',
+            id: event.id,
+            type: 'related'
+        },
+        fields: Ext.data.Record
+                .create(['uri', 'title', 'subtitle', 'episode']),
+        listeners: {
+            'datachanged': showRelated
+        }
+    });
+};
 
 tvheadend.epg = function() {
-       var xg = Ext.grid;
-
-       var actions = new Ext.ux.grid.RowActions({
-               header : '',
-               width : 20,
-               dataIndex : 'actions',
-               actions : [ {
-                       iconIndex : 'schedstate'
-               } ]
-       });
-
-       var epgStore = new Ext.ux.grid.livegrid.Store({
-               autoLoad : true,
-               url : 'epg',
-               bufferSize : 300,
-               reader : new Ext.ux.grid.livegrid.JsonReader({
-                       root : 'entries',
-                       totalProperty : 'totalCount',
-                       id : 'id'
-               }, [ {
-                       name : 'id'
-               }, {
-                       name : 'channel'
-               }, {
-                       name : 'channelid'
-               }, {
-                       name : 'title'
-               }, {
-                       name : 'subtitle'
-               }, {
-                       name : 'episode'
-               }, {
-                       name : 'description'
-               }, {
-                       name : 'chicon'
-               }, {
-                       name : 'start',
-                       type : 'date',
-                       dateFormat : 'U' /* unix time */
-               }, {
-                       name : 'end',
-                       type : 'date',
-                       dateFormat : 'U' /* unix time */
-               }, {
-                       name : 'duration'
-               }, {
-                       name : 'starrating'
-               }, {
-                       name : 'agerating'
-               }, {
-                       name : 'contenttype'
-               }, {
-                       name : 'schedstate'
-               }, {
-                       name : 'serieslink'
-               } ])
-       });
-
-       function setMetaAttr(meta, record) {
-               var now = new Date;
-               var start = record.get('start');
-
-               if (now.getTime() > start.getTime()) {
-                       meta.attr = 'style="font-weight:bold;"';
-               }
-       }
-
-       function renderDate(value, meta, record, rowIndex, colIndex, store) {
-               setMetaAttr(meta, record);
-
-               var dt = new Date(value);
-               return dt.format('D, M d, H:i');
-       }
-
-       function renderDuration(value, meta, record, rowIndex, colIndex, store) {
-               setMetaAttr(meta, record);
-
-               value = Math.floor(value / 60);
-
-               if (value >= 60) {
-                       var min = value % 60;
-                       var hours = Math.floor(value / 60);
-
-                       if (min == 0) {
-                               return hours + ' hrs';
-                       }
-                       return hours + ' hrs, ' + min + ' min';
-               }
-               else {
-                       return value + ' min';
-               }
-       }
+    var xg = Ext.grid;
+
+    var actions = new Ext.ux.grid.RowActions({
+        header: '',
+        width: 20,
+        dataIndex: 'actions',
+        actions: [{
+                iconIndex: 'schedstate'
+            }]
+    });
+
+    var epgStore = new Ext.ux.grid.livegrid.Store({
+        autoLoad: true,
+        url: 'epg',
+        bufferSize: 300,
+        reader: new Ext.ux.grid.livegrid.JsonReader({
+            root: 'entries',
+            totalProperty: 'totalCount',
+            id: 'id'
+        }, [{
+                name: 'id'
+            }, {
+                name: 'channel'
+            }, {
+                name: 'channelid'
+            }, {
+                name: 'title'
+            }, {
+                name: 'subtitle'
+            }, {
+                name: 'episode'
+            }, {
+                name: 'description'
+            }, {
+                name: 'chicon'
+            }, {
+                name: 'start',
+                type: 'date',
+                dateFormat: 'U' /* unix time */
+            }, {
+                name: 'end',
+                type: 'date',
+                dateFormat: 'U' /* unix time */
+            }, {
+                name: 'duration'
+            }, {
+                name: 'starrating'
+            }, {
+                name: 'agerating'
+            }, {
+                name: 'contenttype'
+            }, {
+                name: 'schedstate'
+            }, {
+                name: 'serieslink'
+            }])
+    });
+
+    function setMetaAttr(meta, record) {
+        var now = new Date;
+        var start = record.get('start');
+
+        if (now.getTime() > start.getTime()) {
+            meta.attr = 'style="font-weight:bold;"';
+        }
+    }
+
+    function renderDate(value, meta, record, rowIndex, colIndex, store) {
+        setMetaAttr(meta, record);
+
+        var dt = new Date(value);
+        return dt.format('D, M d, H:i');
+    }
+
+    function renderDuration(value, meta, record, rowIndex, colIndex, store) {
+        setMetaAttr(meta, record);
+
+        value = Math.floor(value / 60);
+
+        if (value >= 60) {
+            var min = value % 60;
+            var hours = Math.floor(value / 60);
+
+            if (min === 0) {
+                return hours + ' hrs';
+            }
+            return hours + ' hrs, ' + min + ' min';
+        }
+        else {
+            return value + ' min';
+        }
+    }
 
     function renderText(value, meta, record, rowIndex, colIndex, store) {
         setMetaAttr(meta, record);
@@ -284,267 +286,267 @@ tvheadend.epg = function() {
         return '' + value;
     }
 
-       var epgCm = new Ext.grid.ColumnModel([ actions, {
-               width : 250,
-               id : 'title',
-               header : "Title",
-               dataIndex : 'title',
-               renderer : renderText
-       }, {
-               width : 250,
-               id : 'subtitle',
-               header : "SubTitle",
-               dataIndex : 'subtitle',
-               renderer : renderText
-       }, {
-               width : 100,
-               id : 'episode',
-               header : "Episode",
-               dataIndex : 'episode',
-               renderer : renderText
-       }, {
-               width : 100,
-               id : 'start',
-               header : "Start",
-               dataIndex : 'start',
-               renderer : renderDate
-       }, {
-               width : 100,
-               hidden : true,
-               id : 'end',
-               header : "End",
-               dataIndex : 'end',
-               renderer : renderDate
-       }, {
-               width : 100,
-               id : 'duration',
-               header : "Duration",
-               dataIndex : 'duration',
-               renderer : renderDuration
-       }, {
-               width : 250,
-               id : 'channel',
-               header : "Channel",
-               dataIndex : 'channel',
-               renderer : renderText
-    }, {
-        width : 50,
-        id : 'starrating',
-        header : "Stars",
-        dataIndex : 'starrating',
-        renderer : renderInt
-    }, {
-        width : 50,
-        id : 'agerating',
-        header : "Age",
-        dataIndex : 'agerating',
-        renderer : renderInt
-       }, {
-               width : 250,
-               id : 'contenttype',
-               header : "Content Type",
-               dataIndex : 'contenttype',
-               renderer : function(v) {
-                       return tvheadend.contentGroupLookupName(v);
-               }
-       ]);
-
-       // Title search box
-
-       var epgFilterTitle = new Ext.form.TextField({
-               emptyText : 'Search title...',
-               width : 200
-       });
-
-       // Channels, uses global store
-
-       var epgFilterChannels = new Ext.form.ComboBox({
-               loadingText : 'Loading...',
-               width : 200,
-               displayField : 'val',
-               store : tvheadend.channels,
-               mode : 'local',
-               editable : true,
-               forceSelection: true,
-               triggerAction : 'all',
-               emptyText : 'Filter channel...'
-       });
-
-       // Tags, uses global store
-
-       var epgFilterChannelTags = new Ext.form.ComboBox({
-               width : 200,
-               displayField : 'name',
-               store : tvheadend.channelTags,
-               mode : 'local',
-               editable : true,
-               forceSelection: true,
-               triggerAction : 'all',
-               emptyText : 'Filter tag...'
-       });
-
-       // Content groups
-
-       var epgFilterContentGroup = new Ext.form.ComboBox({
-               loadingText : 'Loading...',
-               width : 200,
-               displayField : 'name',
-               store : tvheadend.ContentGroupStore,
-               mode : 'local',
-               editable : true,
-               forceSelection: true,
-               triggerAction : 'all',
-               emptyText : 'Filter content type...'
-       });
-
-       function epgQueryClear() {
-               delete epgStore.baseParams.channel;
-               delete epgStore.baseParams.tag;
-               delete epgStore.baseParams.contenttype;
-               delete epgStore.baseParams.title;
-
-               epgFilterChannels.setValue("");
-               epgFilterChannelTags.setValue("");
-               epgFilterContentGroup.setValue("");
-               epgFilterTitle.setValue("");
-
-               epgStore.reload();
-       }
-
-       epgFilterChannels.on('select', function(c, r) {
-               if (epgStore.baseParams.channel != r.data.key) {
-                       epgStore.baseParams.channel = r.data.key;
-                       epgStore.reload();
-               }
-       });
-
-       epgFilterChannelTags.on('select', function(c, r) {
-               if (epgStore.baseParams.tag != r.data.name) {
-                       epgStore.baseParams.tag = r.data.name;
-                       epgStore.reload();
-               }
-       });
-
-       epgFilterContentGroup.on('select', function(c, r) {
-               if (epgStore.baseParams.contenttype != r.data.code) {
-                       epgStore.baseParams.contenttype = r.data.code;
-                       epgStore.reload();
-               }
-       });
-
-       epgFilterTitle.on('valid', function(c) {
-               var value = c.getValue();
-
-               if (value.length < 1) value = null;
-
-               if (epgStore.baseParams.title != value) {
-                       epgStore.baseParams.title = value;
-                       epgStore.reload();
-               }
-       });
-
-       var epgView = new Ext.ux.grid.livegrid.GridView({
-               nearLimit : 100,
-               loadMask : {
-                       msg : 'Buffering. Please wait...'
-               }
-       });
-
-       var panel = new Ext.ux.grid.livegrid.GridPanel({
-               stateful: true,
-               stateId : 'epggrid',
-               enableDragDrop : false,
-               cm : epgCm,
-               plugins : [ actions ],
-               title : 'Electronic Program Guide',
-               iconCls : 'newspaper',
-               store : epgStore,
-               selModel : new Ext.ux.grid.livegrid.RowSelectionModel(),
-               view : epgView,
-               tbar : [
-                       epgFilterTitle,
-                       '-',
-                       epgFilterChannels,
-                       '-',
-                       epgFilterChannelTags,
-                       '-',
-                       epgFilterContentGroup,
-                       '-',
-                       {
-                               text : 'Reset',
-                               handler : epgQueryClear
-                       },
-                       '->',
-                       {
-                               text : 'Watch TV',
-                               iconCls : 'eye',
-                               handler : function() {
-                                       new tvheadend.VideoPlayer();
-                               }
-                       },
-                       '-',
-                       {
-                               text : 'Create AutoRec',
-                               iconCls : 'wand',
-                               tooltip : 'Create an automatic recording entry that will '
-                                       + 'record all future programmes that matches '
-                                       + 'the current query.',
-                               handler : createAutoRec
-                       }, '-', {
-                               text : 'Help',
-                               handler : function() {
-                                       new tvheadend.help('Electronic Program Guide', 'epg.html');
-                               }
-                       } ],
-
-               bbar : new Ext.ux.grid.livegrid.Toolbar({
-                       view : epgView,
-                       displayInfo : true
-               })
-       });
-
-       panel.on('rowclick', rowclicked);
-
-       function rowclicked(grid, index) {
-               new tvheadend.epgDetails(grid.getStore().getAt(index).data);
-       }
-
-       function createAutoRec() {
-
-               var title = epgStore.baseParams.title ? epgStore.baseParams.title
-                       : "<i>Don't care</i>";
-               var channel = epgStore.baseParams.channel ? epgStore.baseParams.channel
-                       : "<i>Don't care</i>";
-               var tag = epgStore.baseParams.tag ? epgStore.baseParams.tag
-                       : "<i>Don't care</i>";
-               var contenttype = epgStore.baseParams.contenttype ? epgStore.baseParams.contenttype
-                       : "<i>Don't care</i>";
-
-               Ext.MessageBox.confirm('Auto Recorder',
-                       'This will create an automatic rule that '
-                               + 'continuously scans the EPG for programmes '
-                               + 'to record that matches this query: ' + '<br><br>'
-                               + '<div class="x-smallhdr">Title:</div>' + title + '<br>'
-                               + '<div class="x-smallhdr">Channel:</div>' + channel + '<br>'
-                               + '<div class="x-smallhdr">Tag:</div>' + tag + '<br>'
-                               + '<div class="x-smallhdr">Genre:</div>' + contenttype + '<br>'
-                               + '<br>' + 'Currently this will match (and record) '
-                               + epgStore.getTotalCount() + ' events. ' + 'Are you sure?',
-
-                       function(button) {
-                               if (button == 'no') return;
-                               createAutoRec2(epgStore.baseParams);
-                       });
-       }
-
-       function createAutoRec2(params) {
-               /* Really do it */
-               params.op = 'createAutoRec';
-               Ext.Ajax.request({
-                       url : 'dvr',
-                       params : params
-               });
-       }
-
-       return panel;
-}
+    var epgCm = new Ext.grid.ColumnModel([actions, {
+            width: 250,
+            id: 'title',
+            header: "Title",
+            dataIndex: 'title',
+            renderer: renderText
+        }, {
+            width: 250,
+            id: 'subtitle',
+            header: "SubTitle",
+            dataIndex: 'subtitle',
+            renderer: renderText
+        }, {
+            width: 100,
+            id: 'episode',
+            header: "Episode",
+            dataIndex: 'episode',
+            renderer: renderText
+        }, {
+            width: 100,
+            id: 'start',
+            header: "Start",
+            dataIndex: 'start',
+            renderer: renderDate
+        }, {
+            width: 100,
+            hidden: true,
+            id: 'end',
+            header: "End",
+            dataIndex: 'end',
+            renderer: renderDate
+        }, {
+            width: 100,
+            id: 'duration',
+            header: "Duration",
+            dataIndex: 'duration',
+            renderer: renderDuration
+        }, {
+            width: 250,
+            id: 'channel',
+            header: "Channel",
+            dataIndex: 'channel',
+            renderer: renderText
+        }, {
+            width: 50,
+            id: 'starrating',
+            header: "Stars",
+            dataIndex: 'starrating',
+            renderer: renderInt
+        }, {
+            width: 50,
+            id: 'agerating',
+            header: "Age",
+            dataIndex: 'agerating',
+            renderer: renderInt
+        }, {
+            width: 250,
+            id: 'contenttype',
+            header: "Content Type",
+            dataIndex: 'contenttype',
+            renderer: function(v) {
+                return tvheadend.contentGroupLookupName(v);
+            }
+        }]);
+
+    // Title search box
+
+    var epgFilterTitle = new Ext.form.TextField({
+        emptyText: 'Search title...',
+        width: 200
+    });
+
+    // Channels, uses global store
+
+    var epgFilterChannels = new Ext.form.ComboBox({
+        loadingText: 'Loading...',
+        width: 200,
+        displayField: 'val',
+        store: tvheadend.channels,
+        mode: 'local',
+        editable: true,
+        forceSelection: true,
+        triggerAction: 'all',
+        emptyText: 'Filter channel...'
+    });
+
+    // Tags, uses global store
+
+    var epgFilterChannelTags = new Ext.form.ComboBox({
+        width: 200,
+        displayField: 'name',
+        store: tvheadend.channelTags,
+        mode: 'local',
+        editable: true,
+        forceSelection: true,
+        triggerAction: 'all',
+        emptyText: 'Filter tag...'
+    });
+
+    // Content groups
+
+    var epgFilterContentGroup = new Ext.form.ComboBox({
+        loadingText: 'Loading...',
+        width: 200,
+        displayField: 'name',
+        store: tvheadend.ContentGroupStore,
+        mode: 'local',
+        editable: true,
+        forceSelection: true,
+        triggerAction: 'all',
+        emptyText: 'Filter content type...'
+    });
+
+    function epgQueryClear() {
+        delete epgStore.baseParams.channel;
+        delete epgStore.baseParams.tag;
+        delete epgStore.baseParams.contenttype;
+        delete epgStore.baseParams.title;
+
+        epgFilterChannels.setValue("");
+        epgFilterChannelTags.setValue("");
+        epgFilterContentGroup.setValue("");
+        epgFilterTitle.setValue("");
+
+        epgStore.reload();
+    }
+
+    epgFilterChannels.on('select', function(c, r) {
+        if (epgStore.baseParams.channel !== r.data.key) {
+            epgStore.baseParams.channel = r.data.key;
+            epgStore.reload();
+        }
+    });
+
+    epgFilterChannelTags.on('select', function(c, r) {
+        if (epgStore.baseParams.tag !== r.data.name) {
+            epgStore.baseParams.tag = r.data.name;
+            epgStore.reload();
+        }
+    });
+
+    epgFilterContentGroup.on('select', function(c, r) {
+        if (epgStore.baseParams.contenttype !== r.data.code) {
+            epgStore.baseParams.contenttype = r.data.code;
+            epgStore.reload();
+        }
+    });
+
+    epgFilterTitle.on('valid', function(c) {
+        var value = c.getValue();
+
+        if (value.length < 1)
+            value = null;
+
+        if (epgStore.baseParams.title !== value) {
+            epgStore.baseParams.title = value;
+            epgStore.reload();
+        }
+    });
+
+    var epgView = new Ext.ux.grid.livegrid.GridView({
+        nearLimit: 100,
+        loadMask: {
+            msg: 'Buffering. Please wait...'
+        }
+    });
+
+    var panel = new Ext.ux.grid.livegrid.GridPanel({
+        stateful: true,
+        stateId: 'epggrid',
+        enableDragDrop: false,
+        cm: epgCm,
+        plugins: [actions],
+        title: 'Electronic Program Guide',
+        iconCls: 'newspaper',
+        store: epgStore,
+        selModel: new Ext.ux.grid.livegrid.RowSelectionModel(),
+        view: epgView,
+        tbar: [
+            epgFilterTitle,
+            '-',
+            epgFilterChannels,
+            '-',
+            epgFilterChannelTags,
+            '-',
+            epgFilterContentGroup,
+            '-',
+            {
+                text: 'Reset',
+                handler: epgQueryClear
+            },
+            '->',
+            {
+                text: 'Watch TV',
+                iconCls: 'eye',
+                handler: function() {
+                    new tvheadend.VideoPlayer();
+                }
+            },
+            '-',
+            {
+                text: 'Create AutoRec',
+                iconCls: 'wand',
+                tooltip: 'Create an automatic recording entry that will '
+                        + 'record all future programmes that matches '
+                        + 'the current query.',
+                handler: createAutoRec
+            }, '-', {
+                text: 'Help',
+                handler: function() {
+                    new tvheadend.help('Electronic Program Guide', 'epg.html');
+                }
+            }],
+        bbar: new Ext.ux.grid.livegrid.Toolbar({
+            view: epgView,
+            displayInfo: true
+        })
+    });
+
+    panel.on('rowclick', rowclicked);
+
+    function rowclicked(grid, index) {
+        new tvheadend.epgDetails(grid.getStore().getAt(index).data);
+    }
+
+    function createAutoRec() {
+
+        var title = epgStore.baseParams.title ? epgStore.baseParams.title
+                : "<i>Don't care</i>";
+        var channel = epgStore.baseParams.channel ? epgStore.baseParams.channel
+                : "<i>Don't care</i>";
+        var tag = epgStore.baseParams.tag ? epgStore.baseParams.tag
+                : "<i>Don't care</i>";
+        var contenttype = epgStore.baseParams.contenttype ? epgStore.baseParams.contenttype
+                : "<i>Don't care</i>";
+
+        Ext.MessageBox.confirm('Auto Recorder',
+                'This will create an automatic rule that '
+                + 'continuously scans the EPG for programmes '
+                + 'to record that matches this query: ' + '<br><br>'
+                + '<div class="x-smallhdr">Title:</div>' + title + '<br>'
+                + '<div class="x-smallhdr">Channel:</div>' + channel + '<br>'
+                + '<div class="x-smallhdr">Tag:</div>' + tag + '<br>'
+                + '<div class="x-smallhdr">Genre:</div>' + contenttype + '<br>'
+                + '<br>' + 'Currently this will match (and record) '
+                + epgStore.getTotalCount() + ' events. ' + 'Are you sure?',
+                function(button) {
+                    if (button === 'no')
+                        return;
+                    createAutoRec2(epgStore.baseParams);
+                });
+    }
+
+    function createAutoRec2(params) {
+        /* Really do it */
+        params.op = 'createAutoRec';
+        Ext.Ajax.request({
+            url: 'dvr',
+            params: params
+        });
+    }
+
+    return panel;
+};
index ad8f80884c264e4c604de62b755bac103c39dc9d..196e5b2df3c9be5b8a4c9cf33d22f3cc9f78d47f 100644 (file)
 tvheadend.epggrabChannels = new Ext.data.JsonStore({
-       root : 'entries',
-       url : 'epggrab',
-       baseParams : {
-               op : 'channelList'
-       },
-       fields : [ 'id', 'mod', 'name', 'icon', 'number', 'channel', 'mod-id',
-               'mod-name' ]
+    root: 'entries',
+    url: 'epggrab',
+    baseParams: {
+        op: 'channelList'
+    },
+    fields: ['id', 'mod', 'name', 'icon', 'number', 'channel', 'mod-id',
+        'mod-name']
 });
 
 tvheadend.epggrab = function() {
 
-       /* ****************************************************************
-        * Data
-        * ***************************************************************/
-
-       /*
-        * Module lists (I'm sure there is a better way!)
-        */
-       var EPGGRAB_MODULE_INTERNAL = "internal";
-       var EPGGRAB_MODULE_EXTERNAL = "external";
-       var EPGGRAB_MODULE_OTA      = "ota";
-
-       var moduleStore = new Ext.data.JsonStore({
-               root : 'entries',
-               url : 'epggrab',
-               baseParams : {
-                       op : 'moduleList'
-               },
-               autoLoad : true,
-               fields : [ 'id', 'name', 'path', 'type', 'enabled' ]
-       });
-       var internalModuleStore = new Ext.data.Store({
-               recordType : moduleStore.recordType
-       });
-       var externalModuleStore = new Ext.data.Store({
-               recordType : moduleStore.recordType
-       });
-       var otaModuleStore = new Ext.data.Store({
-               recordType : moduleStore.recordType
-       });
-       moduleStore.on('load', function() {
-               moduleStore.filterBy(function(r) {
-                       return r.get('type') == EPGGRAB_MODULE_INTERNAL;
-               });
-               r = new internalModuleStore.recordType({
-                       id : '',
-                       name : 'Disabled'
-               });
-               internalModuleStore.add(r);
-               moduleStore.each(function(r) {
-                       internalModuleStore.add(r.copy());
-               });
-               moduleStore.filterBy(function(r) {
-                       return r.get('type') == EPGGRAB_MODULE_EXTERNAL;
-               });
-               moduleStore.each(function(r) {
-                       externalModuleStore.add(r.copy());
-               });
-               moduleStore.filterBy(function(r) {
-                       return r.get('type') == EPGGRAB_MODULE_OTA;
-               });
-               moduleStore.each(function(r) {
-                       otaModuleStore.add(r.copy());
-               });
-               moduleStore.filterBy(function(r) {
-                       return r.get('type') != EPGGRAB_MODULE_INTERNAL;
-               });
-       });
-
-       /* Enable module in one of the stores (will auto update primary) */
-       function moduleSelect(r, e) {
-               r.set('enabled', e);
-               t = moduleStore.getById(r.id);
-               if (t) t.set('enabled', e);
-       }
-
-       /*
-        * Basic Config
-        */
-
-       var confreader = new Ext.data.JsonReader({
-               root : 'epggrabSettings'
-       }, [ 'module', 'interval', 'channel_rename', 'channel_renumber',
-               'channel_reicon', 'epgdb_periodicsave' ]);
-
-       /* ****************************************************************
-        * Basic Fields
-        * ***************************************************************/
-
-       /*
-        * Module selector
-        */
-       var internalModule = new Ext.form.ComboBox({
-               fieldLabel : 'Module',
-               hiddenName : 'module',
-               width : 300,
-               valueField : 'id',
-               displayField : 'name',
-               forceSelection : true,
-               editable : false,
-               mode : 'local',
-               triggerAction : 'all',
-               store : internalModuleStore
-       });
-
-       /*
-        * Interval selector
-        */
-       var intervalUnits = [ [ 86400, 'Days' ], [ 3600, 'Hours' ],
-               [ 60, 'Minutes' ], [ 1, 'Seconds' ] ];
-       var intervalValue = new Ext.form.NumberField({
-               width : 300,
-               allowNegative : false,
-               allowDecimals : false,
-               minValue : 1,
-               maxValue : 7,
-               value : 1,
-               fieldLabel : 'Grab interval',
-               name : 'intervalValue',
-               listeners : {
-                       'valid' : function(e) {
-                               v = e.getValue() * intervalUnit.getValue();
-                               interval.setValue(v);
-                       }
-               }
-       })
-       var intervalUnit = new Ext.form.ComboBox({
-               name : 'intervalUnit',
-               width : 300,
-               valueField : 'key',
-               displayField : 'value',
-               value : 86400,
-               forceSelection : true,
-               editable : false,
-               triggerAction : 'all',
-               mode : 'local',
-               store : new Ext.data.SimpleStore({
-                       fields : [ 'key', 'value' ],
-                       data : intervalUnits
-               }),
-               listeners : {
-                       'change' : function(e, n, o) {
-                               intervalValue.maxValue = (7 * 86400) / n;
-                               intervalValue.validate();
-                       }
-               }
-       });
-       var interval = new Ext.form.Hidden({
-               name : 'interval',
-               value : 86400,
-               listeners : {
-                       'enable' : function(e) {
-                               v = e.getValue();
-                               for (i = 0; i < intervalUnits.length; i++) {
-                                       u = intervalUnits[i][0];
-                                       if ((v % u) == 0) {
-                                               intervalUnit.setValue(u);
-                                               intervalValue.maxValue = (7 * 86400) / u;
-                                               intervalValue.setValue(v / u);
-                                               intervalValue.validate();
-                                               break;
-                                       }
-                               }
-                       }
-               }
-       });
-
-       /*
-        * Channel handling
-        */
-       var channelRename = new Ext.form.Checkbox({
-               name : 'channel_rename',
-               fieldLabel : 'Update channel name'
-       });
-
-       var channelRenumber = new Ext.form.Checkbox({
-               name : 'channel_renumber',
-               fieldLabel : 'Update channel number'
-       });
-
-       var channelReicon = new Ext.form.Checkbox({
-               name : 'channel_reicon',
-               fieldLabel : 'Update channel icon'
-       });
-
-       var epgPeriodicSave = new Ext.form.NumberField({
-                width : 30,
-                allowNegative : false,
-                allowDecimals : false,
-                minValue : 0,
-                maxValue : 24,
-                value : 0,
-                fieldLabel : 'Periodic save EPG to disk',
-                name : 'epgdb_periodicsave',
+    /* ****************************************************************
+     * Data
+     * ***************************************************************/
+
+    /*
+     * Module lists (I'm sure there is a better way!)
+     */
+    var EPGGRAB_MODULE_INTERNAL = "internal";
+    var EPGGRAB_MODULE_EXTERNAL = "external";
+    var EPGGRAB_MODULE_OTA = "ota";
+
+    var moduleStore = new Ext.data.JsonStore({
+        root: 'entries',
+        url: 'epggrab',
+        baseParams: {
+            op: 'moduleList'
+        },
+        autoLoad: true,
+        fields: ['id', 'name', 'path', 'type', 'enabled']
+    });
+    var internalModuleStore = new Ext.data.Store({
+        recordType: moduleStore.recordType
+    });
+    var externalModuleStore = new Ext.data.Store({
+        recordType: moduleStore.recordType
+    });
+    var otaModuleStore = new Ext.data.Store({
+        recordType: moduleStore.recordType
+    });
+    moduleStore.on('load', function() {
+        moduleStore.filterBy(function(r) {
+            return r.get('type') === EPGGRAB_MODULE_INTERNAL;
         });
+        r = new internalModuleStore.recordType({
+            id: '',
+            name: 'Disabled'
+        });
+        internalModuleStore.add(r);
+        moduleStore.each(function(r) {
+            internalModuleStore.add(r.copy());
+        });
+        moduleStore.filterBy(function(r) {
+            return r.get('type') === EPGGRAB_MODULE_EXTERNAL;
+        });
+        moduleStore.each(function(r) {
+            externalModuleStore.add(r.copy());
+        });
+        moduleStore.filterBy(function(r) {
+            return r.get('type') === EPGGRAB_MODULE_OTA;
+        });
+        moduleStore.each(function(r) {
+            otaModuleStore.add(r.copy());
+        });
+        moduleStore.filterBy(function(r) {
+            return r.get('type') !== EPGGRAB_MODULE_INTERNAL;
+        });
+    });
+
+    /* Enable module in one of the stores (will auto update primary) */
+    function moduleSelect(r, e) {
+        r.set('enabled', e);
+        t = moduleStore.getById(r.id);
+        if (t)
+            t.set('enabled', e);
+    }
+
+    /*
+     * Basic Config
+     */
+
+    var confreader = new Ext.data.JsonReader({
+        root: 'epggrabSettings'
+    }, ['module', 'interval', 'channel_rename', 'channel_renumber',
+        'channel_reicon', 'epgdb_periodicsave']);
+
+    /* ****************************************************************
+     * Basic Fields
+     * ***************************************************************/
+
+    /*
+     * Module selector
+     */
+    var internalModule = new Ext.form.ComboBox({
+        fieldLabel: 'Module',
+        hiddenName: 'module',
+        width: 300,
+        valueField: 'id',
+        displayField: 'name',
+        forceSelection: true,
+        editable: false,
+        mode: 'local',
+        triggerAction: 'all',
+        store: internalModuleStore
+    });
+
+    /*
+     * Interval selector
+     */
+    var intervalUnits = [[86400, 'Days'], [3600, 'Hours'],
+        [60, 'Minutes'], [1, 'Seconds']];
+    var intervalValue = new Ext.form.NumberField({
+        width: 300,
+        allowNegative: false,
+        allowDecimals: false,
+        minValue: 1,
+        maxValue: 7,
+        value: 1,
+        fieldLabel: 'Grab interval',
+        name: 'intervalValue',
+        listeners: {
+            'valid': function(e) {
+                v = e.getValue() * intervalUnit.getValue();
+                interval.setValue(v);
+            }
+        }
+    });
+    var intervalUnit = new Ext.form.ComboBox({
+        name: 'intervalUnit',
+        width: 300,
+        valueField: 'key',
+        displayField: 'value',
+        value: 86400,
+        forceSelection: true,
+        editable: false,
+        triggerAction: 'all',
+        mode: 'local',
+        store: new Ext.data.SimpleStore({
+            fields: ['key', 'value'],
+            data: intervalUnits
+        }),
+        listeners: {
+            'change': function(e, n, o) {
+                intervalValue.maxValue = (7 * 86400) / n;
+                intervalValue.validate();
+            }
+        }
+    });
+    var interval = new Ext.form.Hidden({
+        name: 'interval',
+        value: 86400,
+        listeners: {
+            'enable': function(e) {
+                v = e.getValue();
+                for (i = 0; i < intervalUnits.length; i++) {
+                    u = intervalUnits[i][0];
+                    if ((v % u) === 0) {
+                        intervalUnit.setValue(u);
+                        intervalValue.maxValue = (7 * 86400) / u;
+                        intervalValue.setValue(v / u);
+                        intervalValue.validate();
+                        break;
+                    }
+                }
+            }
+        }
+    });
+
+    /*
+     * Channel handling
+     */
+    var channelRename = new Ext.form.Checkbox({
+        name: 'channel_rename',
+        fieldLabel: 'Update channel name'
+    });
+
+    var channelRenumber = new Ext.form.Checkbox({
+        name: 'channel_renumber',
+        fieldLabel: 'Update channel number'
+    });
+
+    var channelReicon = new Ext.form.Checkbox({
+        name: 'channel_reicon',
+        fieldLabel: 'Update channel icon'
+    });
+
+    var epgPeriodicSave = new Ext.form.NumberField({
+        width: 30,
+        allowNegative: false,
+        allowDecimals: false,
+        minValue: 0,
+        maxValue: 24,
+        value: 0,
+        fieldLabel: 'Periodic save EPG to disk',
+        name: 'epgdb_periodicsave'
+    });
+
+    /*
+     * Simple fields
+     */
+    var simplePanel = new Ext.form.FieldSet({
+        title: 'General Config',
+        width: 700,
+        autoHeight: true,
+        collapsible: true,
+        items: [channelRename, channelRenumber, channelReicon, epgPeriodicSave]
+    });
+
+    /*
+     * Internal grabber
+     */
+    var internalPanel = new Ext.form.FieldSet({
+        title: 'Internal Grabber',
+        width: 700,
+        autoHeight: true,
+        collapsible: true,
+        items: [interval, internalModule, intervalValue, intervalUnit]
+    });
+
+    /* ****************************************************************
+     * Advanced Fields
+     * ***************************************************************/
+
+    /*
+     * External modules
+     */
+    var externalSelectionModel = new Ext.grid.CheckboxSelectionModel({
+        singleSelect: false,
+        listeners: {
+            'rowselect': function(s, ri, r) {
+                moduleSelect(r, 1);
+            },
+            'rowdeselect': function(s, ri, r) {
+                moduleSelect(r, 0);
+            }
+        }
+    });
+
+    var externalColumnModel = new Ext.grid.ColumnModel([externalSelectionModel,
+        {
+            header: 'Module',
+            dataIndex: 'name',
+            width: 200,
+            sortable: false
+        }, {
+            header: 'Path',
+            dataIndex: 'path',
+            width: 300,
+            sortable: false
+        }]);
+
+    var externalGrid = new Ext.grid.EditorGridPanel({
+        store: externalModuleStore,
+        cm: externalColumnModel,
+        sm: externalSelectionModel,
+        width: 600,
+        height: 150,
+        frame: false,
+        viewConfig: {
+            forceFit: true
+        },
+        iconCls: 'icon-grid'
+    });
+
+    var externalPanel = new Ext.form.FieldSet({
+        title: 'External Interfaces',
+        width: 700,
+        autoHeight: true,
+        collapsible: true,
+        collapsed: true,
+        items: [externalGrid]
+    });
+
+    /*
+     * OTA modules
+     */
+
+    var otaSelectionModel = new Ext.grid.CheckboxSelectionModel({
+        singleSelect: false,
+        listeners: {
+            'rowselect': function(s, ri, r) {
+                moduleSelect(r, 1);
+            },
+            'rowdeselect': function(s, ri, r) {
+                moduleSelect(r, 0);
+            }
+        }
+    });
+
+    var otaColumnModel = new Ext.grid.ColumnModel([otaSelectionModel, {
+            header: 'Module',
+            dataIndex: 'name',
+            width: 200,
+            sortable: false
+        }]);
+
+    var otaGrid = new Ext.grid.EditorGridPanel({
+        store: otaModuleStore,
+        cm: otaColumnModel,
+        sm: otaSelectionModel,
+        width: 600,
+        height: 150,
+        frame: false,
+        viewConfig: {
+            forceFit: true
+        },
+        iconCls: 'icon-grid'
+    });
+
+    var otaPanel = new Ext.form.FieldSet({
+        title: 'Over-the-air Grabbers',
+        width: 700,
+        autoHeight: true,
+        collapsible: true,
+        collapsed: true,
+        items: [otaGrid]
+    });
+
+    /* ****************************************************************
+     * Form
+     * ***************************************************************/
+
+    var saveButton = new Ext.Button({
+        text: "Save configuration",
+        tooltip: 'Save changes made to configuration below',
+        iconCls: 'save',
+        handler: saveChanges
+    });
+
+    var helpButton = new Ext.Button({
+        text: 'Help',
+        handler: function() {
+            new tvheadend.help('EPG Grab Configuration', 'config_epggrab.html');
+        }
+    });
+
+    var confpanel = new Ext.FormPanel({
+        title: 'EPG Grabber',
+        iconCls: 'xml',
+        border: false,
+        bodyStyle: 'padding:15px',
+        labelAlign: 'left',
+        labelWidth: 150,
+        waitMsgTarget: true,
+        reader: confreader,
+        layout: 'form',
+        defaultType: 'textfield',
+        autoHeight: true,
+        items: [simplePanel, internalPanel, otaPanel, externalPanel],
+        tbar: [saveButton, '->', helpButton]
+    });
+
+    /* ****************************************************************
+     * Load/Save
+     * ***************************************************************/
+
+    /* HACK: get display working */
+    externalGrid.on('render', function() {
+        delay = new Ext.util.DelayedTask(function() {
+            rows = [];
+            externalModuleStore.each(function(r) {
+                if (r.get('enabled'))
+                    rows.push(r);
+            });
+            externalSelectionModel.selectRecords(rows);
+        });
+        delay.delay(100);
+    });
+    otaGrid.on('render', function() {
+        delay = new Ext.util.DelayedTask(function() {
+            rows = [];
+            otaModuleStore.each(function(r) {
+                if (r.get('enabled'))
+                    rows.push(r);
+            });
+            otaSelectionModel.selectRecords(rows);
+        });
+        delay.delay(100);
+    });
+
+    confpanel.on('render', function() {
+
+        /* Hack to get display working */
+        delay = new Ext.util.DelayedTask(function() {
+            simplePanel.doLayout(false);
+            internalPanel.doLayout(false);
+            externalPanel.doLayout(false);
+            otaPanel.doLayout(false);
+        });
+        delay.delay(100);
+
+        confpanel.getForm().load({
+            url: 'epggrab',
+            params: {
+                op: 'loadSettings'
+            },
+            success: function(form, action) {
+                confpanel.enable();
+            }
+        });
+    });
+
+    function saveChanges() {
+        mods = [];
+        moduleStore.each(function(r) {
+            mods.push({
+                id: r.get('id'),
+                enabled: r.get('enabled') ? 1 : 0
+            });
+        });
+        mods = Ext.util.JSON.encode(mods);
+        confpanel.getForm().submit({
+            url: 'epggrab',
+            params: {
+                op: 'saveSettings',
+                external: mods
+            },
+            waitMsg: 'Saving Data...',
+            success: function(form, action) {
+                externalModuleStore.commitChanges();
+            },
+            failure: function(form, action) {
+                Ext.Msg.alert('Save failed', action.result.errormsg);
+            }
+        });
+    }
 
-       /*
-        * Simple fields
-        */
-       var simplePanel = new Ext.form.FieldSet({
-               title : 'General Config',
-               width : 700,
-               autoHeight : true,
-               collapsible : true,
-               items : [ channelRename, channelRenumber, channelReicon, epgPeriodicSave ]
-       });
-
-       /*
-        * Internal grabber
-        */
-       var internalPanel = new Ext.form.FieldSet({
-               title : 'Internal Grabber',
-               width : 700,
-               autoHeight : true,
-               collapsible : true,
-               items : [ interval, internalModule, intervalValue, intervalUnit ]
-       });
-
-       /* ****************************************************************
-        * Advanced Fields
-        * ***************************************************************/
-
-       /*
-        * External modules
-        */
-       var externalSelectionModel = new Ext.grid.CheckboxSelectionModel({
-               singleSelect : false,
-               listeners : {
-                       'rowselect' : function(s, ri, r) {
-                               moduleSelect(r, 1);
-                       },
-                       'rowdeselect' : function(s, ri, r) {
-                               moduleSelect(r, 0);
-                       }
-               }
-       });
-
-       var externalColumnModel = new Ext.grid.ColumnModel([ externalSelectionModel,
-               {
-                       header : 'Module',
-                       dataIndex : 'name',
-                       width : 200,
-                       sortable : false
-               }, {
-                       header : 'Path',
-                       dataIndex : 'path',
-                       width : 300,
-                       sortable : false
-               } ]);
-
-       var externalGrid = new Ext.grid.EditorGridPanel({
-               store : externalModuleStore,
-               cm : externalColumnModel,
-               sm : externalSelectionModel,
-               width : 600,
-               height : 150,
-               frame : false,
-               viewConfig : {
-                       forceFit : true
-               },
-               iconCls : 'icon-grid'
-       });
-
-       var externalPanel = new Ext.form.FieldSet({
-               title : 'External Interfaces',
-               width : 700,
-               autoHeight : true,
-               collapsible : true,
-               collapsed : true,
-               items : [ externalGrid ]
-       });
-
-       /*
-        * OTA modules
-        */
-
-       var otaSelectionModel = new Ext.grid.CheckboxSelectionModel({
-               singleSelect : false,
-               listeners : {
-                       'rowselect' : function(s, ri, r) {
-                               moduleSelect(r, 1);
-                       },
-                       'rowdeselect' : function(s, ri, r) {
-                               moduleSelect(r, 0);
-                       }
-               }
-       });
-
-       var otaColumnModel = new Ext.grid.ColumnModel([ otaSelectionModel, {
-               header : 'Module',
-               dataIndex : 'name',
-               width : 200,
-               sortable : false
-       } ]);
-
-       var otaGrid = new Ext.grid.EditorGridPanel({
-               store : otaModuleStore,
-               cm : otaColumnModel,
-               sm : otaSelectionModel,
-               width : 600,
-               height : 150,
-               frame : false,
-               viewConfig : {
-                       forceFit : true
-               },
-               iconCls : 'icon-grid'
-       });
-
-       var otaPanel = new Ext.form.FieldSet({
-               title : 'Over-the-air Grabbers',
-               width : 700,
-               autoHeight : true,
-               collapsible : true,
-               collapsed : true,
-               items : [ otaGrid ]
-       });
-
-       /* ****************************************************************
-        * Form
-        * ***************************************************************/
-
-       var saveButton = new Ext.Button({
-               text : "Save configuration",
-               tooltip : 'Save changes made to configuration below',
-               iconCls : 'save',
-               handler : saveChanges
-       });
-
-       var helpButton = new Ext.Button({
-               text : 'Help',
-               handler : function() {
-                       new tvheadend.help('EPG Grab Configuration', 'config_epggrab.html');
-               }
-       });
-
-       var confpanel = new Ext.FormPanel({
-               title : 'EPG Grabber',
-               iconCls : 'xml',
-               border : false,
-               bodyStyle : 'padding:15px',
-               labelAlign : 'left',
-               labelWidth : 150,
-               waitMsgTarget : true,
-               reader : confreader,
-               layout : 'form',
-               defaultType : 'textfield',
-               autoHeight : true,
-               items : [ simplePanel, internalPanel, otaPanel, externalPanel ],
-               tbar : [ saveButton, '->', helpButton ]
-       });
-
-       /* ****************************************************************
-        * Load/Save
-        * ***************************************************************/
-
-       /* HACK: get display working */
-       externalGrid.on('render', function() {
-               delay = new Ext.util.DelayedTask(function() {
-                       rows = [];
-                       externalModuleStore.each(function(r) {
-                               if (r.get('enabled')) rows.push(r);
-                       });
-                       externalSelectionModel.selectRecords(rows);
-               });
-               delay.delay(100);
-       });
-       otaGrid.on('render', function() {
-               delay = new Ext.util.DelayedTask(function() {
-                       rows = [];
-                       otaModuleStore.each(function(r) {
-                               if (r.get('enabled')) rows.push(r);
-                       });
-                       otaSelectionModel.selectRecords(rows);
-               });
-               delay.delay(100);
-       });
-
-       confpanel.on('render', function() {
-
-               /* Hack to get display working */
-               delay = new Ext.util.DelayedTask(function() {
-                       simplePanel.doLayout(false);
-                       internalPanel.doLayout(false);
-                       externalPanel.doLayout(false);
-                       otaPanel.doLayout(false);
-               });
-               delay.delay(100);
-
-               confpanel.getForm().load({
-                       url : 'epggrab',
-                       params : {
-                               op : 'loadSettings'
-                       },
-                       success : function(form, action) {
-                               confpanel.enable();
-                       }
-               });
-       });
-
-       function saveChanges() {
-               mods = [];
-               moduleStore.each(function(r) {
-                       mods.push({
-                               id : r.get('id'),
-                               enabled : r.get('enabled') ? 1 : 0
-                       });
-               });
-               mods = Ext.util.JSON.encode(mods);
-               confpanel.getForm().submit({
-                       url : 'epggrab',
-                       params : {
-                               op : 'saveSettings',
-                               external : mods
-                       },
-                       waitMsg : 'Saving Data...',
-                       success : function(form, action) {
-                               externalModuleStore.commitChanges();
-                       },
-                       failure : function(form, action) {
-                               Ext.Msg.alert('Save failed', action.result.errormsg);
-                       }
-               });
-       }
-
-       return confpanel;
-}
+    return confpanel;
+};
index 781b46ad5334d7dcffb639130d9f25731dd1c660..a7ed387334063ae616c37e1d91fe77a28126b203 100644 (file)
 
 tvheadend.esfilter_tab = function(panel)
 {
-  tvheadend.idnode_grid(panel, {
-    url      : 'api/esfilter/video',
-    comet    : 'esfilter_video',
-    titleS   : 'Video Stream Filter',
-    titleP   : 'Video Stream Filters',
-    tabIndex : 0,
-    add      : {
-      url : 'api/esfilter/video',
-      create : {}
-    },
-    del      : true,
-    move     : true,
-    help     : function() {
-      new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
-    }
-  });
+    tvheadend.idnode_grid(panel, {
+        url: 'api/esfilter/video',
+        comet: 'esfilter_video',
+        titleS: 'Video Stream Filter',
+        titleP: 'Video Stream Filters',
+        tabIndex: 0,
+        add: {
+            url: 'api/esfilter/video',
+            create: {}
+        },
+        del: true,
+        move: true,
+        help: function() {
+            new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
+        }
+    });
 
-  tvheadend.idnode_grid(panel, {
-    url      : 'api/esfilter/audio',
-    comet    : 'esfilter_audio',
-    titleS   : 'Audio Stream Filter',
-    titleP   : 'Audio Stream Filters',
-    tabIndex : 1,
-    add      : {
-      url : 'api/esfilter/audio',
-      create : {}
-    },
-    del      : true,
-    move     : true,
-    help     : function() {
-      new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
-    }
-  });
+    tvheadend.idnode_grid(panel, {
+        url: 'api/esfilter/audio',
+        comet: 'esfilter_audio',
+        titleS: 'Audio Stream Filter',
+        titleP: 'Audio Stream Filters',
+        tabIndex: 1,
+        add: {
+            url: 'api/esfilter/audio',
+            create: {}
+        },
+        del: true,
+        move: true,
+        help: function() {
+            new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
+        }
+    });
 
-  tvheadend.idnode_grid(panel, {
-    url      : 'api/esfilter/teletext',
-    comet    : 'esfilter_teletext',
-    titleS   : 'Teletext Stream Filter',
-    titleP   : 'Teletext Stream Filters',
-    tabIndex : 2,
-    add      : {
-      url : 'api/esfilter/teletext',
-      create : {}
-    },
-    del      : true,
-    move     : true,
-    help     : function() {
-      new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
-    }
-  });
+    tvheadend.idnode_grid(panel, {
+        url: 'api/esfilter/teletext',
+        comet: 'esfilter_teletext',
+        titleS: 'Teletext Stream Filter',
+        titleP: 'Teletext Stream Filters',
+        tabIndex: 2,
+        add: {
+            url: 'api/esfilter/teletext',
+            create: {}
+        },
+        del: true,
+        move: true,
+        help: function() {
+            new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
+        }
+    });
 
-  tvheadend.idnode_grid(panel, {
-    url      : 'api/esfilter/subtit',
-    comet    : 'esfilter_subtit',
-    titleS   : 'Subtitle Stream Filter',
-    titleP   : 'Subtitle Stream Filters',
-    tabIndex : 3,
-    add      : {
-      url : 'api/esfilter/subtit',
-      create : {}
-    },
-    del      : true,
-    move     : true,
-    help     : function() {
-      new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
-    }
-  });
+    tvheadend.idnode_grid(panel, {
+        url: 'api/esfilter/subtit',
+        comet: 'esfilter_subtit',
+        titleS: 'Subtitle Stream Filter',
+        titleP: 'Subtitle Stream Filters',
+        tabIndex: 3,
+        add: {
+            url: 'api/esfilter/subtit',
+            create: {}
+        },
+        del: true,
+        move: true,
+        help: function() {
+            new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
+        }
+    });
 
-  tvheadend.idnode_grid(panel, {
-    url      : 'api/esfilter/ca',
-    comet    : 'esfilter_ca',
-    titleS   : 'CA Stream Filter',
-    titleP   : 'CA Stream Filters',
-    tabIndex : 4,
-    add      : {
-      url : 'api/esfilter/ca',
-      create : {}
-    },
-    del      : true,
-    move     : true,
-    help     : function() {
-      new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
-    }
-  });
+    tvheadend.idnode_grid(panel, {
+        url: 'api/esfilter/ca',
+        comet: 'esfilter_ca',
+        titleS: 'CA Stream Filter',
+        titleP: 'CA Stream Filters',
+        tabIndex: 4,
+        add: {
+            url: 'api/esfilter/ca',
+            create: {}
+        },
+        del: true,
+        move: true,
+        help: function() {
+            new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
+        }
+    });
 
-  tvheadend.idnode_grid(panel, {
-    url      : 'api/esfilter/other',
-    comet    : 'esfilter_other',
-    titleS   : 'Other Stream Filter',
-    titleP   : 'Other Stream Filters',
-    tabIndex : 5,
-    add      : {
-      url : 'api/esfilter/other',
-      create : {}
-    },
-    del      : true,
-    move     : true,
-    help     : function() {
-      new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
-    }
-  });
-}
+    tvheadend.idnode_grid(panel, {
+        url: 'api/esfilter/other',
+        comet: 'esfilter_other',
+        titleS: 'Other Stream Filter',
+        titleP: 'Other Stream Filters',
+        tabIndex: 5,
+        add: {
+            url: 'api/esfilter/other',
+            create: {}
+        },
+        del: true,
+        move: true,
+        help: function() {
+            new tvheadend.help('Elementary Stream Filter', 'config_esfilter.html');
+        }
+    });
+};
index 810ed5df11d23fa3f65885f225c7d768cc8ce459..6eb6767fe209eb5d02824433ae66f8c48b9e5069 100644 (file)
@@ -6,7 +6,7 @@
  * http://extjs.com/license
  */ 
 #header {
-       font-family: tahoma,arial;
+    font-family: tahoma,arial;
     background-color: #507AAA;
     color: #F8F8F8;
     height: 5.3em;
@@ -15,8 +15,8 @@
     background: url("../img/bg-header.png") repeat-x scroll 0 0 transparent;
     height: 45px;
 }
- #header > h1 {
+
+#header > h1 {
     background: url("../img/logo.png") no-repeat scroll 10px 20% transparent;
     color: #E0E0E0;
     font-size: 22px;
 }
 
 .x-tree-col {
-       float: left;
-       overflow: hidden;
-       padding: 0 1px;
-       zoom: 1;
+    float: left;
+    overflow: hidden;
+    padding: 0 1px;
+    zoom: 1;
 }
 
 .x-tree-col-text,.x-tree-hd-text {
-       overflow: hidden;
-       -o-text-overflow: ellipsis;
-       text-overflow: ellipsis;
-       padding: 3px 3px 3px 5px;
-       white-space: nowrap;
-       font: normal 11px arial, tahoma, helvetica, sans-serif;
+    overflow: hidden;
+    -o-text-overflow: ellipsis;
+    text-overflow: ellipsis;
+    padding: 3px 3px 3px 5px;
+    white-space: nowrap;
+    font: normal 11px arial, tahoma, helvetica, sans-serif;
 }
 
 .x-tree-headers {
-       background: #f9f9f9
-               url(../extjs/resources/images/default/grid/grid3-hrow.gif) repeat-x 0
-               bottom;
-       cursor: default;
-       zoom: 1;
+    background: #f9f9f9
+        url(../extjs/resources/images/default/grid/grid3-hrow.gif) repeat-x 0
+        bottom;
+    cursor: default;
+    zoom: 1;
 }
 
 .x-tree-hd {
-       float: left;
-       overflow: hidden;
-       border-left: 1px solid #eee;
-       border-right: 1px solid #d0d0d0;
+    float: left;
+    overflow: hidden;
+    border-left: 1px solid #eee;
+    border-right: 1px solid #d0d0d0;
 }
 
 .ux-mselect {
-       overflow: auto;
-       background: white;
-       position: relative; /* for calculating scroll offsets */
-       zoom: 1;
-       overflow: auto;
+    overflow: auto;
+    background: white;
+    position: relative; /* for calculating scroll offsets */
+    zoom: 1;
+    overflow: auto;
 }
 
 .ux-mselect-item {
-       font: normal 12px tahoma, arial, helvetica, sans-serif;
-       padding: 2px;
-       border: 1px solid #fff;
-       white-space: nowrap;
-       cursor: pointer;
+    font: normal 12px tahoma, arial, helvetica, sans-serif;
+    padding: 2px;
+    border: 1px solid #fff;
+    white-space: nowrap;
+    cursor: pointer;
 }
 
 .ux-mselect-selected {
-       border: 1px dotted #a3bae9 !important;
-       background: #DFE8F6;
-       cursor: pointer;
+    border: 1px dotted #a3bae9 !important;
+    background: #DFE8F6;
+    cursor: pointer;
 }
 
 .x-view-drag-insert-above {
-       border-top: 1px dotted #3366cc;
+    border-top: 1px dotted #3366cc;
 }
 
 .x-view-drag-insert-below {
-       border-bottom: 1px dotted #3366cc;
+    border-bottom: 1px dotted #3366cc;
 }
 
 .x-grid3-progresscol .x-grid3-cell-inner {
-       padding: 0px 0px 0px 5px;
+    padding: 0px 0px 0px 5px;
 }
 
 .x-grid3-progresscol .x-progress-bar {
-       height: 16px;
+    height: 16px;
 }
 
 .x-grid3-progresscol .x-progress-inner {
-       height: 16px;
+    height: 16px;
 }
 
 .x-grid3-progresscol .x-progress-text-front-ie6 {
-       padding: 2.5px 5px;
+    padding: 2.5px 5px;
 }
 
 .x-grid3-progresscol .x-progress-text-front {
-       padding: 2px 5px;
+    padding: 2px 5px;
 }
 
 .x-progress-bar-red,.x-progress-bar-orange,.x-progress-bar-green {
-       border-bottom: 1px solid #7fa9e4;
-       float: left;
-       height: 16px;
+    border-bottom: 1px solid #7fa9e4;
+    float: left;
+    height: 16px;
 }
 
 .x-progress-bar-red {
-       background: #ff0000 url(../icons/progress-bg-red.gif) repeat-x scroll
-               left center;
-       border-top: 1px solid #ecb7ad;
+    background: #ff0000 url(../icons/progress-bg-red.gif) repeat-x scroll
+        left center;
+    border-top: 1px solid #ecb7ad;
 }
 
 .x-progress-bar-orange {
-       background: #9cbfee url(../icons/progress-bg-orange.gif) repeat-x scroll
-               left center;
-       border-right: 1px solid #deab7e;
-       border-top: 1px solid #d7b290;
+    background: #9cbfee url(../icons/progress-bg-orange.gif) repeat-x scroll
+        left center;
+    border-right: 1px solid #deab7e;
+    border-top: 1px solid #d7b290;
 }
 
 .x-progress-bar-green {
-       background: #00ff00 url(../icons/progress-bg-green.gif) repeat-x scroll
-               left center;
-       border-right: 1px solid #5bd976;
-       border-top: 1px solid #79e18f;
+    background: #00ff00 url(../icons/progress-bg-green.gif) repeat-x scroll
+        left center;
+    border-right: 1px solid #5bd976;
+    border-top: 1px solid #79e18f;
 }
 
 .tvh-grid-unset {
-       color: #888;
-       font-style: italic;
+    color: #888;
+    font-style: italic;
 }
 
 .add {
-       background-image: url(../icons/add.png) !important;
+    background-image: url(../icons/add.png) !important;
 }
 
 .option {
-       background-image: url(../icons/plugin.png) !important;
+    background-image: url(../icons/plugin.png) !important;
 }
 
 .remove {
-       background-image: url(../icons/delete.png) !important;
+    background-image: url(../icons/delete.png) !important;
 }
 
 .moveup {
-       background-image: url(../icons/arrow_up.png) !important;
+    background-image: url(../icons/arrow_up.png) !important;
 }
 
 .movedown {
-       background-image: url(../icons/arrow_down.png) !important;
+    background-image: url(../icons/arrow_down.png) !important;
 }
 
 .save {
-       background-image: url(../icons/save.png) !important;
+    background-image: url(../icons/save.png) !important;
 }
 
 .rec {
-       background-image: url(../icons/rec.png) !important;
+    background-image: url(../icons/rec.png) !important;
 }
 
 .info {
-       background-image: url(../icons/information.png) !important;
+    background-image: url(../icons/information.png) !important;
 }
 
 .undo {
-       background-image: url(../icons/undo.png) !important;
+    background-image: url(../icons/undo.png) !important;
 }
 
 .edit {
-       background-image: url(../icons/edit.png) !important;
+    background-image: url(../icons/edit.png) !important;
 }
 
 .key {
-       background-image: url(../icons/key.png) !important;
+    background-image: url(../icons/key.png) !important;
 }
 
 .tags {
-       background-image: url(../icons/tag_blue.png) !important;
+    background-image: url(../icons/tag_blue.png) !important;
 }
 
 .xml {
-       background-image: url(../icons/tag.png) !important;
+    background-image: url(../icons/tag.png) !important;
 }
 
 .drive {
-       background-image: url(../icons/drive.png) !important;
+    background-image: url(../icons/drive.png) !important;
 }
 
 .group {
-       background-image: url(../icons/group.png) !important;
+    background-image: url(../icons/group.png) !important;
 }
 
 .hardware {
-       background-image: url(../icons/pci.png) !important;
+    background-image: url(../icons/pci.png) !important;
 }
 
 .television {
-       background-image: url(../icons/television.png) !important;
+    background-image: url(../icons/television.png) !important;
 }
 
 .eye {
-       background-image: url(../icons/eye.png) !important;
+    background-image: url(../icons/eye.png) !important;
 }
 
 .control_play {
-       background-image: url(../icons/control_play.png) !important;
+    background-image: url(../icons/control_play.png) !important;
 }
 
 .control_pause {
-       background-image: url(../icons/control_pause.png) !important;
+    background-image: url(../icons/control_pause.png) !important;
 }
 
 .control_stop {
-       background-image: url(../icons/control_stop.png) !important;
+    background-image: url(../icons/control_stop.png) !important;
 }
 
 .control_volume {
-       background-image: url(../icons/sound.png) !important;
+    background-image: url(../icons/sound.png) !important;
 }
 
 .control_fullscreen {
-       background-image: url(../icons/arrow_out.png) !important;
+    background-image: url(../icons/arrow_out.png) !important;
 }
 
 .newspaper {
-       background-image: url(../icons/newspaper.png) !important;
+    background-image: url(../icons/newspaper.png) !important;
 }
 
 .clock {
-       background-image: url(../icons/clock.png) !important;
+    background-image: url(../icons/clock.png) !important;
 }
 
 .exclamation {
-       background-image: url(../icons/exclamation.png) !important;
+    background-image: url(../icons/exclamation.png) !important;
 }
 
 .wrench {
-       background-image: url(../icons/wrench.png) !important;
+    background-image: url(../icons/wrench.png) !important;
 }
 
 .wand {
-       background-image: url(../icons/wand.png) !important;
+    background-image: url(../icons/wand.png) !important;
 }
 
 .merge {
-       background-image: url(../icons/arrow_join.png) !important;
+    background-image: url(../icons/arrow_join.png) !important;
 }
 
 .iptv {
-       background-image: url(../icons/world.png) !important;
+    background-image: url(../icons/world.png) !important;
 }
 
 .clone {
-       background-image: url(../icons/layers.png) !important;
+    background-image: url(../icons/layers.png) !important;
 }
 
 .scheduled {
-       background-image: url(../icons/clock.png) !important;
+    background-image: url(../icons/clock.png) !important;
 }
 
 .recordingError {
-       background-image: url(../icons/exclamation.png) !important;
+    background-image: url(../icons/exclamation.png) !important;
 }
 
 .completed {
-       background-image: url(../icons/tick.png) !important;
+    background-image: url(../icons/tick.png) !important;
 }
 
 .completedError {
-       background-image: url(../icons/exclamation.png) !important;
+    background-image: url(../icons/exclamation.png) !important;
 }
 
 .recording {
-       background-image: url(../icons/rec.png) !important;
+    background-image: url(../icons/rec.png) !important;
 }
 
 .bullet_add {
-       background-image: url(../icons/bullet_add.png) !important;
+    background-image: url(../icons/bullet_add.png) !important;
 }
 
 .arrow_up {
-       background-image: url(../icons/arrow_up.png) !important;
+    background-image: url(../icons/arrow_up.png) !important;
 }
 
 .arrow_down {
-       background-image: url(../icons/arrow_down.png) !important;
+    background-image: url(../icons/arrow_down.png) !important;
 }
 
 .arrow_switch {
-       background-image: url(../icons/arrow_switch.png) !important;
+    background-image: url(../icons/arrow_switch.png) !important;
 
 }
 
 .stream_config {
-       background-image: url(../icons/film_edit.png) !important;
+    background-image: url(../icons/film_edit.png) !important;
 }
 
 .x-smallhdr {
-       float: left;
-       width: 100px;
+    float: left;
+    width: 100px;
 }
 
 .x-epg-title {
-       margin: 5px;
-       font: normal 15px arial, tahoma, helvetica, sans-serif;
-       font-weight: bold;
+    margin: 5px;
+    font: normal 15px arial, tahoma, helvetica, sans-serif;
+    font-weight: bold;
 }
 
 .x-epg-subtitle {
-       margin: 5px;
-       font: normal 12px arial, tahoma, helvetica, sans-serif;
-       font-weight: bold;
+    margin: 5px;
+    font: normal 12px arial, tahoma, helvetica, sans-serif;
+    font-weight: bold;
 }
 
 .x-epg-desc {
-       margin: 5px;
+    margin: 5px;
 }
 
 .x-epg-chicon {
-       float: right;
-       margin: 5px;
-       max-width: 132px;
-       max-height: 99px;
+    float: right;
+    margin: 5px;
+    max-width: 132px;
+    max-height: 99px;
 }
 
 .x-epg-meta {
-       margin: 5px;
+    margin: 5px;
 }
 
 .hts-t-info {
-       float: left;
-       width: 100px;
+    float: left;
+    width: 100px;
 }
 
 .hts-doc-text {
-       font: normal 11px verdana;
-       padding: 5px;
+    font: normal 11px verdana;
+    padding: 5px;
 }
 
 .hts-doc-text dt {
-       padding-top: 10px;
-       font-weight: bold;
+    padding-top: 10px;
+    font-weight: bold;
 }
 
 .hts-doc-text dl {
-       padding-left: 10px;
-       padding-bottom: 10px;
+    padding-left: 10px;
+    padding-bottom: 10px;
 }
 
 .hts-doc-text li {
-       padding-top: 5px;
-       padding-bottom: 5px;
+    padding-top: 5px;
+    padding-bottom: 5px;
 }
 
 .hts-doc-text img {
-       padding: 10px;
+    padding: 10px;
 }
 
 .tv-video-player {
 }
 
 .about-title {
-       font-size: 24px;
-       font-weight: bold;
+    font-size: 24px;
+    font-weight: bold;
 }
 
 /** vim: ts=4:sw=4:nu:fdc=4:nospell 
 
 /* styles for rows */
 .ux-row-action-cell .x-grid3-cell-inner {
-       padding: 1px 0 0 0;
+    padding: 1px 0 0 0;
 }
 
 .ux-row-action-item {
-       float: left;
-       min-width: 16px;
-       height: 16px;
-       background-repeat: no-repeat;
-       margin: 0 5px 0 0;
-       cursor: pointer;
-       overflow: hidden;
+    float: left;
+    min-width: 16px;
+    height: 16px;
+    background-repeat: no-repeat;
+    margin: 0 5px 0 0;
+    cursor: pointer;
+    overflow: hidden;
 }
 
 .ext-ie .ux-row-action-item {
-       width: 16px;
+    width: 16px;
 }
 
 .ext-ie .ux-row-action-text {
-       width: auto;
+    width: auto;
 }
 
 .ux-row-action-item span {
-       vertical-align: middle;
-       padding: 0 0 0 20px;
-       line-height: 18px;
+    vertical-align: middle;
+    padding: 0 0 0 20px;
+    line-height: 18px;
 }
 
 .ext-ie .ux-row-action-item span {
-       width: auto;
+    width: auto;
 }
 
 /* styles for groups */
 .x-grid-group-hd div {
-       position: relative;
-       height: 16px;
+    position: relative;
+    height: 16px;
 }
 
 .ux-grow-action-item {
-       min-width: 16px;
-       height: 16px;
-       background-repeat: no-repeat;
-       background-position: 0 50% ! important;
-       margin: 0 0 0 4px;
-       padding: 0 ! important;
-       cursor: pointer;
-       float: left;
+    min-width: 16px;
+    height: 16px;
+    background-repeat: no-repeat;
+    background-position: 0 50% ! important;
+    margin: 0 0 0 4px;
+    padding: 0 ! important;
+    cursor: pointer;
+    float: left;
 }
 
 .ext-ie .ux-grow-action-item {
-       width: 16px;
+    width: 16px;
 }
 
 .ux-action-right {
-       float: right;
-       margin: 0 3px 0 2px;
-       padding: 0 ! important;
+    float: right;
+    margin: 0 3px 0 2px;
+    padding: 0 ! important;
 }
 
 .ux-grow-action-text {
-       padding: 0 ! important;
-       margin: 0 ! important;
-       background: transparent none ! important;
-       float: left;
+    padding: 0 ! important;
+    margin: 0 ! important;
+    background: transparent none ! important;
+    float: left;
 }
 
 /** vim: ts=4:sw=4:nu:fdc=4:nospell
  * License details: http://www.gnu.org/licenses/lgpl.html
  */
 .ux-lovcombo-icon {
-       width: 16px;
-       height: 16px;
-       float: left;
-       background-position: -1px -1px ! important;
-       background-repeat: no-repeat ! important;
+    width: 16px;
+    height: 16px;
+    float: left;
+    background-position: -1px -1px ! important;
+    background-repeat: no-repeat ! important;
 }
 
 .ux-lovcombo-icon-checked {
-       background: transparent
-               url(../extjs/resources/images/default/menu/checked.gif);
+    background: transparent
+        url(../extjs/resources/images/default/menu/checked.gif);
 }
 
 .ux-lovcombo-icon-unchecked {
-       background: transparent
-               url(../extjs/resources/images/default/menu/unchecked.gif);
+    background: transparent 
+        url(../extjs/resources/images/default/menu/unchecked.gif);
 }
 
 /* eof */
index 5a14d9f8b34451f47289661d96af8bb1b5b78a26..3ac7579f4572884756042359e9d2ba07f7a9d833 100644 (file)
@@ -9,20 +9,20 @@
 
 
 
-/*\r
- * Ext JS Library 2.2\r
- * Copyright(c) 2006-2008, Ext JS, LLC.\r
- * licensing@extjs.com\r
- * \r
- * http://extjs.com/license\r
- */\r
-\r
+/*
+ * Ext JS Library 2.2
+ * Copyright(c) 2006-2008, Ext JS, LLC.
+ * licensing@extjs.com
+ * 
+ * http://extjs.com/license
+ */
+
 /*
  * Note that this control should still be treated as an example and that the API will most likely
  * change once it is ported into the Ext core as a standard form control.  This is still planned
  * for a future release, so this should not yet be treated as a final, stable API at this time.
  */
+
 /** 
  * @class Ext.ux.MultiSelect
  * @extends Ext.form.Field
@@ -38,7 +38,7 @@
  * Create a new MultiSelect
  * @param {Object} config Configuration options
  */
-Ext.ux.Multiselect = Ext.extend(Ext.form.Field,  {
+Ext.ux.Multiselect = Ext.extend(Ext.form.Field, {
     /**
      * @cfg {String} legend Wraps the object with a fieldset and specified legend.
      */
@@ -50,10 +50,10 @@ Ext.ux.Multiselect = Ext.extend(Ext.form.Field,  {
      */
     /**
      * @cfg {String/Array} dragGroup The ddgroup name(s) for the DDView's DragZone (defaults to undefined). 
-     */ 
+     */
     /**
      * @cfg {String/Array} dropGroup The ddgroup name(s) for the DDView's DropZone (defaults to undefined). 
-     */ 
+     */
     /**
      * @cfg {Object/Array} tbar The top toolbar of the control. This can be a {@link Ext.Toolbar} object, a 
      * toolbar config, or an array of buttons/button configs to be added to the toolbar.
@@ -65,163 +65,157 @@ Ext.ux.Multiselect = Ext.extend(Ext.form.Field,  {
      * @cfg {String} appendOnly True if the list should only allow append drops when drag/drop is enabled 
      * (use for lists which are sorted, defaults to false).
      */
-    appendOnly:false,
+    appendOnly: false,
     /**
      * @cfg {Array} dataFields Inline data definition when not using a pre-initialised store. Known to cause problems 
      * in some browswers for very long lists. Use store for large datasets.
      */
-    dataFields:[],
+    dataFields: [],
     /**
      * @cfg {Array} data Inline data when not using a pre-initialised store. Known to cause problems in some 
      * browswers for very long lists. Use store for large datasets.
      */
-    data:[],
+    data: [],
     /**
      * @cfg {Number} width Width in pixels of the control (defaults to 100).
      */
-    width:100,
+    width: 100,
     /**
      * @cfg {Number} height Height in pixels of the control (defaults to 100).
      */
-    height:100,
+    height: 100,
     /**
      * @cfg {String/Number} displayField Name/Index of the desired display field in the dataset (defaults to 0).
      */
-    displayField:0,
+    displayField: 0,
     /**
      * @cfg {String/Number} valueField Name/Index of the desired value field in the dataset (defaults to 1).
      */
-    valueField:1,
+    valueField: 1,
     /**
      * @cfg {Boolean} allowBlank True to require at least one item in the list to be selected, false to allow no 
      * selection (defaults to true).
      */
-    allowBlank:true,
+    allowBlank: true,
     /**
      * @cfg {Number} minLength Minimum number of selections allowed (defaults to 0).
      */
-    minLength:0,
+    minLength: 0,
     /**
      * @cfg {Number} maxLength Maximum number of selections allowed (defaults to Number.MAX_VALUE). 
      */
-    maxLength:Number.MAX_VALUE,
+    maxLength: Number.MAX_VALUE,
     /**
      * @cfg {String} blankText Default text displayed when the control contains no items (defaults to the same value as
      * {@link Ext.form.TextField#blankText}.
      */
-    blankText:Ext.form.TextField.prototype.blankText,
+    blankText: Ext.form.TextField.prototype.blankText,
     /**
      * @cfg {String} minLengthText Validation message displayed when {@link #minLength} is not met (defaults to 'Minimum {0} 
      * item(s) required').  The {0} token will be replaced by the value of {@link #minLength}.
      */
-    minLengthText:'Minimum {0} item(s) required',
+    minLengthText: 'Minimum {0} item(s) required',
     /**
      * @cfg {String} maxLengthText Validation message displayed when {@link #maxLength} is not met (defaults to 'Maximum {0} 
      * item(s) allowed').  The {0} token will be replaced by the value of {@link #maxLength}.
      */
-    maxLengthText:'Maximum {0} item(s) allowed',
+    maxLengthText: 'Maximum {0} item(s) allowed',
     /**
      * @cfg {String} delimiter The string used to delimit between items when set or returned as a string of values
      * (defaults to ',').
      */
-    delimiter:',',
-    
+    delimiter: ',',
     // DDView settings
-    copy:false,
-    allowDup:false,
-    allowTrash:false,
-    focusClass:undefined,
-    sortDir:'ASC',
-    
+    copy: false,
+    allowDup: false,
+    allowTrash: false,
+    focusClass: undefined,
+    sortDir: 'ASC',
     // private
-    defaultAutoCreate : {tag: "div"},
-    
+    defaultAutoCreate: {tag: "div"},
     // private
-    initComponent: function(){
+    initComponent: function() {
         Ext.ux.Multiselect.superclass.initComponent.call(this);
         this.addEvents({
-            'dblclick' : true,
-            'click' : true,
-            'change' : true,
-            'drop' : true
-        });     
+            'dblclick': true,
+            'click': true,
+            'change': true,
+            'drop': true
+        });
     },
-    
     // private
-    onRender: function(ct, position){
+    onRender: function(ct, position) {
         Ext.ux.Multiselect.superclass.onRender.call(this, ct, position);
-        
+
         var cls = 'ux-mselect';
         var fs = new Ext.form.FieldSet({
-            renderTo:this.el,
-            title:this.legend,
-            height:this.height,
-            width:this.width,
-            style:"padding:0;",
-            tbar:this.tbar
+            renderTo: this.el,
+            title: this.legend,
+            height: this.height,
+            width: this.width,
+            style: "padding:0;",
+            tbar: this.tbar
         });
         //if(!this.legend)fs.el.down('.'+fs.headerCls).remove();
         fs.body.addClass(cls);
 
         var tpl = '<tpl for="."><div class="' + cls + '-item';
-        if(Ext.isIE || Ext.isIE7){
-            tpl+='" unselectable=on';
-        }else{
-            tpl+=' x-unselectable"';
+        if (Ext.isIE || Ext.isIE7) {
+            tpl += '" unselectable=on';
+        } else {
+            tpl += ' x-unselectable"';
         }
-        tpl+='>{' + this.displayField + '}</div></tpl>';
+        tpl += '>{' + this.displayField + '}</div></tpl>';
 
-        if(!this.store){
+        if (!this.store) {
             this.store = new Ext.data.SimpleStore({
                 fields: this.dataFields,
-                data : this.data
+                data: this.data
             });
         }
 
         this.view = new Ext.ux.DDView({
-            multiSelect: true, 
-            store: this.store, 
-            selectedClass: cls+"-selected", 
-            tpl:tpl,
-            allowDup:this.allowDup, 
-            copy: this.copy, 
-            allowTrash: this.allowTrash, 
-            dragGroup: this.dragGroup, 
-            dropGroup: this.dropGroup, 
-            itemSelector:"."+cls+"-item",
-            isFormField:false, 
-            applyTo:fs.body,
-            appendOnly:this.appendOnly,
-            sortField:this.sortField, 
-            sortDir:this.sortDir
+            multiSelect: true,
+            store: this.store,
+            selectedClass: cls + "-selected",
+            tpl: tpl,
+            allowDup: this.allowDup,
+            copy: this.copy,
+            allowTrash: this.allowTrash,
+            dragGroup: this.dragGroup,
+            dropGroup: this.dropGroup,
+            itemSelector: "." + cls + "-item",
+            isFormField: false,
+            applyTo: fs.body,
+            appendOnly: this.appendOnly,
+            sortField: this.sortField,
+            sortDir: this.sortDir
         });
 
         fs.add(this.view);
-        
+
         this.view.on('click', this.onViewClick, this);
         this.view.on('beforeClick', this.onViewBeforeClick, this);
         this.view.on('dblclick', this.onViewDblClick, this);
-        this.view.on('drop', function(ddView, n, dd, e, data){
+        this.view.on('drop', function(ddView, n, dd, e, data) {
             return this.fireEvent("drop", ddView, n, dd, e, data);
         }, this);
-        
+
         this.hiddenName = this.name;
-        var hiddenTag={tag: "input", type: "hidden", value: "", name:this.name};
-        if (this.isFormField) { 
+        var hiddenTag = {tag: "input", type: "hidden", value: "", name: this.name};
+        if (this.isFormField) {
             this.hiddenField = this.el.createChild(hiddenTag);
         } else {
             this.hiddenField = Ext.get(document.body).createChild(hiddenTag);
         }
         fs.doLayout();
     },
-    
     // private
-    initValue:Ext.emptyFn,
-    
+    initValue: Ext.emptyFn,
     // private
     onViewClick: function(vw, index, node, e) {
         var arrayIndex = this.preClickSelections.indexOf(index);
-        if (arrayIndex  != -1)
+        if (arrayIndex !== -1)
         {
             this.preClickSelections.splice(arrayIndex, 1);
             this.view.clearSelections(true);
@@ -230,35 +224,34 @@ Ext.ux.Multiselect = Ext.extend(Ext.form.Field,  {
         this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value);
         this.hiddenField.dom.value = this.getValue();
         this.fireEvent('click', this, e);
-        this.validate();        
+        this.validate();
     },
-
     // private
     onViewBeforeClick: function(vw, index, node, e) {
         this.preClickSelections = this.view.getSelectedIndexes();
-        if (this.disabled) {return false;}
+        if (this.disabled) {
+            return false;
+        }
     },
-
     // private
-    onViewDblClick : function(vw, index, node, e) {
-        return this.fireEvent('dblclick', vw, index, node, e);
-    },  
-    
+    onViewDblClick: function(vw, index, node, e) {
+        return this.fireEvent('dblclick', vw, index, node, e);   },
     /**
      * Returns an array of data values for the selected items in the list. The values will be separated
      * by {@link #delimiter}.
      * @return {Array} value An array of string data values
      */
-    getValue: function(valueField){
+    getValue: function(valueField) {
         var returnArray = [];
         var selectionsArray = this.view.getSelectedIndexes();
-        if (selectionsArray.length == 0) {return '';}
-        for (var i=0; i<selectionsArray.length; i++) {
-            returnArray.push(this.store.getAt(selectionsArray[i]).get(((valueField != null)? valueField : this.valueField)));
+        if (selectionsArray.length === 0) {
+            return '';
+        }
+        for (var i = 0; i < selectionsArray.length; i++) {
+            returnArray.push(this.store.getAt(selectionsArray[i]).get(((valueField != null) ? valueField : this.valueField)));
         }
         return returnArray.join(this.delimiter);
     },
-
     /**
      * Sets a delimited string (using {@link #delimiter}) or array of data values into the list.
      * @param {String/Array} values The values to set
@@ -268,52 +261,52 @@ Ext.ux.Multiselect = Ext.extend(Ext.form.Field,  {
         var selections = [];
         this.view.clearSelections();
         this.hiddenField.dom.value = '';
-        
-        if (!values || (values == '')) { return; }
-        
-        if (!(values instanceof Array)) { values = values.split(this.delimiter); }
-        for (var i=0; i<values.length; i++) {
-            index = this.view.store.indexOf(this.view.store.query(this.valueField, 
-                new RegExp('^' + values[i] + '$', "i")).itemAt(0));
+
+        if (!values || (values === '')) {
+            return;
+        }
+
+        if (!(values instanceof Array)) {
+            values = values.split(this.delimiter);
+        }
+        for (var i = 0; i < values.length; i++) {
+            index = this.view.store.indexOf(this.view.store.query(this.valueField,
+                    new RegExp('^' + values[i] + '$', "i")).itemAt(0));
             selections.push(index);
         }
         this.view.select(selections);
         this.hiddenField.dom.value = this.getValue();
         this.validate();
     },
-    
     // inherit docs
-    reset : function() {
+    reset: function() {
         this.setValue('');
     },
-    
     // inherit docs
     getRawValue: function(valueField) {
         var tmp = this.getValue(valueField);
         if (tmp.length) {
             tmp = tmp.split(this.delimiter);
         }
-        else{
+        else {
             tmp = [];
         }
         return tmp;
     },
-
     // inherit docs
-    setRawValue: function(values){
+    setRawValue: function(values) {
         setValue(values);
     },
-
     // inherit docs
-    validateValue : function(value){
+    validateValue: function(value) {
         if (value.length < 1) { // if it has no value
-             if (this.allowBlank) {
-                 this.clearInvalid();
-                 return true;
-             } else {
-                 this.markInvalid(this.blankText);
-                 return false;
-             }
+            if (this.allowBlank) {
+                this.clearInvalid();
+                return true;
+            } else {
+                this.markInvalid(this.blankText);
+                return false;
+            }
         }
         if (value.length < this.minLength) {
             this.markInvalid(String.format(this.minLengthText, this.minLength));
@@ -355,81 +348,78 @@ Ext.reg("multiselect", Ext.ux.Multiselect);
 Ext.namespace('Ext.ux.grid');
 
 Ext.ux.grid.ProgressColumn = function(config) {
-  Ext.apply(this, config);
-  this.renderer = this.renderer.createDelegate(this);
-  this.addEvents('action');
-  Ext.ux.grid.ProgressColumn.superclass.constructor.call(this);
+    Ext.apply(this, config);
+    this.renderer = this.renderer.createDelegate(this);
+    this.addEvents('action');
+    Ext.ux.grid.ProgressColumn.superclass.constructor.call(this);
 };
 
 Ext.extend(Ext.ux.grid.ProgressColumn, Ext.util.Observable, {
-  /**
-   * @cfg {String} colored determines whether use special progression coloring
-   *      or the standard Ext.ProgressBar coloring for the bar (defaults to
-   *      false)
-   */
-  textPst : '%',
-  /**
-   * @cfg {String} colored determines whether use special progression coloring
-   *      or the standard Ext.ProgressBar coloring for the bar (defaults to
-   *      false)
-   */
-  colored : false,
-  /**
-   * @cfg {String} actionEvent Event to trigger actions, e.g. click, dblclick,
-   *      mouseover (defaults to 'dblclick')
-   */
-  actionEvent : 'dblclick',
-
-  init : function(grid) {
-    this.grid = grid;
-    this.view = grid.getView();
-
-    if (this.editor && grid.isEditor) {
-      var cfg = {
-        scope : this
-      };
-      cfg[this.actionEvent] = this.onClick;
-      grid.afterRender = grid.afterRender.createSequence(function() {
-        this.view.mainBody.on(cfg);
-      }, this);
-    }
-  },
+    /**
+     * @cfg {String} colored determines whether use special progression coloring
+     *      or the standard Ext.ProgressBar coloring for the bar (defaults to
+     *      false)
+     */
+    textPst: '%',
+    /**
+     * @cfg {String} colored determines whether use special progression coloring
+     *      or the standard Ext.ProgressBar coloring for the bar (defaults to
+     *      false)
+     */
+    colored: false,
+    /**
+     * @cfg {String} actionEvent Event to trigger actions, e.g. click, dblclick,
+     *      mouseover (defaults to 'dblclick')
+     */
+    actionEvent: 'dblclick',
+    init: function(grid) {
+        this.grid = grid;
+        this.view = grid.getView();
+
+        if (this.editor && grid.isEditor) {
+            var cfg = {
+                scope: this
+            };
+            cfg[this.actionEvent] = this.onClick;
+            grid.afterRender = grid.afterRender.createSequence(function() {
+                this.view.mainBody.on(cfg);
+            }, this);
+        }
+    },
+    onClick: function(e, target) {
+        var rowIndex = e.getTarget('.x-grid3-row').rowIndex;
+        var colIndex = this.view.findCellIndex(target.parentNode.parentNode);
 
-  onClick : function(e, target) {
-    var rowIndex = e.getTarget('.x-grid3-row').rowIndex;
-    var colIndex = this.view.findCellIndex(target.parentNode.parentNode);
+        var t = e.getTarget('.x-progress-text');
+        if (t) {
+            this.grid.startEditing(rowIndex, colIndex);
+        }
+    },
+    renderer: function(v, p, record) {
+        var style = '';
+        var textClass = (v < 55) ? 'x-progress-text-back' : 'x-progress-text-front' + (Ext.isIE6 ? '-ie6' : '');
+
+        //ugly hack to deal with IE6 issue
+        var text = String.format('</div><div class="x-progress-text {0}" style="width:100%;" id="{1}">{2}</div></div>',
+                textClass, Ext.id(), v + this.textPst
+                );
+        text = (v < 96) ? text.substring(0, text.length - 6) : text.substr(6);
+
+        if (this.colored == true) {
+            if (v <= 100 && v > 66)
+                style = '-green';
+            if (v < 67 && v > 33)
+                style = '-orange';
+            if (v < 34)
+                style = '-red';
+        }
 
-    var t = e.getTarget('.x-progress-text');
-    if (t) {
-      this.grid.startEditing(rowIndex, colIndex);
-    }
-  },
-
-  renderer : function(v, p, record) {
-    var style = '';
-    var textClass = (v < 55) ? 'x-progress-text-back' : 'x-progress-text-front' + (Ext.isIE6 ? '-ie6' : '');
-
-    //ugly hack to deal with IE6 issue
-    var text = String.format('</div><div class="x-progress-text {0}" style="width:100%;" id="{1}">{2}</div></div>',
-      textClass, Ext.id(), v + this.textPst
-    );
-    text = (v<96) ? text.substring(0, text.length - 6) : text.substr(6);
-
-    if (this.colored == true) {
-      if (v <= 100 && v > 66)
-        style = '-green';
-      if (v < 67 && v > 33)
-        style = '-orange';
-      if (v < 34)
-        style = '-red';
+        p.css += ' x-grid3-progresscol';
+        return String.format(
+                '<div class="x-progress-wrap"><div class="x-progress-inner"><div class="x-progress-bar{0}" style="width:{1}%;">{2}</div>' +
+                '</div>', style, v, text
+                );
     }
-
-    p.css += ' x-grid3-progresscol';
-    return String.format(
-      '<div class="x-progress-wrap"><div class="x-progress-inner"><div class="x-progress-bar{0}" style="width:{1}%;">{2}</div>' +
-      '</div>', style, v, text
-    );
-  }
 });
 
 //vim: ts=4:sw=4:nu:fdc=4:nospell
@@ -481,14 +471,14 @@ Ext.extend(Ext.ux.grid.ProgressColumn, Ext.util.Observable, {
 Ext.ns('Ext.ux.grid');
 
 // add RegExp.escape if it has not been already added
-if('function' !== typeof RegExp.escape) {
-       RegExp.escape = function(s) {
-               if('string' !== typeof s) {
-                       return s;
-               }
-               // Note: if pasting from forum, precede ]/\ with backslash manually
-               return s.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g, '\\$1');
-       }; // eo function escape
+if ('function' !== typeof RegExp.escape) {
+    RegExp.escape = function(s) {
+        if ('string' !== typeof s) {
+            return s;
+        }
+        // Note: if pasting from forum, precede ]/\ with backslash manually
+        return s.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g, '\\$1');
+    }; // eo function escape
 }
 
 /**
@@ -497,389 +487,388 @@ if('function' !== typeof RegExp.escape) {
  * @param {Object} config A config object
  */
 Ext.ux.grid.RowActions = function(config) {
-       Ext.apply(this, config);
-
-       // {{{
-       this.addEvents(
-               /**
-                * @event beforeaction
-                * Fires before action event. Return false to cancel the subsequent action event.
-                * @param {Ext.grid.GridPanel} grid
-                * @param {Ext.data.Record} record Record corresponding to row clicked
-                * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
-                * @param {Integer} rowIndex Index of clicked grid row
-                * @param {Integer} colIndex Index of clicked grid column that contains all action icons
-                */
-                'beforeaction'
-               /**
-                * @event action
-                * Fires when icon is clicked
-                * @param {Ext.grid.GridPanel} grid
-                * @param {Ext.data.Record} record Record corresponding to row clicked
-                * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
-                * @param {Integer} rowIndex Index of clicked grid row
-                * @param {Integer} colIndex Index of clicked grid column that contains all action icons
-                */
-               ,'action'
-               /**
-                * @event beforegroupaction
-                * Fires before group action event. Return false to cancel the subsequent groupaction event.
-                * @param {Ext.grid.GridPanel} grid
-                * @param {Array} records Array of records in this group
-                * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
-                * @param {String} groupId Identifies the group clicked
-                */
-               ,'beforegroupaction'
-               /**
-                * @event groupaction
-                * Fires when icon in a group header is clicked
-                * @param {Ext.grid.GridPanel} grid
-                * @param {Array} records Array of records in this group
-                * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
-                * @param {String} groupId Identifies the group clicked
-                */
-               ,'groupaction'
-       );
-       // }}}
-
-       // call parent
-       Ext.ux.grid.RowActions.superclass.constructor.call(this);
+    Ext.apply(this, config);
+
+    // {{{
+    this.addEvents(
+            /**
+             * @event beforeaction
+             * Fires before action event. Return false to cancel the subsequent action event.
+             * @param {Ext.grid.GridPanel} grid
+             * @param {Ext.data.Record} record Record corresponding to row clicked
+             * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
+             * @param {Integer} rowIndex Index of clicked grid row
+             * @param {Integer} colIndex Index of clicked grid column that contains all action icons
+             */
+            'beforeaction'
+            /**
+             * @event action
+             * Fires when icon is clicked
+             * @param {Ext.grid.GridPanel} grid
+             * @param {Ext.data.Record} record Record corresponding to row clicked
+             * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
+             * @param {Integer} rowIndex Index of clicked grid row
+             * @param {Integer} colIndex Index of clicked grid column that contains all action icons
+             */
+            'action'
+            /**
+             * @event beforegroupaction
+             * Fires before group action event. Return false to cancel the subsequent groupaction event.
+             * @param {Ext.grid.GridPanel} grid
+             * @param {Array} records Array of records in this group
+             * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
+             * @param {String} groupId Identifies the group clicked
+             */
+            'beforegroupaction'
+            /**
+             * @event groupaction
+             * Fires when icon in a group header is clicked
+             * @param {Ext.grid.GridPanel} grid
+             * @param {Array} records Array of records in this group
+             * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
+             * @param {String} groupId Identifies the group clicked
+             */
+            'groupaction'
+            );
+    // }}}
+
+    // call parent
+    Ext.ux.grid.RowActions.superclass.constructor.call(this);
 };
 
 Ext.extend(Ext.ux.grid.RowActions, Ext.util.Observable, {
+    // configuration options
+    // {{{
+    /**
+     * @cfg {Array} actions Mandatory. Array of action configuration objects. The action
+     * configuration object recognizes the following options:
+     * <ul class="list">
+     * <li style="list-style-position:outside">
+     *   {Function} <b>callback</b> (optional). Function to call if the action icon is clicked.
+     *   This function is called with same signature as action event and in its original scope.
+     *   If you need to call it in different scope or with another signature use 
+     *   createCallback or createDelegate functions. Works for statically defined actions. Use
+     *   callbacks configuration options for store bound actions.
+     * </li>
+     * <li style="list-style-position:outside">
+     *   {Function} <b>cb</b> Shortcut for callback.
+     * </li>
+     * <li style="list-style-position:outside">
+     *   {String} <b>iconIndex</b> Optional, however either iconIndex or iconCls must be
+     *   configured. Field name of the field of the grid store record that contains
+     *   css class of the icon to show. If configured, shown icons can vary depending
+     *   of the value of this field.
+     * </li>
+     * <li style="list-style-position:outside">
+     *   {String} <b>iconCls</b> CSS class of the icon to show. It is ignored if iconIndex is
+     *   configured. Use this if you want static icons that are not base on the values in the record.
+     * </li>
+     * <li style="list-style-position:outside">
+     *   {Boolean} <b>hide</b> Optional. True to hide this action while still have a space in 
+     *   the grid column allocated to it. IMO, it doesn't make too much sense, use hideIndex instead.
+     * </li>
+     * <li style="list-style-position:outside">
+     *   {String} <b>hideIndex</b> Optional. Field name of the field of the grid store record that
+     *   contains hide flag (falsie [null, '', 0, false, undefined] to show, anything else to hide).
+     * </li>
+     * <li style="list-style-position:outside">
+     *   {String} <b>qtipIndex</b> Optional. Field name of the field of the grid store record that 
+     *   contains tooltip text. If configured, the tooltip texts are taken from the store.
+     * </li>
+     * <li style="list-style-position:outside">
+     *   {String} <b>tooltip</b> Optional. Tooltip text to use as icon tooltip. It is ignored if 
+     *   qtipIndex is configured. Use this if you want static tooltips that are not taken from the store.
+     * </li>
+     * <li style="list-style-position:outside">
+     *   {String} <b>qtip</b> Synonym for tooltip
+     * </li>
+     * <li style="list-style-position:outside">
+     *   {String} <b>textIndex</b> Optional. Field name of the field of the grids store record
+     *   that contains text to display on the right side of the icon. If configured, the text
+     *   shown is taken from record.
+     * </li>
+     * <li style="list-style-position:outside">
+     *   {String} <b>text</b> Optional. Text to display on the right side of the icon. Use this
+     *   if you want static text that are not taken from record. Ignored if textIndex is set.
+     * </li>
+     * <li style="list-style-position:outside">
+     *   {String} <b>style</b> Optional. Style to apply to action icon container.
+     * </li>
+     * </ul>
+     */
+
+    /**
+     * @cfg {String} actionEvent Event to trigger actions, e.g. click, dblclick, mouseover (defaults to 'click')
+     */
+    actionEvent: 'click'
+            /**
+             * @cfg {Boolean} autoWidth true to calculate field width for iconic actions only (defaults to true).
+             * If true, the width is calculated as {@link #widthSlope} * number of actions + {@link #widthIntercept}.
+             */
+    , autoWidth: true
+
+            /**
+             * @cfg {String} dataIndex - Do not touch!
+             * @private
+             */
+    , dataIndex: ''
+
+            /**
+             * @cfg {Boolean} editable - Do not touch!
+             * Must be false to prevent errors in editable grids
+             */
+    , editable: false
+
+            /**
+             * @cfg {Array} groupActions Array of action to use for group headers of grouping grids.
+             * These actions support static icons, texts and tooltips same way as {@link #actions}. There is one
+             * more action config option recognized:
+             * <ul class="list">
+             * <li style="list-style-position:outside">
+             *   {String} <b>align</b> Set it to 'left' to place action icon next to the group header text.
+             *   (defaults to undefined = icons are placed at the right side of the group header.
+             * </li>
+             * </ul>
+             */
+
+            /**
+             * @cfg {Object} callbacks iconCls keyed object that contains callback functions. For example:
+             * <pre>
+             * callbacks:{
+             * &nbsp;    'icon-open':function(...) {...}
+             * &nbsp;   ,'icon-save':function(...) {...}
+             * }
+             * </pre>
+             */
+
+            /**
+             * @cfg {String} header Actions column header
+             */
+    , header: ''
+
+            /**
+             * @cfg {Boolean} isColumn
+             * Tell ColumnModel that we are column. Do not touch!
+             * @private
+             */
+    , isColumn: true
+
+            /**
+             * @cfg {Boolean} keepSelection
+             * Set it to true if you do not want action clicks to affect selected row(s) (defaults to false).
+             * By default, when user clicks an action icon the clicked row is selected and the action events are fired.
+             * If this option is true then the current selection is not affected, only the action events are fired.
+             */
+    , keepSelection: false
+
+            /**
+             * @cfg {Boolean} menuDisabled No sense to display header menu for this column
+             * @private
+             */
+    , menuDisabled: true
+
+            /**
+             * @cfg {Boolean} sortable Usually it has no sense to sort by this column
+             * @private
+             */
+    , sortable: false
+
+            /**
+             * @cfg {String} tplGroup Template for group actions
+             * @private
+             */
+    , tplGroup:
+            '<tpl for="actions">'
+            + '<div class="ux-grow-action-item<tpl if="\'right\'===align"> ux-action-right</tpl> '
+            + '{cls}" style="{style}" qtip="{qtip}">{text}</div>'
+            + '</tpl>'
+
+            /**
+             * @cfg {String} tplRow Template for row actions
+             * @private
+             */
+    , tplRow:
+            '<div class="ux-row-action">'
+            + '<tpl for="actions">'
+            + '<div class="ux-row-action-item {cls} <tpl if="text">'
+            + 'ux-row-action-text</tpl>" style="{hide}{style}" qtip="{qtip}">'
+            + '<tpl if="text"><span qtip="{qtip}">{text}</span></tpl></div>'
+            + '</tpl>'
+            + '</div>'
+
+            /**
+             * @cfg {String} hideMode How to hide hidden icons. Valid values are: 'visibility' and 'display' 
+             * (defaluts to 'visibility'). If the mode is visibility the hidden icon is not visible but there
+             * is still blank space occupied by the icon. In display mode, the visible icons are shifted taking
+             * the space of the hidden icon.
+             */
+    , hideMode: 'visibility'
+
+            /**
+             * @cfg {Number} widthIntercept Constant used for auto-width calculation (defaults to 4).
+             * See {@link #autoWidth} for explanation.
+             */
+    , widthIntercept: 4
+
+            /**
+             * @cfg {Number} widthSlope Constant used for auto-width calculation (defaults to 21).
+             * See {@link #autoWidth} for explanation.
+             */
+    , widthSlope: 21
+            // }}}
+
+            // methods
+            // {{{
+            /**
+             * Init function
+             * @param {Ext.grid.GridPanel} grid Grid this plugin is in
+             */
+    , init: function(grid) {
+        this.grid = grid;
+
+        // the actions column must have an id for Ext 3.x
+        this.id = this.id || Ext.id();
+
+        // for Ext 3.x compatibility
+        var lookup = grid.getColumnModel().lookup;
+        delete(lookup[undefined]);
+        lookup[this.id] = this;
+
+        // {{{
+        // setup template
+        if (!this.tpl) {
+            this.tpl = this.processActions(this.actions);
+
+        } // eo template setup
+        // }}}
+
+        // calculate width
+        if (this.autoWidth) {
+            this.width = this.widthSlope * this.actions.length + this.widthIntercept;
+            this.fixed = true;
+        }
+
+        // body click handler
+        var view = grid.getView();
+        var cfg = {scope: this};
+        cfg[this.actionEvent] = this.onClick;
+        grid.afterRender = grid.afterRender.createSequence(function() {
+            view.mainBody.on(cfg);
+            grid.on('destroy', this.purgeListeners, this);
+        }, this);
+
+        // setup renderer
+        if (!this.renderer) {
+            this.renderer = function(value, cell, record, row, col, store) {
+                cell.css += (cell.css ? ' ' : '') + 'ux-row-action-cell';
+                return this.tpl.apply(this.getData(value, cell, record, row, col, store));
+            }.createDelegate(this);
+        }
+
+        // actions in grouping grids support
+        if (view.groupTextTpl && this.groupActions) {
+            view.interceptMouse = view.interceptMouse.createInterceptor(function(e) {
+                if (e.getTarget('.ux-grow-action-item')) {
+                    return false;
+                }
+            });
+            view.groupTextTpl =
+                    '<div class="ux-grow-action-text">' + view.groupTextTpl + '</div>'
+                    + this.processActions(this.groupActions, this.tplGroup).apply()
+                    ;
+        }
+
+        // cancel click
+        if (true === this.keepSelection) {
+            grid.processEvent = grid.processEvent.createInterceptor(function(name, e) {
+                if ('mousedown' === name) {
+                    return !this.getAction(e);
+                }
+            }, this);
+        }
+
+    } // eo function init
+    // }}}
+    // {{{
+    /**
+     * Returns data to apply to template. Override this if needed.
+     * @param {Mixed} value 
+     * @param {Object} cell object to set some attributes of the grid cell
+     * @param {Ext.data.Record} record from which the data is extracted
+     * @param {Number} row row index
+     * @param {Number} col col index
+     * @param {Ext.data.Store} store object from which the record is extracted
+     * @return {Object} data to apply to template
+     */
+    , getData: function(value, cell, record, row, col, store) {
+        return record.data || {};
+    } // eo function getData
+    // }}}
+    // {{{
+    /**
+     * Processes actions configs and returns template.
+     * @param {Array} actions
+     * @param {String} template Optional. Template to use for one action item.
+     * @return {String}
+     * @private
+     */
+    , processActions: function(actions, template) {
+        var acts = [];
+
+        // actions loop
+        Ext.each(actions, function(a, i) {
+            // save callback
+            if (a.iconCls && 'function' === typeof (a.callback || a.cb)) {
+                this.callbacks = this.callbacks || {};
+                this.callbacks[a.iconCls] = a.callback || a.cb;
+            }
+
+            // data for intermediate template
+            var o = {
+                cls: a.iconIndex ? '{' + a.iconIndex + '}' : (a.iconCls ? a.iconCls : '')
+                , qtip: a.qtipIndex ? '{' + a.qtipIndex + '}' : (a.tooltip || a.qtip ? a.tooltip || a.qtip : '')
+                , text: a.textIndex ? '{' + a.textIndex + '}' : (a.text ? a.text : '')
+                , hide: a.hideIndex
+                        ? '<tpl if="' + a.hideIndex + '">'
+                        + ('display' === this.hideMode ? 'display:none' : 'visibility:hidden') + ';</tpl>'
+                        : (a.hide ? ('display' === this.hideMode ? 'display:none' : 'visibility:hidden;') : '')
+                , align: a.align || 'right'
+                , style: a.style ? a.style : ''
+            };
+            acts.push(o);
+
+        }, this); // eo actions loop
+
+        var xt = new Ext.XTemplate(template || this.tplRow);
+        return new Ext.XTemplate(xt.apply({actions: acts}));
+
+    } // eo function processActions
+    // }}}
+    , getAction: function(e) {
+        var action = false;
+        var t = e.getTarget('.ux-row-action-item');
+        if (t) {
+            action = t.className.replace(/ux-row-action-item /, '');
+            if (action) {
+                action = action.replace(/ ux-row-action-text/, '');
+                action = action.trim();
+            }
+        }
+        return action;
+    } // eo function getAction
+    // {{{
+    /**
+     * Grid body actionEvent event handler
+     * @private
+     */
+    , onClick: function(e, target) {
+
+        var view = this.grid.getView();
 
-       // configuration options
-       // {{{
-       /**
-        * @cfg {Array} actions Mandatory. Array of action configuration objects. The action
-        * configuration object recognizes the following options:
-        * <ul class="list">
-        * <li style="list-style-position:outside">
-        *   {Function} <b>callback</b> (optional). Function to call if the action icon is clicked.
-        *   This function is called with same signature as action event and in its original scope.
-        *   If you need to call it in different scope or with another signature use 
-        *   createCallback or createDelegate functions. Works for statically defined actions. Use
-        *   callbacks configuration options for store bound actions.
-        * </li>
-        * <li style="list-style-position:outside">
-        *   {Function} <b>cb</b> Shortcut for callback.
-        * </li>
-        * <li style="list-style-position:outside">
-        *   {String} <b>iconIndex</b> Optional, however either iconIndex or iconCls must be
-        *   configured. Field name of the field of the grid store record that contains
-        *   css class of the icon to show. If configured, shown icons can vary depending
-        *   of the value of this field.
-        * </li>
-        * <li style="list-style-position:outside">
-        *   {String} <b>iconCls</b> CSS class of the icon to show. It is ignored if iconIndex is
-        *   configured. Use this if you want static icons that are not base on the values in the record.
-        * </li>
-        * <li style="list-style-position:outside">
-        *   {Boolean} <b>hide</b> Optional. True to hide this action while still have a space in 
-        *   the grid column allocated to it. IMO, it doesn't make too much sense, use hideIndex instead.
-        * </li>
-        * <li style="list-style-position:outside">
-        *   {String} <b>hideIndex</b> Optional. Field name of the field of the grid store record that
-        *   contains hide flag (falsie [null, '', 0, false, undefined] to show, anything else to hide).
-        * </li>
-        * <li style="list-style-position:outside">
-        *   {String} <b>qtipIndex</b> Optional. Field name of the field of the grid store record that 
-        *   contains tooltip text. If configured, the tooltip texts are taken from the store.
-        * </li>
-        * <li style="list-style-position:outside">
-        *   {String} <b>tooltip</b> Optional. Tooltip text to use as icon tooltip. It is ignored if 
-        *   qtipIndex is configured. Use this if you want static tooltips that are not taken from the store.
-        * </li>
-        * <li style="list-style-position:outside">
-        *   {String} <b>qtip</b> Synonym for tooltip
-        * </li>
-        * <li style="list-style-position:outside">
-        *   {String} <b>textIndex</b> Optional. Field name of the field of the grids store record
-        *   that contains text to display on the right side of the icon. If configured, the text
-        *   shown is taken from record.
-        * </li>
-        * <li style="list-style-position:outside">
-        *   {String} <b>text</b> Optional. Text to display on the right side of the icon. Use this
-        *   if you want static text that are not taken from record. Ignored if textIndex is set.
-        * </li>
-        * <li style="list-style-position:outside">
-        *   {String} <b>style</b> Optional. Style to apply to action icon container.
-        * </li>
-        * </ul>
-        */
-
-       /**
-        * @cfg {String} actionEvent Event to trigger actions, e.g. click, dblclick, mouseover (defaults to 'click')
-        */
-        actionEvent:'click'
-       /**
-        * @cfg {Boolean} autoWidth true to calculate field width for iconic actions only (defaults to true).
-        * If true, the width is calculated as {@link #widthSlope} * number of actions + {@link #widthIntercept}.
-        */
-       ,autoWidth:true
-
-       /**
-        * @cfg {String} dataIndex - Do not touch!
-        * @private
-        */
-       ,dataIndex:''
-
-       /**
-        * @cfg {Boolean} editable - Do not touch!
-        * Must be false to prevent errors in editable grids
-        */
-       ,editable:false
-
-       /**
-        * @cfg {Array} groupActions Array of action to use for group headers of grouping grids.
-        * These actions support static icons, texts and tooltips same way as {@link #actions}. There is one
-        * more action config option recognized:
-        * <ul class="list">
-        * <li style="list-style-position:outside">
-        *   {String} <b>align</b> Set it to 'left' to place action icon next to the group header text.
-        *   (defaults to undefined = icons are placed at the right side of the group header.
-        * </li>
-        * </ul>
-        */
-
-       /**
-        * @cfg {Object} callbacks iconCls keyed object that contains callback functions. For example:
-        * <pre>
-        * callbacks:{
-        * &nbsp;    'icon-open':function(...) {...}
-        * &nbsp;   ,'icon-save':function(...) {...}
-        * }
-        * </pre>
-        */
-
-       /**
-        * @cfg {String} header Actions column header
-        */
-       ,header:''
-
-       /**
-        * @cfg {Boolean} isColumn
-        * Tell ColumnModel that we are column. Do not touch!
-        * @private
-        */
-       ,isColumn:true
-
-       /**
-        * @cfg {Boolean} keepSelection
-        * Set it to true if you do not want action clicks to affect selected row(s) (defaults to false).
-        * By default, when user clicks an action icon the clicked row is selected and the action events are fired.
-        * If this option is true then the current selection is not affected, only the action events are fired.
-        */
-       ,keepSelection:false
-
-       /**
-        * @cfg {Boolean} menuDisabled No sense to display header menu for this column
-        * @private
-        */
-       ,menuDisabled:true
-
-       /**
-        * @cfg {Boolean} sortable Usually it has no sense to sort by this column
-        * @private
-        */
-       ,sortable:false
-
-       /**
-        * @cfg {String} tplGroup Template for group actions
-        * @private
-        */
-       ,tplGroup:
-                '<tpl for="actions">'
-               +'<div class="ux-grow-action-item<tpl if="\'right\'===align"> ux-action-right</tpl> '
-               +'{cls}" style="{style}" qtip="{qtip}">{text}</div>'
-               +'</tpl>'
-
-       /**
-        * @cfg {String} tplRow Template for row actions
-        * @private
-        */
-       ,tplRow:
-                '<div class="ux-row-action">'
-               +'<tpl for="actions">'
-               +'<div class="ux-row-action-item {cls} <tpl if="text">'
-               +'ux-row-action-text</tpl>" style="{hide}{style}" qtip="{qtip}">'
-               +'<tpl if="text"><span qtip="{qtip}">{text}</span></tpl></div>'
-               +'</tpl>'
-               +'</div>'
-
-       /**
-        * @cfg {String} hideMode How to hide hidden icons. Valid values are: 'visibility' and 'display' 
-        * (defaluts to 'visibility'). If the mode is visibility the hidden icon is not visible but there
-        * is still blank space occupied by the icon. In display mode, the visible icons are shifted taking
-        * the space of the hidden icon.
-        */
-       ,hideMode:'visibility'
-
-       /**
-        * @cfg {Number} widthIntercept Constant used for auto-width calculation (defaults to 4).
-        * See {@link #autoWidth} for explanation.
-        */
-       ,widthIntercept:4
-
-       /**
-        * @cfg {Number} widthSlope Constant used for auto-width calculation (defaults to 21).
-        * See {@link #autoWidth} for explanation.
-        */
-       ,widthSlope:21
-       // }}}
-
-       // methods
-       // {{{
-       /**
-        * Init function
-        * @param {Ext.grid.GridPanel} grid Grid this plugin is in
-        */
-       ,init:function(grid) {
-               this.grid = grid;
-               
-               // the actions column must have an id for Ext 3.x
-               this.id = this.id || Ext.id();
-
-               // for Ext 3.x compatibility
-               var lookup = grid.getColumnModel().lookup;
-               delete(lookup[undefined]);
-               lookup[this.id] = this;
-
-               // {{{
-               // setup template
-               if(!this.tpl) {
-                       this.tpl = this.processActions(this.actions);
-
-               } // eo template setup
-               // }}}
-
-               // calculate width
-               if(this.autoWidth) {
-                       this.width =  this.widthSlope * this.actions.length + this.widthIntercept;
-                       this.fixed = true;
-               }
-
-               // body click handler
-               var view = grid.getView();
-               var cfg = {scope:this};
-               cfg[this.actionEvent] = this.onClick;
-               grid.afterRender = grid.afterRender.createSequence(function() {
-                       view.mainBody.on(cfg);
-                       grid.on('destroy', this.purgeListeners, this);
-               }, this);
-
-               // setup renderer
-               if(!this.renderer) {
-                       this.renderer = function(value, cell, record, row, col, store) {
-                               cell.css += (cell.css ? ' ' : '') + 'ux-row-action-cell';
-                               return this.tpl.apply(this.getData(value, cell, record, row, col, store));
-                       }.createDelegate(this);
-               }
-
-               // actions in grouping grids support
-               if(view.groupTextTpl && this.groupActions) {
-                       view.interceptMouse = view.interceptMouse.createInterceptor(function(e) {
-                               if(e.getTarget('.ux-grow-action-item')) {
-                                       return false;
-                               }
-                       });
-                       view.groupTextTpl = 
-                                '<div class="ux-grow-action-text">' + view.groupTextTpl +'</div>' 
-                               +this.processActions(this.groupActions, this.tplGroup).apply()
-                       ;
-               }
-
-               // cancel click
-               if(true === this.keepSelection) {
-                       grid.processEvent = grid.processEvent.createInterceptor(function(name, e) {
-                               if('mousedown' === name) {
-                                       return !this.getAction(e);
-                               }
-                       }, this);
-               }
-               
-       } // eo function init
-       // }}}
-       // {{{
-       /**
-        * Returns data to apply to template. Override this if needed.
-        * @param {Mixed} value 
-        * @param {Object} cell object to set some attributes of the grid cell
-        * @param {Ext.data.Record} record from which the data is extracted
-        * @param {Number} row row index
-        * @param {Number} col col index
-        * @param {Ext.data.Store} store object from which the record is extracted
-        * @return {Object} data to apply to template
-        */
-       ,getData:function(value, cell, record, row, col, store) {
-               return record.data || {};
-       } // eo function getData
-       // }}}
-       // {{{
-       /**
-        * Processes actions configs and returns template.
-        * @param {Array} actions
-        * @param {String} template Optional. Template to use for one action item.
-        * @return {String}
-        * @private
-        */
-       ,processActions:function(actions, template) {
-               var acts = [];
-
-               // actions loop
-               Ext.each(actions, function(a, i) {
-                       // save callback
-                       if(a.iconCls && 'function' === typeof (a.callback || a.cb)) {
-                               this.callbacks = this.callbacks || {};
-                               this.callbacks[a.iconCls] = a.callback || a.cb;
-                       }
-
-                       // data for intermediate template
-                       var o = {
-                                cls:a.iconIndex ? '{' + a.iconIndex + '}' : (a.iconCls ? a.iconCls : '')
-                               ,qtip:a.qtipIndex ? '{' + a.qtipIndex + '}' : (a.tooltip || a.qtip ? a.tooltip || a.qtip : '')
-                               ,text:a.textIndex ? '{' + a.textIndex + '}' : (a.text ? a.text : '')
-                               ,hide:a.hideIndex 
-                                       ? '<tpl if="' + a.hideIndex + '">' 
-                                               + ('display' === this.hideMode ? 'display:none' :'visibility:hidden') + ';</tpl>' 
-                                       : (a.hide ? ('display' === this.hideMode ? 'display:none' :'visibility:hidden;') : '')
-                               ,align:a.align || 'right'
-                               ,style:a.style ? a.style : ''
-                       };
-                       acts.push(o);
-
-               }, this); // eo actions loop
-
-               var xt = new Ext.XTemplate(template || this.tplRow);
-               return new Ext.XTemplate(xt.apply({actions:acts}));
-
-       } // eo function processActions
-       // }}}
-       ,getAction:function(e) {
-               var action = false;
-               var t = e.getTarget('.ux-row-action-item');
-               if(t) {
-                       action = t.className.replace(/ux-row-action-item /, '');
-                       if(action) {
-                               action = action.replace(/ ux-row-action-text/, '');
-                               action = action.trim();
-                       }
-               }
-               return action;
-       } // eo function getAction
-       // {{{
-       /**
-        * Grid body actionEvent event handler
-        * @private
-        */
-       ,onClick:function(e, target) {
-
-               var view = this.grid.getView();
-
-               // handle row action click
-               var row = e.getTarget('.x-grid3-row');
-               var col = view.findCellIndex(target.parentNode.parentNode);
-               var action = this.getAction(e);
+        // handle row action click
+        var row = e.getTarget('.x-grid3-row');
+        var col = view.findCellIndex(target.parentNode.parentNode);
+        var action = this.getAction(e);
 
 //             var t = e.getTarget('.ux-row-action-item');
 //             if(t) {
@@ -890,55 +879,55 @@ Ext.extend(Ext.ux.grid.RowActions, Ext.util.Observable, {
 //                             action = action.trim();
 //                     }
 //             }
-               if(false !== row && false !== col && false !== action) {
-                       var record = this.grid.store.getAt(row.rowIndex);
-
-                       // call callback if any
-                       if(this.callbacks && 'function' === typeof this.callbacks[action]) {
-                               this.callbacks[action](this.grid, record, action, row.rowIndex, col);
-                       }
-
-                       // fire events
-                       if(true !== this.eventsSuspended && false === this.fireEvent('beforeaction', this.grid, record, action, row.rowIndex, col)) {
-                               return;
-                       }
-                       else if(true !== this.eventsSuspended) {
-                               this.fireEvent('action', this.grid, record, action, row.rowIndex, col);
-                       }
-
-               }
-
-               // handle group action click
-               t = e.getTarget('.ux-grow-action-item');
-               if(t) {
-                       // get groupId
-                       var group = view.findGroup(target);
-                       var groupId = group ? group.id.replace(/ext-gen[0-9]+-gp-/, '') : null;
-
-                       // get matching records
-                       var records;
-                       if(groupId) {
-                               var re = new RegExp(RegExp.escape(groupId));
-                               records = this.grid.store.queryBy(function(r) {
-                                       return r._groupId.match(re);
-                               });
-                               records = records ? records.items : [];
-                       }
-                       action = t.className.replace(/ux-grow-action-item (ux-action-right )*/, '');
-
-                       // call callback if any
-                       if('function' === typeof this.callbacks[action]) {
-                               this.callbacks[action](this.grid, records, action, groupId);
-                       }
-
-                       // fire events
-                       if(true !== this.eventsSuspended && false === this.fireEvent('beforegroupaction', this.grid, records, action, groupId)) {
-                               return false;
-                       }
-                       this.fireEvent('groupaction', this.grid, records, action, groupId);
-               }
-       } // eo function onClick
-       // }}}
+        if (false !== row && false !== col && false !== action) {
+            var record = this.grid.store.getAt(row.rowIndex);
+
+            // call callback if any
+            if (this.callbacks && 'function' === typeof this.callbacks[action]) {
+                this.callbacks[action](this.grid, record, action, row.rowIndex, col);
+            }
+
+            // fire events
+            if (true !== this.eventsSuspended && false === this.fireEvent('beforeaction', this.grid, record, action, row.rowIndex, col)) {
+                return;
+            }
+            else if (true !== this.eventsSuspended) {
+                this.fireEvent('action', this.grid, record, action, row.rowIndex, col);
+            }
+
+        }
+
+        // handle group action click
+        t = e.getTarget('.ux-grow-action-item');
+        if (t) {
+            // get groupId
+            var group = view.findGroup(target);
+            var groupId = group ? group.id.replace(/ext-gen[0-9]+-gp-/, '') : null;
+
+            // get matching records
+            var records;
+            if (groupId) {
+                var re = new RegExp(RegExp.escape(groupId));
+                records = this.grid.store.queryBy(function(r) {
+                    return r._groupId.match(re);
+                });
+                records = records ? records.items : [];
+            }
+            action = t.className.replace(/ux-grow-action-item (ux-action-right )*/, '');
+
+            // call callback if any
+            if ('function' === typeof this.callbacks[action]) {
+                this.callbacks[action](this.grid, records, action, groupId);
+            }
+
+            // fire events
+            if (true !== this.eventsSuspended && false === this.fireEvent('beforegroupaction', this.grid, records, action, groupId)) {
+                return false;
+            }
+            this.fireEvent('groupaction', this.grid, records, action, groupId);
+        }
+    } // eo function onClick
+    // }}}
 
 });
 
@@ -964,251 +953,250 @@ Ext.reg('rowactions', Ext.ux.grid.RowActions);
  * 
  * License details: http://www.gnu.org/licenses/lgpl.html
  */
+
 /*global Ext */
 
 // add RegExp.escape if it has not been already added
-if('function' !== typeof RegExp.escape) {
-       RegExp.escape = function(s) {
-               if('string' !== typeof s) {
-                       return s;
-               }
-               // Note: if pasting from forum, precede ]/\ with backslash manually
-               return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
-       }; // eo function escape
+if ('function' !== typeof RegExp.escape) {
+    RegExp.escape = function(s) {
+        if ('string' !== typeof s) {
+            return s;
+        }
+        // Note: if pasting from forum, precede ]/\ with backslash manually
+        return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+    }; // eo function escape
 }
 
 // create namespace
 Ext.ns('Ext.ux.form');
+
 /**
  *
  * @class Ext.ux.form.LovCombo
  * @extends Ext.form.ComboBox
  */
 Ext.ux.form.LovCombo = Ext.extend(Ext.form.ComboBox, {
-
-       // {{{
-    // configuration options
-       /**
-        * @cfg {String} checkField name of field used to store checked state.
-        * It is automatically added to existing fields.
-        * Change it only if it collides with your normal field.
-        */
-        checkField:'checked'
-
-       /**
-        * @cfg {String} separator separator to use between values and texts
-        */
-    ,separator:','
-
-       /**
-        * @cfg {String/Array} tpl Template for items. 
-        * Change it only if you know what you are doing.
-        */
-       // }}}
     // {{{
-    ,initComponent:function() {
-        
-               // template with checkbox
-               if(!this.tpl) {
-                       this.tpl = 
-                                '<tpl for=".">'
-                               +'<div class="x-combo-list-item">'
-                               +'<img src="' + Ext.BLANK_IMAGE_URL + '" '
-                               +'class="ux-lovcombo-icon ux-lovcombo-icon-'
-                               +'{[values.' + this.checkField + '?"checked":"unchecked"' + ']}">'
-                               +'<div class="ux-lovcombo-item-text">{' + (this.displayField || 'text' )+ '}</div>'
-                               +'</div>'
-                               +'</tpl>'
-                       ;
-               }
+    // configuration options
+    /**
+     * @cfg {String} checkField name of field used to store checked state.
+     * It is automatically added to existing fields.
+     * Change it only if it collides with your normal field.
+     */
+    checkField: 'checked'
+
+            /**
+             * @cfg {String} separator separator to use between values and texts
+             */
+    , separator: ','
+
+            /**
+             * @cfg {String/Array} tpl Template for items. 
+             * Change it only if you know what you are doing.
+             */
+            // }}}
+            // {{{
+    , initComponent: function() {
+
+        // template with checkbox
+        if (!this.tpl) {
+            this.tpl =
+                    '<tpl for=".">'
+                    + '<div class="x-combo-list-item">'
+                    + '<img src="' + Ext.BLANK_IMAGE_URL + '" '
+                    + 'class="ux-lovcombo-icon ux-lovcombo-icon-'
+                    + '{[values.' + this.checkField + '?"checked":"unchecked"' + ']}">'
+                    + '<div class="ux-lovcombo-item-text">{' + (this.displayField || 'text') + '}</div>'
+                    + '</div>'
+                    + '</tpl>'
+                    ;
+        }
+
         // call parent
         Ext.ux.form.LovCombo.superclass.initComponent.apply(this, arguments);
 
-               // install internal event handlers
-               this.on({
-                        scope:this
-                       ,beforequery:this.onBeforeQuery
-                       ,blur:this.onRealBlur
-               });
-
-               // remove selection from input field
-               this.onLoad = this.onLoad.createSequence(function() {
-                       if(this.el) {
-                               var v = this.el.dom.value;
-                               this.el.dom.value = '';
-                               this.el.dom.value = v;
-                       }
-               });
+        // install internal event handlers
+        this.on({
+            scope: this
+            , beforequery: this.onBeforeQuery
+            , blur: this.onRealBlur
+        });
+
+        // remove selection from input field
+        this.onLoad = this.onLoad.createSequence(function() {
+            if (this.el) {
+                var v = this.el.dom.value;
+                this.el.dom.value = '';
+                this.el.dom.value = v;
+            }
+        });
+
     } // e/o function initComponent
     // }}}
-       // {{{
-       /**
-        * Disables default tab key bahavior
-        * @private
-        */
-       ,initEvents:function() {
-               Ext.ux.form.LovCombo.superclass.initEvents.apply(this, arguments);
-
-               // disable default tab handling - does no good
-               this.keyNav.tab = false;
-
-       } // eo function initEvents
-       // }}}
-       // {{{
-       /**
-        * clears value
-        */
-       ,clearValue:function() {
-               this.value = '';
-               this.setRawValue(this.value);
-               this.store.clearFilter();
-               this.store.each(function(r) {
-                       r.set(this.checkField, false);
-               }, this);
-               if(this.hiddenField) {
-                       this.hiddenField.value = '';
-               }
-               this.applyEmptyText();
-       } // eo function clearValue
-       // }}}
-       // {{{
-       /**
-        * @return {String} separator (plus space) separated list of selected displayFields
-        * @private
-        */
-       ,getCheckedDisplay:function() {
-               var re = new RegExp(this.separator, "g");
-               return this.getCheckedValue(this.displayField).replace(re, this.separator + ' ');
-       } // eo function getCheckedDisplay
-       // }}}
-       // {{{
-       /**
-        * @return {String} separator separated list of selected valueFields
-        * @private
-        */
-       ,getCheckedValue:function(field) {
-               field = field || this.valueField;
-               var c = [];
-
-               // store may be filtered so get all records
-               var snapshot = this.store.snapshot || this.store.data;
-
-               snapshot.each(function(r) {
-                       if(r.get(this.checkField)) {
-                               c.push(r.get(field));
-                       }
-               }, this);
-
-               return c.join(this.separator);
-       } // eo function getCheckedValue
-       // }}}
-       // {{{
-       /**
-        * beforequery event handler - handles multiple selections
-        * @param {Object} qe query event
-        * @private
-        */
-       ,onBeforeQuery:function(qe) {
-               qe.query = qe.query.replace(new RegExp(this.getCheckedDisplay() + '[ ' + this.separator + ']*'), '');
-       } // eo function onBeforeQuery
-       // }}}
-       // {{{
-       /**
-        * blur event handler - runs only when real blur event is fired
-        */
-       ,onRealBlur:function() {
-               this.list.hide();
-               var rv = this.getRawValue();
-               var rva = rv.split(new RegExp(RegExp.escape(this.separator) + ' *'));
-               var va = [];
-               var snapshot = this.store.snapshot || this.store.data;
-
-               // iterate through raw values and records and check/uncheck items
-               Ext.each(rva, function(v) {
-                       snapshot.each(function(r) {
-                               if(v === r.get(this.displayField)) {
-                                       va.push(r.get(this.valueField));
-                               }
-                       }, this);
-               }, this);
-               this.setValue(va.join(this.separator));
-               this.store.clearFilter();
-       } // eo function onRealBlur
-       // }}}
-       // {{{
-       /**
-        * Combo's onSelect override
-        * @private
-        * @param {Ext.data.Record} record record that has been selected in the list
-        * @param {Number} index index of selected (clicked) record
-        */
-       ,onSelect:function(record, index) {
-        if(this.fireEvent('beforeselect', this, record, index) !== false){
-
-                       // toggle checked field
-                       record.set(this.checkField, !record.get(this.checkField));
-
-                       // display full list
-                       if(this.store.isFiltered()) {
-                               this.doQuery(this.allQuery);
-                       }
-
-                       // set (update) value and fire event
-                       this.setValue(this.getCheckedValue());
+    // {{{
+    /**
+     * Disables default tab key bahavior
+     * @private
+     */
+    , initEvents: function() {
+        Ext.ux.form.LovCombo.superclass.initEvents.apply(this, arguments);
+
+        // disable default tab handling - does no good
+        this.keyNav.tab = false;
+
+    } // eo function initEvents
+    // }}}
+    // {{{
+    /**
+     * clears value
+     */
+    , clearValue: function() {
+        this.value = '';
+        this.setRawValue(this.value);
+        this.store.clearFilter();
+        this.store.each(function(r) {
+            r.set(this.checkField, false);
+        }, this);
+        if (this.hiddenField) {
+            this.hiddenField.value = '';
+        }
+        this.applyEmptyText();
+    } // eo function clearValue
+    // }}}
+    // {{{
+    /**
+     * @return {String} separator (plus space) separated list of selected displayFields
+     * @private
+     */
+    , getCheckedDisplay: function() {
+        var re = new RegExp(this.separator, "g");
+        return this.getCheckedValue(this.displayField).replace(re, this.separator + ' ');
+    } // eo function getCheckedDisplay
+    // }}}
+    // {{{
+    /**
+     * @return {String} separator separated list of selected valueFields
+     * @private
+     */
+    , getCheckedValue: function(field) {
+        field = field || this.valueField;
+        var c = [];
+
+        // store may be filtered so get all records
+        var snapshot = this.store.snapshot || this.store.data;
+
+        snapshot.each(function(r) {
+            if (r.get(this.checkField)) {
+                c.push(r.get(field));
+            }
+        }, this);
+
+        return c.join(this.separator);
+    } // eo function getCheckedValue
+    // }}}
+    // {{{
+    /**
+     * beforequery event handler - handles multiple selections
+     * @param {Object} qe query event
+     * @private
+     */
+    , onBeforeQuery: function(qe) {
+        qe.query = qe.query.replace(new RegExp(this.getCheckedDisplay() + '[ ' + this.separator + ']*'), '');
+    } // eo function onBeforeQuery
+    // }}}
+    // {{{
+    /**
+     * blur event handler - runs only when real blur event is fired
+     */
+    , onRealBlur: function() {
+        this.list.hide();
+        var rv = this.getRawValue();
+        var rva = rv.split(new RegExp(RegExp.escape(this.separator) + ' *'));
+        var va = [];
+        var snapshot = this.store.snapshot || this.store.data;
+
+        // iterate through raw values and records and check/uncheck items
+        Ext.each(rva, function(v) {
+            snapshot.each(function(r) {
+                if (v === r.get(this.displayField)) {
+                    va.push(r.get(this.valueField));
+                }
+            }, this);
+        }, this);
+        this.setValue(va.join(this.separator));
+        this.store.clearFilter();
+    } // eo function onRealBlur
+    // }}}
+    // {{{
+    /**
+     * Combo's onSelect override
+     * @private
+     * @param {Ext.data.Record} record record that has been selected in the list
+     * @param {Number} index index of selected (clicked) record
+     */
+    , onSelect: function(record, index) {
+        if (this.fireEvent('beforeselect', this, record, index) !== false) {
+
+            // toggle checked field
+            record.set(this.checkField, !record.get(this.checkField));
+
+            // display full list
+            if (this.store.isFiltered()) {
+                this.doQuery(this.allQuery);
+            }
+
+            // set (update) value and fire event
+            this.setValue(this.getCheckedValue());
             this.fireEvent('select', this, record, index);
         }
-       } // eo function onSelect
-       // }}}
-       // {{{
-       /**
-        * Sets the value of the LovCombo
-        * @param {Mixed} v value
-        */
-       ,setValue:function(v) {
-               if(v) {
-                       v = '' + v;
-                       if(this.valueField) {
-                               this.store.clearFilter();
-                               this.store.each(function(r) {
-                                       var checked = !(!v.match(
-                                                '(^|' + this.separator + ')' + RegExp.escape(r.get(this.valueField))
-                                               +'(' + this.separator + '|$)'))
-                                       ;
-
-                                       r.set(this.checkField, checked);
-                               }, this);
-                               this.value = this.getCheckedValue();
-                               this.setRawValue(this.getCheckedDisplay());
-                               if(this.hiddenField) {
-                                       this.hiddenField.value = this.value;
-                               }
-                       }
-                       else {
-                               this.value = v;
-                               this.setRawValue(v);
-                               if(this.hiddenField) {
-                                       this.hiddenField.value = v;
-                               }
-                       }
-                       if(this.el) {
-                               this.el.removeClass(this.emptyClass);
-                       }
-               }
-               else {
-                       this.clearValue();
-               }
-       } // eo function setValue
-       // }}}
-       // {{{
-       /**
-        * Selects all items
-        */
-       ,selectAll:function() {
-        this.store.each(function(record){
+    } // eo function onSelect
+    // }}}
+    // {{{
+    /**
+     * Sets the value of the LovCombo
+     * @param {Mixed} v value
+     */
+    , setValue: function(v) {
+        if (v) {
+            v = '' + v;
+            if (this.valueField) {
+                this.store.clearFilter();
+                this.store.each(function(r) {
+                    var checked = !(!v.match(
+                            '(^|' + this.separator + ')' + RegExp.escape(r.get(this.valueField))
+                            '(' + this.separator + '|$)'))
+                            ;
+
+                    r.set(this.checkField, checked);
+                }, this);
+                this.value = this.getCheckedValue();
+                this.setRawValue(this.getCheckedDisplay());
+                if (this.hiddenField) {
+                    this.hiddenField.value = this.value;
+                }
+            }
+            else {
+                this.value = v;
+                this.setRawValue(v);
+                if (this.hiddenField) {
+                    this.hiddenField.value = v;
+                }
+            }
+            if (this.el) {
+                this.el.removeClass(this.emptyClass);
+            }
+        }
+        else {
+            this.clearValue();
+        }
+    } // eo function setValue
+    // }}}
+    // {{{
+    /**
+     * Selects all items
+     */
+    , selectAll: function() {
+        this.store.each(function(record) {
             // toggle checked field
             record.set(this.checkField, true);
         }, this);
@@ -1217,17 +1205,17 @@ Ext.ux.form.LovCombo = Ext.extend(Ext.form.ComboBox, {
         this.doQuery(this.allQuery);
         this.setValue(this.getCheckedValue());
     } // eo full selectAll
-       // }}}
-       // {{{
-       /**
-        * Deselects all items. Synonym for clearValue
-        */
-    ,deselectAll:function() {
-               this.clearValue();
+    // }}}
+    // {{{
+    /**
+     * Deselects all items. Synonym for clearValue
+     */
+    , deselectAll: function() {
+        this.clearValue();
     } // eo full deselectAll 
-       // }}}
+    // }}}
 
 }); // eo extend
+
 // register xtype
-Ext.reg('lovcombo', Ext.ux.form.LovCombo); 
+Ext.reg('lovcombo', Ext.ux.form.LovCombo);
index 8c7108d1a014cd59c2f3bdaae7a7751e304b9dda..f6648609876fdf0e48236080554bfd78182ceea2 100644 (file)
  */
 tvheadend.idnode_enum_stores = {}
 
-tvheadend.idnode_get_enum = function ( conf )
+tvheadend.idnode_get_enum = function(conf)
 {
-  /* Build key */
-  var key = conf.url;
-  if (conf.params)
-    key += '?' + Ext.util.JSON.encode(conf.params);
-  if (conf.event)
-    key += '+' + conf.event;
-
-  /* Use cached */
-  if (key in tvheadend.idnode_enum_stores)
-    return tvheadend.idnode_enum_stores[key];
-
-  /* Build combobox */
-  var st = new Ext.data.JsonStore({
-    root       : conf.root   || 'entries',
-    url        : conf.url,
-    baseParams : conf.params || {},
-    fields     : conf.fields || [ 'key', 'val' ],
-    id         : conf.id     || 'key',
-    autoLoad   : true,
-    sortInfo   : {
-      field     : 'val',
-      direction : 'ASC'
-    }
-  });
-  tvheadend.idnode_enum_stores[key] = st;
-
-  /* Event to update */
-  if (conf.event) {
-    tvheadend.comet.on(conf.event, function(){
-      st.reload();
+    /* Build key */
+    var key = conf.url;
+    if (conf.params)
+        key += '?' + Ext.util.JSON.encode(conf.params);
+    if (conf.event)
+        key += '+' + conf.event;
+
+    /* Use cached */
+    if (key in tvheadend.idnode_enum_stores)
+        return tvheadend.idnode_enum_stores[key];
+
+    /* Build combobox */
+    var st = new Ext.data.JsonStore({
+        root: conf.root || 'entries',
+        url: conf.url,
+        baseParams: conf.params || {},
+        fields: conf.fields || ['key', 'val'],
+        id: conf.id || 'key',
+        autoLoad: true,
+        sortInfo: {
+            field: 'val',
+            direction: 'ASC'
+        }
     });
-  }
+    tvheadend.idnode_enum_stores[key] = st;
+
+    /* Event to update */
+    if (conf.event) {
+        tvheadend.comet.on(conf.event, function() {
+            st.reload();
+        });
+    }
 
-  return st;
-}
+    return st;
+};
 
 json_decode = function(d)
 {
-  if (d && d.responseText) {
-    d = Ext.util.JSON.decode(d.responseText)
-    if (d.entries)
-      d = d.entries;
-    if (d.nodes)
-      d = d.nodes;
-  } else {
-    d = []
-  }
-  return d;
-}
+    if (d && d.responseText) {
+        d = Ext.util.JSON.decode(d.responseText);
+        if (d.entries)
+            d = d.entries;
+        if (d.nodes)
+            d = d.nodes;
+    } else {
+        d = [];
+    }
+    return d;
+};
 
 /*
  * Build enum data store
  */
 tvheadend.idnode_enum_store = function(f)
 {
-  var store = null;
-
-  /* API fetch */
-  if (f.enum.type == 'api') {
-    return tvheadend.idnode_get_enum({
-      url    : 'api/' + f.enum.uri,
-      params : f.enum.params,
-      event  : f.enum.event
-    });
-  }
-
-  switch (f.type) {
-    case 'str':
-      if (f.enum.length > 0 && f.enum[0] instanceof Object)
-        store = new Ext.data.JsonStore({
-          id      : 'key',
-          fields  : [ 'key', 'val' ],
-          data    : f.enum,
+    var store = null;
+
+    /* API fetch */
+    if (f.enum.type === 'api') {
+        return tvheadend.idnode_get_enum({
+            url: 'api/' + f.enum.uri,
+            params: f.enum.params,
+            event: f.enum.event
         });
-      else
-        store = f.enum;
-      break;
-    case 'int':
-    case 'u32':
-    case 'u16':
-    case 'dbl':
-      var data  = null;
-      if (f.enum.length > 0 && f.enum[0] instanceof Object) {
-        data = f.enum;
-      } else {
-        data = []
-        for ( i = 0; i < f.enum.length; i++ )
-          data.push({ key: i, val: f.enum[i]});
-      }
-      store = new Ext.data.JsonStore({
-        id     : 'key',
-        fields : [ 'key', 'val' ],
-        data   : data
-      });
-      break;
-  }
-  return store;
-}
-
-tvheadend.IdNodeField = function (conf)
-{
-  /*
-   * Properties
-   */
-  this.id     = conf.id;
-  this.text   = conf.caption || this.id;
-  this.type   = conf.type;
-  this.list   = conf.list;
-  this.rdonly = conf.rdonly;
-  this.wronly = conf.wronly;
-  this.wronce = conf.wronce;
-  this.hidden = conf.hidden || conf.advanced;
-  this.enum   = conf.enum;
-  this.store  = null;
-  if (this.enum)
-    this.store = tvheadend.idnode_enum_store(this);
-  this.ordered = false;
-
-  /*
-   * Methods
-   */
-
-  this.column = function ()
-  {
-    var w = 300;
-    var ftype = 'string';
-    if (this.type == 'int' || this.type == 'u32' || 
-        this.type == 'u16' || this.type == 'dbl') {
-      ftype = 'numeric';
-      w = 80;
-    } else if (this.type == 'bool') {
-      ftype = 'boolean';
-      w = 60;
     }
-    if (this.enum || this.list)
-      w = 300;
-    return {
-      width    : w,
-      dataIndex: this.id,
-      header   : this.text,
-      editor   : this.editor({create: false}),
-      renderer : this.renderer(),
-      hidden   : this.hidden,
-      filter   : {
-        type      : ftype,
-        dataIndex : this.id
-      }
-    };
-  }
-
-  this.renderer = function ()
-  {
-    if (!this.store)
-      return null;
-
-    var st = this.store;
-    return function (v) {
-      if (st && st instanceof Ext.data.JsonStore) {
-        var t = []
-        var d;
-        if (v.push)
-          d = v;
-        else
-          d = [ v ];
-        for (var i = 0; i < d.length; i++) {
-          var r = st.find('key', d[i]);
-          if (r != -1) {
-            var nv = st.getAt(r).get('val');
-            if (nv)
-              t.push(nv);
-          }
-        }
-        v = t.join(',');
-      }
-      return v;
-    };
-  }
-
-  this.editor   = function (conf)
-  {
-    var cons = null;
-
-    /* Editable? */
-    var d = this.rdonly;
-    if (this.wronly && !conf.create)
-      d = false;
-
-    /* Basic */
-    var c = {
-      fieldLabel : this.text,
-      name       : this.id,
-      value      : conf.value || null,
-      disabled   : d,
-      width      : 300,
-    };
-
-    /* ComboBox */
-    if (this.enum) {
-      cons = Ext.form.ComboBox;
-      if (this.list)
-        cons = Ext.ux.form.LovCombo;
-
-      /* Combo settings */
-      c['mode']           = 'local';
-      c['valueField']     = 'key';
-      c['displayField']   = 'val';
-      c['store']          = this.store;
-      c['typeAhead']      = true;
-      c['forceSelection'] = false;
-      c['triggerAction']  = 'all',
-      c['emptyText']      = 'Select ' + this.text + ' ...';
-    
-    /* Single */
-    } else {
-      switch (this.type) {
-        case 'bool':
-          cons = Ext.form.Checkbox;
-        break;
 
+    switch (f.type) {
+        case 'str':
+            if (f.enum.length > 0 && f.enum[0] instanceof Object)
+                store = new Ext.data.JsonStore({
+                    id: 'key',
+                    fields: ['key', 'val'],
+                    data: f.enum
+                });
+            else
+                store = f.enum;
+            break;
         case 'int':
         case 'u32':
         case 'u16':
         case 'dbl':
-          cons = Ext.form.NumberField;
-          break;
-
-        default:
-          cons = Ext.form.TextField;
-          break;
-      }
+            var data = null;
+            if (f.enum.length > 0 && f.enum[0] instanceof Object) {
+                data = f.enum;
+            } else {
+                data = [];
+                for (i = 0; i < f.enum.length; i++)
+                    data.push({key: i, val: f.enum[i]});
+            }
+            store = new Ext.data.JsonStore({
+                id: 'key',
+                fields: ['key', 'val'],
+                data: data
+            });
+            break;
     }
+    return store;
+};
 
-    return new cons(c);
-  }
-}
+tvheadend.IdNodeField = function(conf)
+{
+    /*
+     * Properties
+     */
+    this.id = conf.id;
+    this.text = conf.caption || this.id;
+    this.type = conf.type;
+    this.list = conf.list;
+    this.rdonly = conf.rdonly;
+    this.wronly = conf.wronly;
+    this.wronce = conf.wronce;
+    this.hidden = conf.hidden || conf.advanced;
+    this.enum = conf.enum;
+    this.store = null;
+    if (this.enum)
+        this.store = tvheadend.idnode_enum_store(this);
+    this.ordered = false;
+
+    /*
+     * Methods
+     */
+
+    this.column = function()
+    {
+        var w = 300;
+        var ftype = 'string';
+        if (this.type === 'int' || this.type === 'u32' ||
+                this.type === 'u16' || this.type === 'dbl') {
+            ftype = 'numeric';
+            w = 80;
+        } else if (this.type === 'bool') {
+            ftype = 'boolean';
+            w = 60;
+        }
+        if (this.enum || this.list)
+            w = 300;
+        return {
+            width: w,
+            dataIndex: this.id,
+            header: this.text,
+            editor: this.editor({create: false}),
+            renderer: this.renderer(),
+            hidden: this.hidden,
+            filter: {
+                type: ftype,
+                dataIndex: this.id
+            }
+        };
+    };
+
+    this.renderer = function()
+    {
+        if (!this.store)
+            return null;
+
+        var st = this.store;
+        return function(v) {
+            if (st && st instanceof Ext.data.JsonStore) {
+                var t = [];
+                var d;
+                if (v.push)
+                    d = v;
+                else
+                    d = [v];
+                for (var i = 0; i < d.length; i++) {
+                    var r = st.find('key', d[i]);
+                    if (r !== -1) {
+                        var nv = st.getAt(r).get('val');
+                        if (nv)
+                            t.push(nv);
+                    }
+                }
+                v = t.join(',');
+            }
+            return v;
+        };
+    };
+
+    this.editor = function(conf)
+    {
+        var cons = null;
+
+        /* Editable? */
+        var d = this.rdonly;
+        if (this.wronly && !conf.create)
+            d = false;
+
+        /* Basic */
+        var c = {
+            fieldLabel: this.text,
+            name: this.id,
+            value: conf.value || null,
+            disabled: d,
+            width: 300
+        };
+
+        /* ComboBox */
+        if (this.enum) {
+            cons = Ext.form.ComboBox;
+            if (this.list)
+                cons = Ext.ux.form.LovCombo;
+
+            /* Combo settings */
+            c['mode'] = 'local';
+            c['valueField'] = 'key';
+            c['displayField'] = 'val';
+            c['store'] = this.store;
+            c['typeAhead'] = true;
+            c['forceSelection'] = false;
+            c['triggerAction'] = 'all',
+                    c['emptyText'] = 'Select ' + this.text + ' ...';
+
+            /* Single */
+        } else {
+            switch (this.type) {
+                case 'bool':
+                    cons = Ext.form.Checkbox;
+                    break;
+
+                case 'int':
+                case 'u32':
+                case 'u16':
+                case 'dbl':
+                    cons = Ext.form.NumberField;
+                    break;
+
+                default:
+                    cons = Ext.form.TextField;
+                    break;
+            }
+        }
+
+        return new cons(c);
+    };
+};
 
 /*
  * IdNode
  */
-tvheadend.IdNode = function (conf)
+tvheadend.IdNode = function(conf)
 {
-  /*
-   * Properties
-   */
-  this.clazz  = conf.class;
-  this.text   = conf.caption || this.clazz;
-  this.props  = conf.props;
-  this.order  = [];
-  this.fields = [];
-  for (var i = 0; i < this.props.length; i++) {
-    this.fields.push(new tvheadend.IdNodeField(this.props[i]));
-  }
-  var o = [];
-  if (conf.order)
-    o = conf.order.split(',');
-  if (o) {
-    while (o.length < this.fields.length)
-      o.push(null);
-    for (var i = 0; i < o.length; i++) {
-      this.order[i] = null;
-      if (o[i]) {
-        for (var j = 0; j < this.fields.length; j++) {
-          if (this.fields[j].id == o[i]) {
-            this.order[i] = this.fields[j];
-            this.fields[j].ordered = true;
-            break;
-          }
-        }
-      }
+    /*
+     * Properties
+     */
+    this.clazz = conf.class;
+    this.text = conf.caption || this.clazz;
+    this.props = conf.props;
+    this.order = [];
+    this.fields = [];
+    for (var i = 0; i < this.props.length; i++) {
+        this.fields.push(new tvheadend.IdNodeField(this.props[i]));
     }
-    for (var i = 0; i < o.length; i++) {
-      if (this.order[i] == null) {
-        for (var j = 0; j < this.fields.length; j++) {
-          if (!this.fields[j].ordered) {
-            this.fields[j].ordered = true;
-            this.order[i] = this.fields[j];
-            break;
-          }
+    var o = [];
+    if (conf.order)
+        o = conf.order.split(',');
+    if (o) {
+        while (o.length < this.fields.length)
+            o.push(null);
+        for (var i = 0; i < o.length; i++) {
+            this.order[i] = null;
+            if (o[i]) {
+                for (var j = 0; j < this.fields.length; j++) {
+                    if (this.fields[j].id === o[i]) {
+                        this.order[i] = this.fields[j];
+                        this.fields[j].ordered = true;
+                        break;
+                    }
+                }
+            }
+        }
+        for (var i = 0; i < o.length; i++) {
+            if (this.order[i] == null) {
+                for (var j = 0; j < this.fields.length; j++) {
+                    if (!this.fields[j].ordered) {
+                        this.fields[j].ordered = true;
+                        this.order[i] = this.fields[j];
+                        break;
+                    }
+                }
+            }
         }
-      }
     }
-  }
 
-  /*
-   * Methods
-   */
+    /*
+     * Methods
+     */
+
+    this.length = function() {
+        return this.fields.length;
+    };
 
-  this.length = function () {
-    return this.fields.length;
-  }
-  
-  this.field = function ( index ) {
-    if (this.order) return this.order[index]; else return this.fields[index];
-  }
-}
+    this.field = function(index) {
+        if (this.order)
+            return this.order[index];
+        else
+            return this.fields[index];
+    };
+};
 
 
 /*
@@ -307,205 +310,208 @@ tvheadend.IdNode = function (conf)
  */
 tvheadend.idnode_editor_field = function(f, create)
 {
-  var d = f.rdonly || false;
-  if (f.wronly && !create) d = false;
-  var value = f.value;
-  if (value == null)
-    value = f.default;
-    
-  /* Enumerated (combobox) type */
-  if (f.enum) {
-    var cons = Ext.form.ComboBox;
-    if (f.list)
-      cons = Ext.ux.form.LovCombo;
-    var st = tvheadend.idnode_enum_store(f);
-    var r = new cons({
-      fieldLabel      : f.caption,
-      name            : f.id,
-      value           : value,
-      disabled        : d,
-      width           : 300,
-      mode            : 'local',
-      valueField      : 'key',
-      displayField    : 'val',
-      store           : st,
-      typeAhead       : true, // TODO: this does strange things in multi
-      forceSelection  : false,
-      triggerAction   : 'all',
-      emptyText       :'Select ' + f.caption +' ...',
-      listeners       : {
-        beforequery: function(qe){
-          delete qe.combo.lastQuery;
+    var d = f.rdonly || false;
+    if (f.wronly && !create)
+        d = false;
+    var value = f.value;
+    if (value == null)
+        value = f.default;
+
+    /* Enumerated (combobox) type */
+    if (f.enum) {
+        var cons = Ext.form.ComboBox;
+        if (f.list)
+            cons = Ext.ux.form.LovCombo;
+        var st = tvheadend.idnode_enum_store(f);
+        var r = new cons({
+            fieldLabel: f.caption,
+            name: f.id,
+            value: value,
+            disabled: d,
+            width: 300,
+            mode: 'local',
+            valueField: 'key',
+            displayField: 'val',
+            store: st,
+            typeAhead: true, // TODO: this does strange things in multi
+            forceSelection: false,
+            triggerAction: 'all',
+            emptyText: 'Select ' + f.caption + ' ...',
+            listeners: {
+                beforequery: function(qe) {
+                    delete qe.combo.lastQuery;
+                }
+            }
+        });
+        if (st.on) {
+            var fn = function() {
+                st.un('load', fn);
+                r.setValue(value); // HACK: to get extjs to display properly
+            };
+            st.on('load', fn);
         }
-      }
-    });
-    if (st.on) {
-      var fn = function() {
-        st.un('load', fn);
-        r.setValue(value); // HACK: to get extjs to display properly
-      };
-      st.on('load', fn);
+        return r;
+        /* TODO: listeners for regexp?
+         listeners       : { 
+         keyup: function() {
+         this.store.filter('val', this.getRawValue(), true, false);
+         },
+         beforequery: function(queryEvent) {
+         queryEvent.combo.onLoad();
+         // prevent doQuery from firing and clearing out my filter.
+         return false; 
+         }
+         }
+         */
     }
-    return r;
-    /* TODO: listeners for regexp?
-    listeners       : { 
-      keyup: function() {
-        this.store.filter('val', this.getRawValue(), true, false);
-      },
-      beforequery: function(queryEvent) {
-        queryEvent.combo.onLoad();
-        // prevent doQuery from firing and clearing out my filter.
-        return false; 
-      }
+
+    /* Singular */
+    switch (f.type) {
+        case 'str':
+            return new Ext.form.TextField({
+                fieldLabel: f.caption,
+                name: f.id,
+                value: value,
+                disabled: d,
+                width: 300
+            });
+            break;
+
+        case 'bool':
+            return new Ext.ux.form.XCheckbox({
+                fieldLabel: f.caption,
+                name: f.id,
+                checked: value,
+                disabled: d
+            });
+            break;
+
+        case 'int':
+        case 'u32':
+        case 'u16':
+        case 'dbl':
+            return new Ext.form.NumberField({
+                fieldLabel: f.caption,
+                name: f.id,
+                value: value,
+                disabled: d,
+                width: 300
+            });
+            break;
     }
-    */
-  }
-
-  /* Singular */
-  switch(f.type) {
-    case 'str':
-      return new Ext.form.TextField({
-        fieldLabel  : f.caption,
-        name        : f.id,
-        value       : value,
-        disabled    : d,
-        width       : 300
-      });
-      break;
-
-    case 'bool':
-      return new Ext.ux.form.XCheckbox({
-        fieldLabel  : f.caption,
-        name        : f.id,
-        checked     : value,
-        disabled    : d
-      });
-      break;
-
-    case 'int':
-    case 'u32':
-    case 'u16':
-    case 'dbl':
-      return new Ext.form.NumberField({
-        fieldLabel  : f.caption,
-        name        : f.id,
-        value       : value,
-        disabled    : d,
-        width       : 300
-      });
-      break;
-  }
-  return null;
-}
+    return null;
+};
 
 /*
  * ID node editor form fields
  */
-tvheadend.idnode_editor_form = function ( d, panel )
+tvheadend.idnode_editor_form = function(d, panel)
 {
-  var af = [];
-  var rf = [];
-  var df = [];
-
-  /* Fields */
-  for (var i = 0; i < d.length; i++) {
-    var f = tvheadend.idnode_editor_field(d[i]);
-    if (!f) continue;
-    if (d[i].rdonly)
-      rf.push(f)
-    else if (d[i].advanced)
-      af.push(f);
-    else
-      df.push(f);
-  }
-  if (df.length) {
-    panel.add(new Ext.form.FieldSet({
-      title       : 'Basic Settings',
-      autoHeight  : true,
-      autoWidth   : true,
-      collapsible : true,
-      collapsed   : false,
-      items       : df
-    }));
-  }
-  if (af.length) {
-    panel.add(new Ext.form.FieldSet({
-      title       : 'Advanced Settings',
-      autoHeight  : true,
-      autoWidth   : true,
-      collapsible : true,
-      collapsed   : false,//true,
-      items       : af
-    }));
-  }
-  if (rf.length) {
-    panel.add(new Ext.form.FieldSet({
-      title       : 'Read-only Info',
-      autoHeight  : true,
-      autoWidth   : true,
-      collapsible : true,
-      collapsed   : false,//true,
-      items       : rf
-    }));
-  }
-  panel.doLayout();
-}
+    var af = [];
+    var rf = [];
+    var df = [];
+
+    /* Fields */
+    for (var i = 0; i < d.length; i++) {
+        var f = tvheadend.idnode_editor_field(d[i]);
+        if (!f)
+            continue;
+        if (d[i].rdonly)
+            rf.push(f);
+        else if (d[i].advanced)
+            af.push(f);
+        else
+            df.push(f);
+    }
+    if (df.length) {
+        panel.add(new Ext.form.FieldSet({
+            title: 'Basic Settings',
+            autoHeight: true,
+            autoWidth: true,
+            collapsible: true,
+            collapsed: false,
+            items: df
+        }));
+    }
+    if (af.length) {
+        panel.add(new Ext.form.FieldSet({
+            title: 'Advanced Settings',
+            autoHeight: true,
+            autoWidth: true,
+            collapsible: true,
+            collapsed: false, //true,
+            items: af
+        }));
+    }
+    if (rf.length) {
+        panel.add(new Ext.form.FieldSet({
+            title: 'Read-only Info',
+            autoHeight: true,
+            autoWidth: true,
+            collapsible: true,
+            collapsed: false, //true,
+            items: rf
+        }));
+    }
+    panel.doLayout();
+};
 
 /*
  * ID node editor panel
  */
 tvheadend.idnode_editor = function(item, conf)
 {
-  var panel  = null;
-  var buttons = [];
-
-  /* Buttons */
-  var saveBtn = new Ext.Button({
-    text  : 'Save',
-    handler     : function() {
-      var node = panel.getForm().getFieldValues();
-      node.uuid  = item.uuid;
-      Ext.Ajax.request({
-        url      : 'api/idnode/save',
-        params   : {
-          node: Ext.encode(node)
-        },
-        success : function(d) {
-          if (conf.win) conf.win.hide();
+    var panel = null;
+    var buttons = [];
+
+    /* Buttons */
+    var saveBtn = new Ext.Button({
+        text: 'Save',
+        handler: function() {
+            var node = panel.getForm().getFieldValues();
+            node.uuid = item.uuid;
+            Ext.Ajax.request({
+                url: 'api/idnode/save',
+                params: {
+                    node: Ext.encode(node)
+                },
+                success: function(d) {
+                    if (conf.win)
+                        conf.win.hide();
+                }
+            });
         }
-      });
+    });
+    buttons.push(saveBtn);
+
+    if (conf.help) {
+        var helpBtn = new Ext.Button({
+            text: 'Help',
+            handler: conf.help
+        });
+        buttons.push(helpBtn);
     }
-  });
-  buttons.push(saveBtn);
 
-  if (conf.help) {
-    var helpBtn = new Ext.Button({
-      text    : 'Help',
-      handler : conf.help
+    panel = new Ext.FormPanel({
+        title: conf.title || null,
+        frame: true,
+        border: true,
+        bodyStyle: 'padding: 5px',
+        labelAlign: 'left',
+        labelWidth: 200,
+        autoWidth: true,
+        autoHeight: !conf.fixedHeight,
+        width: 600,
+        //defaults: {width: 330},
+        defaultType: 'textfield',
+        buttonAlign: 'left',
+        buttons: buttons
     });
-    buttons.push(helpBtn);
-  }
-
-  panel = new Ext.FormPanel({
-    title       : conf.title || null,
-    frame       : true,
-    border      : true,
-    bodyStyle   : 'padding: 5px',
-    labelAlign  : 'left',
-    labelWidth  : 200,
-    autoWidth   : true,
-    autoHeight  : !conf.fixedHeight,
-    width  : 600,
-    //defaults: {width: 330},
-    defaultType : 'textfield',
-    buttonAlign : 'left',
-    buttons     : buttons
-  });
-
-  tvheadend.idnode_editor_form(item.props || item.params, panel);
-
-  return panel;
-}
+
+    tvheadend.idnode_editor_form(item.props || item.params, panel);
+
+    return panel;
+};
 
 
 /*
@@ -513,139 +519,139 @@ tvheadend.idnode_editor = function(item, conf)
  */
 tvheadend.idnode_create = function(conf)
 {
-  var puuid  = null;
-  var panel  = null;
-  var pclass = null;
-
-  /* Buttons */
-  var saveBtn = new Ext.Button({
-    tooltip     : 'Create new entry',
-    text        : 'Create',
-    hidden      : true,
-    handler     : function(){
-      params = conf.create.params || {}
-      if (puuid)
-        params['uuid'] = puuid;
-      if (pclass)
-        params['class'] = pclass
-      params['conf'] = Ext.encode(panel.getForm().getFieldValues());
-      Ext.Ajax.request({
-        url    : conf.create.url || conf.url + '/create',
-        params : params,
-        success : function(d) {
-          win.close();
+    var puuid = null;
+    var panel = null;
+    var pclass = null;
+
+    /* Buttons */
+    var saveBtn = new Ext.Button({
+        tooltip: 'Create new entry',
+        text: 'Create',
+        hidden: true,
+        handler: function() {
+            params = conf.create.params || {};
+            if (puuid)
+                params['uuid'] = puuid;
+            if (pclass)
+                params['class'] = pclass;
+            params['conf'] = Ext.encode(panel.getForm().getFieldValues());
+            Ext.Ajax.request({
+                url: conf.create.url || conf.url + '/create',
+                params: params,
+                success: function(d) {
+                    win.close();
+                }
+            });
         }
-      });
-    }
-  });
-  var undoBtn = new Ext.Button({
-    tooltip     : 'Cancel operation',
-    text        : 'Cancel',
-    handler     : function(){
-      win.close();
-    }
-  });
-
-  /* Form */
-  panel = new Ext.FormPanel({
-    frame       : true,
-    border      : true,
-    bodyStyle   : 'padding: 5px',
-    labelAlign  : 'left',
-    labelWidth  : 200,
-    autoWidth   : true,
-    autoHeight  : true,
-    defaultType : 'textfield',
-    buttonAlign : 'left',
-    items       : [],
-    buttons     : [ undoBtn, saveBtn ]
-  });
-
-  /* Create window */
-  win = new Ext.Window({
-    title       : 'Add ' + conf.titleS,
-    layout      : 'fit',
-    autoWidth   : true,
-    autoHeight  : true,
-    plain       : true,
-    items       : panel
-  });
-
-  
-  /* Do we need to first select a class? */
-  if (conf.select) {
-    var store = conf.select.store;
-    if (!store) {
-      store = new Ext.data.JsonStore({
-        root        : 'entries',
-        url         : conf.select.url || conf.url,
-        baseParams  : conf.select.params,
-        fields      : [ conf.select.valueField, conf.select.displayField ]
-      });
-    }
-    var select = null;
-    if (conf.select.propField) {
-      select = function (s, n, o) {
-        var r =  store.getAt(s.selectedIndex);
-        if (r) {
-          var d = r.get(conf.select.propField);
-          if (d) {
-            pclass = r.get(conf.select.valueField);
-            win.setTitle('Add ' + s.lastSelectionText);
-            panel.remove(s);
-            tvheadend.idnode_editor_form(d, panel);
-            saveBtn.setVisible(true);
-          }
+    });
+    var undoBtn = new Ext.Button({
+        tooltip: 'Cancel operation',
+        text: 'Cancel',
+        handler: function() {
+            win.close();
         }
-      }
-    } else {
-      select = function (s, n, o) {
-        params = conf.select.clazz.params || {};
-        params['uuid'] = puuid = n.id;
-        Ext.Ajax.request({
-          url     : conf.select.clazz.url || conf.select.url || conf.url,
-          success : function(d) {
-            panel.remove(s);
-            d = json_decode(d);
-            tvheadend.idnode_editor_form(d.props, panel);
-            saveBtn.setVisible(true);
-          },
-          params  : params
-        });
-      };
-    }
+    });
 
-    /* Parent selector */
-    var combo = new Ext.form.ComboBox({
-      fieldLabel    : conf.select.label,
-      grow          : true,
-      editable      : false,
-      allowBlank    : false,
-      displayField  : conf.select.displayField,
-      valueField    : conf.select.valueField,
-      mode          : 'remote',
-      triggerAction : 'all',
-      store         : store,
-      listeners     : {
-        select : select
-      }
+    /* Form */
+    panel = new Ext.FormPanel({
+        frame: true,
+        border: true,
+        bodyStyle: 'padding: 5px',
+        labelAlign: 'left',
+        labelWidth: 200,
+        autoWidth: true,
+        autoHeight: true,
+        defaultType: 'textfield',
+        buttonAlign: 'left',
+        items: [],
+        buttons: [undoBtn, saveBtn]
     });
 
-    panel.add(combo);
-    win.show();
-  } else {
-    Ext.Ajax.request({
-      url     : conf.url + '/class',
-      params  : conf.params,
-      success : function(d) {
-        d = json_decode(d);
-        tvheadend.idnode_editor_form(d.props, panel);
-        saveBtn.setVisible(true);
-        win.show();
-      }
+    /* Create window */
+    win = new Ext.Window({
+        title: 'Add ' + conf.titleS,
+        layout: 'fit',
+        autoWidth: true,
+        autoHeight: true,
+        plain: true,
+        items: panel
     });
-  }
-}
+
+
+    /* Do we need to first select a class? */
+    if (conf.select) {
+        var store = conf.select.store;
+        if (!store) {
+            store = new Ext.data.JsonStore({
+                root: 'entries',
+                url: conf.select.url || conf.url,
+                baseParams: conf.select.params,
+                fields: [conf.select.valueField, conf.select.displayField]
+            });
+        }
+        var select = null;
+        if (conf.select.propField) {
+            select = function(s, n, o) {
+                var r = store.getAt(s.selectedIndex);
+                if (r) {
+                    var d = r.get(conf.select.propField);
+                    if (d) {
+                        pclass = r.get(conf.select.valueField);
+                        win.setTitle('Add ' + s.lastSelectionText);
+                        panel.remove(s);
+                        tvheadend.idnode_editor_form(d, panel);
+                        saveBtn.setVisible(true);
+                    }
+                }
+            };
+        } else {
+            select = function(s, n, o) {
+                params = conf.select.clazz.params || {};
+                params['uuid'] = puuid = n.id;
+                Ext.Ajax.request({
+                    url: conf.select.clazz.url || conf.select.url || conf.url,
+                    success: function(d) {
+                        panel.remove(s);
+                        d = json_decode(d);
+                        tvheadend.idnode_editor_form(d.props, panel);
+                        saveBtn.setVisible(true);
+                    },
+                    params: params
+                });
+            };
+        }
+
+        /* Parent selector */
+        var combo = new Ext.form.ComboBox({
+            fieldLabel: conf.select.label,
+            grow: true,
+            editable: false,
+            allowBlank: false,
+            displayField: conf.select.displayField,
+            valueField: conf.select.valueField,
+            mode: 'remote',
+            triggerAction: 'all',
+            store: store,
+            listeners: {
+                select: select
+            }
+        });
+
+        panel.add(combo);
+        win.show();
+    } else {
+        Ext.Ajax.request({
+            url: conf.url + '/class',
+            params: conf.params,
+            success: function(d) {
+                d = json_decode(d);
+                tvheadend.idnode_editor_form(d.props, panel);
+                saveBtn.setVisible(true);
+                win.show();
+            }
+        });
+    }
+};
 
 
 /*
@@ -653,502 +659,506 @@ tvheadend.idnode_create = function(conf)
  */
 tvheadend.idnode_grid = function(panel, conf)
 {
-  function build (d)
-  {
-    var columns = conf.lcol || [];
-    var filters = [];
-    var fields  = [];
-    var buttons = [];
-    var plugins = conf.plugins || [];
-    var saveBtn = null;
-    var undoBtn = null;
-    var addBtn  = null;
-    var delBtn  = null;
-    var upBtn   = null;
-    var downBtn = null;
-    var editBtn = null;
-
-    /* Model */
-    var idnode = new tvheadend.IdNode(d);
-    for (var i = 0; i < idnode.length(); i++) {
-      var f = idnode.field(i);
-      var c = f.column();
-      fields.push(f.id);
-      columns.push(c);
-      if (c.filter)
-        filters.push(c.filter);
-    }
+    function build(d)
+    {
+        var columns = conf.lcol || [];
+        var filters = [];
+        var fields = [];
+        var buttons = [];
+        var plugins = conf.plugins || [];
+        var saveBtn = null;
+        var undoBtn = null;
+        var addBtn = null;
+        var delBtn = null;
+        var upBtn = null;
+        var downBtn = null;
+        var editBtn = null;
+
+        /* Model */
+        var idnode = new tvheadend.IdNode(d);
+        for (var i = 0; i < idnode.length(); i++) {
+            var f = idnode.field(i);
+            var c = f.column();
+            fields.push(f.id);
+            columns.push(c);
+            if (c.filter)
+                filters.push(c.filter);
+        }
 
-    /* Right-hand columns */
-    if (conf.rcol)
-      for (i = 0; i < conf.rcol.length; i++) 
-        columns.push(conf.rcol[i])
+        /* Right-hand columns */
+        if (conf.rcol)
+            for (i = 0; i < conf.rcol.length; i++)
+                columns.push(conf.rcol[i]);
 
-    /* Filters */
-    var filter = new Ext.ux.grid.GridFilters({
-      encode  : true,
-      local   : false,
-      filters : filters
-    });
+        /* Filters */
+        var filter = new Ext.ux.grid.GridFilters({
+            encode: true,
+            local: false,
+            filters: filters
+        });
 
-    var sort = null;
-    if (conf.sort)
-      sort = conf.sort;
-
-    /* Store */
-    var store  = new Ext.data.JsonStore({
-      root                 : 'entries',
-      url                  : conf.url + '/grid',
-      autoLoad             : true,
-      id                   : 'uuid',
-      totalProperty        : 'total',
-      fields               : fields,
-      remoteSort           : true,
-      pruneModifiedRecords : true,
-      sortInfo             : sort
-    });
+        var sort = null;
+        if (conf.sort)
+            sort = conf.sort;
+
+        /* Store */
+        var store = new Ext.data.JsonStore({
+            root: 'entries',
+            url: conf.url + '/grid',
+            autoLoad: true,
+            id: 'uuid',
+            totalProperty: 'total',
+            fields: fields,
+            remoteSort: true,
+            pruneModifiedRecords: true,
+            sortInfo: sort
+        });
 
-    /* Model */
-    var sortable = true;
-    if (conf.move)
-      sortable = false;
-    var model = new Ext.grid.ColumnModel({
-      defaultSortable : sortable,
-      columns         : columns
-    });
+        /* Model */
+        var sortable = true;
+        if (conf.move)
+            sortable = false;
+        var model = new Ext.grid.ColumnModel({
+            defaultSortable: sortable,
+            columns: columns
+        });
 
-    /* Selection */
-    var select = new Ext.grid.RowSelectionModel({
-      singleSelect    : false
-    });
+        /* Selection */
+        var select = new Ext.grid.RowSelectionModel({
+            singleSelect: false
+        });
 
-    /* Event handlers */
-    store.on('update', function(s, r, o){
-      var d = (s.getModifiedRecords().length == 0);
-      undoBtn.setDisabled(d);
-      saveBtn.setDisabled(d);
-    });
-    select.on('selectionchange', function(s){
-      if (delBtn)
-        delBtn.setDisabled(s.getCount() == 0);
-      if (upBtn) {
-        upBtn.setDisabled(s.getCount() == 0);
-        downBtn.setDisabled(s.getCount() == 0);
-      }
-      editBtn.setDisabled(s.getCount() != 1);
-      if (conf.selected)
-        conf.selected(s);
-    });
+        /* Event handlers */
+        store.on('update', function(s, r, o) {
+            var d = (s.getModifiedRecords().length === 0);
+            undoBtn.setDisabled(d);
+            saveBtn.setDisabled(d);
+        });
+        select.on('selectionchange', function(s) {
+            if (delBtn)
+                delBtn.setDisabled(s.getCount() === 0);
+            if (upBtn) {
+                upBtn.setDisabled(s.getCount() === 0);
+                downBtn.setDisabled(s.getCount() === 0);
+            }
+            editBtn.setDisabled(s.getCount() !== 1);
+            if (conf.selected)
+                conf.selected(s);
+        });
 
-    /* Top bar */
-    saveBtn = new Ext.Toolbar.Button({
-      tooltip     : 'Save pending changes (marked with red border)',
-      iconCls     : 'save',
-      text        : 'Save',
-      disabled    : true,
-      handler     : function(){
-        var mr  = store.getModifiedRecords();
-        var out = new Array();
-        for (var x = 0; x < mr.length; x++) {
-          v           = mr[x].getChanges();
-          out[x]      = v;
-          out[x].uuid = mr[x].id;
-        }
-        Ext.Ajax.request({
-           url     : 'api/idnode/save',
-           params  : {
-             node: Ext.encode(out)
-           },
-           success : function(d)
-           {
-             if (!auto.getValue()) store.reload();
-           }
+        /* Top bar */
+        saveBtn = new Ext.Toolbar.Button({
+            tooltip: 'Save pending changes (marked with red border)',
+            iconCls: 'save',
+            text: 'Save',
+            disabled: true,
+            handler: function() {
+                var mr = store.getModifiedRecords();
+                var out = new Array();
+                for (var x = 0; x < mr.length; x++) {
+                    v = mr[x].getChanges();
+                    out[x] = v;
+                    out[x].uuid = mr[x].id;
+                }
+                Ext.Ajax.request({
+                    url: 'api/idnode/save',
+                    params: {
+                        node: Ext.encode(out)
+                    },
+                    success: function(d)
+                    {
+                        if (!auto.getValue())
+                            store.reload();
+                    }
+                });
+            }
         });
-      }
-    });
-    buttons.push(saveBtn);
-    undoBtn = new Ext.Toolbar.Button({
-      tooltip     : 'Revert pending changes (marked with red border)',
-      iconCls     : 'undo',
-      text        : 'Undo',
-      disabled    : true,
-      handler     : function() {
-        store.rejectChanges();
-      }
-    });
-    buttons.push(undoBtn);
-    buttons.push('-');
-    if (conf.add) {
-      addBtn  = new Ext.Toolbar.Button({
-        tooltip     : 'Add a new entry',
-        iconCls     : 'add',
-        text        : 'Add',
-        disabled    : false,
-        handler     : function() {
-          tvheadend.idnode_create(conf.add);
-        }
-      });
-      buttons.push(addBtn);
-    }
-    if (conf.del) {
-      delBtn  = new Ext.Toolbar.Button({
-        tooltip     : 'Delete selected entries',
-        iconCls     : 'remove',
-        text        : 'Delete',
-        disabled    : true,
-        handler     : function() {
-          var r = select.getSelections();
-          if (r && r.length > 0) {
-            var uuids = []
-            for ( var i = 0; i < r.length; i++ )
-              uuids.push(r[i].id)
-            Ext.Ajax.request({
-              url     : 'api/idnode/delete',
-              params  : {
-                uuid: Ext.encode(uuids)
-              },
-              success : function(d)
-              {
-                if (!auto.getValue()) store.reload();
-              }
+        buttons.push(saveBtn);
+        undoBtn = new Ext.Toolbar.Button({
+            tooltip: 'Revert pending changes (marked with red border)',
+            iconCls: 'undo',
+            text: 'Undo',
+            disabled: true,
+            handler: function() {
+                store.rejectChanges();
+            }
+        });
+        buttons.push(undoBtn);
+        buttons.push('-');
+        if (conf.add) {
+            addBtn = new Ext.Toolbar.Button({
+                tooltip: 'Add a new entry',
+                iconCls: 'add',
+                text: 'Add',
+                disabled: false,
+                handler: function() {
+                    tvheadend.idnode_create(conf.add);
+                }
             });
-          }
+            buttons.push(addBtn);
         }
-      });
-      buttons.push(delBtn);
-    }
-    if (conf.move) {
-      upBtn  = new Ext.Toolbar.Button({
-        tooltip     : 'Move selected entries up',
-        iconCls     : 'moveup',
-        text        : 'Move Up',
-        disabled    : true,
-        handler     : function() {
-          var r = select.getSelections();
-          if (r && r.length > 0) {
-            var uuids = []
-            for ( var i = 0; i < r.length; i++ )
-              uuids.push(r[i].id)
-            Ext.Ajax.request({
-              url     : 'api/idnode/moveup',
-              params  : {
-                uuid: Ext.encode(uuids)
-              },
-              success : function(d)
-              {
-                store.reload();
-              }
+        if (conf.del) {
+            delBtn = new Ext.Toolbar.Button({
+                tooltip: 'Delete selected entries',
+                iconCls: 'remove',
+                text: 'Delete',
+                disabled: true,
+                handler: function() {
+                    var r = select.getSelections();
+                    if (r && r.length > 0) {
+                        var uuids = [];
+                        for (var i = 0; i < r.length; i++)
+                            uuids.push(r[i].id);
+                        Ext.Ajax.request({
+                            url: 'api/idnode/delete',
+                            params: {
+                                uuid: Ext.encode(uuids)
+                            },
+                            success: function(d)
+                            {
+                                if (!auto.getValue())
+                                    store.reload();
+                            }
+                        });
+                    }
+                }
             });
-          }
+            buttons.push(delBtn);
         }
-      });
-      buttons.push(upBtn);
-      downBtn  = new Ext.Toolbar.Button({
-        tooltip     : 'Move selected entries down',
-        iconCls     : 'movedown',
-        text        : 'Move Down',
-        disabled    : true,
-        handler     : function() {
-          var r = select.getSelections();
-          if (r && r.length > 0) {
-            var uuids = []
-            for ( var i = 0; i < r.length; i++ )
-              uuids.push(r[i].id)
-            Ext.Ajax.request({
-              url     : 'api/idnode/movedown',
-              params  : {
-                uuid: Ext.encode(uuids)
-              },
-              success : function(d)
-              {
-                store.reload();
-              }
+        if (conf.move) {
+            upBtn = new Ext.Toolbar.Button({
+                tooltip: 'Move selected entries up',
+                iconCls: 'moveup',
+                text: 'Move Up',
+                disabled: true,
+                handler: function() {
+                    var r = select.getSelections();
+                    if (r && r.length > 0) {
+                        var uuids = [];
+                        for (var i = 0; i < r.length; i++)
+                            uuids.push(r[i].id);
+                        Ext.Ajax.request({
+                            url: 'api/idnode/moveup',
+                            params: {
+                                uuid: Ext.encode(uuids)
+                            },
+                            success: function(d)
+                            {
+                                store.reload();
+                            }
+                        });
+                    }
+                }
             });
-          }
-        }
-      });
-      buttons.push(downBtn);
-    }
-    if (conf.add || conf.del || conf.move)
-      buttons.push('-');
-    editBtn = new Ext.Toolbar.Button({
-      tooltip     : 'Edit selected entry',
-      iconCls     : 'edit',
-      text        : 'Edit',
-      disabled    : true,
-      handler     : function() {
-        var r = select.getSelected();
-        if (r) {
-          if (conf.edittree) {
-            var p = tvheadend.idnode_tree({
-              url     : 'api/idnode/tree',
-              params  : {
-                root : r.id
-              }
-            });
-            p.setSize(800,600);
-            var w = new Ext.Window({
-              title       : 'Edit ' + conf.titleS,
-              layout      : 'fit',
-              autoWidth   : true,
-              autoHeight  : true,
-              plain       : true,
-              items       : p
+            buttons.push(upBtn);
+            downBtn = new Ext.Toolbar.Button({
+                tooltip: 'Move selected entries down',
+                iconCls: 'movedown',
+                text: 'Move Down',
+                disabled: true,
+                handler: function() {
+                    var r = select.getSelections();
+                    if (r && r.length > 0) {
+                        var uuids = [];
+                        for (var i = 0; i < r.length; i++)
+                            uuids.push(r[i].id);
+                        Ext.Ajax.request({
+                            url: 'api/idnode/movedown',
+                            params: {
+                                uuid: Ext.encode(uuids)
+                            },
+                            success: function(d)
+                            {
+                                store.reload();
+                            }
+                        });
+                    }
+                }
             });
-            w.show();
-          } else {
-            Ext.Ajax.request({
-              url     : 'api/idnode/load',
-              params  : {
-                uuid: r.id
-              },
-              success : function(d)
-              {
-                d = json_decode(d);
-                var w = null;
-                var c = { win: w };
-                var p = tvheadend.idnode_editor(d[0], c);
-                w = new Ext.Window({
-                  title       : 'Edit ' + conf.titleS,
-                  layout      : 'fit',
-                  autoWidth   : true,
-                  autoHeight  : true,
-                  plain       : true,
-                  items       : p
-                });
-                c.win = w;
-                w.show();
-              }
+            buttons.push(downBtn);
+        }
+        if (conf.add || conf.del || conf.move)
+            buttons.push('-');
+        editBtn = new Ext.Toolbar.Button({
+            tooltip: 'Edit selected entry',
+            iconCls: 'edit',
+            text: 'Edit',
+            disabled: true,
+            handler: function() {
+                var r = select.getSelected();
+                if (r) {
+                    if (conf.edittree) {
+                        var p = tvheadend.idnode_tree({
+                            url: 'api/idnode/tree',
+                            params: {
+                                root: r.id
+                            }
+                        });
+                        p.setSize(800, 600);
+                        var w = new Ext.Window({
+                            title: 'Edit ' + conf.titleS,
+                            layout: 'fit',
+                            autoWidth: true,
+                            autoHeight: true,
+                            plain: true,
+                            items: p
+                        });
+                        w.show();
+                    } else {
+                        Ext.Ajax.request({
+                            url: 'api/idnode/load',
+                            params: {
+                                uuid: r.id
+                            },
+                            success: function(d)
+                            {
+                                d = json_decode(d);
+                                var w = null;
+                                var c = {win: w};
+                                var p = tvheadend.idnode_editor(d[0], c);
+                                w = new Ext.Window({
+                                    title: 'Edit ' + conf.titleS,
+                                    layout: 'fit',
+                                    autoWidth: true,
+                                    autoHeight: true,
+                                    plain: true,
+                                    items: p
+                                });
+                                c.win = w;
+                                w.show();
+                            }
+                        });
+                    }
+                }
+            }
+        });
+        buttons.push(editBtn);
+
+        /* Hide Mode */
+        if (conf.hidemode) {
+            var hidemode = new Ext.form.ComboBox({
+                width: 100,
+                displayField: 'val',
+                valueField: 'key',
+                store: new Ext.data.ArrayStore({
+                    id: 0,
+                    fields: ['key', 'val'],
+                    data: [['default', 'Parent disabled'],
+                        ['all', 'All'],
+                        ['none', 'None']]
+                }),
+                value: 'default',
+                mode: 'local',
+                forceSelection: false,
+                triggerAction: 'all',
+                listeners: {
+                    select: function(s, r) {
+                        store.baseParams.hidemode = r.id;
+                        page.changePage(0);
+                        store.reload();
+                    }
+                }
             });
-          }
+            buttons.push('-');
+            buttons.push('Hide:');
+            buttons.push(hidemode);
         }
-      }
-    });
-    buttons.push(editBtn);
-
-    /* Hide Mode */
-    if (conf.hidemode) {
-      var hidemode = new Ext.form.ComboBox({
-        width          : 100,
-        displayField   : 'val',
-        valueField     : 'key',
-        store          : new Ext.data.ArrayStore({
-          id     : 0,
-          fields : [ 'key', 'val' ],
-          data   : [ ['default', 'Parent disabled'],
-                     ['all', 'All' ],
-                     ['none', 'None' ] ],
-        }),
-        value          : 'default',
-        mode           : 'local',
-        forceSelection : false,
-        triggerAction  : 'all',
-        listeners      : {
-          select : function (s, r) {
-            store.baseParams.hidemode = r.id;
-            page.changePage(0);
-            store.reload();
-          }
+        var page = new Ext.PagingToolbar({
+            store: store,
+            pageSize: 50,
+            displayInfo: true,
+            displayMsg: conf.titleP + ' {0} - {1} of {2}',
+            width: 50
+        });
+
+        /* Extra buttons */
+        if (conf.tbar) {
+            buttons.push('-');
+            for (i = 0; i < conf.tbar.length; i++) {
+                if (conf.tbar[i].callback) {
+                    conf.tbar[i].handler = function(b, e) {
+                        this.callback(this, e, store, select);
+                    };
+                }
+                buttons.push(conf.tbar[i]);
+            }
         }
-      });
-      buttons.push('-');
-      buttons.push('Hide:');
-      buttons.push(hidemode);
-    }
-    var page = new Ext.PagingToolbar({
-      store       : store,
-      pageSize    : 50,
-      displayInfo : true,
-      displayMsg  :  conf.titleP + ' {0} - {1} of {2}',
-      width : 50,
-    });
 
-    /* Extra buttons */
-    if (conf.tbar) {
-      buttons.push('-')
-      for (i = 0; i < conf.tbar.length; i++) {
-        if (conf.tbar[i].callback) {
-          conf.tbar[i].handler = function (b, e) {
-            this.callback(this, e, store, select);
-          }
+        /* Help */
+        if (conf.help) {
+            buttons.push('->');
+            buttons.push({
+                text: 'Help',
+                handler: conf.help
+            });
         }
-        buttons.push(conf.tbar[i])
-      }
+
+        /* Grid Panel */
+        var auto = new Ext.form.Checkbox({
+            checked: true,
+            listeners: {
+                check: function(s, c) {
+                    if (c)
+                        store.reload();
+                }
+            }
+        });
+        var count = new Ext.form.ComboBox({
+            width: 50,
+            displayField: 'val',
+            valueField: 'key',
+            store: new Ext.data.ArrayStore({
+                id: 0,
+                fields: ['key', 'val'],
+                data: [[25, '25'], [50, '50'], [100, '100'],
+                    [200, '200'], [9999999999, 'All']]
+            }),
+            value: 50,
+            mode: 'local',
+            forceSelection: false,
+            triggerAction: 'all',
+            listeners: {
+                select: function(s, r) {
+                    if (r !== page.pageSize) {
+                        page.pageSize = r.id;
+                        page.changePage(0);
+                        store.reload();
+                        // TODO: currently setting pageSize=-1 to disable paging confuses
+                        //       the UI elements, and I don't know how to sort that!
+                    }
+                }
+            }
+        });
+        var page = new Ext.PagingToolbar({
+            store: store,
+            pageSize: 50,
+            displayInfo: true,
+            displayMsg: conf.titleP + ' {0} - {1} of {2}',
+            emptyMsg: 'No ' + conf.titleP.toLowerCase() + ' to display',
+            items: ['-', 'Auto-refresh', auto,
+                '->', '-', 'Per page', count]
+        });
+        plugins.push(filter);
+        var grid = new Ext.grid.EditorGridPanel({
+            stateful: true,
+            stateId: conf.url,
+            stripeRows: true,
+            title: conf.titleP,
+            store: store,
+            cm: model,
+            selModel: select,
+            plugins: plugins,
+            viewConfig: {
+                forceFit: true
+            },
+            tbar: buttons,
+            bbar: page
+        });
+        grid.on('filterupdate', function() {
+            page.changePage(0);
+        });
+
+        if (conf.tabIndex != null)
+            panel.insert(conf.tabIndex, grid);
+        else
+            panel.add(grid);
+
+        /* Add comet listeners */
+        var update = function(o) {
+            if (auto.getValue())
+                store.reload();
+        };
+        if (conf.comet)
+            tvheadend.comet.on(conf.comet, update);
+        tvheadend.comet.on('idnodeUpdated', update);
+        tvheadend.comet.on('idnodeDeleted', update);
     }
 
-    /* Help */
-    if (conf.help) {
-      buttons.push('->');
-      buttons.push({  
-        text    : 'Help',
-        handler : conf.help
-      });
+    /* Request data */
+    if (!conf.fields) {
+        Ext.Ajax.request({
+            url: conf.url + '/class',
+            success: function(d)
+            {
+                var d = json_decode(d);
+                build(d);
+            }
+        });
+    } else {
+        build(conf.fields);
     }
+};
 
-    /* Grid Panel */
-    var auto   = new Ext.form.Checkbox({
-      checked     : true,
-      listeners   : {
-        check : function ( s, c ) {
-          if (c) store.reload();
-        }
-      }
+tvheadend.idnode_tree = function(conf)
+{
+    var current = null;
+    var params = conf.params || {};
+    var loader = new Ext.tree.TreeLoader({
+        dataUrl: conf.url,
+        baseParams: params,
+        preloadChildren: conf.preload,
+        nodeParameter: 'uuid'
     });
-    var count  = new Ext.form.ComboBox({
-      width          : 50,
-      displayField   : 'val',
-      valueField     : 'key',
-      store          : new Ext.data.ArrayStore({
-        id     : 0,
-        fields : [ 'key', 'val' ],
-        data   : [[25, '25'], [50, '50'], [100, '100'],
-                  [200, '200'], [9999999999, 'All']]
-      }),
-      value          : 50,
-      mode           : 'local',
-      forceSelection : false,
-      triggerAction  : 'all',
-      listeners      : {
-        select : function (s, r) {
-          if (r != page.pageSize) {
-            page.pageSize = r.id;
-            page.changePage(0);
-            store.reload();
-            // TODO: currently setting pageSize=-1 to disable paging confuses
-            //       the UI elements, and I don't know how to sort that!
-          }
+
+    var tree = new Ext.tree.TreePanel({
+        loader: loader,
+        flex: 1,
+        border: false,
+        animate: false,
+        root: new Ext.tree.AsyncTreeNode({
+            id: conf.root || 'root',
+            text: conf.title || ''
+        }),
+        listeners: {
+            click: function(n) {
+                if (current)
+                    panel.remove(current);
+                if (!n.isRoot)
+                    current = panel.add(new tvheadend.idnode_editor(n.attributes, {
+                        title: 'Parameters',
+                        fixedHeight: true,
+                        help: conf.help || null
+                    }));
+                panel.doLayout();
+            }
         }
-      }
-    });
-    var page = new Ext.PagingToolbar({
-      store       : store,
-      pageSize    : 50,
-      displayInfo : true,
-      displayMsg  :  conf.titleP + ' {0} - {1} of {2}',
-      emptyMsg    : 'No ' + conf.titleP.toLowerCase() + ' to display',
-      items       : [ '-', 'Auto-refresh', auto,
-                      '->', '-', 'Per page', count ]
-    });
-    plugins.push(filter);
-    var grid   = new Ext.grid.EditorGridPanel({
-      stateful      : true,
-      stateId       : conf.url,
-      stripeRows    : true,
-      title         : conf.titleP,
-      store         : store,
-      cm            : model,
-      selModel      : select,
-      plugins       : plugins,
-      viewConfig    : {
-        forceFit : true
-      },
-      tbar          : buttons,
-      bbar          : page
-    });
-    grid.on('filterupdate', function() {
-      page.changePage(0);
     });
 
-    if (conf.tabIndex != null)
-      panel.insert(conf.tabIndex, grid);
-    else
-      panel.add(grid);
+    if (conf.comet) {
+        tvheadend.comet.on(conf.comet, function(o) {
+            if (o.reload)
+                tree.getRootNode().reload();
+        });
+    }
 
-    /* Add comet listeners */
-    var update = function(o) {
-      if (auto.getValue())
-        store.reload();
-    };
-    if (conf.comet)
-      tvheadend.comet.on(conf.comet, update);
-    tvheadend.comet.on('idnodeUpdated', update);
-    tvheadend.comet.on('idnodeDeleted', update);
-  }
-
-  /* Request data */
-  if (!conf.fields) {
-    Ext.Ajax.request({
-      url     : conf.url + '/class',
-      success : function(d)
-      {
-        var d = json_decode(d);
-        build(d);
-      }
+    // TODO: top-level reload
+    tvheadend.comet.on('idnodeUpdated', function(o) {
+        var n = tree.getNodeById(o.uuid);
+        if (n) {
+            if (o.text)
+                n.setText(o.text);
+            tree.getRootNode().reload();
+            // cannot get this to properly reload children and maintain state
+        }
+    });
+
+    var panel = new Ext.Panel({
+        title: conf.title || '',
+        layout: 'hbox',
+        flex: 1,
+        padding: 5,
+        border: false,
+        layoutConfig: {
+            align: 'stretch'
+        },
+        items: [tree]
     });
-  } else {
-    build(conf.fields);
-  }
-}
 
-tvheadend.idnode_tree = function (conf)
-{
-  var current = null;
-  var params  = conf.params || {};
-  var loader = new Ext.tree.TreeLoader({
-    dataUrl         : conf.url,
-    baseParams      : params,
-    preloadChildren : conf.preload,
-    nodeParameter   : 'uuid'
-  });
-
-  var tree = new Ext.tree.TreePanel({
-    loader  : loader,
-    flex    : 1,
-    border  : false,
-    animate : false,
-    root    : new Ext.tree.AsyncTreeNode({
-      id    : conf.root  || 'root',
-      text  : conf.title || ''
-    }),
-    listeners : {
-      click: function(n) {
-        if(current)
-          panel.remove(current);
-        if(!n.isRoot)
-          current = panel.add(new tvheadend.idnode_editor(n.attributes, {
-            title       : 'Parameters',
-            fixedHeight : true,
-            help        : conf.help || null,
-          }));
-        panel.doLayout();
-      }
-    }
-  });
 
-  if (conf.comet) {
-    tvheadend.comet.on(conf.comet, function(o) {
-      if (o.reload)
-        tree.getRootNode().reload();
+    tree.on('beforerender', function() {
+        // To be honest this isn't quite right, but it'll do
+        tree.expandAll();
     });
-  }
-
-  // TODO: top-level reload
-  tvheadend.comet.on('idnodeUpdated', function(o) {
-    var n = tree.getNodeById(o.uuid);
-    if (n) {
-      if (o.text) n.setText(o.text);
-      tree.getRootNode().reload();
-      // cannot get this to properly reload children and maintain state
-    }
-  });
-
-  var panel = new Ext.Panel({
-    title          : conf.title || '',
-    layout        : 'hbox',
-    flex          : 1,
-    padding        : 5,
-    border        : false,
-    layoutConfig  : {
-      align : 'stretch'
-    },
-    items: [ tree ]
-  });
-
-
-  tree.on('beforerender', function() {
-    // To be honest this isn't quite right, but it'll do
-    tree.expandAll();
-  });
-
-  return panel;
-}
+
+    return panel;
+};
index d5fc6e19355f34a29816f8ca3b434979b48a67e0..83609b3529399c0971665961c63a990263c2c67a 100644 (file)
  */
 tvheadend.iptv = function(adapterId) {
 
-  var servicetypeStore = new Ext.data.JsonStore({
-         root : 'entries',
-         id : 'val',
-         url : '/iptv/services',
-         baseParams : {
-                 op : 'servicetypeList'
-         },
-         fields : [ 'val', 'str' ],
-         autoLoad : false,
-         sortInfo : {
-               field : 'channelname',
-               direction : 'ASC'
-         }
-  });
+    var servicetypeStore = new Ext.data.JsonStore({
+        root: 'entries',
+        id: 'val',
+        url: '/iptv/services',
+        baseParams: {
+            op: 'servicetypeList'
+        },
+        fields: ['val', 'str'],
+        autoLoad: false,
+        sortInfo: {
+            field: 'channelname',
+            direction: 'ASC'
+        }
+    });
 
-       var fm = Ext.form;
+    var fm = Ext.form;
 
 
-       var actions = new Ext.ux.grid.RowActions({
-               header : '',
-               dataIndex : 'actions',
-               width : 45,
-               actions : [ {
-                       iconCls : 'info',
-                       qtip : 'Detailed information about service',
-                       cb : function(grid, record, action, row, col) {
-                               Ext.Ajax.request({
-                                       url : "servicedetails/" + record.id,
-                                       success : function(response, options) {
-                                               r = Ext.util.JSON.decode(response.responseText);
-                                               tvheadend.showTransportDetails(r);
-                                       }
-                               })
-                       }
-               } ]
-       });
+    var actions = new Ext.ux.grid.RowActions({
+        header: '',
+        dataIndex: 'actions',
+        width: 45,
+        actions: [{
+                iconCls: 'info',
+                qtip: 'Detailed information about service',
+                cb: function(grid, record, action, row, col) {
+                    Ext.Ajax.request({
+                        url: "servicedetails/" + record.id,
+                        success: function(response, options) {
+                            r = Ext.util.JSON.decode(response.responseText);
+                            tvheadend.showTransportDetails(r);
+                        }
+                    });
+                }
+            }]
+    });
 
-       var cm = new Ext.grid.ColumnModel({
-  defaultSortable: true,
-  columns : [
-    {
-      xtype: 'checkcolumn',
-               header : "Enabled",
-               dataIndex : 'enabled',
-               width : 45
-    },
-               {
-                       header : "Channel name",
-                       dataIndex : 'channelname',
-                       width : 150,
-                       renderer : function(value, metadata, record, row, col, store) {
-                               return value ? value
-                                       : '<span class="tvh-grid-unset">Unmapped</span>';
-                       },
-                       editor : new fm.ComboBox({
-                               store : tvheadend.channels,
-                               allowBlank : true,
-                               typeAhead : true,
-                               minChars : 2,
-                               lazyRender : true,
-                               triggerAction : 'all',
-                               mode : 'local',
-                               displayField : 'name'
-                       })
-               },
-               {
-                       header : "Interface",
-                       dataIndex : 'interface',
-                       width : 100,
-                       renderer : function(value, metadata, record, row, col, store) {
-                               return value ? value : '<span class="tvh-grid-unset">Unset</span>';
-                       },
-                       editor : new fm.TextField({
-                               allowBlank : false
-                       })
-               },
-               {
-                       header : "Group",
-                       dataIndex : 'group',
-                       width : 100,
-                       renderer : function(value, metadata, record, row, col, store) {
-                               return value ? value : '<span class="tvh-grid-unset">Unset</span>';
-                       },
-                       editor : new fm.TextField({
-                               allowBlank : false
-                       })
-               },
-               {
-                       header : "UDP Port",
-                       dataIndex : 'port',
-                       width : 60,
-                       editor : new fm.NumberField({
-                               minValue : 1,
-                               maxValue : 65535
-                       })
-               },
-               {
-                       header : "Service ID",
-                       dataIndex : 'sid',
-                       width : 50,
-                       hidden : true
-               },
-               {
-                       header : 'Service Type',
-                       width : 100,
-                       dataIndex : 'stype',
-                       hidden : true,
-                       editor : new fm.ComboBox({
-                               valueField : 'val',
-                               displayField : 'str',
-                               forceSelection : false,
-                               editable : false,
-                               mode : 'local',
-                               triggerAction : 'all',
-                               store : servicetypeStore
-                       }),
-                       renderer : function(value, metadata, record, row, col, store) {
-                               var val = value ? servicetypeStore.getById(value) : null;
-                               return val ? val.get('str')
-                                       : '<span class="tvh-grid-unset">Unset</span>';
-                       }
-               }, {
-                       header : "PMT PID",
-                       dataIndex : 'pmt',
-                       width : 50,
-                       hidden : true
-               }, {
-                       header : "PCR PID",
-                       dataIndex : 'pcr',
-                       width : 50,
-                       hidden : true
-               }, actions ]});
+    var cm = new Ext.grid.ColumnModel({
+        defaultSortable: true,
+        columns: [
+            {
+                xtype: 'checkcolumn',
+                header: "Enabled",
+                dataIndex: 'enabled',
+                width: 45
+            },
+            {
+                header: "Channel name",
+                dataIndex: 'channelname',
+                width: 150,
+                renderer: function(value, metadata, record, row, col, store) {
+                    return value ? value
+                            : '<span class="tvh-grid-unset">Unmapped</span>';
+                },
+                editor: new fm.ComboBox({
+                    store: tvheadend.channels,
+                    allowBlank: true,
+                    typeAhead: true,
+                    minChars: 2,
+                    lazyRender: true,
+                    triggerAction: 'all',
+                    mode: 'local',
+                    displayField: 'name'
+                })
+            },
+            {
+                header: "Interface",
+                dataIndex: 'interface',
+                width: 100,
+                renderer: function(value, metadata, record, row, col, store) {
+                    return value ? value : '<span class="tvh-grid-unset">Unset</span>';
+                },
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            },
+            {
+                header: "Group",
+                dataIndex: 'group',
+                width: 100,
+                renderer: function(value, metadata, record, row, col, store) {
+                    return value ? value : '<span class="tvh-grid-unset">Unset</span>';
+                },
+                editor: new fm.TextField({
+                    allowBlank: false
+                })
+            },
+            {
+                header: "UDP Port",
+                dataIndex: 'port',
+                width: 60,
+                editor: new fm.NumberField({
+                    minValue: 1,
+                    maxValue: 65535
+                })
+            },
+            {
+                header: "Service ID",
+                dataIndex: 'sid',
+                width: 50,
+                hidden: true
+            },
+            {
+                header: 'Service Type',
+                width: 100,
+                dataIndex: 'stype',
+                hidden: true,
+                editor: new fm.ComboBox({
+                    valueField: 'val',
+                    displayField: 'str',
+                    forceSelection: false,
+                    editable: false,
+                    mode: 'local',
+                    triggerAction: 'all',
+                    store: servicetypeStore
+                }),
+                renderer: function(value, metadata, record, row, col, store) {
+                    var val = value ? servicetypeStore.getById(value) : null;
+                    return val ? val.get('str')
+                            : '<span class="tvh-grid-unset">Unset</span>';
+                }
+            }, {
+                header: "PMT PID",
+                dataIndex: 'pmt',
+                width: 50,
+                hidden: true
+            }, {
+                header: "PCR PID",
+                dataIndex: 'pcr',
+                width: 50,
+                hidden: true
+            }, actions]});
 
-       var rec = Ext.data.Record.create([ 'id', 'enabled', 'channelname',
-               'interface', 'group', 'port', 'sid', 'pmt', 'pcr', 'stype' ]);
+    var rec = Ext.data.Record.create(['id', 'enabled', 'channelname',
+        'interface', 'group', 'port', 'sid', 'pmt', 'pcr', 'stype']);
 
-       var store = new Ext.data.JsonStore({
-               root : 'entries',
-               fields : rec,
-               url : "iptv/services",
-               autoLoad : true,
-               id : 'id',
-               baseParams : {
-                       op : "get"
-               },
-               listeners : {
-                       'update' : function(s, r, o) {
-                               d = s.getModifiedRecords().length == 0
-                               saveBtn.setDisabled(d);
-                               rejectBtn.setDisabled(d);
-                       }
-               }
-       });
+    var store = new Ext.data.JsonStore({
+        root: 'entries',
+        fields: rec,
+        url: "iptv/services",
+        autoLoad: true,
+        id: 'id',
+        baseParams: {
+            op: "get"
+        },
+        listeners: {
+            'update': function(s, r, o) {
+                d = s.getModifiedRecords().length === 0
+                saveBtn.setDisabled(d);
+                rejectBtn.setDisabled(d);
+            }
+        }
+    });
 
-       /*
-        var storeReloader = new Ext.util.DelayedTask(function() {
-       store.reload()
-        });
+    /*
+     var storeReloader = new Ext.util.DelayedTask(function() {
+     store.reload()
+     });
+     
+     tvheadend.comet.on('dvbService', function(m) {
+     storeReloader.delay(500);
+     });
+     */
 
-        tvheadend.comet.on('dvbService', function(m) {
-       storeReloader.delay(500);
-        });
-        */
+    function addRecord() {
+        Ext.Ajax.request({
+            url: "iptv/services",
+            params: {
+                op: "create"
+            },
+            failure: function(response, options) {
+                Ext.MessageBox.alert('Server Error',
+                        'Unable to generate new record');
+            },
+            success: function(response, options) {
+                var responseData = Ext.util.JSON.decode(response.responseText);
+                var p = new rec(responseData, responseData.id);
+                grid.stopEditing();
+                store.insert(0, p);
+                grid.startEditing(0, 0);
+            }
+        });
+    }
+    ;
 
-       function addRecord() {
-               Ext.Ajax.request({
-                       url : "iptv/services",
-                       params : {
-                               op : "create"
-                       },
-                       failure : function(response, options) {
-                               Ext.MessageBox.alert('Server Error',
-                                       'Unable to generate new record');
-                       },
-                       success : function(response, options) {
-                               var responseData = Ext.util.JSON.decode(response.responseText);
-                               var p = new rec(responseData, responseData.id);
-                               grid.stopEditing();
-                               store.insert(0, p);
-                               grid.startEditing(0, 0);
-                       }
-               })
-       }
-       ;
+    function delSelected() {
+        var selectedKeys = grid.selModel.selections.keys;
+        if (selectedKeys.length > 0) {
+            Ext.MessageBox.confirm('Message',
+                    'Do you really want to delete selection?', deleteRecord);
+        }
+        else {
+            Ext.MessageBox.alert('Message',
+                    'Please select at least one item to delete');
+        }
+    }
+    ;
 
-       function delSelected() {
-               var selectedKeys = grid.selModel.selections.keys;
-               if (selectedKeys.length > 0) {
-                       Ext.MessageBox.confirm('Message',
-                               'Do you really want to delete selection?', deleteRecord);
-               }
-               else {
-                       Ext.MessageBox.alert('Message',
-                               'Please select at least one item to delete');
-               }
-       }
-       ;
+    function deleteRecord(btn) {
+        if (btn === 'yes') {
+            var selectedKeys = grid.selModel.selections.keys;
 
-       function deleteRecord(btn) {
-               if (btn == 'yes') {
-                       var selectedKeys = grid.selModel.selections.keys;
+            Ext.Ajax.request({
+                url: "iptv/services",
+                params: {
+                    op: "delete",
+                    entries: Ext.encode(selectedKeys)
+                },
+                failure: function(response, options) {
+                    Ext.MessageBox.alert('Server Error', 'Unable to delete');
+                },
+                success: function(response, options) {
+                    store.reload();
+                }
+            });
+        }
+    }
 
-                       Ext.Ajax.request({
-                               url : "iptv/services",
-                               params : {
-                                       op : "delete",
-                                       entries : Ext.encode(selectedKeys)
-                               },
-                               failure : function(response, options) {
-                                       Ext.MessageBox.alert('Server Error', 'Unable to delete');
-                               },
-                               success : function(response, options) {
-                                       store.reload();
-                               }
-                       })
-               }
-       }
+    function saveChanges() {
+        var mr = store.getModifiedRecords();
+        var out = new Array();
+        for (var x = 0; x < mr.length; x++) {
+            v = mr[x].getChanges();
+            out[x] = v;
+            out[x].id = mr[x].id;
+        }
 
-       function saveChanges() {
-               var mr = store.getModifiedRecords();
-               var out = new Array();
-               for ( var x = 0; x < mr.length; x++) {
-                       v = mr[x].getChanges();
-                       out[x] = v;
-                       out[x].id = mr[x].id;
-               }
+        Ext.Ajax.request({
+            url: "iptv/services",
+            params: {
+                op: "update",
+                entries: Ext.encode(out)
+            },
+            success: function(response, options) {
+                store.commitChanges();
+            },
+            failure: function(response, options) {
+                Ext.MessageBox.alert('Message', response.statusText);
+            }
+        });
+    }
 
-               Ext.Ajax.request({
-                       url : "iptv/services",
-                       params : {
-                               op : "update",
-                               entries : Ext.encode(out)
-                       },
-                       success : function(response, options) {
-                               store.commitChanges();
-                       },
-                       failure : function(response, options) {
-                               Ext.MessageBox.alert('Message', response.statusText);
-                       }
-               });
-       }
+    var delButton = new Ext.Toolbar.Button({
+        tooltip: 'Delete one or more selected rows',
+        iconCls: 'remove',
+        text: 'Delete selected services',
+        handler: delSelected,
+        disabled: true
+    });
 
-       var delButton = new Ext.Toolbar.Button({
-               tooltip : 'Delete one or more selected rows',
-               iconCls : 'remove',
-               text : 'Delete selected services',
-               handler : delSelected,
-               disabled : true
-       });
+    var saveBtn = new Ext.Toolbar.Button({
+        tooltip: 'Save any changes made (Changed cells have red borders).',
+        iconCls: 'save',
+        text: "Save changes",
+        handler: saveChanges,
+        disabled: true
+    });
 
-       var saveBtn = new Ext.Toolbar.Button({
-               tooltip : 'Save any changes made (Changed cells have red borders).',
-               iconCls : 'save',
-               text : "Save changes",
-               handler : saveChanges,
-               disabled : true
-       });
+    var rejectBtn = new Ext.Toolbar.Button({
+        tooltip: 'Revert any changes made (Changed cells have red borders).',
+        iconCls: 'undo',
+        text: "Revert changes",
+        handler: function() {
+            store.rejectChanges();
+        },
+        disabled: true
+    });
 
-       var rejectBtn = new Ext.Toolbar.Button({
-               tooltip : 'Revert any changes made (Changed cells have red borders).',
-               iconCls : 'undo',
-               text : "Revert changes",
-               handler : function() {
-                       store.rejectChanges();
-               },
-               disabled : true
-       });
+    var selModel = new Ext.grid.RowSelectionModel({
+        singleSelect: false
+    });
 
-       var selModel = new Ext.grid.RowSelectionModel({
-               singleSelect : false
-       });
+    var grid = new Ext.grid.EditorGridPanel({
+        stripeRows: true,
+        title: 'IPTV',
+        iconCls: 'iptv',
+        plugins: [actions],
+        store: store,
+        clicksToEdit: 2,
+        cm: cm,
+        viewConfig: {
+            forceFit: true
+        },
+        selModel: selModel,
+        tbar: [
+            {
+                tooltip: 'Create a new entry on the server. '
+                        + 'The new entry is initially disabled so it must be enabled '
+                        + 'before it start taking effect.',
+                iconCls: 'add',
+                text: 'Add service',
+                handler: addRecord
+            }, '-', delButton, '-', saveBtn, rejectBtn, '->',
+            {
+                text: 'Help',
+                handler: function() {
+                    new tvheadend.help('IPTV', 'config_iptv.html');
+                }
+            }]
+    });
 
-       var grid = new Ext.grid.EditorGridPanel({
-               stripeRows : true,
-               title : 'IPTV',
-               iconCls : 'iptv',
-               plugins : [ actions ],
-               store : store,
-               clicksToEdit : 2,
-               cm : cm,
-               viewConfig : {
-                       forceFit : true
-               },
-               selModel : selModel,
-               tbar : [
-                       {
-                               tooltip : 'Create a new entry on the server. '
-                                       + 'The new entry is initially disabled so it must be enabled '
-                                       + 'before it start taking effect.',
-                               iconCls : 'add',
-                               text : 'Add service',
-                               handler : addRecord
-                       }, '-', delButton, '-', saveBtn, rejectBtn, '->',
-                       {
-                               text : 'Help',
-                               handler : function() {
-                                       new tvheadend.help('IPTV', 'config_iptv.html');
-                               }
-                       } ]
-       });
+    store.on('update', function(s, r, o) {
+        d = s.getModifiedRecords().length === 0;
+        saveBtn.setDisabled(d);
+        rejectBtn.setDisabled(d);
+    });
 
-       store.on('update', function(s, r, o) {
-               d = s.getModifiedRecords().length == 0
-               saveBtn.setDisabled(d);
-               rejectBtn.setDisabled(d);
-       });
+    selModel.on('selectionchange', function(self) {
+        delButton.setDisabled(self.getCount() === 0);
+    });
 
-       selModel.on('selectionchange', function(self) {
-               delButton.setDisabled(self.getCount() == 0);
-       });
-
-       return grid;
-}
+    return grid;
+};
index 3249bbec1c08191d6c46d52a027bb88b00452d91..920ccd3ee9adb3793db69b69b259b2fa2afd349e 100644 (file)
  */
 
 tvheadend.network_builders = new Ext.data.JsonStore({
-  url              : 'api/mpegts/network/builders',
-  root         : 'entries',
-  fields         : [ 'class', 'caption', 'props' ],
-  id               : 'class',
-  autoLoad     : true,
+    url: 'api/mpegts/network/builders',
+    root: 'entries',
+    fields: ['class', 'caption', 'props'],
+    id: 'class',
+    autoLoad: true
 });
 
 tvheadend.network_list = new Ext.data.JsonStore({
-  url        : 'api/idnode/load',
-  baseParams : { class : 'mpegts_network', enum: 1 },
-  root       : 'entries',
-  fields     : [ 'key', 'val' ],
-  id         : 'key',
-  autoLoad   : true,
+    url: 'api/idnode/load',
+    baseParams: {class: 'mpegts_network', enum: 1},
+    root: 'entries',
+    fields: ['key', 'val'],
+    id: 'key',
+    autoLoad: true
 });
 
 tvheadend.comet.on('mpegts_network', function() {
-  // TODO: Might be a bit excessive
-  tvheadend.network_builders.reload();
-  tvheadend.network_list.reload();
+    // TODO: Might be a bit excessive
+    tvheadend.network_builders.reload();
+    tvheadend.network_list.reload();
 });
 
 tvheadend.networks = function(panel)
 {
-  tvheadend.idnode_grid(panel, {
-    url      : 'api/mpegts/network',
-    comet    : 'mpegts_network',
-    titleS   : 'Network',
-    titleP   : 'Networks',
-    tabIndex : 1,
-    add      : {
-      titleS : 'Network',
-      select : {
-        label        : 'Type',
-             store        : tvheadend.network_builders,
-        displayField : 'caption',
-        valueField   : 'class',
-        propField    : 'props',
-      },
-      create : {
-        url          : 'api/mpegts/network/create'
-      }
-    },
-    del     : true,
-    sort    : {
-      field : 'networkname',
-      direction : 'ASC'
-    }
-  });
-}
+    tvheadend.idnode_grid(panel, {
+        url: 'api/mpegts/network',
+        comet: 'mpegts_network',
+        titleS: 'Network',
+        titleP: 'Networks',
+        tabIndex: 1,
+        add: {
+            titleS: 'Network',
+            select: {
+                label: 'Type',
+                store: tvheadend.network_builders,
+                displayField: 'caption',
+                valueField: 'class',
+                propField: 'props'
+            },
+            create: {
+                url: 'api/mpegts/network/create'
+            }
+        },
+        del: true,
+        sort: {
+            field: 'networkname',
+            direction: 'ASC'
+        }
+    });
+};
 
 tvheadend.muxes = function(panel)
 {
-  tvheadend.idnode_grid(panel, {
-    url      : 'api/mpegts/mux',
-    comet    : 'mpegts_mux',
-    titleS   : 'Mux',
-    titleP   : 'Muxes',
-    tabIndex : 2,
-    hidemode : true,
-    add      : {
-      titleS   : 'Mux',
-      select   : {
-        label        : 'Network',
-        store        : tvheadend.network_list,
-        valueField   : 'key',
-        displayField : 'val',
-        clazz        : {
-          url          : 'api/mpegts/network/mux_class'
+    tvheadend.idnode_grid(panel, {
+        url: 'api/mpegts/mux',
+        comet: 'mpegts_mux',
+        titleS: 'Mux',
+        titleP: 'Muxes',
+        tabIndex: 2,
+        hidemode: true,
+        add: {
+            titleS: 'Mux',
+            select: {
+                label: 'Network',
+                store: tvheadend.network_list,
+                valueField: 'key',
+                displayField: 'val',
+                clazz: {
+                    url: 'api/mpegts/network/mux_class'
+                }
+            },
+            create: {
+                url: 'api/mpegts/network/mux_create'
+            }
+        },
+        del: true,
+        lcol: [
+            {
+                width: 50,
+                header: 'Play',
+                renderer: function(v, o, r) {
+                    return "<a href='stream/mux/" + r.id + "'>Play</a>";
+                }
+            }
+        ],
+        sort: {
+            field: 'name',
+            direction: 'ASC'
         }
-      },
-      create   : {
-        url    : 'api/mpegts/network/mux_create',
-      }
-    },
-    del     : true,
-    lcol    : [
-      {
-        width    : 50,
-        header   : 'Play',
-        renderer : function(v, o, r) {
-          return "<a href='stream/mux/" + r.id + "'>Play</a>";
-        }
-      }
-    ],
-    sort    : {
-      field  : 'name',
-      direction : 'ASC'
+    });
+};
+
+tvheadend.show_service_streams = function(data) {
+    var i, j;
+    var html = '';
+
+    function hexstr(d) {
+        return ('0000' + d.toString(16)).slice(-4);
     }
-  });
-}
 
-tvheadend.show_service_streams = function ( data ) {
-  var i, j;
-       var html = '';
+    function hexstr6(d) {
+        return ('000000' + d.toString(16)).slice(-6);
+    }
 
-  function hexstr ( d ) {
-    return ('0000' + d.toString(16)).slice(-4);
-  }
-    
-  function hexstr6 ( d ) {
-    return ('000000' + d.toString(16)).slice(-6);
-  }
-    
-  function fixstr ( d ) {
-    var r = d.toString();
-    var l = r.length;
-    var i;
-    for (i = l; i < 5; i++) {
-      r = '&nbsp;' + r;
+    function fixstr(d) {
+        var r = d.toString();
+        var l = r.length;
+        var i;
+        for (i = l; i < 5; i++) {
+            r = '&nbsp;' + r;
+        }
+        return r;
     }
-    return r;
-  }
 
-  function header ( ) {
-    html += '<table style="font-size:8pt;font-family:mono;padding:2px"';
-    html += '<tr>';
-    html += '<th style="width:50px;font-weight:bold">Index</th>';
-    html += '<th style="width:120px;font-weight:bold">PID</th>';
-    html += '<th style="width:100px;font-weight:bold">Type</th>';
-    html += '<th style="width:75px;font-weight:bold">Language</th>';
-    html += '<th style="width:*;font-weight:bold">Details</th>';
-    html += '</tr>';
+    function header( ) {
+        html += '<table style="font-size:8pt;font-family:mono;padding:2px"';
+        html += '<tr>';
+        html += '<th style="width:50px;font-weight:bold">Index</th>';
+        html += '<th style="width:120px;font-weight:bold">PID</th>';
+        html += '<th style="width:100px;font-weight:bold">Type</th>';
+        html += '<th style="width:75px;font-weight:bold">Language</th>';
+        html += '<th style="width:*;font-weight:bold">Details</th>';
+        html += '</tr>';
 
-  }
+    }
 
-  function single ( s ) {
-    html += '<tr><td colspan="5">' + s + '</td></tr>';
-  }
+    function single(s) {
+        html += '<tr><td colspan="5">' + s + '</td></tr>';
+    }
 
-  function stream ( s ) {
-    var d = '&nbsp;';
-    var p = '0x' + hexstr(s.pid) + '&nbsp;/&nbsp;' + fixstr(s.pid);
+    function stream(s) {
+        var d = '&nbsp;';
+        var p = '0x' + hexstr(s.pid) + '&nbsp;/&nbsp;' + fixstr(s.pid);
 
-               html += '<tr>';
-               html += '<td>' + (s.index > 0 ? s.index : '&nbsp;') + '</td>';
-               html += '<td>' + p + '</td>';
-               html += '<td>' + s.type + '</td>';
-               html += '<td>' + (s.language || '&nbsp;') + '</td>'
-    if (s.type == 'CA') {
-      d = 'CAIDS: ';
-      for (j = 0; j < s.caids.length; j++) {
-        if (j > 0) d += ', ';
-        d += hexstr(s.caids[j].caid) + ':';
-        d += hexstr6(s.caids[j].provider);
-      }
+        html += '<tr>';
+        html += '<td>' + (s.index > 0 ? s.index : '&nbsp;') + '</td>';
+        html += '<td>' + p + '</td>';
+        html += '<td>' + s.type + '</td>';
+        html += '<td>' + (s.language || '&nbsp;') + '</td>';
+        if (s.type === 'CA') {
+            d = 'CAIDS: ';
+            for (j = 0; j < s.caids.length; j++) {
+                if (j > 0)
+                    d += ', ';
+                d += hexstr(s.caids[j].caid) + ':';
+                d += hexstr6(s.caids[j].provider);
+            }
+        }
+        html += '<td>' + d + '</td>';
+        html += '</tr>';
     }
-               html += '<td>' + d + '</td>';
-               html += '</tr>';
-  }
 
-  header();
+    header();
 
-  if (data.streams.length) {
-         for (i = 0; i < data.streams.length; i++)
-                 stream(data.streams[i]);
-  } else
-    single('None');
+    if (data.streams.length) {
+        for (i = 0; i < data.streams.length; i++)
+            stream(data.streams[i]);
+    } else
+        single('None');
 
-  single('&nbsp;');
-  single('<h3>After filtering and reordering (without PCR and PMT)</h3>');
-  header();
+    single('&nbsp;');
+    single('<h3>After filtering and reordering (without PCR and PMT)</h3>');
+    header();
 
-  if (data.fstreams.length)
-       for (i = 0; i < data.fstreams.length; i++)
-               stream(data.fstreams[i]);
-  else
-    single('<p>None</p>');
+    if (data.fstreams.length)
+        for (i = 0; i < data.fstreams.length; i++)
+            stream(data.fstreams[i]);
+    else
+        single('<p>None</p>');
 
-       var win = new Ext.Window({
-               title : 'Service details for ' + data.name,
-               layout : 'fit',
-               width : 650,
-               height : 400,
-               plain : true,
-               bodyStyle : 'padding: 5px',
-               html : html,
-               autoScroll: true,
-               autoShow: true
-       });
-       win.show();
-}
+    var win = new Ext.Window({
+        title: 'Service details for ' + data.name,
+        layout: 'fit',
+        width: 650,
+        height: 400,
+        plain: true,
+        bodyStyle: 'padding: 5px',
+        html: html,
+        autoScroll: true,
+        autoShow: true
+    });
+    win.show();
+};
 
 tvheadend.services = function(panel)
 {
-  var mapButton = new Ext.Toolbar.Button({
-    tooltip   : 'Map services to channels',
-    iconCls   : 'clone',
-    text      : 'Map All',
-    callback  : tvheadend.service_mapper,
-    disabled  : false,
-  });
-  var selected = function (s)
-  {
-    if (s.getCount() > 0)
-      mapButton.setText('Map Selected')
-    else
-      mapButton.setText('Map All')
-  }
-  var actions = new Ext.ux.grid.RowActions({
-    header    : '',
-    width     : 10,
-    actions   : [ {
-      iconCls : 'info',
-      qtip    : 'Detailed stream info',
-      cb      : function ( grid, rec, act, row, col ) {
-        Ext.Ajax.request({
-          url    : 'api/service/streams',
-          params : {
-            uuid : rec.id
-          },
-          success : function (r, o) {
-            var d = Ext.util.JSON.decode(r.responseText);
-            tvheadend.show_service_streams(d);
-          }
-        });
-      }
-    } ]
-  });
-  tvheadend.idnode_grid(panel, {
-    url      : 'api/mpegts/service',
-    comet    : 'service',
-    titleS   : 'Service',
-    titleP   : 'Services',
-    tabIndex : 3,
-    hidemode : true,
-    add      : false,
-    del      : false,
-    selected : selected,
-    tbar     : [ mapButton ],
-    lcol     : [
-      {
-        width    : 50,
-        header   : 'Play',
-        renderer : function(v, o, r) {
-          return "<a href='stream/service/" + r.id + "'>Play</a>";
+    var mapButton = new Ext.Toolbar.Button({
+        tooltip: 'Map services to channels',
+        iconCls: 'clone',
+        text: 'Map All',
+        callback: tvheadend.service_mapper,
+        disabled: false
+    });
+    var selected = function(s)
+    {
+        if (s.getCount() > 0)
+            mapButton.setText('Map Selected');
+        else
+            mapButton.setText('Map All');
+    };
+    var actions = new Ext.ux.grid.RowActions({
+        header: '',
+        width: 10,
+        actions: [{
+                iconCls: 'info',
+                qtip: 'Detailed stream info',
+                cb: function(grid, rec, act, row, col) {
+                    Ext.Ajax.request({
+                        url: 'api/service/streams',
+                        params: {
+                            uuid: rec.id
+                        },
+                        success: function(r, o) {
+                            var d = Ext.util.JSON.decode(r.responseText);
+                            tvheadend.show_service_streams(d);
+                        }
+                    });
+                }
+            }]
+    });
+    tvheadend.idnode_grid(panel, {
+        url: 'api/mpegts/service',
+        comet: 'service',
+        titleS: 'Service',
+        titleP: 'Services',
+        tabIndex: 3,
+        hidemode: true,
+        add: false,
+        del: false,
+        selected: selected,
+        tbar: [mapButton],
+        lcol: [
+            {
+                width: 50,
+                header: 'Play',
+                renderer: function(v, o, r) {
+                    return "<a href='stream/service/" + r.id + "'>Play</a>";
+                }
+            },
+            actions
+        ],
+        plugins: [actions],
+        sort: {
+            field: 'svcname',
+            direction: 'ASC'
         }
-      },
-      actions
-    ],
-    plugins   : [ actions ],
-    sort     : {
-      field  : 'svcname',
-      direction : 'ASC'
-    }
-  });
-}
+    });
+};
 
 tvheadend.mux_sched = function(panel)
 {
-  tvheadend.idnode_grid(panel, {
-    url      : 'api/mpegts/mux_sched',
-    comet    : 'mpegts_mux_sched',
-    titleS   : 'Mux Scheduler',
-    titleP   : 'Mux Schedulers',
-    tabIndex : 4,
-    hidemode : true,
-    add      : {
-      url    : 'api/mpegts/mux_sched',
-      titleS : 'Mux Scheduler',
-      create : {
-        url          : 'api/mpegts/mux_sched/create'
-      }
-    },
-    del      : true
-  });
-}
+    tvheadend.idnode_grid(panel, {
+        url: 'api/mpegts/mux_sched',
+        comet: 'mpegts_mux_sched',
+        titleS: 'Mux Scheduler',
+        titleP: 'Mux Schedulers',
+        tabIndex: 4,
+        hidemode: true,
+        add: {
+            url: 'api/mpegts/mux_sched',
+            titleS: 'Mux Scheduler',
+            create: {
+                url: 'api/mpegts/mux_sched/create'
+            }
+        },
+        del: true
+    });
+};
index 39e5575acdb8608b4cce58ac2b63fb1ff64fed86..f4e8873df335a35c5cf74988012bbd607ce78e94 100644 (file)
 
 tvheadend.service_mapper_status_panel = null;
 
-tvheadend.service_mapper_status = function ()
+tvheadend.service_mapper_status = function()
 {
-  var panel;
-
-  /* Fields */
-  var ok     = new Ext.form.Label({
-    fieldLabel : 'Mapped',
-    text       : '0'
-  });
-  var fail   = new Ext.form.Label({
-    fieldLabel : 'Failed',
-    text       : '0'
-  });
-  var ignore = new Ext.form.Label({
-    fieldLabel : 'Ignored',
-    text       : '0'
-  });
-  var active = new Ext.form.Label({
-    width      : 200,
-    fieldLabel : 'Active',
-    text       : ''
-  });
-  var prog = new Ext.ProgressBar({
-    text       : '0 / 0'
-  });
-
-  /* Panel */
-  panel = new Ext.FormPanel({
-    method      : 'get',
-    title       : 'Service Mapper',
-    frame       : true,
-    border      : true,
-    bodyStyle   : 'padding: 5px',
-    labelAlign  : 'left',
-    labelWidth  : 200,
-    width       : 400,
-    autoHeight  : true,
-    defaultType : 'textfield',
-    buttonAlign : 'left',
-    items       : [ ok, ignore, fail, active, prog ]
-  });
-
-  /* Comet */
-  tvheadend.comet.on('servicemapper', function(m) {
-    var n = m.ok + m.ignore + m.fail;
-    ok.setText('' + m.ok);
-    ignore.setText('' + m.ignore);
-    fail.setText('' + m.fail);
-    active.setText('');
-    prog.updateProgress(n / m.total, '' + n + ' / ' + m.total);
-
-    if (m.active) {
-      Ext.Ajax.request({
-        url    : 'api/idnode/load',
-        params : {
-          uuid : m.active
-        },
-        success : function (d) {
-          d = Ext.util.JSON.decode(d.responseText);
-          try {
-            active.setText(d.entries[0].text);
-          } catch (e) {
-          }
+    var panel;
+
+    /* Fields */
+    var ok = new Ext.form.Label({
+        fieldLabel: 'Mapped',
+        text: '0'
+    });
+    var fail = new Ext.form.Label({
+        fieldLabel: 'Failed',
+        text: '0'
+    });
+    var ignore = new Ext.form.Label({
+        fieldLabel: 'Ignored',
+        text: '0'
+    });
+    var active = new Ext.form.Label({
+        width: 200,
+        fieldLabel: 'Active',
+        text: ''
+    });
+    var prog = new Ext.ProgressBar({
+        text: '0 / 0'
+    });
+
+    /* Panel */
+    panel = new Ext.FormPanel({
+        method: 'get',
+        title: 'Service Mapper',
+        frame: true,
+        border: true,
+        bodyStyle: 'padding: 5px',
+        labelAlign: 'left',
+        labelWidth: 200,
+        width: 400,
+        autoHeight: true,
+        defaultType: 'textfield',
+        buttonAlign: 'left',
+        items: [ok, ignore, fail, active, prog]
+    });
+
+    /* Comet */
+    tvheadend.comet.on('servicemapper', function(m) {
+        var n = m.ok + m.ignore + m.fail;
+        ok.setText('' + m.ok);
+        ignore.setText('' + m.ignore);
+        fail.setText('' + m.fail);
+        active.setText('');
+        prog.updateProgress(n / m.total, '' + n + ' / ' + m.total);
+
+        if (m.active) {
+            Ext.Ajax.request({
+                url: 'api/idnode/load',
+                params: {
+                    uuid: m.active
+                },
+                success: function(d) {
+                    d = Ext.util.JSON.decode(d.responseText);
+                    try {
+                        active.setText(d.entries[0].text);
+                    } catch (e) {
+                    }
+                }
+            });
         }
-      });
-    }
-  });
+    });
 
-  tvheadend.service_mapper_status_panel = panel;
-  return panel;
-}
+    tvheadend.service_mapper_status_panel = panel;
+    return panel;
+};
 
 /*
  * Start mapping
  */
 tvheadend.service_mapper = function(t, e, store, select)
 {
-  var panel = null;
-  var win   = null;
-
-  /* Form fields */
-  var availCheck = new Ext.form.Checkbox({
-    name        : 'check_availability',
-    fieldLabel  : 'Check availability',
-    checked     : false
-  });
-  var ftaCheck   = new Ext.form.Checkbox({
-    name        : 'encrypted',
-    fieldLabel  : 'Include encrypted services',
-    checked     : false,
-    // TODO: make dependent on CSA config
-  });
-  var mergeCheck = new Ext.form.Checkbox({
-    name        : 'merge_same_name',
-    fieldLabel  : 'Merge same name',
-    checked     : false
-  });
-  var provtagCheck = new Ext.form.Checkbox({
-    name        : 'provider_tags',
-    fieldLabel  : 'Create provider tags',
-    checked     : false
-  });
-
-  // TODO: provider list
-  items = [ availCheck, ftaCheck, mergeCheck, provtagCheck ];
-
-  /* Form */
-  var undoBtn = new Ext.Button({
-    text    : 'Cancel',
-    handler : function () {
-      win.close();
-    }
-  });
-
-  var saveBtn = new Ext.Button({  
-    text    : 'Map',
-    tooltip : 'Begin mapping',
-    handler : function () {
-      p = null;
-      if (select) {
-        var r = select.getSelections();
-        if (r.length > 0) {
-          var uuids = [];
-          for (var i = 0; i < r.length; i++)
-            uuids.push(r[i].id);
-          p = { uuids: Ext.encode(uuids) };
+    var panel = null;
+    var win = null;
+
+    /* Form fields */
+    var availCheck = new Ext.form.Checkbox({
+        name: 'check_availability',
+        fieldLabel: 'Check availability',
+        checked: false
+    });
+    var ftaCheck = new Ext.form.Checkbox({
+        name: 'encrypted',
+        fieldLabel: 'Include encrypted services',
+        checked: false
+        // TODO: make dependent on CSA config
+    });
+    var mergeCheck = new Ext.form.Checkbox({
+        name: 'merge_same_name',
+        fieldLabel: 'Merge same name',
+        checked: false
+    });
+    var provtagCheck = new Ext.form.Checkbox({
+        name: 'provider_tags',
+        fieldLabel: 'Create provider tags',
+        checked: false
+    });
+
+    // TODO: provider list
+    items = [availCheck, ftaCheck, mergeCheck, provtagCheck];
+
+    /* Form */
+    var undoBtn = new Ext.Button({
+        text: 'Cancel',
+        handler: function() {
+            win.close();
         }
-      }
-
-
-      panel.getForm().submit({
-        url         : 'api/service/mapper/start',
-        waitMessage : 'Mapping services...',
-        params      : p
-      });
-
-      win.hide();
-
-      /* Dialog */
-      win = new Ext.Window({
-        title       : 'Service Mapper Status',
-        layout      : 'fit',
-        autoWidth   : true,
-        autoHeight  : true,
-        plain       : false,
-        items       : tvheadend.service_mapper_status_panel
-        // TODO: buttons
-      });
-      win.show();
-    }
-  });
-
-  panel = new Ext.FormPanel({
-    method      : 'post',
-    frame       : true,
-    border      : true,
-    bodyStyle   : 'padding: 5px',
-    labelAlign  : 'left',
-    labelWidth  : 200,
-    autoWidth   : true,
-    autoHeight  : true,
-    defaultType : 'textfield',
-    buttonAlign : 'left',
-    items       : items,
-    buttons     : [ undoBtn, saveBtn ]
-  });
-   
-  /* Create window */
-  win = new Ext.Window({
-    title       : 'Map services',
-    layout      : 'fit',
-    autoWidth   : true,
-    autoHeight  : true,
-    plain       : true,
-    items       : panel
-  });
-
-  win.show();
-}
+    });
+
+    var saveBtn = new Ext.Button({
+        text: 'Map',
+        tooltip: 'Begin mapping',
+        handler: function() {
+            p = null;
+            if (select) {
+                var r = select.getSelections();
+                if (r.length > 0) {
+                    var uuids = [];
+                    for (var i = 0; i < r.length; i++)
+                        uuids.push(r[i].id);
+                    p = {uuids: Ext.encode(uuids)};
+                }
+            }
+
+
+            panel.getForm().submit({
+                url: 'api/service/mapper/start',
+                waitMessage: 'Mapping services...',
+                params: p
+            });
+
+            win.hide();
+
+            /* Dialog */
+            win = new Ext.Window({
+                title: 'Service Mapper Status',
+                layout: 'fit',
+                autoWidth: true,
+                autoHeight: true,
+                plain: false,
+                items: tvheadend.service_mapper_status_panel
+                        // TODO: buttons
+            });
+            win.show();
+        }
+    });
+
+    panel = new Ext.FormPanel({
+        method: 'post',
+        frame: true,
+        border: true,
+        bodyStyle: 'padding: 5px',
+        labelAlign: 'left',
+        labelWidth: 200,
+        autoWidth: true,
+        autoHeight: true,
+        defaultType: 'textfield',
+        buttonAlign: 'left',
+        items: items,
+        buttons: [undoBtn, saveBtn]
+    });
+
+    /* Create window */
+    win = new Ext.Window({
+        title: 'Map services',
+        layout: 'fit',
+        autoWidth: true,
+        autoHeight: true,
+        plain: true,
+        items: panel
+    });
+
+    win.show();
+};
index 546f59bf1d306c28488480ebc0914eba7c21bd19..80d328f759dee886880ff926e126bc374bf873c8 100644 (file)
  */
 tvheadend.status_subs = function() {
 
-       tvheadend.subsStore = new Ext.data.JsonStore({
-               root : 'entries',
-               totalProperty : 'totalCount',
-               fields : [ {
-                       name : 'id'
-               }, {
-                       name : 'hostname'
-               }, {
-                       name : 'username'
-               }, {
-                       name : 'title'
-               }, {
-                       name : 'channel'
-               }, {
-                       name : 'service'
-               }, {
-                       name : 'state'
-               }, {
-                       name : 'errors'
-               }, {
-                       name : 'in'
-               }, {
-                       name : 'out'
-               }, {
-                       name : 'start',
-                       type : 'date',
-                       dateFormat : 'U' /* unix time */
-               } ],
-               url : 'api/status/subscriptions',
-               autoLoad : true,
-               id : 'id'
-       });
-
-
-
-       tvheadend.comet.on('subscriptions', function(m) {
-
-               if (m.reload != null) tvheadend.subsStore.reload();
-
-               if (m.updateEntry != null) {
-                       r = tvheadend.subsStore.getById(m.id)
-                       if (typeof r === 'undefined') {
-                               tvheadend.subsStore.reload();
-                               return;
-                       }
-
-                       r.data.channel  = m.channel;
-                       r.data.service  = m.service;
-                       r.data.state    = m.state;
-                       r.data.errors   = m.errors;
-                       r.data.in       = m.in;
-                       r.data.out      = m.out;
-
-                       tvheadend.subsStore.afterEdit(r);
-                       tvheadend.subsStore.fireEvent('updated', tvheadend.subsStore, r,
-                               Ext.data.Record.COMMIT);
-               }
-       });
-
-       function renderDate(value) {
-               var dt = new Date(value);
-               return dt.format('D j M H:i');
-       }
-
-       function renderBw(value, item, store) {
-               var txt = parseInt(value / 125);
-               var href = 'javascript:tvheadend.subscription_bw_monitor(' + store.id + ');';
-               return '<a href="' + href + '">' + txt + '</a>';
-       }
-
-       var subsCm = new Ext.grid.ColumnModel([{
-               width : 50,
-               id : 'hostname',
-               header : "Hostname",
-               dataIndex : 'hostname'
-       }, {
-               width : 50,
-               id : 'username',
-               header : "Username",
-               dataIndex : 'username'
-       }, {
-               width : 80,
-               id : 'title',
-               header : "Title",
-               dataIndex : 'title'
-       }, {
-               width : 50,
-               id : 'channel',
-               header : "Channel",
-               dataIndex : 'channel'
-       }, {
-               width : 200,
-               id : 'service',
-               header : "Service",
-               dataIndex : 'service',
-       }, {
-               width : 50,
-               id : 'start',
-               header : "Start",
-               dataIndex : 'start',
-               renderer : renderDate
-       }, {
-               width : 50,
-               id : 'state',
-               header : "State",
-               dataIndex : 'state'
-       }, {
-               width : 50,
-               id : 'errors',
-               header : "Errors",
-               dataIndex : 'errors'
-       }, {
-               width : 50,
-               id : 'in',
-               header : "Input (kb/s)",
-               dataIndex : 'in',
-               renderer: renderBw
-       }, {
-               width : 50,
-               id : 'out',
-               header : "Output (kb/s)",
-               dataIndex : 'out',
-               renderer: renderBw
-       } ]);
-
-       var subs = new Ext.grid.GridPanel({
-                border: false,
-               loadMask : true,
-               stripeRows : true,
-               disableSelection : true,
-               title : 'Subscriptions',
-               iconCls : 'eye',
-               store : tvheadend.subsStore,
-               cm : subsCm,
-                flex: 1,
-               viewConfig : {
-                       forceFit : true
-               }
-       });
-        return subs;
-}
+    tvheadend.subsStore = new Ext.data.JsonStore({
+        root: 'entries',
+        totalProperty: 'totalCount',
+        fields: [{
+                name: 'id'
+            }, {
+                name: 'hostname'
+            }, {
+                name: 'username'
+            }, {
+                name: 'title'
+            }, {
+                name: 'channel'
+            }, {
+                name: 'service'
+            }, {
+                name: 'state'
+            }, {
+                name: 'errors'
+            }, {
+                name: 'in'
+            }, {
+                name: 'out'
+            }, {
+                name: 'start',
+                type: 'date',
+                dateFormat: 'U' /* unix time */
+            }],
+        url: 'api/status/subscriptions',
+        autoLoad: true,
+        id: 'id'
+    });
+
+
+
+    tvheadend.comet.on('subscriptions', function(m) {
+
+        if (m.reload != null)
+            tvheadend.subsStore.reload();
+
+        if (m.updateEntry != null) {
+            r = tvheadend.subsStore.getById(m.id);
+            if (typeof r === 'undefined') {
+                tvheadend.subsStore.reload();
+                return;
+            }
+
+            r.data.channel = m.channel;
+            r.data.service = m.service;
+            r.data.state = m.state;
+            r.data.errors = m.errors;
+            r.data.in = m.in;
+            r.data.out = m.out;
+
+            tvheadend.subsStore.afterEdit(r);
+            tvheadend.subsStore.fireEvent('updated', tvheadend.subsStore, r,
+                    Ext.data.Record.COMMIT);
+        }
+    });
+
+    function renderDate(value) {
+        var dt = new Date(value);
+        return dt.format('D j M H:i');
+    }
+
+    function renderBw(value, item, store) {
+        var txt = parseInt(value / 125);
+        var href = 'javascript:tvheadend.subscription_bw_monitor(' + store.id + ');';
+        return '<a href="' + href + '">' + txt + '</a>';
+    }
+
+    var subsCm = new Ext.grid.ColumnModel([{
+            width: 50,
+            id: 'hostname',
+            header: "Hostname",
+            dataIndex: 'hostname'
+        }, {
+            width: 50,
+            id: 'username',
+            header: "Username",
+            dataIndex: 'username'
+        }, {
+            width: 80,
+            id: 'title',
+            header: "Title",
+            dataIndex: 'title'
+        }, {
+            width: 50,
+            id: 'channel',
+            header: "Channel",
+            dataIndex: 'channel'
+        }, {
+            width: 200,
+            id: 'service',
+            header: "Service",
+            dataIndex: 'service'
+        }, {
+            width: 50,
+            id: 'start',
+            header: "Start",
+            dataIndex: 'start',
+            renderer: renderDate
+        }, {
+            width: 50,
+            id: 'state',
+            header: "State",
+            dataIndex: 'state'
+        }, {
+            width: 50,
+            id: 'errors',
+            header: "Errors",
+            dataIndex: 'errors'
+        }, {
+            width: 50,
+            id: 'in',
+            header: "Input (kb/s)",
+            dataIndex: 'in',
+            renderer: renderBw
+        }, {
+            width: 50,
+            id: 'out',
+            header: "Output (kb/s)",
+            dataIndex: 'out',
+            renderer: renderBw
+        }]);
+
+    var subs = new Ext.grid.GridPanel({
+        border: false,
+        loadMask: true,
+        stripeRows: true,
+        disableSelection: true,
+        title: 'Subscriptions',
+        iconCls: 'eye',
+        store: tvheadend.subsStore,
+        cm: subsCm,
+        flex: 1,
+        viewConfig: {
+            forceFit: true
+        }
+    });
+    return subs;
+};
 
 
 /**
@@ -151,419 +152,421 @@ tvheadend.status_subs = function() {
  */
 tvheadend.status_streams = function() {
 
-       tvheadend.streamStatusStore = new Ext.data.JsonStore({
-               root : 'entries',
-               totalProperty : 'totalCount',
-               fields : [ {
-                       name : 'uuid'
-               }, {
-                       name : 'input'
-               }, {
-                       name : 'username'
-               }, {
-                       name : 'stream'
-               }, {
-                       name : 'subs'
-               }, {
-                       name : 'weight'
-               }, {
-                       name : 'signal'
-               }, {
-                       name : 'ber'
-               }, {
-                       name : 'unc'
-               }, {
-                       name : 'snr'
-               }, {
-                       name : 'bps'
-               }, {
-                       name : 'cc'
-               }, {
-                       name : 'te'
-               },
-               ],
-               url : 'api/status/inputs',
-               autoLoad : true,
-               id : 'uuid'
-       });
-
-  tvheadend.comet.on('input_status', function(m){
-    if (m.reload != null) tvheadend.streamStatusStore.reload();
-    if (m.update != null) {
-      var r = tvheadend.streamStatusStore.getById(m.uuid);
-      if (r) {
-        r.data.subs    = m.subs;
-        r.data.weight  = m.weight;
-        r.data.signal  = m.signal;
-        r.data.ber     = m.ber;
-        r.data.unc     = m.unc;
-        r.data.snr     = m.snr;
-        r.data.bps     = m.bps;
-        r.data.cc      = m.cc;
-        r.data.te      = m.te;
-
-        tvheadend.streamStatusStore.afterEdit(r);
-        tvheadend.streamStatusStore.fireEvent('updated',
-                                              tvheadend.streamStatusStore,
-                                              r,
-                                              Ext.data.Record.COMMIT);
-      } else {
-        tvheadend.streamStatusStore.reload();
-      } 
+    tvheadend.streamStatusStore = new Ext.data.JsonStore({
+        root: 'entries',
+        totalProperty: 'totalCount',
+        fields: [{
+                name: 'uuid'
+            }, {
+                name: 'input'
+            }, {
+                name: 'username'
+            }, {
+                name: 'stream'
+            }, {
+                name: 'subs'
+            }, {
+                name: 'weight'
+            }, {
+                name: 'signal'
+            }, {
+                name: 'ber'
+            }, {
+                name: 'unc'
+            }, {
+                name: 'snr'
+            }, {
+                name: 'bps'
+            }, {
+                name: 'cc'
+            }, {
+                name: 'te'
+            }
+        ],
+        url: 'api/status/inputs',
+        autoLoad: true,
+        id: 'uuid'
+    });
+
+    tvheadend.comet.on('input_status', function(m) {
+        if (m.reload != null)
+            tvheadend.streamStatusStore.reload();
+        if (m.update != null) {
+            var r = tvheadend.streamStatusStore.getById(m.uuid);
+            if (r) {
+                r.data.subs = m.subs;
+                r.data.weight = m.weight;
+                r.data.signal = m.signal;
+                r.data.ber = m.ber;
+                r.data.unc = m.unc;
+                r.data.snr = m.snr;
+                r.data.bps = m.bps;
+                r.data.cc = m.cc;
+                r.data.te = m.te;
+
+                tvheadend.streamStatusStore.afterEdit(r);
+                tvheadend.streamStatusStore.fireEvent('updated',
+                        tvheadend.streamStatusStore,
+                        r,
+                        Ext.data.Record.COMMIT);
+            } else {
+                tvheadend.streamStatusStore.reload();
+            }
+        }
+    });
+
+    var signal = new Ext.ux.grid.ProgressColumn({
+        header: "Signal Strength",
+        dataIndex: 'signal',
+        width: 85,
+        textPst: '%',
+        colored: true
+    });
+
+    function renderBw(value, item, store) {
+        var txt = parseInt(value / 1024);
+        var href = "javascript:tvheadend.stream_bw_monitor('" + store.id + "');";
+        return '<a href="' + href + '">' + txt + '</a>';
     }
-  });
-
-       var signal = new Ext.ux.grid.ProgressColumn({
-               header : "Signal Strength",
-               dataIndex : 'signal',
-               width : 85,
-               textPst : '%',
-               colored : true
-       });
-
-       function renderBw(value, item, store) {
-               var txt = parseInt(value / 1024);
-               var href = "javascript:tvheadend.stream_bw_monitor('" + store.id + "');";
-               return '<a href="' + href + '">' + txt + '</a>';
-       }
-
-       var cm = new Ext.grid.ColumnModel([{
-               width : 100,
-               header : "Input",
-               dataIndex : 'input'
-        },{
-               width : 100,
-               header : "Stream",
-               dataIndex : 'stream'
-        },{
-               width : 50,
-               header : "Subs #",
-               dataIndex : 'subs'
-        },{
-               width : 50,
-               header : "Weight",
-               dataIndex : 'weight'
-        },{
-               width : 50,
-               header : "Bandwidth (kb/s)",
-               dataIndex : 'bps',
-               renderer: renderBw
-        },{
-               width : 50,
-               header : "BER",
-               dataIndex : 'ber'
-        },{
-               width : 50,
-               header : "Uncorrected BER",
-               dataIndex : 'unc'
-        },{
-               width : 50,
-               header : "Transport Error",
-               dataIndex : 'te'
-        },{
-               width : 50,
-               header : "Continuity Error",
-               dataIndex : 'cc'
-        },{
-               width : 50,
-               header : "SNR",
-               dataIndex : 'snr',
-                renderer: function(value) {
-                        if(value > 0) {
-                                return value.toFixed(1) + " dB";
-                        } else {
-                                return '<span class="tvh-grid-unset">Unknown</span>';
-                        }
+
+    var cm = new Ext.grid.ColumnModel([{
+            width: 100,
+            header: "Input",
+            dataIndex: 'input'
+        }, {
+            width: 100,
+            header: "Stream",
+            dataIndex: 'stream'
+        }, {
+            width: 50,
+            header: "Subs #",
+            dataIndex: 'subs'
+        }, {
+            width: 50,
+            header: "Weight",
+            dataIndex: 'weight'
+        }, {
+            width: 50,
+            header: "Bandwidth (kb/s)",
+            dataIndex: 'bps',
+            renderer: renderBw
+        }, {
+            width: 50,
+            header: "BER",
+            dataIndex: 'ber'
+        }, {
+            width: 50,
+            header: "Uncorrected BER",
+            dataIndex: 'unc'
+        }, {
+            width: 50,
+            header: "Transport Error",
+            dataIndex: 'te'
+        }, {
+            width: 50,
+            header: "Continuity Error",
+            dataIndex: 'cc'
+        }, {
+            width: 50,
+            header: "SNR",
+            dataIndex: 'snr',
+            renderer: function(value) {
+                if (value > 0) {
+                    return value.toFixed(1) + " dB";
+                } else {
+                    return '<span class="tvh-grid-unset">Unknown</span>';
                 }
+            }
         }, signal]);
 
-       var panel = new Ext.grid.GridPanel({
-                border: false,
-               loadMask : true,
-               stripeRows : true,
-               disableSelection : true,
-               title : 'Stream',
-               iconCls : 'hardware',
-               store : tvheadend.streamStatusStore,
-               cm : cm,
-                flex: 1,
-               viewConfig : {
-                       forceFit : true
-               }
-       });
-        return panel;
-}
+    var panel = new Ext.grid.GridPanel({
+        border: false,
+        loadMask: true,
+        stripeRows: true,
+        disableSelection: true,
+        title: 'Stream',
+        iconCls: 'hardware',
+        store: tvheadend.streamStatusStore,
+        cm: cm,
+        flex: 1,
+        viewConfig: {
+            forceFit: true
+        }
+    });
+    return panel;
+};
 
 /**
  *
  */
 tvheadend.status_conns = function() {
 
-       var store = new Ext.data.JsonStore({
-               root : 'entries',
-               totalProperty : 'totalCount',
-               fields : [ {
-      name : 'id'
-    }, {
-                       name : 'type'
-               }, {
-                       name : 'peer'
-               }, {
-                       name : 'user'
-               }, {
-                       name : 'started',
-                       type : 'date',
-                       dateFormat : 'U' /* unix time */
-               } ],
-               url : 'api/status/connections',
-               autoLoad : true,
-               id : 'id'
-       });
-
-       tvheadend.comet.on('connections', function(m) {
-               if (m.reload != null) store.reload();
-       });
-
-       function renderDate(value) {
-               var dt = new Date(value);
-               return dt.format('Y-m-d H:i:s');
-       }
-
-       var cm = new Ext.grid.ColumnModel([{
-               width : 50,
-               id : 'type',
-               header : "Type",
-               dataIndex : 'type'
-       }, {
-               width : 50,
-               id : 'peer',
-               header : "IP Address",
-               dataIndex : 'peer'
-       }, {
-               width : 50,
-               id : 'user',
-               header : "Username",
-               dataIndex : 'user'
-       }, {
-               width : 50,
-               id : 'started',
-               header : "Started",
-               dataIndex : 'started',
-               renderer : renderDate
-       } ]);
-
-       var panel = new Ext.grid.GridPanel({
-    border: false,
-               loadMask : true,
-               stripeRows : true,
-               disableSelection : true,
-               title : 'Connections',
-               iconCls : 'eye',
-               store : store,
-               cm : cm,
-    flex: 1,
-               viewConfig : {
-                       forceFit : true
-               }
-       });
-  return panel;
-}
+    var store = new Ext.data.JsonStore({
+        root: 'entries',
+        totalProperty: 'totalCount',
+        fields: [{
+                name: 'id'
+            }, {
+                name: 'type'
+            }, {
+                name: 'peer'
+            }, {
+                name: 'user'
+            }, {
+                name: 'started',
+                type: 'date',
+                dateFormat: 'U' /* unix time */
+            }],
+        url: 'api/status/connections',
+        autoLoad: true,
+        id: 'id'
+    });
+
+    tvheadend.comet.on('connections', function(m) {
+        if (m.reload != null)
+            store.reload();
+    });
+
+    function renderDate(value) {
+        var dt = new Date(value);
+        return dt.format('Y-m-d H:i:s');
+    }
+
+    var cm = new Ext.grid.ColumnModel([{
+            width: 50,
+            id: 'type',
+            header: "Type",
+            dataIndex: 'type'
+        }, {
+            width: 50,
+            id: 'peer',
+            header: "IP Address",
+            dataIndex: 'peer'
+        }, {
+            width: 50,
+            id: 'user',
+            header: "Username",
+            dataIndex: 'user'
+        }, {
+            width: 50,
+            id: 'started',
+            header: "Started",
+            dataIndex: 'started',
+            renderer: renderDate
+        }]);
+
+    var panel = new Ext.grid.GridPanel({
+        border: false,
+        loadMask: true,
+        stripeRows: true,
+        disableSelection: true,
+        title: 'Connections',
+        iconCls: 'eye',
+        store: store,
+        cm: cm,
+        flex: 1,
+        viewConfig: {
+            forceFit: true
+        }
+    });
+    return panel;
+};
 
 tvheadend.status = function() {
-  var panel = new Ext.TabPanel({
-               title : 'Status',
-    autoScroll : true,
-    activeTab : 0,
-               iconCls : 'eye',
-               items : [
-      new tvheadend.status_streams,
-      new tvheadend.status_subs,
-      new tvheadend.status_conns,
-      new tvheadend.service_mapper_status
-    ]
-  });
-       return panel;
-}
+    var panel = new Ext.TabPanel({
+        title: 'Status',
+        autoScroll: true,
+        activeTab: 0,
+        iconCls: 'eye',
+        items: [
+            new tvheadend.status_streams,
+            new tvheadend.status_subs,
+            new tvheadend.status_conns,
+            new tvheadend.service_mapper_status
+        ]
+    });
+    return panel;
+};
 
 
 tvheadend.subscription_bw_monitor = function(id) {
-    var inputSeries  = new TimeSeries();
+    var inputSeries = new TimeSeries();
     var outputSeries = new TimeSeries();
     var chart = new SmoothieChart({
-       minValue: 0,
-       grid: {
-           sharpLines: true,
-           fillStyle: 'transparent',
-           verticalSections: 0,
-           millisPerLine: 0
-       },
-       labels: {
-           disabled: false,
-           fillStyle: '#000000',
-           fontSize: 12
-       }
+        minValue: 0,
+        grid: {
+            sharpLines: true,
+            fillStyle: 'transparent',
+            verticalSections: 0,
+            millisPerLine: 0
+        },
+        labels: {
+            disabled: false,
+            fillStyle: '#000000',
+            fontSize: 12
+        }
     });
 
     chart.addTimeSeries(inputSeries, {
-       strokeStyle: 'rgb(0, 255, 0)',
-       fillStyle: 'rgba(0, 255, 0, 0.5)',
-       lineWidth: 3
+        strokeStyle: 'rgb(0, 255, 0)',
+        fillStyle: 'rgba(0, 255, 0, 0.5)',
+        lineWidth: 3
     });
 
     chart.addTimeSeries(outputSeries, {
-       strokeStyle: 'rgb(255, 0, 255)',
-       fillStyle: 'rgba(255, 0, 255, 0.5)',
-       lineWidth: 3
+        strokeStyle: 'rgb(255, 0, 255)',
+        fillStyle: 'rgba(255, 0, 255, 0.5)',
+        lineWidth: 3
     });
-   
+
     var inputLbl = new Ext.form.Label();
     var outputLbl = new Ext.form.Label();
     var comprLbl = new Ext.form.Label();
 
     var win = new Ext.Window({
         title: 'Bandwidth monitor',
-        layout:'fit',
-       resizable: false,
-       width : 450 + 30,
-       height : 150 + 50,
-       constrainHeader : true,
-       tbar : [inputLbl, '-', outputLbl, '-', comprLbl],
-       items: {
-           xtype: 'box',
-           autoEl: {
-               tag: 'canvas',
-               width: 450,
-               height: 150
-           },
-           listeners: {
-               render: {
-                   scope: this,
-                   fn: function(item) {
-                       chart.streamTo(item.el.dom, 1000);
-                   }
-               },
+        layout: 'fit',
+        resizable: false,
+        width: 450 + 30,
+        height: 150 + 50,
+        constrainHeader: true,
+        tbar: [inputLbl, '-', outputLbl, '-', comprLbl],
+        items: {
+            xtype: 'box',
+            autoEl: {
+                tag: 'canvas',
+                width: 450,
+                height: 150
+            },
+            listeners: {
+                render: {
+                    scope: this,
+                    fn: function(item) {
+                        chart.streamTo(item.el.dom, 1000);
+                    }
+                },
                 resize: {
-                   scope: this,
-                   fn: function(item) {
-                       chart.render(item.el.dom, 1000);
+                    scope: this,
+                    fn: function(item) {
+                        chart.render(item.el.dom, 1000);
                     }
-               }
-           }
-       }
+                }
+            }
+        }
     });
 
     var task = {
-       interval: 1000,
-       run: function() {
-           r = tvheadend.subsStore.getById(id);
-           if (typeof r === 'undefined') {
-               chart.stop();
-               Ext.TaskMgr.stop(task);
-               return;
-           }
-
-           var input  = Math.round(r.data.in  / 125);
-           var output = Math.round(r.data.out / 125);
-           var ratio  = new Number(r.data.in / r.data.out).toPrecision(3);
-
-           win.setTitle(r.data.channel);
-           inputLbl.setText('In: ' + input + ' kb/s');
-           outputLbl.setText('Out: ' + output + ' kb/s');
-           comprLbl.setText('Compression ratio: ' + ratio);
-
-           inputSeries.append(new Date().getTime(), input);
-           outputSeries.append(new Date().getTime(), output);
-       }
+        interval: 1000,
+        run: function() {
+            r = tvheadend.subsStore.getById(id);
+            if (typeof r === 'undefined') {
+                chart.stop();
+                Ext.TaskMgr.stop(task);
+                return;
+            }
+
+            var input = Math.round(r.data.in / 125);
+            var output = Math.round(r.data.out / 125);
+            var ratio = new Number(r.data.in / r.data.out).toPrecision(3);
+
+            win.setTitle(r.data.channel);
+            inputLbl.setText('In: ' + input + ' kb/s');
+            outputLbl.setText('Out: ' + output + ' kb/s');
+            comprLbl.setText('Compression ratio: ' + ratio);
+
+            inputSeries.append(new Date().getTime(), input);
+            outputSeries.append(new Date().getTime(), output);
+        }
     };
 
     win.on('close', function() {
-       chart.stop();
-       Ext.TaskMgr.stop(task);
+        chart.stop();
+        Ext.TaskMgr.stop(task);
     });
 
     win.show();
+
     Ext.TaskMgr.start(task);
 };
 
 
 tvheadend.stream_bw_monitor = function(id) {
-    var inputSeries  = new TimeSeries();
+    var inputSeries = new TimeSeries();
     var chart = new SmoothieChart({
-       minValue: 0,
-       grid: {
-           sharpLines: true,
-           fillStyle: 'transparent',
-           verticalSections: 0,
-           millisPerLine: 0
-       },
-       labels: {
-           disabled: false,
-           fillStyle: '#000000',
-           fontSize: 12
-       }
+        minValue: 0,
+        grid: {
+            sharpLines: true,
+            fillStyle: 'transparent',
+            verticalSections: 0,
+            millisPerLine: 0
+        },
+        labels: {
+            disabled: false,
+            fillStyle: '#000000',
+            fontSize: 12
+        }
     });
 
     chart.addTimeSeries(inputSeries, {
-       strokeStyle: 'rgb(0, 255, 0)',
-       fillStyle: 'rgba(0, 255, 0, 0.5)',
-       lineWidth: 3
+        strokeStyle: 'rgb(0, 255, 0)',
+        fillStyle: 'rgba(0, 255, 0, 0.5)',
+        lineWidth: 3
     });
 
     var inputLbl = new Ext.form.Label();
 
     var win = new Ext.Window({
         title: 'Bandwidth monitor',
-        layout:'fit',
-       resizable: false,
-       width : 450 + 30,
-       height : 150 + 50,
-       constrainHeader : true,
-       tbar : [inputLbl],
-       items: {
-           xtype: 'box',
-           autoEl: {
-               tag: 'canvas',
-               width: 450,
-               height: 150
-           },
-           listeners: {
-               render: {
-                   scope: this,
-                   fn: function(item) {
-                       chart.streamTo(item.el.dom, 1000);
-                   }
-               },
+        layout: 'fit',
+        resizable: false,
+        width: 450 + 30,
+        height: 150 + 50,
+        constrainHeader: true,
+        tbar: [inputLbl],
+        items: {
+            xtype: 'box',
+            autoEl: {
+                tag: 'canvas',
+                width: 450,
+                height: 150
+            },
+            listeners: {
+                render: {
+                    scope: this,
+                    fn: function(item) {
+                        chart.streamTo(item.el.dom, 1000);
+                    }
+                },
                 resize: {
-                   scope: this,
-                   fn: function(item) {
-                       chart.render(item.el.dom, 1000);
+                    scope: this,
+                    fn: function(item) {
+                        chart.render(item.el.dom, 1000);
                     }
-               }
-           }
-       }
+                }
+            }
+        }
     });
 
     var task = {
-       interval: 1000,
-       run: function() {
-           r = tvheadend.streamStatusStore.getById(id);
-           if (typeof r === 'undefined') {
-               chart.stop();
-               Ext.TaskMgr.stop(task);
-               return;
-           }
-
-           win.setTitle(r.data.input + ' (' + r.data.stream + ')');
-           var input = Math.round(r.data.bps  / 1024);
-           inputLbl.setText('Input: ' + input + ' kb/s');
-           inputSeries.append(new Date().getTime(), input);
-       }
+        interval: 1000,
+        run: function() {
+            r = tvheadend.streamStatusStore.getById(id);
+            if (typeof r === 'undefined') {
+                chart.stop();
+                Ext.TaskMgr.stop(task);
+                return;
+            }
+
+            win.setTitle(r.data.input + ' (' + r.data.stream + ')');
+            var input = Math.round(r.data.bps / 1024);
+            inputLbl.setText('Input: ' + input + ' kb/s');
+            inputSeries.append(new Date().getTime(), input);
+        }
     };
 
     win.on('close', function() {
-       chart.stop();
-       Ext.TaskMgr.stop(task);
+        chart.stop();
+        Ext.TaskMgr.stop(task);
     });
 
     win.show();
index c36498d862f75b628820250c13bcba360cc7242f..92782612656d8a8fd3a643fb28c2eeab1e7dec34 100644 (file)
 tvheadend.tableEditor = function(title, dtable, cm, rec, plugins, store,
-       helpContent, icon) {
+        helpContent, icon) {
 
-       if (store == null) {
-               store = new Ext.data.JsonStore({
-                       root : 'entries',
-                       fields : rec,
-                       url : "tablemgr",
-                       autoLoad : true,
-                       id : 'id',
-                       baseParams : {
-                               table : dtable,
-                               op : "get"
-                       }
-               });
-       }
+    if (store == null) {
+        store = new Ext.data.JsonStore({
+            root: 'entries',
+            fields: rec,
+            url: "tablemgr",
+            autoLoad: true,
+            id: 'id',
+            baseParams: {
+                table: dtable,
+                op: "get"
+            }
+        });
+    }
 
-       tvheadend.comet.on(dtable, function(m){
-               if (m.reload)
-                       store.reload();
-       });
+    tvheadend.comet.on(dtable, function(m) {
+        if (m.reload)
+            store.reload();
+    });
 
-       function addRecord() {
-               Ext.Ajax.request({
-                       url : "tablemgr",
-                       params : {
-                               op : "create",
-                               table : dtable
-                       },
-                       failure : function(response, options) {
-                               Ext.MessageBox.alert('Server Error',
-                                       'Unable to generate new record');
-                       },
-                       success : function(response, options) {
-                               var responseData = Ext.util.JSON.decode(response.responseText);
-                               var p = new rec(responseData, responseData.id);
-                               grid.stopEditing();
-                               store.insert(0, p);
-                               grid.startEditing(0, 0);
-                       }
-               })
-       }
-       ;
+    function addRecord() {
+        Ext.Ajax.request({
+            url: "tablemgr",
+            params: {
+                op: "create",
+                table: dtable
+            },
+            failure: function(response, options) {
+                Ext.MessageBox.alert('Server Error',
+                        'Unable to generate new record');
+            },
+            success: function(response, options) {
+                var responseData = Ext.util.JSON.decode(response.responseText);
+                var p = new rec(responseData, responseData.id);
+                grid.stopEditing();
+                store.insert(0, p);
+                grid.startEditing(0, 0);
+            }
+        });
+    }
+    ;
 
-       function delSelected() {
-               var selectedKeys = grid.selModel.selections.keys;
-               if (selectedKeys.length > 0) {
-                       Ext.MessageBox.confirm('Message',
-                               'Do you really want to delete selection?', deleteRecord);
-               }
-               else {
-                       Ext.MessageBox.alert('Message',
-                               'Please select at least one item to delete');
-               }
-       }
-       ;
+    function delSelected() {
+        var selectedKeys = grid.selModel.selections.keys;
+        if (selectedKeys.length > 0) {
+            Ext.MessageBox.confirm('Message',
+                    'Do you really want to delete selection?', deleteRecord);
+        }
+        else {
+            Ext.MessageBox.alert('Message',
+                    'Please select at least one item to delete');
+        }
+    }
+    ;
 
-       function deleteRecord(btn) {
-               if (btn == 'yes') {
-                       var selectedKeys = grid.selModel.selections.keys;
+    function deleteRecord(btn) {
+        if (btn === 'yes') {
+            var selectedKeys = grid.selModel.selections.keys;
 
-                       Ext.Ajax.request({
-                               url : "tablemgr",
-                               params : {
-                                       op : "delete",
-                                       table : dtable,
-                                       entries : Ext.encode(selectedKeys)
-                               },
-                               failure : function(response, options) {
-                                       Ext.MessageBox.alert('Server Error', 'Unable to delete');
-                               },
-                               success : function(response, options) {
-                               }
-                       })
-               }
-       }
+            Ext.Ajax.request({
+                url: "tablemgr",
+                params: {
+                    op: "delete",
+                    table: dtable,
+                    entries: Ext.encode(selectedKeys)
+                },
+                failure: function(response, options) {
+                    Ext.MessageBox.alert('Server Error', 'Unable to delete');
+                },
+                success: function(response, options) {
+                }
+            });
+        }
+    }
 
-       function saveChanges() {
-               var mr = store.getModifiedRecords();
-               var out = new Array();
-               for ( var x = 0; x < mr.length; x++) {
-                       v = mr[x].getChanges();
-                       out[x] = v;
-                       out[x].id = mr[x].id;
-               }
+    function saveChanges() {
+        var mr = store.getModifiedRecords();
+        var out = new Array();
+        for (var x = 0; x < mr.length; x++) {
+            v = mr[x].getChanges();
+            out[x] = v;
+            out[x].id = mr[x].id;
+        }
 
-               Ext.Ajax.request({
-                       url : "tablemgr",
-                       params : {
-                               op : "update",
-                               table : dtable,
-                               entries : Ext.encode(out)
-                       },
-                       success : function(response, options) {
-                               // Note: this call is mostly redundant (comet update will pick it up anyway)
-                               store.commitChanges();
-                       },
-                       failure : function(response, options) {
-                               Ext.MessageBox.alert('Message', response.statusText);
-                       }
-               });
-       }
+        Ext.Ajax.request({
+            url: "tablemgr",
+            params: {
+                op: "update",
+                table: dtable,
+                entries: Ext.encode(out)
+            },
+            success: function(response, options) {
+                // Note: this call is mostly redundant (comet update will pick it up anyway)
+                store.commitChanges();
+            },
+            failure: function(response, options) {
+                Ext.MessageBox.alert('Message', response.statusText);
+            }
+        });
+    }
 
-       var selModel = new Ext.grid.RowSelectionModel({
-               singleSelect : false
-       });
+    var selModel = new Ext.grid.RowSelectionModel({
+        singleSelect: false
+    });
 
-       var delButton = new Ext.Toolbar.Button({
-               tooltip : 'Delete one or more selected rows',
-               iconCls : 'remove',
-               text : 'Delete selected',
-               handler : delSelected,
-               disabled : true
-       });
+    var delButton = new Ext.Toolbar.Button({
+        tooltip: 'Delete one or more selected rows',
+        iconCls: 'remove',
+        text: 'Delete selected',
+        handler: delSelected,
+        disabled: true
+    });
 
-       var saveBtn = new Ext.Toolbar.Button({
-               tooltip : 'Save any changes made (Changed cells have red borders)',
-               iconCls : 'save',
-               text : "Save changes",
-               handler : saveChanges,
-               disabled : true
-       });
+    var saveBtn = new Ext.Toolbar.Button({
+        tooltip: 'Save any changes made (Changed cells have red borders)',
+        iconCls: 'save',
+        text: "Save changes",
+        handler: saveChanges,
+        disabled: true
+    });
 
-       var rejectBtn = new Ext.Toolbar.Button({
-               tooltip : 'Revert any changes made (Changed cells have red borders)',
-               iconCls : 'undo',
-               text : "Revert changes",
-               handler : function() {
-                       store.rejectChanges();
-               },
-               disabled : true
-       });
+    var rejectBtn = new Ext.Toolbar.Button({
+        tooltip: 'Revert any changes made (Changed cells have red borders)',
+        iconCls: 'undo',
+        text: "Revert changes",
+        handler: function() {
+            store.rejectChanges();
+        },
+        disabled: true
+    });
 
-       store.on('update', function(s, r, o) {
-               d = s.getModifiedRecords().length == 0
-               saveBtn.setDisabled(d);
-               rejectBtn.setDisabled(d);
-       });
+    store.on('update', function(s, r, o) {
+        d = s.getModifiedRecords().length === 0;
+        saveBtn.setDisabled(d);
+        rejectBtn.setDisabled(d);
+    });
 
-       selModel.on('selectionchange', function(self) {
-               if (self.getCount() > 0) {
-                       delButton.enable();
-               }
-               else {
-                       delButton.disable();
-               }
-       });
+    selModel.on('selectionchange', function(self) {
+        if (self.getCount() > 0) {
+            delButton.enable();
+        }
+        else {
+            delButton.disable();
+        }
+    });
 
-       var grid = new Ext.grid.EditorGridPanel({
-               title : title,
-               iconCls : icon,
-               plugins : plugins,
-               store : store,
-               clicksToEdit : 2,
-               cm : cm,
-               viewConfig : {
-                       forceFit : true
-               },
-               selModel : selModel,
-               stripeRows : true,
-               tbar : [
-                       {
-                               tooltip : 'Create a new entry on the server. '
-                                       + 'The new entry is initially disabled so it must be enabled '
-                                       + 'before it start taking effect.',
-                               iconCls : 'add',
-                               text : 'Add entry',
-                               handler : addRecord
-                       }, '-', delButton, '-', saveBtn, rejectBtn, '->', {
-                               text : 'Help',
-                               handler : function() {
-                                       new tvheadend.help(title, helpContent);
-                               }
-                       } ]
-       });
-       return grid;
-}
+    var grid = new Ext.grid.EditorGridPanel({
+        title: title,
+        iconCls: icon,
+        plugins: plugins,
+        store: store,
+        clicksToEdit: 2,
+        cm: cm,
+        viewConfig: {
+            forceFit: true
+        },
+        selModel: selModel,
+        stripeRows: true,
+        tbar: [
+            {
+                tooltip: 'Create a new entry on the server. '
+                        + 'The new entry is initially disabled so it must be enabled '
+                        + 'before it start taking effect.',
+                iconCls: 'add',
+                text: 'Add entry',
+                handler: addRecord
+            }, '-', delButton, '-', saveBtn, rejectBtn, '->', {
+                text: 'Help',
+                handler: function() {
+                    new tvheadend.help(title, helpContent);
+                }
+            }]
+    });
+    return grid;
+};
index 02335006044fe80ba57a0a87c28a8ed38fa24906..472312ebb5c43aab3aa8743241813707b8e052bb 100644 (file)
 tvheadend.timeshift = function() {
 
-  /* ****************************************************************
-   * Data
-   * ***************************************************************/
-
-  var confreader = new Ext.data.JsonReader(
-    {
-      root: 'config'
-    },
+    /* ****************************************************************
+     * Data
+     * ***************************************************************/
+
+    var confreader = new Ext.data.JsonReader(
+            {
+                root: 'config'
+            },
     [
-      'timeshift_enabled', 'timeshift_ondemand',
-      'timeshift_path',
-      'timeshift_unlimited_period', 'timeshift_max_period',
-      'timeshift_unlimited_size', 'timeshift_max_size'
+        'timeshift_enabled', 'timeshift_ondemand',
+        'timeshift_path',
+        'timeshift_unlimited_period', 'timeshift_max_period',
+        'timeshift_unlimited_size', 'timeshift_max_size'
     ]
-  );
-  
-  /* ****************************************************************
-   * Fields
-   * ***************************************************************/
-
-  var timeshiftEnabled = new Ext.form.Checkbox({
-    fieldLabel: 'Enabled',
-    name: 'timeshift_enabled',
-    width: 300
-  });
-
-  var timeshiftOndemand = new Ext.form.Checkbox({
-    fieldLabel: 'On-Demand',
-    name: 'timeshift_ondemand',
-    width: 300
-  });
-
-  var timeshiftPath = new Ext.form.TextField({
-    fieldLabel: 'Storage Path',
-    name: 'timeshift_path',
-    allowBlank: true,
-    width: 300
-  });
-
-  var timeshiftMaxPeriod = new Ext.form.NumberField({
-    fieldLabel: 'Max. Period (mins)',
-    name: 'timeshift_max_period',
-    allowBlank: false,
-    width: 300
-  });
-
-  var timeshiftUnlPeriod = new Ext.form.Checkbox({
-    fieldLabel: '&nbsp;&nbsp;&nbsp;(unlimited)',
-    name: 'timeshift_unlimited_period',
-    Width: 300
-  });
-
-  var timeshiftMaxSize = new Ext.form.NumberField({
-    fieldLabel: 'Max. Size (MB)',
-    name: 'timeshift_max_size',
-    allowBlank: false,
-    width: 300
-  });
-
-  var timeshiftUnlSize = new Ext.form.Checkbox({
-    fieldLabel: '&nbsp;&nbsp;&nbsp;(unlimited)',
-    name: 'timeshift_unlimited_size',
-    Width: 300
-  });
-
-  /* ****************************************************************
-   * Events
-   * ***************************************************************/
-
-  timeshiftUnlPeriod.on('check', function(e, c){
-    timeshiftMaxPeriod.setDisabled(c);
-  });
-  timeshiftUnlSize.on('check', function(e, c){
-    timeshiftMaxSize.setDisabled(c);
-  });
-
-  /* ****************************************************************
-   * Form
-   * ***************************************************************/
-
-  var saveButton = new Ext.Button({
-    text : "Save configuration",
-    tooltip : 'Save changes made to configuration below',
-    iconCls : 'save',
-    handler : saveChanges
-  });
-
-  var helpButton = new Ext.Button({
-    text : 'Help',
-    handler : function() {
-      new tvheadend.help('Timeshift Configuration', 'config_timeshift.html');
-    }
-  });
-
-  var confpanel = new Ext.FormPanel({
-    title : 'Timeshift',
-    iconCls : 'clock',
-    border : false,
-    bodyStyle : 'padding:15px',
-    labelAlign : 'left',
-    labelWidth : 150,
-    waitMsgTarget : true,
-    reader : confreader,
-    layout : 'form',
-    defaultType : 'textfield',
-    autoHeight : true,
-    items : [
-      timeshiftEnabled, timeshiftOndemand,
-      timeshiftPath,
-      timeshiftMaxPeriod, timeshiftUnlPeriod,
-      timeshiftMaxSize, timeshiftUnlSize
-    ],
-    tbar : [ saveButton, '->', helpButton ]
-  });
-
-  /* ****************************************************************
-   * Load/Save
-   * ***************************************************************/
-
-  confpanel.on('render', function() {
-    confpanel.getForm().load({
-      url: 'timeshift',
-      params: {
-        'op': 'loadSettings'
-      },
-      success: function() {
-        confpanel.enable();
-        timeshiftMaxPeriod.setDisabled(timeshiftUnlPeriod.getValue());
-        timeshiftMaxSize.setDisabled(timeshiftUnlSize.getValue());
-      }
+            );
+
+    /* ****************************************************************
+     * Fields
+     * ***************************************************************/
+
+    var timeshiftEnabled = new Ext.form.Checkbox({
+        fieldLabel: 'Enabled',
+        name: 'timeshift_enabled',
+        width: 300
+    });
+
+    var timeshiftOndemand = new Ext.form.Checkbox({
+        fieldLabel: 'On-Demand',
+        name: 'timeshift_ondemand',
+        width: 300
+    });
+
+    var timeshiftPath = new Ext.form.TextField({
+        fieldLabel: 'Storage Path',
+        name: 'timeshift_path',
+        allowBlank: true,
+        width: 300
     });
-  });
-
-  function saveChanges() {
-    confpanel.getForm().submit({
-      url : 'timeshift',
-      params : {
-        op : 'saveSettings',
-      },
-      waitMsg : 'Saving Data...',
-      success : function(form, action) {
-      },
-      failure : function(form, action) {
-        Ext.Msg.alert('Save failed', action.result.errormsg);
-      }
+
+    var timeshiftMaxPeriod = new Ext.form.NumberField({
+        fieldLabel: 'Max. Period (mins)',
+        name: 'timeshift_max_period',
+        allowBlank: false,
+        width: 300
     });
-  }
 
-  return confpanel;
-}
+    var timeshiftUnlPeriod = new Ext.form.Checkbox({
+        fieldLabel: '&nbsp;&nbsp;&nbsp;(unlimited)',
+        name: 'timeshift_unlimited_period',
+        Width: 300
+    });
+
+    var timeshiftMaxSize = new Ext.form.NumberField({
+        fieldLabel: 'Max. Size (MB)',
+        name: 'timeshift_max_size',
+        allowBlank: false,
+        width: 300
+    });
+
+    var timeshiftUnlSize = new Ext.form.Checkbox({
+        fieldLabel: '&nbsp;&nbsp;&nbsp;(unlimited)',
+        name: 'timeshift_unlimited_size',
+        Width: 300
+    });
+
+    /* ****************************************************************
+     * Events
+     * ***************************************************************/
+
+    timeshiftUnlPeriod.on('check', function(e, c) {
+        timeshiftMaxPeriod.setDisabled(c);
+    });
+    timeshiftUnlSize.on('check', function(e, c) {
+        timeshiftMaxSize.setDisabled(c);
+    });
+
+    /* ****************************************************************
+     * Form
+     * ***************************************************************/
+
+    var saveButton = new Ext.Button({
+        text: "Save configuration",
+        tooltip: 'Save changes made to configuration below',
+        iconCls: 'save',
+        handler: saveChanges
+    });
+
+    var helpButton = new Ext.Button({
+        text: 'Help',
+        handler: function() {
+            new tvheadend.help('Timeshift Configuration', 'config_timeshift.html');
+        }
+    });
+
+    var confpanel = new Ext.FormPanel({
+        title: 'Timeshift',
+        iconCls: 'clock',
+        border: false,
+        bodyStyle: 'padding:15px',
+        labelAlign: 'left',
+        labelWidth: 150,
+        waitMsgTarget: true,
+        reader: confreader,
+        layout: 'form',
+        defaultType: 'textfield',
+        autoHeight: true,
+        items: [
+            timeshiftEnabled, timeshiftOndemand,
+            timeshiftPath,
+            timeshiftMaxPeriod, timeshiftUnlPeriod,
+            timeshiftMaxSize, timeshiftUnlSize
+        ],
+        tbar: [saveButton, '->', helpButton]
+    });
+
+    /* ****************************************************************
+     * Load/Save
+     * ***************************************************************/
+
+    confpanel.on('render', function() {
+        confpanel.getForm().load({
+            url: 'timeshift',
+            params: {
+                'op': 'loadSettings'
+            },
+            success: function() {
+                confpanel.enable();
+                timeshiftMaxPeriod.setDisabled(timeshiftUnlPeriod.getValue());
+                timeshiftMaxSize.setDisabled(timeshiftUnlSize.getValue());
+            }
+        });
+    });
+
+    function saveChanges() {
+        confpanel.getForm().submit({
+            url: 'timeshift',
+            params: {
+                op: 'saveSettings'
+            },
+            waitMsg: 'Saving Data...',
+            success: function(form, action) {
+            },
+            failure: function(form, action) {
+                Ext.Msg.alert('Save failed', action.result.errormsg);
+            }
+        });
+    }
+
+    return confpanel;
+};
index d3353c0936378a9df352a472f00491956eacb81f..50e32480e5947a32ee3a7782271f8793e6245b18 100644 (file)
@@ -1,10 +1,10 @@
 tvheadend.tvadapters = function() {
-  return tvheadend.idnode_tree( {
-    url   : 'api/hardware/tree',
-    title : 'TV adapters',
-    comet : 'hardware',
-    help  : function() {
-      new tvheadend.help('TV adapters', 'config_tvadapters.html');
-    }
-  });
-}
+    return tvheadend.idnode_tree({
+        url: 'api/hardware/tree',
+        title: 'TV adapters',
+        comet: 'hardware',
+        help: function() {
+            new tvheadend.help('TV adapters', 'config_tvadapters.html');
+        }
+    });
+};
index dd9806a9c6d35b6f87779f1556542dec7928daef..7fe99e4e2ebe7b8589f869f9d548d84da8aefce3 100644 (file)
 tvheadend.accessupdate = null;
-tvheadend.capabilties  = null;
-tvheadend.conf_chepg   = null;
-tvheadend.conf_dvbin   = null;
-tvheadend.conf_tsdvr   = null;
-tvheadend.conf_csa     = null;
+tvheadend.capabilties = null;
+tvheadend.conf_chepg = null;
+tvheadend.conf_dvbin = null;
+tvheadend.conf_tsdvr = null;
+tvheadend.conf_csa = null;
 
 /* State Provider */
 Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
-  // 7 days from now
-  expires : new Date(new Date().getTime()+(1000*60*60*24*7)),
+    // 7 days from now
+    expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 7))
 }));
 
 /**
  * Displays a help popup window
  */
 tvheadend.help = function(title, pagename) {
-       Ext.Ajax.request({
-               url : 'docs/' + pagename,
-               success : function(result, request) {
-
-                       var content = new Ext.Panel({
-                               autoScroll : true,
-                               border : false,
-                               layout : 'fit',
-                               html : result.responseText
-                       });
-
-                       var win = new Ext.Window({
-                               title : 'Help for ' + title,
-                               layout : 'fit',
-                               width : 900,
-                               height : 400,
-                               constrainHeader : true,
-                               items : [ content ]
-                       });
-                       win.show();
-
-               }
-       });
-}
+    Ext.Ajax.request({
+        url: 'docs/' + pagename,
+        success: function(result, request) {
+
+            var content = new Ext.Panel({
+                autoScroll: true,
+                border: false,
+                layout: 'fit',
+                html: result.responseText
+            });
+
+            var win = new Ext.Window({
+                title: 'Help for ' + title,
+                layout: 'fit',
+                width: 900,
+                height: 400,
+                constrainHeader: true,
+                items: [content]
+            });
+            win.show();
+
+        }
+    });
+};
 
 /*
  * General capabilities
  */
 Ext.Ajax.request({
-  url: 'capabilities',
-  success: function(d)
-  {
-    if (d && d.responseText)
-      tvheadend.capabilities = Ext.util.JSON.decode(d.responseText);
-    if (tvheadend.capabilities && tvheadend.accessupdate)
-      accessUpdate(tvheadend.accessUpdate);
-    
-  }
+    url: 'capabilities',
+    success: function(d)
+    {
+        if (d && d.responseText)
+            tvheadend.capabilities = Ext.util.JSON.decode(d.responseText);
+        if (tvheadend.capabilities && tvheadend.accessupdate)
+            accessUpdate(tvheadend.accessUpdate);
+
+    }
 });
 
 /**
  * Displays a mediaplayer using the html5 video element
  */
 tvheadend.VideoPlayer = function(url) {
-       
+
     var videoPlayer = new tv.ui.VideoPlayer({
-       params: {
-           resolution: 384
-       }
+        params: {
+            resolution: 384
+        }
     });
 
     var selectChannel = new Ext.form.ComboBox({
-       loadingText : 'Loading...',
-       width : 200,
-       displayField : 'val',
-       store : tvheadend.channels,
-       mode : 'local',
-       editable : true,
-       triggerAction : 'all',
-       emptyText : 'Select channel...'
+        loadingText: 'Loading...',
+        width: 200,
+        displayField: 'val',
+        store: tvheadend.channels,
+        mode: 'local',
+        editable: true,
+        triggerAction: 'all',
+        emptyText: 'Select channel...'
     });
-       
+
     selectChannel.on('select', function(c, r) {
-       videoPlayer.zapTo(r.id);
+        videoPlayer.zapTo(r.id);
     });
 
     var slider = new Ext.Slider({
-       width : 135,
-       height : 20,
-       value : 90,
-       increment : 1,
-       minValue : 0,
-       maxValue : 100
+        width: 135,
+        height: 20,
+        value: 90,
+        increment: 1,
+        minValue: 0,
+        maxValue: 100
     });
 
     var sliderLabel = new Ext.form.Label();
     sliderLabel.setText("90%");
     slider.addListener('change', function() {
-       videoPlayer.setVolume(slider.getValue());
-       sliderLabel.setText(videoPlayer.getVolume() + '%');
+        videoPlayer.setVolume(slider.getValue());
+        sliderLabel.setText(videoPlayer.getVolume() + '%');
     });
 
     var selectResolution = new Ext.form.ComboBox({
         width: 150,
-        displayField:'name',
+        displayField: 'name',
         valueField: 'res',
         value: 384,
         mode: 'local',
@@ -107,101 +107,101 @@ tvheadend.VideoPlayer = function(url) {
         triggerAction: 'all',
         emptyText: 'Select resolution...',
         store: new Ext.data.SimpleStore({
-            fields: ['res','name'],
+            fields: ['res', 'name'],
             id: 0,
             data: [
-                ['288','288p'],
-                ['384','384p'],
-                ['480','480p'],
-                ['576','576p']
+                ['288', '288p'],
+                ['384', '384p'],
+                ['480', '480p'],
+                ['576', '576p']
             ]
         })
     });
 
     selectResolution.on('select', function(c, r) {
-       videoPlayer.setResolution(r.data.res);
-       if(videoPlayer.isIdle())
-           return;
-       
-       var index = selectChannel.selectedIndex;
-       if(index < 0)
-           return;
-       
-       var ch = selectChannel.getStore().getAt(index);
-       videoPlayer.zapTo(ch.id);
+        videoPlayer.setResolution(r.data.res);
+        if (videoPlayer.isIdle())
+            return;
+
+        var index = selectChannel.selectedIndex;
+        if (index < 0)
+            return;
+
+        var ch = selectChannel.getStore().getAt(index);
+        videoPlayer.zapTo(ch.id);
     });
 
     var win = new Ext.Window({
-       title : 'Live TV Player',
-       layout : 'fit',
-       width : 682 + 14,
-       height : 384 + 56,
-       constrainHeader : true,
-       iconCls : 'eye',
-       resizable : true,
-       tbar : [
-           selectChannel,
-           '-',
-           {
-               iconCls : 'control_play',
-               tooltip : 'Play',
-               handler : function() {
-                   if(!videoPlayer.isIdle()) { //probobly paused
-                       videoPlayer.play();
-                       return;
-                   }
-
-                   var index = selectChannel.selectedIndex;
-                   if(index < 0)
-                       return;
-       
-                   var ch = selectChannel.getStore().getAt(index);
-                   videoPlayer.zapTo(ch.id);
-               }
-           },
-           {
-               iconCls : 'control_pause',
-               tooltip : 'Pause',
-               handler : function() {
-                   videoPlayer.pause();
-               }
-           },
-           {
-               iconCls : 'control_stop',
-               tooltip : 'Stop',
-               handler : function() {
-                   videoPlayer.stop();
-               }
-           },
-           '-',
-           {
-               iconCls : 'control_fullscreen',
-               tooltip : 'Fullscreen',
-               handler : function() {
-                   videoPlayer.fullscreen();
-               }
-           }, 
-           '-', 
-           selectResolution,
-           '-',
-           {
-               iconCls : 'control_volume',
-               tooltip : 'Volume',
-               disabled : true
-           }, ],
-       items : [videoPlayer]
+        title: 'Live TV Player',
+        layout: 'fit',
+        width: 682 + 14,
+        height: 384 + 56,
+        constrainHeader: true,
+        iconCls: 'eye',
+        resizable: true,
+        tbar: [
+            selectChannel,
+            '-',
+            {
+                iconCls: 'control_play',
+                tooltip: 'Play',
+                handler: function() {
+                    if (!videoPlayer.isIdle()) { //probobly paused
+                        videoPlayer.play();
+                        return;
+                    }
+
+                    var index = selectChannel.selectedIndex;
+                    if (index < 0)
+                        return;
+
+                    var ch = selectChannel.getStore().getAt(index);
+                    videoPlayer.zapTo(ch.id);
+                }
+            },
+            {
+                iconCls: 'control_pause',
+                tooltip: 'Pause',
+                handler: function() {
+                    videoPlayer.pause();
+                }
+            },
+            {
+                iconCls: 'control_stop',
+                tooltip: 'Stop',
+                handler: function() {
+                    videoPlayer.stop();
+                }
+            },
+            '-',
+            {
+                iconCls: 'control_fullscreen',
+                tooltip: 'Fullscreen',
+                handler: function() {
+                    videoPlayer.fullscreen();
+                }
+            },
+            '-',
+            selectResolution,
+            '-',
+            {
+                iconCls: 'control_volume',
+                tooltip: 'Volume',
+                disabled: true
+            }],
+        items: [videoPlayer]
     });
-    
+
     win.on('beforeShow', function() {
-       win.getTopToolbar().add(slider);
-       win.getTopToolbar().add(new Ext.Toolbar.Spacer());
-       win.getTopToolbar().add(new Ext.Toolbar.Spacer());
-       win.getTopToolbar().add(new Ext.Toolbar.Spacer());
-       win.getTopToolbar().add(sliderLabel);
+        win.getTopToolbar().add(slider);
+        win.getTopToolbar().add(new Ext.Toolbar.Spacer());
+        win.getTopToolbar().add(new Ext.Toolbar.Spacer());
+        win.getTopToolbar().add(new Ext.Toolbar.Spacer());
+        win.getTopToolbar().add(sliderLabel);
     });
-    
+
     win.on('close', function() {
-       videoPlayer.stop();
+        videoPlayer.stop();
     });
 
     win.show();
@@ -214,154 +214,154 @@ tvheadend.VideoPlayer = function(url) {
  * Obviosuly, access is verified in the server too.
  */
 function accessUpdate(o) {
-  tvheadend.accessUpdate = o;
-  if (!tvheadend.capabilities)
-    return;
-
-  if (o.dvr == true && tvheadend.dvrpanel == null) {
-    tvheadend.dvrpanel = new tvheadend.dvr;
-    tvheadend.rootTabPanel.add(tvheadend.dvrpanel);
-  }
-
-  if (o.admin == true && tvheadend.confpanel == null) {
-    var tabs1 = [
-      new tvheadend.miscconf,
-      new tvheadend.acleditor
-    ]
-    var tabs2;
-
-    /* DVB inputs */
-    tabs2 = [];
-    if (tvheadend.capabilities.indexOf('linuxdvb') != -1 ||
-        tvheadend.capabilities.indexOf('v4l')      != -1) {
-      tabs2.push(new tvheadend.tvadapters);
-    }
-/*
-    tabs2.push(new tvheadend.iptv);
-*/
-    tvheadend.conf_dvbin = new Ext.TabPanel({
-      activeTab: 0,
-      autoScroll: true,
-      title: 'DVB Inputs',
-      iconCls: 'hardware',
-      items : tabs2
-    });
-    tvheadend.networks(tvheadend.conf_dvbin);
-    tvheadend.muxes(tvheadend.conf_dvbin);
-    tvheadend.services(tvheadend.conf_dvbin);
-    tvheadend.mux_sched(tvheadend.conf_dvbin);
-    tabs1.push(tvheadend.conf_dvbin);
-
-    /* Channel / EPG */
-    tvheadend.conf_chepg = new Ext.TabPanel({
-      activeTab: 0,
-      autoScroll: true,
-      title : 'Channel / EPG',
-      iconCls : 'television',
-      items : [
-        new tvheadend.cteditor,
-        new tvheadend.epggrab
-      ]
-    });
-    tvheadend.channel_tab(tvheadend.conf_chepg);
-    tabs1.push(tvheadend.conf_chepg);
+    tvheadend.accessUpdate = o;
+    if (!tvheadend.capabilities)
+        return;
 
-    /* DVR / Timeshift */
-    tabs2 = [ new tvheadend.dvrsettings ];
-    if (tvheadend.capabilities.indexOf('timeshift') != -1) {
-      tabs2.push(new tvheadend.timeshift)
+    if (o.dvr == true && tvheadend.dvrpanel == null) {
+        tvheadend.dvrpanel = new tvheadend.dvr;
+        tvheadend.rootTabPanel.add(tvheadend.dvrpanel);
     }
-    tvheadend.conf_tsdvr = new Ext.TabPanel({
-      activeTab: 0,
-      autoScroll: true,
-      title: 'Recording',
-      iconCls: 'drive',
-      items : tabs2
-    });
-    tabs1.push(tvheadend.conf_tsdvr);
-
-    /* CSA */
-    if (tvheadend.capabilities.indexOf('cwc')      != -1) {
-      tvheadend.conf_csa = new Ext.TabPanel({
-        activeTab: 0,
-        autoScroll: true,
-        title: 'CSA',
-        iconCls: 'key',
-        items: [
-          new tvheadend.cwceditor,
-          new tvheadend.capmteditor
-        ]
-      });
-      tabs1.push(tvheadend.conf_csa);
+
+    if (o.admin == true && tvheadend.confpanel == null) {
+        var tabs1 = [
+            new tvheadend.miscconf,
+            new tvheadend.acleditor
+        ];
+        var tabs2;
+
+        /* DVB inputs */
+        tabs2 = [];
+        if (tvheadend.capabilities.indexOf('linuxdvb') !== -1 ||
+                tvheadend.capabilities.indexOf('v4l') !== -1) {
+            tabs2.push(new tvheadend.tvadapters);
+        }
+        /*
+         tabs2.push(new tvheadend.iptv);
+         */
+        tvheadend.conf_dvbin = new Ext.TabPanel({
+            activeTab: 0,
+            autoScroll: true,
+            title: 'DVB Inputs',
+            iconCls: 'hardware',
+            items: tabs2
+        });
+        tvheadend.networks(tvheadend.conf_dvbin);
+        tvheadend.muxes(tvheadend.conf_dvbin);
+        tvheadend.services(tvheadend.conf_dvbin);
+        tvheadend.mux_sched(tvheadend.conf_dvbin);
+        tabs1.push(tvheadend.conf_dvbin);
+
+        /* Channel / EPG */
+        tvheadend.conf_chepg = new Ext.TabPanel({
+            activeTab: 0,
+            autoScroll: true,
+            title: 'Channel / EPG',
+            iconCls: 'television',
+            items: [
+                new tvheadend.cteditor,
+                new tvheadend.epggrab
+            ]
+        });
+        tvheadend.channel_tab(tvheadend.conf_chepg);
+        tabs1.push(tvheadend.conf_chepg);
+
+        /* DVR / Timeshift */
+        tabs2 = [new tvheadend.dvrsettings];
+        if (tvheadend.capabilities.indexOf('timeshift') !== -1) {
+            tabs2.push(new tvheadend.timeshift);
+        }
+        tvheadend.conf_tsdvr = new Ext.TabPanel({
+            activeTab: 0,
+            autoScroll: true,
+            title: 'Recording',
+            iconCls: 'drive',
+            items: tabs2
+        });
+        tabs1.push(tvheadend.conf_tsdvr);
+
+        /* CSA */
+        if (tvheadend.capabilities.indexOf('cwc') !== -1) {
+            tvheadend.conf_csa = new Ext.TabPanel({
+                activeTab: 0,
+                autoScroll: true,
+                title: 'CSA',
+                iconCls: 'key',
+                items: [
+                    new tvheadend.cwceditor,
+                    new tvheadend.capmteditor
+                ]
+            });
+            tabs1.push(tvheadend.conf_csa);
+        }
+
+        /* Stream Config */
+        tvheadend.conf_stream = new Ext.TabPanel({
+            activeTab: 0,
+            autoScroll: true,
+            title: 'Stream',
+            iconCls: 'stream_config',
+            items: []
+        });
+        tvheadend.esfilter_tab(tvheadend.conf_stream);
+        tabs1.push(tvheadend.conf_stream);
+
+        /* Debug */
+        tabs1.push(new tvheadend.tvhlog);
+
+        tvheadend.confpanel = new Ext.TabPanel({
+            activeTab: 0,
+            autoScroll: true,
+            title: 'Configuration',
+            iconCls: 'wrench',
+            items: tabs1
+        });
+
+        tvheadend.rootTabPanel.add(tvheadend.confpanel);
+        tvheadend.confpanel.doLayout();
     }
 
-    /* Stream Config */
-    tvheadend.conf_stream = new Ext.TabPanel({
-      activeTab: 0,
-      autoScroll: true,
-      title: 'Stream',
-      iconCls: 'stream_config',
-      items: []
-    });
-    tvheadend.esfilter_tab(tvheadend.conf_stream);
-    tabs1.push(tvheadend.conf_stream);
-
-    /* Debug */
-    tabs1.push(new tvheadend.tvhlog);
-
-    tvheadend.confpanel = new Ext.TabPanel({
-      activeTab : 0,
-      autoScroll : true,
-      title : 'Configuration',
-      iconCls : 'wrench',
-      items : tabs1
-    });
+    if (o.admin == true && tvheadend.statuspanel == null) {
+        tvheadend.statuspanel = new tvheadend.status;
+        tvheadend.rootTabPanel.add(tvheadend.statuspanel);
+    }
 
-    tvheadend.rootTabPanel.add(tvheadend.confpanel);
-    tvheadend.confpanel.doLayout();
-  }
-
-  if (o.admin == true && tvheadend.statuspanel == null) {
-    tvheadend.statuspanel = new tvheadend.status;
-    tvheadend.rootTabPanel.add(tvheadend.statuspanel);
-  }
-
-  if (tvheadend.aboutPanel == null) {
-    tvheadend.aboutPanel = new Ext.Panel({
-      border : false,
-      layout : 'fit',
-      title : 'About',
-      iconCls : 'info',
-      autoLoad : 'about.html'
-    });
-    tvheadend.rootTabPanel.add(tvheadend.aboutPanel);
-  }
+    if (tvheadend.aboutPanel == null) {
+        tvheadend.aboutPanel = new Ext.Panel({
+            border: false,
+            layout: 'fit',
+            title: 'About',
+            iconCls: 'info',
+            autoLoad: 'about.html'
+        });
+        tvheadend.rootTabPanel.add(tvheadend.aboutPanel);
+    }
 
-  tvheadend.rootTabPanel.doLayout();
+    tvheadend.rootTabPanel.doLayout();
 }
 
 /**
  *
  */
 function setServerIpPort(o) {
-       tvheadend.serverIp = o.ip;
-       tvheadend.serverPort = o.port;
+    tvheadend.serverIp = o.ip;
+    tvheadend.serverPort = o.port;
 }
 
 function makeRTSPprefix() {
-       return 'rtsp://' + tvheadend.serverIp + ':' + tvheadend.serverPort + '/';
+    return 'rtsp://' + tvheadend.serverIp + ':' + tvheadend.serverPort + '/';
 }
 
 /**
  *
  */
 tvheadend.log = function(msg, style) {
-       s = style ? '<div style="' + style + '">' : '<div>'
+    s = style ? '<div style="' + style + '">' : '<div>';
 
-       sl = Ext.get('systemlog');
-       e = Ext.DomHelper.append(sl, s + '<pre>' + msg + '</pre></div>');
-       e.scrollIntoView('systemlog');
-}
+    sl = Ext.get('systemlog');
+    e = Ext.DomHelper.append(sl, s + '<pre>' + msg + '</pre></div>');
+    e.scrollIntoView('systemlog');
+};
 
 /**
  *
@@ -369,73 +369,72 @@ tvheadend.log = function(msg, style) {
 //create application
 tvheadend.app = function() {
 
-       // public space
-       return {
-
-               // public methods
-               init : function() {                     
-                       var header = new Ext.Panel({
-                               split: true,
-                               region: 'north',
-                               height : 45,
-                               boxMaxHeight : 45,
-                               boxMinHeight : 45,
-                               border: false,
-        hidden: true,
-                               html: '<div id="header"><h1>Tvheadend Web-Panel</h1></div>'
-                       });
-                       
-                       tvheadend.rootTabPanel = new Ext.TabPanel({
-                               region : 'center',
-                               activeTab : 0,
-                               items : [ new tvheadend.epg ]
-                       });
-
-                       var viewport = new Ext.Viewport({
-                               layout : 'border',
-                               items : [{
-                                       region : 'south',
-                                       contentEl : 'systemlog',
-                                       split : true,
-                                       autoScroll : true,
-                                       height : 150,
-                                       minSize : 100,
-                                       maxSize : 400,
-                                       collapsible : true,
-                                       collapsed : true,
-                                       title : 'System log',
-                                       margins : '0 0 0 0',
-                                       tools : [ {
-                                               id : 'gear',
-                                               qtip : 'Enable debug output',
-                                               handler : function(event, toolEl, panel) {
-                                                       Ext.Ajax.request({
-                                                               url : 'comet/debug',
-                                                               params : {
-                                                                       boxid : tvheadend.boxid
-                                                               }
-                                                       });
-                                               }
-                                       } ]
-                               }, tvheadend.rootTabPanel, header ]
-                       });
-
-                       tvheadend.comet.on('accessUpdate', accessUpdate);
-
-                       tvheadend.comet.on('setServerIpPort', setServerIpPort);
-
-                       tvheadend.comet.on('logmessage', function(m) {
-                               tvheadend.log(m.logtxt);
-                       });
-
-                       new tvheadend.cometPoller;
-
-                       Ext.QuickTips.init();
-
-                       // Load the chart library smoothie.js, as used by the bandwidth monitor.
-                       Ext.Loader.load('static/smoothie.js');
-               }
-
-       };
+    // public space
+    return {
+        // public methods
+        init: function() {
+            var header = new Ext.Panel({
+                split: true,
+                region: 'north',
+                height: 45,
+                boxMaxHeight: 45,
+                boxMinHeight: 45,
+                border: false,
+                hidden: true,
+                html: '<div id="header"><h1>Tvheadend Web-Panel</h1></div>'
+            });
+
+            tvheadend.rootTabPanel = new Ext.TabPanel({
+                region: 'center',
+                activeTab: 0,
+                items: [new tvheadend.epg]
+            });
+
+            var viewport = new Ext.Viewport({
+                layout: 'border',
+                items: [{
+                        region: 'south',
+                        contentEl: 'systemlog',
+                        split: true,
+                        autoScroll: true,
+                        height: 150,
+                        minSize: 100,
+                        maxSize: 400,
+                        collapsible: true,
+                        collapsed: true,
+                        title: 'System log',
+                        margins: '0 0 0 0',
+                        tools: [{
+                                id: 'gear',
+                                qtip: 'Enable debug output',
+                                handler: function(event, toolEl, panel) {
+                                    Ext.Ajax.request({
+                                        url: 'comet/debug',
+                                        params: {
+                                            boxid: tvheadend.boxid
+                                        }
+                                    });
+                                }
+                            }]
+                    }, tvheadend.rootTabPanel, header]
+            });
+
+            tvheadend.comet.on('accessUpdate', accessUpdate);
+
+            tvheadend.comet.on('setServerIpPort', setServerIpPort);
+
+            tvheadend.comet.on('logmessage', function(m) {
+                tvheadend.log(m.logtxt);
+            });
+
+            new tvheadend.cometPoller;
+
+            Ext.QuickTips.init();
+
+            // Load the chart library smoothie.js, as used by the bandwidth monitor.
+            Ext.Loader.load('static/smoothie.js');
+        }
+
+    };
 }(); // end of app
 
index 2d344ad5d7a0976e2a1e1babc79f0c1b507149eb..d5c6e92cc430397b0acbe80ad87001d795f73a80 100644 (file)
 tvheadend.tvhlog = function() {
-       /*
-        * Basic Config
-        */
-       var confreader = new Ext.data.JsonReader({
-               root : 'config'
-       }, [ 'tvhlog_path', 'tvhlog_dbg_syslog', 'tvhlog_trace_on',
-       'tvhlog_debug', 'tvhlog_trace' ]);
+    /*
+     * Basic Config
+     */
+    var confreader = new Ext.data.JsonReader({
+        root: 'config'
+    }, ['tvhlog_path', 'tvhlog_dbg_syslog', 'tvhlog_trace_on',
+        'tvhlog_debug', 'tvhlog_trace']);
 
-       /* ****************************************************************
-        * Form Fields
-        * ***************************************************************/
+    /* ****************************************************************
+     * Form Fields
+     * ***************************************************************/
 
-       var tvhlogLogPath = new Ext.form.TextField({
-               fieldLabel : 'Debug Log Path',
-               name : 'tvhlog_path',
-               allowBlank : true,
-               width: 400
-       });
+    var tvhlogLogPath = new Ext.form.TextField({
+        fieldLabel: 'Debug Log Path',
+        name: 'tvhlog_path',
+        allowBlank: true,
+        width: 400
+    });
 
-  var tvhlogToSyslog = new Ext.form.Checkbox({
-    name: 'tvhlog_dbg_syslog',
-    fieldLabel: 'Debug to syslog'
-  });
-  
-  var tvhlogTraceOn = new Ext.form.Checkbox({
-    name: 'tvhlog_trace_on',
-    fieldLabel: 'Debug trace (low-level stuff)'
-  });
+    var tvhlogToSyslog = new Ext.form.Checkbox({
+        name: 'tvhlog_dbg_syslog',
+        fieldLabel: 'Debug to syslog'
+    });
 
-       var tvhlogDebugSubsys = new Ext.form.TextField({
-               fieldLabel : 'Debug Subsystems',
-               name : 'tvhlog_debug',
-               allowBlank : true,
-               width: 400
-       });
+    var tvhlogTraceOn = new Ext.form.Checkbox({
+        name: 'tvhlog_trace_on',
+        fieldLabel: 'Debug trace (low-level stuff)'
+    });
 
-       var tvhlogTraceSubsys = new Ext.form.TextField({
-               fieldLabel : 'Trace Subsystems',
-               name : 'tvhlog_trace',
-               allowBlank : true,
-               width: 400
-       });
+    var tvhlogDebugSubsys = new Ext.form.TextField({
+        fieldLabel: 'Debug Subsystems',
+        name: 'tvhlog_debug',
+        allowBlank: true,
+        width: 400
+    });
 
-       /* ****************************************************************
-        * Form
-        * ***************************************************************/
+    var tvhlogTraceSubsys = new Ext.form.TextField({
+        fieldLabel: 'Trace Subsystems',
+        name: 'tvhlog_trace',
+        allowBlank: true,
+        width: 400
+    });
 
-       var saveButton = new Ext.Button({
-               text : "Save configuration",
-               tooltip : 'Save changes made to configuration below',
-               iconCls : 'save',
-               handler : saveChanges
-       });
+    /* ****************************************************************
+     * Form
+     * ***************************************************************/
 
-       var helpButton = new Ext.Button({
-               text : 'Help',
-               handler : function() {
-                       new tvheadend.help('Debug Configuration', 'config_tvhlog.html');
-               }
-       });
+    var saveButton = new Ext.Button({
+        text: "Save configuration",
+        tooltip: 'Save changes made to configuration below',
+        iconCls: 'save',
+        handler: saveChanges
+    });
 
-       var confpanel = new Ext.form.FormPanel({
-               title : 'Debugging',
-               iconCls : 'wrench',
-               border : false,
-               bodyStyle : 'padding:15px',
-               labelAlign : 'left',
-               labelWidth : 200,
-               waitMsgTarget : true,
-               reader : confreader,
-               layout : 'form',
-               defaultType : 'textfield',
-               autoHeight : true,
-               items : [ tvhlogLogPath, tvhlogToSyslog,
-              tvhlogTraceOn, tvhlogDebugSubsys, tvhlogTraceSubsys ],
-               tbar : [ saveButton, '->', helpButton ]
-       });
+    var helpButton = new Ext.Button({
+        text: 'Help',
+        handler: function() {
+            new tvheadend.help('Debug Configuration', 'config_tvhlog.html');
+        }
+    });
 
-       /* ****************************************************************
-        * Load/Save
-        * ***************************************************************/
+    var confpanel = new Ext.form.FormPanel({
+        title: 'Debugging',
+        iconCls: 'wrench',
+        border: false,
+        bodyStyle: 'padding:15px',
+        labelAlign: 'left',
+        labelWidth: 200,
+        waitMsgTarget: true,
+        reader: confreader,
+        layout: 'form',
+        defaultType: 'textfield',
+        autoHeight: true,
+        items: [tvhlogLogPath, tvhlogToSyslog,
+            tvhlogTraceOn, tvhlogDebugSubsys, tvhlogTraceSubsys],
+        tbar: [saveButton, '->', helpButton]
+    });
 
-       confpanel.on('render', function() {
-               confpanel.getForm().load({
-                       url : 'tvhlog',
-                       params : {
-                               op : 'loadSettings'
-                       },
-                       success : function(form, action) {
-                               confpanel.enable();
-                       }
-               });
-       });
+    /* ****************************************************************
+     * Load/Save
+     * ***************************************************************/
 
-       function saveChanges() {
-               confpanel.getForm().submit({
-                       url : 'tvhlog',
-                       params : {
-                               op : 'saveSettings'
-                       },
-                       waitMsg : 'Saving Data...',
-                       failure : function(form, action) {
-                               Ext.Msg.alert('Save failed', action.result.errormsg);
-                       }
-               });
-       }
+    confpanel.on('render', function() {
+        confpanel.getForm().load({
+            url: 'tvhlog',
+            params: {
+                op: 'loadSettings'
+            },
+            success: function(form, action) {
+                confpanel.enable();
+            }
+        });
+    });
 
-       return confpanel;
-}
+    function saveChanges() {
+        confpanel.getForm().submit({
+            url: 'tvhlog',
+            params: {
+                op: 'saveSettings'
+            },
+            waitMsg: 'Saving Data...',
+            failure: function(form, action) {
+                Ext.Msg.alert('Save failed', action.result.errormsg);
+            }
+        });
+    }
+
+    return confpanel;
+};
index d755d714fb839c63632e77712167bb7bf96eeb81..ab355e10f00e1b202113eb569ee230fcab03ce81 100644 (file)
  */
 tvheadend.v4l_adapter_general = function(adapterData) {
 
-       adapterId = adapterData.identifier;
-
-       /* Conf panel */
-
-       var confreader = new Ext.data.JsonReader({
-               root : 'v4ladapters'
-       }, [ 'name', 'logging' ]);
-
-       function saveConfForm() {
-               confform.getForm().submit({
-                       url : 'v4l/adapter/' + adapterId,
-                       params : {
-                               'op' : 'save'
-                       },
-                       waitMsg : 'Saving Data...'
-               });
-       }
-
-       var items = [ {
-               fieldLabel : 'Adapter name',
-               name : 'name',
-               width : 250
-       }, new Ext.form.Checkbox({
-               fieldLabel : 'Detailed logging',
-               name : 'logging'
-       }) ];
-
-       var confform = new Ext.FormPanel({
-               title : 'Adapter configuration',
-               columnWidth : .40,
-               frame : true,
-               border : true,
-               disabled : true,
-               style : 'margin:10px',
-               bodyStyle : 'padding:5px',
-               labelAlign : 'right',
-               labelWidth : 110,
-               waitMsgTarget : true,
-               reader : confreader,
-               defaultType : 'textfield',
-               items : items,
-               buttons : [ {
-                       text : 'Save',
-                       handler : saveConfForm
-               } ]
-       });
-
-       confform.getForm().load({
-               url : 'v4l/adapter/' + adapterId,
-               params : {
-                       'op' : 'load'
-               },
-               success : function(form, action) {
-                       confform.enable();
-               }
-       });
-
-       /**
-        * Information / capabilities panel 
-        */
-
-       var infoTemplate = new Ext.XTemplate(
-               '<h2 style="font-size: 150%">Hardware</h2>'
-                       + '<h3>Device path:</h3>{path}' + '<h3>Device name:</h3>{devicename}'
-                       + '<h2 style="font-size: 150%">Status</h2>'
-                       + '<h3>Currently tuned to:</h3>{currentMux}&nbsp');
-
-       var infoPanel = new Ext.Panel({
-               title : 'Information and capabilities',
-               columnWidth : .35,
-               frame : true,
-               border : true,
-               style : 'margin:10px',
-               bodyStyle : 'padding:5px',
-               html : infoTemplate.applyTemplate(adapterData)
-       });
-
-       /**
-        * Main adapter panel
-        */
-       var panel = new Ext.Panel({
-               title : 'General',
-               layout : 'column',
-               items : [ confform, infoPanel ]
-       });
-
-       /**
-        * Subscribe and react on updates for this adapter
-        */
-       tvheadend.tvAdapterStore.on('update', function(s, r, o) {
-               if (r.data.identifier != adapterId) return;
-               infoTemplate.overwrite(infoPanel.body, r.data);
-       });
-
-       return panel;
-}
+    adapterId = adapterData.identifier;
+
+    /* Conf panel */
+
+    var confreader = new Ext.data.JsonReader({
+        root: 'v4ladapters'
+    }, ['name', 'logging']);
+
+    function saveConfForm() {
+        confform.getForm().submit({
+            url: 'v4l/adapter/' + adapterId,
+            params: {
+                'op': 'save'
+            },
+            waitMsg: 'Saving Data...'
+        });
+    }
+
+    var items = [{
+            fieldLabel: 'Adapter name',
+            name: 'name',
+            width: 250
+        }, new Ext.form.Checkbox({
+            fieldLabel: 'Detailed logging',
+            name: 'logging'
+        })];
+
+    var confform = new Ext.FormPanel({
+        title: 'Adapter configuration',
+        columnWidth: .40,
+        frame: true,
+        border: true,
+        disabled: true,
+        style: 'margin:10px',
+        bodyStyle: 'padding:5px',
+        labelAlign: 'right',
+        labelWidth: 110,
+        waitMsgTarget: true,
+        reader: confreader,
+        defaultType: 'textfield',
+        items: items,
+        buttons: [{
+                text: 'Save',
+                handler: saveConfForm
+            }]
+    });
+
+    confform.getForm().load({
+        url: 'v4l/adapter/' + adapterId,
+        params: {
+            'op': 'load'
+        },
+        success: function(form, action) {
+            confform.enable();
+        }
+    });
+
+    /**
+     * Information / capabilities panel 
+     */
+
+    var infoTemplate = new Ext.XTemplate(
+            '<h2 style="font-size: 150%">Hardware</h2>'
+            + '<h3>Device path:</h3>{path}' + '<h3>Device name:</h3>{devicename}'
+            + '<h2 style="font-size: 150%">Status</h2>'
+            + '<h3>Currently tuned to:</h3>{currentMux}&nbsp');
+
+    var infoPanel = new Ext.Panel({
+        title: 'Information and capabilities',
+        columnWidth: .35,
+        frame: true,
+        border: true,
+        style: 'margin:10px',
+        bodyStyle: 'padding:5px',
+        html: infoTemplate.applyTemplate(adapterData)
+    });
+
+    /**
+     * Main adapter panel
+     */
+    var panel = new Ext.Panel({
+        title: 'General',
+        layout: 'column',
+        items: [confform, infoPanel]
+    });
+
+    /**
+     * Subscribe and react on updates for this adapter
+     */
+    tvheadend.tvAdapterStore.on('update', function(s, r, o) {
+        if (r.data.identifier !== adapterId)
+            return;
+        infoTemplate.overwrite(infoPanel.body, r.data);
+    });
+
+    return panel;
+};
 
 /**
  * V4L service grid
  */
 tvheadend.v4l_services = function(adapterId) {
 
-       var fm = Ext.form;
-
-       var enabledColumn = new Ext.grid.CheckColumn({
-               header : "Enabled",
-               dataIndex : 'enabled',
-               width : 45
-       });
-
-       var cm = new Ext.grid.ColumnModel({
-  defaultSortable: true,
-  columns : [
-    enabledColumn, {
-               header : "Channel name",
-               dataIndex : 'channelname',
-               width : 150,
-               renderer : function(value, metadata, record, row, col, store) {
-                       return value ? value : '<span class="tvh-grid-unset">Unmapped</span>';
-               },
-               editor : new fm.ComboBox({
-                       store : tvheadend.channels,
-                       allowBlank : true,
-                       typeAhead : true,
-                       minChars : 2,
-                       lazyRender : true,
-                       triggerAction : 'all',
-                       mode : 'local',
-                       displayField : 'name'
-               })
-       }, {
-               header : "Frequency",
-               dataIndex : 'frequency',
-               width : 60,
-               editor : new fm.NumberField({
-                       minValue : 10000,
-                       maxValue : 1000000000
-               })
-       } ]});
-
-       var rec = Ext.data.Record.create([ 'id', 'enabled', 'channelname',
-               'frequency' ]);
-
-       var store = new Ext.data.JsonStore({
-               root : 'entries',
-               fields : rec,
-               url : "v4l/services/" + adapterId,
-               autoLoad : true,
-               id : 'id',
-               baseParams : {
-                       op : "get"
-               },
-               listeners : {
-                       'update' : function(s, r, o) {
-                               d = s.getModifiedRecords().length == 0
-                               saveBtn.setDisabled(d);
-                               rejectBtn.setDisabled(d);
-                       }
-               }
-       });
-
-       function addRecord() {
-               Ext.Ajax.request({
-                       url : "v4l/services/" + adapterId,
-                       params : {
-                               op : "create"
-                       },
-                       failure : function(response, options) {
-                               Ext.MessageBox.alert('Server Error',
-                                       'Unable to generate new record');
-                       },
-                       success : function(response, options) {
-                               var responseData = Ext.util.JSON.decode(response.responseText);
-                               var p = new rec(responseData, responseData.id);
-                               grid.stopEditing();
-                               store.insert(0, p);
-                               grid.startEditing(0, 0);
-                       }
-               })
-       }
-       ;
-
-       function delSelected() {
-               var selectedKeys = grid.selModel.selections.keys;
-               if (selectedKeys.length > 0) {
-                       Ext.MessageBox.confirm('Message',
-                               'Do you really want to delete selection?', deleteRecord);
-               }
-               else {
-                       Ext.MessageBox.alert('Message',
-                               'Please select at least one item to delete');
-               }
-       }
-       ;
-
-       function deleteRecord(btn) {
-               if (btn == 'yes') {
-                       var selectedKeys = grid.selModel.selections.keys;
-
-                       Ext.Ajax.request({
-                               url : "v4l/services/" + adapterId,
-                               params : {
-                                       op : "delete",
-                                       entries : Ext.encode(selectedKeys)
-                               },
-                               failure : function(response, options) {
-                                       Ext.MessageBox.alert('Server Error', 'Unable to delete');
-                               },
-                               success : function(response, options) {
-                                       store.reload();
-                               }
-                       })
-               }
-       }
-
-       function saveChanges() {
-               var mr = store.getModifiedRecords();
-               var out = new Array();
-               for ( var x = 0; x < mr.length; x++) {
-                       v = mr[x].getChanges();
-                       out[x] = v;
-                       out[x].id = mr[x].id;
-               }
-
-               Ext.Ajax.request({
-                       url : "v4l/services/" + adapterId,
-                       params : {
-                               op : "update",
-                               entries : Ext.encode(out)
-                       },
-                       success : function(response, options) {
-                               store.commitChanges();
-                       },
-                       failure : function(response, options) {
-                               Ext.MessageBox.alert('Message', response.statusText);
-                       }
-               });
-       }
-
-       var delButton = new Ext.Toolbar.Button({
-               tooltip : 'Delete one or more selected rows',
-               iconCls : 'remove',
-               text : 'Delete selected services',
-               handler : delSelected,
-               disabled : true
-       });
-
-       var saveBtn = new Ext.Toolbar.Button({
-               tooltip : 'Save any changes made (Changed cells have red borders).',
-               iconCls : 'save',
-               text : "Save changes",
-               handler : saveChanges,
-               disabled : true
-       });
-
-       var rejectBtn = new Ext.Toolbar.Button({
-               tooltip : 'Revert any changes made (Changed cells have red borders).',
-               iconCls : 'undo',
-               text : "Revert changes",
-               handler : function() {
-                       store.rejectChanges();
-               },
-               disabled : true
-       });
-
-       var selModel = new Ext.grid.RowSelectionModel({
-               singleSelect : false
-       });
-
-       var grid = new Ext.grid.EditorGridPanel({
-               stripeRows : true,
-               title : 'Services',
-               plugins : [ enabledColumn ],
-               store : store,
-               clicksToEdit : 2,
-               cm : cm,
-               viewConfig : {
-                       forceFit : true
-               },
-               selModel : selModel,
-               tbar : [
-                       {
-                               tooltip : 'Create a new entry on the server. '
-                                       + 'The new entry is initially disabled so it must be enabled '
-                                       + 'before it start taking effect.',
-                               iconCls : 'add',
-                               text : 'Add service',
-                               handler : addRecord
-                       }, '-', delButton, '-', saveBtn, rejectBtn ]
-       });
-
-       store.on('update', function(s, r, o) {
-               d = s.getModifiedRecords().length == 0
-               saveBtn.setDisabled(d);
-               rejectBtn.setDisabled(d);
-       });
-
-       selModel.on('selectionchange', function(self) {
-               delButton.setDisabled(self.getCount() == 0);
-       });
-
-       return grid;
-}
+    var fm = Ext.form;
+
+    var enabledColumn = new Ext.grid.CheckColumn({
+        header: "Enabled",
+        dataIndex: 'enabled',
+        width: 45
+    });
+
+    var cm = new Ext.grid.ColumnModel({
+        defaultSortable: true,
+        columns: [
+            enabledColumn, {
+                header: "Channel name",
+                dataIndex: 'channelname',
+                width: 150,
+                renderer: function(value, metadata, record, row, col, store) {
+                    return value ? value : '<span class="tvh-grid-unset">Unmapped</span>';
+                },
+                editor: new fm.ComboBox({
+                    store: tvheadend.channels,
+                    allowBlank: true,
+                    typeAhead: true,
+                    minChars: 2,
+                    lazyRender: true,
+                    triggerAction: 'all',
+                    mode: 'local',
+                    displayField: 'name'
+                })
+            }, {
+                header: "Frequency",
+                dataIndex: 'frequency',
+                width: 60,
+                editor: new fm.NumberField({
+                    minValue: 10000,
+                    maxValue: 1000000000
+                })
+            }]});
+
+    var rec = Ext.data.Record.create(['id', 'enabled', 'channelname',
+        'frequency']);
+
+    var store = new Ext.data.JsonStore({
+        root: 'entries',
+        fields: rec,
+        url: "v4l/services/" + adapterId,
+        autoLoad: true,
+        id: 'id',
+        baseParams: {
+            op: "get"
+        },
+        listeners: {
+            'update': function(s, r, o) {
+                d = s.getModifiedRecords().length === 0;
+                saveBtn.setDisabled(d);
+                rejectBtn.setDisabled(d);
+            }
+        }
+    });
+
+    function addRecord() {
+        Ext.Ajax.request({
+            url: "v4l/services/" + adapterId,
+            params: {
+                op: "create"
+            },
+            failure: function(response, options) {
+                Ext.MessageBox.alert('Server Error',
+                        'Unable to generate new record');
+            },
+            success: function(response, options) {
+                var responseData = Ext.util.JSON.decode(response.responseText);
+                var p = new rec(responseData, responseData.id);
+                grid.stopEditing();
+                store.insert(0, p);
+                grid.startEditing(0, 0);
+            }
+        });
+    };
+
+    function delSelected() {
+        var selectedKeys = grid.selModel.selections.keys;
+        if (selectedKeys.length > 0) {
+            Ext.MessageBox.confirm('Message',
+                    'Do you really want to delete selection?', deleteRecord);
+        }
+        else {
+            Ext.MessageBox.alert('Message',
+                    'Please select at least one item to delete');
+        }
+    };
+
+    function deleteRecord(btn) {
+        if (btn === 'yes') {
+            var selectedKeys = grid.selModel.selections.keys;
+
+            Ext.Ajax.request({
+                url: "v4l/services/" + adapterId,
+                params: {
+                    op: "delete",
+                    entries: Ext.encode(selectedKeys)
+                },
+                failure: function(response, options) {
+                    Ext.MessageBox.alert('Server Error', 'Unable to delete');
+                },
+                success: function(response, options) {
+                    store.reload();
+                }
+            });
+        }
+    }
+
+    function saveChanges() {
+        var mr = store.getModifiedRecords();
+        var out = new Array();
+        for (var x = 0; x < mr.length; x++) {
+            v = mr[x].getChanges();
+            out[x] = v;
+            out[x].id = mr[x].id;
+        }
+
+        Ext.Ajax.request({
+            url: "v4l/services/" + adapterId,
+            params: {
+                op: "update",
+                entries: Ext.encode(out)
+            },
+            success: function(response, options) {
+                store.commitChanges();
+            },
+            failure: function(response, options) {
+                Ext.MessageBox.alert('Message', response.statusText);
+            }
+        });
+    }
+
+    var delButton = new Ext.Toolbar.Button({
+        tooltip: 'Delete one or more selected rows',
+        iconCls: 'remove',
+        text: 'Delete selected services',
+        handler: delSelected,
+        disabled: true
+    });
+
+    var saveBtn = new Ext.Toolbar.Button({
+        tooltip: 'Save any changes made (Changed cells have red borders).',
+        iconCls: 'save',
+        text: "Save changes",
+        handler: saveChanges,
+        disabled: true
+    });
+
+    var rejectBtn = new Ext.Toolbar.Button({
+        tooltip: 'Revert any changes made (Changed cells have red borders).',
+        iconCls: 'undo',
+        text: "Revert changes",
+        handler: function() {
+            store.rejectChanges();
+        },
+        disabled: true
+    });
+
+    var selModel = new Ext.grid.RowSelectionModel({
+        singleSelect: false
+    });
+
+    var grid = new Ext.grid.EditorGridPanel({
+        stripeRows: true,
+        title: 'Services',
+        plugins: [enabledColumn],
+        store: store,
+        clicksToEdit: 2,
+        cm: cm,
+        viewConfig: {
+            forceFit: true
+        },
+        selModel: selModel,
+        tbar: [
+            {
+                tooltip: 'Create a new entry on the server. '
+                        + 'The new entry is initially disabled so it must be enabled '
+                        + 'before it start taking effect.',
+                iconCls: 'add',
+                text: 'Add service',
+                handler: addRecord
+            }, '-', delButton, '-', saveBtn, rejectBtn]
+    });
+
+    store.on('update', function(s, r, o) {
+        d = s.getModifiedRecords().length === 0;
+        saveBtn.setDisabled(d);
+        rejectBtn.setDisabled(d);
+    });
+
+    selModel.on('selectionchange', function(self) {
+        delButton.setDisabled(self.getCount() === 0);
+    });
+
+    return grid;
+};
 
 /**
  *
  */
 tvheadend.v4l_adapter = function(data) {
-       var panel = new Ext.TabPanel({
-               border : false,
-               activeTab : 0,
-               autoScroll : true,
-               items : [ new tvheadend.v4l_adapter_general(data),
-                       new tvheadend.v4l_services(data.identifier) ]
-       });
-       return panel;
-}
+    var panel = new Ext.TabPanel({
+        border: false,
+        activeTab: 0,
+        autoScroll: true,
+        items: [new tvheadend.v4l_adapter_general(data),
+            new tvheadend.v4l_services(data.identifier)]
+    });
+    return panel;
+};