Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sources / 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("sources/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.ProjectRemoved, this._projectRemoved, 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.set(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", this._revertToOriginal.bind(this, 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     _revertToOriginal: function(uiSourceCode)
120     {
121         uiSourceCode.revertToOriginal();
122
123         WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
124             action: WebInspector.UserMetrics.UserActionNames.ApplyOriginalContent,
125             url: uiSourceCode.url
126         });
127     },
128
129     /**
130      * @param {!WebInspector.UISourceCode} uiSourceCode
131      */
132     _clearHistory: function(uiSourceCode)
133     {
134         uiSourceCode.revertAndClearHistory(this._removeUISourceCode.bind(this));
135
136         WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
137             action: WebInspector.UserMetrics.UserActionNames.RevertRevision,
138             url: uiSourceCode.url
139         });
140     },
141
142     _revisionAdded: function(event)
143     {
144         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data.uiSourceCode);
145         var uiSourceCodeItem = this._uiSourceCodeItems.get(uiSourceCode);
146         if (!uiSourceCodeItem) {
147             uiSourceCodeItem = this._createUISourceCodeItem(uiSourceCode);
148             return;
149         }
150
151         var historyLength = uiSourceCode.history.length;
152         var historyItem = new WebInspector.RevisionHistoryTreeElement(uiSourceCode.history[historyLength - 1], uiSourceCode.history[historyLength - 2], false);
153         if (uiSourceCodeItem.children.length)
154             uiSourceCodeItem.children[0].allowRevert();
155         uiSourceCodeItem.insertChild(historyItem, 0);
156     },
157
158     /**
159      * @param {!WebInspector.UISourceCode} uiSourceCode
160      */
161     _revealUISourceCode: function(uiSourceCode)
162     {
163         var uiSourceCodeItem = this._uiSourceCodeItems.get(uiSourceCode);
164         if (uiSourceCodeItem) {
165             uiSourceCodeItem.reveal();
166             uiSourceCodeItem.expand();
167         }
168     },
169
170     _uiSourceCodeRemoved: function(event)
171     {
172         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
173         this._removeUISourceCode(uiSourceCode);
174     },
175
176     /**
177      * @param {!WebInspector.UISourceCode} uiSourceCode
178      */
179     _removeUISourceCode: function(uiSourceCode)
180     {
181         var uiSourceCodeItem = this._uiSourceCodeItems.get(uiSourceCode);
182         if (!uiSourceCodeItem)
183             return;
184         this._treeOutline.removeChild(uiSourceCodeItem);
185         this._uiSourceCodeItems.remove(uiSourceCode);
186     },
187
188     _projectRemoved: function(event)
189     {
190         var project = event.data;
191         project.uiSourceCodes().forEach(this._removeUISourceCode.bind(this));
192     },
193
194     __proto__: WebInspector.VBox.prototype
195 }
196
197 /**
198  * @constructor
199  * @extends {TreeElement}
200  * @param {!WebInspector.Revision} revision
201  * @param {!WebInspector.Revision} baseRevision
202  * @param {boolean} allowRevert
203  */
204 WebInspector.RevisionHistoryTreeElement = function(revision, baseRevision, allowRevert)
205 {
206     TreeElement.call(this, revision.timestamp.toLocaleTimeString(), null, true);
207     this.selectable = false;
208
209     this._revision = revision;
210     this._baseRevision = baseRevision;
211
212     this._revertElement = createElement("span");
213     this._revertElement.className = "revision-history-link";
214     this._revertElement.textContent = WebInspector.UIString("apply revision content");
215     this._revertElement.addEventListener("click", this._revision.revertToThis.bind(this._revision), false);
216     if (!allowRevert)
217         this._revertElement.classList.add("hidden");
218 }
219
220 WebInspector.RevisionHistoryTreeElement.prototype = {
221     onattach: function()
222     {
223         this.listItemElement.classList.add("revision-history-revision");
224     },
225
226     onexpand: function()
227     {
228         this.listItemElement.appendChild(this._revertElement);
229
230         if (this._wasExpandedOnce)
231             return;
232         this._wasExpandedOnce = true;
233
234         this.childrenListElement.classList.add("source-code");
235         if (this._baseRevision)
236             this._baseRevision.requestContent(step1.bind(this));
237         else
238             this._revision.uiSourceCode.requestOriginalContent(step1.bind(this));
239
240         /**
241          * @param {?string} baseContent
242          * @this {WebInspector.RevisionHistoryTreeElement}
243          */
244         function step1(baseContent)
245         {
246             this._revision.requestContent(step2.bind(this, baseContent));
247         }
248
249         /**
250          * @param {?string} baseContent
251          * @param {?string} newContent
252          * @this {WebInspector.RevisionHistoryTreeElement}
253          */
254         function step2(baseContent, newContent)
255         {
256             var baseLines = difflib.stringAsLines(baseContent);
257             var newLines = difflib.stringAsLines(newContent);
258             var sm = new difflib.SequenceMatcher(baseLines, newLines);
259             var opcodes = sm.get_opcodes();
260             var lastWasSeparator = false;
261
262             for (var idx = 0; idx < opcodes.length; idx++) {
263                 var code = opcodes[idx];
264                 var change = code[0];
265                 var b = code[1];
266                 var be = code[2];
267                 var n = code[3];
268                 var ne = code[4];
269                 var rowCount = Math.max(be - b, ne - n);
270                 var topRows = [];
271                 var bottomRows = [];
272                 for (var i = 0; i < rowCount; i++) {
273                     if (change === "delete" || (change === "replace" && b < be)) {
274                         var lineNumber = b++;
275                         this._createLine(lineNumber, null, baseLines[lineNumber], "removed");
276                         lastWasSeparator = false;
277                     }
278
279                     if (change === "insert" || (change === "replace" && n < ne)) {
280                         var lineNumber = n++;
281                         this._createLine(null, lineNumber, newLines[lineNumber], "added");
282                         lastWasSeparator = false;
283                     }
284
285                     if (change === "equal") {
286                         b++;
287                         n++;
288                         if (!lastWasSeparator)
289                             this._createLine(null, null, "    \u2026", "separator");
290                         lastWasSeparator = true;
291                     }
292                 }
293             }
294         }
295     },
296
297     oncollapse: function()
298     {
299         this._revertElement.remove();
300     },
301
302     /**
303      * @param {?number} baseLineNumber
304      * @param {?number} newLineNumber
305      * @param {string} lineContent
306      * @param {string} changeType
307      */
308     _createLine: function(baseLineNumber, newLineNumber, lineContent, changeType)
309     {
310         var child = new TreeElement("", null, false);
311         child.selectable = false;
312         this.appendChild(child);
313         var lineElement = createElement("span");
314
315         function appendLineNumber(lineNumber)
316         {
317             var numberString = lineNumber !== null ? numberToStringWithSpacesPadding(lineNumber + 1, 4) : spacesPadding(4);
318             var lineNumberSpan = createElement("span");
319             lineNumberSpan.classList.add("webkit-line-number");
320             lineNumberSpan.textContent = numberString;
321             child.listItemElement.appendChild(lineNumberSpan);
322         }
323
324         appendLineNumber(baseLineNumber);
325         appendLineNumber(newLineNumber);
326
327         var contentSpan = createElement("span");
328         contentSpan.textContent = lineContent;
329         child.listItemElement.appendChild(contentSpan);
330         child.listItemElement.classList.add("revision-history-line");
331         contentSpan.classList.add("revision-history-line-" + changeType);
332     },
333
334     allowRevert: function()
335     {
336         this._revertElement.classList.remove("hidden");
337     },
338
339     __proto__: TreeElement.prototype
340 }