bug 348731, 348554, unify test harness, one ring to test them all, one page to watch it

This commit is contained in:
axel%pike.org 2006-08-28 22:24:57 +00:00
parent 4864773bb2
commit d076e64b5e
13 changed files with 1413 additions and 605 deletions

View File

@ -51,7 +51,10 @@ exceptions = [
__regify(('mail|browser', 'defines.inc', 'MOZ_LANGPACK_CONTRIBUTORS')),
# ignore search engine order for browser
__regify(('browser', 'chrome\\/browser-region\\/region\\.properties',
'browser\\.search\\.order\.[1-9]'))]
'browser\\.search\\.order\.[1-9]')),
# ignore feed engine order for browser
__regify(('browser', 'chrome\\/browser-region\\/region\\.properties',
'browser\\.contentHandlers\\.types\.[0-5]'))]
def __dont_ignore(tpl):
for mod, path, key in exceptions:

View File

@ -0,0 +1,300 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is l10n test automation.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation
# Portions created by the Initial Developer are Copyright (C) 2006
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Axel Hecht <l10n@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import logging
import Paths
import Parser
class Base:
'''Base class for all tests'''
def __init__(self):
if not hasattr(self, 'leafName'):
self.leafName = 'TODO'
logging.warning(' ' + self + ' does not have leafName set, writing to TODO')
def run(self):
'''Run this test for all locales, returns a dictionary with results'''
pass
def serialize(self, result, saveHandler):
'''Serialize the previously generated result, writes the dictionary
by default'''
return saveHandler(result, self.leafName)
pass
def failureTest(self, myResult, failureResult):
pass
import CompareLocales
class CompareTest(Base):
'''Test class to compare locales'''
def __init__(self):
'''Initializes the test object'''
# nothing to be done here
pass
def run(self):
'''Runs CompareLocales.compare()'''
return CompareLocales.compare()
def serialize(self, result, saveHandler):
'''Serialize the CompareLocales result by locale into
cmp-details-ab-CD
and a compacted version into
cmp-data
'''
class Separator:
def __init__(self):
self.leafBase = 'cmp-details-'
def getDetails(self, res, locale):
dic = {}
res[locale]['tested'].sort()
self.collectList('missing', res[locale], dic)
self.collectList('obsolete', res[locale], dic)
saveHandler(dic, self.leafBase + locale + '.json')
def collectList(self, name, res, dic):
dic[name] = {}
if not res.has_key(name):
res[name] = []
counts = dict([(mod,0) for mod in res['tested']])
counts['total'] = len(res[name])
for mod, path, key in res[name]:
counts[Paths.components[mod]] +=1
if not dic[name].has_key(mod):
dic[name][mod] = {path:[key]}
continue
if not dic[name][mod].has_key(path):
dic[name][mod][path] = [key]
else:
dic[name][mod][path].append(key)
res[name] = counts
name += 'Files'
dic[name] = {}
if not res.has_key(name):
res[name] = []
counts = dict([(mod,0) for mod in res['tested']])
counts['total'] = len(res[name])
for mod, path in res[name]:
counts[Paths.components[mod]] +=1
if not dic[name].has_key(mod):
dic[name][mod] = [path]
else:
dic[name][mod].append(path)
res[name] = counts
s = Separator()
for loc, lResult in result.iteritems():
s.getDetails(result, loc)
saveHandler(result, 'cmp-data.json')
def failureTest(self, myResult, failureResult):
'''signal pass/warn/failure for each locale'''
def sumdata(data, part):
res = 0
for mod in [u'browser', u'toolkit']:
res += data[part][mod] + data[part + u'Files'][mod]
return res
def getState(data):
ret = 0
if sumdata(data, u'obsolete') > 0:
ret |= 1
if sumdata(data, u'missing') > 0:
ret |= 2
return ret
for loc, val in myResult.iteritems():
if not failureResult.has_key(loc):
failureResult[loc] = getState(val)
else:
failureResult |= getState(val)
from xml import sax
from types import DictType, FunctionType
import md5
from codecs import utf_8_encode
import re
import os
class SearchTest(Base):
"""Test class to collect information from search plugins and to
verify that they're doing something good.
"""
def __init__(self):
'''Set up the test class with a good leaf name'''
self.leafName = 'search-results.json'
pass
def run(self):
'''Collect all data from the MozSearch plugins in both the /cvsroot
repository for en-US and the /l10n repository for locales
'''
class DummyHandler(sax.handler.ContentHandler):
def startDocument(self):
self.md5 = md5.new()
self.indent = ''
self.engine = {'urls':[]}
self.lNames = []
self.textField = None
return
def endDocument(self):
self.engine['md5'] = self.md5.hexdigest()
return
def startElementNS(self, (ns, local), qname, attrs):
self.indent += ' '
if ns != u'http://www.mozilla.org/2006/browser/search/':
raise UserWarning, ('bad namespace: ' + ns)
self.lNames.append(local)
handler = self.getOpenHandler()
if handler:
handler(self, attrs)
self.update(ns+local)
for qna in attrs.getQNames():
self.update(qna[0] + qna[1] + attrs.getValueByQName(qna))
return
def endElementNS(self, (ns, local), qname):
self.lNames.pop()
self.indent = self.indent[0:-2]
def characters(self, content):
self.update(content)
if not self.textField:
return
self.engine[self.textField] = content
self.textField = None
def update(self, content):
self.md5.update(utf_8_encode(content)[0])
def openURL(self, attrs):
entry = {'params':{},
'type': attrs.getValueByQName(u'type'),
'template': attrs.getValueByQName(u'template')}
self.engine['urls'].append(entry)
def handleParam(self, attrs):
try:
self.engine['urls'][-1]['params'][attrs.getValueByQName(u'name')] = attrs.getValueByQName(u'value')
except KeyError:
raise UserWarning, 'bad param'
return
def handleMozParam(self, attrs):
try:
self.engine['urls'][-1]['MozParams'] = {
'name': attrs.getValueByQName(u'name'),
'condition': attrs.getValueByQName(u'condition'),
'trueValue': attrs.getValueByQName(u'trueValue'),
'falseValue': attrs.getValueByQName(u'falseValue')}
except KeyError:
raise UserWarning, 'bad mozParam'
return
def handleShortName(self, attrs):
self.textField = 'ShortName'
return
def handleImage(self, attrs):
self.textField = 'Image'
return
def getOpenHandler(self):
return self.getHandler(DummyHandler.openHandlers)
def getHandler(self, handlers):
for local in self.lNames:
if type(handlers) != DictType or not handlers.has_key(local):
return
handlers = handlers[local]
if handlers.has_key('_handler'):
return handlers['_handler']
return
openHandlers = {'SearchPlugin':
{'ShortName': {'_handler': handleShortName},
'Image': {'_handler': handleImage},
'Url':{'_handler': openURL,
'Param': {'_handler':handleParam},
'MozParam': {'_handler':handleMozParam}
}
}
}
handler = DummyHandler()
parser = sax.make_parser()
parser.setContentHandler(handler)
parser.setFeature(sax.handler.feature_namespaces, True)
locales = [loc.strip() for loc in open('mozilla/browser/locales/all-locales')]
locales.insert(0, 'en-US')
sets = {}
details = {}
for loc in locales:
try:
lst = open(Paths.get_path('browser',loc,'searchplugins/list.txt'),'r')
except IOError:
logging.error("Locale " + loc + " doesn't have search plugins")
details[Paths.get_path('browser',loc,'searchplugins/list.txt')] = {
'error': 'not found'
}
continue
sets[loc] = {'list': []}
regprop = Paths.get_path('browser', loc, 'chrome/browser-region/region.properties')
p = Parser.getParser(regprop)
p.read(regprop)
orders = {}
for key, val in p:
m = re.match('browser.search.order.([1-9])', key)
if m:
orders[val.strip()] = int(m.group(1))
sets[loc]['orders'] = orders
for fn in lst:
name = fn.strip()
leaf = 'searchplugins/' + name + '.xml'
_path = Paths.get_path('browser','en-US', leaf)
if not os.access(_path, os.R_OK):
_path = Paths.get_path('browser', loc, leaf)
logging.info('testing ' + _path)
sets[loc]['list'].append(_path)
try:
parser.parse(_path)
except IOError:
logging.error("can't open " + _path)
details[_path] = {'_name': name, 'error': 'not found'}
continue
except UserWarning, ex:
logging.error("error in searchplugin " + _path)
details[_path] = {'_name': name, 'error': ex.args[0]}
continue
except sax._exceptions.SAXParseException, ex:
logging.error("error in searchplugin " + _path)
details[_path] = {'_name': name, 'error': ex.args[0]}
continue
details[_path] = handler.engine
details[_path]['_name'] = name
engines = {'locales': sets,
'details': details}
return engines

View File

@ -0,0 +1,165 @@
#! python
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is l10n test automation.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation
# Portions created by the Initial Developer are Copyright (C) 2006
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Axel Hecht <l10n@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import logging
import sys
import os
import os.path
from datetime import datetime
import time
from optparse import OptionParser
import gzip
import codecs
from Mozilla import Parser, CompareLocales, Paths, Tests
import simplejson
testLocales = []
lvl = logging.WARNING
date = datetime.utcnow().replace(minute=0,second=0,microsecond=0).isoformat(' ')
# parse commandline arguments
cp = OptionParser(version='0.2')
cp.add_option('-v', '--verbose', action='count', dest='v', default=0,
help='Make more noise')
cp.add_option('-q', '--quiet', action='count', dest='q', default=0,
help='Make less noise')
cp.add_option('-O', '--base-dir', type='string', dest='target',
default='results',
help='Destination base directory')
cp.add_option('-d', '--date', type='string', dest='date', default=date,
help='Explicit start date [Default: last full hour]')
cp.add_option('-z',action="store_true", dest="gzip", default=False,
help='Use gzip compression for output')
cp.add_option('-w','--enable-waterfall', action="store_true",
dest="waterfall", default=False,
help='Update waterfall data')
cp.add_option('--end-date', type='string', dest='enddate',
help='Explicit (faked) end date')
opts, optlist = cp.parse_args(sys.argv[1:])
logging.basicConfig(level=(logging.WARNING + 10*(opts.q - opts.v)))
logging.debug(' Ensure output directory')
# replace : with -
opts.date = opts.date.replace(':','-')
if not os.path.isdir(opts.target):
sys.exit('error: ' + opts.target + ' is not a directory')
startdate = time.mktime(time.strptime(opts.date, '%Y-%m-%d %H-%M-%S'))
basePath = os.path.join(opts.target, opts.date)
if not os.path.isdir(basePath):
os.mkdir(basePath)
class Wrapper:
def __init__(self, path, name):
self.p = os.path.join(path, name)
self.n = name
self.f = None
if opts.gzip:
self.p += '.gz'
def open(self, mode):
if self.f:
self.f.close()
if opts.gzip:
mode += 'b'
self.f = open(self.p, mode)
if opts.gzip:
self.f = gzip.GzipFile(fileobj = self.f, filename = self.n)
self.w = codecs.getwriter('utf-8')(self.f)
self.r = codecs.getreader('utf-8')(self.f)
def write(self, str):
self.w.write(str)
def read(self, size = -1):
return self.r.read(size)
def rewind(self):
if opts.gzip:
self.f.rewind()
else:
self.f.seek(0,0)
def close(self):
if opts.gzip:
f = self.f.fileobj;
self.f.close()
if opts.gzip:
f.close()
self.w = self.r = self.f = None
def saveJSON(dic, localName):
name = os.path.join(basePath, localName) ;
if opts.gzip:
f = open(name + '.gz', 'wb')
s = gzip.GzipFile(fileobj = f, filename = localName)
else:
f = open(name, 'w')
s = f
sw = codecs.getwriter('utf-8')(s)
sw.write(simplejson.dumps(dic, sort_keys=True))
sw.reset()
if opts.gzip:
s.close()
f.close()
tests = [Tests.SearchTest(),Tests.CompareTest()]
drop = {}
for test in tests:
res = test.run()
test.serialize(res, saveJSON)
if opts.waterfall:
test.failureTest(res, drop)
if not opts.waterfall:
sys.exit()
if opts.enddate:
endtime = time.mktime(time.strptime(opts.enddate, '%Y-%m-%d %H:%M:%S'))
else:
endtime = time.mktime(datetime.utcnow().timetuple())
f = None
w = Wrapper(opts.target, 'waterfall.json')
if os.path.isfile(w.p):
w.open('r')
water = simplejson.load(w)
else:
water = []
water.append((opts.date, (startdate, endtime), drop))
w.open('w')
simplejson.dump(water, w, sort_keys=True)
w.close()

View File

@ -1,9 +1,31 @@
from distutils.core import setup
from distutils.cmd import Command
import glob
class web(Command):
description = 'install web files'
user_options = [('target=','d','base directory for installation')]
def initialize_options(self):
self.target = None
pass
def finalize_options(self):
pass
def run(self):
self.ensure_dirname('target')
for f in glob.glob('web/*.*'):
if f.find('/CVS') >=0 or f.find('~') >= 0:
continue
self.copy_file(f, self.target)
setup(name="l10n-tools",
version="0.1",
version="0.2",
author="Axel Hecht",
author_email="l10n@mozilla.com",
scripts=['scripts/compare-locales', 'scripts/verify-search'],
scripts=['scripts/compare-locales', 'scripts/verify-search',
'scripts/test-locales'],
package_dir={'': 'lib'},
packages=['Mozilla']
packages=['Mozilla'],
cmdclass={'web': web}
)

View File

@ -0,0 +1,309 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Axel Hecht <axel@pike.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var controller = {}, view = {};
const kModules = ['browser','mail','toolkit'];
const Tier1 = ['en-GB', 'fr', 'de', 'ja', 'ja-JP-mac', 'pl', 'es-ES'];
const Tier2 = ['zh-CN', 'zh-TW', 'cs', 'da', 'nl', 'fi', 'hu', 'it', 'ko', 'pt-BR', 'pt-PT', 'ru', 'es-AR', 'sv-SE', 'tr'];
tierMap = {};
for each (var loc in Tier1) tierMap[loc] = 'Tier-1';
for each (var loc in Tier2) tierMap[loc] = 'Tier-2';
function keys(obj) {
var k = [];
for (var f in obj) k.push(f);
k.sort();
return k;
}
function JSON_c() {
this._requests = [];
this._getRequest = function() {
for each (var req in this._requests) {
if (!req.status)
return req;
}
req = new XMLHttpRequest();
this._requests.push(req);
return req;
};
};
JSON_c.prototype = {
get : function(aURL, aCallback) {
var _t = this;
var req = this._getRequest();
req.overrideMimeType("text/plain"); // don't parse XML, this is js
req.onerror = function(event) {
_t.handleFailure(event.target);
event.stopPropagation();
};
req.onload = function(event) {
_t.handleSuccess(event.target);
event.stopPropagation();
};
if (aCallback) {
req.callback = aCallback;
}
req.open("GET", aURL, true);
try {
req.send(null);
} catch (e) {
// work around send sending errors, but not the callback
controller.handleFailure(req);
}
return req;
},
// callbacks
handleSuccess : function(o) {
if (o.responseText === undefined)
throw "expected response text in handleSuccess";
if (o.callback) {
var obj = eval('('+o.responseText+')');
o.callback(obj);
delete o.callback;
}
},
handleFailure : function(o) {
if (o.callback) {
o.callback.handleFailure();
delete o.callback;
return;
}
throw("load failed with " + o.status + " " + o.statusText);
}
};
var JSON = new JSON_c();
baseView = {
init: function() {
this.content = document.getElementById('content');
var checks = document.getElementById('checks');
checks.addEventListener("click", view.onClickDisplay, true);
for (var i = 0; i < checks.childNodes.length; i++) {
if (checks.childNodes[i] instanceof HTMLInputElement) {
this.setTierDisplay(checks.childNodes[i]);
}
}
},
showHeader: function() {
document.getElementById('head').innerHTML='<td class="locale">Locale</td>';
},
updateView: function(aLocales, aClosure) {
YAHOO.widget.Logger.log('updateView called');
this.showHeader();
var oldIndex = 0;
var selection = null;
for each (loc in aLocales) {
var id = "row-" + loc;
var oldChild = this.content.childNodes[oldIndex++];
var t;
if (oldChild) {
t = oldChild;
t.innerHTML = '';
if (t.id != id) {
// we don't have the right row, make sure to remove the ID
// from the pre-existing one, just in case
var obsRow = document.getElementById(id);
if (obsRow) {
obsRow.id = '';
}
}
}
else {
t = document.createElement("tr");
this.content.appendChild(t);
}
t.id = id;
t.className = '';
if (tierMap[loc])
t.className += ' ' + tierMap[loc];
else
t.className += ' Tier-3';
t.innerHTML = '<td class="locale">' + loc + '</td>';
t.appendChild(controller.getContent(loc));
};
while ((oldChild = this.content.childNodes[oldIndex])) {
this.content.removeChild(oldChild);
}
},
addTab: function(aName, aKey) {
var tab = document.createElement('li');
tab.textContent = aName;
tab.id = '_tab_' + aKey;
document.getElementById('tabs').appendChild(tab);
tab.onclick = function(event){return controller.select(aKey);};
},
selectTab: function(aKey) {
var tabs = document.getElementById('tabs');
var id = '_tab_' + aKey;
for (var i=0; i < tabs.childNodes.length; i++) {
var tab = tabs.childNodes[i];
var toBeSelected = tab.id == id;
if (toBeSelected) {
tab.setAttribute('selected','selected');
}
else {
tab.removeAttribute('selected');
}
}
return true;
},
onClickDisplay: function(event) {
if (event.target.localName != 'INPUT') {
return false;
}
var handler = function() {return function(){view.setTierDisplay(event.target)}};
window.setTimeout(handler(),10);
return true;
},
setTierDisplay: function(aTier, aTruth) {
if (aTruth === undefined && aTier instanceof HTMLInputElement) {
aTruth = aTier.checked;
}
var disable = aTruth;
var name = aTier;
if (aTier instanceof HTMLInputElement) {
name = aTier.name;
}
var id = 'style-' + name;
document.getElementById(id).disabled = disable;
return false;
},
setTag: function(aTag) {
document.getElementById('tag-view').value = aTag;
},
getCell: function() {
return document.createElement('td');
},
getClass: function(aLoc) {
if (tierMap[aLoc]) {
return tierMap[aLoc];
}
return 'Tier-3';
}
};
baseController = {
_l: [],
_c: null,
_pending: [],
_target: null,
_tag: '.',
beforeUnSelect: function(){},
beforeSelect: function(){},
get locales() {
return this._l;
},
get tag() {
return baseController._tag;
},
set tag(aTag) {
baseController._tag = aTag;
view.setTag(aTag);
},
addLocales: function(lst) {
var nl = lst.filter(function(el,i,a) {return this._l.indexOf(el) < 0;}, this);
this._l = this._l.concat(nl);
this._l.sort();
},
select: function(aKey) {
if (this._target == aKey) {
return;
}
if (this._target)
this._c[this._target].controller.beforeUnSelect();
this._target = aKey;
var nCV = this._c[this._target];
view = nCV.view;
controller = nCV.controller;
controller.beforeSelect();
controller._target = aKey;
view.selectTab(aKey);
this.delay(controller, controller.showView, []);
},
showView: function(aClosure) {
view.updateView(controller.locales, aClosure);
},
getContent: function(aLoc) {
if (! this._target) return;
return this._c[this._target].getContent(aLoc);
},
addPane: function(aName, aKey, aController, aView) {
if (! this._c) {
this._pending.push([aName, aKey, aController, aView]);
return;
}
view.addTab(aName, aKey);
this._c[aKey] = {controller: aController, view: aView};
},
loaded: function() {
this._c = {};
for each (var c in this._pending) {
this.addPane.apply(this, c);
}
this.select(this._pending[0][1]);
delete this._pending;
},
delay: function(context, f, args) {
window.setTimeout(function(){f.apply(context,args);}, 10);
}
};
var outlineController = {
__proto__: baseController,
getHeader: function() {
var h = document.createElement('th');
h.textContent = 'Details';
return h;
},
getContent: function(aLoc) {
var cell = view.getCell();
cell.innerHTML = 'Details for ' + aLoc;
return cell;
}
};
var outlineView = {
__proto__: baseView
};
//baseController.addPane('Outline', 'outline', outlineController, outlineView);
view = baseView;
controller = baseController;

View File

@ -1,344 +0,0 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Axel Hecht <axel@pike.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var controller, view;
const kModules = ['browser','mail','toolkit'];
const Tier1 = ['en-GB', 'fr', 'de', 'ja', 'ja-JP-mac', 'pl', 'es-ES'];
const Tier2 = ['zh-CN', 'zh-TW', 'cs', 'da', 'nl', 'fi', 'hu', 'it', 'ko', 'pt-BR', 'pt-PT', 'ru', 'es-AR', 'sv-SE', 'tr'];
tierMap = {};
for each (var loc in Tier1) tierMap[loc] = 'Tier-1';
for each (var loc in Tier2) tierMap[loc] = 'Tier-2';
var calHelper = {
formatDate : function(aDate) {
function format(aNum) {
if (aNum < 10)
return '0' + aNum;
return String(aNum);
}
return aDate.getUTCFullYear() + '-'
+ format(aDate.getUTCMonth() + 1) + '-'
+ format(aDate.getUTCDate());
}
};
function Observer(aLoc, aNode, aDate) {
this._loc = aLoc;
this._node = aNode;
this._date = aDate;
}
Observer.prototype = {
handleSuccess: function(aData) {
r = eval('('+aData+')');
view.createFullTree(r, this._node, "missing");
view.createFullTree(r, this._node, "obsolete");
delete this._node.__do_load;
this._node.tree.removeNode(this._node.children[0]);
this._node.refresh();
},
handleFailure: function() {
throw "couldn't load details";
}
};
view = {
setupResults: function(responseText) {
res = eval('(' + responseText + ')');
this._locales = [];
for (var loc in res) {
this._locales.push(loc);
}
this._locales.sort();
this._results = res;
},
updateDateView: function(aDate) {
if (!aDate)
throw "Date expected in view.updateDateView";
this._gView = document.getElementById("view");
document.getElementById('currentDate').textContent = aDate;
var oldIndex = 0;
for each (loc in this._locales) {
var r = this._results[loc];
var id = "tree-" + loc;
var oldChild = this._gView.childNodes[oldIndex++];
var t;
if (oldChild) {
t = oldChild;
t.innerHTML = '';
if (t.id != id) {
// we don't have the right div, make sure to remove the ID
// from the pre-existing one, just in case
var obsDiv = document.getElementById(id);
if (obsDiv) {
obsDiv.id = '';
}
}
}
else {
t = document.createElement("div");
}
t.id = id;
var hasDetails = true;
if (r.missing.total + r.missingFiles.total) {
t.className = "has_missing";
}
else if (r.obsolete.total + r.obsoleteFiles.total) {
t.className = "has_obsolete";
}
else {
t.className = "is_good";
hasDetails = false;
}
if (tierMap[loc])
t.className += ' ' + tierMap[loc];
else
t.className += ' Tier-3';
if (!oldChild) {
this._gView.appendChild(t);
}
t = new YAHOO.widget.TreeView("tree-" + loc);
var closure = function() {
var _loc = loc;
var _d = aDate;
return function(node) {
if (!node.__do_load)
return true;
var callback = new Observer(_loc, node, _d);
controller.getDetails(callback._date, callback._loc, callback);
delete callback;
return true;
};
}
if (hasDetails) {
t.onExpand = closure();
}
var row = {
_c : '',
pushCell: function(content, class) {
this._c += '<td';
if (content == 0)
class += ' zero'
if (class)
this._c += ' class="' + class + '"';
this._c += '>' + content + '</td>';
},
get content() {return '<table class="locale-row"><tr>' +
this._c + '</tr></table>';}
};
row.pushCell(loc, "locale");
var res = r;
for each (app in kModules) {
var class;
if (res.tested.indexOf(app) < 0) {
class = 'void';
}
else if (res.missing[app] + res.missingFiles[app]) {
class = 'missing';
}
else if (res.obsolete[app] + res.obsoleteFiles[app]) {
class = 'obsolete';
}
else {
class = "good";
}
row.pushCell(app, 'app-res ' + class);
}
row.pushCell(r.missingFiles.total, "missing count");
row.pushCell(r.missing.total, "missing count");
row.pushCell(r.obsoleteFiles.total, "obsolete count");
row.pushCell(r.obsolete.total, "obsolete count");
row.pushCell(r.unchanged +r.changed, "good count");
var tld = new YAHOO.widget.HTMLNode(row.content, t.getRoot(), false, hasDetails);
if (hasDetails) {
tld.__cl_locale = loc;
tld.__cl_date = aDate;
tld.__do_load = true;
tmp = new YAHOO.widget.HTMLNode("<span class='ygtvloading'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;loading&hellip;", tld, true, true);
}
t.draw();
var rowToggle = function(e){
this.toggle()
};
YAHOO.util.Event.addListener(tld.getContentEl(), "click", rowToggle, tld, true);
};
while ((oldChild = this._gView.childNodes[oldIndex])) {
this._gView.removeChild(oldChild);
}
},
onClickDisplay: function(event) {
YAHOO.util.Event.stopEvent(event);
if (event.target.localName != 'INPUT') {
return false;
}
var handler = function() {return function(){view.onCDReal(event.target)}};
window.setTimeout(handler(),0);
return false;
},
onCDReal: function(target) {
var dsp = !target.checked;
target.checked = dsp;
dsp = dsp ? 'block' : 'none';
var layout = document.styleSheets[2];
layout.insertRule('div.' + target.name + ' {display: ' + dsp + ';}',
layout.cssRules.length);
return false;
},
// Helper methods
createFullTree: function ct(aDetails, aNode, aName) {
if (!aDetails || ! (aDetails instanceof Object))
return;
var cat = new YAHOO.widget.TextNode(aName, aNode, true);
for each (postfix in ["Files", ""]) {
var subRes = aDetails[aName + postfix];
var append = postfix ? ' (files)' : '';
for (mod in subRes) {
var mn = new YAHOO.widget.TextNode(mod + append, cat, true);
for (fl in subRes[mod]) {
if (postfix) {
new YAHOO.widget.TextNode(subRes[mod][fl], mn, false);
continue;
}
var fn = new YAHOO.widget.TextNode(fl, mn, false);
var keys = [];
for each (k in subRes[mod][fl]) {
keys.push(k);
}
new YAHOO.widget.HTMLNode("<pre>" + keys.join("\n") + "</pre>", fn, true, false);
}
}
}
return cat;
}
};
controller = {
_requests : [],
getDataForCal : function() {
var d = cal.getSelectedDates()[0];
d.setHours(12);
d = new Date(d.valueOf() - d.getTimezoneOffset() * 60000)
var callback = {
_date: calHelper.formatDate(d),
handleSuccess: function(aData) {
view.setupResults(aData);
view.updateDateView(this._date);
},
handleFailure: function() {
throw "couldn't load something";
}
};
this.getData(callback._date, callback);
},
kPrefix: 'results/',
getData : function _gda(aDate, aCallback) {
return this._getInternal(aDate, 'data-', null, aCallback);
},
getDetails : function _gde(aDate, aLocale, aCallback) {
return this._getInternal(aDate, 'details-', aLocale, aCallback);
},
_getInternal : function _gi(aDate, aName, aPostfix, aCallback) {
var url = this.kPrefix + aName + aDate;
if (aPostfix)
url += '-' + aPostfix;
url += '.js';
var req = this._getRequest();
req.overrideMimeType("text/plain"); // don't parse XML, this is js
req.onerror = function(event) {controller.handleFailure(event.target);event.stopPropagation();};
req.onload = function(event) {controller.handleSuccess(event.target);event.stopPropagation();};
if (aCallback) {
req.callback = aCallback;
}
req.open("GET", url, true);
try {
req.send(null);
} catch (e) {
// work around send sending errors, but not the callback
controller.handleFailure(req);
}
return true;
},
_getRequest : function() {
for each (var req in this._requests) {
if (!req.status)
return req;
}
req = new XMLHttpRequest();
this._requests.push(req);
return req;
},
// Ajax interface
handleSuccess : function(o) {
if (o.responseText === undefined)
throw "expected response text in handleSuccess";
if (o.callback) {
o.callback.handleSuccess(o.responseText);
delete o.callback;
}
},
handleFailure : function(o) {
if (o.callback) {
o.callback.handleFailure();
delete o.callback;
return;
}
throw("load failed with " + o.status + " " + o.statusText);
}
}
var cal;
function onLoad(event) {
document.removeEventListener("load", onLoad, true);
document.getElementById("checks").addEventListener("click", view.onClickDisplay, true);
cal = new YAHOO.widget.Calendar('cal', 'calDiv');
cal.minDate = new Date(2006, 5, 1);
// cal.maxDate = new Date();
cal.render();
cal.onSelect = function() {controller.getDataForCal();};
var n = new Date();
if (n.getUTCHours() < 12 && n.getUTCDate() == n.getDate()) {
// do yesterday, we didn't check out today yet
n = new Date(n - YAHOO.widget.DateMath.ONE_DAY_MS);
}
n = new Date(n.getFullYear(), n.getMonth(), n.getDate());
cal.select(n);
return true;
}

View File

@ -0,0 +1,196 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Axel Hecht <axel@pike.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var comparisonView = {
__proto__: baseView,
setUpHandlers: function() {
_t = this;
//this.omv = function(event){_t.onMouseOver.apply(_t, [event]);};
//this.omo = function(event){_t.onMouseOut.apply(_t, [event]);};
//this.content.addEventListener("mouseover", this.omv, true);
//this.content.addEventListener("mouseout", this.omv, true);
},
destroyHandlers: function() {
//this.content.removeEventListener("mouseover", this.omv, true);
//this.content.removeEventListener("mouseout", this.omv, true);
},
// Helper methods
createFullTree: function ct(aDetails, aNode, aName) {
if (!aDetails || ! (aDetails instanceof Object))
return;
var cat = new YAHOO.widget.TextNode(aName, aNode, true);
for each (postfix in ["Files", ""]) {
var subRes = aDetails[aName + postfix];
var append = postfix ? ' (files)' : '';
for (mod in subRes) {
var mn = new YAHOO.widget.TextNode(mod + append, cat, true);
for (fl in subRes[mod]) {
if (postfix) {
new YAHOO.widget.TextNode(subRes[mod][fl], mn, false);
continue;
}
var fn = new YAHOO.widget.TextNode(fl, mn, false);
var keys = [];
for each (k in subRes[mod][fl]) {
keys.push(k);
}
new YAHOO.widget.HTMLNode("<pre>" + keys.join("\n") + "</pre>", fn, true, false);
}
}
}
return cat;
}
};
var comparisonController = {
__proto__: baseController,
beforeSelect: function() {
this.result = {};
this.isShown = false;
comparisonView.setUpHandlers();
var _t = this;
var callback = function(obj) {
delete _t.req;
if (view != comparisonView) {
// ignore, we have switched again
return;
}
_t.result = obj;
comparisonView.updateView(keys(_t.result));
};
this.req = JSON.get('results/' + this.tag + '/cmp-data.json', callback);
},
beforeUnSelect: function() {
if (this.req) {
this.req.abort();
delete this.req;
}
comparisonView.destroyHandlers();
},
showView: function(aClosure) {
// json onload handler does this;
},
getContent: function(aLoc) {
var row = view.getCell();
var inner = document.createElement('div');
row.appendChild(inner);
var r = this.result[aLoc];
var id = "tree-" + aLoc;
inner.id = id;
var hasDetails = true;
if (r.missing.total + r.missingFiles.total) {
inner.className = "has_missing";
}
else if (r.obsolete.total + r.obsoleteFiles.total) {
inner.className = "has_obsolete";
}
else {
inner.className = "is_good";
hasDetails = false;
}
t = new YAHOO.widget.TreeView(inner);
var _l = aLoc;
var rowCollector = {
_c : '',
pushCell: function(content, class) {
this._c += '<td';
if (content == 0)
class += ' zero'
if (class)
this._c += ' class="' + class + '"';
this._c += '>' + content + '</td>';
},
get content() {return '<table class="locale-row"><tr>' +
this._c + '</tr></table>';}
};
for each (app in kModules) {
var class;
if (r.tested.indexOf(app) < 0) {
class = 'void';
}
else if (r.missing[app] + r.missingFiles[app]) {
class = 'missing';
}
else if (r.obsolete[app] + r.obsoleteFiles[app]) {
class = 'obsolete';
}
else {
class = "good";
}
rowCollector.pushCell(app, 'app-res ' + class);
}
rowCollector.pushCell(r.missingFiles.total, "missing count");
rowCollector.pushCell(r.missing.total, "missing count");
rowCollector.pushCell(r.obsoleteFiles.total, "obsolete count");
rowCollector.pushCell(r.obsolete.total, "obsolete count");
rowCollector.pushCell(r.unchanged +r.changed, "good count");
var tld = new YAHOO.widget.HTMLNode(rowCollector.content, t.getRoot(), false, hasDetails);
if (hasDetails) {
tld.__cl_locale = aLoc;
tld.__do_load = true;
tmp = new YAHOO.widget.HTMLNode("<span class='ygtvloading'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;loading&hellip;", tld, true, true);
var l = aLoc;
var _c = this;
var expander = function(node) {
if (!node.__do_load)
return true;
var callback = function(aRes) {
comparisonView.createFullTree(aRes, node, 'missing');
comparisonView.createFullTree(aRes, node, 'obsolete');
delete node.__do_load;
node.tree.removeNode(node.children[0]);
node.refresh();
};
JSON.get('results/' + _c.tag + '/cmp-details-'+aLoc+'.json', callback);
}
t.onExpand = expander;
delete expander;
}
t.draw();
var rowToggle = function(){
YAHOO.util.Event.addListener(tld.getContentEl(), "click",
function(){tld.toggle();}, tld, true);
};
YAHOO.util.Event.onAvailable(tld.contentElId, rowToggle, tld);
//YAHOO.util.Event.addListener(tld.getContentEl(), "click", rowToggle, tld, true);
return row;
}
};
controller.addPane('Compare', 'comparison', comparisonController,
comparisonView);
//controller.addLocales(keys(results.locales));

View File

@ -39,35 +39,54 @@
<html>
<head>
<title>Locales status</title>
<link rel="stylesheet" type="text/css" href="yui/examples/treeview/css/local/tree.css">
<link rel="stylesheet" type="text/css" href="yui/build/calendar/assets/calendar.css">
<link rel="stylesheet" type="text/css" href="layout.css">
<script type="text/javascript" src="yui/build/yahoo/yahoo.js"></script>
<script type="text/javascript" src="yui/build/dom/dom.js"></script>
<script type="text/javascript" src="yui/build/event/event.js"></script>
<script type="text/javascript" src="yui/build/treeview/treeview.js"></script>
<script type="text/javascript" src="yui/build/calendar/calendar.js"></script>
<script type="application/javascript" src="code.js"></script>
<title>1.8.1 Locales status</title>
<style type="text/css" id="style-Tier-1">
.Tier-1 {display: none}
</style>
<style type="text/css" id="style-Tier-2">
.Tier-2 {display: none}
</style>
<style type="text/css" id="style-Tier-3">
.Tier-3 {display: none}
</style>
<style type="text/css">
div {min-height: 5px;}
</style>
<link rel="stylesheet" type="text/css" href="yui/examples/treeview/css/local/tree.css">
<link rel="stylesheet" type="text/css" href="yui/build/container/assets/container.css">
<link rel="stylesheet" type="text/css" href="layout.css">
<script type="text/javascript" src="yui/build/yahoo/yahoo.js"></script>
<script type="text/javascript" src="yui/build/dom/dom.js"></script>
<script type="text/javascript" src="yui/build/event/event.js"></script>
<script type="text/javascript" src="yui/build/logger/logger.js"></script>
<script type="text/javascript" src="yui/build/treeview/treeview.js"></script>
<script type="text/javascript" src="yui/build/calendar/calendar.js"></script>
<script type="text/javascript" src="yui/build/dragdrop/dragdrop.js"></script>
<script type="text/javascript" src="yui/build/container/container.js"></script>
<script type="text/javascript" src="code-base.js"></script>
<script type="text/javascript" src="waterfall-code.js"></script>
<script type="text/javascript" src="search-code.js"></script>
<script type="text/javascript" src="comparison-code.js"></script>
</head>
<body onload="return onLoad(event)">
<h1>Locales overview</h1>
<p><strong>Legend:</strong> For each locale, this chart shows locale name, an indication of status for browser, mail, toolkit, and then, more importantly, number of missing files, missing entities, obsolete files and entities, and good entities, in red, orange and green, resp. You can click on the summary row to get (and hide) all the details for a particular locale.</p>
<div id="calContainer"><div id="currentDate">date</div><div id="calDiv"></div>
<p id="checks">
<input type="checkbox" name="Tier-1" checked> Tier 1<br>
<input type="checkbox" name="Tier-2" checked> Tier 2<br>
<input type="checkbox" name="Tier-3" checked> Tier 3<br>
</p>
</div>
<table><tr><td class="ygtvlph" style="background-image: none;">&nbsp;</td><td class="locale">&nbsp;</td>
<td class="app-res">&nbsp;</td><td class="app-res">&nbsp;</td>
<td class="app-res">&nbsp;</td>
<td class="count missing" title="missing Files">m.F.</td>
<td class="count missing" title="missing Entities">m.E.</td>
<td class="count obsolete" title="obsolete Files">o.F.</td>
<td class="count obsolete" title="obsolete Entities">o.E.</td>
<td class="count good" title="unchanged or localized Entities">good</td></tr></table>
<div id="view"></div>
<body onload="">
<div id="sidebar">
<p id="checks">Showing Tier 1
<input type="checkbox" name="Tier-1" checked>&nbsp;
2 <input type="checkbox" name="Tier-2" checked>&nbsp;
3 <input type="checkbox" name="Tier-3" checked> for
</p>
<input type="text" id="tag-view" size="30">
</div>
<h1>Locale Tests</h1>
<div id="menu">
<ul id="tabs"></ul>
</div>
<table id="main">
<thead>
<tr id="head"></tr>
</thead>
<tbody id="content"></tbody>
</table>
<script type="text/javascript" src="post.js"></script>
</body>
</html>

View File

@ -35,29 +35,60 @@
*
* ***** END LICENSE BLOCK ***** */
#sidebar {
float: right;
margin-top: -8px;
}
#menu li[selected] {
background-color: lightgrey;
border-style: inset inset none inset;
}
#menu li {
display: block;
float: left;
font-weight: bold;
padding: .5em;
margin-left:1px;
margin-right:1px;
border-style: outset outset none outset;
-moz-border-radius-topleft: 15px;
-moz-border-radius-topright: 15px;
}
#main {
clear: left;
}
body {
overflow: scroll;
}
#content {
vertical-align: top;
}
div.is_good {
padding-left: 16px;
}
td.missing {
.missing, .busted {
background-color: red;
}
td.obsolete {
.obsolete, .fair {
background-color: orange;
}
td.zero {
.zero {
opacity: .3;
background-color: green !important;
}
td.good {
.good {
background-color: limegreen;
}
td.void {
.void {
color: grey;
background-color: lightgrey;
}
@ -79,10 +110,6 @@ td.count {
text-align: right;
}
div#calContainer {
float: right;
margin-top: -8px;
}
div.calbordered {
float: none;
}

40
testing/tests/l10n/web/post.js Executable file
View File

@ -0,0 +1,40 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Axel Hecht <axel@pike.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
view.init();
controller.addLocales(keys(tierMap));
controller.loaded();

View File

@ -35,106 +35,105 @@
*
* ***** END LICENSE BLOCK ***** */
var controller, view;
const Tier1 = ['en-GB', 'fr', 'de', 'ja', 'ja-JP-mac', 'pl', 'es-ES'];
const Tier2 = ['zh-CN', 'zh-TW', 'cs', 'da', 'nl', 'fi', 'hu', 'it', 'ko', 'pt-BR', 'pt-PT', 'ru', 'es-AR', 'sv-SE', 'tr'];
tierMap = {};
var loc;
for each (loc in Tier1) tierMap[loc] = 'Tier-1';
for each (loc in Tier2) tierMap[loc] = 'Tier-2';
var view;
view = {
hashes: {},
updateView: function() {
this._gView = document.getElementById("view");
var cmp = function(l,r) {
var lName = results.details[l].ShortName;
var rName = results.details[r].ShortName;
if (lName) lName = lName.toLowerCase();
if (rName) rName = rName.toLowerCase();
return lName < rName ? -1 : lName > rName ? 1 : 0;
}
for (loc in results.locales) {
var row = document.createElement("tr");
if (tierMap[loc])
row.className += ' ' + tierMap[loc];
else
row.className += ' Tier-3';
this._gView.appendChild(row);
var lst = results.locales[loc].list;
lst.sort(cmp);
var orders = results.locales[loc].orders;
if (orders) {
var explicit = [];
var implicit = [];
for (var sn in orders) {
if (explicit[sn]) {
fatal = true;
break;
}
explicit[sn] = orders[sn];
}
explicit = [];
for each (var path in lst) {
var shortName = results.details[path].ShortName;
if (orders[shortName]) {
explicit[orders[shortName] - 1] = path;
}
else {
implicit.push(path);
}
}
lst = explicit.concat(implicit);
var searchController = {
__proto__: baseController,
beforeSelect: function() {
this.hashes = {};
searchView.setUpHandlers();
var _t = this;
var callback = function(obj) {
delete _t.req;
if (view != searchView) {
// ignore, we have switched again
return;
}
row.innerHTML = '<td class="locale"><a href="https://bugzilla.mozilla.org/buglist.cgi?query_format=advanced&amp;short_desc_type=regexp&amp;short_desc=' + loc + '%5B%20-%5D&amp;chfieldto=Now&amp;field0-0-0=blocked&amp;type0-0-0=substring&amp;value0-0-0=347914">' + loc + '</a></td>';
for each (var path in lst) {
//YAHOO.widget.Logger.log('testing ' + path);
var innerContent;
var cl = '';
if (path.match(/^mozilla/)) {
cl += ' enUS';
_t.result = obj;
searchView.updateView(keys(_t.result.locales));
};
this.req = JSON.get('results/' + this.tag + '/search-results.json',
callback);
},
beforeUnSelect: function() {
searchView.destroyHandlers();
},
showView: function(aClosure) {
// json onload handler does this;
},
getContent: function(aLoc) {
var row = document.createDocumentFragment();
var lst = this.result.locales[aLoc].list;
var _t = this;
lst.sort(function(a,b){return _t.cmp.apply(_t,[a,b])});
var orders = this.result.locales[aLoc].orders;
if (orders) {
var explicit = [];
var implicit = [];
for (var sn in orders) {
if (explicit[sn]) {
fatal = true;
break;
}
var localName = path.substr(path.lastIndexOf('/') + 1)
if (results.details[path].error) {
innerContent = 'error in ' + localName;
cl += ' error';
explicit[sn] = orders[sn];
}
explicit = [];
for each (var path in lst) {
var shortName = this.result.details[path].ShortName;
if (orders[shortName]) {
explicit[orders[shortName] - 1] = path;
}
else {
var shortName = results.details[path].ShortName;
var img = results.details[path].Image;
if (results.locales[loc].orders && results.locales[loc].orders[shortName]) {
cl += " ordered";
}
innerContent = '<img src="' + img + '">' + shortName;
implicit.push(path);
}
var td = document.createElement('td');
td.className = 'searchplugin' + cl;
td.innerHTML = innerContent;
row.appendChild(td);
td.details = results.details[path];
// test the hash code
if (td.details.error) {
// ignore errorenous plugins
continue;
}
lst = explicit.concat(implicit);
}
row.innerHTML = '<td class="locale"><a href="https://bugzilla.mozilla.org/buglist.cgi?query_format=advanced&amp;short_desc_type=regexp&amp;short_desc=' + aLoc + '%5B%20-%5D&amp;chfieldto=Now&amp;field0-0-0=blocked&amp;type0-0-0=substring&amp;value0-0-0=347914">bug</a></td>';
for each (var path in lst) {
//YAHOO.widget.Logger.log('testing ' + path);
var innerContent;
var cl = '';
if (path.match(/^mozilla/)) {
cl += ' enUS';
}
var localName = path.substr(path.lastIndexOf('/') + 1);
if (this.result.details[path].error) {
innerContent = 'error in ' + localName;
cl += ' error';
}
else {
var shortName = this.result.details[path].ShortName;
var img = this.result.details[path].Image;
if (this.result.locales[aLoc].orders && this.result.locales[aLoc].orders[shortName]) {
cl += " ordered";
}
if (this.hashes[localName]) {
this.hashes[localName].nodes.push(td);
if (this.hashes[localName].conflict) {
innerContent = '<img src="' + img + '">' + shortName;
}
var td = document.createElement('td');
td.className = 'searchplugin' + cl;
td.innerHTML = innerContent;
row.appendChild(td);
td.details = this.result.details[path];
// test the hash code
if (td.details.error) {
// ignore errorenous plugins
continue;
}
if (this.hashes[localName]) {
this.hashes[localName].nodes.push(td);
if (this.hashes[localName].conflict) {
td.className += ' conflict';
}
else if (this.hashes[localName].key != td.details.md5) {
this.hashes[localName].conflict = true;
for each (td in this.hashes[localName].nodes) {
td.className += ' conflict';
}
else if (this.hashes[localName].key != td.details.md5) {
this.hashes[localName].conflict = true;
for each (td in this.hashes[localName].nodes) {
td.className += ' conflict';
}
}
}
else {
this.hashes[localName] = {key: td.details.md5, nodes: [td]};
}
}
else {
this.hashes[localName] = {key: td.details.md5, nodes: [td]};
}
}
for (localName in this.hashes) {
if ( ! ('conflict' in this.hashes[localName])) {
@ -146,6 +145,31 @@ view = {
}
YAHOO.widget.Logger.log('difference in ' + localName + ' for ' + locs.join(', '));
}
return row;
},
cmp: function(l,r) {
var lName = this.result.details[l].ShortName;
var rName = this.result.details[r].ShortName;
if (lName) lName = lName.toLowerCase();
if (rName) rName = rName.toLowerCase();
return lName < rName ? -1 : lName > rName ? 1 : 0;
}
};
var searchView = {
__proto__: baseView,
setUpHandlers: function() {
_t = this;
this.omv = function(event){_t.onMouseOver.apply(_t, [event]);};
this.omo = function(event){_t.onMouseOut.apply(_t, [event]);};
this.content.addEventListener("mouseover", this.omv, true);
this.content.addEventListener("mouseout", this.omv, true);
},
destroyHandlers: function() {
this.content.removeEventListener("mouseover", this.omv, true);
this.content.removeEventListener("mouseout", this.omv, true);
if (this._dv) {
this._dv.hide();
}
},
onMouseOver: function(event) {
if (!event.target.details)
@ -155,30 +179,30 @@ view = {
clientX: event.clientX,
clientY: event.clientY
};
view.pending = setTimeout(function(){view.showDetail(_e);}, 500);
this.pending = setTimeout(function(){searchView.showDetail(_e);}, 500);
},
onMouseOut: function(event) {
if (view.pending) {
clearTimeout(view.pending);
delete view.pending;
if (this.pending) {
clearTimeout(this.pending);
delete this.pending;
return;
}
if (!event.target.details)
return;
},
showDetail: function(event) {
delete view.pending;
if (!view._dv) {
view._dv = new YAHOO.widget.Panel('dv', {visible:true,draggable:true,constraintoviewport:true});
view._dv.beforeHideEvent.subscribe(function(){delete view._dv;}, null);
delete this.pending;
if (!this._dv) {
this._dv = new YAHOO.widget.Panel('dv', {visible:true,draggable:true,constraintoviewport:true});
this._dv.beforeHideEvent.subscribe(function(){delete this._dv;}, null);
}
var dt = event.details;
if (dt.error) {
view._dv.setHeader("Error");
view._dv.setBody(dt.error);
this._dv.setHeader("Error");
this._dv.setBody(dt.error);
}
else {
view._dv.setHeader("Details");
this._dv.setHeader("Details");
var c = '';
var q = 'test';
var len = 0;
@ -193,41 +217,12 @@ view = {
c += '<button onclick="window.open(this.textContent)">' + uc + '</button><br>';
len = len < c.length ? c.length : len;
}
view._dv.setBody(c);
this._dv.setBody(c);
}
view._dv.render(document.body);
view._dv.moveTo(event.clientX + window.scrollX, event.clientY + window.scrollY);
},
onClickDisplay: function(event) {
if (event.target.localName != 'INPUT') {
return false;
}
YAHOO.widget.Logger.log('clicking ' + event);
var handler = function() {return function(){view.onCDReal(event.target)}};
window.setTimeout(handler(),0);
return false;
},
onCDReal: function(target) {
var dsp = target.checked;
target.checked = dsp;
dsp = dsp ? 'table-row' : 'none';
var layout = document.styleSheets[document.styleSheets.length - 1];
var i = Number(target.name);
layout.deleteRule(i);
layout.insertRule('.Tier-' + (i+1) + ' {display: ' + dsp + ';}', i);
return false;
this._dv.render(document.body);
this._dv.moveTo(event.clientX + window.scrollX, event.clientY + window.scrollY);
this._dv.show();
}
};
controller = {
kPrefix: 'results/',
}
function onLoad(event) {
document.removeEventListener("load", onLoad, true);
document.getElementById("checks").addEventListener("click", view.onClickDisplay, true);
view.updateView();
document.getElementById("view").addEventListener("mouseover", view.onMouseOver, true);
document.getElementById("view").addEventListener("mouseout", view.onMouseOut, true);
return true;
}
controller.addPane('Search', 'search', searchController, searchView);
//controller.addLocales(keys(results.locales));

View File

@ -1,84 +0,0 @@
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is Mozilla.
-
- The Initial Developer of the Original Code is
- Mozilla Foundation.
- Portions created by the Initial Developer are Copyright (C) 2006
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Axel Hecht <axel@pike.org>
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the LGPL or the GPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>l10n search engines</title>
<link rel="stylesheet" type="text/css" href="yui/examples/treeview/css/local/tree.css">
<link rel="stylesheet" type="text/css" href="yui/build/container/assets/container.css">
<link rel="stylesheet" type="text/css" href="layout.css">
<script type="text/javascript" src="yui/build/yahoo/yahoo.js"></script>
<script type="text/javascript" src="yui/build/dom/dom.js"></script>
<script type="text/javascript" src="yui/build/event/event.js"></script>
<script type="text/javascript" src="yui/build/logger/logger.js"></script>
<script type="text/javascript" src="yui/build/treeview/treeview.js"></script>
<script type="text/javascript" src="yui/build/dragdrop/dragdrop.js"></script>
<script type="text/javascript" src="yui/build/container/container.js"></script>
<script type="application/javascript" src="results/search-results.js"></script>
<script type="application/javascript" src="search-code.js"></script>
<style type="text/css">
td.ordered {border: 2px solid black;}
td.error {background-color: coral;}
td.conflict {background-color: grey;}
td.enUS {opacity: .5;}
</style>
<style type="text/css">
.Tier-1 {display: table-row;}
.Tier-2 {display: table-row;}
.Tier-3 {display: table-row;}
</style>
</head>
<body onload="return onLoad(event)">
<h1>Locales overview</h1>
<p><strong>Legend:</strong> For each locale, this chart shows search engine name.</p>
<div id="calContainer">
<p id="checks">
<input type="checkbox" name="0" checked> Tier 1<br>
<input type="checkbox" name="1" checked> Tier 2<br>
<input type="checkbox" name="2" checked> Tier 3<br>
</p>
</div>
<table>
<tbody id="view">
<tr><td class="locale">&nbsp;</td><td class="searchplugin">&nbsp;</td></tr>
</tbody>
</table>
</body>
</html>

View File

@ -0,0 +1,160 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Axel Hecht <axel@pike.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var waterfallView = {
__proto__: baseView,
scale: 1/60,
unstyle: function() {
this.content.innerHTML = '';
},
updateView: function(aResult) {
YAHOO.widget.Logger.log('waterfallView.updateView called');
var head = document.getElementById('head');
head.innerHTML = '<th></th>';
this.content.innerHTML = '';
for each (var loc in controller.locales) {
var h = document.createElement('th');
h.className = 'wf-locale-head ' + view.getClass(loc);
h.textContent = loc;
head.appendChild(h);
}
var heads = head.childNodes;
for (var i = aResult.length - 1; i >= 0; i--) {
var wf = document.createElement('tr');
wf.className = 'waterfall-row';
wf.setAttribute('style','vertical-align: top;');
this.content.appendChild(wf);
var b = aResult[i];
var height = Math.floor((b[1][1] - b[1][0]) * this.scale);
var style = "height: " + height + "px;";
wf.setAttribute('style', style);
var cell = view.getCell();
cell.className = 'cell-label';
cell.textContent = b[0];
wf.appendChild(cell);
var locs = keys(b[2]);
var h = 1;
for each (loc in locs) {
if (h >= heads.length) {
YAHOO.widget.Logger.log("dropping result for " + loc);
continue;
}
while (heads[h].textContent < loc) {
// we don't have a result for this column in this build
wf.appendChild(view.getCell());
h++;
if (h >= heads.length) {
YAHOO.widget.Logger.log("dropping result for " + loc);
continue;
}
}
if (heads[h].textContent > loc) {
YAHOO.widget.Logger.log("dropping result for " + loc);
continue;
}
cell = view.getCell();
cell.innerHTML = '&nbsp;';
if (b[2][loc] & 2) {
cell.className = 'busted ';
}
else if (b[2][loc] & 1) {
cell.className = 'fair ';
}
else {
cell.className = 'good ';
}
cell.className += ' ' + view.getClass(loc);
wf.appendChild(cell);
h++;
}
if (i == 0) {
continue;
}
// XXX don't make output sparse
continue;
wf = document.createElement('tr');
wf.className = 'waterfall-row';
wf.setAttribute('style','vertical-align: top;');
this.content.appendChild(wf);
var b = aResult[i];
var height = Math.floor((b[1][0] - aResult[i-1][1][1]) * this.scale);
var style = "height: " + height + "px;";
wf.setAttribute('style', style);
//var cell = view.getCell();
}
},
setUpHandlers: function() {
_t = this;
},
destroyHandlers: function() {
}
};
var waterfallController = {
__proto__: baseController,
beforeSelect: function() {
this.result = {};
this.isShown = false;
waterfallView.setUpHandlers();
var _t = this;
var callback = function(obj) {
delete _t.req;
if (view != waterfallView) {
// ignore, we have switched again
return;
}
_t.result = obj;
_t.tag = obj[obj.length - 1][0]; // set the subdir to latest build
controller.addLocales(keys(obj[obj.length - 1][2]));
waterfallView.updateView(_t.result);
};
this.req = JSON.get('results/waterfall.json', callback);
},
beforeUnSelect: function() {
if (this.req) {
this.req.abort();
delete this.req;
}
waterfallView.unstyle();
waterfallView.destroyHandlers();
},
showView: function(aClosure) {
// json onload handler does this;
}
};
controller.addPane('Tinder', 'waterfall', waterfallController,
waterfallView);