spectacular
Version:
Advanced BDD framework for CoffeeScript and JavaScript
344 lines (287 loc) • 11.8 kB
JavaScript
/*jshint curly: true, eqeqeq: true, immed: true, indent: 4, browser: true, jquery: true, evil: true, regexdash: true, browser: true, trailing: true, sub: true, unused: true, devel: true */
// author: andi smith
// website: www.andismith.com
// version: 0.1
var canIUse = (function () {
/* CONFIGURATION =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
// URL to the data feed
var SOURCE_DATA_URL = 'https://raw.github.com/Fyrd/caniuse/master/data.json';
/* Turn auto run on page load on or off with AUTO_RUN.
If you turn it off, just use canIUse.render() to kick it off again.
If you don't want the WhenCanIUse data to load in the background, turn BE_READY to false.
*/
var AUTO_RUN = true,
BE_READY = true;
/* Configure the browsers you want to show here.
The order defines the order they will appear on the page.
Browser options are:
* android - Android
* and_ff - Android Firefox
* and_chr - Android Chrome
* bb - Blackberry
* chrome - Google Chrome
* firefox - Mozilla Firefox
* ie - Internet Explorer
* ios_saf - iOS Safari
* opera - Opera
* op_mini - Opera Mini
* op_mob - Opera Mobile
* safari - Apple Safari
*/
var BROWSERS = ['chrome', 'firefox', 'ie', 'opera', 'safari', 'ios_saf', 'android', 'op_mob', 'and_chr', 'and_ff'];
// Customise HTML here
var TMPL_TITLE = '<h1>{title}</h1>', // feature title {title}
TMPL_STATUS = '<p class="status">{status}</p>', // feature status (W3C Recommendation) {status}
TMPL_DESCRIPTION = '<p>Supported from the following versions:</p>', // description to user
TMPL_DESKTOP_TITLE = '<h2>Desktop</h2>', // desktop header
TMPL_MOBILE_TITLE = '<h2>Mobile / Tablet</h2>', // mobile header
TMPL_SUPPORT_WRAPPER = '<ul class="agents">{items}</ul>', // support wrapper {items}
TMPL_SUPPORT = '<li title="{browser} - {support}" class="icon-{browsercode} {supportcode}"><span class="version">{version}{prefixed}</span></li>',
TMPL_PREFIX_NOTE = '<p>* denotes prefix required.</p>',
TMPL_LEGEND = '<ul class="legend"><li>Supported:</li><li class="y">Yes</li><li class="n">No</li><li class="a">Partially</li><li class="p">Polyfill</li></ul>',
TMPL_FOOTER = '<p class="stats">Stats from <a href="http://caniuse.com/#feat={feature}" target="_blank">caniuse.com</a></p>',
TMPL_LOADING = '<h1>Loading</h1>',
TMPL_ERROR = '<h1>Error</h1><p>Feature "{feature}" not found!</p>';
/* END CONFIGURATION =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
var canIUseData, // store data for multiple uses.
storeElementId, // temp storage for elementId if we need to JSONP request
populateOnLoad; // temp storage for if we should populate
// get feature data based on feature name
function getFeature(featureName) {
featureName = featureName.toLowerCase();
if (canIUseData.query) {
return canIUseData.query.results.json.data[featureName];
} else if (canIUseData.data) {
return canIUseData.data[featureName];
} else {
return null;
}
}
// get the full text description for the support status
function getSupportStatus(key) {
var status = {
"y": "Yes",
"x": "With Prefix",
"n": "No",
"a": "Partial Support",
"p": "Polyfill",
"u": "Unknown"
};
return status[key];
}
// get the specification status
function getSpecStatus(key) {
var status = {
"rec": "W3C Recommendation",
"pr": "W3C Proposed Recommendation",
"cr": "W3C Candidate Recommendation",
"wd": "W3C Working Draft",
"other": "Non-W3C, but Reputable",
"unoff": "Unofficial or W3C 'Note'"
};
return status[key] || "Unknown";
}
// find the first version that had this status
function find(needle, haystack) {
var result = {
"version": -1,
"prefixed": false
},
compare = -1;
for (var item in haystack) {
if (haystack.hasOwnProperty(item) && haystack[item].indexOf(needle) > -1) {
// some browser versions are formatted n-n, take the first number for comparison
compare = parseFloat(item.split('-')[0]);
// is this version lower than the current version we have stored?
if (result.version === -1 || result.version > compare) {
result.version = compare;
result.prefixed = (haystack[item].indexOf('x') > -1);
}
}
}
return result;
}
function findSupport(browserData) {
var status = ['y', 'a', 'p'],
result = {};
// find what support is available for this browser
for (var i = 0; i < status.length; i++) {
result = find(status[i], browserData);
if (result.version !== -1) {
return {
'result': status[i], // what type of support
'prefixed': result.prefixed,
'version': (result.version !== '0') ? result.version : '0' // the version with that support
};
}
}
return {
'result': 'n',
'prefixed': false,
'version': 'No'
};
}
/* put the data in a more platable format */
function generateResults(feature) {
var agents = {},
results = {},
currentBrowser = '',
support = {};
agents = canIUseData.agents || canIUseData.query.results.json.agents;
results.title = feature.title; // feature name
results.code = feature; // feature code?
results.status = getSpecStatus(feature.status); // feature specification status
results.agents = [];
for (var i = 0, l = BROWSERS.length; i < l; i++) {
currentBrowser = BROWSERS[i];
if (agents[currentBrowser]) {
support = findSupport(feature.stats[BROWSERS[i]]);
results.agents.push({
"browsercode": currentBrowser,
"prefixed": support.prefixed,
"supportcode": support.result,
"support": getSupportStatus(support.result),
"title": agents[currentBrowser].browser,
"type": agents[currentBrowser].type.toLowerCase(),
"version": support.version
});
}
}
return results;
}
function generateHtml(results) {
var html = '',
resultHtml = '',
desktopHtml = '',
mobileHtml = '',
prefixes = false,
result = {},
i = 0,
l = 0;
resultHtml = TMPL_TITLE.replace('{title}', results.title);
resultHtml += TMPL_STATUS.replace('{status}', results.status);
resultHtml += TMPL_DESCRIPTION;
for (i = 0, l = results.agents.length; i < l; i++) {
result = results.agents[i]; // simply things
// we need to show that prefix notice, captain
if (result.prefixed) {
prefixes = true;
}
html = TMPL_SUPPORT.replace(/\{browsercode\}/g, result.browsercode)
.replace(/\{prefixed\}/g, (result.prefixed === true) ? '*' : '')
.replace(/\{supportcode\}/g, result.supportcode)
.replace(/\{support\}/g, result.support)
.replace(/\{browser\}/g, result.title)
.replace(/\{version\}/g, result.version);
if (result.type === 'desktop') {
desktopHtml += html;
} else if (result.type === 'mobile') {
mobileHtml += html;
}
}
// only show if we are including desktop browsers
if (desktopHtml !== '') {
resultHtml += TMPL_DESKTOP_TITLE;
resultHtml += TMPL_SUPPORT_WRAPPER.replace(/\{items\}/g, desktopHtml);
}
// only show if we are including mobile browsers
if (mobileHtml !== '') {
resultHtml += TMPL_MOBILE_TITLE;
resultHtml += TMPL_SUPPORT_WRAPPER.replace(/\{items\}/g, mobileHtml);
}
if (prefixes) {
resultHtml += TMPL_PREFIX_NOTE;
}
resultHtml += TMPL_LEGEND;
resultHtml += TMPL_FOOTER.replace(/\{feature\}/g, results.featureCode);
return resultHtml;
}
function generate(elementId) {
var $canIUse = [],
$instance,
featureCode = '',
feature = {},
result = {},
i = 0,
l = 0;
if (typeof elementId === "undefined") {
$canIUse = document.querySelectorAll('.caniuse');
} else {
$canIUse.push(document.getElementById(elementId));
}
l = $canIUse.length;
for (i = 0; i < l; i++) {
$instance = $canIUse[i];
featureCode = $instance.getAttribute('data-feature') || 'unknown';
feature = getFeature(featureCode);
if (feature) {
result = generateResults(feature);
result.featureCode = featureCode;
$instance.innerHTML = generateHtml(result);
} else {
$instance.innerHTML = TMPL_ERROR.replace(/\{feature\}/g, featureCode);
}
}
}
function showLoading(elementId) {
var $canIUse = [],
$instance,
i = 0,
l = 0;
if (typeof elementId === "undefined") {
$canIUse = document.querySelectorAll('.caniuse');
} else {
$canIUse.push(document.getElementById(elementId));
}
l = $canIUse.length;
for (i = 0; i < l; i++) {
$instance = $canIUse[i];
$instance.innerHTML = TMPL_LOADING;
}
}
/*
* Load the data that will be used to display information.
*/
function loadData(elementId, populate) {
var url = '',
script = document.createElement('SCRIPT');
url = 'http://query.yahooapis.com/v1/public/yql?q=' +
'select * from json where url = \'' + SOURCE_DATA_URL + '\'' +
'&format=json&jsonCompat=new&callback=canIUseDataLoaded';
// remember these for when our JSONP returns
storeElementId = elementId;
populateOnLoad = populate;
script.src = url;
document.body.appendChild(script);
}
function populate(elementId) {
if (typeof canIUseData === 'undefined') {
showLoading(elementId);
loadData(elementId, true);
} else {
generate(elementId);
}
}
/*
* Public Methods
*/
return {
render: populate,
dataLoaded: function (data) {
canIUseData = data;
if (populateOnLoad) {
generate(storeElementId);
}
},
init: (function () {
if (AUTO_RUN) {
populate();
} else if (BE_READY) {
loadData(undefined, false);
}
})()
};
}());
function canIUseDataLoaded(data) {
canIUse.dataLoaded(data);
}