1/* 2 * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. 3 * Copyright (C) 2013 Samsung Electronics. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24 * THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27WebInspector.DOMStorageContentView = function(representedObject) 28{ 29 WebInspector.ContentView.call(this, representedObject); 30 31 this.element.classList.add(WebInspector.DOMStorageContentView.StyleClassName); 32 33 representedObject.addEventListener(WebInspector.DOMStorageObject.Event.ItemsCleared, this.itemsCleared, this); 34 representedObject.addEventListener(WebInspector.DOMStorageObject.Event.ItemAdded, this.itemAdded, this); 35 representedObject.addEventListener(WebInspector.DOMStorageObject.Event.ItemRemoved, this.itemRemoved, this); 36 representedObject.addEventListener(WebInspector.DOMStorageObject.Event.ItemUpdated, this.itemUpdated, this); 37 38 this.reset(); 39}; 40 41WebInspector.DOMStorageContentView.StyleClassName = "dom-storage"; 42WebInspector.DOMStorageContentView.DuplicateKeyStyleClassName = "duplicate-key"; 43WebInspector.DOMStorageContentView.MissingKeyStyleClassName = "missing-key"; 44WebInspector.DOMStorageContentView.MissingValueStyleClassName = "missing-value"; 45 46 47WebInspector.DOMStorageContentView.prototype = { 48 constructor: WebInspector.DOMStorageContentView, 49 __proto__: WebInspector.ContentView.prototype, 50 51 // Public 52 53 reset: function() 54 { 55 this.representedObject.getEntries(function(error, entries) { 56 if (error) 57 return; 58 59 if (!this._dataGrid) { 60 var columns = {}; 61 columns.key = {title: WebInspector.UIString("Key"), sortable: true}; 62 columns.value = {title: WebInspector.UIString("Value"), sortable: true}; 63 64 this._dataGrid = new WebInspector.DataGrid(columns, this._editingCallback.bind(this), this._deleteCallback.bind(this)); 65 this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SortChanged, this._sortDataGrid, this); 66 67 this.element.appendChild(this._dataGrid.element); 68 } 69 70 console.assert(this._dataGrid); 71 72 var nodes = []; 73 for (var entry of entries) { 74 if (!entry[0] || !entry[1]) 75 continue; 76 var data = {key: entry[0], value: entry[1]}; 77 var node = new WebInspector.DataGridNode(data, false); 78 node.selectable = true; 79 this._dataGrid.appendChild(node); 80 } 81 82 this._sortDataGrid(); 83 this._dataGrid.addPlaceholderNode(); 84 this._dataGrid.updateLayout(); 85 }.bind(this)); 86 }, 87 88 saveToCookie: function(cookie) 89 { 90 cookie.type = WebInspector.ContentViewCookieType.DOMStorage; 91 cookie.isLocalStorage = this.representedObject.isLocalStorage(); 92 cookie.host = this.representedObject.host; 93 }, 94 95 itemsCleared: function(event) 96 { 97 this._dataGrid.removeChildren(); 98 this._dataGrid.addPlaceholderNode(); 99 }, 100 101 itemRemoved: function(event) 102 { 103 for (var node of this._dataGrid.children) { 104 if (node.data.key === event.data.key) 105 return this._dataGrid.removeChild(node); 106 } 107 }, 108 109 itemAdded: function(event) 110 { 111 var key = event.data.key; 112 var value = event.data.value; 113 114 // Enforce key uniqueness. 115 for (var node of this._dataGrid.children) { 116 if (node.data.key === key) 117 return; 118 } 119 120 var data = {key: key, value: value}; 121 this._dataGrid.appendChild(new WebInspector.DataGridNode(data, false)); 122 this._sortDataGrid(); 123 }, 124 125 itemUpdated: function(event) 126 { 127 var key = event.data.key; 128 var value = event.data.value; 129 130 var keyFound = false; 131 for (var childNode of this._dataGrid.children) { 132 if (childNode.data.key === key) { 133 // Remove any rows that are now duplicates. 134 if (keyFound) { 135 this._dataGrid.removeChild(childNode); 136 continue; 137 } 138 139 keyFound = true; 140 childNode.data.value = value; 141 childNode.refresh(); 142 } 143 } 144 this._sortDataGrid(); 145 }, 146 147 updateLayout: function() 148 { 149 if (this._dataGrid) 150 this._dataGrid.updateLayout(); 151 }, 152 153 get scrollableElements() 154 { 155 if (!this._dataGrid) 156 return []; 157 return [this._dataGrid.scrollContainer]; 158 }, 159 160 // Private 161 162 _sortDataGrid: function() 163 { 164 if (!this._dataGrid.sortOrder) 165 return; 166 167 var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier || "key"; 168 169 function comparator(a, b) 170 { 171 return b.data[sortColumnIdentifier].localeCompare(a.data[sortColumnIdentifier]); 172 } 173 174 this._dataGrid.sortNodes(comparator, this._dataGrid.sortOrder); 175 }, 176 177 _deleteCallback: function(node) 178 { 179 if (!node || node.isPlaceholderNode) 180 return; 181 182 this._dataGrid.removeChild(node); 183 this.representedObject.removeItem(node.data["key"]); 184 }, 185 186 _editingCallback: function(editingNode, columnIdentifier, oldText, newText, moveDirection) 187 { 188 var key = editingNode.data["key"].trim(); 189 var value = editingNode.data["value"].trim(); 190 var previousValue = oldText.trim(); 191 var enteredValue = newText.trim(); 192 var columnIndex = this._dataGrid.orderedColumns.indexOf(columnIdentifier); 193 var mayMoveToNextRow = moveDirection === "forward" && columnIndex == this._dataGrid.orderedColumns.length - 1; 194 var mayMoveToPreviousRow = moveDirection === "backward" && columnIndex == 0; 195 var willMoveRow = mayMoveToNextRow || mayMoveToPreviousRow; 196 var shouldCommitRow = willMoveRow && key.length && value.length; 197 198 // Remove the row if its values are newly cleared, and it's not a placeholder. 199 if (!key.length && !value.length && willMoveRow) { 200 if (previousValue.length && !editingNode.isPlaceholderNode) 201 this._dataGrid.removeChild(editingNode); 202 return; 203 } 204 205 // If the key field was deleted, restore it when committing the row. 206 if (key === enteredValue && !key.length) { 207 if (willMoveRow && !editingNode.isPlaceholderNode) { 208 editingNode.data.key = previousValue; 209 editingNode.refresh(); 210 } else 211 editingNode.element.classList.add(WebInspector.DOMStorageContentView.MissingKeyStyleClassName); 212 } else if (key.length) { 213 editingNode.element.classList.remove(WebInspector.DOMStorageContentView.MissingKeyStyleClassName); 214 editingNode.__previousKeyValue = previousValue; 215 } 216 217 if (value === enteredValue && !value.length) 218 editingNode.element.classList.add(WebInspector.DOMStorageContentView.MissingValueStyleClassName); 219 else 220 editingNode.element.classList.remove(WebInspector.DOMStorageContentView.MissingValueStyleClassName); 221 222 if (editingNode.isPlaceholderNode && previousValue !== enteredValue) 223 this._dataGrid.addPlaceholderNode(); 224 225 if (!shouldCommitRow) 226 return; // One of the inputs is missing, or we aren't moving between rows. 227 228 var domStorage = this.representedObject; 229 if (domStorage.entries.has(key)) { 230 editingNode.element.classList.add(WebInspector.DOMStorageContentView.DuplicateKeyStyleClassName); 231 return; 232 } 233 234 editingNode.element.classList.remove(WebInspector.DOMStorageContentView.DuplicateKeySyleClassName); 235 236 if (editingNode.__previousKeyValue != key) 237 domStorage.removeItem(editingNode.__previousKeyValue); 238 239 domStorage.setItem(key, value); 240 // The table will be re-sorted when the backend fires the itemUpdated event. 241 } 242}; 243