| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| |
| <link rel="stylesheet" href="/core/tracks/rect_track.css"> |
| |
| <link rel="import" href="/core/tracks/heading_track.html"> |
| <link rel="import" href="/core/fast_rect_renderer.html"> |
| <link rel="import" href="/core/draw_helpers.html"> |
| <link rel="import" href="/base/sorted_array_utils.html"> |
| <link rel="import" href="/base/ui.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tv.exportTo('tv.c.tracks', function() { |
| |
| /** |
| * A track that displays an array of Rect objects. |
| * @constructor |
| * @extends {HeadingTrack} |
| */ |
| var RectTrack = tv.b.ui.define( |
| 'rect-track', tv.c.tracks.HeadingTrack); |
| |
| RectTrack.prototype = { |
| |
| __proto__: tv.c.tracks.HeadingTrack.prototype, |
| |
| decorate: function(viewport) { |
| tv.c.tracks.HeadingTrack.prototype.decorate.call(this, viewport); |
| this.classList.add('rect-track'); |
| this.asyncStyle_ = false; |
| this.rects_ = null; |
| }, |
| |
| get asyncStyle() { |
| return this.asyncStyle_; |
| }, |
| |
| set asyncStyle(v) { |
| this.asyncStyle_ = !!v; |
| }, |
| |
| get rects() { |
| return this.rects_; |
| }, |
| |
| set rects(rects) { |
| this.rects_ = rects || []; |
| this.invalidateDrawingContainer(); |
| }, |
| |
| get height() { |
| return window.getComputedStyle(this).height; |
| }, |
| |
| set height(height) { |
| this.style.height = height; |
| this.invalidateDrawingContainer(); |
| }, |
| |
| get hasVisibleContent() { |
| return this.rects_.length > 0; |
| }, |
| |
| getModelEventFromItem: function(rect) { |
| throw new Error('Not implemented.'); |
| }, |
| |
| draw: function(type, viewLWorld, viewRWorld) { |
| switch (type) { |
| case tv.c.tracks.DrawType.SLICE: |
| this.drawRects_(viewLWorld, viewRWorld); |
| break; |
| } |
| }, |
| |
| drawRects_: function(viewLWorld, viewRWorld) { |
| var ctx = this.context(); |
| |
| ctx.save(); |
| var bounds = this.getBoundingClientRect(); |
| tv.c.drawSlices( |
| ctx, |
| this.viewport.currentDisplayTransform, |
| viewLWorld, |
| viewRWorld, |
| bounds.height, |
| this.rects_, |
| this.asyncStyle_); |
| ctx.restore(); |
| |
| if (bounds.height <= 6) |
| return; |
| |
| var fontSize, yOffset; |
| if (bounds.height < 15) { |
| fontSize = 6; |
| yOffset = 1.0; |
| } else { |
| fontSize = 10; |
| yOffset = 2.5; |
| } |
| tv.c.drawLabels( |
| ctx, |
| this.viewport.currentDisplayTransform, |
| viewLWorld, |
| viewRWorld, |
| this.rects_, |
| this.asyncStyle_, |
| fontSize, |
| yOffset); |
| }, |
| |
| addEventsToTrackMap: function(eventToTrackMap) { |
| if (this.rects_ === undefined || this.rects_ === null) |
| return; |
| |
| this.rects_.forEach(function(rect) { |
| var event = this.getModelEventFromItem(rect); |
| if (event) |
| eventToTrackMap.addEvent(event, this); |
| }, this); |
| }, |
| |
| addIntersectingItemsInRangeToSelectionInWorldSpace: function( |
| loWX, hiWX, viewPixWidthWorld, selection) { |
| function onRect(rect) { |
| var event = this.getModelEventFromItem(rect); |
| if (event) |
| selection.push(event); |
| } |
| onRect = onRect.bind(this); |
| tv.b.iterateOverIntersectingIntervals(this.rects_, |
| function(x) { return x.start; }, |
| function(x) { return x.duration; }, |
| loWX, hiWX, |
| onRect); |
| }, |
| |
| /** |
| * Find the index for the given rect. |
| * @return {index} Index of the given rect, or undefined. |
| * @private |
| */ |
| indexOfRect_: function(rect) { |
| var index = tv.b.findLowIndexInSortedArray(this.rects_, |
| function(x) { return x.start; }, |
| rect.start); |
| while (index < this.rects_.length && |
| rect.start == this.rects_[index].start && |
| rect.colorId != this.rects_[index].colorId) { |
| index++; |
| } |
| return index < this.rects_.length ? index : undefined; |
| }, |
| |
| /** |
| * Add the item to the left or right of the provided event, if any, to the |
| * selection. |
| * @param {rect} The current rect. |
| * @param {Number} offset Number of rects away from the event to look. |
| * @param {Selection} selection The selection to add an event to, |
| * if found. |
| * @return {boolean} Whether an event was found. |
| * @private |
| */ |
| addItemNearToProvidedEventToSelection: function(event, offset, selection) { |
| var index = this.indexOfRect_(event); |
| if (index === undefined) |
| return false; |
| |
| var newIndex = index + offset; |
| if (newIndex < 0 || newIndex >= this.rects_.length) |
| return false; |
| |
| var event = this.rects_[newIndex]; |
| if (event) |
| selection.push(event); |
| return true; |
| }, |
| |
| addAllObjectsMatchingFilterToSelection: function(filter, selection) { |
| for (var i = 0; i < this.rects_.length; ++i) { |
| if (filter.matchSlice(this.rects_[i])) { |
| var event = this.getModelEventFromItem(this.rects_[i]); |
| if (event) |
| selection.push(event); |
| } |
| } |
| }, |
| |
| addClosestEventToSelection: function(worldX, worldMaxDist, loY, hiY, |
| selection) { |
| var rect = tv.b.findClosestIntervalInSortedIntervals( |
| this.rects_, |
| function(x) { return x.start; }, |
| function(x) { return x.end; }, |
| worldX, |
| worldMaxDist); |
| |
| if (rect) { |
| var event = this.getModelEventFromItem(rect); |
| if (event) |
| selection.push(event); |
| } |
| } |
| }; |
| |
| return { |
| RectTrack: RectTrack |
| }; |
| }); |
| </script> |