Update To 11.40.268.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("documentation/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))
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 = 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 = 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 = 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 = 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 = 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 = createElement("span");
254             break;
255         case elementTypes.Link:
256             element = WebInspector.createExternalAnchor(article.url(), article.children().length ? "" : article.url());
257             break;
258         case elementTypes.Code:
259             element = createElementWithClass("span", "documentation-code-tag");
260             break;
261         case elementTypes.CodeBlock:
262             element = createElementWithClass("pre", "documentation-code source-code");
263             element.textContent = article.code();
264             break;
265         case elementTypes.PlainText:
266             element = createElement("span");
267             element.textContent = article.text();
268             if (article.isHighlighted())
269                 element.classList.add("documentation-highlighted-text");
270             break;
271         case elementTypes.Block:
272             element = createElement(article.hasBullet() ? "li" : "div");
273             if (!article.hasBullet())
274                 element.classList.add("documentation-paragraph");
275             break;
276         case elementTypes.Table:
277             return this._renderTable(/** @type {!WebInspector.WikiParser.Table} */(article));
278         default:
279             throw new Error("Unknown ArticleElement type " + article.type());
280         }
281
282         if (article.type() === WebInspector.WikiParser.ArticleElement.Type.Block
283             || article.type() === WebInspector.WikiParser.ArticleElement.Type.Code
284             || article.type() === WebInspector.WikiParser.ArticleElement.Type.Inline) {
285             for (var i = 0; i < article.children().length; ++i) {
286                 var child = this._renderBlock(article.children()[i]);
287                 if (child)
288                     element.appendChild(child);
289             }
290         }
291
292         return element;
293     },
294
295     /**
296      * @param {!WebInspector.WikiParser.Table} table
297      * @return {!Element}
298      */
299     _renderTable: function(table)
300     {
301         var tableElement = createElementWithClass("table", "documentation-table");
302         var tableBody = tableElement.createChild("tbody");
303         var headerRow = tableBody.createChild("tr", "documentation-table-row");
304         for (var i = 0; i < table.columnNames().length; ++i) {
305             var tableHeader = headerRow.createChild("th", "documentation-table-header");
306             tableHeader.appendChild(this._renderBlock(table.columnNames()[i]));
307         }
308         for (var i = 0; i < table.rows().length; ++i) {
309             var tableRow = tableBody.createChild("tr", "documentation-table-row");
310             var row = table.rows()[i];
311             for (var j = 0; j < row.length; ++j) {
312                 var cell = tableRow.createChild("td", "documentation-table-cell");
313                 cell.appendChild(this._renderBlock(row[j]));
314             }
315         }
316
317         return tableElement;
318     }
319 }
320
321 /**
322  * @constructor
323  * @implements {WebInspector.ContextMenu.Provider}
324  */
325 WebInspector.DocumentationView.ContextMenuProvider = function()
326 {
327 }
328
329 WebInspector.DocumentationView.ContextMenuProvider.prototype = {
330     /**
331      * @param {!Event} event
332      * @param {!WebInspector.ContextMenu} contextMenu
333      * @param {!Object} target
334      */
335     appendApplicableItems: function(event, contextMenu, target)
336     {
337         if (!(target instanceof WebInspector.CodeMirrorTextEditor))
338             return;
339         WebInspector.DocumentationCatalog.instance().startLoadingIfNeeded();
340         if (WebInspector.DocumentationCatalog.instance().isLoading()) {
341             var itemName = WebInspector.useLowerCaseMenuTitles() ? "Loading documentation..." : "Loading Documentation...";
342             contextMenu.appendItem(itemName, function() {}, true);
343             return;
344         }
345         var textEditor = /** @type {!WebInspector.CodeMirrorTextEditor} */ (target);
346         var descriptors = this._determineDescriptors(textEditor);
347         if (!descriptors.length)
348             return;
349         if (descriptors.length === 1) {
350             var formatString = WebInspector.useLowerCaseMenuTitles() ? "Show documentation for %s.%s" : "Show Documentation for %s.%s";
351             var methodName = String.sprintf("%s.%s", descriptors[0].name(), descriptors[0].searchItem());
352             contextMenu.appendItem(WebInspector.UIString(formatString, descriptors[0].name(), descriptors[0].searchItem()), WebInspector.DocumentationView.showDocumentationURL.bind(null, descriptors[0].url(), methodName));
353             return;
354         }
355         var subMenuItem = contextMenu.appendSubMenuItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Show documentation for..." : "Show Documentation for..."));
356         for (var i = 0; i < descriptors.length; ++i) {
357             var methodName = String.sprintf("%s.%s", descriptors[i].name(), descriptors[i].searchItem());
358             subMenuItem.appendItem(methodName, WebInspector.DocumentationView.showDocumentationURL.bind(null, descriptors[i].url(), methodName));
359         }
360     },
361
362     /**
363      * @param {!WebInspector.CodeMirrorTextEditor} textEditor
364      * @return {!Array.<!WebInspector.DocumentationCatalog.ItemDescriptor>}
365      */
366     _determineDescriptors: function(textEditor)
367     {
368         var catalog = WebInspector.DocumentationCatalog.instance();
369         var textSelection = textEditor.selection().normalize();
370         var previousTokenText = findPreviousToken(textSelection);
371
372         if (!textSelection.isEmpty()) {
373             if (textSelection.startLine !== textSelection.endLine)
374                 return [];
375             return computeDescriptors(textSelection);
376         }
377
378         var descriptors = computeDescriptors(getTokenRangeByColumn(textSelection.startColumn));
379         if (descriptors.length)
380             return descriptors;
381
382         return computeDescriptors(getTokenRangeByColumn(textSelection.startColumn - 1));
383
384         /**
385          * @param {number} column
386          * @return {?WebInspector.TextRange}
387          */
388         function getTokenRangeByColumn(column)
389         {
390             var token = textEditor.tokenAtTextPosition(textSelection.startLine, column);
391             if (!token)
392                 return null;
393             return new WebInspector.TextRange(textSelection.startLine, token.startColumn, textSelection.startLine, token.endColumn);
394         }
395
396         /**
397          * @param {?WebInspector.TextRange} textRange
398          * @return {!Array.<!WebInspector.DocumentationCatalog.ItemDescriptor>}
399          */
400         function computeDescriptors(textRange)
401         {
402             if (!textRange)
403                 return [];
404             var propertyName = textEditor.copyRange(textRange);
405             var descriptors = catalog.itemDescriptors(propertyName);
406             if (descriptors.length)
407                 return descriptors;
408             if (propertyName.toUpperCase() !== propertyName || !previousTokenText || !window[previousTokenText] || !window[previousTokenText][propertyName])
409                 return [];
410             return catalog.constantDescriptors(previousTokenText);
411         }
412
413         /**
414          * @param {!WebInspector.TextRange} textRange
415          * @return {?string}
416          */
417         function findPreviousToken(textRange)
418         {
419             var line = textEditor.line(textRange.startLine);
420             if (textRange.startColumn < 3 || line[textRange.startColumn - 1] !== ".")
421                 return null;
422             var token = textEditor.tokenAtTextPosition(textRange.startLine, textRange.startColumn - 2);
423             return token ? line.substring(token.startColumn, token.endColumn) : null;
424         }
425     }
426 }