nav tabs on admin dashboard
This commit is contained in:
+324
-82
@@ -1,13 +1,31 @@
|
||||
var jsReleases = require('node-releases/data/processed/envs.json')
|
||||
var agents = require('caniuse-lite/dist/unpacker/agents').agents
|
||||
var jsEOL = require('node-releases/data/release-schedule/release-schedule.json')
|
||||
var path = require('path')
|
||||
var e2c = require('electron-to-chromium/versions')
|
||||
|
||||
var agents = require('caniuse-lite/dist/unpacker/agents').agents
|
||||
var region = require('caniuse-lite/dist/unpacker/region').default
|
||||
|
||||
var BrowserslistError = require('./error')
|
||||
var env = require('./node') // Will load browser.js in webpack
|
||||
|
||||
var FLOAT_RANGE = /^\d+(\.\d+)?(-\d+(\.\d+)?)*$/
|
||||
var YEAR = 365.259641 * 24 * 60 * 60 * 1000
|
||||
|
||||
// Enum values MUST be powers of 2, so combination are safe
|
||||
/** @constant {number} */
|
||||
var QUERY_OR = 1
|
||||
/** @constant {number} */
|
||||
var QUERY_AND = 2
|
||||
|
||||
function isVersionsMatch (versionA, versionB) {
|
||||
return (versionA + '.').indexOf(versionB + '.') === 0
|
||||
}
|
||||
|
||||
function isEolReleased (name) {
|
||||
var version = name.slice(1)
|
||||
return jsReleases.some(function (i) {
|
||||
return isVersionsMatch(i.version, version)
|
||||
})
|
||||
}
|
||||
|
||||
function normalize (versions) {
|
||||
return versions.filter(function (version) {
|
||||
@@ -91,21 +109,8 @@ function normalizeVersion (data, version) {
|
||||
}
|
||||
}
|
||||
|
||||
function loadCountryStatistics (country) {
|
||||
country = country.replace(/[^\w-]/g, '')
|
||||
if (!browserslist.usage[country]) {
|
||||
var usage = { }
|
||||
// eslint-disable-next-line security/detect-non-literal-require
|
||||
var compressed = require('caniuse-lite/data/regions/' + country + '.js')
|
||||
var data = region(compressed)
|
||||
for (var i in data) {
|
||||
fillUsage(usage, i, data[i])
|
||||
}
|
||||
browserslist.usage[country] = usage
|
||||
}
|
||||
}
|
||||
|
||||
function filterByYear (since) {
|
||||
since = since / 1000
|
||||
return Object.keys(agents).reduce(function (selected, name) {
|
||||
var data = byName(name)
|
||||
if (!data) return selected
|
||||
@@ -128,10 +133,30 @@ function checkName (name) {
|
||||
return data
|
||||
}
|
||||
|
||||
function unknownQuery (query) {
|
||||
return new BrowserslistError(
|
||||
'Unknown browser query `' + query + '`. ' +
|
||||
'Maybe you are using old Browserslist or made typo in query.'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves queries into a browser list.
|
||||
* @param {string|string[]} queries Queries to combine.
|
||||
* Either an array of queries or a long string of queries.
|
||||
* @param {object} [context] Optional arguments to
|
||||
* the select function in `queries`.
|
||||
* @returns {string[]} A list of browsers
|
||||
*/
|
||||
function resolve (queries, context) {
|
||||
return queries.reduce(function (result, selection, index) {
|
||||
selection = selection.trim()
|
||||
if (selection === '') return result
|
||||
if (Array.isArray(queries)) {
|
||||
queries = flatten(queries.map(parse))
|
||||
} else {
|
||||
queries = parse(queries)
|
||||
}
|
||||
|
||||
return queries.reduce(function (result, query, index) {
|
||||
var selection = query.queryString
|
||||
|
||||
var isExclude = selection.indexOf('not ') === 0
|
||||
if (isExclude) {
|
||||
@@ -148,20 +173,48 @@ function resolve (queries, context) {
|
||||
var match = selection.match(type.regexp)
|
||||
if (match) {
|
||||
var args = [context].concat(match.slice(1))
|
||||
var array = type.select.apply(browserslist, args)
|
||||
if (isExclude) {
|
||||
array = array.concat(array.map(function (j) {
|
||||
return j.replace(/\s\d+/, ' 0')
|
||||
}))
|
||||
return result.filter(function (j) {
|
||||
return array.indexOf(j) === -1
|
||||
})
|
||||
var array = type.select.apply(browserslist, args).map(function (j) {
|
||||
var parts = j.split(' ')
|
||||
if (parts[1] === '0') {
|
||||
return parts[0] + ' ' + byName(parts[0]).versions[0]
|
||||
} else {
|
||||
return j
|
||||
}
|
||||
})
|
||||
|
||||
switch (query.type) {
|
||||
case QUERY_AND:
|
||||
if (isExclude) {
|
||||
return result.filter(function (j) {
|
||||
// remove result items that are in array
|
||||
// (the relative complement of array in result)
|
||||
return array.indexOf(j) === -1
|
||||
})
|
||||
} else {
|
||||
return result.filter(function (j) {
|
||||
// remove result items not in array
|
||||
// (intersect of result and array)
|
||||
return array.indexOf(j) !== -1
|
||||
})
|
||||
}
|
||||
case QUERY_OR:
|
||||
default:
|
||||
if (isExclude) {
|
||||
var filter = { }
|
||||
array.forEach(function (j) {
|
||||
filter[j] = true
|
||||
})
|
||||
return result.filter(function (j) {
|
||||
return !filter[j]
|
||||
})
|
||||
}
|
||||
// union of result and array
|
||||
return result.concat(array)
|
||||
}
|
||||
return result.concat(array)
|
||||
}
|
||||
}
|
||||
|
||||
throw new BrowserslistError('Unknown browser query `' + selection + '`')
|
||||
throw unknownQuery(selection)
|
||||
}, [])
|
||||
}
|
||||
|
||||
@@ -169,16 +222,20 @@ function resolve (queries, context) {
|
||||
* Return array of browsers by selection queries.
|
||||
*
|
||||
* @param {(string|string[])} [queries=browserslist.defaults] Browser queries.
|
||||
* @param {object} opts Options.
|
||||
* @param {object} [opts] Options.
|
||||
* @param {string} [opts.path="."] Path to processed file.
|
||||
* It will be used to find config files.
|
||||
* @param {string} [opts.env="development"] Processing environment.
|
||||
* It will be used to take right
|
||||
* queries from config file.
|
||||
* @param {string} [opts.env="production"] Processing environment.
|
||||
* It will be used to take right
|
||||
* queries from config file.
|
||||
* @param {string} [opts.config] Path to config file with queries.
|
||||
* @param {object} [opts.stats] Custom browser usage statistics
|
||||
* for "> 1% in my stats" query.
|
||||
* @return {string[]} Array with browser names in Can I Use.
|
||||
* @param {boolean} [opts.ignoreUnknownVersions=false] Do not throw on unknown
|
||||
* version in direct query.
|
||||
* @param {boolean} [opts.dangerousExtend] Disable security checks
|
||||
* for extend query.
|
||||
* @returns {string[]} Array with browser names in Can I Use.
|
||||
*
|
||||
* @example
|
||||
* browserslist('IE >= 10, IE 8') //=> ['ie 11', 'ie 10', 'ie 8']
|
||||
@@ -186,12 +243,12 @@ function resolve (queries, context) {
|
||||
function browserslist (queries, opts) {
|
||||
if (typeof opts === 'undefined') opts = { }
|
||||
|
||||
if (!opts.hasOwnProperty('path')) {
|
||||
if (typeof opts.path === 'undefined') {
|
||||
opts.path = path.resolve ? path.resolve('.') : '.'
|
||||
}
|
||||
|
||||
if (typeof queries === 'undefined' || queries === null) {
|
||||
var config = env.loadConfig(opts)
|
||||
var config = browserslist.loadConfig(opts)
|
||||
if (config) {
|
||||
queries = config
|
||||
} else {
|
||||
@@ -199,38 +256,26 @@ function browserslist (queries, opts) {
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof queries === 'string') {
|
||||
queries = queries.split(/,\s*/)
|
||||
}
|
||||
|
||||
if (!Array.isArray(queries)) {
|
||||
if (!(typeof queries === 'string' || Array.isArray(queries))) {
|
||||
throw new BrowserslistError(
|
||||
'Browser queries must be an array. Got ' + typeof queries + '.')
|
||||
'Browser queries must be an array or string. Got ' + typeof queries + '.')
|
||||
}
|
||||
|
||||
var context = { dangerousExtend: opts.dangerousExtend }
|
||||
var context = {
|
||||
ignoreUnknownVersions: opts.ignoreUnknownVersions,
|
||||
dangerousExtend: opts.dangerousExtend
|
||||
}
|
||||
|
||||
var stats = env.getStat(opts)
|
||||
env.oldDataWarning(browserslist.data)
|
||||
var stats = env.getStat(opts, browserslist.data)
|
||||
if (stats) {
|
||||
if ('dataByBrowser' in stats) {
|
||||
stats = stats.dataByBrowser
|
||||
}
|
||||
context.customUsage = { }
|
||||
for (var browser in stats) {
|
||||
fillUsage(context.customUsage, browser, stats[browser])
|
||||
}
|
||||
}
|
||||
|
||||
var result = resolve(queries, context).map(function (i) {
|
||||
var parts = i.split(' ')
|
||||
var name = parts[0]
|
||||
var version = parts[1]
|
||||
if (version === '0') {
|
||||
return name + ' ' + byName(name).versions[0]
|
||||
} else {
|
||||
return i
|
||||
}
|
||||
}).sort(function (name1, name2) {
|
||||
var result = resolve(queries, context).sort(function (name1, name2) {
|
||||
name1 = name1.split(' ')
|
||||
name2 = name2.split(' ')
|
||||
if (name1[0] === name2[0]) {
|
||||
@@ -247,6 +292,74 @@ function browserslist (queries, opts) {
|
||||
return uniq(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} BrowserslistQuery
|
||||
* @property {number} type A type constant like QUERY_OR @see QUERY_OR.
|
||||
* @property {string} queryString A query like "not ie < 11".
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parse a browserslist string query
|
||||
* @param {string} queries One or more queries as a string
|
||||
* @returns {BrowserslistQuery[]} An array of BrowserslistQuery
|
||||
*/
|
||||
function parse (queries) {
|
||||
var qs = []
|
||||
|
||||
do {
|
||||
queries = doMatch(queries, qs)
|
||||
} while (queries)
|
||||
|
||||
return qs
|
||||
}
|
||||
|
||||
/**
|
||||
* Find query matches in a string. This function is meant to be called
|
||||
* repeatedly with the returned query string until there is no more matches.
|
||||
* @param {string} string A string with one or more queries.
|
||||
* @param {BrowserslistQuery[]} qs Out parameter,
|
||||
* will be filled with `BrowserslistQuery`.
|
||||
* @returns {string} The rest of the query string minus the matched part.
|
||||
*/
|
||||
function doMatch (string, qs) {
|
||||
var or = /^(?:,\s*|\s+OR\s+)(.*)/i
|
||||
var and = /^\s+AND\s+(.*)/i
|
||||
|
||||
return find(
|
||||
string,
|
||||
function (parsed, n, max) {
|
||||
if (and.test(parsed)) {
|
||||
qs.unshift({ type: QUERY_AND, queryString: parsed.match(and)[1] })
|
||||
return true
|
||||
} else if (or.test(parsed)) {
|
||||
qs.unshift({ type: QUERY_OR, queryString: parsed.match(or)[1] })
|
||||
return true
|
||||
} else if (n === max) {
|
||||
qs.unshift({ type: QUERY_OR, queryString: parsed.trim() })
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function find (string, predicate) {
|
||||
for (var n = 1, max = string.length; n <= max; n++) {
|
||||
var parsed = string.substr(-n, n)
|
||||
if (predicate(parsed, n, max)) {
|
||||
return string.replace(parsed, '')
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function flatten (array) {
|
||||
if (!Array.isArray(array)) return [array]
|
||||
return array.reduce(function (a, b) {
|
||||
return a.concat(flatten(b))
|
||||
}, [])
|
||||
}
|
||||
|
||||
// Will be filled by Can I Use data below
|
||||
browserslist.data = { }
|
||||
browserslist.usage = {
|
||||
@@ -256,9 +369,10 @@ browserslist.usage = {
|
||||
|
||||
// Default browsers query
|
||||
browserslist.defaults = [
|
||||
'> 1%',
|
||||
'> 0.5%',
|
||||
'last 2 versions',
|
||||
'Firefox ESR'
|
||||
'Firefox ESR',
|
||||
'not dead'
|
||||
]
|
||||
|
||||
// Browser names aliases
|
||||
@@ -284,34 +398,61 @@ browserslist.clearCaches = env.clearCaches
|
||||
browserslist.parseConfig = env.parseConfig
|
||||
browserslist.readConfig = env.readConfig
|
||||
browserslist.findConfig = env.findConfig
|
||||
browserslist.loadConfig = env.loadConfig
|
||||
|
||||
/**
|
||||
* Return browsers market coverage.
|
||||
*
|
||||
* @param {string[]} browsers Browsers names in Can I Use.
|
||||
* @param {string} [country="global"] Which country statistics should be used.
|
||||
* @param {string|object} [stats="global"] Which statistics should be used.
|
||||
* Country code or custom statistics.
|
||||
* Pass `"my stats"` to load statistics
|
||||
* from Browserslist files.
|
||||
*
|
||||
* @return {number} Total market coverage for all selected browsers.
|
||||
*
|
||||
* @example
|
||||
* browserslist.coverage(browserslist('> 1% in US'), 'US') //=> 83.1
|
||||
*/
|
||||
browserslist.coverage = function (browsers, country) {
|
||||
if (country && country !== 'global') {
|
||||
if (country.length > 2) {
|
||||
country = country.toLowerCase()
|
||||
} else {
|
||||
country = country.toUpperCase()
|
||||
browserslist.coverage = function (browsers, stats) {
|
||||
var data
|
||||
if (typeof stats === 'undefined') {
|
||||
data = browserslist.usage.global
|
||||
} else if (stats === 'my stats') {
|
||||
var opts = {}
|
||||
opts.path = path.resolve ? path.resolve('.') : '.'
|
||||
var customStats = env.getStat(opts)
|
||||
if (!customStats) {
|
||||
throw new BrowserslistError('Custom usage statistics was not provided')
|
||||
}
|
||||
loadCountryStatistics(country)
|
||||
data = {}
|
||||
for (var browser in customStats) {
|
||||
fillUsage(data, browser, customStats[browser])
|
||||
}
|
||||
} else if (typeof stats === 'string') {
|
||||
if (stats.length > 2) {
|
||||
stats = stats.toLowerCase()
|
||||
} else {
|
||||
stats = stats.toUpperCase()
|
||||
}
|
||||
env.loadCountry(browserslist.usage, stats)
|
||||
data = browserslist.usage[stats]
|
||||
} else {
|
||||
country = 'global'
|
||||
if ('dataByBrowser' in stats) {
|
||||
stats = stats.dataByBrowser
|
||||
}
|
||||
data = { }
|
||||
for (var name in stats) {
|
||||
for (var version in stats[name]) {
|
||||
data[name + ' ' + version] = stats[name][version]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return browsers.reduce(function (all, i) {
|
||||
var usage = browserslist.usage[country][i]
|
||||
var usage = data[i]
|
||||
if (usage === undefined) {
|
||||
usage = browserslist.usage[country][i.replace(/ [\d.]+$/, ' 0')]
|
||||
usage = data[i.replace(/ \S+$/, ' 0')]
|
||||
}
|
||||
return all + (usage || 0)
|
||||
}, 0)
|
||||
@@ -407,12 +548,9 @@ var QUERIES = [
|
||||
}
|
||||
},
|
||||
{
|
||||
regexp: /^last\s+(\d+)\s+years?$/i,
|
||||
regexp: /^last\s+(\d*.?\d+)\s+years?$/i,
|
||||
select: function (context, years) {
|
||||
var date = new Date()
|
||||
var since = date.setFullYear(date.getFullYear() - years) / 1000
|
||||
|
||||
return filterByYear(since)
|
||||
return filterByYear(Date.now() - YEAR * years)
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -421,9 +559,7 @@ var QUERIES = [
|
||||
year = parseInt(year)
|
||||
month = parseInt(month || '01') - 1
|
||||
date = parseInt(date || '01')
|
||||
var since = Date.UTC(year, month, date, 0, 0, 0) / 1000
|
||||
|
||||
return filterByYear(since)
|
||||
return filterByYear(Date.UTC(year, month, date, 0, 0, 0))
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -494,7 +630,7 @@ var QUERIES = [
|
||||
place = place.toLowerCase()
|
||||
}
|
||||
|
||||
loadCountryStatistics(place)
|
||||
env.loadCountry(browserslist.usage, place)
|
||||
var usage = browserslist.usage[place]
|
||||
|
||||
return Object.keys(usage).reduce(function (result, version) {
|
||||
@@ -517,6 +653,52 @@ var QUERIES = [
|
||||
}, [])
|
||||
}
|
||||
},
|
||||
{
|
||||
regexp: /^cover\s+(\d*\.?\d+)%(\s+in\s+(my\s+stats|(alt-)?\w\w))?$/,
|
||||
select: function (context, coverage, statMode) {
|
||||
coverage = parseFloat(coverage)
|
||||
|
||||
var usage = browserslist.usage.global
|
||||
if (statMode) {
|
||||
if (statMode.match(/^\s+in\s+my\s+stats$/)) {
|
||||
if (!context.customUsage) {
|
||||
throw new BrowserslistError(
|
||||
'Custom usage statistics was not provided'
|
||||
)
|
||||
}
|
||||
usage = context.customUsage
|
||||
} else {
|
||||
var match = statMode.match(/\s+in\s+((alt-)?\w\w)/)
|
||||
var place = match[1]
|
||||
if (place.length === 2) {
|
||||
place = place.toUpperCase()
|
||||
} else {
|
||||
place = place.toLowerCase()
|
||||
}
|
||||
env.loadCountry(browserslist.usage, place)
|
||||
usage = browserslist.usage[place]
|
||||
}
|
||||
}
|
||||
|
||||
var versions = Object.keys(usage).sort(function (a, b) {
|
||||
return usage[b] - usage[a]
|
||||
})
|
||||
|
||||
var coveraged = 0
|
||||
var result = []
|
||||
var version
|
||||
for (var i = 0; i <= versions.length; i++) {
|
||||
version = versions[i]
|
||||
if (usage[version] === 0) break
|
||||
|
||||
coveraged += usage[version]
|
||||
result.push(version)
|
||||
if (coveraged >= coverage) break
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
},
|
||||
{
|
||||
regexp: /^electron\s+([\d.]+)\s*-\s*([\d.]+)$/i,
|
||||
select: function (context, from, to) {
|
||||
@@ -581,7 +763,7 @@ var QUERIES = [
|
||||
{
|
||||
regexp: /^(firefox|ff|fx)\s+esr$/i,
|
||||
select: function () {
|
||||
return ['firefox 52']
|
||||
return ['firefox 60']
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -601,6 +783,46 @@ var QUERIES = [
|
||||
return ['chrome ' + chrome]
|
||||
}
|
||||
},
|
||||
{
|
||||
regexp: /^node\s+(\d+(\.\d+)?(\.\d+)?)$/i,
|
||||
select: function (context, version) {
|
||||
var nodeReleases = jsReleases.filter(function (i) {
|
||||
return i.name === 'nodejs'
|
||||
})
|
||||
var matched = nodeReleases.filter(function (i) {
|
||||
return isVersionsMatch(i.version, version)
|
||||
})
|
||||
if (matched.length === 0) {
|
||||
if (context.ignoreUnknownVersions) {
|
||||
return []
|
||||
} else {
|
||||
throw new BrowserslistError(
|
||||
'Unknown version ' + version + ' of Node.js')
|
||||
}
|
||||
}
|
||||
return ['node ' + matched[matched.length - 1].version]
|
||||
}
|
||||
},
|
||||
{
|
||||
regexp: /^current\s+node$/i,
|
||||
select: function (context) {
|
||||
return [env.currentNode(resolve, context)]
|
||||
}
|
||||
},
|
||||
{
|
||||
regexp: /^maintained\s+node\s+versions$/i,
|
||||
select: function (context) {
|
||||
var now = Date.now()
|
||||
var queries = Object.keys(jsEOL).filter(function (key) {
|
||||
return now < Date.parse(jsEOL[key].end) &&
|
||||
now > Date.parse(jsEOL[key].start) &&
|
||||
isEolReleased(key)
|
||||
}).map(function (key) {
|
||||
return 'node ' + key.slice(1)
|
||||
})
|
||||
return resolve(queries, context)
|
||||
}
|
||||
},
|
||||
{
|
||||
regexp: /^(\w+)\s+(tp|[\d.]+)$/i,
|
||||
select: function (context, name, version) {
|
||||
@@ -612,12 +834,14 @@ var QUERIES = [
|
||||
} else {
|
||||
if (version.indexOf('.') === -1) {
|
||||
alias = version + '.0'
|
||||
} else if (/\.0$/.test(version)) {
|
||||
} else {
|
||||
alias = version.replace(/\.0$/, '')
|
||||
}
|
||||
alias = normalizeVersion(data, alias)
|
||||
if (alias) {
|
||||
version = alias
|
||||
} else if (context.ignoreUnknownVersions) {
|
||||
return []
|
||||
} else {
|
||||
throw new BrowserslistError(
|
||||
'Unknown version ' + version + ' of ' + name)
|
||||
@@ -637,6 +861,24 @@ var QUERIES = [
|
||||
select: function () {
|
||||
return browserslist(browserslist.defaults)
|
||||
}
|
||||
},
|
||||
{
|
||||
regexp: /^dead$/i,
|
||||
select: function (context) {
|
||||
var dead = ['ie <= 10', 'ie_mob <= 10', 'bb <= 10', 'op_mob <= 12.1']
|
||||
return resolve(dead, context)
|
||||
}
|
||||
},
|
||||
{
|
||||
regexp: /^(\w+)$/i,
|
||||
select: function (context, name) {
|
||||
if (byName(name)) {
|
||||
throw new BrowserslistError(
|
||||
'Specify versions in Browserslist query for browser ' + name)
|
||||
} else {
|
||||
throw unknownQuery(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user