LiveSearchGridPanel.html 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-ux-LiveSearchGridPanel'>/**
  19. </span> * @class Ext.ux.LiveSearchGridPanel
  20. * @extends Ext.grid.Panel
  21. * &lt;p&gt;A GridPanel class with live search support.&lt;/p&gt;
  22. * @author Nicolas Ferrero
  23. */
  24. Ext.define('Ext.ux.LiveSearchGridPanel', {
  25. extend: 'Ext.grid.Panel',
  26. requires: [
  27. 'Ext.toolbar.TextItem',
  28. 'Ext.form.field.Checkbox',
  29. 'Ext.form.field.Text',
  30. 'Ext.ux.statusbar.StatusBar'
  31. ],
  32. <span id='Ext-ux-LiveSearchGridPanel-property-searchValue'> /**
  33. </span> * @private
  34. * search value initialization
  35. */
  36. searchValue: null,
  37. <span id='Ext-ux-LiveSearchGridPanel-property-indexes'> /**
  38. </span> * @private
  39. * The row indexes where matching strings are found. (used by previous and next buttons)
  40. */
  41. indexes: [],
  42. <span id='Ext-ux-LiveSearchGridPanel-property-currentIndex'> /**
  43. </span> * @private
  44. * The row index of the first search, it could change if next or previous buttons are used.
  45. */
  46. currentIndex: null,
  47. <span id='Ext-ux-LiveSearchGridPanel-property-searchRegExp'> /**
  48. </span> * @private
  49. * The generated regular expression used for searching.
  50. */
  51. searchRegExp: null,
  52. <span id='Ext-ux-LiveSearchGridPanel-property-caseSensitive'> /**
  53. </span> * @private
  54. * Case sensitive mode.
  55. */
  56. caseSensitive: false,
  57. <span id='Ext-ux-LiveSearchGridPanel-property-regExpMode'> /**
  58. </span> * @private
  59. * Regular expression mode.
  60. */
  61. regExpMode: false,
  62. <span id='Ext-ux-LiveSearchGridPanel-cfg-matchCls'> /**
  63. </span> * @cfg {String} matchCls
  64. * The matched string css classe.
  65. */
  66. matchCls: 'x-livesearch-match',
  67. defaultStatusText: 'Nothing Found',
  68. // Component initialization override: adds the top and bottom toolbars and setup headers renderer.
  69. initComponent: function() {
  70. var me = this;
  71. me.tbar = ['Search',{
  72. xtype: 'textfield',
  73. name: 'searchField',
  74. hideLabel: true,
  75. width: 200,
  76. listeners: {
  77. change: {
  78. fn: me.onTextFieldChange,
  79. scope: this,
  80. buffer: 100
  81. }
  82. }
  83. }, {
  84. xtype: 'button',
  85. text: '&amp;lt;',
  86. tooltip: 'Find Previous Row',
  87. handler: me.onPreviousClick,
  88. scope: me
  89. },{
  90. xtype: 'button',
  91. text: '&amp;gt;',
  92. tooltip: 'Find Next Row',
  93. handler: me.onNextClick,
  94. scope: me
  95. }, '-', {
  96. xtype: 'checkbox',
  97. hideLabel: true,
  98. margin: '0 0 0 4px',
  99. handler: me.regExpToggle,
  100. scope: me
  101. }, 'Regular expression', {
  102. xtype: 'checkbox',
  103. hideLabel: true,
  104. margin: '0 0 0 4px',
  105. handler: me.caseSensitiveToggle,
  106. scope: me
  107. }, 'Case sensitive'];
  108. me.bbar = Ext.create('Ext.ux.StatusBar', {
  109. defaultText: me.defaultStatusText,
  110. name: 'searchStatusBar'
  111. });
  112. me.callParent(arguments);
  113. },
  114. // afterRender override: it adds textfield and statusbar reference and start monitoring keydown events in textfield input
  115. afterRender: function() {
  116. var me = this;
  117. me.callParent(arguments);
  118. me.textField = me.down('textfield[name=searchField]');
  119. me.statusBar = me.down('statusbar[name=searchStatusBar]');
  120. },
  121. // detects html tag
  122. tagsRe: /&lt;[^&gt;]*&gt;/gm,
  123. // DEL ASCII code
  124. tagsProtect: '\x0f',
  125. // detects regexp reserved word
  126. regExpProtect: /\\|\/|\+|\\|\.|\[|\]|\{|\}|\?|\$|\*|\^|\|/gm,
  127. <span id='Ext-ux-LiveSearchGridPanel-method-getSearchValue'> /**
  128. </span> * In normal mode it returns the value with protected regexp characters.
  129. * In regular expression mode it returns the raw value except if the regexp is invalid.
  130. * @return {String} The value to process or null if the textfield value is blank or invalid.
  131. * @private
  132. */
  133. getSearchValue: function() {
  134. var me = this,
  135. value = me.textField.getValue();
  136. if (value === '') {
  137. return null;
  138. }
  139. if (!me.regExpMode) {
  140. value = value.replace(me.regExpProtect, function(m) {
  141. return '\\' + m;
  142. });
  143. } else {
  144. try {
  145. new RegExp(value);
  146. } catch (error) {
  147. me.statusBar.setStatus({
  148. text: error.message,
  149. iconCls: 'x-status-error'
  150. });
  151. return null;
  152. }
  153. // this is stupid
  154. if (value === '^' || value === '$') {
  155. return null;
  156. }
  157. }
  158. return value;
  159. },
  160. <span id='Ext-ux-LiveSearchGridPanel-method-onTextFieldChange'> /**
  161. </span> * Finds all strings that matches the searched value in each grid cells.
  162. * @private
  163. */
  164. onTextFieldChange: function() {
  165. var me = this,
  166. count = 0;
  167. me.view.refresh();
  168. // reset the statusbar
  169. me.statusBar.setStatus({
  170. text: me.defaultStatusText,
  171. iconCls: ''
  172. });
  173. me.searchValue = me.getSearchValue();
  174. me.indexes = [];
  175. me.currentIndex = null;
  176. if (me.searchValue !== null) {
  177. me.searchRegExp = new RegExp(me.searchValue, 'g' + (me.caseSensitive ? '' : 'i'));
  178. me.store.each(function(record, idx) {
  179. var td = Ext.fly(me.view.getNode(idx)).down('td'),
  180. cell, matches, cellHTML;
  181. while(td) {
  182. cell = td.down('.x-grid-cell-inner');
  183. matches = cell.dom.innerHTML.match(me.tagsRe);
  184. cellHTML = cell.dom.innerHTML.replace(me.tagsRe, me.tagsProtect);
  185. // populate indexes array, set currentIndex, and replace wrap matched string in a span
  186. cellHTML = cellHTML.replace(me.searchRegExp, function(m) {
  187. count += 1;
  188. if (Ext.Array.indexOf(me.indexes, idx) === -1) {
  189. me.indexes.push(idx);
  190. }
  191. if (me.currentIndex === null) {
  192. me.currentIndex = idx;
  193. }
  194. return '&lt;span class=&quot;' + me.matchCls + '&quot;&gt;' + m + '&lt;/span&gt;';
  195. });
  196. // restore protected tags
  197. Ext.each(matches, function(match) {
  198. cellHTML = cellHTML.replace(me.tagsProtect, match);
  199. });
  200. // update cell html
  201. cell.dom.innerHTML = cellHTML;
  202. td = td.next();
  203. }
  204. }, me);
  205. // results found
  206. if (me.currentIndex !== null) {
  207. me.getSelectionModel().select(me.currentIndex);
  208. me.statusBar.setStatus({
  209. text: count + ' matche(s) found.',
  210. iconCls: 'x-status-valid'
  211. });
  212. }
  213. }
  214. // no results found
  215. if (me.currentIndex === null) {
  216. me.getSelectionModel().deselectAll();
  217. }
  218. // force textfield focus
  219. me.textField.focus();
  220. },
  221. <span id='Ext-ux-LiveSearchGridPanel-method-onPreviousClick'> /**
  222. </span> * Selects the previous row containing a match.
  223. * @private
  224. */
  225. onPreviousClick: function() {
  226. var me = this,
  227. idx;
  228. if ((idx = Ext.Array.indexOf(me.indexes, me.currentIndex)) !== -1) {
  229. me.currentIndex = me.indexes[idx - 1] || me.indexes[me.indexes.length - 1];
  230. me.getSelectionModel().select(me.currentIndex);
  231. }
  232. },
  233. <span id='Ext-ux-LiveSearchGridPanel-method-onNextClick'> /**
  234. </span> * Selects the next row containing a match.
  235. * @private
  236. */
  237. onNextClick: function() {
  238. var me = this,
  239. idx;
  240. if ((idx = Ext.Array.indexOf(me.indexes, me.currentIndex)) !== -1) {
  241. me.currentIndex = me.indexes[idx + 1] || me.indexes[0];
  242. me.getSelectionModel().select(me.currentIndex);
  243. }
  244. },
  245. <span id='Ext-ux-LiveSearchGridPanel-method-caseSensitiveToggle'> /**
  246. </span> * Switch to case sensitive mode.
  247. * @private
  248. */
  249. caseSensitiveToggle: function(checkbox, checked) {
  250. this.caseSensitive = checked;
  251. this.onTextFieldChange();
  252. },
  253. <span id='Ext-ux-LiveSearchGridPanel-method-regExpToggle'> /**
  254. </span> * Switch to regular expression mode
  255. * @private
  256. */
  257. regExpToggle: function(checkbox, checked) {
  258. this.regExpMode = checked;
  259. this.onTextFieldChange();
  260. }
  261. });</pre>
  262. </body>
  263. </html>