a62077a4cc6c8e4d50d2381ab557f425bf5338e9
[WebKit-https.git] / Websites / webkit.org / wp-content / themes / webkit / status.php
1 <?php
2 /**
3  * Template Name: Status Page
4  **/
5 ?>
6 <?php get_header(); ?>
7 <script>
8 function xhrPromise(url) {
9     return new Promise(function(resolve, reject) {
10         var xhrRequest = new XMLHttpRequest();
11         xhrRequest.open('GET', url, true);
12         xhrRequest.responseType = "json";
13
14         xhrRequest.onload = function() {
15             if (xhrRequest.status == 200) {
16                 if (xhrRequest.response) {
17                     resolve(xhrRequest.response);
18                 } else {
19                     reject({ request: xhrRequest, url:url});
20                 }
21             } else {
22                 reject({ request: xhrRequest, url:url});
23             }
24         };
25         xhrRequest.onerror = function() {
26             reject({ request: xhrRequest, url:url});
27         };
28         xhrRequest.send();
29     });
30 }
31 var origin = new URL("https://<?php echo strpos(WP_HOST, "webkit.org") !== false ? "svn.webkit.org" : WP_HOST; ?>/");
32 var loadJavaScriptCoreFeatures = xhrPromise(new URL("/repository/webkit/trunk/Source/JavaScriptCore/features.json", origin));
33 var loadWebCoreFeatures = xhrPromise(new URL("/repository/webkit/trunk/Source/WebCore/features.json", origin));
34 </script>
35
36 <style>
37 .page {
38     display: -webkit-flex;
39     display: flex;
40     -webkit-flex-wrap: wrap;
41     flex-wrap: wrap;
42     -webkit-justify-content: space-between;
43     justify-content: space-between;
44     box-sizing: border-box;
45     width: 100%;
46 }
47
48 .page h1 {
49     font-size: 4.2rem;
50     font-weight: 200;
51     line-height: 6rem;
52     color: black;
53     text-align: left;
54     margin: 3rem auto;
55     width: 100%;
56 }
57
58 .page h1 a {
59     color: #444444;
60 }
61
62 .page h2 {
63     font-weight: 200;
64     font-size: 3rem;
65 }
66
67 .page h3 {
68     font-weight: 400;
69     font-size: 2.5rem;
70 }
71
72 .page p {
73     margin-bottom: 3rem;
74 }
75
76 #feature-list {
77     display: inline-block;
78     width: 66%;
79     word-wrap: break-word;
80 }
81
82 /* Hide the internal links on search since they are unlikely to work. */
83 #search:required:valid + *  .internal-reference {
84     display: none;
85 }
86
87 .feature-header {
88     display: -webkit-flex;
89     display: flex;
90     -webkit-flex-direction: row;
91     flex-direction: row;
92 }
93 .feature-header > h3:first-of-type {
94     -webkit-flex-grow: 1;
95     flex-grow: 1;
96     margin: 0;
97 }
98
99 ul.features {
100     padding: 0;
101 }
102
103 .features .feature {
104     position: relative;
105     display: block;
106     background-color: #f9f9f9;
107     border: 1px solid #dddddd;
108     border-radius: 3px;
109     padding: 1em;
110     margin: 1em 0 !important;
111     max-height: intrinsic;
112     min-height: 3rem;
113     overflow-y: hidden;
114     cursor: pointer;
115     -webkit-transition: background-color 0.3s ease-in;
116     -moz-transition: background-color 0.3s ease-in;
117     transition: background-color 0.3s ease-in;
118 }
119
120 .features .feature:hover {
121     background-color: white;
122 }
123
124 .feature.opened {
125     background-color: white;
126     max-height: 120rem;
127 }
128
129 .feature-description + *,
130 .feature-description .feature-desc,
131 .feature-description .comment {
132     display: none;
133     margin: 0;
134 }
135
136 .feature.opened .feature-description + *,
137 .feature.opened .feature-desc,
138 .feature.opened .feature-description .comment {
139     display: block;
140     margin-bottom: 3rem;
141 }
142
143 .feature.opened .feature-description + *:last-child {
144     margin-bottom: 0;
145 }
146
147 .sub-features {
148     font-size: 1.5rem;
149     color: #555;
150 }
151
152 .sub-features ul {
153     list-style: none;
154     display: inline-block;
155     padding: 0;
156     margin: 0;
157 }
158
159 .sub-features li {
160     display: inline;
161 }
162
163 .sub-features li:after {
164     content: ", ";
165 }
166
167 .sub-features li:last-child:after {
168     content: "";
169 }
170
171 .feature-header {
172     position: relative;
173     padding-right: 3rem;
174 }
175
176 .feature-header h3 .internal-reference a {
177     color: #999999;
178     text-decoration: none;
179     padding-left: 0.5em;
180 }
181
182 .feature-header h3 a {
183     color: #444;
184 }
185
186 .feature-header h3 .internal-reference a:hover {
187     color: inherit;
188     text-decoration: underline;
189 }
190
191 .feature.is-hidden {
192     display: none;
193 }
194
195 ul.feature-details {
196     margin: 0;
197 }
198 .feature-statusItem {
199     margin-right: 0.5em;
200 }
201
202 .feature-status {
203     font-size: 2rem;
204     display: inline-block;
205     position: relative;
206     min-width: 5em;
207     text-align: right;
208 }
209
210 .feature-status,
211 .feature-status a {
212     color: #999999;
213 }
214
215 .feature .status-marker {
216     width: 0;
217     height: 0;
218     position: absolute;
219     top: 0;
220     left: 0;
221     border-style: solid;
222     border-width: 20px 20px 0 0;
223     border-color: transparent transparent transparent transparent;
224 }
225
226 #status-filters .supported,
227 .feature-status.supported,
228 .feature-status.supported a {
229     color: #339900;
230 }
231
232 .status-marker.supported {
233     border-color: #339900 transparent transparent transparent;
234 }
235
236 #status-filters .in-development,
237 .feature-status.in-development,
238 .feature-status.in-development a {
239     color: #f46c0e;
240 }
241
242 .status-marker.in-development {
243     border-color: #f46c0e transparent transparent transparent;
244 }
245
246 #status-filters .no-active-development,
247 .feature-status.no-active-development,
248 .feature-status.no-active-development a {
249     color: #5858D6;
250 }
251
252 .status-marker.no-active-development {
253     border-color: #5858D6 transparent transparent transparent;
254 }
255
256 #status-filters .partial-support,
257 .feature-status.partial-support,
258 .feature-status.partial-support a {
259     color: #548c8c;
260 }
261
262 .status-marker.partial-support {
263     border-color: #548c8c transparent transparent transparent;
264 }
265
266 #status-filters .prototyping,
267 .feature-status.prototyping,
268 .feature-status.prototyping a {
269     color: #007AFF;
270 }
271
272 .status-marker.prototyping {
273     border-color: #007AFF transparent transparent transparent;
274 }
275
276
277 #status-filters .under-consideration,
278 .feature-status.under-consideration,
279 .feature-status.under-consideration a {
280     color: #cc9d00;
281 }
282
283 .status-marker.under-consideration {
284     border-color: #FFC500 transparent transparent transparent;
285 }
286
287 .feature-status.removed,
288 .feature-status.removed a {
289     color: #999999;
290 }
291
292 .status-marker.removed {
293     border-color: #999999 transparent transparent transparent;
294 }
295
296 .feature-filters {
297     position: relative;
298     top: 0;
299     display: inline-block;
300
301     width: -webkit-calc(33.33% - 3rem);
302     width: -moz-calc(33.33% - 3rem);
303     width: calc(33.33% - 3rem);
304     margin-right: 3rem;
305     font-size: 2rem;
306 }
307
308 #search {
309     font-size: 2rem;
310     padding: 1rem;
311     border-radius: 3px;
312     border: 1px solid #cccccc;
313     width: 100%;
314     margin-top: 1.5rem;
315     box-sizing: border-box;
316 }
317
318 .feature-filters ul {
319     margin-top: 3rem;
320 }
321
322 .feature-filters ul li {
323     margin-bottom: 1rem;
324 }
325
326 .feature-filters label > input {
327     position: relative;
328     top: -3px;
329 }
330
331 h3 a[name], .admin-bar h3 a[name] {
332     top: initial;
333     width: auto;
334     display: inline-block;
335     visibility: visible;
336 }
337
338 @media only screen and (max-width: 508px) {
339     #feature-filters,
340     #feature-list {
341         width: 100%;
342     }
343
344     #feature-filters {
345         border: 1px solid #dddddd;
346         border-radius: 3px;
347         background: #f6f6f6;
348         padding: 1rem;
349         box-sizing: border-box;
350         margin-right: 0;
351         margin-bottom: 3rem;
352     }
353
354     .feature-header h3 {
355         font-size: 2rem;
356     }
357
358     .feature-status {
359         font-size: 1.6rem;
360         margin-top: 0.4rem;
361     }
362 }
363
364 </style>
365         <?php if (have_posts()) : while (have_posts()) : the_post(); ?>
366
367         <div class="page feature-status-page" id="post-<?php the_ID(); ?>">
368             <?php echo str_repeat('&nbsp;', 200);?>
369                         <h1><a href="<?php echo get_permalink() ?>" rel="bookmark" title="Permanent Link: <?php the_title(); ?>"><?php the_title(); ?></a></h1>
370             <form id="feature-filters" class="feature-filters">
371                 <h2>Filters</h2>
372                 <input type="text" id="search" placeholder="Search filter&hellip;" title="Filter the feature list." required>
373                 <ul id="status-filters">
374                 </ul>
375
376             </form>
377
378             <div id="feature-list">
379             <h2>Features</h2>
380             </div>
381
382             <template id="success-template">
383                 <ul class="features" id="features-container"></ul>
384                 <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>
385                 <p>You can also <a href="/contributing-code/">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>
386             </template>
387             <template id="error-template">
388                 <p>Error: unable to load the features list (<span id="error-message"></span>).</p>
389                 <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>
390             </template>
391         </div>
392
393         <?php //comments_template(); ?>
394
395         <?php endwhile; else: ?>
396
397                 <p>No posts.</p>
398
399         <?php endif; ?>
400
401
402 <script>
403 function initializeStatusPage() {
404
405     function sortAlphabetically(array) {
406         array.sort(function(a, b){
407             var aName = a.name.toLowerCase();
408             var bName = b.name.toLowerCase();
409
410             var nameCompareResult = aName.localeCompare(bName);
411
412             if ( nameCompareResult )
413                 return nameCompareResult;
414
415             // Status sort
416             var aStatus = a.status != undefined ? a.status.status.toLowerCase() : '';
417             var bStatus = b.status != undefined ? b.status.status.toLowerCase() : '';
418
419             return aStatus.localeCompare(bStatus);
420         });
421     }
422
423     function createFeatureView(featureObject) {
424         function createLinkWithHeading(elementName, heading, linkText, linkUrl) {
425             var container = document.createElement(elementName);
426             if (heading) {
427                 container.textContent = heading + ": ";
428             }
429             var link = document.createElement("a");
430             link.textContent = linkText;
431             link.href = linkUrl;
432             container.appendChild(link);
433             return container;
434         }
435
436         function makeTwitterLink(twitterHandle) {
437             if (twitterHandle[0] == "@")
438                 twitterHandle = twitterHandle.substring(1);
439             return "https://twitter.com/" + twitterHandle;
440         }
441
442         var container = document.createElement('li');
443         var hasDocumentationLink = "documentation-url" in featureObject;
444         var hasReferenceLink = "url" in featureObject;
445         var hasContactObject = "contact" in featureObject;
446         var hasSpecificationObject = "specification" in featureObject;
447
448         container.addEventListener('click', function (e) {
449             if ( container.className.indexOf('opened') !== -1 ) {
450                 container.className = container.className.replace(' opened','');
451             } else container.className += " opened";
452         });
453
454         container.className = "feature";
455
456         var slug = featureObject.name.toLowerCase().replace(/ /g, '-');
457         if ("features" in featureObject) {
458             container.setAttribute("id", "specification-" + slug);
459         } else {
460             container.setAttribute("id", "feature-" + slug);
461         }
462
463         if (window.location.hash && window.location.hash == "#" + container.getAttribute('id')) {
464             container.className += " opened";
465         }
466
467         var cornerStatus = document.createElement('div');
468         cornerStatus.className = "status-marker ";
469         container.appendChild(cornerStatus);
470
471         var descriptionContainer = document.createElement('div');
472         descriptionContainer.className = "feature-description";
473
474         var featureHeaderContainer = document.createElement('div');
475         featureHeaderContainer.className = "feature-header";
476         descriptionContainer.appendChild(featureHeaderContainer);
477
478         var titleElement = document.createElement("h3");
479         var anchorLinkElement = document.createElement("a");
480         anchorLinkElement.href = "#" + container.getAttribute("id");
481         anchorLinkElement.name = container.getAttribute("id");
482         anchorLinkElement.textContent = featureObject.name;
483         titleElement.appendChild(anchorLinkElement);
484
485         // Add sub-feature here
486         if (hasSpecificationObject) {
487             var specification = featureObject.specification;
488             var specSpan = createLinkWithHeading("span", null, specification.name, "#specification-" + specification.name.toLowerCase().replace(/ /g, '-'));
489             specSpan.className = "internal-reference";
490             titleElement.appendChild(specSpan);
491         }
492
493         featureHeaderContainer.appendChild(titleElement);
494
495         if ("status" in featureObject) {
496             var statusContainer = document.createElement("span");
497             cornerStatus.className += statusClassName = featureObject.status.status.toLowerCase().replace(/ /g, '-');
498             statusContainer.className = "feature-status " + statusClassName;
499             if ("webkit-url" in featureObject) {
500                 var statusLink = document.createElement("a");
501                 statusLink.href = featureObject["webkit-url"];
502                 statusLink.textContent = featureObject.status.status;
503                 statusContainer.appendChild(statusLink);
504             } else {
505                 statusContainer.textContent = featureObject.status.status;
506             }
507
508             featureHeaderContainer.appendChild(statusContainer);
509         }
510
511         if ("description" in featureObject) {
512             var testDescription = document.createElement('p');
513             testDescription.className = "feature-desc";
514             testDescription.innerHTML = featureObject.description;
515             descriptionContainer.appendChild(testDescription);
516         }
517
518         if ("comment" in featureObject) {
519             if ("description" in featureObject) {
520                 var hr = document.createElement("hr");
521                 hr.className = 'comment';
522                 descriptionContainer.appendChild(hr);
523             }
524             var comment = document.createElement('p');
525             comment.className = 'comment';
526             comment.innerHTML = featureObject.comment;
527             descriptionContainer.appendChild(comment);
528         }
529
530         container.appendChild(descriptionContainer);
531
532         if (hasDocumentationLink || hasReferenceLink || hasContactObject) {
533             var moreInfoList = document.createElement("ul");
534             if (hasDocumentationLink) {
535                 var url = featureObject["documentation-url"];
536                 moreInfoList.appendChild(createLinkWithHeading("li", "Documentation", url, url));
537             }
538
539             if (hasReferenceLink) {
540                 var url = featureObject.url;
541                 moreInfoList.appendChild(createLinkWithHeading("li", "Reference", url, url));
542             }
543
544             if (hasSpecificationObject) {
545                 var specification = featureObject.specification;
546                 var li = createLinkWithHeading("li", "Parent feature", specification.name, "#specification-" + specification.name.toLowerCase().replace(/ /g, '-'));
547                 li.className = "internal-reference";
548                 moreInfoList.appendChild(li);
549             }
550
551             if (hasContactObject) {
552                 var li = document.createElement("li");
553                 li.textContent = "Contact: ";
554                 if (featureObject.contact.twitter) {
555                     li.appendChild(createLinkWithHeading("span", null, featureObject.contact.twitter, makeTwitterLink(featureObject.contact.twitter)));
556                 }
557                 if (featureObject.contact.email) {
558                     if (featureObject.contact.twitter) {
559                         li.appendChild(document.createTextNode(" - "));
560                     }
561                     var emailText = featureObject.contact.email;
562                     if (featureObject.contact.name) {
563                         emailText = featureObject.contact.name;
564                     }
565                     li.appendChild(createLinkWithHeading("span", null, emailText, "mailto:" + featureObject.contact.email));
566                 }
567                 moreInfoList.appendChild(li);
568             }
569
570             container.appendChild(moreInfoList);
571         }
572
573         if ("features" in featureObject && featureObject.features.length) {
574             var internalLinkContainer = document.createElement("div");
575             internalLinkContainer.className = "internal-reference sub-features";
576             internalLinkContainer.textContent = "Includes: "
577             // internalLinkContainer.appendChild(trackedFeatures);
578
579             var list = document.createElement("ul");
580             for (var feature of featureObject.features) {
581                 var link = document.createElement("a");
582                 link.textContent = feature.name;
583                 link.href = "#feature-" + feature.name.toLowerCase().replace(/ /g, '-');
584
585                 var li = document.createElement("li");
586                 li.appendChild(link);
587                 list.appendChild(li);
588             }
589             internalLinkContainer.appendChild(list);
590             container.appendChild(internalLinkContainer);
591         }
592
593         return container;
594     }
595
596     function renderFeaturesAndSpecifications(featureLikeObjects) {
597         var featureContainer = document.getElementById('features-container');
598         for (var featureLikeObject of featureLikeObjects) {
599             featureContainer.appendChild(createFeatureView(featureLikeObject));
600         }
601     }
602
603     function initSearch(featuresArray) {
604         var filtersForm = document.getElementById('feature-filters');
605         var statusContainer = document.getElementById('status-filters');
606         var inputField = document.getElementById('search');
607         var featuresEls = document.querySelectorAll('.features > li');
608         var statusFilters = {};
609
610         featuresArray.forEach(function(feature, i) {
611             feature.el = featuresEls[i];
612             feature.visible = true;
613
614             if (feature.status != undefined) {
615                 featureStatusKey = feature.status.status.toLocaleLowerCase();
616
617                 if (!statusFilters[featureStatusKey])
618                     statusFilters[featureStatusKey] = feature.status.status;
619
620             }
621         });
622
623         var statusLength = statusFilters.length;
624
625         for (var key in statusFilters) {
626             var status = statusFilters[key];
627             var entry = document.createElement("li");
628             var label = document.createElement("label");
629             var input = document.createElement("input");
630             input.setAttribute('type','checkbox');
631             input.setAttribute('value', key);
632             input.addEventListener('change', search);
633             label.appendChild(input);
634             label.className = status.toLocaleLowerCase().replace(/ /g, '-');
635             label.appendChild(document.createTextNode(" " + status));
636             entry.appendChild(label);
637             statusContainer.appendChild(entry);
638         }
639
640         filtersForm.addEventListener('click', function (e) {
641             if ( filtersForm.className.indexOf('opened') !== -1 ) {
642                 filtersForm.className = filtersForm.className.replace(' opened','');
643             } else filtersForm.className += " opened";
644         });
645
646         inputField.addEventListener('input', search);
647
648         var inputs = [].slice.call(filtersForm.getElementsByTagName('input'));
649         inputs.forEach(function (input,i) {
650             input.addEventListener('click', function (e) {
651                 e.stopPropagation();
652             });
653         });
654
655         function search(ev) {
656             var searchTerm = inputField.value.trim().toLowerCase();
657             var activeStatusFilters = [];
658             var checkboxes = [].slice.call(statusContainer.getElementsByTagName('input'));
659             checkboxes.forEach(function(checkbox,i) {
660                 if ( checkbox.checked )
661                     activeStatusFilters.push(checkbox.value);
662             });
663
664             searchFeatures(featuresArray, searchTerm, activeStatusFilters);
665         }
666     }
667
668     function searchFeatures(featuresArray, searchTerm, statusFilters) {
669         featuresArray.forEach(function(feature) {
670             var visible = isSearchMatch(feature, searchTerm) && isStatusFiltered(feature, statusFilters);
671
672             if (visible && !feature.visible) {
673                 feature.el.className = 'feature';
674             } else if (!visible && feature.visible) {
675                 feature.el.className = 'feature is-hidden';
676             }
677
678             feature.visible = visible;
679         });
680     }
681
682     function isSearchMatch(feature, searchTerm) {
683         if (feature.name.toLowerCase().indexOf(searchTerm) !== -1)
684             return true;
685         if ("keywords" in feature) {
686             for (var keyword of feature.keywords) {
687                 if (keyword.toLowerCase().indexOf(searchTerm) !== -1)
688                     return true;
689             }
690         }
691         return false;
692     }
693
694     function isStatusFiltered(feature, activeFilters) {
695         if (activeFilters.length == 0)
696             return true;
697         if (feature.status === undefined)
698             return false;
699         if (activeFilters.indexOf(feature.status.status.toLowerCase()) != -1)
700             return true;
701
702         return false;
703     }
704
705     function displayFeatures(results) {
706         var mainContent = document.getElementById("feature-list");
707         var successSubtree = document.importNode(document.getElementById("success-template").content, true);
708         mainContent.appendChild(successSubtree);
709
710         var allSpecifications = [];
711         for (var i in results) {
712             allSpecifications = allSpecifications.concat(results[i].specification);
713         }
714         var specificationsByName = {}
715         for (var specification of allSpecifications) {
716             specification.features = [];
717             specification.isSpecification = true;
718             specificationsByName[specification.name] = specification;
719         }
720
721         var allFeatures = [];
722         for (var i in results) {
723             allFeatures = allFeatures.concat(results[i].features);
724         }
725         var featuresByName = {};
726         for (var feature of allFeatures) {
727             if ('specification' in feature) {
728                 var specificationObject = specificationsByName[feature.specification];
729                 specificationObject.features.push(feature);
730                 feature.specification = specificationObject;
731             }
732             feature.isSpecification = false;
733             featuresByName[feature.name] = feature;
734         }
735
736         var everythingToShow = allFeatures.concat(allSpecifications);
737
738         sortAlphabetically(everythingToShow);
739         renderFeaturesAndSpecifications(everythingToShow);
740         initSearch(everythingToShow);
741
742         if (window.location.hash) {
743             var hash = window.location.hash;
744             window.location.hash = ""; // Change hash so navigation takes place
745             window.location.hash = hash;
746         }
747     }
748
749     function displayError(error) {
750         var mainContent = document.getElementById("feature-list");
751         var successSubtree = document.importNode(document.getElementById("error-template").content, true);
752
753         var errorMessage = "Unable to load " + error.url;
754
755         if (error.request.status !== 200) {
756             errorMessage += ", status: " + error.request.status + " - " + error.request.statusText;
757         } else if (!error.response) {
758             errorMessage += ", the JSON file cannot be processed.";
759         }
760
761         successSubtree.querySelector("#error-message").textContent = errorMessage;
762
763         mainContent.appendChild(successSubtree);
764     }
765
766     Promise.all([loadJavaScriptCoreFeatures, loadWebCoreFeatures]).then(displayFeatures).catch(displayError);
767 }
768
769 document.addEventListener("DOMContentLoaded", initializeStatusPage);
770 </script>
771
772 <?php get_footer(); ?>