blob: 7edb84b94ef331dcfb95db2182c159b8814ae4e2 [file] [log] [blame]
Chris Craikb122baf2015-03-05 13:58:42 -08001<!DOCTYPE html>
2<!--
3Copyright 2015 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/base/task.html">
9<link rel="import" href="/core/filter.html">
10<link rel="import" href="/core/selection.html">
11<link rel="import" href="/core/scripting_object.html">
12<link rel="import" href="/extras/tquery/context.html">
Chris Craikbeca7ae2015-04-07 13:29:55 -070013<link rel="import" href="/extras/tquery/filter_all_of.html">
14<link rel="import" href="/extras/tquery/filter_any_of.html">
Chris Craikb122baf2015-03-05 13:58:42 -080015<link rel="import" href="/extras/tquery/filter_has_ancestor.html">
Chris Craik19832152015-04-16 15:43:38 -070016<link rel="import" href="/extras/tquery/filter_has_duration.html">
Chris Craikb122baf2015-03-05 13:58:42 -080017<link rel="import" href="/extras/tquery/filter_has_title.html">
Chris Craikbeca7ae2015-04-07 13:29:55 -070018<link rel="import" href="/extras/tquery/filter_is_top_level.html">
Chris Craikb122baf2015-03-05 13:58:42 -080019
Chris Craik44c28202015-05-12 17:25:16 -070020<script>
21'use strict';
Chris Craikb122baf2015-03-05 13:58:42 -080022
Chris Craik44c28202015-05-12 17:25:16 -070023tv.exportTo('tv.e.tquery', function() {
24 function TQuery(selectionController) {
25 tv.c.ScriptingObject.call(this);
26
27 this.selectionController_ = selectionController;
28 this.parent_ = undefined;
29 this.filterExpression_ = undefined;
30 // Memoized filtering result.
31 this.selection_ = undefined;
32 };
33
34 TQuery.prototype = {
35 __proto__: tv.c.ScriptingObject.prototype,
36
37 onModelChanged: function() {
Chris Craikb122baf2015-03-05 13:58:42 -080038 this.selection_ = undefined;
39 },
40
Chris Craik44c28202015-05-12 17:25:16 -070041 get selectionController() {
42 return this.selectionController_;
Chris Craikb122baf2015-03-05 13:58:42 -080043 },
44
45 // Append a new filter expression to this query and return a query node
46 // that represents the result.
47 filter: function(filterExpression) {
Chris Craik44c28202015-05-12 17:25:16 -070048 var result = new TQuery(this.selectionController_);
Chris Craikb122baf2015-03-05 13:58:42 -080049 result.parent_ = this;
Chris Craikb122baf2015-03-05 13:58:42 -080050 result.filterExpression_ =
Chris Craik44c28202015-05-12 17:25:16 -070051 tv.e.tquery.Filter.normalizeFilterExpression(filterExpression);
Chris Craikb122baf2015-03-05 13:58:42 -080052 return result;
53 },
54
55 // Creates a graph of {Task} objects which will compute the selections for
56 // this filter object and all of its parents. The return value is an object
57 // with the following fields:
58 // - rootTask: {Task} which should be executed to kick off processing for
59 // the entire task graph.
60 // - lastTask: The final {Task} of the graph. Can be used by the caller to
61 // enqueue additional processing at the end.
62 // - lastNode: The last filter object in the task. It's selection property
63 // will contain the filtering result once |finalTask|
64 // completes.
65 createFilterTaskGraph_: function() {
66 // List of nodes in order from the current one to the root.
67 var nodes = [];
68 var node = this;
69 while (node !== undefined) {
70 nodes.push(node);
71 node = node.parent_;
72 }
73
74 var rootTask = new tv.b.Task();
75 var lastTask = rootTask;
76 for (var i = nodes.length - 1; i >= 0; i--) {
77 var node = nodes[i];
78 // Reuse any memoized result.
79 if (node.selection_ !== undefined)
80 continue;
81 node.selection_ = new tv.c.Selection();
82 if (node.parent_ === undefined) {
83 // If this is the root, start by collecting all objects from the
Chris Craik44c28202015-05-12 17:25:16 -070084 // selection controller.
Chris Craikb122baf2015-03-05 13:58:42 -080085 lastTask = lastTask.after(
86 this.selectEverythingAsTask_(node.selection_));
87 } else {
88 // Otherwise execute the filter expression for this node and fill
89 // in its selection.
90 var prevNode = nodes[i + 1];
91 lastTask = this.createFilterTaskForNode_(lastTask, node, prevNode);
92 }
93 }
94 return {rootTask: rootTask, lastTask: lastTask, lastNode: node};
95 },
96
97 createFilterTaskForNode_: function(lastTask, node, prevNode) {
98 return lastTask.after(function() {
99 // TODO(skyostil): Break into subtasks.
100 node.evaluateFilterExpression_(
101 prevNode.selection_, node.selection_);
102 }, this);
103 },
104
105 // Applies the result of a filter expression for a given event and all
106 // of its subslices and adds the matching events to an output selection.
107 evaluateFilterExpression_: function(inputSelection, outputSelection) {
108 var seenEvents = {};
109 inputSelection.forEach(function(event) {
Chris Craik44c28202015-05-12 17:25:16 -0700110 var context = new tv.e.tquery.Context();
Chris Craikb122baf2015-03-05 13:58:42 -0800111 context.event = event;
112 this.evaluateFilterExpressionForEvent_(
113 context, inputSelection, outputSelection, seenEvents);
114 }.bind(this));
115 },
116
117 evaluateFilterExpressionForEvent_: function(
118 context, inputSelection, outputSelection, seenEvents) {
119 var event = context.event;
120 if (inputSelection.contains(event) && !seenEvents[event.guid]) {
121 seenEvents[event.guid] = true;
122 if (!this.filterExpression_ ||
123 this.filterExpression_.evaluate(context))
124 outputSelection.push(event);
125 }
126 if (!event.subSlices)
127 return;
128 context = context.push(event);
129 for (var i = 0; i < event.subSlices.length; i++) {
130 context.event = event.subSlices[i];
131 this.evaluateFilterExpressionForEvent_(
132 context, inputSelection, outputSelection, seenEvents);
133 }
134 },
135
Chris Craik44c28202015-05-12 17:25:16 -0700136 // Show the result as a highlight on the selection controller. Returns a
Chris Craikb122baf2015-03-05 13:58:42 -0800137 // {Task} which runs the query and sets the highlight.
138 show: function() {
139 var graph = this.createFilterTaskGraph_();
140
141 graph.lastTask = graph.lastTask.after(function() {
Chris Craik44c28202015-05-12 17:25:16 -0700142 this.selectionController.showScriptControlSelection(
143 graph.lastNode.selection_);
Chris Craikb122baf2015-03-05 13:58:42 -0800144 }, this);
145 return graph.rootTask;
146 },
147
Chris Craik44c28202015-05-12 17:25:16 -0700148 // Returns a task that fills the given selection with everything reachable
149 // by the selection controller.
Chris Craikb122baf2015-03-05 13:58:42 -0800150 selectEverythingAsTask_: function(selection) {
151 var passThroughFilter = new tv.c.Filter();
152 var filterTask =
Chris Craik44c28202015-05-12 17:25:16 -0700153 this.selectionController.addAllEventsMatchingFilterToSelectionAsTask(
Chris Craikb122baf2015-03-05 13:58:42 -0800154 passThroughFilter, selection);
155 return filterTask;
156 },
157
158 get selection() {
159 if (this.selection_ === undefined) {
160 var graph = this.createFilterTaskGraph_();
161 tv.b.Task.RunSynchronously(graph.rootTask);
162 }
163 return this.selection_;
164 }
Chris Craik44c28202015-05-12 17:25:16 -0700165 };
166 tv.c.ScriptingObjectRegistry.register(
167 new TQuery(),
168 {
169 name: '$t'
170 }
171 );
172
173 return {
174 TQuery: TQuery
175 };
176});
177</script>