Home | History | Annotate | Download | only in cc
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 'use strict';
      6 
      7 base.requireStylesheet('cc.picture_ops_list_view');
      8 
      9 base.require('cc.constants');
     10 base.require('cc.selection');
     11 base.require('ui.list_view');
     12 base.require('ui.dom_helpers');
     13 
     14 base.exportTo('cc', function() {
     15   var ANNOTATION = 'Comment';
     16   var BEGIN_ANNOTATION = 'BeginCommentGroup';
     17   var END_ANNOTATION = 'EndCommentGroup';
     18   var ANNOTATION_ID = 'ID: ';
     19   var ANNOTATION_CLASS = 'CLASS: ';
     20   var ANNOTATION_TAG = 'TAG: ';
     21 
     22   var constants = cc.constants;
     23 
     24   /**
     25    * @constructor
     26    */
     27   var PictureOpsListView = ui.define('picture-ops-list-view');
     28 
     29   PictureOpsListView.prototype = {
     30     __proto__: HTMLUnknownElement.prototype,
     31 
     32     decorate: function() {
     33       this.opsList_ = new ui.ListView();
     34       this.appendChild(this.opsList_);
     35 
     36       this.selectedOp_ = undefined;
     37       this.selectedOpIndex_ = undefined;
     38       this.opsList_.addEventListener(
     39           'selection-changed', this.onSelectionChanged_.bind(this));
     40 
     41       this.picture_ = undefined;
     42     },
     43 
     44     get picture() {
     45       return this.picture_;
     46     },
     47 
     48     set picture(picture) {
     49       this.picture_ = picture;
     50       this.updateContents_();
     51     },
     52 
     53     updateContents_: function() {
     54       this.opsList_.clear();
     55 
     56       if (!this.picture_)
     57         return;
     58 
     59       var ops = this.picture_.getOps();
     60       if (!ops)
     61         return;
     62 
     63       ops = this.opsTaggedWithAnnotations_(ops);
     64 
     65       for (var i = 0; i < ops.length; i++) {
     66         var op = ops[i];
     67         var item = document.createElement('div');
     68         item.opIndex = op.opIndex;
     69         item.textContent = i + ') ' + op.cmd_string;
     70 
     71         // Display the element info associated with the op, if available.
     72         if (op.elementInfo.tag || op.elementInfo.id || op.elementInfo.class) {
     73           var elementInfo = document.createElement('span');
     74           elementInfo.classList.add('elementInfo');
     75           var tag = op.elementInfo.tag ? op.elementInfo.tag : 'unknown';
     76           var id = op.elementInfo.id ? 'id=' + op.elementInfo.id : undefined;
     77           var className = op.elementInfo.class ? 'class=' +
     78               op.elementInfo.class : undefined;
     79           elementInfo.textContent =
     80               '<' + tag + (id ? ' ' : '') +
     81               (id ? id : '') + (className ? ' ' : '') +
     82               (className ? className : '') + '>';
     83           item.appendChild(elementInfo);
     84         }
     85 
     86         // Display each of the Skia ops.
     87         op.info.forEach(function(info) {
     88           var infoItem = document.createElement('div');
     89           infoItem.textContent = info;
     90           item.appendChild(infoItem);
     91         });
     92 
     93         this.opsList_.appendChild(item);
     94       }
     95     },
     96 
     97     onSelectionChanged_: function(e) {
     98       var beforeSelectedOp = true;
     99 
    100       // Deselect on re-selection.
    101       if (this.opsList_.selectedElement === this.selectedOp_) {
    102         this.opsList_.selectedElement = undefined;
    103         beforeSelectedOp = false;
    104         this.selectedOpIndex_ = undefined;
    105       }
    106 
    107       this.selectedOp_ = this.opsList_.selectedElement;
    108 
    109       // Set selection on all previous ops.
    110       var ops = this.opsList_.children;
    111       for (var i = 0; i < ops.length; i++) {
    112         var op = ops[i];
    113         if (op === this.selectedOp_) {
    114           beforeSelectedOp = false;
    115           this.selectedOpIndex_ = op.opIndex;
    116         } else if (beforeSelectedOp) {
    117           op.setAttribute('beforeSelection', 'beforeSelection');
    118         } else {
    119           op.removeAttribute('beforeSelection');
    120         }
    121       }
    122 
    123       base.dispatchSimpleEvent(this, 'selection-changed', false);
    124     },
    125 
    126     get selectedOpIndex() {
    127       return this.selectedOpIndex_;
    128     },
    129 
    130     /**
    131      * Return Skia operations tagged by annotation.
    132      *
    133      * The ops returned from Picture.getOps() contain both Skia ops and
    134      * annotations threaded together. This function removes all annotations
    135      * from the list and tags each op with the associated annotations.
    136      * Additionally, the last {tag, id, class} is stored as elementInfo on
    137      * each op.
    138      *
    139      * @param {Array} ops Array of Skia operations and annotations.
    140      * @return {Array} Skia ops where op.annotations contains the associated
    141      *         annotations for a given op.
    142      */
    143     opsTaggedWithAnnotations_: function(ops) {
    144       // This algorithm works by walking all the ops and pushing any
    145       // annotations onto a stack. When a non-annotation op is found, the
    146       // annotations stack is traversed and stored with the op.
    147       var annotationGroups = new Array();
    148       var opsWithoutAnnotations = new Array();
    149       for (var opIndex = 0; opIndex < ops.length; opIndex++) {
    150         var op = ops[opIndex];
    151         op.opIndex = opIndex;
    152         switch (op.cmd_string) {
    153           case BEGIN_ANNOTATION:
    154             annotationGroups.push(new Array());
    155             break;
    156           case END_ANNOTATION:
    157             annotationGroups.pop();
    158             break;
    159           case ANNOTATION:
    160             annotationGroups[annotationGroups.length - 1].push(op);
    161             break;
    162           default:
    163             var annotations = new Array();
    164             var elementInfo = {};
    165             annotationGroups.forEach(function(annotationGroup) {
    166               elementInfo = {};
    167               annotationGroup.forEach(function(annotation) {
    168                 annotation.info.forEach(function(info) {
    169                   if (info.indexOf(ANNOTATION_TAG) != -1)
    170                     elementInfo.tag = info.substring(
    171                         info.indexOf(ANNOTATION_TAG) +
    172                         ANNOTATION_TAG.length).toLowerCase();
    173                   else if (info.indexOf(ANNOTATION_ID) != -1)
    174                     elementInfo.id = info.substring(
    175                         info.indexOf(ANNOTATION_ID) +
    176                         ANNOTATION_ID.length);
    177                   else if (info.indexOf(ANNOTATION_CLASS) != -1)
    178                     elementInfo.class = info.substring(
    179                         info.indexOf(ANNOTATION_CLASS) +
    180                         ANNOTATION_CLASS.length);
    181 
    182                   annotations.push(info);
    183                 });
    184               });
    185             });
    186             op.annotations = annotations;
    187             op.elementInfo = elementInfo;
    188             opsWithoutAnnotations.push(op);
    189         }
    190       }
    191       return opsWithoutAnnotations;
    192     }
    193   };
    194 
    195   return {
    196     PictureOpsListView: PictureOpsListView
    197   };
    198 });
    199