[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:
Erik Rose 2011-05-18 21:55:22 -07:00
Родитель 369c9dc07a
Коммит 4cf188a682
2 изменённых файлов: 92 добавлений и 25 удалений

Просмотреть файл

@ -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
}
});