OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * EventsView displays a filtered list of all events sharing a source, and | 6 * EventsView displays a filtered list of all events sharing a source, and |
7 * a details pane for the selected sources. | 7 * a details pane for the selected sources. |
8 * | 8 * |
9 * +----------------------++----------------+ | 9 * +----------------------++----------------+ |
10 * | filter box || | | 10 * | filter box || | |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
171 var nextSourceRow = this.sourceIdToRowMap_[nextSourceId]; | 171 var nextSourceRow = this.sourceIdToRowMap_[nextSourceId]; |
172 sourceRow.moveBefore(nextSourceRow); | 172 sourceRow.moveBefore(nextSourceRow); |
173 } | 173 } |
174 } | 174 } |
175 }, | 175 }, |
176 | 176 |
177 setFilter_: function(filterText) { | 177 setFilter_: function(filterText) { |
178 var lastComparisonFunction = this.comparisonFunction_; | 178 var lastComparisonFunction = this.comparisonFunction_; |
179 var lastDoSortBackwards = this.doSortBackwards_; | 179 var lastDoSortBackwards = this.doSortBackwards_; |
180 | 180 |
181 this.pickSortFunction_(filterText); | 181 var filterParser = new SourceFilterParser(filterText); |
| 182 this.currentFilter_ = filterParser.filter; |
| 183 |
| 184 this.pickSortFunction_(filterParser.sort); |
182 | 185 |
183 if (lastComparisonFunction != this.comparisonFunction_ || | 186 if (lastComparisonFunction != this.comparisonFunction_ || |
184 lastDoSortBackwards != this.doSortBackwards_) { | 187 lastDoSortBackwards != this.doSortBackwards_) { |
185 this.sort_(); | 188 this.sort_(); |
186 } | 189 } |
187 | 190 |
188 var oldFilter = this.currentFilter_; | |
189 this.currentFilter_ = createFilter_(filterText); | |
190 | |
191 // No need to filter again if filters match. | |
192 if (oldFilter && | |
193 JSON.stringify(oldFilter) == JSON.stringify(this.currentFilter_)) { | |
194 return; | |
195 } | |
196 | |
197 // Iterate through all of the rows and see if they match the filter. | 191 // Iterate through all of the rows and see if they match the filter. |
198 for (var id in this.sourceIdToRowMap_) { | 192 for (var id in this.sourceIdToRowMap_) { |
199 var entry = this.sourceIdToRowMap_[id]; | 193 var entry = this.sourceIdToRowMap_[id]; |
200 entry.setIsMatchedByFilter(entry.matchesFilter(this.currentFilter_)); | 194 entry.setIsMatchedByFilter(this.currentFilter_(entry.getSourceEntry())); |
201 } | 195 } |
202 }, | 196 }, |
203 | 197 |
204 /** | 198 /** |
205 * Parse any "sort:" directives, and update |comparisonFunction_| and | 199 * Given a "sort" object with "method" and "backwards" keys, looks up and |
206 * |doSortBackwards_| as needed. Note only the last valid sort directive | 200 * sets |comparisonFunction_| and |doSortBackwards_|. If the ID does not |
207 * is used. | 201 * correspond to a sort function, defaults to sorting by ID. |
208 */ | 202 */ |
209 pickSortFunction_: function(filterText) { | 203 pickSortFunction_: function(sort) { |
210 this.comparisonFunction_ = compareSourceId_; | 204 this.doSortBackwards_ = sort.backwards; |
211 this.doSortBackwards_ = false; | 205 this.comparisonFunction_ = COMPARISON_FUNCTION_TABLE[sort.method]; |
212 | 206 if (!this.comparisonFunction_) { |
213 var filterList = parseFilter_(filterText); | 207 this.doSortBackwards_ = false; |
214 for (var i = 0; i < filterList.length; ++i) { | 208 this.comparisonFunction_ = compareSourceId_; |
215 var sort = parseSortDirective_(filterList[i].parsed); | |
216 if (sort != null) { | |
217 this.comparisonFunction_ = sort.comparisonFunction; | |
218 this.doSortBackwards_ = sort.backwards; | |
219 } | |
220 } | 209 } |
221 }, | 210 }, |
222 | 211 |
223 /** | 212 /** |
224 * Repositions |sourceRow|'s in the table using an insertion sort. | 213 * Repositions |sourceRow|'s in the table using an insertion sort. |
225 * Significantly faster than sorting the entire table again, when only | 214 * Significantly faster than sorting the entire table again, when only |
226 * one entry has changed. | 215 * one entry has changed. |
227 */ | 216 */ |
228 insertionSort_: function(sourceRow) { | 217 insertionSort_: function(sourceRow) { |
229 // SourceRow that should be after |sourceRow|, if it needs | 218 // SourceRow that should be after |sourceRow|, if it needs |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
398 scrollToSourceId: function(sourceId) { | 387 scrollToSourceId: function(sourceId) { |
399 this.detailsView_.scrollToSourceId(sourceId); | 388 this.detailsView_.scrollToSourceId(sourceId); |
400 }, | 389 }, |
401 | 390 |
402 /** | 391 /** |
403 * If already using the specified sort method, flips direction. Otherwise, | 392 * If already using the specified sort method, flips direction. Otherwise, |
404 * removes pre-existing sort parameter before adding the new one. | 393 * removes pre-existing sort parameter before adding the new one. |
405 */ | 394 */ |
406 toggleSortMethod_: function(sortMethod) { | 395 toggleSortMethod_: function(sortMethod) { |
407 // Get old filter text and remove old sort directives, if any. | 396 // Get old filter text and remove old sort directives, if any. |
408 var filterList = parseFilter_(this.getFilterText_()); | 397 var filterParser = new SourceFilterParser(this.getFilterText_()); |
409 var filterText = ''; | 398 var filterText = filterParser.filterTextWithoutSort; |
410 for (var i = 0; i < filterList.length; ++i) { | 399 |
411 if (parseSortDirective_(filterList[i].parsed) == null) | 400 filterText = 'sort:' + sortMethod + ' ' + filterText; |
412 filterText += filterList[i].original; | |
413 } | |
414 | 401 |
415 // If already using specified sortMethod, sort backwards. | 402 // If already using specified sortMethod, sort backwards. |
416 if (!this.doSortBackwards_ && | 403 if (!this.doSortBackwards_ && |
417 COMPARISON_FUNCTION_TABLE[sortMethod] == this.comparisonFunction_) { | 404 COMPARISON_FUNCTION_TABLE[sortMethod] == this.comparisonFunction_) { |
418 sortMethod = '-' + sortMethod; | 405 filterText = '-' + filterText; |
419 } | 406 } |
420 | 407 |
421 filterText = 'sort:' + sortMethod + ' ' + filterText; | |
422 this.setFilterText_(filterText.trim()); | 408 this.setFilterText_(filterText.trim()); |
423 }, | 409 }, |
424 | 410 |
425 sortById_: function(event) { | 411 sortById_: function(event) { |
426 this.toggleSortMethod_('id'); | 412 this.toggleSortMethod_('id'); |
427 }, | 413 }, |
428 | 414 |
429 sortBySourceType_: function(event) { | 415 sortBySourceType_: function(event) { |
430 this.toggleSortMethod_('source'); | 416 this.toggleSortMethod_('source'); |
431 }, | 417 }, |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
573 | 559 |
574 function compareSourceType_(source1, source2) { | 560 function compareSourceType_(source1, source2) { |
575 var source1Text = source1.getSourceTypeString(); | 561 var source1Text = source1.getSourceTypeString(); |
576 var source2Text = source2.getSourceTypeString(); | 562 var source2Text = source2.getSourceTypeString(); |
577 var compareResult = source1Text.localeCompare(source2Text); | 563 var compareResult = source1Text.localeCompare(source2Text); |
578 if (compareResult != 0) | 564 if (compareResult != 0) |
579 return compareResult; | 565 return compareResult; |
580 return compareSourceId_(source1, source2); | 566 return compareSourceId_(source1, source2); |
581 } | 567 } |
582 | 568 |
583 /** | |
584 * Parses a single "sort:" directive, and returns a dictionary containing | |
585 * the sort function and direction. Returns null on failure, including | |
586 * the case when no such sort function exists. | |
587 */ | |
588 | |
589 function parseSortDirective_(filterElement) { | |
590 var match = /^sort:(-?)(.*)$/.exec(filterElement); | |
591 if (!match || !COMPARISON_FUNCTION_TABLE[match[2]]) | |
592 return null; | |
593 return { | |
594 comparisonFunction: COMPARISON_FUNCTION_TABLE[match[2]], | |
595 backwards: (match[1] == '-'), | |
596 }; | |
597 } | |
598 | |
599 /** | |
600 * Parses an "is:" directive, and updates |filter| accordingly. | |
601 * | |
602 * Returns true on success, and false if |filterElement| is not an "is:" | |
603 * directive. | |
604 */ | |
605 function parseRestrictDirective_(filterElement, filter) { | |
606 var match = /^is:(-?)(.*)$/.exec(filterElement); | |
607 if (!match) | |
608 return false; | |
609 if (match[2] == 'active') { | |
610 if (match[1] == '-') { | |
611 filter.isInactive = true; | |
612 } else { | |
613 filter.isActive = true; | |
614 } | |
615 return true; | |
616 } | |
617 if (match[2] == 'error') { | |
618 if (match[1] == '-') { | |
619 filter.isNotError = true; | |
620 } else { | |
621 filter.isError = true; | |
622 } | |
623 return true; | |
624 } | |
625 return false; | |
626 } | |
627 | |
628 /** | |
629 * Parses all directives that take arbitrary strings as input, | |
630 * and updates |filter| accordingly. Directives of these types | |
631 * are stored as lists. | |
632 * | |
633 * Returns true on success, and false if |filterElement| is not a | |
634 * recognized directive. | |
635 */ | |
636 function parseStringDirective_(filterElement, filter) { | |
637 var directives = ['type', 'id']; | |
638 for (var i = 0; i < directives.length; ++i) { | |
639 var directive = directives[i]; | |
640 var match = RegExp('^' + directive + ':(.*)$').exec(filterElement); | |
641 if (!match) | |
642 continue; | |
643 | |
644 // Split parameters around commas and remove empty elements. | |
645 var parameters = match[1].split(','); | |
646 parameters = parameters.filter(function(string) { | |
647 return string.length > 0; | |
648 }); | |
649 | |
650 // If there's already a matching filter, take the intersection. | |
651 // This behavior primarily exists for tests. It is not correct | |
652 // when one of the 'type' filters is a partial match. | |
653 if (filter[directive]) { | |
654 parameters = parameters.filter(function(string) { | |
655 return filter[directive].indexOf(string) != -1; | |
656 }); | |
657 } | |
658 | |
659 filter[directive] = parameters; | |
660 return true; | |
661 } | |
662 return false; | |
663 } | |
664 | |
665 /** | |
666 * Takes in the text of a filter and returns a list of {parsed, original} | |
667 * pairs that correspond to substrings of the filter before and after | |
668 * filtering. This function is used both to parse filters and to remove | |
669 * the sort rule from a filter. Extra whitespace other than a single | |
670 * character after each element is ignored. Parsed strings are all | |
671 * lowercase. | |
672 */ | |
673 function parseFilter_(filterText) { | |
674 filterText = filterText.toLowerCase(); | |
675 | |
676 // Assemble a list of quoted and unquoted strings in the filter. | |
677 var filterList = []; | |
678 var position = 0; | |
679 while (position < filterText.length) { | |
680 var inQuote = false; | |
681 var filterElement = ''; | |
682 var startPosition = position; | |
683 while (position < filterText.length) { | |
684 var nextCharacter = filterText[position]; | |
685 ++position; | |
686 if (nextCharacter == '\\' && | |
687 position < filterText.length) { | |
688 // If there's a backslash, skip the backslash and add the next | |
689 // character to the element. | |
690 filterElement += filterText[position]; | |
691 ++position; | |
692 continue; | |
693 } else if (nextCharacter == '"') { | |
694 // If there's an unescaped quote character, toggle |inQuote| without | |
695 // modifying the element. | |
696 inQuote = !inQuote; | |
697 } else if (!inQuote && /\s/.test(nextCharacter)) { | |
698 // If not in a quote and have a whitespace character, that's the | |
699 // end of the element. | |
700 break; | |
701 } else { | |
702 // Otherwise, add the next character to the element. | |
703 filterElement += nextCharacter; | |
704 } | |
705 } | |
706 | |
707 if (filterElement.length > 0) { | |
708 var filter = { | |
709 parsed: filterElement, | |
710 original: filterText.substring(startPosition, position), | |
711 }; | |
712 filterList.push(filter); | |
713 } | |
714 } | |
715 return filterList; | |
716 } | |
717 | |
718 /** | |
719 * Converts |filterText| into an object representing the filter. | |
720 */ | |
721 function createFilter_(filterText) { | |
722 var filter = {}; | |
723 var filterList = parseFilter_(filterText); | |
724 | |
725 for (var i = 0; i < filterList.length; ++i) { | |
726 if (parseSortDirective_(filterList[i].parsed) || | |
727 parseRestrictDirective_(filterList[i].parsed, filter) || | |
728 parseStringDirective_(filterList[i].parsed, filter)) { | |
729 continue; | |
730 } | |
731 if (filter.textFilters == undefined) | |
732 filter.textFilters = []; | |
733 filter.textFilters.push(filterList[i].parsed); | |
734 } | |
735 return filter; | |
736 } | |
737 | |
738 return EventsView; | 569 return EventsView; |
739 })(); | 570 })(); |
OLD | NEW |