Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / RevisionHistoryView.js
1 /*
2  * Copyright (C) 2012 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 /**
32  * @constructor
33  * @extends {WebInspector.VBox}
34  */
35 WebInspector.RevisionHistoryView = function()
36 {
37     WebInspector.VBox.call(this);
38     this.registerRequiredCSS("revisionHistory.css");
39     this.element.classList.add("revision-history-drawer");
40     this.element.classList.add("outline-disclosure");
41     this._uiSourceCodeItems = new Map();
42
43     var olElement = this.element.createChild("ol");
44     this._treeOutline = new TreeOutline(olElement);
45
46     /**
47      * @param {!WebInspector.UISourceCode} uiSourceCode
48      * @this {WebInspector.RevisionHistoryView}
49      */
50     function populateRevisions(uiSourceCode)
51     {
52         if (uiSourceCode.history.length)
53             this._createUISourceCodeItem(uiSourceCode);
54     }
55
56     WebInspector.workspace.uiSourceCodes().forEach(populateRevisions.bind(this));
57     WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeContentCommitted, this._revisionAdded, this);
58     WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
59     WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._projectWillReset, this);
60 }
61
62 /**
63  * @param {!WebInspector.UISourceCode} uiSourceCode
64  */
65 WebInspector.RevisionHistoryView.showHistory = function(uiSourceCode)
66 {
67     if (!WebInspector.RevisionHistoryView._view) 
68         WebInspector.RevisionHistoryView._view = new WebInspector.RevisionHistoryView();
69     var view = WebInspector.RevisionHistoryView._view;
70     WebInspector.inspectorView.showCloseableViewInDrawer("history", WebInspector.UIString("History"), view);
71     view._revealUISourceCode(uiSourceCode);
72 }
73
74 WebInspector.RevisionHistoryView.prototype = {
75     /**
76      * @param {!WebInspector.UISourceCode} uiSourceCode
77      */
78     _createUISourceCodeItem: function(uiSourceCode)
79     {
80         var uiSourceCodeItem = new TreeElement(uiSourceCode.displayName(), null, true);
81         uiSourceCodeItem.selectable = false;
82
83         // Insert in sorted order
84         for (var i = 0; i < this._treeOutline.children.length; ++i) {
85             if (this._treeOutline.children[i].title.localeCompare(uiSourceCode.displayName()) > 0) {
86                 this._treeOutline.insertChild(uiSourceCodeItem, i);
87                 break;
88             }
89         }
90         if (i === this._treeOutline.children.length)
91             this._treeOutline.appendChild(uiSourceCodeItem);
92
93         this._uiSourceCodeItems.put(uiSourceCode, uiSourceCodeItem);
94
95         var revisionCount = uiSourceCode.history.length;
96         for (var i = revisionCount - 1; i >= 0; --i) {
97             var revision = uiSourceCode.history[i];
98             var historyItem = new WebInspector.RevisionHistoryTreeElement(revision, uiSourceCode.history[i - 1], i !== revisionCount - 1);
99             uiSourceCodeItem.appendChild(historyItem);
100         }
101
102         var linkItem = new TreeElement("", null, false);
103         linkItem.selectable = false;
104         uiSourceCodeItem.appendChild(linkItem);
105
106         var revertToOriginal = linkItem.listItemElement.createChild("span", "revision-history-link revision-history-link-row");
107         revertToOriginal.textContent = WebInspector.UIString("apply original content");
108         revertToOriginal.addEventListener("click", uiSourceCode.revertToOriginal.bind(uiSourceCode));
109
110         var clearHistoryElement = uiSourceCodeItem.listItemElement.createChild("span", "revision-history-link");
111         clearHistoryElement.textContent = WebInspector.UIString("revert");
112         clearHistoryElement.addEventListener("click", this._clearHistory.bind(this, uiSourceCode));
113         return uiSourceCodeItem;
114     },
115
116     /**
117      * @param {!WebInspector.UISourceCode} uiSourceCode
118      */
119     _clearHistory: function(uiSourceCode)
120     {
121         uiSourceCode.revertAndClearHistory(this._removeUISourceCode.bind(this));
122     },
123
124     _revisionAdded: function(event)
125     {
126         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data.uiSourceCode);
127         var uiSourceCodeItem = this._uiSourceCodeItems.get(uiSourceCode);
128         if (!uiSourceCodeItem) {
129             uiSourceCodeItem = this._createUISourceCodeItem(uiSourceCode);
130             return;
131         }
132
133         var historyLength = uiSourceCode.history.length;
134         var historyItem = new WebInspector.RevisionHistoryTreeElement(uiSourceCode.history[historyLength - 1], uiSourceCode.history[historyLength - 2], false);
135         if (uiSourceCodeItem.children.length)
136             uiSourceCodeItem.children[0].allowRevert();
137         uiSourceCodeItem.insertChild(historyItem, 0);
138     },
139
140     /**
141      * @param {!WebInspector.UISourceCode} uiSourceCode
142      */
143     _revealUISourceCode: function(uiSourceCode)
144     {
145         var uiSourceCodeItem = this._uiSourceCodeItems.get(uiSourceCode);
146         if (uiSourceCodeItem) {
147             uiSourceCodeItem.reveal();
148             uiSourceCodeItem.expand();
149         }
150     },
151
152     _uiSourceCodeRemoved: function(event)
153     {
154         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
155         this._removeUISourceCode(uiSourceCode);
156     },
157
158     /**
159      * @param {!WebInspector.UISourceCode} uiSourceCode
160      */
161     _removeUISourceCode: function(uiSourceCode)
162     {
163         var uiSourceCodeItem = this._uiSourceCodeItems.get(uiSourceCode);
164         if (!uiSourceCodeItem)
165             return;
166         this._treeOutline.removeChild(uiSourceCodeItem);
167         this._uiSourceCodeItems.remove(uiSourceCode);
168     },
169
170     _projectWillReset: function(event)
171     {
172         var project = event.data;
173         project.uiSourceCodes().forEach(this._removeUISourceCode.bind(this));
174     },
175
176     __proto__: WebInspector.VBox.prototype
177 }
178
179 /**
180  * @constructor
181  * @extends {TreeElement}
182  * @param {!WebInspector.Revision} revision
183  * @param {!WebInspector.Revision} baseRevision
184  * @param {boolean} allowRevert
185  */
186 WebInspector.RevisionHistoryTreeElement = function(revision, baseRevision, allowRevert)
187 {
188     TreeElement.call(this, revision.timestamp.toLocaleTimeString(), null, true);
189     this.selectable = false;
190
191     this._revision = revision;
192     this._baseRevision = baseRevision;
193
194     this._revertElement = document.createElement("span");
195     this._revertElement.className = "revision-history-link";
196     this._revertElement.textContent = WebInspector.UIString("apply revision content");
197     this._revertElement.addEventListener("click", this._revision.revertToThis.bind(this._revision), false);
198     if (!allowRevert)
199         this._revertElement.classList.add("hidden");
200 }
201
202 WebInspector.RevisionHistoryTreeElement.prototype = {
203     onattach: function()
204     {
205         this.listItemElement.classList.add("revision-history-revision");
206     },
207
208     onexpand: function()
209     {
210         this.listItemElement.appendChild(this._revertElement);
211
212         if (this._wasExpandedOnce)
213             return;
214         this._wasExpandedOnce = true;
215
216         this.childrenListElement.classList.add("source-code");
217         if (this._baseRevision)
218             this._baseRevision.requestContent(step1.bind(this));
219         else
220             this._revision.uiSourceCode.requestOriginalContent(step1.bind(this));
221
222         /**
223          * @param {?string} baseContent
224          * @this {WebInspector.RevisionHistoryTreeElement}
225          */
226         function step1(baseContent)
227         {
228             this._revision.requestContent(step2.bind(this, baseContent));
229         }
230
231         /**
232          * @param {?string} baseContent
233          * @param {?string} newContent
234          * @this {WebInspector.RevisionHistoryTreeElement}
235          */
236         function step2(baseContent, newContent)
237         {
238             var baseLines = difflib.stringAsLines(baseContent);
239             var newLines = difflib.stringAsLines(newContent);
240             var sm = new difflib.SequenceMatcher(baseLines, newLines);
241             var opcodes = sm.get_opcodes();
242             var lastWasSeparator = false;
243
244             for (var idx = 0; idx < opcodes.length; idx++) {
245                 var code = opcodes[idx];
246                 var change = code[0];
247                 var b = code[1];
248                 var be = code[2];
249                 var n = code[3];
250                 var ne = code[4];
251                 var rowCount = Math.max(be - b, ne - n);
252                 var topRows = [];
253                 var bottomRows = [];
254                 for (var i = 0; i < rowCount; i++) {
255                     if (change === "delete" || (change === "replace" && b < be)) {
256                         var lineNumber = b++;
257                         this._createLine(lineNumber, null, baseLines[lineNumber], "removed");
258                         lastWasSeparator = false;
259                     }
260
261                     if (change === "insert" || (change === "replace" && n < ne)) {
262                         var lineNumber = n++;
263                         this._createLine(null, lineNumber, newLines[lineNumber], "added");
264                         lastWasSeparator = false;
265                     }
266
267                     if (change === "equal") {
268                         b++;
269                         n++;
270                         if (!lastWasSeparator)
271                             this._createLine(null, null, "    \u2026", "separator");
272                         lastWasSeparator = true;
273                     }
274                 }
275             }
276         }
277     },
278
279     oncollapse: function()
280     {
281         this._revertElement.remove();
282     },
283
284     /**
285      * @param {?number} baseLineNumber
286      * @param {?number} newLineNumber
287      * @param {string} lineContent
288      * @param {string} changeType
289      */
290     _createLine: function(baseLineNumber, newLineNumber, lineContent, changeType)
291     {
292         var child = new TreeElement("", null, false);
293         child.selectable = false;
294         this.appendChild(child);
295         var lineElement = document.createElement("span");
296
297         function appendLineNumber(lineNumber)
298         {
299             var numberString = lineNumber !== null ? numberToStringWithSpacesPadding(lineNumber + 1, 4) : "    ";
300             var lineNumberSpan = document.createElement("span");
301             lineNumberSpan.classList.add("webkit-line-number");
302             lineNumberSpan.textContent = numberString;
303             child.listItemElement.appendChild(lineNumberSpan);
304         }
305
306         appendLineNumber(baseLineNumber);
307         appendLineNumber(newLineNumber);
308
309         var contentSpan = document.createElement("span");
310         contentSpan.textContent = lineContent;
311         child.listItemElement.appendChild(contentSpan);
312         child.listItemElement.classList.add("revision-history-line");
313         child.listItemElement.classList.add("revision-history-line-" + changeType);
314     },
315
316     allowRevert: function()
317     {
318         this._revertElement.classList.remove("hidden");
319     },
320
321     __proto__: TreeElement.prototype
322 }