 5313a88c55
			
		
	
	5313a88c55
	
	
	
		
			
			This reverts commit 0f8964bb62.
Reason for revert: Still needs more places to have undefined checks.
Notably this breaks the diff screen.
Change-Id: Ifeecb2272144a688c96e63a942b221482d4eb69d
		
	
		
			
				
	
	
		
			210 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * @license
 | |
|  * Copyright (C) 2016 The Android Open Source Project
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  * http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| (function() {
 | |
|   'use strict';
 | |
| 
 | |
|   // Polymer 1 adds # before array's key, while Polymer 2 doesn't
 | |
|   const HOVER_PATH_PATTERN = /^(commentRanges\.\#?\d+)\.hovering$/;
 | |
| 
 | |
|   const RANGE_HIGHLIGHT = 'style-scope gr-diff range';
 | |
|   const HOVER_HIGHLIGHT = 'style-scope gr-diff rangeHighlight';
 | |
| 
 | |
|   /** @typedef {{side: string, range: Gerrit.Range, hovering: boolean}} */
 | |
|   Gerrit.HoveredRange;
 | |
| 
 | |
|   Polymer({
 | |
|     is: 'gr-ranged-comment-layer',
 | |
|     _legacyUndefinedCheck: true,
 | |
| 
 | |
|     /**
 | |
|      * Fired when the range in a range comment was malformed and had to be
 | |
|      * normalized.
 | |
|      *
 | |
|      * It's `detail` has a `lineNum` and `side` parameter.
 | |
|      *
 | |
|      * @event normalize-range
 | |
|      */
 | |
| 
 | |
|     properties: {
 | |
|       /** @type {!Array<!Gerrit.HoveredRange>} */
 | |
|       commentRanges: Array,
 | |
|       _listeners: {
 | |
|         type: Array,
 | |
|         value() { return []; },
 | |
|       },
 | |
|       _rangesMap: {
 | |
|         type: Object,
 | |
|         value() { return {left: {}, right: {}}; },
 | |
|       },
 | |
|     },
 | |
| 
 | |
|     observers: [
 | |
|       '_handleCommentRangesChange(commentRanges.*)',
 | |
|     ],
 | |
| 
 | |
|     get styleModuleName() {
 | |
|       return 'gr-ranged-comment-styles';
 | |
|     },
 | |
| 
 | |
|     /**
 | |
|      * Layer method to add annotations to a line.
 | |
|      * @param {!HTMLElement} el The DIV.contentText element to apply the
 | |
|      *     annotation to.
 | |
|      * @param {!HTMLElement} lineNumberEl
 | |
|      * @param {!Object} line The line object. (GrDiffLine)
 | |
|      */
 | |
|     annotate(el, lineNumberEl, line) {
 | |
|       let ranges = [];
 | |
|       if (line.type === GrDiffLine.Type.REMOVE || (
 | |
|           line.type === GrDiffLine.Type.BOTH &&
 | |
|           el.getAttribute('data-side') !== 'right')) {
 | |
|         ranges = ranges.concat(this._getRangesForLine(line, 'left'));
 | |
|       }
 | |
|       if (line.type === GrDiffLine.Type.ADD || (
 | |
|           line.type === GrDiffLine.Type.BOTH &&
 | |
|           el.getAttribute('data-side') !== 'left')) {
 | |
|         ranges = ranges.concat(this._getRangesForLine(line, 'right'));
 | |
|       }
 | |
| 
 | |
|       for (const range of ranges) {
 | |
|         GrAnnotation.annotateElement(el, range.start,
 | |
|             range.end - range.start,
 | |
|             range.hovering ? HOVER_HIGHLIGHT : RANGE_HIGHLIGHT);
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     /**
 | |
|      * Register a listener for layer updates.
 | |
|      * @param {function(number, number, string)} fn The update handler function.
 | |
|      *     Should accept as arguments the line numbers for the start and end of
 | |
|      *     the update and the side as a string.
 | |
|      */
 | |
|     addListener(fn) {
 | |
|       this._listeners.push(fn);
 | |
|     },
 | |
| 
 | |
|     /**
 | |
|      * Notify Layer listeners of changes to annotations.
 | |
|      * @param {number} start The line where the update starts.
 | |
|      * @param {number} end The line where the update ends.
 | |
|      * @param {string} side The side of the update. ('left' or 'right')
 | |
|      */
 | |
|     _notifyUpdateRange(start, end, side) {
 | |
|       for (const listener of this._listeners) {
 | |
|         listener(start, end, side);
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     /**
 | |
|      * Handle change in the ranges by updating the ranges maps and by
 | |
|      * emitting appropriate update notifications.
 | |
|      * @param {Object} record The change record.
 | |
|      */
 | |
|     _handleCommentRangesChange(record) {
 | |
|       if (!record) return;
 | |
| 
 | |
|       // If the entire set of comments was changed.
 | |
|       if (record.path === 'commentRanges') {
 | |
|         this._rangesMap = {left: {}, right: {}};
 | |
|         for (const {side, range, hovering} of record.value) {
 | |
|           this._updateRangesMap(
 | |
|               side, range, hovering, (forLine, start, end, hovering) => {
 | |
|                 forLine.push({start, end, hovering});
 | |
|               });
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // If the change only changed the `hovering` property of a comment.
 | |
|       const match = record.path.match(HOVER_PATH_PATTERN);
 | |
|       if (match) {
 | |
|         // The #number indicates the key of that item in the array
 | |
|         // not the index, especially in polymer 1.
 | |
|         const {side, range, hovering} = this.get(match[1]);
 | |
| 
 | |
|         this._updateRangesMap(
 | |
|             side, range, hovering, (forLine, start, end, hovering) => {
 | |
|               const index = forLine.findIndex(lineRange =>
 | |
|                   lineRange.start === start && lineRange.end === end);
 | |
|               forLine[index].hovering = hovering;
 | |
|             });
 | |
|       }
 | |
| 
 | |
|       // If comments were spliced in or out.
 | |
|       if (record.path === 'commentRanges.splices') {
 | |
|         for (const indexSplice of record.value.indexSplices) {
 | |
|           const removed = indexSplice.removed;
 | |
|           for (const {side, range, hovering} of removed) {
 | |
|             this._updateRangesMap(
 | |
|                 side, range, hovering, (forLine, start, end) => {
 | |
|                   const index = forLine.findIndex(lineRange =>
 | |
|                       lineRange.start === start && lineRange.end === end);
 | |
|                   forLine.splice(index, 1);
 | |
|                 });
 | |
|           }
 | |
|           const added = indexSplice.object.slice(
 | |
|               indexSplice.index, indexSplice.index + indexSplice.addedCount);
 | |
|           for (const {side, range, hovering} of added) {
 | |
|             this._updateRangesMap(
 | |
|                 side, range, hovering, (forLine, start, end, hovering) => {
 | |
|                   forLine.push({start, end, hovering});
 | |
|                 });
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     _updateRangesMap(side, range, hovering, operation) {
 | |
|       const forSide = this._rangesMap[side] || (this._rangesMap[side] = {});
 | |
|       for (let line = range.start_line; line <= range.end_line; line++) {
 | |
|         const forLine = forSide[line] || (forSide[line] = []);
 | |
|         const start = line === range.start_line ? range.start_character : 0;
 | |
|         const end = line === range.end_line ? range.end_character : -1;
 | |
|         operation(forLine, start, end, hovering);
 | |
|       }
 | |
|       this._notifyUpdateRange(range.start_line, range.end_line, side);
 | |
|     },
 | |
| 
 | |
|     _getRangesForLine(line, side) {
 | |
|       const lineNum = side === 'left' ? line.beforeNumber : line.afterNumber;
 | |
|       const ranges = this.get(['_rangesMap', side, lineNum]) || [];
 | |
|       return ranges
 | |
|           .map(range => {
 | |
|             // Make a copy, so that the normalization below does not mess with
 | |
|             // our map.
 | |
|             range = Object.assign({}, range);
 | |
|             range.end = range.end === -1 ? line.text.length : range.end;
 | |
| 
 | |
|             // Normalize invalid ranges where the start is after the end but the
 | |
|             // start still makes sense. Set the end to the end of the line.
 | |
|             // @see Issue 5744
 | |
|             if (range.start >= range.end && range.start < line.text.length) {
 | |
|               range.end = line.text.length;
 | |
|               this.dispatchEvent(new CustomEvent('normalize-range', {
 | |
|                 bubbles: true,
 | |
|                 composed: true,
 | |
|                 detail: {lineNum, side},
 | |
|               }));
 | |
|             }
 | |
| 
 | |
|             return range;
 | |
|           })
 | |
|           // Sort the ranges so that hovering highlights are on top.
 | |
|           .sort((a, b) => a.hovering && !b.hovering ? 1 : 0);
 | |
|     },
 | |
|   });
 | |
| })();
 |