build.webkit.org/dashboard: Move layoutTestResultsDirectoryURLForIteration implementa...
[WebKit-https.git] / Websites / webkit.org / status.html
1 <?php
2 $title = "Web Platform Status";
3 $extra_head_content = <<<EOF
4 <script>
5 function xhrPromise(url) {
6     return new Promise(function(resolve, reject) {
7         var xhrRequest = new XMLHttpRequest();
8         xhrRequest.open('GET', url, true);
9         xhrRequest.responseType = "json";
10
11         xhrRequest.onload = function() {
12             if (xhrRequest.status == 200) {
13                 if (xhrRequest.response) {
14                     resolve(xhrRequest.response);
15                 } else {
16                     reject({ request: xhrRequest, url:url});
17                 }
18             } else {
19                 reject({ request: xhrRequest, url:url});
20             }
21         };
22         xhrRequest.onerror = function() {
23             reject({ request: xhrRequest, url:url});
24         };
25         xhrRequest.send();
26     });
27 }
28 var origin = new URL("https://svn.webkit.org/")
29 var loadJavaScriptCoreFeatures = xhrPromise(new URL("/repository/webkit/trunk/Source/JavaScriptCore/features.json", origin));
30 var loadWebCoreFeatures = xhrPromise(new URL("/repository/webkit/trunk/Source/WebCore/features.json", origin));
31 </script>
32 EOF;
33 include("header.inc");
34 ?>
35 <style>
36 #feature-list {
37     margin-top: 2em;
38     word-wrap: break-word;
39     -webkit-text-size-adjust:135%;
40 }
41 #search {
42     width: 50%;
43     margin-top: 1em;
44     margin-bottom: 1em;
45 }
46 /* Hide the internal links on search since they are unlikely to work. */
47 #search:required:valid + *  .internal-reference {
48     display: none;
49 }
50
51 .feature-header {
52     display: -webkit-flex;
53     display: flex;
54     -webkit-flex-direction: row;
55     flex-direction: row;
56 }
57 .feature-header > h3:first-of-type {
58     -webkit-flex-grow: 1;
59     flex-grow: 1;
60     margin: 0;
61     font-size: 16px;
62     line-height: 1.4em;
63     text-shadow: none;
64 }
65 ul.features {
66     padding: 0;
67 }
68 .feature {
69     display: block;
70     background: linear-gradient(#fff, #f9f9f9);
71     border: 1px solid #bbb;
72     border-radius: 5px;
73     padding: 1em;
74     margin: 1em 0 !important;
75 }
76
77 .feature.is-hidden {
78     display: none;
79 }
80 .feature-desc {
81     margin: 0.4em 0;
82 }
83 ul.feature-details {
84     margin: 0;
85 }
86 .feature-statusItem {
87     margin-right: .5em;
88 }
89 </style>
90
91 <h2>WebKit Web Platform Status</h2>
92 <div id="feature-list">
93 </div>
94
95 <template id="success-template">
96     <input type="search" id="search" placeholder="Filter" title="Filter the feature list." required>
97     <ul class="features" id="features-container"></ul>
98     <p>Cannot find something? You can contact <a href="https://twitter.com/webkit">@webkit</a> on Twitter or contact the <a href="https://lists.webkit.org/mailman/listinfo/webkit-help">webkit-help</a> mailing list for questions.</p>
99     <p>You can also <a href="coding/contributing.html">contribute to features</a> directly, the entire project is Open Source. To report bugs on existing features or check existing bug reports, see <a href="https://bugs.webkit.org">https://bugs.webkit.org</a>.</p>
100 </template>
101 <template id="error-template">
102     <p>Error: unable to load the features list (<span id="error-message"></span>).</p>
103     <p>If this is not resolved soon, please contact <a href="https://twitter.com/webkit">@webkit</a> on Twitter or the <a href="https://lists.webkit.org/mailman/listinfo/webkit-help">webkit-help</a> mailing list.</p>
104 </template>
105
106 <script>
107 function initializeStatusPage() {
108     function sortAlphabetically(array) {
109         array.sort(function(a, b){
110             var aName = a.name.toLocaleLowerCase();
111             var bName = b.name.toLocaleLowerCase();
112             
113             if (aName < bName) {
114                 return -1;
115             }
116             if (aName > bName) {
117                 return 1;
118             }
119             return 0;
120         });
121     }
122
123     function createFeatureView(featureObject) {
124         function createLinkWithHeading(elementName, heading, linkText, linkUrl) {
125             var container = document.createElement(elementName);
126             if (heading) {
127                 container.textContent = heading + ": ";
128             }
129             var link = document.createElement("a");
130             link.textContent = linkText;
131             link.href = linkUrl;
132             container.appendChild(link);
133             return container;
134         }
135
136         function makeTwitterLink(twitterHandle) {
137             if (twitterHandle[0] == "@")
138                 twitterHandle = twitterHandle.substring(1);
139             return "https://twitter.com/" + twitterHandle;
140         }
141
142         var container = document.createElement('li');
143         container.className = "feature";
144
145         if ("features" in featureObject) {
146             container.setAttribute("id", "specification-" + featureObject.name);
147         } else {
148             container.setAttribute("id", "feature-" + featureObject.name);
149         }
150
151         var descriptionContainer = document.createElement('div');
152         descriptionContainer.className = "feature-description";
153
154         var featureHeaderContainer = document.createElement('div');
155         featureHeaderContainer.className = "feature-header";
156         descriptionContainer.appendChild(featureHeaderContainer);
157
158         var titleElement = document.createElement("h3");
159         titleElement.textContent = featureObject.name;
160         featureHeaderContainer.appendChild(titleElement);
161
162         if ("status" in featureObject) {
163             var statusContainer = document.createElement("span");
164             statusContainer.className = "feature-status";
165             if ("webkit-url" in featureObject) {
166                 statusContainer.textContent = "Status: ";
167                 var statusLink = document.createElement("a");
168                 statusLink.href = featureObject["webkit-url"];
169                 statusLink.textContent = featureObject.status.status;
170                 statusContainer.appendChild(statusLink);
171             } else {
172                 statusContainer.textContent = "Status: " + featureObject.status.status;
173             }
174
175             featureHeaderContainer.appendChild(statusContainer);
176         }
177
178         if ("description" in featureObject) {
179             var testDescription = document.createElement('p');
180             testDescription.className = "feature-desc";
181             testDescription.innerHTML = featureObject.description;
182             descriptionContainer.appendChild(testDescription);
183         }
184
185         if ("comment" in featureObject) {
186             if ("description" in featureObject) {
187                 descriptionContainer.appendChild(document.createElement("hr"));
188             }
189             var comment = document.createElement('p');
190             comment.innerHTML = featureObject.comment;
191             descriptionContainer.appendChild(comment);
192         }
193
194         container.appendChild(descriptionContainer);
195
196         var hasDocumentationLink = "documentation-url" in featureObject;
197         var hasReferenceLink = "url" in featureObject;
198         var hasContactObject = "contact" in featureObject;
199         var hasSpecificationObject = "specification" in featureObject;
200         if (hasDocumentationLink || hasReferenceLink || hasContactObject) {
201             var moreInfoList = document.createElement("ul");
202             if (hasDocumentationLink) {
203                 var url = featureObject["documentation-url"];
204                 moreInfoList.appendChild(createLinkWithHeading("li", "Documentation", url, url));
205             }
206
207             if (hasReferenceLink) {
208                 var url = featureObject.url;
209                 moreInfoList.appendChild(createLinkWithHeading("li", "Reference", url, url));
210             }
211
212             if (hasSpecificationObject) {
213                 var specification = featureObject.specification;
214                 var li = createLinkWithHeading("li", "Parent feature", specification.name, ("#specification-" + specification.name));
215                 li.className = "internal-reference";
216                 moreInfoList.appendChild(li);
217             }
218
219             if (hasContactObject) {
220                 var li = document.createElement("li");
221                 li.textContent = "Contact: ";
222                 if (featureObject.contact.twitter) {
223                     li.appendChild(createLinkWithHeading("span", null, featureObject.contact.twitter, makeTwitterLink(featureObject.contact.twitter)));
224                 }
225                 if (featureObject.contact.email) {
226                     if (featureObject.contact.twitter) {
227                         li.appendChild(document.createTextNode(" - "));
228                     }
229                     var emailText = featureObject.contact.email;
230                     if (featureObject.contact.name) {
231                         emailText = featureObject.contact.name;
232                     }
233                     li.appendChild(createLinkWithHeading("span", null, emailText, "mailto:" + featureObject.contact.email));
234                 }
235                 moreInfoList.appendChild(li);
236             }
237
238             container.appendChild(moreInfoList);
239         }
240
241         if ("features" in featureObject && featureObject.features.length) {
242             var internalLinkContainer = document.createElement("div");
243             internalLinkContainer.className = "internal-reference";
244             var trackedFeatures = document.createElement("p");
245             trackedFeatures.textContent = "Subfeatures: ";
246             internalLinkContainer.appendChild(trackedFeatures);
247
248             var list = document.createElement("ul");
249             for (var feature of featureObject.features) {
250                 var link = document.createElement("a");
251                 link.textContent = feature.name;
252                 link.href = "#feature-" + feature.name;
253
254                 var li = document.createElement("li");
255                 li.appendChild(link);
256                 list.appendChild(li);
257             }
258             internalLinkContainer.appendChild(list);
259             container.appendChild(internalLinkContainer);
260         }
261
262         return container;
263     }
264
265     function renderFeaturesAndSpecifications(featureLikeObjects) {
266         var featureContainer = document.getElementById('features-container');
267         for (var featureLikeObject of featureLikeObjects) {
268             featureContainer.appendChild(createFeatureView(featureLikeObject));
269         }
270     }
271
272     function initSearch(featuresArray) {
273         var inputField = document.getElementById('search');
274         var featuresEls = document.querySelectorAll('.features > li');
275         
276         featuresArray.forEach(function(feature, i) {
277             feature.el = featuresEls[i];
278             feature.visible = true;
279         });
280
281         inputField.addEventListener('input', search);
282
283         function search(ev) {
284             var searchTerm = inputField.value.trim().toLowerCase();
285             searchFeatures(featuresArray, searchTerm);
286         }
287     }
288
289     function searchFeatures(featuresArray, searchTerm) {
290         featuresArray.forEach(function(feature) {
291             var visible = isSearchMatch(feature, searchTerm);
292             
293             if (visible && !feature.visible) {
294                 feature.el.className = 'feature';
295             } else if (!visible && feature.visible) {
296                 feature.el.className = 'feature is-hidden';
297             }
298             
299             feature.visible = visible;
300         });
301     }
302
303     function isSearchMatch(feature, searchTerm) {
304         if (feature.name.toLowerCase().indexOf(searchTerm) !== -1)
305             return true;
306         if ("keywords" in feature) {
307             for (var keyword of feature.keywords) {
308                 if (keyword.toLowerCase().indexOf(searchTerm) !== -1)
309                     return true;
310             }
311         }
312         return false;
313     }
314
315     function displayFeatures(results)
316     {
317         var mainContent = document.getElementById("feature-list");
318         var successSubtree = document.importNode(document.getElementById("success-template").content, true);
319         mainContent.appendChild(successSubtree);
320
321         var allSpecifications = [];
322         for (var i in results) {
323             allSpecifications = allSpecifications.concat(results[i].specification);
324         }
325         var specificationsByName = {}
326         for (var specification of allSpecifications) {
327             specification.features = [];
328             specification.isSpecification = true;
329             specificationsByName[specification.name] = specification;
330         }
331
332         var allFeatures = [];
333         for (var i in results) {
334             allFeatures = allFeatures.concat(results[i].features);
335         }
336         var featuresByName = {};
337         for (var feature of allFeatures) {
338             if ('specification' in feature) {
339                 var specificationObject = specificationsByName[feature.specification];
340                 specificationObject.features.push(feature);
341                 feature.specification = specificationObject;
342             }
343             feature.isSpecification = false;
344             featuresByName[feature.name] = feature;
345         }
346
347         var everythingToShow = allFeatures.concat(allSpecifications);
348         sortAlphabetically(everythingToShow);
349         renderFeaturesAndSpecifications(everythingToShow);
350         initSearch(everythingToShow);
351     }
352
353     function displayError(error)
354     {
355         var mainContent = document.getElementById("feature-list");
356         var successSubtree = document.importNode(document.getElementById("error-template").content, true);
357
358         var errorMessage = "Unable to load " + error.url;
359
360         if (error.request.status !== 200) {
361             errorMessage += ", status: " + error.request.status + " - " + error.request.statusText;
362         } else if (!error.response) {
363             errorMessage += ", the JSON file cannot be processed.";
364         }
365
366         successSubtree.querySelector("#error-message").textContent = errorMessage;
367
368         mainContent.appendChild(successSubtree);
369     }
370
371     Promise.all([loadJavaScriptCoreFeatures, loadWebCoreFeatures]).then(displayFeatures).catch(displayError);
372 }
373
374 document.addEventListener("DOMContentLoaded", initializeStatusPage);
375 </script>
376
377 <?php include("footer.inc"); ?>