From 25964491dd077dbc3c12d56a3f319ce2c18d46cc Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 14 Apr 2011 19:28:38 +0200 Subject: [PATCH] euscanwww: big commit - fix commands - import DataTables - initial category view Signed-off-by: Corentin Chary --- euscan | 59 +- .../management/commands/list-packages.py | 10 + .../management/commands/scan-portage.py | 11 +- .../management/commands/scan-upstream.py | 40 +- .../management/commands/update-counters.py | 90 + euscanwww/euscan/templatetags/__init__.py | 0 euscanwww/euscan/templatetags/math_.py | 29 + euscanwww/euscan/templatetags/sub.py | 8 + euscanwww/euscan/views.py | 16 +- euscanwww/euscan/views.pyc | Bin 1691 -> 2780 bytes euscanwww/media/css/table.css | 540 ++ euscanwww/media/img/back_disabled.jpg | Bin 0 -> 612 bytes euscanwww/media/img/back_enabled.jpg | Bin 0 -> 807 bytes euscanwww/media/img/forward_disabled.jpg | Bin 0 -> 635 bytes euscanwww/media/img/forward_enabled.jpg | Bin 0 -> 852 bytes euscanwww/media/img/sort_asc.png | Bin 0 -> 263 bytes euscanwww/media/img/sort_asc_disabled.png | Bin 0 -> 252 bytes euscanwww/media/img/sort_both.png | Bin 0 -> 282 bytes euscanwww/media/img/sort_desc.png | Bin 0 -> 260 bytes euscanwww/media/img/sort_desc_disabled.png | Bin 0 -> 251 bytes euscanwww/media/js/jquery.dataTables.js | 6862 +++++++++++++++++ euscanwww/media/js/jquery.dataTables.min.js | 143 + .../media/js/jquery.dataTables.min.js.gz | Bin 0 -> 18052 bytes euscanwww/media/js/jquery.js | 16 + euscanwww/settings.py | 2 +- euscanwww/templates/_base.html | 2 +- euscanwww/templates/euscan/_base.html | 6 +- euscanwww/templates/euscan/_datatable.html | 21 + euscanwww/templates/euscan/categories.html | 21 + euscanwww/templates/euscan/index.html | 12 +- 30 files changed, 7841 insertions(+), 47 deletions(-) create mode 100644 euscanwww/euscan/management/commands/list-packages.py create mode 100644 euscanwww/euscan/management/commands/update-counters.py create mode 100644 euscanwww/euscan/templatetags/__init__.py create mode 100644 euscanwww/euscan/templatetags/math_.py create mode 100644 euscanwww/euscan/templatetags/sub.py create mode 100644 euscanwww/media/css/table.css create mode 100644 euscanwww/media/img/back_disabled.jpg create mode 100644 euscanwww/media/img/back_enabled.jpg create mode 100644 euscanwww/media/img/forward_disabled.jpg create mode 100644 euscanwww/media/img/forward_enabled.jpg create mode 100644 euscanwww/media/img/sort_asc.png create mode 100644 euscanwww/media/img/sort_asc_disabled.png create mode 100644 euscanwww/media/img/sort_both.png create mode 100644 euscanwww/media/img/sort_desc.png create mode 100644 euscanwww/media/img/sort_desc_disabled.png create mode 100644 euscanwww/media/js/jquery.dataTables.js create mode 100644 euscanwww/media/js/jquery.dataTables.min.js create mode 100644 euscanwww/media/js/jquery.dataTables.min.js.gz create mode 100644 euscanwww/media/js/jquery.js create mode 100644 euscanwww/templates/euscan/_datatable.html create mode 100644 euscanwww/templates/euscan/categories.html diff --git a/euscan b/euscan index 6cf4497..ff54642 100755 --- a/euscan +++ b/euscan @@ -45,6 +45,8 @@ from gentoolkit.eclean.search import (port_settings) QUERY_OPTS = {"include_masked": True} +SCANDIR_BLACKLIST_URLS = ['mirror://rubygems/(.*)', 'mirror://gentoo/(.*)'] + BRUTEFORCE_BLACKLIST_PACKAGES = ['dev-util/patchelf', 'net-zope/plonepopoll'] BRUTEFORCE_BLACKLIST_URLS = ['http://www.dockapps.org/download.php/id/(.*)'] @@ -182,7 +184,9 @@ def tryurl(fileurl, output): else: result = True except urllib2.URLError: - retult = False + result = False + except IOError: + result = False output.eend(errno.ENOENT if not result else 0) @@ -242,6 +246,8 @@ def scan_directory_recursive(url, steps, vmin, vmax, output): fp = urllib2.urlopen(url, None, 5) except urllib2.URLError: return [] + except IOError: + return [] data = fp.read() @@ -302,7 +308,7 @@ def scan_directory(cpv, fileurl, options, output, limit=None): template = template_from_url(fileurl, ver) if '${' not in template: - output.ewarn("Url doesn't seems to depend on version: %s not found in %s" + output.einfo("Url doesn't seems to depend on version: %s not found in %s" % (ver, fileurl)) return [] else: @@ -321,12 +327,12 @@ def brute_force(cpv, fileurl, options, output, limit=None): for bp in BRUTEFORCE_BLACKLIST_PACKAGES: if re.match(bp, catpkg): - output.ewarn("%s is blacklisted by rule %s" % (catpkg, bp)) + output.einfo("%s is blacklisted by rule %s" % (catpkg, bp)) return [] for bp in BRUTEFORCE_BLACKLIST_URLS: if re.match(bp, fileurl): - output.ewarn("%s is blacklisted by rule %s" % (catpkg, bp)) + output.einfo("%s is blacklisted by rule %s" % (catpkg, bp)) return [] components = split_version(ver) @@ -335,13 +341,13 @@ def brute_force(cpv, fileurl, options, output, limit=None): output.einfo("Generating version from " + ver) if not versions: - output.ewarn("Can't generate new versions from " + ver) + output.einfo("Can't generate new versions from " + ver) return [] template = template_from_url(fileurl, ver) if '${PV}' not in template: - output.ewarn("Url doesn't seems to depend on full version: %s not found in %s" + output.einfo("Url doesn't seems to depend on full version: %s not found in %s" % (ver, fileurl)) return [] else: @@ -384,6 +390,8 @@ def brute_force(cpv, fileurl, options, output, limit=None): def parseMirror(uri, output): + from random import shuffle + mirrors = portage.settings.thirdpartymirrors() if not uri.startswith("mirror://"): @@ -391,17 +399,19 @@ def parseMirror(uri, output): eidx = uri.find("/", 9) if eidx == -1: - output.ewarn("Invalid mirror definition in SRC_URI:\n") - output.ewarn(" %s\n" % (uri)) + output.einfo("Invalid mirror definition in SRC_URI:\n") + output.einfo(" %s\n" % (uri)) return None mirrorname = uri[9:eidx] path = uri[eidx+1:] if mirrorname in mirrors: - uri = mirrors[mirrorname][0].strip("/") + "/" + path + mirrors = mirrors[mirrorname] + shuffle(mirrors) + uri = mirrors[0].strip("/") + "/" + path else: - output.ewarn("No known mirror by the name: %s\n" % (mirrorname)) + output.einfo("No known mirror by the name: %s\n" % (mirrorname)) return None return uri @@ -567,7 +577,11 @@ def scanUpstream(options, package, output): matches = sorted(matches) pkg = matches.pop() if pkg.version == '9999': - pkg = matches.pop() + if len(matches) == 0: + sys.stderr.write(pp.warn("Package '%s' only have a dev version (9999)" % pp.pkgquery(package))) + sys.exit(errno.ENOENT) + else: + pkg = matches.pop() pp.uprint(" * %s [%s]" % (pp.cpv(pkg.cpv), pp.section(pkg.repo_name()))) pp.uprint() @@ -582,7 +596,7 @@ def scanUpstream(options, package, output): pp.uprint() cpv = pkg.cpv - metadata = { + metadata = { "EAPI" : port_settings["EAPI"], "SRC_URI" : pkg.environment("SRC_URI", False), } @@ -604,16 +618,29 @@ def scanUpstream(options, package, output): for filename in fetchme: for fileurl in fetchme[filename]: - fileurl = parseMirror(fileurl, output) + skipscan = False - # Try list dir - versions.extend(scan_directory(cpv, fileurl, options, output)) + if '://' not in fileurl: + output.einfo("Invalid url '%s'" % fileurl) + continue + + for bp in SCANDIR_BLACKLIST_URLS: + if re.match(bp, fileurl): + output.einfo("%s is blacklisted by rule %s" % (fileurl, bp)) + skipscan = True + + url = parseMirror(fileurl, output) + + + # Try list dir, but not for gentoo mirrors, it's too slow + if not skipscan: + versions.extend(scan_directory(cpv, url, options, output)) if versions and options['oneshot']: break # Try manual bump - versions.extend(brute_force(cpv, fileurl, options, output)) + versions.extend(brute_force(cpv, url, options, output)) if versions and options['oneshot']: break diff --git a/euscanwww/euscan/management/commands/list-packages.py b/euscanwww/euscan/management/commands/list-packages.py new file mode 100644 index 0000000..8233966 --- /dev/null +++ b/euscanwww/euscan/management/commands/list-packages.py @@ -0,0 +1,10 @@ +from django.core.management.base import BaseCommand, CommandError +from euscanwww.euscan.models import Package + +class Command(BaseCommand): + _overlays = {} + help = 'List packages' + + def handle(self, *args, **options): + for pkg in Package.objects.all(): + self.stdout.write('%s/%s\n' % (pkg.category, pkg.name)) diff --git a/euscanwww/euscan/management/commands/scan-portage.py b/euscanwww/euscan/management/commands/scan-portage.py index 4969742..f08fcd8 100644 --- a/euscanwww/euscan/management/commands/scan-portage.py +++ b/euscanwww/euscan/management/commands/scan-portage.py @@ -141,9 +141,14 @@ class Command(BaseCommand): obj, created = Version.objects.get_or_create(package=package, slot=slot, revision=rev, version=ver, overlay=overlay) + + if created or not package.n_packaged: + package.n_packaged += 1 + if created: + package.n_versions += 1 + + package.save() + obj.packaged = True obj.save() - package.n_versions += 1 - package.n_packaged += 1 - package.save() diff --git a/euscanwww/euscan/management/commands/scan-upstream.py b/euscanwww/euscan/management/commands/scan-upstream.py index 946b9b1..87013a0 100644 --- a/euscanwww/euscan/management/commands/scan-upstream.py +++ b/euscanwww/euscan/management/commands/scan-upstream.py @@ -4,6 +4,7 @@ import sys import os import re +from StringIO import StringIO from datetime import datetime from portage import versions from optparse import make_option @@ -21,11 +22,11 @@ class Command(BaseCommand): dest='all', default=False, help='Scan all packages'), - make_option('--parallel', + make_option('--feed', action='store_true', - dest='parallel', + dest='feed', default=False, - help='Use GNU Parallel'), + help='Read euscan output from stdin'), make_option('--quiet', action='store_true', dest='quiet', @@ -36,9 +37,13 @@ class Command(BaseCommand): help = 'Scans metadata and fills database' def handle(self, *args, **options): - if len(args) == 0 and options['all'] == False: + if len(args) == 0 and options['all'] == False and options['feed'] == False: raise CommandError('You must specify a package or use --all') + if options['feed']: + self.parse_output(options, sys.stdin) + return + if not options['quiet']: self.stdout.write('Scanning upstream...\n') @@ -57,22 +62,13 @@ class Command(BaseCommand): @commit_on_success def scan(self, options, packages=None): - if options['parallel']: - jobs = '\n'.join(packages) - cmd = ['gparallel', '--jobs', '150%', 'euscan'] + for package in packages: + cmd = ['euscan', package] - fp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE) - output = fp.communicate(jobs)[0] + fp = subprocess.Popen(cmd, stdout=subprocess.PIPE) + output = StringIO(fp.communicate()[0]) self.parse_output(options, output) - else: - for package in packages: - cmd = ['euscan', package] - - fp = subprocess.Popen(cmd, stdout=subprocess.PIPE) - output = fp.communicate()[0] - - self.parse_output(options, output) def parse_output(self, options, output): from portage.versions import _cp @@ -83,7 +79,10 @@ class Command(BaseCommand): package = None log = "" - for line in output.split('\n'): + while True: + line = output.readline() + if line == '': + break match = package_re.match(line) if match: if package: @@ -143,5 +142,6 @@ class Command(BaseCommand): obj.packaged = False obj.save() - package.n_versions += 1 - package.save() + if created: + package.n_versions += 1 + package.save() diff --git a/euscanwww/euscan/management/commands/update-counters.py b/euscanwww/euscan/management/commands/update-counters.py new file mode 100644 index 0000000..4625f01 --- /dev/null +++ b/euscanwww/euscan/management/commands/update-counters.py @@ -0,0 +1,90 @@ +import datetime + +from optparse import make_option + +from django.db.models import Count, Sum +from django.db.transaction import commit_on_success +from django.core.management.base import BaseCommand, CommandError +from euscanwww.euscan.models import Package, HerdLog, MaintainerLog, CategoryLog, Herd, Maintainer, Version + +class Command(BaseCommand): + _overlays = {} + help = 'Update counters' + + option_list = BaseCommand.option_list + ( + make_option('--quiet', + action='store_true', + dest='quiet', + default=False, + help='Be quiet'), + ) + + @commit_on_success + def handle(self, *args, **options): + now = datetime.datetime.now() + + categories = {} + herds = {} + maintainers = {} + + # Could be done using raw SQL queries, but I don't have time for that + # right now ... + + for cat in Package.objects.values('category').distinct(): + clog = CategoryLog() + clog.datetime = now + clog.category = cat['category'] + categories[clog.category] = clog + + for herd in Herd.objects.all(): + hlog = HerdLog() + hlog.datetime = now + hlog.herd = herd + herds[herd] = hlog + + for maintainer in Maintainer.objects.all(): + mlog = MaintainerLog() + mlog.datetime = now + mlog.maintainer = maintainer + maintainers[maintainer] = mlog + + for package in Package.objects.all(): + # Should not be needed, but can't hurt + package.n_versions = Version.objects.filter(package=package).count() + package.n_packaged != Version.objects.filter(package=package,packaged=True).count() + package.save() + + for herd in package.herds.all(): + herds[herd].n_packages += 1 + herds[herd].n_versions += package.n_versions + herds[herd].n_packaged += package.n_packaged + + for maintainer in package.maintainers.all(): + maintainers[maintainer].n_packages += 1 + maintainers[maintainer].n_versions += package.n_versions + maintainers[maintainer].n_packaged += package.n_packaged + + categories[package.category].n_packages += 1 + categories[package.category].n_versions += package.n_versions + categories[package.category].n_packaged += package.n_packaged + + for clog in categories.values(): + if not options['quiet']: + self.stdout.write('[c] %s - [%d, %d/%d]\n' % + (clog.category, clog.n_packages, + clog.n_packaged, clog.n_versions)) + clog.save() + + for hlog in herds.values(): + if not options['quiet']: + self.stdout.write('[h] %s - [%d, %d/%d]\n' % + (hlog.herd, hlog.n_packages, + hlog.n_packaged, hlog.n_versions)) + hlog.save() + + for mlog in maintainers.values(): + if not options['quiet']: + self.stdout.write('[m] %s - [%d, %d/%d]\n' % + (mlog.maintainer, mlog.n_packages, + mlog.n_packaged, mlog.n_versions)) + mlog.save() diff --git a/euscanwww/euscan/templatetags/__init__.py b/euscanwww/euscan/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/euscanwww/euscan/templatetags/math_.py b/euscanwww/euscan/templatetags/math_.py new file mode 100644 index 0000000..226ea8f --- /dev/null +++ b/euscanwww/euscan/templatetags/math_.py @@ -0,0 +1,29 @@ +from django.template import Node, Library +import math + +register = Library() + +# taken from http://lybniz2.sourceforge.net/safeeval.html +# make a list of safe functions +math_safe_list = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh'] + +# use the list to filter the local namespace +math_safe_dict = dict([(k, getattr(math, k)) for k in math_safe_list]) + +# add any needed builtins back in. +math_safe_dict['abs'] = abs + +@register.filter('math') +def math_(lopr, expr): + """Evals a math expression and returns it's value. + + "$1" is a placeholder. Insert "$1" in the expression where the value is to be used. All math functions such as abs, sin, cos, floor are supported. + Example, + a. You will be redirected in {{ seconds|math:"$1 / 60.0" }} minutes + b. Square of {{ x }} is {{ x|math:"$1 * $1" }} + c. Square root of {{ x }} is {{ x|math:"sqrt($1)" }} + d. Given x = {{ x }}, (2 + x) * 6 = {{ x|math:"(2 + $1) * 6" }} + """ + if lopr: + return eval(expr.replace('$1', str(lopr)), {"__builtins__": None}, math_safe_dict) + return '' diff --git a/euscanwww/euscan/templatetags/sub.py b/euscanwww/euscan/templatetags/sub.py new file mode 100644 index 0000000..9da87c1 --- /dev/null +++ b/euscanwww/euscan/templatetags/sub.py @@ -0,0 +1,8 @@ +from django import template + +register = template.Library() + +def sub(value, arg=None): + return value-arg + +register.filter('sub', sub) diff --git a/euscanwww/euscan/views.py b/euscanwww/euscan/views.py index 3fdf543..2c1d47d 100644 --- a/euscanwww/euscan/views.py +++ b/euscanwww/euscan/views.py @@ -1,9 +1,20 @@ from annoying.decorators import render_to from django.http import Http404 +from django.db.models import Sum, Max + +from euscan.models import Version, Package, Herd, Maintainer, EuscanResult @render_to('euscan/index.html') def index(request): - return {} + ctx = {} + ctx['n_packaged'] = Package.objects.aggregate(Sum('n_packaged'))['n_packaged__sum'] + ctx['n_versions'] = Package.objects.aggregate(Sum('n_versions'))['n_versions__sum'] + ctx['n_upstream'] = ctx['n_versions'] - ctx['n_packaged'] + ctx['n_packages'] = Package.objects.count() + ctx['n_herds'] = Herd.objects.count() + ctx['n_maintainers'] = Maintainer.objects.count() + ctx['last_scan'] = EuscanResult.objects.aggregate(Max('datetime'))['datetime__max'] + return ctx @render_to('euscan/logs.html') def logs(request): @@ -11,7 +22,8 @@ def logs(request): @render_to('euscan/categories.html') def categories(request): - return {} + categories = Package.objects.values('category').annotate(n_packaged=Sum('n_packaged'), n_versions=Sum('n_versions')) + return { 'categories' : categories } @render_to('euscan/category.html') def category(request, category): diff --git a/euscanwww/euscan/views.pyc b/euscanwww/euscan/views.pyc index 862a5a9e086a7dd7aeb45ddd99db15b71f7bcaa7..4aa64aa4ddda665eedd4bd8838dad2fe21f21bd8 100644 GIT binary patch literal 2780 zcmc&$%Wm676uqRLmgHD|$cftmanUZcC@dEUx~WkVC<#Is^at8|E;$n2MHbc~qUUnv%;n6TbI;89{Xe_CcOU+~jHvpx`TZjo z{oEmn`Ol#_QR;i3vN z;O5UO9fg^v^eQ!ap8+MnB;on*&SqwjMhFU%V$Pqq=(lWUM1MMDIB-T!A&y=G;<5Kvf!p9R97yxS$A12wJB6+zo-V)l7lRm zG4tB&%3zB%`Py_Di9@4IvQW}(8$qxL??eS@s7-+UjG+l{o#g!0YZVzSKH+p)s40)* zi7?<3<*Uqi*n%D;;bA5Z*(VjHl ztZq^MRT&vNy{Bxp4iuwf@AP#$s8^EvYN~rra@)taP~0X0@s*@|N9aWiH(`prb-5a5 zS#E6JC9}ei_XYQDOOp%&zBTwF_uSD4;UP))WdcDD%109jhcdj+xwdt97zFFI8LS;# zvfVz3w6F(c_S0HqHO-^%F_g*qjgPmc4S{`K3l15sa#}l?J_&iye=Pheez9P%nvKVUH$|A7si Rp>ynx-O*sw9JRPq&wp=UMq2;? literal 1691 zcmc&!%Z?K<5bSy6ouEa#2nmS;x12H{?GYiwUbwIabIWCAjm^YlG81fvkRyU$;e+;9 z_yOp05)#k~X&H%~qIKjv)eoRgY7oGH)G&Y{ zsS&*$-(wc$2S4M8k?(P`x>DL+`FdOGaOt)8%k!t_?mlC^a=ENB!-GCeW`0rTn7NM& z#xCVFBkqps<(Ly4dRhHprJdha@t1qXDVx;`ote3l=3VA&p6!;|zSisYzVOlLwVNzA zzQwZw39RN~P88ovMcqI}U_xUKSh}nW*X8$?LiE2W^m*xAdR^4kXg4?_f*m4o2)~Zy zPc1MYXyLJkU87A?VMLpo!CH4D3cNAXRt3ZEIowhYoGp4Z)&jzw-JKSp_Nb7<=x z9x=J4BBS3rU75zxc$oLV?a>jngfZf;=EZ c4 + ee > d5 + Level 2: + dd > d1 + ee > e2 + */ +/* +tr.odd.gradeA td.sorting_1 { + background-color: #c4ffc4; +} + +tr.odd.gradeA td.sorting_2 { + background-color: #d1ffd1; +} + +tr.odd.gradeA td.sorting_3 { + background-color: #d1ffd1; +} + +tr.even.gradeA td.sorting_1 { + background-color: #d5ffd5; +} + +tr.even.gradeA td.sorting_2 { + background-color: #e2ffe2; +} + +tr.even.gradeA td.sorting_3 { + background-color: #e2ffe2; +} + +tr.odd.gradeC td.sorting_1 { + background-color: #c4c4ff; +} + +tr.odd.gradeC td.sorting_2 { + background-color: #d1d1ff; +} + +tr.odd.gradeC td.sorting_3 { + background-color: #d1d1ff; +} + +tr.even.gradeC td.sorting_1 { + background-color: #d5d5ff; +} + +tr.even.gradeC td.sorting_2 { + background-color: #e2e2ff; +} + +tr.even.gradeC td.sorting_3 { + background-color: #e2e2ff; +} + +tr.odd.gradeX td.sorting_1 { + background-color: #ffc4c4; +} + +tr.odd.gradeX td.sorting_2 { + background-color: #ffd1d1; +} + +tr.odd.gradeX td.sorting_3 { + background-color: #ffd1d1; +} + +tr.even.gradeX td.sorting_1 { + background-color: #ffd5d5; +} + +tr.even.gradeX td.sorting_2 { + background-color: #ffe2e2; +} + +tr.even.gradeX td.sorting_3 { + background-color: #ffe2e2; +} + +tr.odd.gradeU td.sorting_1 { + background-color: #c4c4c4; +} + +tr.odd.gradeU td.sorting_2 { + background-color: #d1d1d1; +} + +tr.odd.gradeU td.sorting_3 { + background-color: #d1d1d1; +} + +tr.even.gradeU td.sorting_1 { + background-color: #d5d5d5; +} + +tr.even.gradeU td.sorting_2 { + background-color: #e2e2e2; +} + +tr.even.gradeU td.sorting_3 { + background-color: #e2e2e2; +} +*/ + +/* + * Row highlighting example + */ +.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted { + background-color: #ECFFB3; +} + +.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted { + background-color: #E6FF99; +} + +.ex_highlight_row #example tr.even:hover { + background-color: #ECFFB3; +} + +.ex_highlight_row #example tr.even:hover td.sorting_1 { + background-color: #DDFF75; +} + +.ex_highlight_row #example tr.even:hover td.sorting_2 { + background-color: #E7FF9E; +} + +.ex_highlight_row #example tr.even:hover td.sorting_3 { + background-color: #E2FF89; +} + +.ex_highlight_row #example tr.odd:hover { + background-color: #E6FF99; +} + +.ex_highlight_row #example tr.odd:hover td.sorting_1 { + background-color: #D6FF5C; +} + +.ex_highlight_row #example tr.odd:hover td.sorting_2 { + background-color: #E0FF84; +} + +.ex_highlight_row #example tr.odd:hover td.sorting_3 { + background-color: #DBFF70; +} + + +/* + * KeyTable + */ +table.KeyTable td { + border: 3px solid transparent; +} + +table.KeyTable td.focus { + border: 3px solid #3366FF; +} + +table.display tr.gradeA { + background-color: #eeffee; +} + +table.display tr.gradeC { + background-color: #ddddff; +} + +table.display tr.gradeX { + background-color: #ffdddd; +} + +table.display tr.gradeU { + background-color: #ddd; +} + +div.box { + height: 100px; + padding: 10px; + overflow: auto; + border: 1px solid #8080FF; + background-color: #E5E5FF; +} diff --git a/euscanwww/media/img/back_disabled.jpg b/euscanwww/media/img/back_disabled.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1e73a546e3609636f9cf4c543c2a4fe4050866c3 GIT binary patch literal 612 zcmex=C5UDGKfoZ!!63{a%*-grz$D1XEXer(2tyUn z+02YUSHb}sD?0}l&^EXLguy7lz`(-H!phDIlVN0HVHIR%V-ON%S2Pq6HFFG{Xryc+ zmRQ($@S#&sld*HEn>dFcBLmQ_$YwDLDjGT#PW*q1fd^^;oX+iLI)z|@;wX;U8Xr> zT8Qgxn;R!Cottl}#(8Ri${a3d=J*$R5qBRbK9bRJe*4BYwq9(uIM4mLS9%TSY?*y6 zE==owYH)vf>dq6(Uzum+IyVJqc3N&dRPD57vW(=l(=(DZ-K_!LSx zwPk@>bN@4B-OI9gCf>Wc_;CDAS%c@BJZ}kBlbDz~@y-*&Y^_U+@};q`xNUthN} On^Cj9W}Eu@|2F})$f79# literal 0 HcmV?d00001 diff --git a/euscanwww/media/img/back_enabled.jpg b/euscanwww/media/img/back_enabled.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a6d764c79c7a2047b6bf65bf9d96fcafd5161e38 GIT binary patch literal 807 zcmex=C5UDGKfoZ!!63{a%*-grz$D1XEXer(2*U&h z1}0{pBN2d|gO!bok%5r~RhW^Pm5qg+Lx6#Sm5Gs^od+h%$i&RTD#*spplB#0EW!~u z(MZ`u)XXulu<_tSr=TWdXR%Z_aiE1jZ7eM8Fl&IpCkWK1$Y$s$#2&a&N#sM~M5iWU z<^Q)Bc$gW1&SMs2uxGfw`}E06etb^r9k|jMc@Dlk(IlSD-Wwx*G{%4ThHhR%HnTsw zbu3z!=BtbC=-In5f5nt;je{+vrKZdWckk$U`c^FEYC6}Jn=lG)17QB>6z3khm7PHhg<-pEGSO0F>VA}N6`w53!8pr$>MbUW| zMEsFn=(%w+^5v0yp7Lmb+~3PpI4(#G~bQ$y_;t)oo+U5`bnSHZcn){CH~m7Ys2rz z2!R@xgGrCtcPz?eOPOzKr2P26p~(mTGjR1-c3?psA>uUGsyt|R2Ts3~!B zN71DGSrW%W*`w8#2{O$LX)%%8*<4Ycyr;tN(Hm8|M=_HtvR>P P+Rcr(b5Xx$|NkZcT0jT? literal 0 HcmV?d00001 diff --git a/euscanwww/media/img/forward_disabled.jpg b/euscanwww/media/img/forward_disabled.jpg new file mode 100644 index 0000000000000000000000000000000000000000..28a9dc53fa232919299ac980dc9b61167c1868bd GIT binary patch literal 635 zcmex=C5UDGKfoZ!!63{a%*-grz$D1XEXer(2tySE z1JEp>E8&2Nm4%I+g9~UMTnNHo6kuRrVP;|B;)Y2uGBL9Vva&GlzGLeh=CM*_u2I6%$1L~6;-1yGa|?*FvavCW+Av>{Flt>+*?d?iZ`9)^O?_H|D3t8;Og6lD<#WHQvW(mo1ETsD>A@mr8@sqg(?1R z%d@qrmWLWmIn1#pQuo}{brVW*C8t%EDXbOt_DabwXgGW3;wDX1;~7hWrYL+ZX=qre z&-y^2q0zm?-FJb#(AWJTHOJ-3`S`D|JEhzC{KvY_h0kZinXP-?w!g_L@p#JQO^!z$ mYCgVwS7#SryXs@?>&L&t^Z(SozHVt~@Z#>58}s)6zX<@qORf(9 literal 0 HcmV?d00001 diff --git a/euscanwww/media/img/forward_enabled.jpg b/euscanwww/media/img/forward_enabled.jpg new file mode 100644 index 0000000000000000000000000000000000000000..598c075f13ab0e4bbe05a4eeea7cf777ba73cf03 GIT binary patch literal 852 zcmex=C5UDGKfoZ!!63{a%*-grz$D1XEXer(2tywO z0~0eN(3Nn&%Fe>Z!NthH$ciG&$iU3P%Em6hz`z1zu=B#yGcqx=2(q#<2(fb*3M-3< zDh5t8G7&R#Oe}1C=oHjs?40T*F37+LbR`oj+(bq}CT0c}Rz*W0Hg?Cv#)(EB0yioN zi~PUEzyov)lOVGogFVA5dn3uE%Z<*rn&ugY^FIG9<<=87L*?mOy^~u`Rq(Eg+UDw^ z@^Uh}zJ$t~P2YpRT#sm2p`oEt8zJ-EBRfY;>b?DxZo!24Z)Q(O&}!Mbt}27`dKa(x zr)AHkZg5MjKa&4$syaupXWNXgp32dQs_rj-?|M=Q9pN^hZg^mV7=UWs>}D5 z$40(ME7Q1T4h8p}ZJ8O!G-Xk84;P~#ci!&o`A5I3kG@gLm*VlJbn%kvSqIh?3Y?HQ zAX0NL>XIkpf=MN83*!%}9-aN5)p7^py2Am>F9gW&pFSp#V0dxX#ZPW;UoD(gaI@8E z(rNQz1DBIiM13!LtZ|85lHB@$wW~n0fhRz-$?R&m{@z;uI}N|35As;}zdXB<I#+N1k=r*G-&mI78vJ_Ezs!yIV%J}l7d*c9>7Bn;xyLT84V&`$ z@9WMTAJ6DZyR)~=FS6P2e3MDv7lHB>{C&1H^7EJ9PIf=EdUwUYtGE9%$X9(m*ZWLf M_rC2^{;K~s0TRp{r2qf` literal 0 HcmV?d00001 diff --git a/euscanwww/media/img/sort_asc.png b/euscanwww/media/img/sort_asc.png new file mode 100644 index 0000000000000000000000000000000000000000..a56d0e21902ce620cd57f87fa58a6d3e804135cf GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>l2Ka=y{`vF&J9*~+_wWBdfBFCW_y37A{>@tOZ^^2EJ(K_a|Nno&o$%d2jr=7+e!)O45@5J; zXU0OHl&hzUV@SoVE9Xw~GAQscA1s)^?0*rXqM}DeLv7?|BU8z@QCOGupVzP9Hek5&L+Gnl%BD>~0~tJB{an^LB{Ts5b0m7= literal 0 HcmV?d00001 diff --git a/euscanwww/media/img/sort_asc_disabled.png b/euscanwww/media/img/sort_asc_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..b7e621ef1c68da839ce077cec6e201a55c1ba168 GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=)2l#}z{`>du$&)AV-@pIyd9;k)4B*-rq$b|z2+ZRf0KnW*L z7srr_TUXBQ7GeV6G?@}f$7k6Jhw{qGm$-QDzD`qrDaZ3s$b2XF;fBBPU nqjNDy&Nw~puHFA+-v&m8`?>-yUFVHK{$ucT^>bP0l+XkKhw^g= literal 0 HcmV?d00001 diff --git a/euscanwww/media/img/sort_both.png b/euscanwww/media/img/sort_both.png new file mode 100644 index 0000000000000000000000000000000000000000..839ac4bb5b0b414834c950de9deafff6dd94ed2d GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=)2l#}z{`>du$&)AV-@pIyd9;k)4B*-rq$b|z2+ZRf0K#2rT z7srr_TRjsz`4|*ASOSl%eg6Ob+(JtRwX|O@S}a^IESQCgTe~DWM4fR9b+X literal 0 HcmV?d00001 diff --git a/euscanwww/media/img/sort_desc.png b/euscanwww/media/img/sort_desc.png new file mode 100644 index 0000000000000000000000000000000000000000..90b295159df995329b3a84340d32761f3d1dbade GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>l2Ka=y{`vF&J9*~+_wWBdfBFCW_y37A{>@tOZ^^2EJ(K_a|Nno&o$%d2jr=7+e!)O45@5J; zXU0OHl#{26V@SoVqz8vtc$~v}dc~O{CLEF2anNavMpdMP)0v(X&o(k0opIq!LdvtJ wj~up7@0`wiSoe($&y07EkGxK6U|?nlJSz0{?wJWUfu=Edy85}Sb4q9e0C9MGu>b%7 literal 0 HcmV?d00001 diff --git a/euscanwww/media/img/sort_desc_disabled.png b/euscanwww/media/img/sort_desc_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..2409653dc94cd21a281a31c0e3819323b84704b7 GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=)2l#}z{`>du$&)AV-@pIyd9;k)4B*-rq$b|z2+ZRf0KnX`r z7srr_TS*TNvhX;E^>j)zMNBv%VR68zN24n8K+>5N&kUJbBQCy4c=qgBiu3mAQ)hD? n)_oJhGvi(If!Apb49pB3GXz|UGFKJ=4P)?h^>bP0l+XkK*57ji literal 0 HcmV?d00001 diff --git a/euscanwww/media/js/jquery.dataTables.js b/euscanwww/media/js/jquery.dataTables.js new file mode 100644 index 0000000..581222e --- /dev/null +++ b/euscanwww/media/js/jquery.dataTables.js @@ -0,0 +1,6862 @@ +/* + * File: jquery.dataTables.js + * Version: 1.7.6 + * Description: Paginate, search and sort HTML tables + * Author: Allan Jardine (www.sprymedia.co.uk) + * Created: 28/3/2008 + * Language: Javascript + * License: GPL v2 or BSD 3 point style + * Project: Mtaala + * Contact: allan.jardine@sprymedia.co.uk + * + * Copyright 2008-2010 Allan Jardine, all rights reserved. + * + * This source file is free software, under either the GPL v2 license or a + * BSD style license, as supplied with this software. + * + * This source file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. + * + * For details please refer to: http://www.datatables.net + */ + +/* + * When considering jsLint, we need to allow eval() as it it is used for reading cookies and + * building the dynamic multi-column sort functions. + */ +/*jslint evil: true, undef: true, browser: true */ +/*globals $, jQuery,_fnExternApiFunc,_fnInitalise,_fnInitComplete,_fnLanguageProcess,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnGatherData,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxUpdateDraw,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnArrayCmp,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap*/ + +(function($, window, document) { + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Section - DataTables variables + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /* + * Variable: dataTableSettings + * Purpose: Store the settings for each dataTables instance + * Scope: jQuery.fn + */ + $.fn.dataTableSettings = []; + var _aoSettings = $.fn.dataTableSettings; /* Short reference for fast internal lookup */ + + /* + * Variable: dataTableExt + * Purpose: Container for customisable parts of DataTables + * Scope: jQuery.fn + */ + $.fn.dataTableExt = {}; + var _oExt = $.fn.dataTableExt; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Section - DataTables extensible objects + * + * The _oExt object is used to provide an area where user dfined plugins can be + * added to DataTables. The following properties of the object are used: + * oApi - Plug-in API functions + * aTypes - Auto-detection of types + * oSort - Sorting functions used by DataTables (based on the type) + * oPagination - Pagination functions for different input styles + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /* + * Variable: sVersion + * Purpose: Version string for plug-ins to check compatibility + * Scope: jQuery.fn.dataTableExt + * Notes: Allowed format is a.b.c.d.e where: + * a:int, b:int, c:int, d:string(dev|beta), e:int. d and e are optional + */ + _oExt.sVersion = "1.7.6"; + + /* + * Variable: sErrMode + * Purpose: How should DataTables report an error. Can take the value 'alert' or 'throw' + * Scope: jQuery.fn.dataTableExt + */ + _oExt.sErrMode = "alert"; + + /* + * Variable: iApiIndex + * Purpose: Index for what 'this' index API functions should use + * Scope: jQuery.fn.dataTableExt + */ + _oExt.iApiIndex = 0; + + /* + * Variable: oApi + * Purpose: Container for plugin API functions + * Scope: jQuery.fn.dataTableExt + */ + _oExt.oApi = { }; + + /* + * Variable: aFiltering + * Purpose: Container for plugin filtering functions + * Scope: jQuery.fn.dataTableExt + */ + _oExt.afnFiltering = [ ]; + + /* + * Variable: aoFeatures + * Purpose: Container for plugin function functions + * Scope: jQuery.fn.dataTableExt + * Notes: Array of objects with the following parameters: + * fnInit: Function for initialisation of Feature. Takes oSettings and returns node + * cFeature: Character that will be matched in sDom - case sensitive + * sFeature: Feature name - just for completeness :-) + */ + _oExt.aoFeatures = [ ]; + + /* + * Variable: ofnSearch + * Purpose: Container for custom filtering functions + * Scope: jQuery.fn.dataTableExt + * Notes: This is an object (the name should match the type) for custom filtering function, + * which can be used for live DOM checking or formatted text filtering + */ + _oExt.ofnSearch = { }; + + /* + * Variable: afnSortData + * Purpose: Container for custom sorting data source functions + * Scope: jQuery.fn.dataTableExt + * Notes: Array (associative) of functions which is run prior to a column of this + * 'SortDataType' being sorted upon. + * Function input parameters: + * object:oSettings- DataTables settings object + * int:iColumn - Target column number + * Return value: Array of data which exactly matched the full data set size for the column to + * be sorted upon + */ + _oExt.afnSortData = [ ]; + + /* + * Variable: oStdClasses + * Purpose: Storage for the various classes that DataTables uses + * Scope: jQuery.fn.dataTableExt + */ + _oExt.oStdClasses = { + /* Two buttons buttons */ + "sPagePrevEnabled": "paginate_enabled_previous", + "sPagePrevDisabled": "paginate_disabled_previous", + "sPageNextEnabled": "paginate_enabled_next", + "sPageNextDisabled": "paginate_disabled_next", + "sPageJUINext": "", + "sPageJUIPrev": "", + + /* Full numbers paging buttons */ + "sPageButton": "paginate_button", + "sPageButtonActive": "paginate_active", + "sPageButtonStaticDisabled": "paginate_button", + "sPageFirst": "first", + "sPagePrevious": "previous", + "sPageNext": "next", + "sPageLast": "last", + + /* Stripping classes */ + "sStripOdd": "odd", + "sStripEven": "even", + + /* Empty row */ + "sRowEmpty": "dataTables_empty", + + /* Features */ + "sWrapper": "dataTables_wrapper", + "sFilter": "dataTables_filter", + "sInfo": "dataTables_info", + "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */ + "sLength": "dataTables_length", + "sProcessing": "dataTables_processing", + + /* Sorting */ + "sSortAsc": "sorting_asc", + "sSortDesc": "sorting_desc", + "sSortable": "sorting", /* Sortable in both directions */ + "sSortableAsc": "sorting_asc_disabled", + "sSortableDesc": "sorting_desc_disabled", + "sSortableNone": "sorting_disabled", + "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ + "sSortJUIAsc": "", + "sSortJUIDesc": "", + "sSortJUI": "", + "sSortJUIAscAllowed": "", + "sSortJUIDescAllowed": "", + "sSortJUIWrapper": "", + + /* Scrolling */ + "sScrollWrapper": "dataTables_scroll", + "sScrollHead": "dataTables_scrollHead", + "sScrollHeadInner": "dataTables_scrollHeadInner", + "sScrollBody": "dataTables_scrollBody", + "sScrollFoot": "dataTables_scrollFoot", + "sScrollFootInner": "dataTables_scrollFootInner", + + /* Misc */ + "sFooterTH": "" + }; + + /* + * Variable: oJUIClasses + * Purpose: Storage for the various classes that DataTables uses - jQuery UI suitable + * Scope: jQuery.fn.dataTableExt + */ + _oExt.oJUIClasses = { + /* Two buttons buttons */ + "sPagePrevEnabled": "fg-button ui-button ui-state-default ui-corner-left", + "sPagePrevDisabled": "fg-button ui-button ui-state-default ui-corner-left ui-state-disabled", + "sPageNextEnabled": "fg-button ui-button ui-state-default ui-corner-right", + "sPageNextDisabled": "fg-button ui-button ui-state-default ui-corner-right ui-state-disabled", + "sPageJUINext": "ui-icon ui-icon-circle-arrow-e", + "sPageJUIPrev": "ui-icon ui-icon-circle-arrow-w", + + /* Full numbers paging buttons */ + "sPageButton": "fg-button ui-button ui-state-default", + "sPageButtonActive": "fg-button ui-button ui-state-default ui-state-disabled", + "sPageButtonStaticDisabled": "fg-button ui-button ui-state-default ui-state-disabled", + "sPageFirst": "first ui-corner-tl ui-corner-bl", + "sPagePrevious": "previous", + "sPageNext": "next", + "sPageLast": "last ui-corner-tr ui-corner-br", + + /* Stripping classes */ + "sStripOdd": "odd", + "sStripEven": "even", + + /* Empty row */ + "sRowEmpty": "dataTables_empty", + + /* Features */ + "sWrapper": "dataTables_wrapper", + "sFilter": "dataTables_filter", + "sInfo": "dataTables_info", + "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+ + "ui-buttonset-multi paging_", /* Note that the type is postfixed */ + "sLength": "dataTables_length", + "sProcessing": "dataTables_processing", + + /* Sorting */ + "sSortAsc": "ui-state-default", + "sSortDesc": "ui-state-default", + "sSortable": "ui-state-default", + "sSortableAsc": "ui-state-default", + "sSortableDesc": "ui-state-default", + "sSortableNone": "ui-state-default", + "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ + "sSortJUIAsc": "css_right ui-icon ui-icon-triangle-1-n", + "sSortJUIDesc": "css_right ui-icon ui-icon-triangle-1-s", + "sSortJUI": "css_right ui-icon ui-icon-carat-2-n-s", + "sSortJUIAscAllowed": "css_right ui-icon ui-icon-carat-1-n", + "sSortJUIDescAllowed": "css_right ui-icon ui-icon-carat-1-s", + "sSortJUIWrapper": "DataTables_sort_wrapper", + + /* Scrolling */ + "sScrollWrapper": "dataTables_scroll", + "sScrollHead": "dataTables_scrollHead ui-state-default", + "sScrollHeadInner": "dataTables_scrollHeadInner", + "sScrollBody": "dataTables_scrollBody", + "sScrollFoot": "dataTables_scrollFoot ui-state-default", + "sScrollFootInner": "dataTables_scrollFootInner", + + /* Misc */ + "sFooterTH": "ui-state-default" + }; + + /* + * Variable: oPagination + * Purpose: Container for the various type of pagination that dataTables supports + * Scope: jQuery.fn.dataTableExt + */ + _oExt.oPagination = { + /* + * Variable: two_button + * Purpose: Standard two button (forward/back) pagination + * Scope: jQuery.fn.dataTableExt.oPagination + */ + "two_button": { + /* + * Function: oPagination.two_button.fnInit + * Purpose: Initalise dom elements required for pagination with forward/back buttons only + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * node:nPaging - the DIV which contains this pagination control + * function:fnCallbackDraw - draw function which must be called on update + */ + "fnInit": function ( oSettings, nPaging, fnCallbackDraw ) + { + var nPrevious, nNext, nPreviousInner, nNextInner; + + /* Store the next and previous elements in the oSettings object as they can be very + * usful for automation - particularly testing + */ + if ( !oSettings.bJUI ) + { + nPrevious = document.createElement( 'div' ); + nNext = document.createElement( 'div' ); + } + else + { + nPrevious = document.createElement( 'a' ); + nNext = document.createElement( 'a' ); + + nNextInner = document.createElement('span'); + nNextInner.className = oSettings.oClasses.sPageJUINext; + nNext.appendChild( nNextInner ); + + nPreviousInner = document.createElement('span'); + nPreviousInner.className = oSettings.oClasses.sPageJUIPrev; + nPrevious.appendChild( nPreviousInner ); + } + + nPrevious.className = oSettings.oClasses.sPagePrevDisabled; + nNext.className = oSettings.oClasses.sPageNextDisabled; + + nPrevious.title = oSettings.oLanguage.oPaginate.sPrevious; + nNext.title = oSettings.oLanguage.oPaginate.sNext; + + nPaging.appendChild( nPrevious ); + nPaging.appendChild( nNext ); + + $(nPrevious).bind( 'click.DT', function() { + if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) ) + { + /* Only draw when the page has actually changed */ + fnCallbackDraw( oSettings ); + } + } ); + + $(nNext).bind( 'click.DT', function() { + if ( oSettings.oApi._fnPageChange( oSettings, "next" ) ) + { + fnCallbackDraw( oSettings ); + } + } ); + + /* Take the brutal approach to cancelling text selection */ + $(nPrevious).bind( 'selectstart.DT', function () { return false; } ); + $(nNext).bind( 'selectstart.DT', function () { return false; } ); + + /* ID the first elements only */ + if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" ) + { + nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' ); + nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' ); + nNext.setAttribute( 'id', oSettings.sTableId+'_next' ); + } + }, + + /* + * Function: oPagination.two_button.fnUpdate + * Purpose: Update the two button pagination at the end of the draw + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * function:fnCallbackDraw - draw function to call on page change + */ + "fnUpdate": function ( oSettings, fnCallbackDraw ) + { + if ( !oSettings.aanFeatures.p ) + { + return; + } + + /* Loop over each instance of the pager */ + var an = oSettings.aanFeatures.p; + for ( var i=0, iLen=an.length ; i= (iPages - iPageCountHalf)) + { + iStartButton = iPages - iPageCount + 1; + iEndButton = iPages; + } + else + { + iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1; + iEndButton = iStartButton + iPageCount - 1; + } + } + } + + /* Build the dynamic list */ + for ( i=iStartButton ; i<=iEndButton ; i++ ) + { + if ( iCurrentPage != i ) + { + sList += ''+i+''; + } + else + { + sList += ''+i+''; + } + } + + /* Loop over each instance of the pager */ + var an = oSettings.aanFeatures.p; + var anButtons, anStatic, nPaginateList; + var fnClick = function() { + /* Use the information in the element to jump to the required page */ + var iTarget = (this.innerHTML * 1) - 1; + oSettings._iDisplayStart = iTarget * oSettings._iDisplayLength; + fnCallbackDraw( oSettings ); + return false; + }; + var fnFalse = function () { return false; }; + + for ( i=0, iLen=an.length ; i y) ? 1 : 0)); + }, + + "string-desc": function ( a, b ) + { + var x = a.toLowerCase(); + var y = b.toLowerCase(); + return ((x < y) ? 1 : ((x > y) ? -1 : 0)); + }, + + + /* + * html sorting (ignore html tags) + */ + "html-asc": function ( a, b ) + { + var x = a.replace( /<.*?>/g, "" ).toLowerCase(); + var y = b.replace( /<.*?>/g, "" ).toLowerCase(); + return ((x < y) ? -1 : ((x > y) ? 1 : 0)); + }, + + "html-desc": function ( a, b ) + { + var x = a.replace( /<.*?>/g, "" ).toLowerCase(); + var y = b.replace( /<.*?>/g, "" ).toLowerCase(); + return ((x < y) ? 1 : ((x > y) ? -1 : 0)); + }, + + + /* + * date sorting + */ + "date-asc": function ( a, b ) + { + var x = Date.parse( a ); + var y = Date.parse( b ); + + if ( isNaN(x) || x==="" ) + { + x = Date.parse( "01/01/1970 00:00:00" ); + } + if ( isNaN(y) || y==="" ) + { + y = Date.parse( "01/01/1970 00:00:00" ); + } + + return x - y; + }, + + "date-desc": function ( a, b ) + { + var x = Date.parse( a ); + var y = Date.parse( b ); + + if ( isNaN(x) || x==="" ) + { + x = Date.parse( "01/01/1970 00:00:00" ); + } + if ( isNaN(y) || y==="" ) + { + y = Date.parse( "01/01/1970 00:00:00" ); + } + + return y - x; + }, + + + /* + * numerical sorting + */ + "numeric-asc": function ( a, b ) + { + var x = (a=="-" || a==="") ? 0 : a*1; + var y = (b=="-" || b==="") ? 0 : b*1; + return x - y; + }, + + "numeric-desc": function ( a, b ) + { + var x = (a=="-" || a==="") ? 0 : a*1; + var y = (b=="-" || b==="") ? 0 : b*1; + return y - x; + } + }; + + + /* + * Variable: aTypes + * Purpose: Container for the various type of type detection that dataTables supports + * Scope: jQuery.fn.dataTableExt + * Notes: The functions in this array are expected to parse a string to see if it is a data + * type that it recognises. If so then the function should return the name of the type (a + * corresponding sort function should be defined!), if the type is not recognised then the + * function should return null such that the parser and move on to check the next type. + * Note that ordering is important in this array - the functions are processed linearly, + * starting at index 0. + * Note that the input for these functions is always a string! It cannot be any other data + * type + */ + _oExt.aTypes = [ + /* + * Function: - + * Purpose: Check to see if a string is numeric + * Returns: string:'numeric' or null + * Inputs: string:sText - string to check + */ + function ( sData ) + { + /* Allow zero length strings as a number */ + if ( sData.length === 0 ) + { + return 'numeric'; + } + + var sValidFirstChars = "0123456789-"; + var sValidChars = "0123456789."; + var Char; + var bDecimal = false; + + /* Check for a valid first char (no period and allow negatives) */ + Char = sData.charAt(0); + if (sValidFirstChars.indexOf(Char) == -1) + { + return null; + } + + /* Check all the other characters are valid */ + for ( var i=1 ; i') != -1 ) + { + return 'html'; + } + return null; + } + ]; + + /* + * Function: fnVersionCheck + * Purpose: Check a version string against this version of DataTables. Useful for plug-ins + * Returns: bool:true -this version of DataTables is greater or equal to the required version + * false -this version of DataTales is not suitable + * Inputs: string:sVersion - the version to check against. May be in the following formats: + * "a", "a.b" or "a.b.c" + * Notes: This function will only check the first three parts of a version string. It is + * assumed that beta and dev versions will meet the requirements. This might change in future + */ + _oExt.fnVersionCheck = function( sVersion ) + { + /* This is cheap, but very effective */ + var fnZPad = function (Zpad, count) + { + while(Zpad.length < count) { + Zpad += '0'; + } + return Zpad; + }; + var aThis = _oExt.sVersion.split('.'); + var aThat = sVersion.split('.'); + var sThis = '', sThat = ''; + + for ( var i=0, iLen=aThat.length ; i= parseInt(sThat, 10); + }; + + /* + * Variable: _oExternConfig + * Purpose: Store information for DataTables to access globally about other instances + * Scope: jQuery.fn.dataTableExt + */ + _oExt._oExternConfig = { + /* int:iNextUnique - next unique number for an instance */ + "iNextUnique": 0 + }; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Section - DataTables prototype + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /* + * Function: dataTable + * Purpose: DataTables information + * Returns: - + * Inputs: object:oInit - initalisation options for the table + */ + $.fn.dataTable = function( oInit ) + { + /* + * Function: classSettings + * Purpose: Settings container function for all 'class' properties which are required + * by dataTables + * Returns: - + * Inputs: - + */ + function classSettings () + { + this.fnRecordsTotal = function () + { + if ( this.oFeatures.bServerSide ) { + return parseInt(this._iRecordsTotal, 10); + } else { + return this.aiDisplayMaster.length; + } + }; + + this.fnRecordsDisplay = function () + { + if ( this.oFeatures.bServerSide ) { + return parseInt(this._iRecordsDisplay, 10); + } else { + return this.aiDisplay.length; + } + }; + + this.fnDisplayEnd = function () + { + if ( this.oFeatures.bServerSide ) { + if ( this.oFeatures.bPaginate === false || this._iDisplayLength == -1 ) { + return this._iDisplayStart+this.aiDisplay.length; + } else { + return Math.min( this._iDisplayStart+this._iDisplayLength, + this._iRecordsDisplay ); + } + } else { + return this._iDisplayEnd; + } + }; + + /* + * Variable: oInstance + * Purpose: The DataTables object for this table + * Scope: jQuery.dataTable.classSettings + */ + this.oInstance = null; + + /* + * Variable: sInstance + * Purpose: Unique idendifier for each instance of the DataTables object + * Scope: jQuery.dataTable.classSettings + */ + this.sInstance = null; + + /* + * Variable: oFeatures + * Purpose: Indicate the enablement of key dataTable features + * Scope: jQuery.dataTable.classSettings + */ + this.oFeatures = { + "bPaginate": true, + "bLengthChange": true, + "bFilter": true, + "bSort": true, + "bInfo": true, + "bAutoWidth": true, + "bProcessing": false, + "bSortClasses": true, + "bStateSave": false, + "bServerSide": false + }; + + /* + * Variable: oScroll + * Purpose: Container for scrolling options + * Scope: jQuery.dataTable.classSettings + */ + this.oScroll = { + "sX": "", + "sXInner": "", + "sY": "", + "bCollapse": false, + "bInfinite": false, + "iLoadGap": 100, + "iBarWidth": 0, + "bAutoCss": true + }; + + /* + * Variable: aanFeatures + * Purpose: Array referencing the nodes which are used for the features + * Scope: jQuery.dataTable.classSettings + * Notes: The parameters of this object match what is allowed by sDom - i.e. + * 'l' - Length changing + * 'f' - Filtering input + * 't' - The table! + * 'i' - Information + * 'p' - Pagination + * 'r' - pRocessing + */ + this.aanFeatures = []; + + /* + * Variable: oLanguage + * Purpose: Store the language strings used by dataTables + * Scope: jQuery.dataTable.classSettings + * Notes: The words in the format _VAR_ are variables which are dynamically replaced + * by javascript + */ + this.oLanguage = { + "sProcessing": "Processing...", + "sLengthMenu": "Show _MENU_ entries", + "sZeroRecords": "No matching records found", + "sEmptyTable": "No data available in table", + "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries", + "sInfoEmpty": "Showing 0 to 0 of 0 entries", + "sInfoFiltered": "(filtered from _MAX_ total entries)", + "sInfoPostFix": "", + "sSearch": "Search:", + "sUrl": "", + "oPaginate": { + "sFirst": "First", + "sPrevious": "Previous", + "sNext": "Next", + "sLast": "Last" + }, + "fnInfoCallback": null + }; + + /* + * Variable: aoData + * Purpose: Store data information + * Scope: jQuery.dataTable.classSettings + * Notes: This is an array of objects with the following parameters: + * int: _iId - internal id for tracking + * array: _aData - internal data - used for sorting / filtering etc + * node: nTr - display node + * array node: _anHidden - hidden TD nodes + * string: _sRowStripe + */ + this.aoData = []; + + /* + * Variable: aiDisplay + * Purpose: Array of indexes which are in the current display (after filtering etc) + * Scope: jQuery.dataTable.classSettings + */ + this.aiDisplay = []; + + /* + * Variable: aiDisplayMaster + * Purpose: Array of indexes for display - no filtering + * Scope: jQuery.dataTable.classSettings + */ + this.aiDisplayMaster = []; + + /* + * Variable: aoColumns + * Purpose: Store information about each column that is in use + * Scope: jQuery.dataTable.classSettings + */ + this.aoColumns = []; + + /* + * Variable: iNextId + * Purpose: Store the next unique id to be used for a new row + * Scope: jQuery.dataTable.classSettings + */ + this.iNextId = 0; + + /* + * Variable: asDataSearch + * Purpose: Search data array for regular expression searching + * Scope: jQuery.dataTable.classSettings + */ + this.asDataSearch = []; + + /* + * Variable: oPreviousSearch + * Purpose: Store the previous search incase we want to force a re-search + * or compare the old search to a new one + * Scope: jQuery.dataTable.classSettings + */ + this.oPreviousSearch = { + "sSearch": "", + "bRegex": false, + "bSmart": true + }; + + /* + * Variable: aoPreSearchCols + * Purpose: Store the previous search for each column + * Scope: jQuery.dataTable.classSettings + */ + this.aoPreSearchCols = []; + + /* + * Variable: aaSorting + * Purpose: Sorting information + * Scope: jQuery.dataTable.classSettings + * Notes: Index 0 - column number + * Index 1 - current sorting direction + * Index 2 - index of asSorting for this column + */ + this.aaSorting = [ [0, 'asc', 0] ]; + + /* + * Variable: aaSortingFixed + * Purpose: Sorting information that is always applied + * Scope: jQuery.dataTable.classSettings + */ + this.aaSortingFixed = null; + + /* + * Variable: asStripClasses + * Purpose: Classes to use for the striping of a table + * Scope: jQuery.dataTable.classSettings + */ + this.asStripClasses = []; + + /* + * Variable: asDestoryStrips + * Purpose: If restoring a table - we should restore it's striping classes as well + * Scope: jQuery.dataTable.classSettings + */ + this.asDestoryStrips = []; + + /* + * Variable: sDestroyWidth + * Purpose: If restoring a table - we should restore it's width + * Scope: jQuery.dataTable.classSettings + */ + this.sDestroyWidth = 0; + + /* + * Variable: fnRowCallback + * Purpose: Call this function every time a row is inserted (draw) + * Scope: jQuery.dataTable.classSettings + */ + this.fnRowCallback = null; + + /* + * Variable: fnHeaderCallback + * Purpose: Callback function for the header on each draw + * Scope: jQuery.dataTable.classSettings + */ + this.fnHeaderCallback = null; + + /* + * Variable: fnFooterCallback + * Purpose: Callback function for the footer on each draw + * Scope: jQuery.dataTable.classSettings + */ + this.fnFooterCallback = null; + + /* + * Variable: aoDrawCallback + * Purpose: Array of callback functions for draw callback functions + * Scope: jQuery.dataTable.classSettings + * Notes: Each array element is an object with the following parameters: + * function:fn - function to call + * string:sName - name callback (feature). useful for arranging array + */ + this.aoDrawCallback = []; + + /* + * Variable: fnInitComplete + * Purpose: Callback function for when the table has been initalised + * Scope: jQuery.dataTable.classSettings + */ + this.fnInitComplete = null; + + /* + * Variable: sTableId + * Purpose: Cache the table ID for quick access + * Scope: jQuery.dataTable.classSettings + */ + this.sTableId = ""; + + /* + * Variable: nTable + * Purpose: Cache the table node for quick access + * Scope: jQuery.dataTable.classSettings + */ + this.nTable = null; + + /* + * Variable: nTHead + * Purpose: Permanent ref to the thead element + * Scope: jQuery.dataTable.classSettings + */ + this.nTHead = null; + + /* + * Variable: nTFoot + * Purpose: Permanent ref to the tfoot element - if it exists + * Scope: jQuery.dataTable.classSettings + */ + this.nTFoot = null; + + /* + * Variable: nTBody + * Purpose: Permanent ref to the tbody element + * Scope: jQuery.dataTable.classSettings + */ + this.nTBody = null; + + /* + * Variable: nTableWrapper + * Purpose: Cache the wrapper node (contains all DataTables controlled elements) + * Scope: jQuery.dataTable.classSettings + */ + this.nTableWrapper = null; + + /* + * Variable: bInitialised + * Purpose: Indicate if all required information has been read in + * Scope: jQuery.dataTable.classSettings + */ + this.bInitialised = false; + + /* + * Variable: aoOpenRows + * Purpose: Information about open rows + * Scope: jQuery.dataTable.classSettings + * Notes: Has the parameters 'nTr' and 'nParent' + */ + this.aoOpenRows = []; + + /* + * Variable: sDom + * Purpose: Dictate the positioning that the created elements will take + * Scope: jQuery.dataTable.classSettings + * Notes: + * The following options are allowed: + * 'l' - Length changing + * 'f' - Filtering input + * 't' - The table! + * 'i' - Information + * 'p' - Pagination + * 'r' - pRocessing + * The following constants are allowed: + * 'H' - jQueryUI theme "header" classes + * 'F' - jQueryUI theme "footer" classes + * The following syntax is expected: + * '<' and '>' - div elements + * '<"class" and '>' - div with a class + * Examples: + * '<"wrapper"flipt>', 'ip>' + */ + this.sDom = 'lfrtip'; + + /* + * Variable: sPaginationType + * Purpose: Note which type of sorting should be used + * Scope: jQuery.dataTable.classSettings + */ + this.sPaginationType = "two_button"; + + /* + * Variable: iCookieDuration + * Purpose: The cookie duration (for bStateSave) in seconds - default 2 hours + * Scope: jQuery.dataTable.classSettings + */ + this.iCookieDuration = 60 * 60 * 2; + + /* + * Variable: sCookiePrefix + * Purpose: The cookie name prefix + * Scope: jQuery.dataTable.classSettings + */ + this.sCookiePrefix = "SpryMedia_DataTables_"; + + /* + * Variable: fnCookieCallback + * Purpose: Callback function for cookie creation + * Scope: jQuery.dataTable.classSettings + */ + this.fnCookieCallback = null; + + /* + * Variable: aoStateSave + * Purpose: Array of callback functions for state saving + * Scope: jQuery.dataTable.classSettings + * Notes: Each array element is an object with the following parameters: + * function:fn - function to call. Takes two parameters, oSettings and the JSON string to + * save that has been thus far created. Returns a JSON string to be inserted into a + * json object (i.e. '"param": [ 0, 1, 2]') + * string:sName - name of callback + */ + this.aoStateSave = []; + + /* + * Variable: aoStateLoad + * Purpose: Array of callback functions for state loading + * Scope: jQuery.dataTable.classSettings + * Notes: Each array element is an object with the following parameters: + * function:fn - function to call. Takes two parameters, oSettings and the object stored. + * May return false to cancel state loading. + * string:sName - name of callback + */ + this.aoStateLoad = []; + + /* + * Variable: oLoadedState + * Purpose: State that was loaded from the cookie. Useful for back reference + * Scope: jQuery.dataTable.classSettings + */ + this.oLoadedState = null; + + /* + * Variable: sAjaxSource + * Purpose: Source url for AJAX data for the table + * Scope: jQuery.dataTable.classSettings + */ + this.sAjaxSource = null; + + /* + * Variable: bAjaxDataGet + * Purpose: Note if draw should be blocked while getting data + * Scope: jQuery.dataTable.classSettings + */ + this.bAjaxDataGet = true; + + /* + * Variable: fnServerData + * Purpose: Function to get the server-side data - can be overruled by the developer + * Scope: jQuery.dataTable.classSettings + */ + this.fnServerData = function ( url, data, callback ) { + $.ajax( { + "url": url, + "data": data, + "success": callback, + "dataType": "json", + "cache": false, + "error": function (xhr, error, thrown) { + if ( error == "parsererror" ) { + alert( "DataTables warning: JSON data from server could not be parsed. "+ + "This is caused by a JSON formatting error." ); + } + } + } ); + }; + + /* + * Variable: fnFormatNumber + * Purpose: Format numbers for display + * Scope: jQuery.dataTable.classSettings + */ + this.fnFormatNumber = function ( iIn ) + { + if ( iIn < 1000 ) + { + /* A small optimisation for what is likely to be the vast majority of use cases */ + return iIn; + } + else + { + var s=(iIn+""), a=s.split(""), out="", iLen=s.length; + + for ( var i=0 ; i