2 $title = "Web Platform Status";
3 $extra_head_content = <<<EOF
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";
11 xhrRequest.onload = function() {
12 if (xhrRequest.status == 200) {
13 if (xhrRequest.response) {
14 resolve(xhrRequest.response);
16 reject({ request: xhrRequest, url:url});
19 reject({ request: xhrRequest, url:url});
22 xhrRequest.onerror = function() {
23 reject({ request: xhrRequest, url:url});
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));
33 include("header.inc");
38 word-wrap: break-word;
39 -webkit-text-size-adjust:135%;
46 /* Hide the internal links on search since they are unlikely to work. */
47 #search:required:valid + * .internal-reference {
52 display: -webkit-flex;
54 -webkit-flex-direction: row;
57 .feature-header > h3:first-of-type {
70 background: linear-gradient(#fff, #f9f9f9);
71 border: 1px solid #bbb;
74 margin: 1em 0 !important;
91 <h2>WebKit Web Platform Status</h2>
92 <div id="feature-list">
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="http://127.0.0.1:8000/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>
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>
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();
123 function createFeatureView(featureObject) {
124 function createLinkWithHeading(elementName, heading, linkText, linkUrl) {
125 var container = document.createElement(elementName);
127 container.textContent = heading + ": ";
129 var link = document.createElement("a");
130 link.textContent = linkText;
132 container.appendChild(link);
136 var container = document.createElement('li');
137 container.className = "feature";
139 if ("features" in featureObject) {
140 container.setAttribute("id", "specification-" + featureObject.name);
142 container.setAttribute("id", "feature-" + featureObject.name);
145 var descriptionContainer = document.createElement('div');
146 descriptionContainer.className = "feature-description";
148 var featureHeaderContainer = document.createElement('div');
149 featureHeaderContainer.className = "feature-header";
150 descriptionContainer.appendChild(featureHeaderContainer);
152 var titleElement = document.createElement("h3");
153 titleElement.textContent = featureObject.name;
154 featureHeaderContainer.appendChild(titleElement);
156 if ("status" in featureObject) {
157 var statusContainer = document.createElement("span");
158 statusContainer.className = "feature-status";
159 if ("webkit-url" in featureObject) {
160 statusContainer.textContent = "Status: ";
161 var statusLink = document.createElement("a");
162 statusLink.href = featureObject["webkit-url"];
163 statusLink.textContent = featureObject.status.status;
164 statusContainer.appendChild(statusLink);
166 statusContainer.textContent = "Status: " + featureObject.status.status;
169 featureHeaderContainer.appendChild(statusContainer);
172 if ("description" in featureObject) {
173 var testDescription = document.createElement('p');
174 testDescription.className = "feature-desc";
175 testDescription.innerHTML = featureObject.description;
176 descriptionContainer.appendChild(testDescription);
179 if ("comment" in featureObject) {
180 if ("description" in featureObject) {
181 descriptionContainer.appendChild(document.createElement("hr"));
183 var comment = document.createElement('p');
184 comment.innerHTML = featureObject.comment;
185 descriptionContainer.appendChild(comment);
188 container.appendChild(descriptionContainer);
190 var hasDocumentationLink = "documentation-url" in featureObject;
191 var hasReferenceLink = "url" in featureObject;
192 var hasContactObject = "contact" in featureObject;
193 var hasSpecificationObject = "specification" in featureObject;
194 if (hasDocumentationLink || hasReferenceLink || hasContactObject) {
195 var moreInfoList = document.createElement("ul");
196 if (hasDocumentationLink) {
197 var url = featureObject["documentation-url"];
198 moreInfoList.appendChild(createLinkWithHeading("li", "Documentation", url, url));
201 if (hasReferenceLink) {
202 var url = featureObject.url;
203 moreInfoList.appendChild(createLinkWithHeading("li", "Reference", url, url));
206 if (hasSpecificationObject) {
207 var specification = featureObject.specification;
208 var li = createLinkWithHeading("li", "Parent feature", specification.name, ("#specification-" + specification.name));
209 li.className = "internal-reference";
210 moreInfoList.appendChild(li);
213 if (hasContactObject) {
214 var li = document.createElement("li");
215 li.textContent = "Contact: ";
216 if (featureObject.contact.twitter) {
217 li.appendChild(createLinkWithHeading("span", null, featureObject.contact.twitter, featureObject.contact.twitter));
219 if (featureObject.contact.email) {
220 if (featureObject.contact.twitter) {
221 li.appendChild(document.createTextNode(" - "));
223 var emailText = featureObject.contact.email;
224 if (featureObject.contact.name) {
225 emailText = featureObject.contact.name;
227 li.appendChild(createLinkWithHeading("span", null, emailText, featureObject.contact.email));
229 moreInfoList.appendChild(li);
232 container.appendChild(moreInfoList);
235 if ("features" in featureObject && featureObject.features.length) {
236 var internalLinkContainer = document.createElement("div");
237 internalLinkContainer.className = "internal-reference";
238 var trackedFeatures = document.createElement("p");
239 trackedFeatures.textContent = "Subfeatures: ";
240 internalLinkContainer.appendChild(trackedFeatures);
242 var list = document.createElement("ul");
243 for (var feature of featureObject.features) {
244 var link = document.createElement("a");
245 link.textContent = feature.name;
246 link.href = "#feature-" + feature.name;
248 var li = document.createElement("li");
249 li.appendChild(link);
250 list.appendChild(li);
252 internalLinkContainer.appendChild(list);
253 container.appendChild(internalLinkContainer);
259 function renderFeaturesAndSpecifications(featureLikeObjects) {
260 var featureContainer = document.getElementById('features-container');
261 for (var featureLikeObject of featureLikeObjects) {
262 featureContainer.appendChild(createFeatureView(featureLikeObject));
266 function initSearch(featuresArray) {
267 var inputField = document.getElementById('search');
268 var featuresEls = document.querySelectorAll('.features > li');
270 featuresArray.forEach(function(feature, i) {
271 feature.el = featuresEls[i];
272 feature.visible = true;
275 inputField.addEventListener('input', search);
277 function search(ev) {
278 var searchTerm = inputField.value.trim().toLowerCase();
279 searchFeatures(featuresArray, searchTerm);
283 function searchFeatures(featuresArray, searchTerm) {
284 featuresArray.forEach(function(feature) {
285 var visible = isSearchMatch(feature, searchTerm);
287 if (visible && !feature.visible) {
288 feature.el.className = 'feature';
289 } else if (!visible && feature.visible) {
290 feature.el.className = 'feature is-hidden';
293 feature.visible = visible;
297 function isSearchMatch(feature, searchTerm) {
298 if (feature.name.toLowerCase().indexOf(searchTerm) !== -1)
300 if ("keywords" in feature) {
301 for (var keyword of feature.keywords) {
302 if (keyword.toLowerCase().indexOf(searchTerm) !== -1)
309 function displayFeatures(results)
311 var mainContent = document.getElementById("feature-list");
312 var successSubtree = document.importNode(document.getElementById("success-template").content, true);
313 mainContent.appendChild(successSubtree);
315 var allSpecifications = [];
316 for (var i in results) {
317 allSpecifications = allSpecifications.concat(results[i].specification);
319 var specificationsByName = {}
320 for (var specification of allSpecifications) {
321 specification.features = [];
322 specification.isSpecification = true;
323 specificationsByName[specification.name] = specification;
326 var allFeatures = [];
327 for (var i in results) {
328 allFeatures = allFeatures.concat(results[i].features);
330 var featuresByName = {};
331 for (var feature of allFeatures) {
332 if ('specification' in feature) {
333 var specificationObject = specificationsByName[feature.specification];
334 specificationObject.features.push(feature);
335 feature.specification = specificationObject;
337 feature.isSpecification = false;
338 featuresByName[feature.name] = feature;
341 var everythingToShow = allFeatures.concat(allSpecifications);
342 sortAlphabetically(everythingToShow);
343 renderFeaturesAndSpecifications(everythingToShow);
344 initSearch(everythingToShow);
347 function displayError(error)
349 var mainContent = document.getElementById("feature-list");
350 var successSubtree = document.importNode(document.getElementById("error-template").content, true);
352 var errorMessage = "Unable to load " + error.url;
354 if (error.request.status !== 200) {
355 errorMessage += ", status: " + error.request.status + " - " + error.request.statusText;
356 } else if (!error.response) {
357 errorMessage += ", the JSON file cannot be processed.";
360 successSubtree.querySelector("#error-message").textContent = errorMessage;
362 mainContent.appendChild(successSubtree);
365 Promise.all([loadJavaScriptCoreFeatures, loadWebCoreFeatures]).then(displayFeatures).catch(displayError);
368 document.addEventListener("DOMContentLoaded", initializeStatusPage);
371 <?php include("footer.inc"); ?>