home *** CD-ROM | disk | FTP | other *** search
/ Computer Active Guide 2009 July / CAG7.ISO / Internetas / SafariSetup.exe / AppleApplicationSupport.msi / WebKit.resources_inspector_TimelineOverviewPane.js < prev    next >
Encoding:
Text File  |  2010-06-03  |  19.5 KB  |  501 lines

  1. /*
  2.  * Copyright (C) 2009 Google Inc. All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions are
  6.  * met:
  7.  *
  8.  *     * Redistributions of source code must retain the above copyright
  9.  * notice, this list of conditions and the following disclaimer.
  10.  *     * Redistributions in binary form must reproduce the above
  11.  * copyright notice, this list of conditions and the following disclaimer
  12.  * in the documentation and/or other materials provided with the
  13.  * distribution.
  14.  *     * Neither the name of Google Inc. nor the names of its
  15.  * contributors may be used to endorse or promote products derived from
  16.  * this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29.  */
  30.  
  31. WebInspector.TimelineOverviewPane = function(categories)
  32. {
  33.     this.element = document.createElement("div");
  34.     this.element.id = "timeline-overview-panel";
  35.  
  36.     this._categories = categories;
  37.     this._overviewSidebarElement = document.createElement("div");
  38.     this._overviewSidebarElement.id = "timeline-overview-sidebar";
  39.     this.element.appendChild(this._overviewSidebarElement);
  40.  
  41.     var overviewTreeElement = document.createElement("ol");
  42.     overviewTreeElement.className = "sidebar-tree";
  43.     this._overviewSidebarElement.appendChild(overviewTreeElement);
  44.     var sidebarTree = new TreeOutline(overviewTreeElement);
  45.  
  46.     var categoriesTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("TIMELINES"), {}, true);
  47.     categoriesTreeElement.expanded = true;
  48.     sidebarTree.appendChild(categoriesTreeElement);
  49.     for (var categoryName in this._categories) {
  50.         var category = this._categories[categoryName];
  51.         categoriesTreeElement.appendChild(new WebInspector.TimelineCategoryTreeElement(category, this._onCheckboxClicked.bind(this, category)));
  52.     }
  53.  
  54.     this._overviewGrid = new WebInspector.TimelineGrid();
  55.     this._overviewGrid.element.id = "timeline-overview-grid";
  56.     this._overviewGrid.itemsGraphsElement.id = "timeline-overview-graphs";
  57.     this._overviewGrid.element.addEventListener("mousedown", this._dragWindow.bind(this), true);
  58.     this.element.appendChild(this._overviewGrid.element);
  59.  
  60.     this._categoryGraphs = {};
  61.     var i = 0;
  62.     for (var category in this._categories) {
  63.         var categoryGraph = new WebInspector.TimelineCategoryGraph(this._categories[category], i++ % 2);
  64.         this._categoryGraphs[category] = categoryGraph;
  65.         this._overviewGrid.itemsGraphsElement.appendChild(categoryGraph.graphElement);
  66.     }
  67.     this._overviewGrid.setScrollAndDividerTop(0, 0);
  68.  
  69.     this._overviewWindowElement = document.createElement("div");
  70.     this._overviewWindowElement.id = "timeline-overview-window";
  71.     this._overviewGrid.element.appendChild(this._overviewWindowElement);
  72.  
  73.     this._overviewWindowBordersElement = document.createElement("div");
  74.     this._overviewWindowBordersElement.className = "timeline-overview-window-rulers";
  75.     this._overviewGrid.element.appendChild(this._overviewWindowBordersElement);
  76.  
  77.     var overviewDividersBackground = document.createElement("div");
  78.     overviewDividersBackground.className = "timeline-overview-dividers-background";
  79.     this._overviewGrid.element.appendChild(overviewDividersBackground);
  80.  
  81.     this._leftResizeElement = document.createElement("div");
  82.     this._leftResizeElement.className = "timeline-window-resizer";
  83.     this._leftResizeElement.style.left = 0;
  84.     this._overviewGrid.element.appendChild(this._leftResizeElement);
  85.  
  86.     this._rightResizeElement = document.createElement("div");
  87.     this._rightResizeElement.className = "timeline-window-resizer timeline-window-resizer-right";
  88.     this._rightResizeElement.style.right = 0;
  89.     this._overviewGrid.element.appendChild(this._rightResizeElement);
  90.  
  91.     this._overviewCalculator = new WebInspector.TimelineOverviewCalculator();
  92.  
  93.     var separatorElement = document.createElement("div");
  94.     separatorElement.id = "timeline-overview-separator";
  95.     this.element.appendChild(separatorElement);
  96.  
  97.     this.windowLeft = 0.0;
  98.     this.windowRight = 1.0;
  99. }
  100.  
  101. WebInspector.TimelineOverviewPane.minSelectableSize = 12;
  102.  
  103. WebInspector.TimelineOverviewPane.prototype = {
  104.     _onCheckboxClicked: function (category, event) {
  105.         if (event.target.checked)
  106.             category.hidden = false;
  107.         else
  108.             category.hidden = true;
  109.         this._categoryGraphs[category.name].dimmed = !event.target.checked;
  110.         this.dispatchEventToListeners("filter changed");
  111.     },
  112.  
  113.     update: function(records, showShortEvents)
  114.     {
  115.         this._showShortEvents = showShortEvents;
  116.         // Clear summary bars.
  117.         var timelines = {};
  118.         for (var category in this._categories) {
  119.             timelines[category] = [];
  120.             this._categoryGraphs[category].clearChunks();
  121.         }
  122.  
  123.         function forAllRecords(recordsArray, callback)
  124.         {
  125.             if (!recordsArray)
  126.                 return;
  127.             for (var i = 0; i < recordsArray.length; ++i) {
  128.                 callback(recordsArray[i]);
  129.                 forAllRecords(recordsArray[i].children, callback);
  130.             }
  131.         }
  132.  
  133.         // Create sparse arrays with 101 cells each to fill with chunks for a given category.
  134.         this._overviewCalculator.reset();
  135.         forAllRecords(records, this._overviewCalculator.updateBoundaries.bind(this._overviewCalculator));
  136.  
  137.         function markTimeline(record)
  138.         {
  139.             if (!(this._showShortEvents || record.isLong()))
  140.                 return;
  141.             var percentages = this._overviewCalculator.computeBarGraphPercentages(record);
  142.  
  143.             var end = Math.round(percentages.end);
  144.             var categoryName = record.category.name;
  145.             for (var j = Math.round(percentages.start); j <= end; ++j)
  146.                 timelines[categoryName][j] = true;
  147.         }
  148.         forAllRecords(records, markTimeline.bind(this));
  149.  
  150.         // Convert sparse arrays to continuous segments, render graphs for each.
  151.         for (var category in this._categories) {
  152.             var timeline = timelines[category];
  153.             window.timelineSaved = timeline;
  154.             var chunkStart = -1;
  155.             for (var j = 0; j < 101; ++j) {
  156.                 if (timeline[j]) {
  157.                     if (chunkStart === -1)
  158.                         chunkStart = j;
  159.                 } else {
  160.                     if (chunkStart !== -1) {
  161.                         this._categoryGraphs[category].addChunk(chunkStart, j);
  162.                         chunkStart = -1;
  163.                     }
  164.                 }
  165.             }
  166.             if (chunkStart !== -1) {
  167.                 this._categoryGraphs[category].addChunk(chunkStart, 100);
  168.                 chunkStart = -1;
  169.             }
  170.         }
  171.         this._overviewGrid.updateDividers(true, this._overviewCalculator);
  172.     },
  173.  
  174.     updateEventDividers: function(records, dividerConstructor)
  175.     {
  176.         this._overviewGrid.removeEventDividers();
  177.         var dividers = [];
  178.         for (var i = 0; i < records.length; ++i) {
  179.             var record = records[i];
  180.             var positions = this._overviewCalculator.computeBarGraphPercentages(record);
  181.             var dividerPosition = Math.round(positions.start * 10);
  182.             if (dividers[dividerPosition])
  183.                 continue;
  184.             var divider = dividerConstructor(record);
  185.             divider.style.left = positions.start + "%";
  186.             dividers[dividerPosition] = divider;
  187.         }
  188.         this._overviewGrid.addEventDividers(dividers);
  189.     },
  190.  
  191.     setSidebarWidth: function(width)
  192.     {
  193.         this._overviewSidebarElement.style.width = width + "px";
  194.     },
  195.  
  196.     updateMainViewWidth: function(width)
  197.     {
  198.         this._overviewGrid.element.style.left = width + "px";
  199.     },
  200.  
  201.     reset: function()
  202.     {
  203.         this.windowLeft = 0.0;
  204.         this.windowRight = 1.0;
  205.         this._overviewWindowElement.style.left = "0%";
  206.         this._overviewWindowElement.style.width = "100%";
  207.         this._overviewWindowBordersElement.style.left = "0%";
  208.         this._overviewWindowBordersElement.style.right = "0%";
  209.         this._leftResizeElement.style.left = "0%";
  210.         this._rightResizeElement.style.left = "100%";
  211.         this._overviewCalculator.reset();
  212.         this._overviewGrid.updateDividers(true, this._overviewCalculator);
  213.     },
  214.  
  215.     _resizeWindow: function(resizeElement, event)
  216.     {
  217.         WebInspector.elementDragStart(resizeElement, this._windowResizeDragging.bind(this, resizeElement), this._endWindowDragging.bind(this), event, "col-resize");
  218.     },
  219.  
  220.     _windowResizeDragging: function(resizeElement, event)
  221.     {
  222.         if (resizeElement === this._leftResizeElement)
  223.             this._resizeWindowLeft(event.pageX - this._overviewGrid.element.offsetLeft);
  224.         else
  225.             this._resizeWindowRight(event.pageX - this._overviewGrid.element.offsetLeft);
  226.         event.preventDefault();
  227.     },
  228.  
  229.     _dragWindow: function(event)
  230.     {
  231.         var node = event.target;
  232.         while (node) {
  233.             if (node === this._overviewGrid._dividersLabelBarElement) {
  234.                 WebInspector.elementDragStart(this._overviewWindowElement, this._windowDragging.bind(this, event.pageX,
  235.                     this._leftResizeElement.offsetLeft, this._rightResizeElement.offsetLeft), this._endWindowDragging.bind(this), event, "ew-resize");
  236.                 break;
  237.             } else if (node === this._overviewGrid.element) {
  238.                 var position = event.pageX - this._overviewGrid.element.offsetLeft;
  239.                 this._overviewWindowSelector = new WebInspector.TimelinePanel.WindowSelector(this._overviewGrid.element, position, event);
  240.                 WebInspector.elementDragStart(null, this._windowSelectorDragging.bind(this), this._endWindowSelectorDragging.bind(this), event, "col-resize");
  241.                 break;
  242.             } else if (node === this._leftResizeElement || node === this._rightResizeElement) {
  243.                 this._resizeWindow(node, event);
  244.                 break;
  245.             }
  246.             node = node.parentNode;
  247.         }
  248.     },
  249.  
  250.     _windowSelectorDragging: function(event)
  251.     {
  252.         this._overviewWindowSelector._updatePosition(event.pageX - this._overviewGrid.element.offsetLeft);
  253.         event.preventDefault();
  254.     },
  255.  
  256.     _endWindowSelectorDragging: function(event)
  257.     {
  258.         WebInspector.elementDragEnd(event);
  259.         var window = this._overviewWindowSelector._close(event.pageX - this._overviewGrid.element.offsetLeft);
  260.         delete this._overviewWindowSelector;
  261.         if (window.end - window.start < WebInspector.TimelineOverviewPane.minSelectableSize)
  262.             if (this._overviewGrid.itemsGraphsElement.offsetWidth - window.end > WebInspector.TimelineOverviewPane.minSelectableSize)
  263.                 window.end = window.start + WebInspector.TimelineOverviewPane.minSelectableSize;
  264.             else
  265.                 window.start = window.end - WebInspector.TimelineOverviewPane.minSelectableSize;
  266.         this._setWindowPosition(window.start, window.end);
  267.     },
  268.  
  269.     _windowDragging: function(startX, windowLeft, windowRight, event)
  270.     {
  271.         var delta = event.pageX - startX;
  272.         var start = windowLeft + delta;
  273.         var end = windowRight + delta;
  274.         var windowSize = windowRight - windowLeft;
  275.  
  276.         if (start < 0) {
  277.             start = 0;
  278.             end = windowSize;
  279.         }
  280.  
  281.         if (end > this._overviewGrid.element.clientWidth) {
  282.             end = this._overviewGrid.element.clientWidth;
  283.             start = end - windowSize;
  284.         }
  285.         this._setWindowPosition(start, end);
  286.  
  287.         event.preventDefault();
  288.     },
  289.  
  290.     _resizeWindowLeft: function(start)
  291.     {
  292.         // Glue to edge.
  293.         if (start < 10)
  294.             start = 0;
  295.         else if (start > this._rightResizeElement.offsetLeft -  4)
  296.             start = this._rightResizeElement.offsetLeft - 4;
  297.         this._setWindowPosition(start, null);
  298.     },
  299.  
  300.     _resizeWindowRight: function(end)
  301.     {
  302.         // Glue to edge.
  303.         if (end > this._overviewGrid.element.clientWidth - 10)
  304.             end = this._overviewGrid.element.clientWidth;
  305.         else if (end < this._leftResizeElement.offsetLeft + WebInspector.TimelineOverviewPane.minSelectableSize)
  306.             end = this._leftResizeElement.offsetLeft + WebInspector.TimelineOverviewPane.minSelectableSize;
  307.         this._setWindowPosition(null, end);
  308.     },
  309.  
  310.     _setWindowPosition: function(start, end)
  311.     {
  312.         const rulerAdjustment = 1 / this._overviewGrid.element.clientWidth;
  313.         if (typeof start === "number") {
  314.             this.windowLeft = start / this._overviewGrid.element.clientWidth;
  315.             this._leftResizeElement.style.left = this.windowLeft * 100 + "%";
  316.             this._overviewWindowElement.style.left = this.windowLeft * 100 + "%";
  317.             this._overviewWindowBordersElement.style.left = (this.windowLeft - rulerAdjustment) * 100 + "%";
  318.         }
  319.         if (typeof end === "number") {
  320.             this.windowRight = end / this._overviewGrid.element.clientWidth;
  321.             this._rightResizeElement.style.left = this.windowRight * 100 + "%";
  322.         }
  323.         this._overviewWindowElement.style.width = (this.windowRight - this.windowLeft) * 100 + "%";
  324.         this._overviewWindowBordersElement.style.right = (1 - this.windowRight + 2 * rulerAdjustment) * 100 + "%";
  325.         this.dispatchEventToListeners("window changed");
  326.     },
  327.  
  328.     _endWindowDragging: function(event)
  329.     {
  330.         WebInspector.elementDragEnd(event);
  331.     }
  332. }
  333.  
  334. WebInspector.TimelineOverviewPane.prototype.__proto__ = WebInspector.Object.prototype;
  335.  
  336.  
  337. WebInspector.TimelineOverviewCalculator = function()
  338. {
  339.     this._uiString = WebInspector.UIString.bind(WebInspector);
  340. }
  341.  
  342. WebInspector.TimelineOverviewCalculator.prototype = {
  343.     computeBarGraphPercentages: function(record)
  344.     {
  345.         var start = (record.startTime - this.minimumBoundary) / this.boundarySpan * 100;
  346.         var end = (record.endTime - this.minimumBoundary) / this.boundarySpan * 100;
  347.         return {start: start, end: end};
  348.     },
  349.  
  350.     reset: function()
  351.     {
  352.         delete this.minimumBoundary;
  353.         delete this.maximumBoundary;
  354.     },
  355.  
  356.     updateBoundaries: function(record)
  357.     {
  358.         if (typeof this.minimumBoundary === "undefined" || record.startTime < this.minimumBoundary) {
  359.             this.minimumBoundary = record.startTime;
  360.             return true;
  361.         }
  362.         if (typeof this.maximumBoundary === "undefined" || record.endTime > this.maximumBoundary) {
  363.             this.maximumBoundary = record.endTime;
  364.             return true;
  365.         }
  366.         return false;
  367.     },
  368.  
  369.     get boundarySpan()
  370.     {
  371.         return this.maximumBoundary - this.minimumBoundary;
  372.     },
  373.  
  374.     formatValue: function(value)
  375.     {
  376.         return Number.secondsToString(value, this._uiString);
  377.     }
  378. }
  379.  
  380.  
  381. WebInspector.TimelineCategoryTreeElement = function(category, onCheckboxClicked)
  382. {
  383.     this._category = category;
  384.     this._onCheckboxClicked = onCheckboxClicked;
  385.     // Pass an empty title, the title gets made later in onattach.
  386.     TreeElement.call(this, "", null, false);
  387. }
  388.  
  389. WebInspector.TimelineCategoryTreeElement.prototype = {
  390.     onattach: function()
  391.     {
  392.         this.listItemElement.removeChildren();
  393.         this.listItemElement.addStyleClass("timeline-category-tree-item");
  394.         this.listItemElement.addStyleClass("timeline-category-" + this._category.name);
  395.  
  396.         var label = document.createElement("label");
  397.  
  398.         var checkElement = document.createElement("input");
  399.         checkElement.type = "checkbox";
  400.         checkElement.className = "timeline-category-checkbox";
  401.         checkElement.checked = true;
  402.         checkElement.addEventListener("click", this._onCheckboxClicked);
  403.         label.appendChild(checkElement);
  404.  
  405.         var typeElement = document.createElement("span");
  406.         typeElement.className = "type";
  407.         typeElement.textContent = this._category.title;
  408.         label.appendChild(typeElement);
  409.  
  410.         this.listItemElement.appendChild(label);
  411.     }
  412. }
  413.  
  414. WebInspector.TimelineCategoryTreeElement.prototype.__proto__ = TreeElement.prototype;
  415.  
  416. WebInspector.TimelineCategoryGraph = function(category, isEven)
  417. {
  418.     this._category = category;
  419.  
  420.     this._graphElement = document.createElement("div");
  421.     this._graphElement.className = "timeline-graph-side timeline-overview-graph-side" + (isEven ? " even" : "");
  422.  
  423.     this._barAreaElement = document.createElement("div");
  424.     this._barAreaElement.className = "timeline-graph-bar-area timeline-category-" + category.name;
  425.     this._graphElement.appendChild(this._barAreaElement);
  426. }
  427.  
  428. WebInspector.TimelineCategoryGraph.prototype = {
  429.     get graphElement()
  430.     {
  431.         return this._graphElement;
  432.     },
  433.  
  434.     addChunk: function(start, end)
  435.     {
  436.         var chunk = document.createElement("div");
  437.         chunk.className = "timeline-graph-bar";
  438.         this._barAreaElement.appendChild(chunk);
  439.         chunk.style.setProperty("left", start + "%");
  440.         chunk.style.setProperty("width", (end - start) + "%");
  441.     },
  442.  
  443.     clearChunks: function()
  444.     {
  445.         this._barAreaElement.removeChildren();
  446.     },
  447.  
  448.     set dimmed(dimmed)
  449.     {
  450.         if (dimmed)
  451.             this._barAreaElement.removeStyleClass("timeline-category-" + this._category.name);
  452.         else
  453.             this._barAreaElement.addStyleClass("timeline-category-" + this._category.name);
  454.     }
  455. }
  456.  
  457. WebInspector.TimelinePanel.WindowSelector = function(parent, position, event)
  458. {
  459.     this._startPosition = position;
  460.     this._width = parent.offsetWidth;
  461.     this._windowSelector = document.createElement("div");
  462.     this._windowSelector.className = "timeline-window-selector";
  463.     this._windowSelector.style.left = this._startPosition + "px";
  464.     this._windowSelector.style.right = this._width - this._startPosition +  + "px";
  465.     parent.appendChild(this._windowSelector);
  466. }
  467.  
  468. WebInspector.TimelinePanel.WindowSelector.prototype = {
  469.     _createSelectorElement: function(parent, left, width, height)
  470.     {
  471.         var selectorElement = document.createElement("div");
  472.         selectorElement.className = "timeline-window-selector";
  473.         selectorElement.style.left = left + "px";
  474.         selectorElement.style.width = width + "px";
  475.         selectorElement.style.top = "0px";
  476.         selectorElement.style.height = height + "px";
  477.         parent.appendChild(selectorElement);
  478.         return selectorElement;
  479.     },
  480.  
  481.     _close: function(position)
  482.     {
  483.         position = Math.max(0, Math.min(position, this._width));
  484.         this._windowSelector.parentNode.removeChild(this._windowSelector);
  485.         return this._startPosition < position ? {start: this._startPosition, end: position} : {start: position, end: this._startPosition};
  486.     },
  487.  
  488.     _updatePosition: function(position)
  489.     {
  490.         position = Math.max(0, Math.min(position, this._width));
  491.         if (position < this._startPosition) {
  492.             this._windowSelector.style.left = position + "px";
  493.             this._windowSelector.style.right = this._width - this._startPosition + "px";
  494.         } else {
  495.             this._windowSelector.style.left = this._startPosition + "px";
  496.             this._windowSelector.style.right = this._width - position + "px";
  497.         }
  498.     }
  499. }
  500.  
  501.