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 */
8WebInspector.DocumentationCatalog = function()
9{
10    /** @type {!StringMap.<!Array.<!WebInspector.DocumentationCatalog.ItemDescriptor>>} */
11    this._articleList = new StringMap();
12    this._loader = new WebInspector.DocumentationCatalog.Loader(this);
13}
14
15/**
16 * @return {!WebInspector.DocumentationCatalog}
17 */
18WebInspector.DocumentationCatalog.instance = function()
19{
20    if (!WebInspector.DocumentationCatalog._instance)
21        WebInspector.DocumentationCatalog._instance = new WebInspector.DocumentationCatalog();
22    return WebInspector.DocumentationCatalog._instance;
23}
24
25/**
26 * @constructor
27 * @param {string} url
28 * @param {string} name
29 * @param {string} searchTerm
30 */
31WebInspector.DocumentationCatalog.ItemDescriptor = function(url, name, searchTerm)
32{
33    this._url = String.sprintf(WebInspector.DocumentationCatalog._articleURLFormat, url, searchTerm);
34    this._name = name;
35    this._searchItem = searchTerm;
36}
37
38WebInspector.DocumentationCatalog.ItemDescriptor.prototype = {
39    /**
40     * @return {string}
41     */
42    url: function()
43    {
44        return this._url;
45    },
46
47    /**
48     * @return {string}
49     */
50    name: function()
51    {
52        return this._name;
53    },
54
55    /**
56     * @return {string}
57     */
58    searchItem: function()
59    {
60        return this._searchItem;
61    }
62}
63
64/**
65 * @const
66 */
67WebInspector.DocumentationCatalog.apiURLPrefix = "http://docs.webplatform.org/w/api.php?action=query";
68
69/**
70 * @const
71 */
72WebInspector.DocumentationCatalog._articleURLFormat = WebInspector.DocumentationCatalog.apiURLPrefix + "&titles=%s%s&prop=revisions&rvprop=timestamp|content&format=json";
73
74/**
75 * @const
76 */
77WebInspector.DocumentationCatalog._articleListURLFormat = WebInspector.DocumentationCatalog.apiURLPrefix + "&generator=allpages&gaplimit=500&gapfrom=%s&format=json";
78
79WebInspector.DocumentationCatalog.prototype = {
80    /**
81     * @param {string} searchTerm
82     * @return {!Array.<!WebInspector.DocumentationCatalog.ItemDescriptor>}
83     */
84    itemDescriptors: function(searchTerm)
85    {
86        return this._articleList.get(searchTerm) || [];
87    },
88
89    /**
90     * @param {string} sourceName
91     * @return {!Array.<!WebInspector.DocumentationCatalog.ItemDescriptor>}
92     */
93    constantDescriptors: function(sourceName)
94    {
95        return [new WebInspector.DocumentationCatalog.ItemDescriptor("javascript/" + sourceName + "/", sourceName, "constants")]
96    },
97
98    startLoadingIfNeeded: function()
99    {
100        if (this._loader._state === WebInspector.DocumentationCatalog.Loader.DownloadStates.NotStarted)
101            this._loader._loadArticleList();
102    },
103
104    /**
105     * @return {boolean}
106     */
107    isLoading: function()
108    {
109        return this._loader._state === WebInspector.DocumentationCatalog.Loader.DownloadStates.InProgress;
110    },
111
112    /**
113     * @param {string} itemPath
114     */
115    _addDescriptorToList: function(itemPath)
116    {
117        // There are some properties that have several words in their name.
118        // In article list they are written with whitespace, while in URL they are written with underscore.
119        // We are creating URL for current property, so we have to replace all the whitespaces with underscores.
120        var correctedItemPath = itemPath.replace(" ", "_");
121        var tokens = correctedItemPath.split("/");
122        if (tokens.length === 1)
123            return;
124        var propertyName = tokens.pop();
125        var sourceName = tokens.length === 1 ? "window" : tokens.pop();
126        if (!sourceName)
127            return;
128        var descriptors = this._articleList.get(propertyName);
129        if (!descriptors) {
130            descriptors = [];
131            this._articleList.set(propertyName, descriptors);
132        }
133        var sourcePath = tokens.join("/") + "/" + (sourceName === "window" ? "" : sourceName + "/");
134        descriptors.push(new WebInspector.DocumentationCatalog.ItemDescriptor(sourcePath, sourceName, propertyName));
135    },
136}
137
138/**
139 * @constructor
140 * @param {!WebInspector.DocumentationCatalog} catalog
141 */
142WebInspector.DocumentationCatalog.Loader = function(catalog)
143{
144    this._sectionIndex = 0;
145    this._section = WebInspector.DocumentationCatalog.Loader._sections[0];
146    this._state = WebInspector.DocumentationCatalog.Loader.DownloadStates.NotStarted;
147    this._catalog = catalog;
148}
149
150/**
151 * @enum {string}
152 */
153WebInspector.DocumentationCatalog.Loader.DownloadStates = {
154    NotStarted: "NotStarted",
155    InProgress: "InProgress",
156    Finished: "Finished",
157    Failed: "Failed"
158};
159
160/**
161 * @const
162 * This array should be sorted alphabetically for correct WebInspector.DocumentationCatalog.Loader work.
163 */
164WebInspector.DocumentationCatalog.Loader._sections = [
165    "dom/",
166    "javascript/"
167];
168
169WebInspector.DocumentationCatalog.Loader.prototype = {
170    _loadArticleList: function()
171    {
172        if (this._state === WebInspector.DocumentationCatalog.Loader.DownloadStates.Finished)
173            return;
174        this._state = WebInspector.DocumentationCatalog.Loader.DownloadStates.InProgress;
175        var url = String.sprintf(WebInspector.DocumentationCatalog._articleListURLFormat, this._section);
176        var boundReset = this._resetDownload.bind(this);
177        loadXHR(url).then(this._processData.bind(this), boundReset).catch(boundReset);
178    },
179
180    /**
181     * @param {?string} responseText
182     */
183    _processData: function(responseText)
184    {
185        if (!responseText) {
186            this._resetDownload.call(this);
187            return;
188        }
189        var json = JSON.parse(responseText);
190        var pages = json["query"]["pages"];
191        for (var article in pages)
192            this._catalog._addDescriptorToList(pages[article]["title"]);
193        var sections = WebInspector.DocumentationCatalog.Loader._sections;
194        this._section = json["query-continue"]["allpages"]["gapcontinue"];
195        while (this._sectionIndex < sections.length && this._section > sections[this._sectionIndex] && !this._section.startsWith(sections[this._sectionIndex]))
196            ++this._sectionIndex;
197        if (this._sectionIndex === sections.length) {
198            this._state = WebInspector.DocumentationCatalog.Loader.DownloadStates.Finished;
199            return;
200        }
201        if (this._section < sections[this._sectionIndex])
202            this._section = sections[this._sectionIndex];
203        this._loadArticleList();
204    },
205
206    _resetDownload: function()
207    {
208        WebInspector.console.error("Documentation article list download failed");
209        this._state = WebInspector.DocumentationCatalog.Loader.DownloadStates.Failed;
210    }
211}