1// Copyright 2021 the V8 project 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 5import {App} from '../index.mjs' 6 7import {FocusEvent, SelectRelatedEvent} from './events.mjs'; 8import {DOM, entriesEquals, ExpandableText, V8CustomElement} from './helper.mjs'; 9 10DOM.defineCustomElement('view/property-link-table', 11 template => 12 class PropertyLinkTable extends V8CustomElement { 13 _object; 14 _propertyDict; 15 _instanceLinkButtons = false; 16 17 _showHandler = this._handleShow.bind(this); 18 _showSourcePositionHandler = this._handleShowSourcePosition.bind(this); 19 _showRelatedHandler = this._handleShowRelated.bind(this); 20 _arrayValueSelectHandler = this._handleArrayValueSelect.bind(this); 21 22 constructor() { 23 super(template); 24 } 25 26 set instanceLinkButtons(newValue) { 27 this._instanceLinkButtons = newValue; 28 } 29 30 set propertyDict(propertyDict) { 31 if (entriesEquals(this._propertyDict, propertyDict)) return; 32 if (typeof propertyDict !== 'object') { 33 throw new Error( 34 `Invalid property dict, expected object: ${propertyDict}`); 35 } 36 this._propertyDict = propertyDict; 37 this.requestUpdate(); 38 } 39 40 _update() { 41 this._fragment = new DocumentFragment(); 42 this._table = DOM.table(); 43 for (let key in this._propertyDict) { 44 const value = this._propertyDict[key]; 45 this._addKeyValue(key, value); 46 } 47 48 const tableDiv = DOM.div('properties'); 49 tableDiv.appendChild(this._table); 50 this._fragment.appendChild(tableDiv); 51 this._createFooter(); 52 53 const newContent = DOM.div(); 54 newContent.appendChild(this._fragment); 55 this.$('#content').replaceWith(newContent); 56 newContent.id = 'content'; 57 this._fragment = undefined; 58 } 59 60 _addKeyValue(key, value) { 61 if (key == 'title') { 62 this._addTitle(value); 63 return; 64 } 65 if (key == '__this__') { 66 this._object = value; 67 return; 68 } 69 const row = this._table.insertRow(); 70 row.insertCell().innerText = key; 71 const cell = row.insertCell(); 72 if (value == undefined) return; 73 if (Array.isArray(value)) { 74 cell.appendChild(this._addArrayValue(value)); 75 return; 76 } else if (App.isClickable(value)) { 77 cell.className = 'clickable'; 78 cell.onclick = this._showHandler; 79 cell.data = value; 80 } 81 if (value.isCode) { 82 cell.classList.add('code'); 83 } 84 new ExpandableText(cell, value.toString()); 85 } 86 87 _addArrayValue(array) { 88 if (array.length == 0) { 89 return DOM.text('empty'); 90 } else if (array.length > 200) { 91 return DOM.text(`${array.length} items`); 92 } 93 const select = DOM.element('select'); 94 select.onchange = this._arrayValueSelectHandler; 95 for (let value of array) { 96 const option = DOM.element('option'); 97 option.innerText = value === undefined ? '' : value.toString(); 98 option.data = value; 99 select.add(option); 100 } 101 return select; 102 } 103 104 _addTitle(value) { 105 const title = DOM.element('h3'); 106 title.innerText = value; 107 this._fragment.appendChild(title); 108 } 109 110 _createFooter() { 111 if (this._object === undefined) return; 112 if (!this._instanceLinkButtons) return; 113 const footer = DOM.div('footer'); 114 let showButton = footer.appendChild(DOM.button('Show', this._showHandler)); 115 showButton.data = this._object; 116 if (this._object.sourcePosition) { 117 let showSourcePositionButton = footer.appendChild( 118 DOM.button('Source Position', this._showSourcePositionHandler)); 119 showSourcePositionButton.data = this._object; 120 } 121 let showRelatedButton = footer.appendChild( 122 DOM.button('Show Related', this._showRelatedHandler)); 123 showRelatedButton.data = this._object; 124 this._fragment.appendChild(footer); 125 } 126 127 _handleArrayValueSelect(event) { 128 const logEntry = event.currentTarget.selectedOptions[0].data; 129 this.dispatchEvent(new FocusEvent(logEntry)); 130 } 131 132 _handleShow(event) { 133 this.dispatchEvent(new FocusEvent(event.currentTarget.data)); 134 } 135 136 _handleShowSourcePosition(event) { 137 this.dispatchEvent(new FocusEvent(event.currentTarget.data.sourcePosition)); 138 } 139 140 _handleShowRelated(event) { 141 this.dispatchEvent(new SelectRelatedEvent(event.currentTarget.data)); 142 } 143}); 144