浏览代码

Merge pull request #12 from amnuts/optimise-filter

Fixes #11
Andrew Collington 9 年之前
父节点
当前提交
d9fa5c2533
共有 3 个文件被更改,包括 62 次插入34 次删除
  1. 4 1
      README.md
  2. 56 31
      index.php
  3. 2 2
      src/status.jsx

+ 4 - 1
README.md

@@ -6,6 +6,8 @@ A clean and responsive interface for Zend OPcache information, showing statistic
 
 ## What's new
 
+Version 2.2.2 brings in optimisations for the file listing when filtering
+
 Version 2.2.1 has the gauges now updating with the real-time pulse and a couple rounding issues fixed
 
 Version 2.2.0 provides the ability to turn on/off the file list (default is on)
@@ -37,7 +39,8 @@ $options = [
     'refresh_time'     => 5,     // how often the data will refresh, in seconds
     'size_precision'   => 2,     // Digits after decimal point
     'size_space'       => false, // have '1MB' or '1 MB' when showing sizes
-    'charts'           => true   // show gauge chart or just big numbers
+    'charts'           => true,  // show gauge chart or just big numbers
+    'debounce_rate'    => 250    // milliseconds after key press to send keyup event when filtering
 ];
 ```
 

+ 56 - 31
index.php

@@ -6,7 +6,7 @@
  * A simple but effective single-file GUI for the OPcache PHP extension.
  *
  * @author Andrew Collington, andy@amnuts.com
- * @version 2.2.1
+ * @version 2.2.2
  * @link https://github.com/amnuts/opcache-gui
  * @license MIT, http://acollington.mit-license.org/
  */
@@ -24,7 +24,8 @@ $options = [
     'refresh_time'     => 5,     // how often the data will refresh, in seconds
     'size_precision'   => 2,     // Digits after decimal point
     'size_space'       => false, // have '1MB' or '1 MB' when showing sizes
-    'charts'           => true   // show gauge chart or just big numbers
+    'charts'           => true,  // show gauge chart or just big numbers
+    'debounce_rate'    => 250    // milliseconds after key press to send keyup event when filtering
 ];
 
 /*
@@ -47,7 +48,8 @@ class OpCacheService
         'refresh_time'     => 5,
         'size_precision'   => 2,
         'size_space'       => false,
-        'charts'           => true
+        'charts'           => true,
+        'debounce_rate'    => 250
     ];
 
     private function __construct($options = [])
@@ -248,6 +250,8 @@ $opcache = OpCacheService::init($options);
         table td { padding: 4px 6px; line-height: 1.4em; vertical-align: top; border-color: #fff; }
         table tr:nth-child(odd) { background-color: #EFFEFF; }
         table tr:nth-child(even) { background-color: #E0ECEF; }
+        #filelist table tr { background-color: #EFFEFF; }
+        #filelist table tr.alternate { background-color: #E0ECEF; }
         td.pathname { width: 70%; }
         footer { border-top: 1px solid #ccc; padding: 1em 2em; }
         footer a { padding: 2em; text-decoration: none; opacity: 0.7; }
@@ -371,6 +375,36 @@ $opcache = OpCacheService::init($options);
     var canInvalidate = <?php echo json_encode($opcache->canInvalidate()); ?>;
     var useCharts = <?php echo json_encode($opcache->getOption('charts')); ?>;
     var allowFiles = <?php echo json_encode($opcache->getOption('allow_filelist')); ?>;
+    var debounce = function(func, wait, immediate) {
+        var timeout;
+        wait = wait || 250;
+        return function() {
+            var context = this, args = arguments;
+            var later = function() {
+                timeout = null;
+                if (!immediate) {
+                    func.apply(context, args);
+                }
+            };
+            var callNow = immediate && !timeout;
+            clearTimeout(timeout);
+            timeout = setTimeout(later, wait);
+            if (callNow) {
+                func.apply(context, args);
+            }
+        };
+    };
+    function keyUp(event){
+        var compare = $('#frmFilter').val().toLowerCase();
+        $('#filelist').find('table tbody tr').each(function(index){
+            if ($(this).data('path').indexOf(compare) == -1) {
+                $(this).addClass('hide');
+            } else {
+                $(this).removeClass('hide');
+            }
+        });
+        $('#filelist table tbody').trigger('paint');
+    };
 
     <?php if ($opcache->getOption('charts')): ?>
     var Gauge = function(el, colour) {
@@ -449,7 +483,7 @@ $opcache = OpCacheService::init($options);
                         count_formatted : opstate.overview.readable.num_cached_scripts,
                         count : opstate.overview.num_cached_scripts
                     });
-                    $('#frmFilter').trigger('keyup');
+                    keyUp();
                 }
             });
         }
@@ -472,24 +506,15 @@ $opcache = OpCacheService::init($options);
             return false;
         });
         $(document).on('paint', '#filelist table tbody', function(event, params) {
-            var trs = $('tr', $(this)).not('.hide');
-            trs.filter(':odd').css({backgroundColor:'#E0ECEF'})
-               .end().filter(':even').css({backgroundColor:'#EFFEFF'});
-            filesObj.setState({showing: trs.length});
-        });
-        $('#frmFilter').bind('keyup', function(event){
-            $('span.pathname').each(function(index){
-                if ($(this).text().toLowerCase().indexOf($('#frmFilter').val().toLowerCase()) == -1) {
-                    $(this).closest('tr').addClass('hide');
-                } else {
-                    $(this).closest('tr').removeClass('hide');
-                }
-            });
-            $('#filelist table tbody').trigger('paint');
+            var trs = $('#filelist').find('tbody tr');
+            trs.removeClass('alternate');
+            trs.filter(':not(.hide):odd').addClass('alternate');
+            filesObj.setState({showing: trs.filter(':not(.hide)').length});
         });
+        $('#frmFilter').bind('keyup', debounce(keyUp, <?php echo $opcache->getOption('debounce_rate'); ?>));
     });
 
-    var MemoryUsage = React.createClass({displayName: 'MemoryUsage',
+    var MemoryUsage = React.createClass({displayName: "MemoryUsage",
         getInitialState: function() {
             return {
                 memoryUsageGauge : null
@@ -508,13 +533,13 @@ $opcache = OpCacheService::init($options);
         },
         render: function() {
             if (this.props.chart == true) {
-                return(React.createElement("canvas", {id: "memoryUsageCanvas", width: "250", height: "250", 'data-value': this.props.value}));
+                return(React.createElement("canvas", {id: "memoryUsageCanvas", width: "250", height: "250", "data-value": this.props.value}));
             }
             return(React.createElement("p", null, React.createElement("span", {className: "large"}, this.props.value), React.createElement("span", null, "%")));
         }
     });
 
-    var HitRate = React.createClass({displayName: 'HitRate',
+    var HitRate = React.createClass({displayName: "HitRate",
         getInitialState: function() {
             return {
                 hitRateGauge : null
@@ -533,13 +558,13 @@ $opcache = OpCacheService::init($options);
         },
         render: function() {
             if (this.props.chart == true) {
-                return(React.createElement("canvas", {id: "hitRateCanvas", width: "250", height: "250", 'data-value': this.props.value}));
+                return(React.createElement("canvas", {id: "hitRateCanvas", width: "250", height: "250", "data-value": this.props.value}));
             }
             return(React.createElement("p", null, React.createElement("span", {className: "large"}, this.props.value), React.createElement("span", null, "%")));
         }
     });
 
-    var OverviewCounts = React.createClass({displayName: 'OverviewCounts',
+    var OverviewCounts = React.createClass({displayName: "OverviewCounts",
         getInitialState: function() {
             return {
                 data  : opstate.overview,
@@ -574,7 +599,7 @@ $opcache = OpCacheService::init($options);
         }
     });
 
-    var GeneralInfo = React.createClass({displayName: 'GeneralInfo',
+    var GeneralInfo = React.createClass({displayName: "GeneralInfo",
         getInitialState: function() {
             return {
                 version : opstate.version,
@@ -601,7 +626,7 @@ $opcache = OpCacheService::init($options);
         }
     });
 
-    var Directives = React.createClass({displayName: 'Directives',
+    var Directives = React.createClass({displayName: "Directives",
         getInitialState: function() {
             return { data : opstate.directives };
         },
@@ -638,7 +663,7 @@ $opcache = OpCacheService::init($options);
         }
     });
 
-    var Files = React.createClass({displayName: 'Files',
+    var Files = React.createClass({displayName: "Files",
         getInitialState: function() {
             return {
                 data : opstate.files,
@@ -658,17 +683,17 @@ $opcache = OpCacheService::init($options);
         },
         render: function() {
             if (this.state.allowFiles) {
-                var fileNodes = this.state.data.map(function(file) {
+                var fileNodes = this.state.data.map(function(file, i) {
                     var invalidate, invalidated;
                     if (file.timestamp == 0) {
                         invalidated = React.createElement("span", null, React.createElement("i", {className: "invalid metainfo"}, " - has been invalidated"));
                     }
                     if (canInvalidate) {
                         invalidate = React.createElement("span", null, ", ", React.createElement("a", {className: "metainfo", href: '?invalidate='
-                        + file.full_path, 'data-file': file.full_path, onClick: this.handleInvalidate}, "force file invalidation"));
+                        + file.full_path, "data-file": file.full_path, onClick: this.handleInvalidate}, "force file invalidation"));
                     }
                     return (
-                        React.createElement("tr", {key: file.full_path},
+                        React.createElement("tr", {key: file.full_path, "data-path": file.full_path.toLowerCase(), className: i%2?'alternate':''},
                             React.createElement("td", null,
                                 React.createElement("div", null,
                                     React.createElement("span", {className: "pathname"}, file.full_path), React.createElement("br", null),
@@ -699,7 +724,7 @@ $opcache = OpCacheService::init($options);
         }
     });
 
-    var FilesMeta = React.createClass({displayName: 'FilesMeta',
+    var FilesMeta = React.createClass({displayName: "FilesMeta",
         render: function() {
             return (
                 React.createElement("span", {className: "metainfo"},
@@ -711,7 +736,7 @@ $opcache = OpCacheService::init($options);
         }
     });
 
-    var FilesListed = React.createClass({displayName: 'FilesListed',
+    var FilesListed = React.createClass({displayName: "FilesListed",
         getInitialState: function() {
             return {
                 formatted : opstate.overview.readable.num_cached_scripts,

+ 2 - 2
src/status.jsx

@@ -167,7 +167,7 @@ var Files = React.createClass({
     },
     render: function() {
         if (this.state.allowFiles) {
-            var fileNodes = this.state.data.map(function(file) {
+            var fileNodes = this.state.data.map(function(file, i) {
                 var invalidate, invalidated;
                 if (file.timestamp == 0) {
                     invalidated = <span><i className="invalid metainfo"> - has been invalidated</i></span>;
@@ -177,7 +177,7 @@ var Files = React.createClass({
                         + file.full_path} data-file={file.full_path} onClick={this.handleInvalidate}>force file invalidation</a></span>;
                 }
                 return (
-                    <tr key={file.full_path}>
+                    <tr key={file.full_path} data-path={file.full_path.toLowerCase()} className={i%2?'alternate':''}>
                         <td>
                             <div>
                                 <span className="pathname">{file.full_path}</span><br/>