Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / documentation / DocumentationView.js
1 // Copyright 2014 The Chromium 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
5 /**
6  * @constructor
7  * @extends {WebInspector.View}
8  */
9 WebInspector.DocumentationView = function()
10 {
11     WebInspector.View.call(this);
12     this.element.classList.add("documentation-view");
13     this.registerRequiredCSS("documentationView.css");
14 }
15
16 /**
17  * @param {string} url
18  * @param {string} searchItem
19  */
20 WebInspector.DocumentationView.showDocumentationURL = function(url, searchItem)
21 {
22     if (!WebInspector.DocumentationView._view)
23         WebInspector.DocumentationView._view = new WebInspector.DocumentationView();
24     var view = WebInspector.DocumentationView._view;
25     view.element.removeChildren();
26     WebInspector.inspectorView.showCloseableViewInDrawer("documentation", WebInspector.UIString("Documentation"), view);
27     view.showDocumentation(url, searchItem);
28 }
29
30 WebInspector.DocumentationView._languageToMimeType = {
31     "javascript": "text/javascript",
32     "html": "text/html"
33 };
34
35 WebInspector.DocumentationView.prototype = {
36     /**
37      * @param {string} url
38      * @param {string} searchItem
39      */
40     showDocumentation: function(url, searchItem)
41     {
42         if (!url) {
43             this._createEmptyPage();
44             return;
45         }
46         loadXHR(url)
47             .then(this._createArticle.bind(this, searchItem), this._createEmptyPage.bind(this))
48             .catch(this._createEmptyPage.bind(this));
49     },
50
51     /**
52      * @param {string} searchItem
53      * @param {string} responseText
54      */
55     _createArticle: function(searchItem, responseText)
56     {
57         var json = JSON.parse(responseText);
58         var pages = json["query"]["pages"];
59         var wikiKeys = Object.keys(pages);
60         if (wikiKeys.length === 1 && wikiKeys[0] === "-1") {
61             this._createEmptyPage();
62             return;
63         }
64         var wikiMarkupText = pages[wikiKeys[0]]["revisions"]["0"]["*"];
65         var article;
66         try {
67             article = WebInspector.JSArticle.parse(wikiMarkupText);
68         } catch (error) {
69             console.error("Article could not be parsed. " + error.message);
70         }
71         if (!article) {
72             this._createEmptyPage();
73             return;
74         }
75
76         this.element.removeChildren();
77         var renderer = new WebInspector.DocumentationView.Renderer(article, searchItem);
78         this.element.appendChild(renderer.renderJSArticle());
79     },
80
81     _createEmptyPage: function()
82     {
83         this.element.removeChildren();
84         var emptyPage = this.element.createChild("div", "documentation-empty-page fill");
85         var pageTitle = emptyPage.createChild("div", "documentation-not-found");
86         pageTitle.textContent = WebInspector.UIString("No documentation found.");
87         emptyPage.createChild("div", "documentation-empty-page-align");
88     },
89
90     __proto__: WebInspector.View.prototype
91 }
92
93 /**
94  * @constructor
95  * @param {!WebInspector.JSArticle} article
96  * @param {string} searchItem
97  */
98 WebInspector.DocumentationView.Renderer = function(article, searchItem)
99 {
100     this._searchItem = searchItem;
101     this._element = document.createElement("div");
102     this._article = article;
103 }
104
105 WebInspector.DocumentationView.Renderer.prototype = {
106     /**
107      * @return {!Element}
108      */
109     renderJSArticle: function()
110     {
111         this._element.appendChild(this._createPageTitle(this._article.pageTitle, this._searchItem));
112         var signatureElement = this._createSignatureSection(this._article.parameters, this._article.methods);
113         if (signatureElement)
114             this._element.appendChild(signatureElement);
115
116         var descriptionElement = this._element.createChild("div", "documentation-description");
117         var summarySection = this._article.summary ? this._renderBlock(this._article.summary) : null;
118         if (summarySection)
119             descriptionElement.appendChild(summarySection);
120         var parametersSection = this._createParametersSection(this._article.parameters);
121         if (parametersSection)
122             descriptionElement.appendChild(parametersSection);
123
124         var examplesSection = this._createExamplesSection(this._article.examples);
125         if (examplesSection) {
126             var examplesTitle = this._element.createChild("div", "documentation-title");
127             examplesTitle.textContent = WebInspector.UIString("Examples");
128             descriptionElement = this._element.createChild("div", "documentation-description");
129             descriptionElement.appendChild(examplesSection);
130         }
131
132         var remarksSection = this._article.remarks ? this._renderBlock(this._article.remarks) : null;
133         if (remarksSection) {
134             var remarksTitle = this._element.createChild("div", "documentation-title");
135             remarksTitle.textContent = WebInspector.UIString("Remarks");
136             descriptionElement = this._element.createChild("div", "documentation-description");
137             descriptionElement.appendChild(remarksSection);
138         }
139         return this._element;
140     },
141
142     /**
143      * @param {string} titleText
144      * @param {string} searchItem
145      * @return {!Element}
146      */
147     _createPageTitle: function(titleText, searchItem)
148     {
149         var pageTitle = document.createElementWithClass("div", "documentation-page-title");
150         if (titleText)
151             pageTitle.textContent = titleText;
152         else if (searchItem)
153             pageTitle.textContent = searchItem;
154         return pageTitle;
155     },
156
157     /**
158      * @param {!Array.<!WebInspector.JSArticle.Parameter>} parameters
159      * @param {?WebInspector.JSArticle.Method} method
160      * @return {?Element}
161      */
162     _createSignatureSection: function(parameters, method)
163     {
164         if (!parameters.length && !method)
165             return null;
166         var signature = document.createElementWithClass("div", "documentation-method-signature monospace");
167         if (method && method.returnValueName) {
168             var returnTypeElement = signature.createChild("span", "documentation-parameter-data-type-value");
169             returnTypeElement.textContent = method.returnValueName;
170         }
171         var methodName = signature.createChild("span", "documentation-method-name");
172         methodName.textContent = this._searchItem.split(".").peekLast() + "(";
173         for (var i = 0; i < parameters.length; ++i) {
174             if (i > 0)
175                 signature.createTextChild(",")
176             var parameterType = signature.createChild("span", "documentation-parameter-data-type-value");
177             parameterType.textContent = parameters[i].dataType;
178             var parameterName = signature.createChild("span", "documentation-parameter-name");
179             parameterName.textContent = parameters[i].name;
180         }
181
182         signature.createTextChild(")");
183         return signature;
184     },
185
186     /**
187      * @param {!Array.<!WebInspector.JSArticle.Parameter>} parameters
188      * @return {?Element}
189      */
190     _createParametersSection: function(parameters)
191     {
192         if (!parameters.length)
193             return null;
194         var table = document.createElementWithClass("table", "documentation-table");
195         var tableBody = table.createChild("tbody");
196         var headerRow = tableBody.createChild("tr", "documentation-table-row");
197         var tableHeader = headerRow.createChild("th", "documentation-table-header");
198         tableHeader.textContent = WebInspector.UIString("Parameters");
199         tableHeader.colSpan = 3;
200         for (var i = 0; i < parameters.length; ++i) {
201             var tableRow = tableBody.createChild("tr", "documentation-table-row");
202             var type = tableRow.createChild("td", "documentation-table-cell");
203             type.textContent = parameters[i].dataType;
204             var name = tableRow.createChild("td", "documentation-table-cell");
205             name.textContent = parameters[i].optional ? WebInspector.UIString("(optional)\n") : "";
206             name.textContent += parameters[i].name;
207             var description = tableRow.createChild("td", "documentation-table-cell");
208             if (parameters[i].description)
209                 description.appendChild(this._renderBlock(/** @type {!WebInspector.WikiParser.Block} */(parameters[i].description)));
210         }
211         return table;
212     },
213
214     /**
215      * @param {!Array.<!WebInspector.JSArticle.Example>} examples
216      */
217     _createExamplesSection: function(examples)
218     {
219         if (!examples.length)
220             return;
221
222         var section = document.createElementWithClass("div", "documentation-section");
223
224         for (var i = 0; i < examples.length; ++i) {
225             var example = section.createChild("div", "documentation-example");
226             var exampleDescription = example.createChild("div", "documentation-example-description-section");
227             if (examples[i].description) {
228                 var description = this._renderBlock(/** @type {!WebInspector.WikiParser.Block} */(examples[i].description));
229                 description.classList.add("documentation-text");
230                 exampleDescription.appendChild(description);
231             }
232             var code = example.createChild("div", "documentation-code source-code");
233             code.textContent = examples[i].code;
234             if (!examples[i].language)
235                 continue;
236             var syntaxHighlighter = new WebInspector.DOMSyntaxHighlighter(WebInspector.DocumentationView._languageToMimeType[examples[i].language.toLowerCase()], true);
237             syntaxHighlighter.syntaxHighlightNode(code);
238         }
239         return section;
240     },
241
242     /**
243      * @param {!WebInspector.WikiParser.ArticleElement} article
244      * @return {!Element}
245      */
246     _renderBlock: function(article)
247     {
248         var element;
249         var elementTypes = WebInspector.WikiParser.ArticleElement.Type;
250
251         switch (article.type()) {
252         case elementTypes.Inline:
253             element = document.createElement("span");
254             break;
255         case elementTypes.Link:
256             element = document.createElementWithClass("a", "documentation-link");
257             element.href = article.url();
258             if (!article.children().length)
259                 element.textContent = article.url();
260             break;
261         case elementTypes.Code:
262             element = document.createElementWithClass("span", "documentation-code-tag");
263             break;
264         case elementTypes.CodeBlock:
265             element = document.createElementWithClass("pre", "documentation-code source-code");
266             element.textContent = article.code();
267             break;
268         case elementTypes.PlainText:
269             element = document.createElement("span");
270             element.textContent = article.text();
271             if (article.isHighlighted())
272                 element.classList.add("documentation-highlighted-text");
273             break;
274         case elementTypes.Block:
275             element = document.createElement(article.hasBullet() ? "li" : "div");
276             if (!article.hasBullet())
277                 element.classList.add("documentation-paragraph");
278             break;
279         case elementTypes.Table:
280             return this._renderTable(/** @type {!WebInspector.WikiParser.Table} */(article));
281         default:
282             throw new Error("Unknown ArticleElement type " + article.type());
283         }
284
285         if (article.type() === WebInspector.WikiParser.ArticleElement.Type.Block
286             || article.type() === WebInspector.WikiParser.ArticleElement.Type.Code
287             || article.type() === WebInspector.WikiParser.ArticleElement.Type.Inline) {
288             for (var i = 0; i < article.children().length; ++i) {
289                 var child = this._renderBlock(article.children()[i]);
290                 if (child)
291                     element.appendChild(child);
292             }
293         }
294
295         return element;
296     },
297
298     /**
299      * @param {!WebInspector.WikiParser.Table} table
300      * @return {!Element}
301      */
302     _renderTable: function(table)
303     {
304         var tableElement = document.createElementWithClass("table", "documentation-table");
305         var tableBody = tableElement.createChild("tbody");
306         var headerRow = tableBody.createChild("tr", "documentation-table-row");
307         for (var i = 0; i < table.columnNames().length; ++i) {
308             var tableHeader = headerRow.createChild("th", "documentation-table-header");
309             tableHeader.appendChild(this._renderBlock(table.columnNames()[i]));
310         }
311         for (var i = 0; i < table.rows().length; ++i) {
312             var tableRow = tableBody.createChild("tr", "documentation-table-row");
313             var row = table.rows()[i];
314             for (var j = 0; j < row.length; ++j) {
315                 var cell = tableRow.createChild("td", "documentation-table-cell");
316                 cell.appendChild(this._renderBlock(row[j]));
317             }
318         }
319
320         return tableElement;
321     }
322 }
323
324 /**
325  * @constructor
326  * @implements {WebInspector.ContextMenu.Provider}
327  */
328 WebInspector.DocumentationView.ContextMenuProvider = function()
329 {
330 }
331
332 WebInspector.DocumentationView.ContextMenuProvider.prototype = {
333     /**
334      * @param {!Event} event
335      * @param {!WebInspector.ContextMenu} contextMenu
336      * @param {!Object} target
337      */
338     appendApplicableItems: function(event, contextMenu, target)
339     {
340         if (!(target instanceof WebInspector.CodeMirrorTextEditor))
341             return;
342         WebInspector.DocumentationCatalog.instance().startLoadingIfNeeded();
343         if (WebInspector.DocumentationCatalog.instance().isLoading()) {
344             var itemName = WebInspector.useLowerCaseMenuTitles() ? "Loading documentation..." : "Loading Documentation...";
345             contextMenu.appendItem(itemName, function() {}, true);
346             return;
347         }
348         var textEditor = /** @type {!WebInspector.CodeMirrorTextEditor} */ (target);
349         var descriptors = this._determineDescriptors(textEditor);
350         if (!descriptors.length)
351             return;
352         if (descriptors.length === 1) {
353             var formatString = WebInspector.useLowerCaseMenuTitles() ? "Show documentation for %s.%s" : "Show Documentation for %s.%s";
354             var methodName = String.sprintf("%s.%s", descriptors[0].name(), descriptors[0].searchItem());
355             contextMenu.appendItem(WebInspector.UIString(formatString, descriptors[0].name(), descriptors[0].searchItem()), WebInspector.DocumentationView.showDocumentationURL.bind(null, descriptors[0].url(), methodName));
356             return;
357         }
358         var subMenuItem = contextMenu.appendSubMenuItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Show documentation for..." : "Show Documentation for..."));
359         for (var i = 0; i < descriptors.length; ++i) {
360             var methodName = String.sprintf("%s.%s", descriptors[i].name(), descriptors[i].searchItem());
361             subMenuItem.appendItem(methodName, WebInspector.DocumentationView.showDocumentationURL.bind(null, descriptors[i].url(), methodName));
362         }
363     },
364
365     /**
366      * @param {!WebInspector.CodeMirrorTextEditor} textEditor
367      * @return {!Array.<!WebInspector.DocumentationCatalog.ItemDescriptor>}
368      */
369     _determineDescriptors: function(textEditor)
370     {
371         var catalog = WebInspector.DocumentationCatalog.instance();
372         var textSelection = textEditor.selection().normalize();
373         var previousTokenText = findPreviousToken(textSelection);
374
375         if (!textSelection.isEmpty()) {
376             if (textSelection.startLine !== textSelection.endLine)
377                 return [];
378             return computeDescriptors(textSelection);
379         }
380
381         var descriptors = computeDescriptors(getTokenRangeByColumn(textSelection.startColumn));
382         if (descriptors.length)
383             return descriptors;
384
385         return computeDescriptors(getTokenRangeByColumn(textSelection.startColumn - 1));
386
387         /**
388          * @param {number} column
389          * @return {?WebInspector.TextRange}
390          */
391         function getTokenRangeByColumn(column)
392         {
393             var token = textEditor.tokenAtTextPosition(textSelection.startLine, column);
394             if (!token)
395                 return null;
396             return new WebInspector.TextRange(textSelection.startLine, token.startColumn, textSelection.startLine, token.endColumn);
397         }
398
399         /**
400          * @param {?WebInspector.TextRange} textRange
401          * @return {!Array.<!WebInspector.DocumentationCatalog.ItemDescriptor>}
402          */
403         function computeDescriptors(textRange)
404         {
405             if (!textRange)
406                 return [];
407             var propertyName = textEditor.copyRange(textRange);
408             var descriptors = catalog.itemDescriptors(propertyName);
409             if (descriptors.length)
410                 return descriptors;
411             if (propertyName.toUpperCase() !== propertyName || !previousTokenText || !window[previousTokenText] || !window[previousTokenText][propertyName])
412                 return [];
413             return catalog.constantDescriptors(previousTokenText);
414         }
415
416         /**
417          * @param {!WebInspector.TextRange} textRange
418          * @return {?string}
419          */
420         function findPreviousToken(textRange)
421         {
422             var line = textEditor.line(textRange.startLine);
423             if (textRange.startColumn < 3 || line[textRange.startColumn - 1] !== ".")
424                 return null;
425             var token = textEditor.tokenAtTextPosition(textRange.startLine, textRange.startColumn - 2);
426             return token ? line.substring(token.startColumn, token.endColumn) : null;
427         }
428     }
429 }