Javascript  |  348行  |  8.68 KB

// Copyright (c) 2012 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.

'use strict';

/**
 * @fileoverview Code for the viewport.
 */
base.require('base.events');
base.require('base.guid');
base.require('base.range');

base.exportTo('tracing', function() {

  function SelectionSliceHit(track, slice) {
    this.track = track;
    this.slice = slice;
  }
  SelectionSliceHit.prototype = {
    get modelObject() {
      return this.slice;
    },

    get selected() {
      return this.slice.selected;
    },
    set selected(v) {
      this.slice.selected = v;
    },
    addBoundsToRange: function(range) {
      range.addValue(this.slice.start);
      range.addValue(this.slice.end);
    }
  };

  function SelectionCounterSampleHit(track, counter, sampleIndex) {
    this.track = track;
    this.counter = counter;
    this.sampleIndex = sampleIndex;
  }

  SelectionCounterSampleHit.prototype = {
    get modelObject() {
      return this.sampleIndex;
    },

    get selected() {
      return this.track.selectedSamples[this.sampleIndex] == true;
    },

    set selected(v) {
      if (v)
        this.track.selectedSamples[this.sampleIndex] = true;
      else
        this.track.selectedSamples[this.sampleIndex] = false;
    },

    addBoundsToRange: function(range) {
      if (!this.track.timestamps)
        return;
      range.addValue(this.track.timestamps[this.sampleIndex]);
    }
  };

  function SelectionObjectSnapshotHit(track, objectSnapshot) {
    this.track = track;
    this.objectSnapshot = objectSnapshot;
  }
  SelectionObjectSnapshotHit.prototype = {
    get modelObject() {
      return this.objectSnapshot;
    },

    get selected() {
      return this.objectSnapshot.selected;
    },
    set selected(v) {
      this.objectSnapshot.selected = v;
    },
    addBoundsToRange: function(range) {
      range.addValue(this.objectSnapshot.ts);
    }
  };

  function SelectionObjectInstanceHit(track, objectInstance) {
    this.track = track;
    this.objectInstance = objectInstance;
  }
  SelectionObjectInstanceHit.prototype = {
    get modelObject() {
      return this.objectInstance;
    },

    get selected() {
      return this.objectInstance.selected;
    },
    set selected(v) {
      this.objectInstance.selected = v;
    },
    addBoundsToRange: function(range) {
      range.addRange(this.objectInstance.bounds);
    }
  };

  var HIT_TYPES = [
    {
      constructor: SelectionSliceHit,
      name: 'slice',
      pluralName: 'slices'
    },
    {
      constructor: SelectionCounterSampleHit,
      name: 'counterSample',
      pluralName: 'counterSamples'
    },
    {
      constructor: SelectionObjectSnapshotHit,
      name: 'objectSnapshot',
      pluralName: 'objectSnapshots'
    },
    {
      constructor: SelectionObjectInstanceHit,
      name: 'objectInstance',
      pluralName: 'objectInstances'
    }
  ];

  /**
   * Represents a selection within a  and its associated set of tracks.
   * @constructor
   */
  function Selection(opt_hits) {
    this.bounds_dirty_ = true;
    this.bounds_ = new base.Range();
    this.length_ = 0;
    this.guid_ = base.GUID.allocate();

    if (opt_hits)
      this.pushHits(opt_hits);
  }
  Selection.prototype = {
    __proto__: Object.prototype,

    get bounds() {
      if (this.bounds_dirty_) {
        this.bounds_.reset();
        for (var i = 0; i < this.length_; i++) {
          var hit = this[i];
          hit.addBoundsToRange(this.bounds_);
        }
        this.bounds_dirty_ = false;
      }
      return this.bounds_;
    },

    get duration() {
      if (this.bounds_.isEmpty)
        return 0;
      return this.bounds_.max - this.bounds_.min;
    },

    get length() {
      return this.length_;
    },

    get guid() {
      return this.guid_;
    },

    clear: function() {
      for (var i = 0; i < this.length_; ++i)
        delete this[i];
      this.length_ = 0;
      this.bounds_dirty_ = true;
    },

    pushHit: function(hit) {
      this.push_(hit);
    },

    pushHits: function(hits) {
      for (var i = 0; i < hits.length; i++)
        this.pushHit(hits[i]);
    },

    push_: function(hit) {
      this[this.length_++] = hit;
      this.bounds_dirty_ = true;
      return hit;
    },

    addSlice: function(track, slice) {
      return this.push_(new SelectionSliceHit(track, slice));
    },

    addCounterSample: function(track, counter, sampleIndex) {
      return this.push_(
          new SelectionCounterSampleHit(
          track, counter, sampleIndex));
    },

    addObjectSnapshot: function(track, objectSnapshot) {
      return this.push_(
          new SelectionObjectSnapshotHit(track, objectSnapshot));
    },

    addObjectInstance: function(track, objectInstance) {
      return this.push_(
          new SelectionObjectInstanceHit(track, objectInstance));
    },

    addSelection: function(selection) {
      for (var i = 0; i < selection.length; i++)
        this.push_(selection[i]);
    },

    subSelection: function(index, count) {
      count = count || 1;

      var selection = new Selection();
      selection.bounds_dirty_ = true;
      if (index < 0 || index + count > this.length_)
        throw new Error('Index out of bounds');

      for (var i = index; i < index + count; i++)
        selection.push_(this[i]);

      return selection;
    },

    getCounterSampleHitsAsSelection: function() {
      var selection = new Selection();
      this.enumHitsOfType(SelectionCounterSampleHit,
                          selection.push_.bind(selection));
      return selection;
    },

    getSliceHitsAsSelection: function() {
      var selection = new Selection();
      this.enumHitsOfType(SelectionSliceHit,
                          selection.push_.bind(selection));
      return selection;
    },

    getHitsOrganizedByType: function() {
      var hits = {};
      HIT_TYPES.forEach(function(hitType) {
        hits[hitType.pluralName] = new Selection();
      });
      for (var i = 0; i < this.length_; i++) {
        var hit = this[i];
        HIT_TYPES.forEach(function(hitType) {
          if (hit instanceof hitType.constructor)
            hits[hitType.pluralName].push_(hit);
        });
      }
      return hits;
    },

    enumHitsOfType: function(type, func) {
      for (var i = 0; i < this.length_; i++)
        if (this[i] instanceof type)
          func(this[i]);
    },

    getNumSliceHits: function() {
      var numHits = 0;
      this.enumHitsOfType(SelectionSliceHit, function(hit) { numHits++; });
      return numHits;
    },

    getNumCounterHits: function() {
      var numHits = 0;
      this.enumHitsOfType(SelectionCounterSampleHit, function(hit) {
        numHits++;
      });
      return numHits;
    },

    getNumObjectSnapshotHits: function() {
      var numHits = 0;
      this.enumHitsOfType(SelectionObjectSnapshotHit, function(hit) {
        numHits++;
      });
      return numHits;
    },

    getNumObjectInstanceHits: function() {
      var numHits = 0;
      this.enumHitsOfType(SelectionObjectInstanceHit, function(hit) {
        numHits++;
      });
      return numHits;
    },

    map: function(fn) {
      for (var i = 0; i < this.length_; i++)
        fn(this[i]);
    },

    /**
     * Helper for selection previous or next.
     * @param {boolean} forwardp If true, select one forward (next).
     *   Else, select previous.
     * @return {boolean} true if current selection changed.
     */
    getShiftedSelection: function(offset) {
      var newSelection = new Selection();
      for (var i = 0; i < this.length_; i++) {
        var hit = this[i];
        hit.track.addItemNearToProvidedHitToSelection(
            hit, offset, newSelection);
      }

      if (newSelection.length == 0)
        return undefined;
      return newSelection;
    }
  };

  function createSelectionFromObjectAndView(obj, opt_view) {
    // TODO: fill in the track intelligently by finding the TimelineView, then
    // finding the right track based on the provided object.
    var track = undefined;

    var selection = new Selection();
    if (obj instanceof tracing.trace_model.Slice)
      selection.addSlice(track, obj);
    else if (obj instanceof tracing.trace_model.ObjectSnapshot)
      selection.addObjectSnapshot(track, obj);
    else if (obj instanceof tracing.trace_model.ObjectInstance)
      selection.addObjectInstance(track, obj);
    else
      throw new Error('Unrecognized selection type');
    return selection;
  }

  return {
    SelectionSliceHit: SelectionSliceHit,
    SelectionCounterSampleHit: SelectionCounterSampleHit,
    SelectionObjectSnapshotHit: SelectionObjectSnapshotHit,
    SelectionObjectInstanceHit: SelectionObjectInstanceHit,
    Selection: Selection,
    createSelectionFromObjectAndView: createSelectionFromObjectAndView
  };
});