зеркало из https://github.com/mozilla/kitsune.git
[bug 651225] Revamp showfor semantics for Firefox 5 and future quick release cycles.
{for} expressions like "fx4" now mean "fx 4 and up". Saying "=fx4" goes back to exact-matching semantics. Haven't looked at the rat's nest of special cases about mobile and desktop switcheroos yet. Hoping Ricky will help with that. (pleasepleaseplease)
This commit is contained in:
Родитель
369c9dc07a
Коммит
4cf188a682
|
@ -37,10 +37,20 @@ from wiki.tasks import send_reviewed_notification, schedule_rebuild_kb
|
|||
log = logging.getLogger('k.wiki')
|
||||
|
||||
|
||||
def _split_browser_slug(slug):
|
||||
"""Given something like fx35, split it into an alphabetic prefix and a
|
||||
suffix, returning a 2-tuple like ('fx', '35')."""
|
||||
right = slug.lstrip(ascii_letters)
|
||||
left_len = len(slug) - len(right)
|
||||
return slug[:left_len], slug[left_len:]
|
||||
|
||||
|
||||
OS_ABBR_JSON = json.dumps(dict([(o.slug, True)
|
||||
for o in OPERATING_SYSTEMS]))
|
||||
BROWSER_ABBR_JSON = json.dumps(dict([(v.slug, v.show_in_ui)
|
||||
for v in FIREFOX_VERSIONS]))
|
||||
BROWSER_ABBR_JSON = json.dumps(
|
||||
dict([(v.slug, {'product': _split_browser_slug(v.slug)[0],
|
||||
'maxFloatVersion': v.max_version})
|
||||
for v in FIREFOX_VERSIONS]))
|
||||
|
||||
|
||||
def _version_groups(versions):
|
||||
|
@ -49,16 +59,9 @@ def _version_groups(versions):
|
|||
See test_version_groups for an example.
|
||||
|
||||
"""
|
||||
def split_slug(slug):
|
||||
"""Given something like fx35, split it into an alphabetic prefix and a
|
||||
suffix, returning a 2-tuple like ('fx', '35')."""
|
||||
right = slug.lstrip(ascii_letters)
|
||||
left_len = len(slug) - len(right)
|
||||
return slug[:left_len], slug[left_len:]
|
||||
|
||||
slug_groups = {}
|
||||
for v in versions:
|
||||
left, right = split_slug(v.slug)
|
||||
left, right = _split_browser_slug(v.slug)
|
||||
slug_groups.setdefault(left, []).append((v.max_version, right))
|
||||
for g in slug_groups.itervalues():
|
||||
g.sort()
|
||||
|
|
|
@ -65,13 +65,38 @@ var ShowFor = {
|
|||
isSetManually;
|
||||
|
||||
OSES = $osMenu.data('oses'); // {'mac': true, 'win': true, ...}
|
||||
BROWSERS = $browserMenu.data('browsers'); // {'fx4': true, ...}
|
||||
BROWSERS = $browserMenu.data('browsers'); // {'fx4': {product: 'fx', maxFloatVersion: 4.9999}, ...}
|
||||
VERSIONS = $browserMenu.data('version-groups'); // {'fx': [[3.4999, '3'], [3.9999, '35']], 'm': [[1.0999, '1'], [1.9999, '11']]}
|
||||
MISSING_MSG = gettext('[missing header]');
|
||||
|
||||
// Make the 'Table of Contents' header localizable.
|
||||
$('#toc > h2').text(gettext('Table of Contents'));
|
||||
|
||||
// Given a symbol like 'm4' or '=fx4', return something like
|
||||
// {comparator: '>', product: 'm', version: 4.9999}. Even if there's no
|
||||
// explicit comparator in the symbol, the comparator that's assumed
|
||||
// will be made explicit in the returned object. If it's not a known
|
||||
// product/version combo, return undefined.
|
||||
function conditionFromSymbol(symbol) {
|
||||
var slug, browser, comparator;
|
||||
|
||||
// TODO: Put crappy special cases here.
|
||||
|
||||
// Figure out comparator:
|
||||
if (symbol.substring(0, 1) == '=') {
|
||||
comparator = '=';
|
||||
slug = symbol.substring(1);
|
||||
} else { // If no leading =, assume >=.
|
||||
comparator = '>=';
|
||||
slug = symbol;
|
||||
}
|
||||
|
||||
browser = BROWSERS[slug];
|
||||
return {comparator: comparator,
|
||||
product: browser.product,
|
||||
version: browser.maxFloatVersion};
|
||||
}
|
||||
|
||||
function updateForsAndToc(calledOnLoad) {
|
||||
// Hide and show document sections accordingly:
|
||||
showAndHideFors($osMenu.val(), $browserMenu.val());
|
||||
|
@ -97,25 +122,64 @@ var ShowFor = {
|
|||
// Set the {for} nodes to the proper visibility for the given OS and
|
||||
// browser combination.
|
||||
//
|
||||
// Hidden are {for}s that {list at least one OS but not the passed-in one}
|
||||
// or that {list at least one browser but not the passed-in one}. Also, the
|
||||
// entire condition can be inverted by prefixing it with "not ", as in {for
|
||||
// not mac,linux}.
|
||||
// Hidden are {for}s that {list at least one OS but not the passed-in
|
||||
// one} or that {list at least one browser expression but none matching
|
||||
// the passed-in one}. Also, the entire condition can be inverted by
|
||||
// prefixing it with "not ", as in {for not mac,linux}.
|
||||
//
|
||||
// Takes a browser slug like "fx4" rather than a browser code and a
|
||||
// raw floating-point version because it has to be able to take both
|
||||
// detected browsers and slugs chosen explicitly from the <select>.
|
||||
function showAndHideFors(os, browser) {
|
||||
$container.find('.for').each(function(index) {
|
||||
var osAttrs = {}, browserAttrs = {},
|
||||
var osAttrs = {}, browserAttrs = {}, // TODO: Eliminate browserAttrs?
|
||||
foundAnyOses = false, foundAnyBrowsers = false,
|
||||
forData,
|
||||
isInverted,
|
||||
shouldHide;
|
||||
shouldHide,
|
||||
browserConditions = [];
|
||||
|
||||
// Catch the "not" operator if it's there:
|
||||
// Return whether the given browser slug matches any of the
|
||||
// given conditions. Passing an unknown slug results in
|
||||
// undefined behavior.
|
||||
// TODO: Implement with a generic any() instead--maybe underscore's.
|
||||
function meetsAnyOfConditions(slug, conditions) {
|
||||
// Return whether a slug (like 'fx4' or 'fx35') meets a condition like {comparator: '>' product: 'm', version: 4.9999}.
|
||||
function meets(slug, condition) { // 'fx4' is matched by {comparator: '=', product: 'fx', version: 3.9999}?
|
||||
var browser = BROWSERS[slug];
|
||||
if (browser.product != condition.product) {
|
||||
return false;
|
||||
}
|
||||
switch (condition.comparator) {
|
||||
case '=':
|
||||
// =fx35 --> {comparator: '=' browser: 'fx', version: 3.9999}
|
||||
return browser.maxFloatVersion == condition.version;
|
||||
case '>=':
|
||||
// fx4 --> {comparator: '>=' browser: 'fx', version: 4.9999}
|
||||
return browser.maxFloatVersion >= condition.version;
|
||||
// Insert '<' here someday.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < conditions.length; i++) {
|
||||
if (meets(slug, conditions[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function slugWithoutComparators(slug) {
|
||||
return (slug.substring(0, 1) == '=') ? slug.substring(1) : slug;
|
||||
}
|
||||
|
||||
// If the data-for attribute is missing, return.
|
||||
forData = $(this).data('for');
|
||||
if (!forData) {
|
||||
// If the data-for attribute is missing, move on.
|
||||
return;
|
||||
}
|
||||
|
||||
// Catch the "not" operator if it's there:
|
||||
isInverted = forData.substring(0, 4) == 'not ';
|
||||
if (isInverted) {
|
||||
forData = forData.substring(4); // strip off "not "
|
||||
|
@ -126,15 +190,16 @@ var ShowFor = {
|
|||
if (OSES[this] != undefined) {
|
||||
osAttrs[this] = true;
|
||||
foundAnyOses = true;
|
||||
} else if (BROWSERS[this] != undefined) {
|
||||
} else if (BROWSERS[slugWithoutComparators(this)] !== undefined) {
|
||||
browserAttrs[this] = true;
|
||||
browserConditions.push(conditionFromSymbol(this));
|
||||
foundAnyBrowsers = true;
|
||||
}
|
||||
});
|
||||
|
||||
shouldHide = ((foundAnyOses && osAttrs[os] == undefined) ||
|
||||
(foundAnyBrowsers && browserAttrs[browser] == undefined)) &&
|
||||
// Special cases ):
|
||||
(foundAnyBrowsers && !meetsAnyOfConditions(browser, browserConditions))) &&
|
||||
// Special cases:
|
||||
// TODO: make this easier to maintain somehow?
|
||||
// Show android/m4 on desktop selection
|
||||
!(osAttrs['android'] && os !== 'maemo' /* only one mobile browser ATM */) &&
|
||||
|
@ -143,10 +208,9 @@ var ShowFor = {
|
|||
!(osAttrs['win'] && (os === 'android' || os == 'maemo') && (browserAttrs['fx4'] || !foundAnyBrowsers)) &&
|
||||
!(browserAttrs['fx4'] && browser === 'm4' && (osAttrs['win'] || !foundAnyOses));
|
||||
|
||||
if ((shouldHide && !isInverted) || (!shouldHide && isInverted)) {
|
||||
if (shouldHide != isInverted) {
|
||||
$(this).hide(); // saves original visibility, which is nice but not necessary
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$(this).show(); // restores original visibility
|
||||
}
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче