Mercurial > hg > audiodb
changeset 640:901803e1305f
First instance of audioDB browser code.
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/browser/export/cat2rdf.py Thu Oct 08 11:19:11 2009 +0000 @@ -0,0 +1,152 @@ +#!/usr/bin/python + +import sys +import psycopg2 +import psycopg2.extras + +from rdflib.Graph import ConjunctiveGraph as Graph +from rdflib import Namespace, Literal, URIRef, BNode, RDF + +catalogue = sys.argv[1] + +foaf = Namespace("http://xmlns.com/foaf/0.1/") +mo = Namespace("http://purl.org/ontology/mo/") +mb_artist = Namespace("http://dbtune.org/musicbrainz/resource/artist/") +dc = Namespace("http://purl.org/dc/elements/1.1/") +default_graph_uri = "http://omras2.gold.ac.uk/catalogue/"+catalogue.lower() + +username = "USERNAME" +host = "HOST" +database = "DATABASE" + +counters = {} +namespaces = {} + +def loadCatalogue(catalogue): + try: + conn = psycopg2.connect("dbname='"+database+"' user='"+username+"' host='"+host+"'"); + except: + print "Unable to connect to the database" + + cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + + cursor.execute("""SELECT * from media WHERE catalogue LIKE '"""+catalogue+"""'""") + + rows = cursor.fetchall() + + return rows + +def createGraph(rows): + albums = {} + + artists = { + 'Madonna': mb_artist['79239441-bfd5-4981-a70c-55c3f15c1287'], + 'John Coltrane': mb_artist['b625448e-bf4a-41c3-a421-72ad46cdb831'], + 'Miles Davis' : mb_artist['561d854a-6a28-4aa7-8c99-323e6ce46c2a']} + + counter = 1 + for row in rows: + graph = Graph(identifier = URIRef(default_graph_uri)) + + # Create all the relevant nodes (with the correct IDs) + + work = getNewNode('work') + composition = getNewNode('composition') + track = getNewNode('track') + signal = getNewNode('signal') + record = getNewNode('record') + performance = getNewNode('performance') + + # If we don't have an artist url, make a foaf Agent instead. + if row['artist']: + try: + artist = artists[row['artist']] + except KeyError: + artist = getNewNode('artist') + graph.add((artist, RDF.type, foaf['Agent'])) + graph.add((artist, foaf['name'], Literal(row['artist'].strip()))) + artists[row['artist']] = artist; + + if row['composer']: + try: + composer = artists[row['composer']] + except KeyError: + composer = getNewNode('artist') + graph.add((composer, RDF.type, foaf['Agent'])) + graph.add((composer, foaf['name'], Literal(row['composer'].strip()))) + artists[row['composer']] = composer; + else: + composer = artist + + + # Work + graph.add((work, RDF.type, mo['MusicalWork'])) + + # Composition + graph.add((composition, RDF.type, mo['Composition'])) + if composer: + graph.add((composition, mo['composer'], composer)) + graph.add((composition, mo['produced_work'], work)) + + # Track + graph.add((track, RDF.type, mo['Track'])) + if row['artist']: + graph.add((track, foaf['maker'], artist)) + if row['tracknum']: + graph.add((track, mo['track_number'], Literal(row['tracknum']))) + + # Album + try: + album = albums[row['album']] + except KeyError: + album = getNewNode('album') + graph.add((album, RDF.type, mo['Record'])) + graph.add((album, dc['title'], Literal(row['album'].strip()))) + graph.add((album, mo['release_type'], mo['album'])) + albums[row['album']] = album + graph.add((album, mo['track'], track)) + + # Signal + graph.add((signal, RDF.type, mo['Signal'])) + graph.add((signal, mo['published_as'], record)) + + if row['track']: + graph.add((signal, dc['title'], Literal(row['track'].strip()))) + if row['isrc']: + graph.add((signal, mo['isrc'], Literal(row['isrc'].strip()))) + + # Record + graph.add((record, RDF.type, mo['Record'])) + graph.add((record, mo['publication_of'], signal)) + graph.add((record, mo['track'], track)) + + # Performance + graph.add((performance, RDF.type, mo['Performance'])) + graph.add((performance, mo['performance_of'], work)) + if row['artist']: + graph.add((performance, mo['performer'], artist)) + graph.add((performance, mo['recorded_as'], signal)) + + #graph.close() + + graph.serialize(format='xml',destination="output/"+catalogue.lower()+"_"+str(counter)+".rdf") + counter += 1 + +def getNewNode(type): + try: + count = counters[type] + except KeyError: + counters[type] = 1 + count = counters[type] + + try: + namespace = namespaces[type] + except KeyError: + namespaces[type] = Namespace(default_graph_uri+"/"+type+"/") + namespace = namespaces[type] + + node = namespace[str(count)] + counters[type] += 1 + return node + +createGraph(loadCatalogue(catalogue))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/browser/ontology/audiodb.owl Thu Oct 08 11:19:11 2009 +0000 @@ -0,0 +1,71 @@ +<?xml version="1.0"?> + +<!DOCTYPE owl [ + <!ENTITY xsd "http://www.w3.org/2001/XMLSchema#"> + <!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#"> + <!ENTITY owl "http://www.w3.org/2002/07/owl#"> + <!ENTITY base "http://omras2.gold.ac.uk/ontology/audiodb#"> + <!ENTITY dc "http://purl.org/dc/elements/1.1/"> + <!ENTITY dct "http://purl.org/dc/terms/"> +]> + + + +<rdf:RDF xmlns:rdf="&rdf;" + xmlns:rdfs="&rdfs;" + xmlns:owl="&owl;" + xmlns:xsd="&xsd;" + xmlns:dat="&dat;" + xml:base="&base;" + xmlns:dc="&dc;" + xmlns:dct="&dct;"> + + + <owl:Ontology rdf:about="&base;"> + <rdfs:label>AudioDB Ontology</rdfs:label> + <dc:title xml:lang="en">AudioDB Ontology</dc:title> + <dc:description xml:lang="en">Describes the contents of an AudioDB instance</dc:description> + <dc:creator>Michael O. Jewell (mailto:mas01mj@gold.ac.uk)</dc:creator> + <dct:created>2009-10-07</dct:created> + <owl:versionInfo>0.1</owl:versionInfo> + </owl:Ontology> + + <owl:Class rdf:ID="Database"> + <rdfs:label>AudioDB</rdfs:label> + <rdfs:comment>Represents a collection of extracted features and information about their extraction.</rdfs:comment> + <rdfs:subClassOf rdf:resource="foaf:Document" /> + </owl:Class> + + <owl:Class rdf:ID="Feature"> + <rdfs:label>Feature</rdfs:label> + <rdfs:comment>Information about an audio feature</rdfs:comment> + </owl:Class> + + <owl:ObjectProperty rdf:ID="window-type"> + <rdfs:label>Window Type</rdfs:label> + <rdfs:comment>This property indicates that there is some no link between the named Expressions</rdfs:comment> + <owl:inverseOf rdf:resource="#is-not-linked-to"/> + <rdfs:domain rdf:resource="#Expression"/> + <rdfs:range rdf:resource="#Expression"/> + </owl:ObjectProperty> + + <owl:Class rdf:ID="ChromogramFeature"> + <rdfs:label>Chromogram Feature</rdfs:label> + <rdfs:comment>Information about an audio feature</rdfs:comment> + <rdfs:subClassOf rdf:resource="audiodb:Feature" /> + </owl:Class> + + <owl:Class rdf:ID="MFCCFeature"> + <rdfs:label>MFCC Feature</rdfs:label> + <rdfs:comment>Information about an audio feature</rdfs:comment> + <rdfs:subClassOf rdf:resource="audiodb:Feature" /> + </owl:Class> + + <owl:Class rdf:ID="CQTFeature"> + <rdfs:label>CQT Feature</rdfs:label> + <rdfs:comment>Information about an audio feature</rdfs:comment> + <rdfs:subClassOf rdf:resource="audiodb:Feature" /> + </owl:Class> + +</rdf:RDF>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/browser/web/css/data_table.css Thu Oct 08 11:19:11 2009 +0000 @@ -0,0 +1,447 @@ +/* + * File: demo_table.css + * CVS: $Id$ + * Description: CSS descriptions for DataTables demo pages + * Author: Allan Jardine + * Created: Tue May 12 06:47:22 BST 2009 + * Modified: $Date$ by $Author$ + * Language: CSS + * Project: DataTables + * + * Copyright 2009 Allan Jardine. All Rights Reserved. + * + * *************************************************************************** + * DESCRIPTION + * + * The styles given here are suitable for the demos that are used with the standard DataTables + * distribution (see www.datatables.net). You will most likely wish to modify these styles to + * meet the layout requirements of your site. + * + * Common issues: + * 'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is + * no conflict between the two pagination types. If you want to use full_numbers pagination + * ensure that you either have "example_alt_pagination" as a body class name, or better yet, + * modify that selector. + * Note that the path used for Images is relative. All images are by default located in + * ../images/ - relative to this CSS file. + */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * DataTables features + */ + +.dataTables_wrapper { + position: relative; + min-height: 302px; + _height: 302px; + clear: both; +} + +.dataTables_processing { + position: absolute; + top: 0px; + left: 50%; + width: 250px; + margin-left: -125px; + border: 1px solid #ddd; + text-align: center; + color: #999; + font-size: 11px; + padding: 2px 0; +} + +.dataTables_length { + width: 40%; + float: left; +} + +.dataTables_filter { + width: 50%; + float: right; + text-align: right; +} + +.dataTables_info { + width: 60%; + float: left; +} + +.dataTables_paginate { + width: 44px; + * width: 50px; + float: right; + text-align: right; +} + +/* Pagination nested */ +.paginate_disabled_previous, .paginate_enabled_previous, .paginate_disabled_next, .paginate_enabled_next { + height: 19px; + width: 19px; + margin-left: 3px; + float: left; +} + +.paginate_disabled_previous { + background-image: url('../img/back_disabled.jpg'); +} + +.paginate_enabled_previous { + background-image: url('../img/back_enabled.jpg'); +} + +.paginate_disabled_next { + background-image: url('../img/forward_disabled.jpg'); +} + +.paginate_enabled_next { + background-image: url('../img/forward_enabled.jpg'); +} + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * DataTables display + */ +table.display { + margin: 0 auto; + width: 100%; + clear: both; +} + +table.display thead th { + padding: 3px 18px 3px 10px; + border-bottom: 1px solid black; + font-weight: bold; + cursor: pointer; + * cursor: hand; +} + +table.display tfoot th { + padding: 3px 10px; + border-top: 1px solid black; + font-weight: bold; +} + +table.display tr.heading2 td { + border-bottom: 1px solid #aaa; +} + +table.display td { + padding: 3px 10px; +} + +table.display td.center { + text-align: center; +} + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * DataTables sorting + */ + +.sorting_asc { + background: url('../img/sort_asc.jpg') no-repeat center right; +} + +.sorting_desc { + background: url('../img/sort_desc.jpg') no-repeat center right; +} + +.sorting { + background: url('../img/sort_both.jpg') no-repeat center right; +} + + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * DataTables row classes + */ +table.display tr.odd.gradeA { + background-color: #ddffdd; +} + +table.display tr.even.gradeA { + background-color: #eeffee; +} + + + + +table.display tr.odd.gradeA { + background-color: #ddffdd; +} + +table.display tr.even.gradeA { + background-color: #eeffee; +} + +table.display tr.odd.gradeC { + background-color: #ddddff; +} + +table.display tr.even.gradeC { + background-color: #eeeeff; +} + +table.display tr.odd.gradeX { + background-color: #ffdddd; +} + +table.display tr.even.gradeX { + background-color: #ffeeee; +} + +table.display tr.odd.gradeU { + background-color: #ddd; +} + +table.display tr.even.gradeU { + background-color: #eee; +} + + +tr.odd { + background-color: #E2E4FF; +} + +tr.even { + background-color: white; +} + + + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Misc + */ +.top, .bottom { + padding: 15px; + background-color: #F5F5F5; + border: 1px solid #CCCCCC; +} + +.top .dataTables_info { + float: none; +} + +.clear { + clear: both; +} + +.dataTables_empty { + text-align: center; +} + +tfoot input { + margin: 0.5em 0; + width: 100%; + color: #444; +} + +tfoot input.search_init { + color: #999; +} + +td.group { + background-color: #d1cfd0; + border-bottom: 2px solid #A19B9E; + border-top: 2px solid #A19B9E; +} + +td.details { + background-color: #d1cfd0; + border: 2px solid #A19B9E; +} + + +.example_alt_pagination div.dataTables_info { + width: 40%; +} + +.paging_full_numbers { + width: 400px; + height: 22px; + line-height: 22px; +} + +.paging_full_numbers span.paginate_button, + .paging_full_numbers span.paginate_active { + border: 1px solid #aaa; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + padding: 2px 5px; + margin: 0 3px; + cursor: pointer; + *cursor: hand; +} + +.paging_full_numbers span.paginate_button { + background-color: #ddd; +} + +.paging_full_numbers span.paginate_button:hover { + background-color: #ccc; +} + +.paging_full_numbers span.paginate_active { + background-color: #99B3FF; +} + +table.display tr.even.row_selected td { + background-color: #B0BED9; +} + +table.display tr.odd.row_selected td { + background-color: #9FAFD1; +} + + +/* + * Sorting classes for columns + */ +/* For the standard odd/even */ +tr.odd td.sorting_1 { + background-color: #D3D6FF; +} + +tr.odd td.sorting_2 { + background-color: #DADCFF; +} + +tr.odd td.sorting_3 { + background-color: #E0E2FF; +} + +tr.even td.sorting_1 { + background-color: #EAEBFF; +} + +tr.even td.sorting_2 { + background-color: #F2F3FF; +} + +tr.even td.sorting_3 { + background-color: #F9F9FF; +} + + +/* For the Conditional-CSS grading rows */ +/* + Colour calculations (based off the main row colours) + Level 1: + dd > 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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/browser/web/css/jOWL.css Thu Oct 08 11:19:11 2009 +0000 @@ -0,0 +1,47 @@ +/** +* jOWL CSS Generic Style Settings. +* Creator - David Decraene +* Version 1.0 +* http://jowl.ontologyonline.org +*/ + +/** bordered box */ +.ui-widget-content {border:1px solid steelblue;text-align:center;} +/** shaded titlebar */ +.ui-dialog-titlebar {margin:0px;padding:3px;text-align:center;background-color:#F8F8FF} + +.jowl-content {padding:5px;min-height:200px;} + +/** jowl-navbar */ +.jowl-navbar span {margin-right:5px;cursor:pointer;} +.jowl-navbar div {margin-bottom:5px;} +.jowl-navbar .external {font-weight:bold;color:red;font-style:italic;} + +/** jowl_autocomplete */ +.jowl_autocomplete_results{ + display:none;min-height:20px;text-align:left;width:250px;z-index:20;position:absolute; + border: 1px solid WindowFrame; background-color: Window; + list-style-position: outside; list-style: none; margin: 0px;padding-left:0px; + } +.jowl_autocomplete_results li {margin: 0px;cursor: pointer;display: block;font: menu;font-size: 12px;padding: 2px 5px;margin-bottom:2px;} +.jowl_autocomplete_results .type{font-style:italic;float:right} +.jowl_autocomplete_results .termlabel{font-size:8px} +.jowl_autocomplete_results .name{font-weight:bold} + +/*Treeview*/ +.jowl-tree {text-align: left} +.jowl-treeview{list-style-type: none;} +.jowl-treeview .root > .name{ font-weight:bold;} +.jowl-treeview .focus>.name{font-weight:bold;color:steelblue} +.jowl-treeview ul.tv, .jowl-treeview .tv ul { list-style-image:none; list-style-position:outside; list-style-type:none; margin:0pt; padding:0pt; } +.jowl-treeview .tv li { margin:0pt; padding:1px 0pt 3px 20px; position:relative; z-index:10; } +.jowl-treeview .tvie .name, .jowl-treeview .tvic .name, .jowl-treeview .tvilc .name, .jowl-treeview .tvile .name, .jowl-treeview .root .name { cursor:pointer; position:relative;} +.jowl-treeview .tv li, .jowl-treeview .tv .tvi { background:transparent url(../img/treeView/dotted/tvi.gif) no-repeat scroll top left;} +.jowl-treeview .tv .tvic { background-image:url(../img/treeView/dotted/tvic.gif) ;} +.jowl-treeview .tv .tvie { background-image:url(../img/treeView/dotted/tvie.gif); } +.jowl-treeview .tv .tvil { background-image:url(../img/treeView/dotted/tvil.gif); } +.jowl-treeview .tv .tvilc { background-image:url(../img/treeView/dotted/tvilc.gif); } +.jowl-treeview .tv .tvile { background-image:url(../img/treeView/dotted/tvile.gif); } +.jowl-treeview .tvload { background-image:url(../img/treeView/dotted/tviload.gif);} + +
Binary file examples/browser/web/css/jq/custom-theme/images/ui-bg_flat_0_aaaaaa_40x100.png has changed
Binary file examples/browser/web/css/jq/custom-theme/images/ui-bg_glass_55_fbf9ee_1x400.png has changed
Binary file examples/browser/web/css/jq/custom-theme/images/ui-bg_glass_65_ffffff_1x400.png has changed
Binary file examples/browser/web/css/jq/custom-theme/images/ui-bg_glass_75_dadada_1x400.png has changed
Binary file examples/browser/web/css/jq/custom-theme/images/ui-bg_glass_75_e6e6e6_1x400.png has changed
Binary file examples/browser/web/css/jq/custom-theme/images/ui-bg_glass_75_ffffff_1x400.png has changed
Binary file examples/browser/web/css/jq/custom-theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png has changed
Binary file examples/browser/web/css/jq/custom-theme/images/ui-bg_inset-soft_95_fef1ec_1x100.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/browser/web/css/jq/custom-theme/jquery-ui-1.7.custom.css Thu Oct 08 11:19:11 2009 +0000 @@ -0,0 +1,408 @@ +/* +* jQuery UI CSS Framework +* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +*/ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute; left: -99999999px; } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + +/* +* jQuery UI CSS Framework +* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ctl=themeroller +*/ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_75_ffffff_1x400.png) 50% 50% repeat-x; color: #222222; } +.ui-widget-content a { color: #222222; } +.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; } +.ui-widget-header a { color: #222222; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; outline: none; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; outline: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; outline: none; } +.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; outline: none; } +.ui-state-active, .ui-widget-content .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; outline: none; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; outline: none; text-decoration: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_inset-soft_95_fef1ec_1x100.png) 50% bottom repeat-x; color: #cd0a0a; } +.ui-state-error a, .ui-widget-content .ui-state-error a { color: #cd0a0a; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text { color: #cd0a0a; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; } +.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; } +.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; } +.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; } +.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; } +.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; } +.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; } +.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; } +.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; } + +/* Overlays */ +.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); } +.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; }/* Resizable +----------------------------------*/ +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;} +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Accordion +----------------------------------*/ +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } +.ui-accordion .ui-accordion-li-fix { display: inline; } +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } +.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; } +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; } +.ui-accordion .ui-accordion-content-active { display: block; }/* Dialog +----------------------------------*/ +.ui-dialog { position: relative; padding: .2em; width: 300px; } +.ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative; } +.ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; } +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } +.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } +.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; } +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } +.ui-draggable .ui-dialog-titlebar { cursor: move; } +/* Slider +----------------------------------*/ +.ui-slider { position: relative; text-align: left; } +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; } + +.ui-slider-horizontal { height: .8em; } +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: .8em; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs +----------------------------------*/ +.ui-tabs { padding: .2em; zoom: 1; } +.ui-tabs .ui-tabs-nav { list-style: none; position: relative; padding: .2em .2em 0; } +.ui-tabs .ui-tabs-nav li { position: relative; float: left; border-bottom-width: 0 !important; margin: 0 .2em -1px 0; padding: 0; } +.ui-tabs .ui-tabs-nav li a { float: left; text-decoration: none; padding: .5em 1em; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { padding-bottom: 1px; border-bottom-width: 0; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { padding: 1em 1.4em; display: block; border-width: 0; background: none; } +.ui-tabs .ui-tabs-hide { display: none !important; } +/* Datepicker +----------------------------------*/ +.ui-datepicker { width: 17em; padding: .2em .2em 0; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; } +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +}/* Progressbar +----------------------------------*/ +.ui-progressbar { height:2em; text-align: left; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } + +#search_options ul { display:table} +#search_options li { display:table-cell; padding: 5px}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/browser/web/index.html Thu Oct 08 11:19:11 2009 +0000 @@ -0,0 +1,56 @@ +<html> +<head> + +<link rel="stylesheet" href="http://ontologyonline.org/css/blueprint/screen.css" type="text/css" media="screen, projection"/> +<link rel="stylesheet" href="css/jOWL.css" type="text/css"/> +<link rel="stylesheet" href="css/data_table.css" type="text/css"/> +<link rel="stylesheet" href="css/jq/custom-theme/jquery-ui-1.7.custom.css" type="text/css"/> + +<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> +<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.0/jquery-ui.min.js"></script> +<script type="text/javascript" src="js/jquery.dataTables.js"></script> +<script type="text/javascript" src="js/sparql.js"></script> +<script type="text/javascript" src="js/jOWL.js"></script> +<script type="text/javascript" src="js/jOWL_UI.js"></script> +<script type="text/javascript" src="js/browser.js"></script> +</head> +<body> + +<div class="ui-widget"> + <div class="ui-widget-header">Search Options</div> + <div class="ui-widget-content"> + <div align="center"> + <ul id="search_options"> + <li>Artist: <input type="text" id="artistsearch" /></li> + <li>Track: <input type="text" id="tracksearch" /></li> + <li><input type="button" id="search" value="Search" /></li> + </ul> + </div> + <div align="center"><span id="spinner">Searching...</span></div> + </div> +</div> + +<div class="ui-widget"> +<div class="ui-widget-header">Search Results</div> +<div class="ui-widget-content"> +<div align="center"> +<table class="display" id="results" width="50%"> + <thead> + <tr> + <th>Artist Name</th> + <th>Track Name</th> + <th>Track #</th> + <th>Album Name</th> + </tr> + </thead> + <tbody> + </tbody> +</table> +</div> +</div> +</div> +<div style="clear:both" id="query">Current Query</div> + + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/browser/web/js/browser.js Thu Oct 08 11:19:11 2009 +0000 @@ -0,0 +1,130 @@ +var sparqler = new SPARQL.Service("http://harrison/sparql/"); + +sparqler.setPrefix("mo", "http://purl.org/ontology/mo/"); +sparqler.setPrefix("foaf", "http://xmlns.com/foaf/0.1/"); +sparqler.setPrefix("dc", "http://purl.org/dc/elements/1.1/"); + +sparqler.setRequestHeader("Accept", "application/json"); + +var resultsTable; + +$(document).ready(function() { + $("#search").click(search) + $("#spinner").hide(); + resultsTable = $('#results').dataTable({"bFilter":false,"bLengthChange":false,"bPaginate":true, + "fnRowCallback": function(nRow, aData, iDisplayIndex) + { + $(nRow).attr("typeof", "mo:Track"); + return nRow; + }, + "fnDrawCallback": function () + { + $(".artist_name").click(function(event) { searchArtist($(this).attr("href")); return false; }); + $(".album_name").click(function(event) { searchAlbum($(this).attr("href")); return false; }); + } + }); + + $("#results tbody").click(function(event) { + $(resultsTable.fnSettings().aoData).each(function (){ + $(this.nTr).removeClass('row_selected'); + }); + $(event.target.parentNode).addClass('row_selected'); + }); + +}); + +function search(event) { + + var trackSearchString = $("#tracksearch").val(); + var artistSearchString = $("#artistsearch").val(); + + if(trackSearchString.length == 0 && artistSearchString.length == 0) + { + resultsTable.fnClearTable(); + return; + } + + var queryString = "SELECT ?maker ?album ?album_title ?tracknum ?artist_name ?track_title WHERE {"; + + queryString += " ?track a mo:Track; mo:track_number ?tracknum; foaf:maker ?maker. ?album mo:track ?track; dc:title ?album_title. ?maker foaf:name ?artist_name"; + + if(artistSearchString.length > 0) + { + queryString += ' FILTER regex(?artist_name, "'+artistSearchString+'", "i")'; + } + else + { + queryString += "."; + } + + queryString += " ?record mo:track ?track; mo:publication_of ?signal. ?signal dc:title ?track_title"; + + if(trackSearchString.length > 0) + { + queryString += ' FILTER regex(?track_title, "'+trackSearchString+'", "i")'; + } + else + { + queryString += "."; + } + + queryString += " }"; + + performSearch(queryString); +} + +function searchArtist(id) { + var queryString = "SELECT ?maker ?album ?album_title ?tracknum ?artist_name ?track_title WHERE {"; + queryString += " ?track a mo:Track; mo:track_number ?tracknum; foaf:maker ?maker. ?album mo:track ?track; dc:title ?album_title. ?maker foaf:name ?artist_name"; + queryString += " ?record mo:track ?track; mo:publication_of ?signal. ?signal dc:title ?track_title."; + queryString += " FILTER(sameTerm(?maker, <"+id+">))"; + queryString += " }"; + performSearch(queryString); +} + +function searchAlbum(id) { + var queryString = "SELECT ?maker ?album ?album_title ?tracknum ?artist_name ?track_title WHERE {"; + queryString += " ?track a mo:Track; mo:track_number ?tracknum; foaf:maker ?maker. ?album mo:track ?track; dc:title ?album_title. ?maker foaf:name ?artist_name"; + queryString += " ?record mo:track ?track; mo:publication_of ?signal. ?signal dc:title ?track_title."; + queryString += " FILTER(sameTerm(?album, <"+id+">))"; + queryString += " }"; + performSearch(queryString); +} + + +function performSearch(queryString) { + $("#spinner").show(); + $("#query").text(queryString); + var query = sparqler.createQuery(); + query.query(queryString, {failure: function(xhr) { alert("Bad response! "+xhr.responseText) }, success: displayResults}); +} + +function displayResults(json) { + resultsTable.fnClearTable(); + if(json) { + + var bindings = json.results.bindings; + for(var i=0; i<bindings.length; i++) + { + var artistEl = $('<div />'); + var artistLink = $('<a/>'); + artistEl.append(artistLink); + artistLink.attr("href", bindings[i].maker.value); + artistLink.attr("rel", "foaf:maker"); + artistLink.addClass("artist_name"); + artistLink.append(bindings[i].artist_name.value); + + var albumEl = $('<div />'); + var albumLink = $('<a/>'); + albumEl.append(albumLink); + albumLink.attr("href", bindings[i].album.value); + albumLink.attr("rel", "dc:title"); + albumLink.addClass("album_name"); + albumLink.append(bindings[i].album_title.value); + + resultsTable.fnAddData([artistEl.html(), bindings[i].track_title.value, bindings[i].tracknum.value, albumEl.html()]); + } + + } + $("#spinner").hide(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/browser/web/js/jOWL.js Thu Oct 08 11:19:11 2009 +0000 @@ -0,0 +1,2242 @@ +/** +* jOWL - a jQuery plugin for traversing and visualizing OWL-DL documents. +* Creator - David Decraene +* Version 1.0 +* Website: +* http://Ontologyonline.org +* Licensed under the MIT license +* http://www.opensource.org/licenses/mit-license.php +* Verified with JSLint +* http://www.jslint.com/ +*/ + +jOWL = window.jOWL = function( resource, options ){ return jOWL.getResource( resource, options ); }; +jOWL.version = "1.0"; + +/** for debugging compatibility */ + try { console.log('...'); } catch(e) { console = window.console = { log: function() {} } } + if ($.browser.opera && opera.postError) { console = window.console = { log : function(){opera.postError(arguments); } }; } + + + +(function($){ + +/** +* if no param: @return string of main namespaces +* if 1 param: assume a documentElement, parse namespaces +* if prefix & URI: Bind prefix to namespace URI +*/ +jOWL.NS = function(prefix, URI){ + if(!arguments.length) + { return "xmlns:"+jOWL.NS.owl.prefix+"='"+jOWL.NS.owl()+"' xmlns:"+jOWL.NS.rdf.prefix+"='"+jOWL.NS.rdf()+"' xmlns:"+jOWL.NS.rdfs.prefix+"='"+jOWL.NS.rdfs()+"' xmlns:"+jOWL.NS.xsd.prefix+" ='"+jOWL.NS.xsd()+"'";} + + if(arguments.length == 1){ + var attr = prefix.get(0).attributes; + for(var i=0;i<attr.length;i++){ + var nn = attr[i].nodeName.split(':'); + if(nn.length == 2){ + if(attr[i].nodeValue == jOWL.NS.owl.URI){ jOWL.NS.owl.prefix = nn[1];} + else if(attr[i].nodeValue == jOWL.NS.rdf.URI){ jOWL.NS.rdf.prefix = nn[1];} + else if(attr[i].nodeValue == jOWL.NS.rdfs.URI){ jOWL.NS.rdfs.prefix = nn[1];} + else if(attr[i].nodeValue == jOWL.NS.xsd.URI){ jOWL.NS.xsd.prefix = nn[1];} + else { jOWL.NS(nn[1], attr[i].nodeValue);} + } + } + jOWL.namespace = prefix.xmlAttr('xml:base') || prefix.xmlAttr('xmlns'); + return; + } + jOWL.NS[prefix] = function(element){ + if(element){ + return (arguments.callee.prefix == 'base') ? element : arguments.callee.prefix + ":" + element; + } + return arguments.callee.URI; + }; + jOWL.NS[prefix].prefix = prefix; + jOWL.NS[prefix].URI = URI; +}; + +var __ = jOWL.NS; + +/** set Main namespaces */ +__("owl", "http://www.w3.org/2002/07/owl#"); +__("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); +__("rdfs", "http://www.w3.org/2000/01/rdf-schema#"); +__("xsd", "http://www.w3.org/2001/XMLSchema#"); + +/** jQuery function additions for easy parsing of identities */ +$.fn.extend({ + /** Used for Opera compatibility when parsing xml attributes, nodeName must be checked, in contrast to native jquery call attr() */ + xmlAttr : function(nodeName){ + var t = this[0].attributes; if(!t){ return;} + for(var i =0;i<t.length;i++){ + if(t[i].nodeName == nodeName){ return t[i].nodeValue;} + } + }, + RDF_ID : function(match){ + var res = this.xmlAttr(__.rdf('ID')); + if(!res){ return false;} + res = jOWL.resolveURI(res); + if(match){ + return res.toLowerCase() == (jOWL.resolveURI(match.toString())).toLowerCase();} + return res; + }, + RDF_Resource : function(match){ + function getClassName(dom){ + var cl = jOWL.Xpath(__.owl("Class"), dom); + if(cl.length == 1){ return new jOWL.Ontology.Class(cl).URI;} + return false; + } + if(!this.length){ return false;} + var rsrc = this.xmlAttr(__.rdf('resource')); + if(!rsrc){ + var dom = this.get(0); + switch(dom.nodeName){ + case __.rdfs("subClassOf"): rsrc = getClassName(dom); break; + case __.owl("disjointWith"): rsrc = getClassName(dom); break; + case __.owl("allValuesFrom"): rsrc = getClassName(dom); break; + case __.owl("someValuesFrom"): rsrc = getClassName(dom); break; + case __.owl("onProperty"): + var t = jOWL.Xpath(__.owl("ObjectProperty"), dom); + if(t.length === 0){ t = jOWL.Xpath(__.owl("DatatypeProperty"), dom);} + if(t.length === 0){ t = jOWL.Xpath(__.owl("FunctionalProperty"), dom);} + rsrc = t.xmlAttr(__.rdf('about')); break; + default: return false; + } + } + if(!rsrc){ return false;} + rsrc = jOWL.resolveURI(rsrc); + if(match){ return rsrc.toLowerCase() == (jOWL.resolveURI(match.toString())).toLowerCase();} + return rsrc; + }, + RDF_About : function(match){ + var res = this.xmlAttr(__.rdf('about')); + if(!res){ return false;} + res = jOWL.resolveURI(res); + if(match){ + return res.toLowerCase() == (jOWL.resolveURI(match.toString())).toLowerCase();} + return res; + } +}); + +/** Check XPath implementation */ +if( document.implementation.hasFeature("XPath", "3.0") ){ + XMLDocument.prototype.selectNodes = function(cXPathString, xNode){ + if( !xNode ){ xNode = this;} + var oNSResolver = this.createNSResolver(this.documentElement); + var aItems = this.evaluate(cXPathString, xNode, oNSResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); var aResult = []; for( var i = 0; i < aItems.snapshotLength; i++){ aResult[i] = aItems.snapshotItem(i);} + return aResult; + }; + Element.prototype.selectNodes = function(cXPathString){ + if(this.ownerDocument.selectNodes) { return this.ownerDocument.selectNodes(cXPathString, this);} + else{throw "For XML Elements Only";} + }; + XMLDocument.prototype.selectSingleNode = function(cXPathString, xNode){ if( !xNode ){ xNode = this;} + var xItems = this.selectNodes(cXPathString, xNode); if( xItems.length > 0 ){ return xItems[0];} else { return null;} + }; + Element.prototype.selectSingleNode = function(cXPathString){ + if(this.ownerDocument.selectSingleNode) { return this.ownerDocument.selectSingleNode(cXPathString, this);} + else{throw "For XML Elements Only";} + }; +} + +/** @return A jQuery array of xml elements */ +jOWL.Xpath = function(selector, elem){ + var node = null; + if(elem){ if(elem.each){ node = elem.get(0);} else { node = elem;} } + var arr = node ? node.selectNodes(selector) : jOWL.document.selectNodes(selector); + if($.browser.msie){ return $($.makeArray(arr));} return $(arr); //this is needed for IE, it returns a length of 1 on empty node array +}; + +/** @return a String array of class references */ +jOWL.Xpath.classes = function(jnode){ + var cl = []; + jOWL.Xpath(__.rdfs("subClassOf"), jnode) + .each(function(){ + var res = $(this).RDF_Resource(); + if(res){ cl.push(res);} + }); + + jOWL.Xpath(__.owl("intersectionOf")+"/"+__.owl("Class"), jnode) + .each(function(){ + var p = $(this).RDF_About(); if(p){ cl.push(p);} + }); + return cl; +}; + +/** Functions stored in jOWL.priv are intended for local access only, to avoid a closure function */ +jOWL.priv = { + /** Arrray functions */ + Array : { + isArray : function(array){ + return Object.prototype.toString.call(array) === '[object Array]'; + }, + pushUnique : function(array, item){ + if(jOWL.priv.Array.getIndex(array, item) === -1){ array.push(item); return true;} + return false; + }, + getIndex : function(array, item){ + for (var i=0; i<array.length; i++){ if(item == array[i]){ return i;} } + return -1; + }, + /** Sorted array as input, returns the same array without duplicates. */ + unique : function(array){ + var result = []; var lastValue=""; + for (var i=0; i<array.length; i++) + { + var curValue=array[i]; + if(curValue != lastValue){ result[result.length] = curValue;} + lastValue=curValue; + } + return result; + } + } +}; + +/** Make values work with jOWL.Ontology.Array */ +jOWL.Literal = function(value){ + this.name = value; +}; + +/** Access to the owl:Ontology element, also the main coding namespace for ontology objects */ +jOWL.Ontology = function(){ + if(!(this instanceof arguments.callee)){ return new jOWL.Ontology();} + this.parse(jOWL.Xpath("/"+__.rdf("RDF")+"/"+__.owl("Ontology"))); + return this; +}; + +/** 'superclass' for referencable ontology objects */ +jOWL.Ontology.Thing = function(jnode){ + this.parse(jnode); +}; + +jOWL.Ontology.Thing.prototype = { + jOWL : jOWL.version, + equals : function(id){ + var URI = (typeof id == "string") ? jOWL.resolveURI(id) : id.URI; + return URI === this.URI; + }, + /** Initialization */ + parse : function(jnode){ + if(!jnode.length){ return;} + var identifier; + if(typeof jnode == 'string'){ + identifier = jnode; + jnode = $(); + } + else { + identifier = jnode.RDF_ID() || jnode.RDF_About(); + if(!identifier){identifier = "anonymousOntologyObject"; + this.isAnonymous = true; + } + } + identifier = jOWL.resolveURI(identifier); + this.isExternal = jOWL.isExternal(identifier); + if(this.isExternal){this.baseURI = this.isExternal[0]; this.name = this.isExternal[1]; this.URI = this.baseURI+this.name;} + else { this.baseURI = jOWL.namespace; this.name = identifier; this.URI = this.name;} + this.jnode = jnode; + this.type = jnode.get(0).nodeName; + }, + /** @return A jQuery array of elements matching the annotation (qualified name or annotation Property) */ + annotations : function(annotation){ + return jOWL.Xpath(annotation, this.jnode); + }, + /** @return rdfs:comment annotations */ + description : function(){ + return $.map(this.annotations(__.rdfs('comment')), function(n){ return $(n).text();}); + }, + /** + @return Array of Arrays, where secondary array is of form: [0] = term (rdfs:label) , [1] = identifier, [2] = language; [3] = type of object + example: + [ + ["bleu", "blue", "fr", "owl:Class"] + ] + */ + terms : function(){ + var terms = [], self = this; + if(jOWL.options.dictionary.addID && this.name != "anonymousOntologyObject"){ terms.push([this.name.beautify(), this.URI, jOWL.options.defaultlocale, this.type]);} + this.annotations(__.rdfs('label')).each(function(){ + var lbl = $(this); + var locale = lbl.xmlAttr("xml:lang") || jOWL.options.defaultlocale; + var txt = lbl.text(); + var match = false; + for(var i =0;i<terms.length;i++){ + if(terms[i][0].toUpperCase() == txt.toUpperCase() && terms[i][2] == locale){ match = true;} + } + if(!match){ terms.push([lbl.text(), self.URI, locale, self.type]);} + }); + return terms; + }, + /** @return A representation name */ + label : function(){ + var label = false; + this.annotations(__.rdfs('label')).each(function(){ + var $label = $(this); + if(jOWL.options.locale){ + var lang = $label.xmlAttr('xml:lang') || jOWL.options.defaultlocale; + if(lang == jOWL.options.locale){ label = $label.text(); return false;} + } else { label = $label.text(); return false;} + }); + if(label){ return label;} + if(this.name == "anonymousOntologyObject"){ return jOWL.Manchester(this) || "anonymous Object";} + if(jOWL.options.niceClassLabels && (this.isClass || this.isThing)){ + return this.name.beautify(); + } + return this.name; + }, + /** Binds the Ontology element to the jQuery element for visual representation + * @return jQuery Element + */ + bind : function(jqelem){ + return jqelem.text(this.label()).attr('typeof', this.type).attr('title', this.URI); + } +}; + +jOWL.Ontology.prototype = jOWL.Ontology.Thing.prototype; + +/** used for jOWL.Ontology.Individual.sourceof */ +jOWL.priv.testObjectTarget = function(target, matchtarget){ + if(target.isArray){ + for(var i=0;i<target.length;i++){ + if(jOWL.priv.testObjectTarget(target.get(i), matchtarget)){ return true;} + } + return false; + } + //if the target is a class, fetch individuals instead. + else if(target.isClass){ + var a = target.individuals(); + for(var i=0;i<a.length;i++){ + if(a.get(i).URI == matchtarget){ return true;} + } + } + else if(target.URI == matchtarget){ return true;} + return false; +}; + +/** access to Individuals of the ontology*/ +jOWL.Ontology.Individual = function(jnode, owlclass){ + this.parse(jnode); + if(this.type == __.owl("Thing")){ + var t = jOWL.Xpath(__.rdf('type'), this.jnode); + if(!t.length){ throw "unable to find a Class for the Individual "+this.name;} + this.Class = $(t[0]).RDF_Resource(); + } + else { + this.Class = jOWL.resolveURI(jnode.get(0)); + } + this.type = __.owl("Thing"); + if(owlclass){ this.owlClass(owlclass);} +}; + +jOWL.Ontology.Individual.prototype = $.extend({}, jOWL.Ontology.Thing.prototype, { + isThing : true, + /** @return The owl:Class */ + owlClass : function(owlclass){ + if(owlclass){ jOWL.data(this.name, "class", owlclass);} + else { + var cl = jOWL.data(this.name, "class"); + if(!cl){ cl = jOWL(this.Class); if(cl){ this.owlClass(cl);} } + return cl; + } + }, + /** Access to restrictions */ + sourceof : function(property, target, options){ + options = $.extend({ + inherited : true, // add restrictions specified on parents as well + transitive : true, + ignoreGenerics : false, //if a parent has an identical property, with another target 'Thing', skip that restriction + ignoreClasses : true, + valuesOnly : true + }, options); + + var results = new jOWL.Ontology.Array(); + + this.jnode.children().filter(function(){return (this.prefix != __.rdfs.prefix && this.prefix != __.rdf.prefix && this.prefix != __.owl.prefix);}) + .each(function(){ + var restriction = new jOWL.Ontology.Restriction($(this)); + var propertyMatch = property ? false : true; + var targetMatch = target ? false : true; + + if(!propertyMatch){ + if( property.isArray){ propertyMatch = property.contains(restriction.property);} + else { propertyMatch = (property.URI == restriction.property.URI);} + if(!propertyMatch){ return;} + } + + if(!target){ + if(options.transitive && restriction.property.isTransitive && !options.ignoreGenerics){ + var rTarget = restriction.getTarget(); + var transitives = rTarget.sourceof(restriction.property, null, options); + results.concat(transitives); + } + } + else { + if(restriction.property.isObjectProperty){ + targetMatch = jOWL.priv.testObjectTarget(target, restriction.target); + if(!targetMatch && options.transitive && restriction.property.isTransitive){ + var rTransitives = restriction.getTarget().sourceof(restriction.property, target, options); + if(rTransitives.length > 0){ targetMatch = true;} + } + } + else if(restriction.property.isDatatypeProperty){ + targetMatch = restriction.property.assert(restriction.target, target); + } + else { targetMatch = (target == restriction.target);} + } + if(propertyMatch && targetMatch){ results.pushUnique(restriction);} + + }); + if(options.inherited){ + var clRestrictions = this.owlClass().sourceof(property, target, options) + .each(function(){ + //target can be a class, null, a duplicate individual... + var clRestr = this; + if(options.valuesOnly && clRestr.target === null){return;} + var clTarget = this.getTarget(); + if(clTarget.isClass && options.ignoreClasses){ return;} + + var containsProperty = false; + for(var i = 0;i<results.length;i++){ + var restr = results.get(i); + if(restr.property.URI == clRestr.property.URI){ + containsProperty = true; + if(!options.ignoreGenerics){ + if(clRestr.target != restr.target){ results.pushUnique(clRestr);} + } + } + } + if(!containsProperty){ results.pushUnique(clRestr);} + }); + } + return results; + + }, + localRestrictions : function(property, target){ + return this.sourceof(property, target, {inherited : false, transitive : false }); + }, + /** Include generic will add transitivity reasoning */ + valueRestrictions : function(includeGeneric){ + return this.sourceof(null, null, {ignoreGenerics : !includeGeneric, valuesOnly : true }); + } +}); + +/** jNode is of type owl:Restriction */ +jOWL.Ontology.Restriction = function(jnode){ + + var jprop, prop, op, restrtype; + + this.cachedTarget = null; + + if(jnode.get(0).nodeName != __.owl("Restriction")){ + this.property = jOWL(jOWL.resolveURI(jnode.get(0)), {type: "property"}); + this.target = jnode.RDF_Resource() || jnode.text(); + restrtype = "Individual"; + } + else + { + jprop = jOWL.Xpath(__.owl("onProperty"), jnode); + prop = jprop.RDF_Resource(); if(!prop){ throw "no property found for the given owl:restriction";} + op = jprop.siblings(); + restrtype = op.get(0).nodeName; + this.property = jOWL(prop, {type: "property"}); + this.target = null; //string only + } + + this.restriction = { minCard: false, maxCard : false, some: [], all : [], value : false }; + this.type = jnode.get(0).nodeName; + this.isAnonymous = true; + this.isValueRestriction = (restrtype == __.owl('someValuesFrom') || restrtype == __.owl('allValuesFrom') || restrtype == __.owl('hasValue')); + this.isCardinalityRestriction = (restrtype == __.owl('cardinality') || restrtype == __.owl('maxCardinality') || restrtype == __.owl('minCardinality')); + + if(!this.property || !restrtype){ throw "badly formed owl:restriction";} + switch(restrtype){ + case __.owl('cardinality'): this.restriction.minCard = this.restriction.maxCard = parseInt(op.text(), 10); break; + case __.owl('maxCardinality'): this.restriction.maxCard = parseInt(op.text(), 10); break; + case __.owl('minCardinality'): this.restriction.minCard = parseInt(op.text(), 10); break; + case __.owl('hasValue'): var res = op.RDF_Resource(); if(res){ this.target = res;} break; + } + if(this.property.isObjectProperty){ + if(this.isCardinalityRestriction && this.property.range){ this.target = this.property.range;} + else if(this.isValueRestriction){ + var t = op.RDF_Resource(); + if(t == "anonymousOntologyObject"){//nested groupings, anonymous classes + this.cachedTarget = new jOWL.Ontology.Class(jOWL.Xpath(__.owl("Class"), op)); + } + this.target = t; + } + } + + var suffix = this.target || this.restrtype; + this.name = this.property.name+'#'+suffix; + return this; +}; + +jOWL.Ontology.Restriction.prototype = { + jOWL : jOWL.version, + isRestriction : true, + bind : function(){return null;}, + merge : function(crit){ + if(this.isCardinalityRestriction && crit.isValueRestriction ){ this.target = crit.target; return true;} + else if(this.isValueRestriction && crit.isCardinalityRestriction){ + switch(crit.restrtype){ + case __.owl('cardinality'): this.restriction.minCard = this.restriction.maxCard = crit.restriction.minCard; return true; + case __.owl('minCardinality'): this.restriction.minCard = crit.restriction.minCard; return true; + case __.owl('maxCardinality'): this.restriction.maxCard = crit.restriction.maxCard; return true; + } + } + return false; + }, + getTarget : function(){ + if(!this.target){ return jOWL('Thing');} + if(this.cachedTarget){ return this.cachedTarget;} + this.cachedTarget = (this.property.isObjectProperty) ? jOWL(this.target) : new jOWL.Literal(this.target); + return this.cachedTarget; + }, + equals : function(restr){ + if(!restr.isRestriction){ return false;} + if(this.property.URI == restr.property.URI){ + if(this.target == 'anonymousOntologyObject'){return false;}//oneof lists etc unsupported right now + if(this.target && this.target === restr.target){ return true;} + } + return false; + } +}; + +/** Datatype Logic, local functions */ +jOWL.priv.Dt = function(options){ + this.settings = $.extend({base: null, pattern : null, assert: function(b){return true;}, match: function(a, b){return true;}}, options); + this.base = jOWL.Ontology.Datatype[this.settings.base]; +}; + +jOWL.priv.Dt.prototype = { + sanitize : function(b){ + if(this.settings.sanitize){ return this.settings.sanitize(b);} + if(this.base && this.base.sanitize){ return this.base.sanitize(b);} + }, + assert : function(b){ + var v = this.sanitize(b); if(v !== undefined){ b = v;} + if(this.base && !this.base.assert(b)){ return false;} + if(this.settings.pattern && !this.settings.pattern.test(b)){ return false;} + return this.settings.assert(b); + }, + match : function(a, b){ + var v = this.sanitize(b); if(v !== undefined){ b = v;} + if(!this.assert(b)){ return false;} + if(this.base && !this.base.match(a, b)){ return false;} + return this.settings.match(a, b); + } +}; + +jOWL.Ontology.Datatype = function(URI, options){ + jOWL.Ontology.Datatype[URI] = new jOWL.priv.Dt(options); +}; + +/** Datatype Definitions */ +jOWL.Ontology.Datatype(__.xsd()+"integer", {sanitize : function(x){return parseInt(x, 10);}, assert : function(x){ return Math.round(x) == x;}, match : function(a, b){ + var check = parseInt(a, 10); + if(!isNaN(check)){ return check == b;} + var arr = a.split('&&'); + for(var i=0;i<arr.length;i++){ arr[i] = b+arr[i];} + try { + return eval(arr.join(' && ')); + } catch(e){ return false;} +} }); +jOWL.Ontology.Datatype(__.xsd()+"positiveInteger", {base: __.xsd()+"integer", assert : function(x){ return x > 0;} }); +jOWL.Ontology.Datatype(__.xsd()+"decimal", {base: __.xsd()+"integer" }); +jOWL.Ontology.Datatype(__.xsd()+"float", {base: __.xsd()+"integer" }); +jOWL.Ontology.Datatype(__.xsd()+"double", {base: __.xsd()+"integer" }); +jOWL.Ontology.Datatype(__.xsd()+"negativeInteger", {base: __.xsd()+"integer", assert : function(x){ return x < 0;} }); +jOWL.Ontology.Datatype(__.xsd()+"nonNegativeInteger", {base: __.xsd()+"integer", assert : function(x){ return x >= 0;} }); +jOWL.Ontology.Datatype(__.xsd()+"nonPositiveInteger", {base: __.xsd()+"integer", assert : function(x){ return x <= 0;} }); +jOWL.Ontology.Datatype(__.xsd()+"string"); + +var URIPattern = /^([a-z0-9+.\-]+):(?:\/\/(?:((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(?::(\d*))?(\/(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?|(\/?(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9A-F]{2})+(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?)(?:\?((?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*))?(?:#((?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*))?$/i; + +jOWL.Ontology.Datatype(__.xsd()+"anyURI", {base: __.xsd()+"string", pattern : URIPattern }); +jOWL.Ontology.Datatype(__.xsd()+"boolean", {sanitize : function(x){ + if(typeof x == 'boolean'){ return x;} + if(x == 'true'){ return true;} + if(x == 'false'){ return false;} + }, assert : function(x){ + return typeof x == 'boolean'; + }, match: function(a, b){ + if(a === "false"){ a = false;} + if(a === "true"){ a = true;} + return (a === b); +}}); + +/** 'superclass' for Properties */ +jOWL.Ontology.Property = function(jnode){ + var r = this.parseProperty(jnode); + if(r){ return r;} +}; + +jOWL.Ontology.Property.prototype = $.extend({}, jOWL.Ontology.Thing.prototype,{ + isProperty : true, + parseProperty : function(jnode){ + if(!jnode || typeof jnode == 'string'){ + this.domain = this.range = null; + this.parse(jnode); + return; + } + if(jOWL.options.cacheProperties && jOWL.indices.IDs){ + var res = jnode.RDF_ID() || jnode.RDF_About(); + var c = jOWL.index('property').get(res); + if(c){ return c;} + } + this.parse(jnode); + this.domain= $(this.jnode.get(0).selectSingleNode(__.rdfs('domain'))).RDF_Resource(); + this.range = $(this.jnode.get(0).selectSingleNode(__.rdfs('range'))).RDF_Resource(); + } +}); + +/** access to Datatype properties */ +jOWL.Ontology.DatatypeProperty = function(jnode){ + var r = this.parseProperty(jnode); + if(r){ return r;} + if(this.type == __.owl("AnnotationProperty")){ this.range = __.xsd()+"string";} +}; + +jOWL.Ontology.DatatypeProperty.prototype = $.extend({}, jOWL.Ontology.Thing.prototype, jOWL.Ontology.Property.prototype, { + isDatatypeProperty : true, + /** check datatype values against this */ + assert : function(targetValue, value){ + var self = this; + var dt = jOWL.Ontology.Datatype[this.range]; + if(!dt){ + console.log(this.range+" datatype reasoning not implemented"); + return true; + } + if(value === undefined){ return dt.assert(targetValue);} + else {return dt.match(value, targetValue);} + } +}); + +/** access to Object properties */ +jOWL.Ontology.ObjectProperty = function(jnode){ + var r = this.parseProperty(jnode); + if(r){ return r;} + var self = this; + jOWL.Xpath(__.rdf('type'), this.jnode).each(function(){ + if($(this).RDF_Resource() == __.owl()+"TransitiveProperty"){ self.isTransitive = true;} + }); + if(this.jnode.get(0).nodeName == __.owl("TransitiveProperty")){ self.isTransitive = true;} +}; + +jOWL.Ontology.ObjectProperty.prototype = $.extend({}, jOWL.Ontology.Thing.prototype, jOWL.Ontology.Property.prototype, { + isObjectProperty : true +}); + +/** access to an owl:Class */ +jOWL.Ontology.Class = function(jnode){ + this.parse(jnode); +}; + +/** @return jOWL Array of Restrictions */ +jOWL.Xpath.restrictions = function(jnode){ + var result = new jOWL.Ontology.Array(); + jOWL.Xpath(__.rdfs("subClassOf")+"/"+__.owl("Restriction"), jnode) + .add(jOWL.Xpath(__.owl("intersectionOf")+"/"+__.owl("Restriction"), jnode)) + .each(function(){ + result.push(new jOWL.Ontology.Restriction($(this))); + }); + return result; +}; + +/** Internal Use */ +jOWL.Ontology.Intersection = function(jnode){ + var self = this; + this.jnode = jnode; + this._arr = []; + this.URI = this.jnode.parent().RDF_ID(); + this.matches = {}; + jOWL.Xpath(__.owl("Restriction"), jnode).each(function(){ + var restr = new jOWL.Ontology.Restriction($(this)); + if(restr.isValueRestriction){self._arr.push(restr);} + }); + jOWL.Xpath(__.owl('Class'), jnode).each(function(){ + var uri = $(this).RDF_About(); + if(uri){ self._arr.push(jOWL(uri));} + }); +}; + +jOWL.Ontology.Intersection.prototype = { + isIntersection : true, + jOWL : jOWL.version, + match : function(id, cls, clRestr){ + if(id == this.URI){ return false;} + if(this.matches[id] !== undefined){ return this.matches[id]; }//local cache + + for(var i =0;i<this._arr.length;i++){ + var entry = this._arr[i]; + var m = false; + if(entry.isRestriction){ + clRestr.each(function(){ + if(this.equals(entry)){ m = true; return false;} + }); + if(!m) { + this.matches[id] = false; + return false; + } + } else if(entry.isClass){ + for(var j = 0;j<cls.length;j++){ + if(entry.equals(cls[j])){m = true; break;} + var it = jOWL.index('ID')[cls[j]]; + if(it){ + var narr = jOWL.Xpath.classes(jOWL.index('ID')[cls[j]].jnode); + for (var z=0;z<narr.length ; z++){ + if(entry.equals(narr[z])){m = true; break;} + } + } + } + if(!m){ + this.matches[id] = false; + return false; + } + } + } + this.matches[id] = true; + return this.matches[id]; + }, + equals : function(isect){ + if(!isect.isIntersection){ return false;} + for(var i =0;i<this._arr.length;i++){ + var match = false; + for(var j = 0;j<isect._arr.length;j++){ + if(isect._arr[j].equals(this._arr[i])){ match = true;} + } + if(!match){ return false;} + } + return true; + } +}; + +jOWL.Ontology.Class.prototype = $.extend({}, jOWL.Ontology.Thing.prototype, { + isClass : true, + /** @return A jOWL.Ontology.Array of individuals for this class & its subclasses */ + individuals : function(){ + var arr = new jOWL.Ontology.Array(); + var q = new jOWL.SPARQL_DL("Type(?x, "+this.name+")").execute({async: false, onComplete: function(r){ arr = r.jOWLArray("?x");} }); + return arr; + }, + /** @return A jOWL.Ontology.Array of individuals (if oneOf list) */ + oneOf : function(){ + var arr = new jOWL.Ontology.Array(); + var oneOf = this.jnode.children().filter(function(){return this.tagName == __.owl("oneOf");}); + oneOf.children().each(function(){ + arr.push(jOWL($(this).RDF_About())); + }); + return arr; + }, + /** @return A jOWL.Ontology.Array of direct children */ + children : function(){ + var that = this; + var oChildren = jOWL.data(this.name, "children"); + if(oChildren){ return oChildren;} + oChildren = new jOWL.Ontology.Array(); + if(this.oneOf().length){return oChildren;} + var URI = this.URI; + + for(x in jOWL.index('ID')){ + if(x === this.URI){ continue;} + var node = jOWL.index('ID')[x]; + if(!node.isClass){continue;} + var cls = jOWL.Xpath.classes(node.jnode); //direct subClasses + for(var i=0;i<cls.length;i++){ + if(this.equals(cls[i])){ + oChildren.push(node); + } + } + var clRestr = jOWL.Xpath.restrictions(node.jnode); + var intersections = jOWL.index("intersection")[URI]; + if(intersections){ + intersections.each(function(){//fully defined Subclasses + if(this.match(x, cls, clRestr)){oChildren.push(node);} + }); + } + } + //an ObjectProperty mentions this as domain + jOWL.index("property").each(function(){ + if(this.domain == that.name){ + var nodes = jOWL.Xpath('//'+__.owl('onProperty')+'[@'+__.rdf('resource')+'="#'+this.name+'"]/parent::'+__.owl('Restriction')+'/..'); + nodes.filter(function(){ return (this.nodeName == __.owl('intersectionOf') || this.nodeName == __.rdfs('subClassOf')); + }).each(function(){ + var cl = jOWL($(this.selectSingleNode('parent::'+__.owl('Class')))); + if(!oChildren.contains(cl) && cl.name != that.name && cl.name !== undefined){ oChildren.push(cl);} + }); + } + }); + //filter out redundancies + oChildren.filter(function(){ + this.hierarchy(false); + return (this.parents().contains(URI)); + }); + jOWL.data(this.name, "children", oChildren); + return oChildren; + }, + setParents : function(parents){ + jOWL.data(this.name, "parents", parents); return parents; + }, + /** @return A jOWL.Ontology.Array of parents, includes redundancies, to exclude do a hierarchy search first.*/ + parents : function(){ + var self = this; + var oParents = jOWL.data(this.name, "parents"); + if(oParents){ return oParents;} + + var temp = []; + + var cls = jOWL.Xpath.classes(this.jnode); + for(var i=0;i<cls.length;i++){ jOWL.priv.Array.pushUnique(temp, cls[i]);} + + var restr = jOWL.Xpath.restrictions(this.jnode); + restr.each(function(){ + if(this.property.domain && this.property.domain != self.name){ jOWL.priv.Array.pushUnique(temp, this.property.domain); + } + }); + + var iSectLoop = function(){ + if(this.match(self.URI, cls, restr)){ + jOWL.priv.Array.pushUnique(temp, this.URI); + } + + }; + + if(jOWL.options.reason){ + for(resource in jOWL.index('intersection')){ + jOWL.index('intersection')[resource].each(iSectLoop); + } + } + + oParents = new jOWL.Ontology.Array( jOWL.getXML(temp), true); + if(!oParents.length){ oParents.push(jOWL('Thing'));} + else if(oParents.length > 1){ oParents.filter(function(){return this.name != ('Thing');});} //Remove Thing reference if other parents exist + jOWL.data(this.name, "parents", oParents); + return oParents; + }, +/** @return ancestors to the class in a jOWL.Ontology.Array */ + ancestors : function(){ + return this.hierarchy(false).flatindex; + }, +/** +Constructs the entire (parent) hierarchy for a class +@return a jOWL.Ontology.Array containing top nodes (classes directly subsumed by 'owl:Thing') +@param addInverse add a variable invParents (jOWL.Ontology.Array of child references) to each node with exception of the leaves (original concept) +*/ + hierarchy : function(addInverse){ + var endNodes = new jOWL.Ontology.Array(); + var self = this; + endNodes.flatindex = new jOWL.Ontology.Array(); + + function URIARR(p_arr, obj){ + var add = true; + if(!obj){ obj = {}; add = false;} + if(p_arr.each){ + p_arr.each(function(){ + if(obj[this.URI]){return;} + if(this.URI == __.owl()+'Thing'){ return;} + if(add){ obj[this.URI] = true;} + if(this.parents){ URIARR(this.parents(), obj);} + }); + } + return obj; + } + + function traverse(concept){ + var parents = concept.parents(); + if(parents.length == 1 && parents.contains(__.owl()+'Thing')){ endNodes.pushUnique(concept); return;} + else + { + var asso = jOWL.options.reason ? URIARR(parents) : {}; + parents.filter(function(){ return (!asso[this.URI]);}); //throw out redundancies + parents.each(function(){ + var item = endNodes.flatindex.pushUnique(this); + if(addInverse){ + if(!item.invParents){ item.invParents = new jOWL.Ontology.Array();} + item.invParents.pushUnique(concept); + } + traverse(item); + }); + concept.setParents(parents); + } + } + + traverse(this); + return endNodes; + + }, + /** + @param level depth to fetch children, Default 5 + @return jOWL array of classes that are descendant + */ + descendants : function(level){ + level = (typeof level == 'number') ? level : 5; + var oDescendants = jOWL.data(this.name, "descendants"); + if(oDescendants && oDescendants.level >= level){ return oDescendants;} + oDescendants = new jOWL.Ontology.Array(); + oDescendants.level = level; + + function descend(concept, i){ + if(i <= level){ + i++; + var ch = concept.children(); + oDescendants.concat(ch); + ch.each(function(item){ descend(item, i);}); + } + } + + descend(this, 1); + jOWL.data(this.name, "descendants", oDescendants); + return oDescendants; + }, + /** @return jOWL.Array of Restrictions, target is an individual, not a class or undefined (unless includeAll is specified) - deprecated */ + valueRestrictions : function(includeAll, array){ + return this.sourceof(null, null, {ignoreClasses : !includeAll}); + }, + /** + get all restrictions that satisfy the arguments + @param property property or array of properties, or null + @param target class, individuals of array of them, or null + @return jOWL.Array of Restrictions + */ + sourceof : function(property, target, options){ + options = $.extend({ + inherited : true, // add restrictions specified on parents as well + transitive : true, //expand on transitive relations too + ignoreGenerics : true, //if a parent has an identical property, with another target 'Thing', skip that restriction + ignoreClasses : false, //only individuals should return + valuesOnly : true //do not return valueless criteria + }, options); + var self = this; + var crit = jOWL.data(this.name, "sourceof"); + var jnode = this.jnode; + + if(!crit){ + crit = new jOWL.Ontology.Array(); + var arr = jOWL.Xpath(__.rdfs("subClassOf")+"/"+__.owl("Restriction"), jnode) + .add(jOWL.Xpath(__.owl("intersectionOf")+"/"+__.owl("Restriction"), jnode)); + arr.each(function(index, entry){ + var cr = new jOWL.Ontology.Restriction($(entry)); + var dupe = false; + crit.each(function(item, i){ + if(this.property.name == cr.property.name){ dupe = item;} + }); + if(dupe){ if(!dupe.merge(cr)){ crit.push(cr);} } + else { crit.push(cr);} + }); + jOWL.data(self.name, "sourceof", crit); + } + var results = new jOWL.Ontology.Array(); + + crit.each(function(){ + + var propertyMatch = property ? false : true; + var targetMatch = target ? false : true; + + if(!propertyMatch){ + if(property.isArray){ propertyMatch = property.contains(this.property);} + else { propertyMatch = (property.URI == this.property.URI);} + } + + if(!target){ + if(options.transitive && this.property.isTransitive){ + var rTarget = this.getTarget(); + var transitives = rTarget.sourceof(this.property, null, options); + results.concat(transitives); + } + } + + if(!targetMatch && !this.target){ + targetMatch = !options.valuesOnly; + } + + if(!targetMatch){ + var targ = this.getTarget(); + if(targ.isClass && options.ignoreClasses){ return;} + targetMatch = jOWL.priv.testObjectTarget(target, this.target); + if(!targetMatch && options.transitive && propertyMatch && this.property.isTransitive){ + if(targ.isThing){ + if(targ.sourceof(property, target).length){ targetMatch = true;} + } + } + } + + if(propertyMatch && targetMatch){ results.pushUnique(this);} + }); + + if(!options.inherited){ return results;} + + this.parents().each(function(){ + if(this.sourceof){ + this.sourceof(property, target, options).each(function(parentsource){ + var ptarget = this.getTarget(); + var containsProperty = false; + var tempArray = new jOWL.Ontology.Array(); + results.filter(function(){ + var restr = this, keep = true; + if(restr.property.URI == parentsource.property.URI){ + containsProperty = true; + if(!options.ignoreGenerics){ + if(parentsource.target != restr.target){ tempArray.push(parentsource);} + } else { + if(ptarget.isThing){ + keep = restr.getTarget().isThing && parentsource.target != restr.target; + tempArray.push(parentsource); + } + } + } + return keep; + }); + if(!containsProperty){ results.push(parentsource);} + results.concat(tempArray); + }); + } + }); + return results; + } +}); + +/** Utility object */ +jOWL.Ontology.Array = function(arr, isXML){ + var self = this; + this.items = []; + if(arr){ + if(isXML){ $.each(arr, function(){ + var entry = this.jOWL ? this : jOWL($(this)); + self.items.push(entry);}); + } + else { this.items = arr;} + } + this.length = this.items.length; +}; + +jOWL.Ontology.Array.prototype = { + jOWL : jOWL.version, + isArray : true, + bind : function(listitem, fn){ + return this.map(function(){ + var syntax = listitem ? listitem.clone(true) : $('<span/>'); + var html = this.bind(syntax).append(document.createTextNode(' ')); + if(fn){ fn.call(html, html, this);} + return html.get(0); + }); + }, + concat : function(arr, ignoreUnique){ + var self = this; + if(arr.each){ arr.each(function(){ + if(ignoreUnique){ self.push(this); } + else { self.pushUnique(this); } + }); + } + else { self.items = self.items.concat(arr.items); this.length = self.items.length;} + return this; + }, + contains : function(o){ + return this.get(o) ? true: false; + }, + each : function(fn, reverse){ + var i, self = this; + var stop = false; + if(reverse){ + for(i=this.items.length - 1; i>=0;i--){ + if(stop){ break;} + (function(){ + var item = self.eq(i); + if(fn.call(item, item, i) === false){ stop = true;} + })(); + } + } + else { + for(i=0;i<this.items.length;i++){ + if(stop){ break;} + (function(){ + var item = self.eq(i); + if(fn.call(item, item, i) === false){ stop = true;} + })();} + } + return this; + }, + eq : function(index){ + if(index < 0 || index > this.items.length -1){ return null;} + return this.items[index]; + }, + filter : function(fn){ + var self = this; + this.each(function(item, i){ + var q = fn.call(item, item, i); + if(!q){ self.items.splice(i, 1);} + }, true); + this.length = this.items.length; + return this; + }, + getIndex : function(o){ + var found = -1; + if(o.equals){ + this.each(function(a, i){ + if(this.equals && this.equals(o)){ found = i; return false;} + }); + } + else { + if(typeof o == 'number'){ return o;} + var name = typeof o == "string" ? o : o.name; + var URI = o.URI || name; + + this.each(function(a, i){ + if(this.URI){ if(this.URI == URI){ found = i;}} + else if(this.name == name){ found = i;} + }); + } + return found; + }, + get : function(o){ + return this.eq(this.getIndex(o)); + }, + map : function(fn){ + var arr = []; + this.each(function(){ arr.push(fn.call(this, this));}); + return arr; + }, + push : function(o){ + this.items.push(o); + this.length = this.items.length; + return this; + }, + pushUnique : function(o){ + return this.get(o) || this.push(o).get(o); + }, + toString : function(){ + return this.map(function(){return this.URI;}).join(', '); + }, + /** Convert this array into an associative array with key = URI */ + associative : function(){ + var arr = {}; + this.each(function(){ + if(this.URI){ arr[this.URI] = this;} + }); + return arr; + } +}; + + +jOWL.options = {reason: true, locale:false, defaultlocale: 'en', + dictionary : { create: true, addID : true }, + onParseError : function(msg){alert("jOWL parseError: "+msg);}, cacheProperties : true, niceClassLabels : true}; +jOWL.document = null; +jOWL.namespace = null; +jOWL.indices = { //internal indices + P : null, //jOWL array + data : {}, + IDs : null, + I : null, //Intersection + T : null, //Thing + D : null, //dictionary + reset : function(){var i = jOWL.indices; i.data = {}; i.P = null; i.T = null; i.IDs = null; i.I = null;i.D = null;} +}; + +jOWL.index = function(type, wipe){ + var i = jOWL.indices; + switch (type) + { + /**jOWL indexes all elements with rdf:ID, and first order ontology elements specified with rdf:about + @return Associative array with key = URI and value = jOWL object. + */ + case "ID": + if(i.IDs === null || wipe){ + if(wipe){ i.reset();} + i.IDs = {}; + i.T = {}; + var start = new Date(); + + var rID = jOWL.Xpath("//*[@"+__.rdf("ID")+"]").each(function(){ + var jowl = jOWL.getResource($(this)); + if(jowl){ + i.IDs[jowl.URI] = jowl; + if(jowl.isThing){ + if(!i.T[jowl.Class]){ i.T[jowl.Class] = new jOWL.Ontology.Array();} + i.T[jowl.Class].push(jowl); + } + } + }); + + var rAbout = jOWL.Xpath("/"+__.rdf("RDF")+"/*[@"+__.rdf("about")+"]").each(function(){ + var jnode = $(this); + var jowl = jOWL.getResource($(this)); + if(!jowl){ return;} + if(jowl.isClass || jowl.isProperty || jowl.isThing){ + if(i.IDs[jowl.URI]){ jnode.children().appendTo(i.IDs[jowl.URI].jnode); return;} + i.IDs[jowl.URI] = jowl; + if(jowl.isThing){ + if(!i.T[jowl.Class]){ i.T[jowl.Class] = new jOWL.Ontology.Array();} + i.T[jowl.Class].push(jowl); + } + return; + } + }); + console.log("Loaded in "+(new Date().getTime() - start.getTime())+"ms"); + } + return i.IDs; + /** Generated together with ID index. + * @return Associative Array, key = class, value = jOWL Array of individuals. + */ + case "Thing": + return i.T; + case "intersection": + if(i.I === null || wipe){ + var temp = new jOWL.Ontology.Array(); + i.I = {}; + jOWL.Xpath("//"+__.owl("intersectionOf")).each(function(){ + var isect = new jOWL.Ontology.Intersection($(this)); + if(!isect.URI){return;} + var dupe = temp.get(isect); + if(dupe){ + console.log("duplicate intersection found between : (Ignoring) "+isect.URI+" and "+dupe.URI); + } else { + if(!i.I[isect.URI]){i.I[isect.URI] = new jOWL.Ontology.Array();} + temp.push(isect); + i.I[isect.URI].push(isect); + } + }); + } + return i.I; + case "property": + if(i.P === null || wipe) + { + jOWL.options.cacheProperties = false; + i.P = new jOWL.Ontology.Array(); + for(x in i.IDs){ + var jowl = i.IDs[x]; + if(jowl.isProperty){ i.P.push(jowl);} + } + jOWL.options.cacheProperties = true; + } + return i.P; + case "dictionary": + /**Dictionary: Array of Arrays, where secondary array is of form: [0] = term, [1] = rdfID, [2] = locale */ + if(i.D === null || wipe) + { + i.D = []; + for(x in i.IDs){ + var entry = i.IDs[x]; + i.D = i.D.concat(entry.terms()); + } + } + return i.D; + } +}; + +/** Internal Function, storing data in associative array (JSON), +jquery data function cannot be used as expando data does not work in IE for ActiveX XMLhttprequest*/ +jOWL.data = function(rdfID, dtype, data){ + var d = jOWL.indices.data; + if(!d[rdfID]){ d[rdfID] = {};} + if(!data){ return d[rdfID][dtype];} + d[rdfID][dtype] = data; +}; + +/** +* Initialize jOWL with an OWL-RDFS document. +* @param path relative path to xml document +* @param callback callback function to be called when loaded. +* @options : optional settings: +* onParseError : function(msg){} function to ba called when parsing fails +* reason : true/false, turns on additional reasoning at the expense of performance +* locale: set preferred language (if available), examples en, fr... +*/ +jOWL.load = function(path, callback, options){ + var that = this; + if($.browser.msie && location.toString().indexOf('file') === 0){ //IE won't load local xml files otherwise + var xml = document.createElement("xml"); + xml.validateOnParse = false; //IE throws DTD errors (for 'rdf:') on perfectly defined OWL files otherwise + xml.src = path; + xml.onreadystatechange = function(){ + if(xml.readyState == "interactive"){ var xmldoc = xml.XMLDocument; document.body.removeChild(xml);callback(that.parse(xmldoc, options));} + }; + document.body.appendChild(xml); + } + else { + $.get(path, function(xml){callback(that.parse(xml, options));}); + } +}; + +/** +* initialize jOWL with some OWL-RDFS syntax +* @param doc Either an xmlString or an xmlDocument +* @param options optional, onParseError(msg) : function to execute when parse fails +* @returns false on failure, or the jOWL object +*/ +jOWL.parse = function(doc, options){ + jOWL.document = null; + this.options = $.extend(jOWL.options, options); + if(typeof doc == 'string'){ doc = jOWL.fromString(doc);} + jOWL.document = doc; + if($.browser.msie){ + if(doc.parseError.errorCode !== 0){ jOWL.options.onParseError(doc.parseError.reason); return false;} + } + else if(doc.documentElement.nodeName == 'parsererror'){jOWL.options.onParseError(doc.documentElement.firstChild.nodeValue); return false;} + var root = $(doc.documentElement); + jOWL.NS(root); + if($.browser.msie){ + jOWL.document.setProperty("SelectionLanguage", "XPath"); + jOWL.document.setProperty("SelectionNamespaces", __()); + } + this.index('ID', true); + if(jOWL.options.cacheProperties){ this.index('property', true);} + if(jOWL.options.dictionary.create){ jOWL.index("dictionary");} + jOWL.Thing = new jOWL.Ontology.Thing($(jOWL.create(__.owl, "Class").attr(__.rdf, 'about', __.owl()+'Thing').node)); + jOWL.Thing.type = false; + return this; +}; + +/** +* A String representation of the OWL-RDFS document +* @param xmlNode optional, node to generate a string from, when unspecified the entire document +*/ +jOWL.toString = function(xmlNode){ + if(!xmlNode){ return jOWL.toString(jOWL.document);} + if($.browser.msie){ return xmlNode.xml;} + return new XMLSerializer().serializeToString(xmlNode);// Gecko-based browsers, Safari, Opera. +}; + +/** create a document from string */ +jOWL.fromString = function(doc){ + var owldoc; + if(document.implementation.createDocument){ owldoc = new DOMParser().parseFromString(doc, "text/xml");} // Mozilla and Netscape browsers + else if(window.ActiveXObject){ // MSIE + var xmldoc = new ActiveXObject("Microsoft.XMLDOM"); + xmldoc.async="false"; + xmldoc.validateOnParse = false; + xmldoc.loadXML(doc); + owldoc = xmldoc; + } + return owldoc; +}; + +/** @return false if belongs to this namespace, or an array with length two, arr[0] == url, arr[1] == id */ +jOWL.isExternal = function(resource){ + var r = jOWL.resolveURI(resource, true); + return r[0] != jOWL.namespace ? r : false; +}; + +/** +if a URI belongs to the loaded namespace, then strips the prefix url of, else preserves URI +also able to parse and reference html (or jquery) elements for their URI. +*/ +jOWL.resolveURI = function(URI, array){ + if(typeof URI != "string"){ + var node = URI.jquery ? URI.get(0) : URI; + URI = node.localName || node.baseName; + if(node.namespaceURI){ URI = node.namespaceURI + URI;} + return jOWL.resolveURI(URI, array); + } + var rs = URI, ns = jOWL.namespace; + if(URI.indexOf('http') === 0){ + var tr = URI.indexOf('#'); + if(tr <= 0){ tr = URI.lastIndexOf('/');} + if(tr > 0) + { + ns = URI.substring(0, tr+1); + rs = URI.substring(tr+1); + } + } else if(URI.charAt(0) == '#'){ return URI.substring(1);} + if(array){ return [ns, rs];} + if(ns == jOWL.namespace){ return rs;} + return URI; +}; + +/** +Main method to get an Ontology Object, access via jOWL(>String>, options); +resource: rdfID/rdfResource<String> or jQuery node. +*/ +jOWL.getResource = function(resource, options){ + if(!jOWL.document){ throw "You must successfully load an ontology before you can find anything";} + if(!resource){ throw "No resource specified";} + var node; + var opts = $.extend({}, options); + if(typeof resource == 'string'){ + resource = jOWL.resolveURI(resource); + if(resource == 'Thing' || resource == __.owl()+'Thing'){ return jOWL.Thing;} + if(opts.type == 'property' && jOWL.options.cacheProperties){ + var c = jOWL.index('property').get(resource); + if(c){ return c;} + if(jOWL.isExternal(resource)){ console.log("undeclared resource: "+resource); return new jOWL.Ontology.Property(resource);} + } + var match = jOWL.index("ID")[resource]; + if(!match){ //try case insensitive + for(caseIns in jOWL.index("ID")){ + if(caseIns.toLowerCase() == resource.replace(/ /g, "").toLowerCase()){ match = jOWL.index("ID")[caseIns]; break;} + } + } + if(!match){ + if(jOWL.isExternal(resource)){ + console.log("undeclared resource: "+resource); + return new jOWL.Ontology.Thing(resource); + } + console.log(resource+" not found"); + return null; + } + return match; + } + node = resource.jquery ? resource : $(resource); + var jj = jOWL.type(node); if(!jj){ return null;} + return new (jj)(node); +}; + +/** +* @param node jquery or html element. +* @return the ontology type of the object. +*/ +jOWL.type = function(node){ + var xmlNode = node.jquery ? node.get(0) : node; + switch(xmlNode.nodeName){ + case __.owl("Class") : return jOWL.Ontology.Class; + case __.rdfs("Class") : return jOWL.Ontology.Class; //test + case __.owl("Ontology") : return jOWL.Ontology; + case __.owl("ObjectProperty") : return jOWL.Ontology.ObjectProperty; + case __.owl("DatatypeProperty") : return jOWL.Ontology.DatatypeProperty; + case __.owl("FunctionalProperty") : return jOWL.Ontology.Property; + case __.rdf("Property") : return jOWL.Ontology.Property; + case __.owl("InverseFunctionalProperty") : return jOWL.Ontology.ObjectProperty; + case __.owl("TransitiveProperty") : return jOWL.Ontology.ObjectProperty; + case __.owl("SymmetricProperty") : return jOWL.Ontology.ObjectProperty; + //jOWL currently treats annotationproperties as string datatypeproperties. + case __.owl("AnnotationProperty") : return jOWL.Ontology.DatatypeProperty; + default : + switch(xmlNode.namespaceURI){ + case __.owl(): if(xmlNode.nodeName == __.owl("Thing") ){ return jOWL.Ontology.Individual;} return false; + case __.rdf(): return false; + case __.rdfs(): return false; + default : return jOWL.Ontology.Individual; + } + } +}; + +/** +@param rdfID <String> or Array<String> +@return Array of DOM (xml) Nodes +*/ +jOWL.getXML = function(rdfID){ + var node = []; + function fetchFromIndex(rdfID){ + var el = jOWL.index("ID")[rdfID]; + return el ? el : null; + } + + if(typeof rdfID == 'string'){ var q = fetchFromIndex(rdfID); if(q){ node.push(q);} } + else if(jOWL.priv.Array.isArray(rdfID)){ //assume an array of string rdfIDs + $.each(rdfID, function(){ + var el = fetchFromIndex(this); if(el){ node.push(el);} + }); + } + return node; +}; + +/** Create new ontology elements */ +jOWL.create = function(namespace, name, document){ + var doc = document ? document : jOWL.document; + + var el = { + attr : function(namespace, name, value){ + if($.browser.msie){ + var attribute = doc.createNode(2, namespace(name), namespace()); + attribute.nodeValue = value; + this.node.setAttributeNode(attribute); + } + else { this.node.setAttributeNS(namespace(), namespace(name), value);} + return this; + }, + appendTo : function(node){ + var n = node.node ? node.node : node; + n.appendChild(this.node); + return this; + }, + text : function(text, cdata){ + var txt = cdata ? doc.createCDATASection(text) : doc.createTextNode(text); + this.node.appendChild(txt); + return this; + } + }; + + if($.browser.msie){ el.node = doc.createNode(1, namespace(name), namespace());} + else { el.node = doc.createElementNS(namespace(), namespace(name));} + return el; +}; + +/** Create a blank ontology document */ +jOWL.create.document = function(href){ + var owl = []; + var base = href || window.location.href+"#"; + owl.push('<?xml version="1.0"?>'); + owl.push('<'+__.rdf('RDF')+' xml:base="'+base+'" xmlns="'+base+'" '+__()+'>'); + owl.push(' <'+__.owl('Ontology')+' '+__.rdf('about')+'=""/>'); + owl.push('</'+__.rdf('RDF')+'>'); + return jOWL.fromString(owl.join('\n')); +}; + +/** Extracts RDFa syntax from current page and feeds it to jOWL, simple implementation, only classes for the time being */ +jOWL.parseRDFa = function(fn, options){ + var entries = options.node ? $("[typeof]", options.node) : $("[typeof]"); + var doc = jOWL.create.document(); + + function property(p, node){ + var arr = []; + $("[property="+p+"]", node).each(function(){ arr.push($(this).attr('content') || $(this).html());}); + if(node.attr('property') === p){ arr.push(node.attr('content') || node.html());} + return arr; + } + + function rel(p, node){ + var arr = []; + $("[rel="+p+"]", node).each(function(){ arr.push($(this).attr('resource'));}); + if(node.attr("rel") === p){ arr.push(node.attr('resource'));} + return arr; + } + + function makeClass(node, ID){ + var cl = jOWL.create(__.owl, "Class", doc).attr(__.rdf, 'about', ID).appendTo(doc.documentElement); + + var parents = property(__.rdfs("subClassOf"), node).concat(rel(__.rdfs("subClassOf"), node)); + for(var i = 0;i<parents.length;i++){ + var p = jOWL.create(__.rdfs, "subClassOf", doc).attr(__.rdf, "resource", parents[i]).appendTo(cl); + } + return cl; + } + + entries.each(function(){ + var node = $(this); + var type = node.attr("typeof"), el; + + if(type == __.owl("Class")){ el = makeClass(node, jOWL.resolveURI(node.attr("about")));} + + $.each(property(__.rdfs('comment'), node), function(){ + jOWL.create(__.rdfs, "comment", doc).appendTo(el).text(this, true); + }); + + $.each(property(__.rdfs('label'), node), function(){ + jOWL.create(__.rdfs, "label", doc).appendTo(el).text(this); + }); + }); + jOWL.parse(doc, options); + fn(); +}; + +/** +Match part or whole of the rdfResource<String> +Used for term searches, intend to (partially) replace it by a sparql-dl query later on +options: + filter: filter on a specific type, possible values: Class, Thing, ObjectProperty, DatatypeProperty + exclude: exclude specific types, not fully implemented +*/ +jOWL.query = function(match, options){ + options = $.extend({exclude : false}, options); + if(options.filter == 'Class'){ options.filter = __.owl("Class");} + var that = this; + //filter : [], exclude : false + var items = new jOWL.Ontology.Array(); + var jsonobj = {}; + var test = jOWL.index("dictionary"); + + function store(item){ + var include = false, i = 0; + if(options.filter){ + if(typeof options.filter == 'string'){ include = (options.filter == item[3]);} + else { for(i = 0;i<options.filter.length;i++){ if(options.filter[i] == item[3]){ include = true;} } } + } + else if(options.exclude){ + include = true; + if(typeof options.exclude == 'string'){ include = (options.exclude !== item[3]);} + else { for(i = 0;i<options.exclude.length;i++){ if(options.exclude[i] == item[3]){ include = false;} } } + } + else { include = true;} + if(!include){ return;} + if(!jsonobj[item[1]]){ jsonobj[item[1]] = [];} + jsonobj[item[1]].push( { term : item[0], locale: item[2], type: item[3] }); + } + + for(var y = 0;y<test.length;y++){ + var item = test[y]; + var bool = options.exclude; + var r = item[0].searchMatch(match); + if(r > -1){ + if(options.locale){ if(options.locale == item[2]){ store(item);} } + else { store(item);} + } + } + return jsonobj; +}; + +/** +allows asynchronous looping over arrays (prevent bowser freezing). +arr the array to loop asynchonrously over. +options.modify(item) things to do with each item of the array +options.onUpdate array the size of chewsize or smaller, containing processed entries +options.onComplete(array of results) function triggered when looping has completed +*/ +jOWL.throttle =function(array, options){ + options = $.extend({ + modify : function(result){}, + //onUpdate : function(arr){}, + onComplete : function(arr){}, + async : true, + chewsize : 5, + startIndex : 0, + timing : 5 + }, options); + var temp = array.jOWL ? array.items : (array.jquery) ? $.makeArray(array) : array; + var items = options.startIndex ? temp.slice(startIndex) : temp.concat(); //clone the array + var results = []; + + (function(){ + var count = options.chewsize; + var a = []; + while (count > 0 && items.length > 0) + { + var item = items.shift(); count--; + var result = options.modify.call(item, item); + if(result){ results.push(result); a.push(result);} + } + if(options.onUpdate){ options.onUpdate(a);} + + if(items.length> 0){ + if(options.async){ setTimeout(arguments.callee, options.timing);} + else {arguments.callee();} + } + else{ options.onComplete(results);} + })(); +}; + +/** Creates a new resultobj for the SPARQL-DL functionality */ +jOWL.SPARQL_DL_Result = function(){ + this.assert = undefined; + this.head = {}; //associative array of query parameters, with value jOWL Array of results + this.results = []; //sparql-dl bindings + this.isBound = false; +}; + +jOWL.SPARQL_DL_Result.prototype = { + sort : function(param){ + if(!param){ throw "parameter must be defined for sort function";} + function sortResults(a, b){ + var o = a[param].name || a[param]; + var p = b[param].name || b[param]; + return (o < p) ? -1 : 1; + } + if(this.results){ this.results.sort(sortResults); } + }, + jOWLArray : function(param){ + if(!param){ throw "parameter must be defined for jOWLArray function";} + var arr = new jOWL.Ontology.Array(); + for(var i=0;i<this.results.length;i++){ + if(this.results[i][param]){ arr.pushUnique(this.results[i][param]);} + } + return arr; + }, + /** Filter head Parameters */ + filter : function(param, arr){ + if(this.head[param] === undefined){this.head[param] = arr;} + else { + var self = this; + this.head[param].filter(function(){ return (arr.contains(this));}); + arr.filter(function(){ return (self.head[param].contains(this));}); + } + }, + /** Update result section, results = SPARQL_DL_Array */ + bind : function(results){ + if(!this.isBound){//new results + this.results = this.results.concat(results.arr); + this.isBound = true; + return; + } + var multimapping = -1; + for(x in results.mappings){ multimapping++; } + var toAdd = []; + + for(x in results.mappings){ + var otherKeys; + if(multimapping){ + otherKeys = results.keyCentric(x); + } + for(var i = this.results.length-1;i>=0;i--){ + var valueX = this.results[i][x]; + if(valueX){ + if(!results.mappings[x].contains(valueX)){ + this.results.splice(i, 1); + continue; + } + if(multimapping){ + var keyArr= otherKeys[valueX.URI]; + //ignoring the opposite for now (assuming original key x is unique (limits statements)) + //TODO: improve these result merging methods/flexibility + for(var oK = 0; oK < keyArr.length;oK++){ + var obj = (oK === 0) ? this.results[i] : {}; + var valueY = keyArr[oK]; + obj[x] = valueX; + for(yK in valueY){ obj[yK] = valueY[yK]; } + toAdd.push(obj); + } + this.results.splice(i, 1); + } + } + } + } + this.results = this.results.concat(toAdd); + } +}; +/** Creates a new query for the SPARQL-DL functionality */ +jOWL.SPARQL_DL_Query = function(syntax, parameters){ + this.parse(syntax); + this.fill(parameters); + this.entries = this.entries.sort(this.sort); +}; + +jOWL.SPARQL_DL_Query.prototype = { + parse : function(syntax){ + var r2 = /(\w+)[(]([^)]+)[)]/; + var entries = syntax.match(/(\w+[(][^)]+[)])/g); + if(!entries){ this.error = "invalid abstract sparql-dl syntax"; return;} + entries = jOWL.priv.Array.unique(entries); + for(var i = 0;i<entries.length;i++){ + var y = entries[i].match(r2); + if(y.length != 3){ this.error = "invalid abstract sparql-dl syntax"; return;} + entries[i] = [y[1], y[2].replace(/ /g, "").split(',')]; + } + this.entries = entries; + }, + fill : function(parameters){ + for(var i = 0;i<this.entries.length;i++){ + for(var j =0; j<this.entries[i][1].length; j++){ + var p = parameters[this.entries[i][1][j]]; + if(p !== undefined) { this.entries[i][1][j] = p;} + else { + p = this.entries[i][1][j]; + if(p.charAt(0) != '?') + { + if(this.entries[i][0] == "PropertyValue" && j == 2) + { + var m = p.match(/^["'](.+)["']$/); + if(m && m.length == 2){ this.entries[i][1][j] = {test: m[1]}; break;} + } + this.entries[i][1][j] = jOWL(p); + if(this.entries[i][1][j] === null){this.entries.error = "a parameter in the query was not found"; return;} + } + } + } + } + }, + sort : function(a, b){ + var i; + if(a[1].length == 1){ return (b[0] == 'PropertyValue') ? 1 : -1;} + if(b[1].length == 1){ return (a[0] == 'PropertyValue') ? -1 : 1;} + var avar = 0; for(i = 0;i<a[1].length;i++){ if(typeof a[1][i] == 'string'){ avar++;} } + var bvar = 0; for(i = 0;i<a[1].length;i++){ if(typeof b[1][i] == 'string'){ bvar++;} } + if(avar != bvar){ return avar - bvar;} + if(a[0] == 'Type' && b[0] != 'Type'){ return -1;} + if(a[0] != 'Type' && b[0] == 'Type'){ return 1;} + return 0; + } +}; + +/** Private function */ +function _Binding(bindingarray){ + this.value = {}; + this.arr = bindingarray; +} + +_Binding.prototype = { + bind : function(key, value){ + this.value[key] = value; + if(!this.arr.mappings[key]){ this.arr.mappings[key] = new jOWL.Ontology.Array();} + this.arr.mappings[key].push(value); + return this; + } +}; + +/** Local Function, private access, Temp results */ +function SPARQL_DL_Array(keys){ + this.arr = []; + this.mappings = {}; + + if(keys){ + for(var i =0;i<keys.length;i++){ + if(keys[i]){this.mappings[keys[i]] = new jOWL.Ontology.Array();} + } + } +} + +SPARQL_DL_Array.prototype = { + add : function(binding){ + this.arr.push(binding.value); + return binding; + }, + push : function(key, value){ + var binding = new _Binding(this); + binding.bind(key, value); + this.arr.push(binding.value); + return binding; + }, + keyCentric : function(keyX){ + var arr = {}; + for(var i = this.arr.length-1;i>=0;i--){ + if(this.arr[i][keyX]){ + if(!arr[this.arr[i][keyX].URI]){ arr[this.arr[i][keyX].URI] = []; } + arr[this.arr[i][keyX].URI].push(this.arr[i]); + } + } + return arr; + }, + get : function(key) + { + return (this.mappings[key]) ? this.mappings[key] : new jOWL.Ontology.Array(); + }, + getArray : function(){ + //check mappings for presence, discard arr entries based on that, return remainder. + for(var i = this.arr.length - 1;i>=0;i--){ + var binding = this.arr[i], splice = false; + for(key in binding){ + if(!splice){ + splice = (!this.mappings[key] || !this.mappings[key].contains(binding[key])); + } + } + if(splice){ + this.arr.splice(i, 1); + } + } + return this; + } +}; + +/** +Support for abstract SPARQl-DL syntax +options.onComplete: function triggered when all individuals have been looped over +options.childDepth: depth to fetch children, default 5, impacts performance +options.chewsize: arrays will be processed in smaller chunks (asynchronous), with size indicated by chewsize, default 10 +options.async: default true, query asynchronously +parameters: prefill some sparql-dl parameters with jOWL objects +execute: start query, results are passed through options.onComplete +*/ +jOWL.SPARQL_DL = function(syntax, parameters, options){ + if(!(this instanceof arguments.callee)){ return new jOWL.SPARQL_DL(syntax, parameters, options);} + var self = this; + this.parameters = $.extend({}, parameters); + this.query = new jOWL.SPARQL_DL_Query(syntax, this.parameters).entries; + this.result = new jOWL.SPARQL_DL_Result(); + this.options = $.extend({onComplete: function(results){}}, options); +}; + +jOWL.SPARQL_DL.prototype = { + error: function(msg){ this.result.error = msg; return this.options.onComplete(this.result);}, + /** + if(options.async == false) then this method returns the result of options.onComplete, + no matter what, result is always passed in options.onComplete + */ + execute : function(options){ + var self = this; + this.options = $.extend(this.options, options); + if(this.query.error){ return this.error(this.query.error);} + + var resultobj = this.result; + var i = 0; + var loopoptions = $.extend({}, this.options); + loopoptions.onComplete = function(results){ i++; resultobj = results; loop(i);}; + + if(!this.query.length){ + resultobj.error = "no query found or query did not parse properly"; + return self.options.onComplete(resultobj); + } + + function loop(i){ + if(i < self.query.length){ + self.process(self.query[i], resultobj, loopoptions ); + } + else { + for(var j =0;j<resultobj.results.length;j++){ //Convert Literals into strings + var b = resultobj.results[j]; + for(x in b){ + if(b[x] instanceof jOWL.Literal){b[x] = b[x].name;} + } + } + return self.options.onComplete(resultobj); + } + } + loop(i); + }, + /** results are passed in the options.onComplete function */ + process: function(entry, resultobj, options){ + var self = this; + options = $.extend({chewsize: 10, async : true, onComplete : function(results){}}, options); + var q = entry[0]; + var sizes = { + "Type": [__.owl('Thing'), __.owl('Class')], + "DirectType": [__.owl('Thing'), __.owl('Class')], + "PropertyValue" : [false, false, false], + "Class": [false], + "Thing": [false], + "ObjectProperty": [false], + "DatatypeProperty": [false], + "SubClassOf" : [__.owl('Class'), __.owl('Class')], + "DirectSubClassOf" : [__.owl('Class'), __.owl('Class')] + }; + + if(!sizes[q]){ return self.error("'"+q+"' queries are not implemented");} + if(sizes[q].length != entry[1].length){ return self.error("invalid SPARQL-DL "+q+" specifications, "+sizes[q].length+" parameters required");} + for(var i = 0;i<entry[1].length;i++){ + var v = sizes[q][i]; + if(v){ + var m = entry[1][i]; + if(typeof m != 'string' && m.type != v){ return self.error("Parameter "+i+" in SPARQL-DL Query for "+q+" must be of the type: "+v);} + } + } + if(q == "DirectType"){ options.childDepth = 0; return self.fn.Type.call(self, entry[1], resultobj, options);} + else if(q == "DirectSubClassOf"){ options.childDepth = 1; return self.fn.SubClassOf.call(self, entry[1], resultobj, options);} + return self.fn[q].call(self, entry[1], resultobj, options); + }, + fn : { + "SubClassOf" : function(syntax, resultobj, options){ + var atom = new jOWL.SPARQL_DL.DoubleAtom(syntax, resultobj.head); + var results = new SPARQL_DL_Array(); + + if(atom.source.isURI() && atom.target.isURI()){//assert + if(resultobj.assert !== false){ + var parents = atom.source.value.ancestors(); + resultobj.assert = parents.contains(atom.target.value); + } + return options.onComplete(resultobj); + } + else if(atom.source.isURI()){//get parents + atom.source.value.ancestors().each(function(){ + results.push(atom.target.value, this); + }); + resultobj.filter(atom.target.value, results.get(atom.target.value)); + resultobj.bind(results.getArray()); + return options.onComplete(resultobj); + } + else if(atom.target.isURI()){//get children + atom.target.value.descendants(options.childDepth).each(function(){ + results.push(atom.source.value, this); + }); + resultobj.filter(atom.source.value, results.get(atom.source.value)); + resultobj.bind(results.getArray()); + return options.onComplete(resultobj); + } + else{//both undefined + return this.error('Unsupported SubClassOf query'); + } + }, + "Type" : function(syntax, resultobj, options){ + var atom = new jOWL.SPARQL_DL.DoubleAtom(syntax, resultobj.head); + + function addIndividual(cl){ + if(indivs[this.URI]){ return;} + var b = results.push(atom.source.value, this); + if(addTarget){ b.bind(atom.target.value, cl);} + indivs[this.URI] = true; + } + + function traverse(node, match){ + var a = node.parents(); + var found = false; + if(a.contains(match)){ found = true;} + else { + a.each(function(){ + if(this == jOWL.Thing){ return;} + if(!found && traverse(this, match)){ found = true;} }); + } + return found; + } + + if(atom.source.isURI() && atom.target.isURI()){//assert + return jOWL.SPARQL_DL.priv.assert(resultobj, function(){ + var cl = atom.source.value.owlClass(); + if(cl.URI == atom.target.value.URI){ return true;} + return traverse(cl, atom.target.value); + }, options.onComplete); + } + else if(atom.source.getURIs() && !atom.target.getURIs()){//get class + var results = new SPARQL_DL_Array(); + var addSource = !atom.source.isURI(); + var addTarget = !atom.target.isURI(); + atom.source.getURIs().each(function(){ + var b; + if(addTarget){ b = results.push(atom.target.value, this.owlClass());} + if(addSource){ + if(addTarget){ b.bind(atom.source.value, this);} + else {results.push(atom.source.value, this);} + } + }); + if(addSource){ resultobj.filter(atom.source.value, results.get(atom.source.value));} + if(addTarget){ resultobj.filter(atom.target.value, results.get(atom.target.value));} + resultobj.bind(results.getArray()); + return options.onComplete(resultobj); + } + else if(atom.target.getURIs()){//get Individuals, slow + var addTarget = !atom.target.isURI(); + var classlist = atom.target.getURIs(), + classes = {}, indivs = {}; + + var results = new SPARQL_DL_Array(); + + + classlist.each(function(){ //expand list of classes, not very fast! + if(classes[this.URI]){ return;} + var oneOf = this.oneOf(), cl = this; + if(oneOf.length){ oneOf.each(function(){ addIndividual.call(this, cl);});} + else{ this.descendants(options.childDepth).each(function(){ //this is the slower call + classes[this.URI] = true; + }); } + classes[this.URI] = true; + }); + + for(x in classes){ + var individuals = jOWL.index("Thing")[x]; + if(individuals){ + var cl = jOWL.index('ID')[x]; + if(options.onUpdate){ options.onUpdate(individuals);} + individuals.each(function(){ + addIndividual.call(this, cl); + }); + } + } + resultobj.filter(atom.source.value, results.get(atom.source.value)); + resultobj.bind(results.getArray()); + return options.onComplete(resultobj); + } + return this.error('Unsupported Type query'); + }, + "Thing" : function(syntax, resultobj, options){ + jOWL.SPARQL_DL.priv.IDQuery(syntax[0], "isThing", resultobj, options); + }, + "Class" : function(syntax, resultobj, options){ console.log('cl'); + jOWL.SPARQL_DL.priv.IDQuery(syntax[0], "isClass", resultobj, options); + }, + "ObjectProperty" : function(syntax, resultobj, options){ + jOWL.SPARQL_DL.priv.PropertyQuery(syntax[0], jOWL.index("property").items, "isObjectProperty", resultobj, options); + }, + "DatatypeProperty" : function(syntax, resultobj, options){ + jOWL.SPARQL_DL.priv.PropertyQuery(syntax[0], jOWL.index("property").items, "isDatatypeProperty", resultobj, options); + }, + "PropertyValue" : function(syntax, resultobj, options){ + var atom = new jOWL.SPARQL_DL.TripleAtom(syntax, resultobj.head); + + if(atom.source.isURI() && atom.property.isURI() && atom.target.isURI()){//assert + if(resultobj.assert !== false){ + jOWL.SPARQL_DL.priv.PropertyValuegetSourceInfo(atom.source.value, atom.property.value, atom.target.value, resultobj, { assert : true }); + } + return options.onComplete(resultobj); + } + + if(!atom.source.getURIs()){ + jOWL.SPARQL_DL.priv.IDQuery(atom.source.value, ["isClass", "isThing"], resultobj, options); + return; + } + var filterTarget = atom.target.isVar() ? atom.target.value : false; + var filterProperty = atom.property.isVar() ? atom.property.value : false; + var filterSource = atom.source.isVar() ? atom.source.value : false; + jOWL.SPARQL_DL.priv.PropertyValuegetSourceInfo(atom.source.getURIs(), atom.property.getURIs(), atom.target.getURIs(), resultobj, + { + filterTarget : filterTarget, filterProperty : filterProperty, filterSource : filterSource + }); + return options.onComplete(resultobj); + } + } +}; + +jOWL.SPARQL_DL.priv = { + assert : function(resultobj, fn, onComplete){ + if(resultobj.assert !== false){ + resultobj.assert = fn(); + } + onComplete(resultobj); + }, + //reusable function + PropertyValuegetSourceInfo : function(jSource, property, target, resultobj, options){ + if(!(jSource.isArray)){ + return jOWL.SPARQL_DL.priv.PropertyValuegetSourceInfo(new jOWL.Ontology.Array([jSource]), property, target, resultobj, options); + } + + options = $.extend({}, options); + var results = new SPARQL_DL_Array([options.filterSource, options.filterProperty, options.filterTarget]), + match = false; + jSource.each(function(){ + var source = this; + if(target && target.isArray && target.length == 1){ + var literal = target.get(0).test; + if(literal){ target = literal;}//unwrap literal expressions + } + var restrictions = source.sourceof(property, target); + if(options.assert){ + if(restrictions.length > 0){ match = true;} + return; + } + if(!restrictions.length){ return;} + restrictions.each(function(){ + var binding = new _Binding(results); + if(options.filterSource){ + binding.bind(options.filterSource, source); + if(!options.filterProperty && !options.filterTarget){ results.add(binding); return false;} + } + if(options.filterProperty){ + binding.bind(options.filterProperty, this.property); + } + if(options.filterTarget){ + binding.bind(options.filterTarget, this.getTarget()); + } + results.add(binding); + }); + return true; + }); + if(options.assert){ + resultobj.assert = match; + return resultobj.assert; + } + if(options.filterSource){ resultobj.filter(options.filterSource, results.get(options.filterSource));} + if(options.filterProperty){ resultobj.filter(options.filterProperty, results.get(options.filterProperty));} + if(options.filterTarget) { resultobj.filter(options.filterTarget, results.get(options.filterTarget));} + resultobj.bind(results.getArray()); + }, + hasClassID: function(match, classID){ + if(Object.prototype.toString.call(classID) === '[object Array]'){ + for(var i =0;i<classID.length;i++){ + if(match[classID]){ return true;} + } + } else if(match[classID]){ return true;} + return false; + }, + IDQuery : function(parameter, classID, resultobj, options){ + var atom = new jOWL.SPARQL_DL.Atom(parameter, resultobj.head); + if(atom.isURI()){ + return jOWL.SPARQL_DL.priv.assert(resultobj, function(){ + return jOWL.SPARQL_DL.priv.hasClassID(atom.getURIs().get(0), classID); + }, options.onComplete); + } + var results = new SPARQL_DL_Array(); + for(x in jOWL.index("ID")){ + var match = jOWL.index("ID")[x]; + if(jOWL.SPARQL_DL.priv.hasClassID(match, classID)){ results.push(parameter, match);} + } + resultobj.filter(parameter, results.get(parameter)); + resultobj.bind(results.getArray()); + options.onComplete(resultobj); + }, + PropertyQuery : function(parameter, index, className, resultobj, options){ + var atom = new jOWL.SPARQL_DL.Atom(parameter, resultobj.head); + if(atom.isURI()){ + return jOWL.SPARQL_DL.priv.assert(resultobj, function(){ + return jOWL.SPARQL_DL.priv.hasClassID(atom.getURIs().get(0), className); + }, options.onComplete); + } + var results = new SPARQL_DL_Array(); + var tr = new jOWL.throttle(index, $.extend({}, options, { + modify : function(result){ + if(!result.jOWL){ result = jOWL(result);} + if(jOWL.SPARQL_DL.priv.hasClassID(result, className)){results.push(parameter, result);} + return false; + }, + onComplete : function(){ + resultobj.filter(parameter, results.get(parameter)); + resultobj.bind(results.getArray()); + options.onComplete(resultobj); + } + })); + } +}; + +jOWL.SPARQL_DL.TripleAtom = function(syntax, store){ + this.source = new jOWL.SPARQL_DL.Atom(syntax[0], store); + this.property = new jOWL.SPARQL_DL.Atom(syntax[1], store); + this.target = new jOWL.SPARQL_DL.Atom(syntax[2], store); +}; + +jOWL.SPARQL_DL.DoubleAtom = function(syntax, store){ + this.source = new jOWL.SPARQL_DL.Atom(syntax[0], store); + this.target = new jOWL.SPARQL_DL.Atom(syntax[1], store); +}; + + +jOWL.SPARQL_DL.Atom = function(syntax, store){ + this.value = syntax; + this.type = 0; + if(typeof syntax == 'string'){ + if(syntax.indexOf('?') === 0){ + this.type = this.VAR; + if(store && store[syntax]){ this.mappings = store[syntax];} + } else { + this.type = this.LITERAL; + } + } else { + this.type = this.URI; + } +}; + +jOWL.SPARQL_DL.Atom.prototype = { + URI : 1, LITERAL : 2, VAR : 3, + getURIs : function(){ + if(this.isURI()){return new jOWL.Ontology.Array([this.value]);} + return this.mappings; + }, + isVar : function(){return this.type == this.VAR;}, + isLiteral : function(){return this.type == this.LITERAL;}, + isURI : function(){ return this.type == this.URI;} +}; + +/** +* @return Associative array of parameters in the current documents URL +*/ +jOWL.getURLParameters = function(){ + var href = window.location.href.split("?", 2), param = {}; + if(href.length == 1){ return {};} + var qstr = href[1].split('&'); + for(var i =0;i<qstr.length;i++){ + var arr = qstr[i].split("="); + if(arr.length == 2){ param[arr[0]] = arr[1];} + } + return param; +}; + +/** +Without arguments this function will parse the current url and see if any parameters are defined, returns a JOWL object +@return With argument it will return a string that identifies the potential permalink fr the given entry +*/ +jOWL.permalink = function(entry){ + if(!entry){ + var param = jOWL.getURLParameters(); + if(param.owlClass){ return jOWL(unescape(param.owlClass));} + } + else { + if(!entry.URI){ return false;} + var href = window.location.href.split("?", 2); + if(window.location.search){ href = href[0];} + if(entry.isClass){ return href+'?owlClass='+escape(entry.URI);} + } + return false; +}; + +/** Convert an item into Manchester syntax, currently only for oneOf +* @return String +*/ +jOWL.Manchester = function(owlElement){ + var syntax = []; + if(owlElement.isClass){ + var oneOf = owlElement.oneOf().map(function(){ return this.label();}); + if(oneOf.length){ syntax.push("{ "+oneOf.join(", ")+" }");} + } + return syntax.join(", "); +}; + +})(jQuery); + +/** +* @return 1 for exact match, 0 for partial match, -1 for no match. +*/ +String.prototype.searchMatch = function(matchstring, exact){ + if(this.search(new RegExp(matchstring, "i")) > -1){ return 1;} //contained within + var c = 0; var arr = matchstring.match(new RegExp("\\w+", "ig")); + for(var i = 0;i<arr.length;i++){ if(this.search(arr[i]) > -1){ c++;} } + if(c == arr.length){ return 0;} //word shift + return -1; //nomatch +}; +/** +* @return Modified String. +*/ +String.prototype.beautify = function(){ + var e1 = new RegExp("([a-z0-9])([A-Z])", "g"); + var e2 = new RegExp("([A-Z])([A-Z0-9])([a-z])", "g"); + var e3 = new RegExp("_", "g"); + return this.replace(e1, "$1 $2").replace(e2, "$1 $2$3").replace(e3, " "); +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/browser/web/js/jOWL_UI.js Thu Oct 08 11:19:11 2009 +0000 @@ -0,0 +1,698 @@ +/* + * jOWL_UI, User Interface Elements for jOWL, semantic javascript library + * Creator - David Decraene + * Version 1.0 + * Website: http://Ontologyonline.org + * Licensed under the MIT license + * Verified with JSLint http://www.jslint.com/ + */ +(function($){ + +jOWL.UI = { + broadcaster : function(){ + var listeners = []; + this.addListener = function(obj){ + function add(obj){if(obj.propertyChange) { listeners.push(obj); } } + if(obj.constructor == Array){for(var i=0;i<obj.length;i++){ add(obj[i]); } } + else { add(obj); } + return this; + }; + this.broadcast = function(item){ for(var i=0;i<listeners.length;i++){ listeners[i].propertyChange.call(item, item); } }; + if(!this.propertyChange){ this.propertyChange = function(item){}; } + }, + asBroadcaster : function(ui_elem){ ui_elem.broadcaster = jOWL.UI.broadcaster; ui_elem.broadcaster(); }, + defaults : { + contentClass: "jowl-content", + focusClass: "ui-state-hover", + wrapperClass : "ui-widget-content" + } +}; + +/** +WIDGETS +all widgets: + addListener : add an object with a propertyChange function, will be triggered on select + propertyChange: update the widget with a new jowl object +Events: + onSelect: (look into propertylens click), return false to suppress the event, this = jquery element, first argument = jOWL object +CSS: + wrapperClass: css class(es) for main element, the el itself + contentClass: css class(es) for main content element, accessible by el.content + focusClass: css class(es) for element in focus, +*/ +$.fn.extend({ +/* +owl_navbar +options: + onSelect : this element refers to jquery node, first argument = jOWL object +*/ + owl_navbar: function(options){ + options = $.extend({ + contentClass : jOWL.UI.defaults.contentClass, + focusClass : jOWL.UI.defaults.focusClass, + onSelect : function(item){}, + onPropertyChange : function(item){} + }, options); + var self = this; + this.addClass("jowl-navbar"); + this.content = $("."+options.contentClass, this).empty(); + if(!this.content.length) { this.content = $("<div/>").addClass(options.contentClass).appendTo(this); } + this.parents = $('<div/>').appendTo(this.content); + this.focus = $('<div/>').addClass(options.focusClass).appendTo(this.content); + this.children = $('<div/>').appendTo(this.content); + var listnode = $('<span/>').click(function(){ + var node = $(this); + var res = jOWL(node.attr('title')); + if(options.onSelect.call(node, res) === false) { return; } + if(res && res.isClass) { self.propertyChange.call(res, res); self.broadcast(res); } + }); + + jOWL.UI.asBroadcaster(this); + + this.propertyChange = function(item){ + if(options.onPropertyChange.call(this, item) === false) { return; } + if(item.isClass){ + item.bind(self.focus); + if(jOWL.options.reason) { item.hierarchy();} + self.parents.empty().append(item.parents().bind(listnode)); + self.children.empty().append(item.children().bind(listnode)); + } + }; + return this; + }, +/** +autocomplete field. +*/ + owl_autocomplete : function(options){ + options = $.extend({ + time:500, //responsetime to check for new keystrokes, default 500 + chars:3, //number of characters needed before autocomplete starts searching + focus:false, //put cursor on the input field when loading + limit:10, //limit size of result list to given amount + contentClass : "ui-widget-content", + focusClass : jOWL.UI.defaults.focusClass, + hintClass : "ui-autocomplete-hint", + hint: false, //Message (if any) to show when unfocused. + onSelect : function(item){}, //function that can be overridden + formatListItem : function(listitem, type, identifier, termarray){ //formatting of results, can be overridden + if(type){ listitem.append($('<div class="type"/>').text(type)); } + listitem.append($('<div class="name"/>').text(identifier)); + if(termarray.length) { listitem.append($('<div class="terms"/>').text(termarray.join(', ')) + .prepend($('<span/>').addClass('termlabel').text("Terms: "))); + } + }}, options); + jOWL.UI.asBroadcaster(this); + + this.showHint = function(){ + this.hinted = true; + if(options.hint){ + this.addClass(options.hintClass).val(options.hint); + } + else {this.val(''); } + }; + this.showHint(); + + var self = this; var old = ''; var open = false; self.val(''); + var results = $('<ul/>').addClass(options.contentClass).addClass("jowl_autocomplete_results"); + var div = $("<div/>").addClass(options.wrapperClass).append(results); this.after(div); + results.cache = {}; + results.isEmpty = function(){ for(x in results.cache) { return false; } return true; }; + results.close = function(){this.hide();}; + results.open = function(q, cache){ + this.show(); + if(q){ + if(!cache || results.isEmpty()) { results.cache = jOWL.query(q, options); } + else { + var newcache = {}; + for(x in results.cache){ + var entry = results.cache[x]; + var found = false; + var newentries = []; + if(x.searchMatch(q) > -1) { found = true; } + for(var i = 0;i<entry.length;i++){ + if(entry[i].term.searchMatch(q) > -1) { found = true; newentries.push(entry[i]); } + } + if(found) { newcache[x] = newentries; } + } + results.cache = newcache; + } + this.populate(results.cache); + } + }; + + results.populate = function(data){ + var res = this; this.empty(); var count =0; + var clickFunction = function(){ + var node = $(this), res = jOWL(node.data("jowltype")); + if(options.onSelect.call(node, res) === false) { return; } + self.broadcast(res); + }; + + var onHover = function(){ $(this).addClass(options.focusClass); }; + var offHover = function(){$(this).removeClass(options.focusClass);}; + + for(x in data){ + if(count < options.limit){ + var item = data[x]; + var v = jOWL.isExternal(x); + v = v ? v[1] : x; + var list = $('<li/>').data("jowltype", x) + .click(clickFunction).hover(onHover, offHover) + .appendTo(res); + var terms = []; + for(var l = 0;l<item.length;l++){ + var found = false; var newterm = item[l].term; + for(var y=0; y < terms.length;y++){ if(terms[y].toLowerCase() == newterm.toLowerCase()) { found = true; } } + if(!found) { terms.push(newterm); } + } + options.formatListItem.call(list, list, item[0].type, v, terms); + + } + count++; + } + }; + + setInterval(function(){ + var newvalue = self.val(); + var cache = true; + if(old != newvalue){ + var longervalue = newvalue.length > old.length && newvalue.indexOf(old) === 0; + if(!old) { cache = false; } + old = newvalue; + if(newvalue.length < options.chars && open){ results.close();open = false;} + else if(newvalue.length >=options.chars && newvalue.length > 0){ + if(cache) { cache = longervalue && newvalue.length > options.chars; } + results.open(newvalue, cache); + open = true; + } + + } + }, options.time); + + self.bind('keyup', function(){ if(!this.value.length){ results.close(); open = false; } }); + self.bind('blur', function(){ + if(open){setTimeout(function(){results.close();}, 200);open = false;} + if(!self.val().length){self.showHint();} + }); + //timeout for registering clicks on results. + self.bind('focus', function(){ + if(self.hinted){ + self.hinted = false; + $(this).removeClass(options.hintClass).val(''); + } + if(self.val().length && !open){results.open('', open);open = true;}}); + //reopen, but do not get results + return this; + }, +/** +Tree View +*/ + owl_treeview : function(options){ + options = $.extend({ + contentClass : jOWL.UI.defaults.contentClass, + focusClass: "focus", + nameClass: "name", + treeClass: "jowl-treeview", + rootClass: "root", + onSelect : function(item){}, //function that can be overwritten to specfy behavior when something is selected + rootThing : false, //if true then topnode is (owl) 'Thing' + isStatic : false, // if static then selections will refresh the entire tree + addChildren : false //add a given objects children to the treeview as well + }, options); + + /** construct the hierarchy & make a tree of it */ + function TreeModel(owlobject){ + + function clear(el){ //reset invParents, for later use. + if(el.parents) { el.parents().each(function(){ + this.invParents = null; clear(this); + }); } + } + + function leaf(node){ + node.jnode.addClass(options.focusClass); + if(options.addChildren){ + var entry = jOWL(node.$name.attr('title')); + if(entry && entry.children){ entry.children().each(function(){ node.add(this); }); } } + } + + function traverse(itemarray, appendto){ + if(!itemarray) { return; } + itemarray.each(function(){ + var node = appendto.add(this); + if(this.invParents){ traverse(this.invParents, node); } + else { leaf(node); } + }); + + } + + var h = owlobject.hierarchy(true); + if(options.rootThing) { traverse(h, tree.root(jOWL("Thing"))); } + else { + var root = tree.root(h); + for(var i=0;i<root.length;i++){ + traverse(root[i].invParents, root[i]); + if(!root[i].invParents) { leaf(root[i]); } + } + + } + clear(owlobject); + + } + + /** + var tree = $(selector).owl_treeview(); + var root = tree.root("node"); + root.add("node2").add("child"); + */ + function Tree(node, treemodel, options){ + var rack = $('<ul/>').addClass(options.treeClass).appendTo(node); + var tree = this; + /**item can be text, a jOWL object, or a jOWL array */ + this.root = function(item){ + var rt = null; //root + rack.empty(); + if(item && item.each) { + rt = []; + item.each(function(it){ + var x = new fn.node(it, true); + x.wrapper.addClass("tv"); + x.jnode.appendTo(rack); + x.invParents = it.invParents; it.invParents = null; //reset for later use + rt.push(x); + }); + return rt; + } + rt = new fn.node(item, true); + rt.wrapper.addClass("tv"); + rt.jnode.appendTo(rack); + return rt; + }; + + var fn = {}; + fn.node = function(text, isRoot){ //creates a new node + this.jnode = isRoot ? $('<li/>').addClass(options.rootClass) : $('<li class="tvi"/>'); + this.$name = null; + if(text){ + this.$name = $('<span/>').addClass(options.nameClass); + if(typeof text == "string") { this.$name.html(text); } + else if(text.bind) { text.bind(this.$name); } + var n = this.$name; + this.$name.appendTo(this.jnode).click(function(){ + var entry = jOWL(n.attr('title')); + if(entry && options.onSelect.call(n, entry) === false) { return; } + tree.broadcast(entry); + if(options.isStatic) { tree.propertyChange(entry); } + return false;}); + } + + this.wrapper = $('<ul/>').appendTo(this.jnode); + var self = this; + self.jnode.click(function(){toggle(); return false;}); + + this.add = function(text){ + var nn = new fn.node(text); + if(!self.wrapper.children().length) { toNode(); } + else { + var lastchild = self.wrapper.children(':last'); + lastchild.swapClass("tvilc", "tvic"); + lastchild.swapClass("tvile", "tvie"); + lastchild.swapClass("tvil", "tvi"); + + }//children - change end of list + self.wrapper.append(nn.jnode.swapClass('tvi', 'tvil')); + return nn; + }; + + function toggle(){ + var t = self.jnode.hasClass("tvic") || self.jnode.hasClass("tvie") || self.jnode.hasClass("tvilc") || self.jnode.hasClass("tvile"); + if(!t) { return; } + self.jnode.swapClass('tvic', 'tvie'); self.jnode.swapClass('tvilc', 'tvile'); + self.wrapper.slideToggle(); + } + + function toNode(){ + self.jnode.swapClass('tvil', 'tvilc'); + self.jnode.swapClass('tvi', 'tvic'); + } + }; + return this; + }// end Tree + + this.addClass("jowl-tree"); + this.content = $("."+options.contentClass, this).empty(); + if(!this.content.length){ this.content = $('<div/>').addClass(options.contentClass).appendTo(this); } + var tree = new Tree(this.content, null, options); + jOWL.UI.asBroadcaster(tree); + tree.propertyChange = function(item){ if(item.isClass) { var m = new TreeModel(item); } }; + return tree; + }, +/** Uses templating +options: +onChange: owl:Class, owl:Thing, etc..., tell the widget what to do with the different kinds of Ontology Objects. +"data-jowl" : {split: ", ", "somevariable" : function_triggered_for_each_result } + example: "rdfs:label" : {split: ", ", "rdfs:label" : function(){ //'this' keyword refers to HTML element}} ) + example: "sparql-dl:PropertyValue(owl:Class, ?p, ?x)" : {"?p": function(){ //'this' keyword refers to HTML element }} + //prefil: for sparql-dl queries + //onComplete: function to trigger when the specific propertybox query is completed, this refers to the HTML element for propertybox + //sort: sort results on specified parameter, for sparql-dl results. +onUpdate: called when the widget updates itself +*/ + owl_propertyLens : function(options){ + var self = this; + self.options = $.extend({ + backlinkClass : "backlink", + split: {}, + disable : {}, + click : {}}, + options); + self.resourcetype = this.attr('data-jowl') || "owl:Class"; + var propertyboxes = []; + $('.propertybox', this).each(function(){ + var node = new jOWL.UI.PropertyBox($(this), self); + propertyboxes.push(node); + node.el.hide(); + }); + var backlink = $('.backlink', this).hide(); + if(!backlink.length) { backlink = $('<div class="jowl_link"/>').addClass(self.options.backlinkClass).text("Back").hide().appendTo(this); } + jOWL.UI.asBroadcaster(this); + + /** fn: optional function to execute*/ + this.link = function(source, target, htmlel, fn){ + htmlel.addClass("jowl_link").click(function(){ + if(fn) { fn(); } + self.broadcast(target); + self.propertyChange(target); + backlink.source = source.name; + backlink.show().unbind('click').click(function(){ + self.broadcast(source); self.propertyChange(source); backlink.hide(); + }); + + }); + + }; + + var action = { + "rdfs:label": function(item){ return [{"rdfs:label": item.label() }]; }, + "rdf:ID" : function(item){ return [{"rdf:ID": [item.name, item] }]; }, + "rdfs:comment" : function(item){ + return $.map(item.description(), function(n){return {"rdfs:comment":n }; }); + }, + "rdf:type" : function(item){ + if(item.owlClass) { return [{"rdf:type": item.owlClass() }]; } + return [{"rdf:type": item.type }]; + }, + "term" : function(item){ + return $.map(item.terms(), function(n, i){ return { "term" : n[0] }; }); + }, + "rdfs:range": function(item){if(item.range) { return [{"rdfs:range": item.range }]; } }, + "rdfs:domain": function(item){if(item.domain) { return [{"rdfs:domain": item.domain }]; } }, + "permalink": function(item){ + var href = jOWL.permalink(item); + return [{"permalink": "<a href='"+href+"'>Permalink</a>" }]; + }, + "owl:disjointWith": function(item){ + if(!(item.isClass)) { return; } + return $.map( + jOWL.Xpath('*', item.jnode) + .filter(function(){return this.nodeName == "owl:disjointWith"; }), + function(n, i){ return {"owl:disjointWith": jOWL($(n).RDF_Resource())}; + }); + }, + "default" : function(item){ + var type = this.attr("data-jowl"); + return $.map( + jOWL.Xpath('*', item.jnode).filter(function(){return this.nodeName == type; }), + function(n, i){ var x = {}; x[type] = $(n).text(); return x; } + ); + } + }; + + this.propertyChange = function(item){ + if(!item) { return; } + self.property = item; + if(backlink.source != item.name) { backlink.hide(); } else { backlink.source = false; } + + if(item.type != self.resourcetype){ + if(item.isDatatypeProperty && self.resourcetype == "rdf:Property") {} + else if(item.isObjectProperty && self.resourcetype == "rdf:Property"){} + else { return; } + } + + for(var i = 0;i<propertyboxes.length;i++){ + var pbox = propertyboxes[i]; + pbox.clear(); + if(!pbox.actiontype){return; } + var actiontype = pbox.actiontype; + if(self.options.disable[actiontype]) { return; } + + if(!self.options[actiontype]) { self.options[actiontype] = {}; } + + if(actiontype.indexOf("sparql-dl:") === 0){ + var query = actiontype.split("sparql-dl:", 2)[1]; + var fill = {}; fill[self.resourcetype] = item; + if(self.options[actiontype].prefill) { $.extend(fill, self.options[actiontype].prefill); } + var qr = new jOWL.SPARQL_DL(query, fill).execute({onComplete : function(r){ + if(self.options[actiontype].sort) { r.sort(self.options[actiontype].sort); } + pbox.setResults(r.results, item); + }}); + } + else { + var choice = (action[actiontype]) ? actiontype : "default"; + var results = action[choice].call(pbox.valuebox, item); + pbox.setResults(results, item); + } + } + + if(self.options.onUpdate) { self.options.onUpdate.call(this, item); } + }; //end property change + + if(self.options.tooltip){ + var lens = this.remove(); + this.display = function(element, htmlel){ + htmlel.tooltip({ + title: element.label(), + html: function(){ lens.propertyChange(element); backlink.hide(); return lens.get(0); } + }); + }; + } + return this; + }, + +/** +Use propertyChange to set the class +Use addField to add property refinements +Use serialize to serialize input +*/ + owl_datafield: function(options){ + options = $.extend({ + selectorClass : "jowl-datafield-selector", + messageClass : "jowl-datafield-message", + labelClass : "jowl-datafield-property-label" + }, options); + var self = this; + var pArray = {}; //associative array for properties. + this.messages = {}; + this.messages[jOWL.NS.xsd()+"positiveInteger"] = "Allowed values: positive numbers or comparisons like '>5 && <15' "; + + this.addClass("owl_UI"); + jOWL.UI.asBroadcaster(this); + + this.property = null; + + this.propertyChange = function(item){ + if(item.isClass){ + this.property = item; + for(x in pArray){//reset all properties + if(pArray[x].remove){ pArray[x].remove(); delete pArray[x]; } + } + } + }; + + /** Sets up a new field */ + this.addField = function(property){ + if(pArray[property.URI]){ + //allow for multiple fields? + return; + } + + var $content = $("<div/>"); + pArray[property.URI] = $content; + + var $title = property.bind($("<div/>")).addClass(options.labelClass).appendTo($content).click(function(){ $content.remove(); delete pArray[property.URI]; }); + + if(property.isObjectProperty){ + + var sp = new jOWL.SPARQL_DL("Type(?t, ?c),PropertyValue(concept, property, ?c)", {concept : self.property, property : property }).execute({ + onComplete : function(obj){ + if(!obj.results.length){ return; } //modify to deal with non value results + obj.sort("?t"); + + $select = $("<select class='"+options.selectorClass+"'/>").appendTo($content); + + for(var i=0;i<obj.results.length;i++){ + obj.results[i]['?t'].bind($("<option/>")).appendTo($select); + } + + $content.appendTo(self); + }}); + + } + else if(property.isDatatypeProperty){ + var msg =""; + if(self.messages[property.range]){ msg = self.messages[property.range]; } + + var $input = $('<div/>').addClass(options.selectorClass).attr("title", property.range).append($('<input type="text" style="font-size:11px;width:100px;"/>')); + var $message = $('<div/>').addClass(options.messageClass).text(msg).appendTo($input); + + $content.append($input).appendTo(self); + $('input', $content).focus(function(){ + $message.animate({opacity: 1.0}, 1500).fadeOut(); + }); + + + } + + }; + + this.serialize = function(){ + var q = { "Type": self.property, "PropertyValue" : [] }; + + $('.'+options.selectorClass, self).each(function(){ + var $this = $(this); + var $prop = $this.siblings('.'+options.labelClass); + var prop = $prop.attr('title'); + if( $this.is("select")){ + var s = $this.get(0); + var thing = $(s[s.selectedIndex]).attr('title'); + q.PropertyValue.push([prop, thing]); + } + else { + var $input = $this.find("input"); + var datatype = $this.attr('title'); + var entry = $input.get(0).value; + if(entry) { q.PropertyValue.push([prop, '"'+entry+'"']); } + } + }); + return q; + }; + + return this; + } +}); + +/** Used by owl_PropertyLens */ +jOWL.UI.PropertyBox = function($el, resourcebox){ + var v = $('[data-jowl]', $el); + if(v.length){ this.descendant = true;} + + this.el = $el; + this.resourcebox = resourcebox; + this.valuebox = v.length ? v : $el; + this.actiontype = this.valuebox.attr('data-jowl'); +}; + +jOWL.UI.PropertyBox.prototype = { + setResults : function(results, item){ + var nodes = jOWL.UI.Template(results, this.valuebox, this.resourcebox.options[this.actiontype].split); + this.complete(nodes, item); + if(nodes && nodes.length && this.descendant) { this.el.show(); this.valuebox.hide(); } + if(this.resourcebox.options[this.actiontype].onComplete) { this.resourcebox.options[this.actiontype].onComplete.call(this.el.get(0)); } + }, + complete : function(nodes, item){ + var res = this.resourcebox; + if(!nodes || !nodes.length) { return; } + var v = $.data(nodes, "parameters"); + for(x in v){ + if(v[x].length && typeof res.options[this.actiontype][x] == "function") { + v[x].each(res.options[this.actiontype][x]); + }} + for(x in res.options.onChange){ + var data = $('[typeof='+x+']', nodes).add(nodes.filter('[typeof='+x+']')); + if(x.charAt(0) == "." || x.charAt(0) == "#"){ data = data.add($(x, nodes));} + data.each(function(){ + var node = $(this); + $.data(node, 'data-jowl', x); + var id = node.attr('title'); + if(id != "anonymousOntologyObject") { res.options.onChange[$.data(node, 'data-jowl')].call(node, item, jOWL(id), res); } + }); + } + }, + clear : function(){ + var prev = this.valuebox.prev('.jowl-template-result'); + if(!prev.length){ prev = this.valuebox.prev('.jowl-template-splitter');} + if(prev.length) { prev.remove(); this.clear(this.valuebox); } + } +}; + +/**arr: associative array of variablrd, jqel: node for which variables need to be substituted, */ +jOWL.UI.Template = function(arr, jqel, splitter){ + var options = { + resultClass : "jowl-template-result", + splitterClass : "jowl-template-splitter" + }; + if(!arr) { return; } + + function bindObject(value, jnode){ + var bound = false; + if(!value) { return false; } + if(typeof value == 'string') { jnode.html(value); bound = true;} + else if(value.constructor == Array){ + if(value.length == 2) { value[1].bind(jnode).text(value[0]); bound = true; } + } + else if(value.bind){ value.bind(jnode); bound = true; } + return bound; + } + var count = 0, a = [], b = {}; + var remnantFn = function(){ + var txt = $(this).text(); + if(txt.indexOf('${') === 0 && txt.lastIndexOf('}') == txt.length-1 ) { $(this).hide(); } + }; + for(var i=0;i<arr.length;i++){ + var x = jqel.clone(true).wrapInner("<"+jqel.get(0).nodeName+" class='"+options.resultClass+"'/>").children(); + /** copy style settings */ + x.addClass(jqel.attr('class')).removeClass('propertybox'); + /** accepted obj types= string, array["string", "jowlobject"], jowlobject*/ + for(obj in arr[i]){ + if(!b[obj]) { b[obj] = []; } + var occurrences = $(':contains(${'+obj+'})', x); + if(!occurrences.length){ + if(x.text() == "${"+obj+"}") { if(bindObject(arr[i][obj], x)) { + count++; b[obj].push(x.get(0)); + }} + } + else { + occurrences.each(function(){ + if(this.innerHTML == "${"+obj+"}") { var node = $(this); if(bindObject(arr[i][obj], node)) { count++; b[obj].push(this); } } + }); + } + } + var remnants = $(':contains(${)', x); //hide parameters that weren't substituted + remnants.each(remnantFn); + if(count){ + x.insertBefore(jqel); + a.push(x.get(0)); + if(count > 1 && splitter) { + $splitter = (splitter.indexOf('<') === 0) ? $(splitter) : $("<span/>").text(splitter); + $splitter.addClass(options.splitterClass).insertBefore(x); + } + } + } + for(x in b){ if(b[x].length) { b[x] = $(b[x]); } } + var nodes = $(a); + $.data(nodes, "parameters", b); + return nodes; +}; + +/** +Supporting functionality +*/ + +$.fn.swapClass = function(c1,c2) { + return this.each(function() { + if ($(this).hasClass(c1)) { $(this).removeClass(c1); $(this).addClass(c2);} + else if ($(this).hasClass(c2)) {$(this).removeClass(c2);$(this).addClass(c1);} + }); +}; + +})(jQuery); \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/browser/web/js/jquery.dataTables.js Thu Oct 08 11:19:11 2009 +0000 @@ -0,0 +1,4522 @@ +/* + * File: jquery.dataTables.js + * Version: 1.5.2 + * CVS: $Id$ + * Description: Paginate, search and sort HTML tables + * Author: Allan Jardine (www.sprymedia.co.uk) + * Created: 28/3/2008 + * Modified: $Date$ by $Author$ + * Language: Javascript + * License: GPL v2 or BSD 3 point style + * Project: Mtaala + * Contact: allan.jardine@sprymedia.co.uk + * + * Copyright 2008-2009 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 pleease 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,_fnReadCookie,_fnProcessingDisplay,_fnDraw,_fnSort,_fnReDraw,_fnDetectType,_fnSortingClasses,_fnSettingsFromNode,_fnBuildSearchArray,_fnCalculateEnd,_fnFeatureHtmlProcessing,_fnFeatureHtmlPaginate,_fnFeatureHtmlInfo,_fnFeatureHtmlFilter,_fnFilter,_fnSaveState,_fnFilterColumn,_fnEscapeRegex,_fnFilterComplete,_fnFeatureHtmlLength,_fnGetDataMaster,_fnVisibleToColumnIndex,_fnDrawHead,_fnAddData,_fnGetTrNodes,_fnColumnIndexToVisible,_fnCreateCookie,_fnAddOptionsHtml,_fnMap,_fnClearTable,_fnDataToSearch,_fnReOrderIndex,_fnFilterCustom,_fnVisbleColumns,_fnAjaxUpdate,_fnAjaxUpdateDraw,_fnColumnOrdering,fnGetMaxLenString*/ + +(function($) { + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * DataTables variables + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /* + * Variable: dataTableSettings + * Purpose: Store the settings for each dataTables instance + * Scope: jQuery.fn + */ + $.fn.dataTableSettings = []; + + /* + * Variable: dataTableExt + * Purpose: Container for customisable parts of DataTables + * Scope: jQuery.fn + */ + $.fn.dataTableExt = {}; + var _oExt = $.fn.dataTableExt; /* Short reference for fast internal lookup */ + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + */ + _oExt.sVersion = "1.5.2"; + + /* + * 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: 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", + "sPageButtonStaticActive": "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", + "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ + "sSortJUIAsc": "", + "sSortJUIDesc": "", + "sSortJUI": "" + }; + + /* + * 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-state-default ui-corner-left", + "sPagePrevDisabled": "fg-button ui-state-default ui-corner-left ui-state-disabled", + "sPageNextEnabled": "fg-button ui-state-default ui-corner-right", + "sPageNextDisabled": "fg-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-state-default", + "sPageButtonActive": "fg-button ui-state-default ui-state-disabled", + "sPageButtonStaticActive": "fg-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 fg-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", + "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-triangle-2-n-s" + }; + + /* + * 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 + * function:fnCallbackDraw - draw function which must be called on update + */ + "fnInit": function ( oSettings, fnCallbackDraw ) + { + var nPaging = oSettings.anFeatures.p; + + /* Store the next and previous elements in the oSettings object as they can be very + * usful for automation - particularly testing + */ + if ( !oSettings.bJUI ) + { + oSettings.nPrevious = document.createElement( 'div' ); + oSettings.nNext = document.createElement( 'div' ); + } + else + { + oSettings.nPrevious = document.createElement( 'a' ); + oSettings.nNext = document.createElement( 'a' ); + + var nNextInner = document.createElement('span'); + nNextInner.className = oSettings.oClasses.sPageJUINext; + oSettings.nNext.appendChild( nNextInner ); + + var nPreviousInner = document.createElement('span'); + nPreviousInner.className = oSettings.oClasses.sPageJUIPrev; + oSettings.nPrevious.appendChild( nPreviousInner ); + } + + if ( oSettings.sTableId !== '' ) + { + nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' ); + oSettings.nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' ); + oSettings.nNext.setAttribute( 'id', oSettings.sTableId+'_next' ); + } + + oSettings.nPrevious.className = oSettings.oClasses.sPagePrevDisabled; + oSettings.nNext.className = oSettings.oClasses.sPageNextDisabled; + + oSettings.nPrevious.title = oSettings.oLanguage.oPaginate.sPrevious; + oSettings.nNext.title = oSettings.oLanguage.oPaginate.sNext; + + nPaging.appendChild( oSettings.nPrevious ); + nPaging.appendChild( oSettings.nNext ); + $(nPaging).insertAfter( oSettings.nTable ); + + $(oSettings.nPrevious).click( function() { + oSettings._iDisplayStart -= oSettings._iDisplayLength; + + /* Correct for underrun */ + if ( oSettings._iDisplayStart < 0 ) + { + oSettings._iDisplayStart = 0; + } + + fnCallbackDraw( oSettings ); + } ); + + $(oSettings.nNext).click( function() { + /* Make sure we are not over running the display array */ + if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() ) + { + oSettings._iDisplayStart += oSettings._iDisplayLength; + } + + fnCallbackDraw( oSettings ); + } ); + + /* Take the brutal approach to cancelling text selection */ + $(oSettings.nPrevious).bind( 'selectstart', function () { return false; } ); + $(oSettings.nNext).bind( 'selectstart', function () { return false; } ); + }, + + /* + * 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 which must be called on update + */ + "fnUpdate": function ( oSettings, fnCallbackDraw ) + { + if ( !oSettings.anFeatures.p ) + { + return; + } + + oSettings.nPrevious.className = + ( oSettings._iDisplayStart === 0 ) ? + oSettings.oClasses.sPagePrevDisabled : oSettings.oClasses.sPagePrevEnabled; + + oSettings.nNext.className = + ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) ? + oSettings.oClasses.sPageNextDisabled : oSettings.oClasses.sPageNextEnabled; + } + }, + + + /* + * Variable: iFullNumbersShowPages + * Purpose: Change the number of pages which can be seen + * Scope: jQuery.fn.dataTableExt.oPagination + */ + "iFullNumbersShowPages": 5, + + /* + * Variable: full_numbers + * Purpose: Full numbers pagination + * Scope: jQuery.fn.dataTableExt.oPagination + */ + "full_numbers": { + /* + * Function: oPagination.full_numbers.fnInit + * Purpose: Initalise dom elements required for pagination with a list of the pages + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * function:fnCallbackDraw - draw function which must be called on update + */ + "fnInit": function ( oSettings, fnCallbackDraw ) + { + var nPaging = oSettings.anFeatures.p; + var nFirst = document.createElement( 'span' ); + var nPrevious = document.createElement( 'span' ); + var nList = document.createElement( 'span' ); + var nNext = document.createElement( 'span' ); + var nLast = document.createElement( 'span' ); + + nFirst.innerHTML = oSettings.oLanguage.oPaginate.sFirst; + nPrevious.innerHTML = oSettings.oLanguage.oPaginate.sPrevious; + nNext.innerHTML = oSettings.oLanguage.oPaginate.sNext; + nLast.innerHTML = oSettings.oLanguage.oPaginate.sLast; + + var oClasses = oSettings.oClasses; + nFirst.className = oClasses.sPageButton+" "+oClasses.sPageFirst; + nPrevious.className = oClasses.sPageButton+" "+oClasses.sPagePrevious; + nNext.className= oClasses.sPageButton+" "+oClasses.sPageNext; + nLast.className = oClasses.sPageButton+" "+oClasses.sPageLast; + + if ( oSettings.sTableId !== '' ) + { + nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' ); + nFirst.setAttribute( 'id', oSettings.sTableId+'_first' ); + nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' ); + nNext.setAttribute( 'id', oSettings.sTableId+'_next' ); + nLast.setAttribute( 'id', oSettings.sTableId+'_last' ); + } + + nPaging.appendChild( nFirst ); + nPaging.appendChild( nPrevious ); + nPaging.appendChild( nList ); + nPaging.appendChild( nNext ); + nPaging.appendChild( nLast ); + + $(nFirst).click( function () { + oSettings._iDisplayStart = 0; + fnCallbackDraw( oSettings ); + } ); + + $(nPrevious).click( function() { + oSettings._iDisplayStart -= oSettings._iDisplayLength; + + /* Correct for underrun */ + if ( oSettings._iDisplayStart < 0 ) + { + oSettings._iDisplayStart = 0; + } + + fnCallbackDraw( oSettings ); + } ); + + $(nNext).click( function() { + /* Make sure we are not over running the display array */ + if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() ) + { + oSettings._iDisplayStart += oSettings._iDisplayLength; + } + + fnCallbackDraw( oSettings ); + } ); + + $(nLast).click( function() { + var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1; + oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength; + + fnCallbackDraw( oSettings ); + } ); + + /* Take the brutal approach to cancelling text selection */ + $('span', nPaging).bind( 'mousedown', function () { return false; } ); + $('span', nPaging).bind( 'selectstart', function () { return false; } ); + + oSettings.nPaginateList = nList; + }, + + /* + * Function: oPagination.full_numbers.fnUpdate + * Purpose: Update the list of page buttons shows + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * function:fnCallbackDraw - draw function which must be called on update + */ + "fnUpdate": function ( oSettings, fnCallbackDraw ) + { + if ( !oSettings.anFeatures.p ) + { + return; + } + + var iPageCount = jQuery.fn.dataTableExt.oPagination.iFullNumbersShowPages; + var iPageCountHalf = Math.floor(iPageCount / 2); + var iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength); + var iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1; + var sList = ""; + var iStartButton; + var iEndButton; + var oClasses = oSettings.oClasses; + + if (iPages < iPageCount) + { + iStartButton = 1; + iEndButton = iPages; + } + else + { + if (iCurrentPage <= iPageCountHalf) + { + iStartButton = 1; + iEndButton = iPageCount; + } + else + { + if (iCurrentPage >= (iPages - iPageCountHalf)) + { + iStartButton = iPages - iPageCount + 1; + iEndButton = iPages; + } + else + { + iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1; + iEndButton = iStartButton + iPageCount - 1; + } + } + } + + for ( var i=iStartButton ; i<=iEndButton ; i++ ) + { + if ( iCurrentPage != i ) + { + sList += '<span class="'+oClasses.sPageButton+'">'+i+'</span>'; + } + else + { + sList += '<span class="'+oClasses.sPageButtonActive+'">'+i+'</span>'; + } + } + + oSettings.nPaginateList.innerHTML = sList; + + /* Take the brutal approach to cancelling text selection */ + $('span', oSettings.nPaginateList).bind( 'mousedown', function () { return false; } ); + $('span', oSettings.nPaginateList).bind( 'selectstart', function () { return false; } ); + + $('span', oSettings.nPaginateList).click( function() { + var iTarget = (this.innerHTML * 1) - 1; + oSettings._iDisplayStart = iTarget * oSettings._iDisplayLength; + + fnCallbackDraw( oSettings ); + return false; + } ); + + /* Update the 'premanent botton's classes */ + var nButtons = $('span', oSettings.anFeatures.p); + var nStatic = [ nButtons[0], nButtons[1], nButtons[nButtons.length-2], nButtons[nButtons.length-1] ]; + $(nStatic).removeClass( oClasses.sPageButton+" "+oClasses.sPageButtonActive ); + if ( iCurrentPage == 1 ) + { + nStatic[0].className += " "+oClasses.sPageButtonStaticActive; + nStatic[1].className += " "+oClasses.sPageButtonStaticActive; + } + else + { + nStatic[0].className += " "+oClasses.sPageButton; + nStatic[1].className += " "+oClasses.sPageButton; + } + + if ( iCurrentPage == iPages ) + { + nStatic[2].className += " "+oClasses.sPageButtonStaticActive; + nStatic[3].className += " "+oClasses.sPageButtonStaticActive; + } + else + { + nStatic[2].className += " "+oClasses.sPageButton; + nStatic[3].className += " "+oClasses.sPageButton; + } + } + } + }; + + /* + * Variable: oSort + * Purpose: Wrapper for the sorting functions that can be used in DataTables + * Scope: jQuery.fn.dataTableExt + * Notes: The functions provided in this object are basically standard javascript sort + * functions - they expect two inputs which they then compare and then return a priority + * result. For each sort method added, two functions need to be defined, an ascending sort and + * a descending sort. + */ + _oExt.oSort = { + /* + * text sorting + */ + "string-asc": function ( a, b ) + { + var x = a.toLowerCase(); + var y = b.toLowerCase(); + return ((x < y) ? -1 : ((x > 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 = Date.parse( "01/01/1970 00:00:00" ); + } + if ( isNaN( 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 = Date.parse( "01/01/1970 00:00:00" ); + } + if ( isNaN( y ) ) + { + y = Date.parse( "01/01/1970 00:00:00" ); + } + + return y - x; + }, + + + /* + * numerical sorting + */ + "numeric-asc": function ( a, b ) + { + var x = a == "-" ? 0 : a; + var y = b == "-" ? 0 : b; + return x - y; + }, + + "numeric-desc": function ( a, b ) + { + var x = a == "-" ? 0 : a; + var y = b == "-" ? 0 : b; + 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. + */ + _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 ) + { + /* Snaity check that we are dealing with a string or quick return for a number */ + if ( typeof sData == 'number' ) + { + return 'numeric'; + } + else if ( typeof sData.charAt != 'function' ) + { + return null; + } + + 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<sData.length ; i++ ) + { + Char = sData.charAt(i); + if (sValidChars.indexOf(Char) == -1) + { + return null; + } + + /* Only allowed one decimal place... */ + if ( Char == "." ) + { + if ( bDecimal ) + { + return null; + } + bDecimal = true; + } + } + + return 'numeric'; + }, + + /* + * Function: - + * Purpose: Check to see if a string is actually a formatted date + * Returns: string:'date' or null + * Inputs: string:sText - string to check + */ + function ( sData ) + { + var iParse = Date.parse(sData); + if ( iParse !== null && !isNaN(iParse) ) + { + return 'date'; + } + return null; + } + ]; + + + /* + * 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 + }; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * DataTables prototype + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /* + * Function: dataTable + * Purpose: DataTables information + * Returns: - + * Inputs: object:oInit - initalisation options for the table + */ + $.fn.dataTable = function( oInit ) + { + /* + * Variable: _aoSettings + * Purpose: Easy reference to data table settings + * Scope: jQuery.dataTable + */ + var _aoSettings = $.fn.dataTableSettings; + + /* + * 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 this._iRecordsTotal; + } else { + return this.aiDisplayMaster.length; + } + }; + + this.fnRecordsDisplay = function () + { + if ( this.oFeatures.bServerSide ) { + return this._iRecordsDisplay; + } else { + return this.aiDisplay.length; + } + }; + + this.fnDisplayEnd = function () + { + if ( this.oFeatures.bServerSide ) { + return this._iDisplayStart + this.aiDisplay.length; + } else { + return this._iDisplayEnd; + } + }; + + /* + * 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: anFeatures + * 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.anFeatures = []; + + /* + * 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", + "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" + } + }; + + /* + * 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 + */ + 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": "", + "bEscapeRegex": 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 + */ + this.aaSorting = [ [0, 'asc'] ]; + + /* + * 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: 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: fnDrawCallback + * Purpose: Callback function for the whole table on each draw + * Scope: jQuery.dataTable.classSettings + */ + this.fnDrawCallback = null; + + /* + * 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: iDefaultSortIndex + * Purpose: Sorting index which will be used by default + * Scope: jQuery.dataTable.classSettings + */ + this.iDefaultSortIndex = 0; + + /* + * 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: sDomPositioning + * Purpose: Dictate the positioning that the created elements will take + * Scope: jQuery.dataTable.classSettings + * Notes: The following syntax is expected: + * 'l' - Length changing + * 'f' - Filtering input + * 't' - The table! + * 'i' - Information + * 'p' - Pagination + * 'r' - pRocessing + * '<' and '>' - div elements + * '<"class" and '>' - div with a class + * Examples: '<"wrapper"flipt>', '<lf<t>ip>' + */ + this.sDomPositioning = '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: 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 = $.getJSON; + + /* + * Variable: iServerDraw + * Purpose: Counter and tracker for server-side processing draws + * Scope: jQuery.dataTable.classSettings + */ + this.iServerDraw = 0; + + /* + * Variable: _iDisplayLength, _iDisplayStart, _iDisplayEnd + * Purpose: Display length variables + * Scope: jQuery.dataTable.classSettings + * Notes: These variable must NOT be used externally to get the data length. Rather, use + * the fnRecordsTotal() (etc) functions. + */ + this._iDisplayLength = 10; + this._iDisplayStart = 0; + this._iDisplayEnd = 10; + + /* + * Variable: _iRecordsTotal, _iRecordsDisplay + * Purpose: Display length variables used for server side processing + * Scope: jQuery.dataTable.classSettings + * Notes: These variable must NOT be used externally to get the data length. Rather, use + * the fnRecordsTotal() (etc) functions. + */ + this._iRecordsTotal = 0; + this._iRecordsDisplay = 0; + + /* + * Variable: bJUI + * Purpose: Should we add the markup needed for jQuery UI theming? + * Scope: jQuery.dataTable.classSettings + */ + this.bJUI = false; + + /* + * Variable: bJUI + * Purpose: Should we add the markup needed for jQuery UI theming? + * Scope: jQuery.dataTable.classSettings + */ + this.oClasses = _oExt.oStdClasses; + } + + /* + * Variable: oApi + * Purpose: Container for publicly exposed 'private' functions + * Scope: jQuery.dataTable + */ + this.oApi = {}; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * API functions + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /* + * Function: fnDraw + * Purpose: Redraw the table + * Returns: - + * Inputs: - + */ + this.fnDraw = function() + { + _fnReDraw( _fnSettingsFromNode( this[_oExt.iApiIndex] ) ); + }; + + /* + * Function: fnFilter + * Purpose: Filter the input based on data + * Returns: - + * Inputs: string:sInput - string to filter the table on + * int:iColumn - optional - column to limit filtering to + * bool:bEscapeRegex - optional - escape regex characters or not - default true + */ + this.fnFilter = function( sInput, iColumn, bEscapeRegex ) + { + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + + if ( typeof bEscapeRegex == 'undefined' ) + { + bEscapeRegex = true; + } + + if ( typeof iColumn == "undefined" || iColumn === null ) + { + /* Global filter */ + _fnFilterComplete( oSettings, {"sSearch":sInput, "bEscapeRegex": bEscapeRegex}, 1 ); + } + else + { + /* Single column filter */ + oSettings.aoPreSearchCols[ iColumn ].sSearch = sInput; + oSettings.aoPreSearchCols[ iColumn ].bEscapeRegex = bEscapeRegex; + _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 ); + } + }; + + /* + * Function: fnSettings + * Purpose: Get the settings for a particular table for extern. manipulation + * Returns: - + * Inputs: - + */ + this.fnSettings = function( nNode ) + { + return _fnSettingsFromNode( this[_oExt.iApiIndex] ); + }; + + /* + * Function: fnSort + * Purpose: Sort the table by a particular row + * Returns: - + * Inputs: int:iCol - the data index to sort on. Note that this will + * not match the 'display index' if you have hidden data entries + */ + this.fnSort = function( aaSort ) + { + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + oSettings.aaSorting = aaSort; + _fnSort( oSettings ); + }; + + /* + * Function: fnAddData + * Purpose: Add new row(s) into the table + * Returns: array int: array of indexes (aoData) which have been added (zero length on error) + * Inputs: array:mData - the data to be added. The length must match + * the original data from the DOM + * or + * array array:mData - 2D array of data to be added + * bool:bRedraw - redraw the table or not - default true + * Notes: Warning - the refilter here will cause the table to redraw + * starting at zero + * Notes: Thanks to Yekimov Denis for contributing the basis for this function! + */ + this.fnAddData = function( mData, bRedraw ) + { + var aiReturn = []; + var iTest; + if ( typeof bRedraw == 'undefined' ) + { + bRedraw = true; + } + + /* Find settings from table node */ + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + + /* Check if we want to add multiple rows or not */ + if ( typeof mData[0] == "object" ) + { + for ( var i=0 ; i<mData.length ; i++ ) + { + iTest = _fnAddData( oSettings, mData[i] ); + if ( iTest == -1 ) + { + return aiReturn; + } + aiReturn.push( iTest ); + } + } + else + { + iTest = _fnAddData( oSettings, mData ); + if ( iTest == -1 ) + { + return aiReturn; + } + aiReturn.push( iTest ); + } + + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + + /* Rebuild the search */ + _fnBuildSearchArray( oSettings, 1 ); + + if ( bRedraw ) + { + _fnReDraw( oSettings ); + } + return aiReturn; + }; + + /* + * Function: fnDeleteRow + * Purpose: Remove a row for the table + * Returns: array:aReturn - the row that was deleted + * Inputs: int:iIndex - index of aoData to be deleted + * function:fnCallBack - callback function - default null + * bool:bNullRow - remove the row information from aoData by setting the value to + * null - default false + * Notes: This function requires a little explanation - we don't actually delete the data + * from aoData - rather we remove it's references from aiDisplayMastr and aiDisplay. This + * in effect prevnts DataTables from drawing it (hence deleting it) - it could be restored + * if you really wanted. The reason for this is that actually removing the aoData object + * would mess up all the subsequent indexes in the display arrays (they could be ajusted - + * but this appears to do what is required). + */ + this.fnDeleteRow = function( iAODataIndex, fnCallBack, bNullRow ) + { + /* Find settings from table node */ + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + var i; + + /* Delete from the display master */ + for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ ) + { + if ( oSettings.aiDisplayMaster[i] == iAODataIndex ) + { + oSettings.aiDisplayMaster.splice( i, 1 ); + break; + } + } + + /* Delete from the current display index */ + for ( i=0 ; i<oSettings.aiDisplay.length ; i++ ) + { + if ( oSettings.aiDisplay[i] == iAODataIndex ) + { + oSettings.aiDisplay.splice( i, 1 ); + break; + } + } + + /* Rebuild the search */ + _fnBuildSearchArray( oSettings, 1 ); + + /* If there is a user callback function - call it */ + if ( typeof fnCallBack == "function" ) + { + fnCallBack.call( this ); + } + + /* Check for an 'overflow' they case for dislaying the table */ + if ( oSettings._iDisplayStart >= oSettings.aiDisplay.length ) + { + oSettings._iDisplayStart -= oSettings._iDisplayLength; + if ( oSettings._iDisplayStart < 0 ) + { + oSettings._iDisplayStart = 0; + } + } + + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + + /* Return the data array from this row */ + var aData = oSettings.aoData[iAODataIndex]._aData.slice(); + + if ( typeof bNullRow != "undefined" && bNullRow === true ) + { + oSettings.aoData[iAODataIndex] = null; + } + + return aData; + }; + + /* + * Function: fnClearTable + * Purpose: Quickly and simply clear a table + * Returns: - + * Inputs: bool:bRedraw - redraw the table or not - default true + * Notes: Thanks to Yekimov Denis for contributing the basis for this function! + */ + this.fnClearTable = function( bRedraw ) + { + /* Find settings from table node */ + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + _fnClearTable( oSettings ); + + if ( typeof bRedraw == 'undefined' || bRedraw ) + { + _fnDraw( oSettings ); + } + }; + + /* + * Function: fnOpen + * Purpose: Open a display row (append a row after the row in question) + * Returns: - + * Inputs: node:nTr - the table row to 'open' + * string:sHtml - the HTML to put into the row + * string:sClass - class to give the new cell + */ + this.fnOpen = function( nTr, sHtml, sClass ) + { + /* Find settings from table node */ + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + + /* the old open one if there is one */ + this.fnClose( nTr ); + + + var nNewRow = document.createElement("tr"); + var nNewCell = document.createElement("td"); + nNewRow.appendChild( nNewCell ); + nNewCell.className = sClass; + nNewCell.colSpan = _fnVisbleColumns( oSettings ); + nNewCell.innerHTML = sHtml; + + $(nNewRow).insertAfter(nTr); + + /* No point in storing the row if using server-side processing since the nParent will be + * nuked on a re-draw anyway + */ + if ( !oSettings.oFeatures.bServerSide ) + { + oSettings.aoOpenRows.push( { + "nTr": nNewRow, + "nParent": nTr + } ); + } + }; + + /* + * Function: fnClose + * Purpose: Close a display row + * Returns: int: 0 (success) or 1 (failed) + * Inputs: node:nTr - the table row to 'close' + */ + this.fnClose = function( nTr ) + { + /* Find settings from table node */ + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + + for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ ) + { + if ( oSettings.aoOpenRows[i].nParent == nTr ) + { + var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode; + if ( nTrParent ) + { + /* Remove it if it is currently on display */ + nTrParent.removeChild( oSettings.aoOpenRows[i].nTr ); + } + oSettings.aoOpenRows.splice( i, 1 ); + return 0; + } + } + return 1; + }; + + /* + * Function: fnGetData + * Purpose: Return an array with the data which is used to make up the table + * Returns: array array string: 2d data array ([row][column]) or array string: 1d data array + * or + * array string (if iRow specified) + * Inputs: int:iRow - optional - if present then the array returned will be the data for + * the row with the index 'iRow' + */ + this.fnGetData = function( iRow ) + { + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + + if ( typeof iRow != 'undefined' ) + { + return oSettings.aoData[iRow]._aData; + } + return _fnGetDataMaster( oSettings ); + }; + + /* + * Function: fnGetNodes + * Purpose: Return an array with the TR nodes used for drawing the table + * Returns: array node: TR elements + * or + * node (if iRow specified) + * Inputs: int:iRow - optional - if present then the array returned will be the node for + * the row with the index 'iRow' + */ + this.fnGetNodes = function( iRow ) + { + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + + if ( typeof iRow != 'undefined' ) + { + return oSettings.aoData[iRow].nTr; + } + return _fnGetTrNodes( oSettings ); + }; + + /* + * Function: fnGetPosition + * Purpose: Get the array indexes of a particular cell from it's DOM element + * Returns: int: - row index, or array[ int, int ]: - row index and column index + * Inputs: node:nNode - this can either be a TR or a TD in the table, the return is + * dependent on this input + */ + this.fnGetPosition = function( nNode ) + { + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + var i; + + if ( nNode.nodeName == "TR" ) + { + for ( i=0 ; i<oSettings.aoData.length ; i++ ) + { + if ( oSettings.aoData[i] !== null && oSettings.aoData[i].nTr == nNode ) + { + return i; + } + } + } + else if ( nNode.nodeName == "TD" ) + { + for ( i=0 ; i<oSettings.aoData.length ; i++ ) + { + var iCorrector = 0; + for ( var j=0 ; j<oSettings.aoColumns.length ; j++ ) + { + if ( oSettings.aoColumns[j].bVisible ) + { + //$('>td', oSettings.aoData[i].nTr)[j-iCorrector] == nNode ) + if ( oSettings.aoData[i] !== null && + oSettings.aoData[i].nTr.getElementsByTagName('td')[j-iCorrector] == nNode ) + { + return [ i, j-iCorrector, j ]; + } + } + else + { + iCorrector++; + } + } + } + } + return null; + }; + + /* + * Function: fnUpdate + * Purpose: Update a table cell or row + * Returns: int: 0 okay, 1 error + * Inputs: array string 'or' string:mData - data to update the cell/row with + * int:iRow - the row (from aoData) to update + * int:iColumn - the column to update + * bool:bRedraw - redraw the table or not - default true + */ + this.fnUpdate = function( mData, iRow, iColumn, bRedraw ) + { + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + var iVisibleColumn; + var sDisplay; + if ( typeof bRedraw == 'undefined' ) + { + bRedraw = true; + } + + if ( typeof mData != 'object' ) + { + sDisplay = mData; + oSettings.aoData[iRow]._aData[iColumn] = sDisplay; + + if ( oSettings.aoColumns[iColumn].fnRender !== null ) + { + sDisplay = oSettings.aoColumns[iColumn].fnRender( { + "iDataRow": iRow, + "iDataColumn": iColumn, + "aData": oSettings.aoData[iRow]._aData + } ); + + if ( oSettings.aoColumns[iColumn].bUseRendered ) + { + oSettings.aoData[iRow]._aData[iColumn] = sDisplay; + } + } + + iVisibleColumn = _fnColumnIndexToVisible( oSettings, iColumn ); + if ( iVisibleColumn !== null ) + { + oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML = + sDisplay; + } + } + else + { + if ( mData.length != oSettings.aoColumns.length ) + { + alert( 'Warning: An array passed to fnUpdate must have the same number of columns as '+ + 'the table in question - in this case '+oSettings.aoColumns.length ); + return 1; + } + + for ( var i=0 ; i<mData.length ; i++ ) + { + sDisplay = mData[i]; + oSettings.aoData[iRow]._aData[i] = sDisplay; + + if ( oSettings.aoColumns[i].fnRender !== null ) + { + sDisplay = oSettings.aoColumns[i].fnRender( { + "iDataRow": iRow, + "iDataColumn": i, + "aData": oSettings.aoData[iRow]._aData + } ); + + if ( oSettings.aoColumns[i].bUseRendered ) + { + oSettings.aoData[iRow]._aData[i] = sDisplay; + } + } + + iVisibleColumn = _fnColumnIndexToVisible( oSettings, i ); + if ( iVisibleColumn !== null ) + { + oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML = + sDisplay; + } + } + } + + /* Update the search array */ + _fnBuildSearchArray( oSettings, 1 ); + + /* Redraw the table */ + if ( bRedraw ) + { + _fnReDraw( oSettings ); + } + return 0; + }; + + + /* + * Function: fnShowColoumn + * Purpose: Show a particular column + * Returns: - + * Inputs: int:iCol - the column whose display should be changed + * bool:bShow - show (true) or hide (false) the column + */ + this.fnSetColumnVis = function ( iCol, bShow ) + { + var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); + var i, iLen; + var iColumns = oSettings.aoColumns.length; + var nTd; + + /* No point in doing anything if we are requesting what is already true */ + if ( oSettings.aoColumns[iCol].bVisible == bShow ) + { + return; + } + + var nTrHead = $('thead tr', oSettings.nTable)[0]; + var nTrFoot = $('tfoot tr', oSettings.nTable)[0]; + var anTheadTh = []; + var anTfootTh = []; + for ( i=0 ; i<iColumns ; i++ ) + { + anTheadTh.push( oSettings.aoColumns[i].nTh ); + anTfootTh.push( oSettings.aoColumns[i].nTf ); + } + + /* Show the column */ + if ( bShow ) + { + var iInsert = 0; + for ( i=0 ; i<iCol ; i++ ) + { + if ( oSettings.aoColumns[i].bVisible ) + { + iInsert++; + } + } + + /* Need to decide if we should use appendChild or insertBefore */ + if ( iInsert >= _fnVisbleColumns( oSettings ) ) + { + nTrHead.appendChild( anTheadTh[iCol] ); + if ( nTrFoot ) + { + nTrFoot.appendChild( anTfootTh[iCol] ); + } + + for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) + { + nTd = oSettings.aoData[i]._anHidden[iCol]; + oSettings.aoData[i].nTr.appendChild( nTd ); + } + } + else + { + /* Which coloumn should we be inserting before? */ + var iBefore; + for ( i=iCol ; i<iColumns ; i++ ) + { + iBefore = _fnColumnIndexToVisible( oSettings, i ); + if ( iBefore !== null ) + { + break; + } + } + + nTrHead.insertBefore( anTheadTh[iCol], nTrHead.getElementsByTagName('th')[iBefore] ); + if ( nTrFoot ) + { + nTrFoot.insertBefore( anTfootTh[iCol], nTrFoot.getElementsByTagName('th')[iBefore] ); + } + + for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) + { + nTd = oSettings.aoData[i]._anHidden[iCol]; + oSettings.aoData[i].nTr.insertBefore( nTd, oSettings.aoData[i].nTr.getElementsByTagName('td')[iBefore] ); + } + } + + oSettings.aoColumns[iCol].bVisible = true; + } + else + { + /* Remove a column from display */ + nTrHead.removeChild( anTheadTh[iCol] ); + if ( nTrFoot ) + { + nTrFoot.removeChild( anTfootTh[iCol] ); + } + + var iVisCol = _fnColumnIndexToVisible(oSettings, iCol); + for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) + { + nTd = oSettings.aoData[i].nTr.getElementsByTagName('td')[ iVisCol ]; + oSettings.aoData[i]._anHidden[iCol] = nTd; + nTd.parentNode.removeChild( nTd ); + } + + oSettings.aoColumns[iCol].bVisible = false; + } + + /* If there are any 'open' rows, then we need to alter the colspan for this col change */ + for ( i=0, iLen=oSettings.aoOpenRows.length ; i<iLen ; i++ ) + { + oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns( oSettings ); + } + + /* Since there is no redraw done here, we need to save the state manually */ + _fnSaveState( oSettings ); + }; + + + /* + * Plugin API functions + * + * This call will add the functions which are defined in _oExt.oApi to the + * DataTables object, providing a rather nice way to allow plug-in API functions. Note that + * this is done here, so that API function can actually override the built in API functions if + * required for a particular purpose. + */ + + /* + * Function: _fnExternApiFunc + * Purpose: Create a wrapper function for exporting an internal func to an external API func + * Returns: function: - wrapped function + * Inputs: string:sFunc - API function name + */ + function _fnExternApiFunc (sFunc) + { + return function() { + var aArgs = [_fnSettingsFromNode(this[_oExt.iApiIndex])].concat( + Array.prototype.slice.call(arguments) ); + return _oExt.oApi[sFunc].apply( this, aArgs ); + }; + } + + for ( var sFunc in _oExt.oApi ) + { + if ( sFunc ) + { + /* + * Function: anon + * Purpose: Wrap the plug-in API functions in order to provide the settings as 1st arg + * and execute in this scope + * Returns: - + * Inputs: - + */ + this[sFunc] = _fnExternApiFunc(sFunc); + } + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Local functions + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Initalisation + */ + + /* + * Function: _fnInitalise + * Purpose: Draw the table for the first time, adding all required features + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnInitalise ( oSettings ) + { + /* Ensure that the table data is fully initialised */ + if ( oSettings.bInitialised === false ) + { + setTimeout( function(){ _fnInitalise( oSettings ); }, 200 ); + return; + } + + /* Show the display HTML options */ + _fnAddOptionsHtml( oSettings ); + + /* Draw the headers for the table */ + _fnDrawHead( oSettings ); + + /* If there is default sorting required - let's do it. The sort function + * will do the drawing for us. Otherwise we draw the table + */ + if ( oSettings.oFeatures.bSort ) + { + _fnSort( oSettings, false ); + /* + * Add the sorting classes to the header and the body (if needed). + * Reason for doing it here after the first draw is to stop classes being applied to the + * 'static' table. + */ + _fnSortingClasses( oSettings ); + } + else + { + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + } + + /* if there is an ajax source */ + if ( oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide ) + { + _fnProcessingDisplay( oSettings, true ); + + $.getJSON( oSettings.sAjaxSource, null, function(json) { + + /* Got the data - add it to the table */ + for ( var i=0 ; i<json.aaData.length ; i++ ) + { + _fnAddData( oSettings, json.aaData[i] ); + } + + /* Reset the init display for cookie saving. We've already done a filter, and + * therefore cleared it before. So we need to make it appear 'fresh' + */ + oSettings.iInitDisplayStart = oSettings._iDisplayStart; + + if ( oSettings.oFeatures.bSort ) + { + _fnSort( oSettings ); + } + else + { + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + } + _fnProcessingDisplay( oSettings, false ); + + /* Run the init callback if there is one */ + if ( typeof oSettings.fnInitComplete == 'function' ) + { + oSettings.fnInitComplete( oSettings, json ); + } + } ); + return; + } + + /* Run the init callback if there is one */ + if ( typeof oSettings.fnInitComplete == 'function' ) + { + oSettings.fnInitComplete( oSettings ); + } + _fnProcessingDisplay( oSettings, false ); + } + + /* + * Function: _fnLanguageProcess + * Purpose: Copy language variables from remote object to a local one + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * object:oLanguage - Language information + * bool:bInit - init once complete + */ + function _fnLanguageProcess( oSettings, oLanguage, bInit ) + { + _fnMap( oSettings.oLanguage, oLanguage, 'sProcessing' ); + _fnMap( oSettings.oLanguage, oLanguage, 'sLengthMenu' ); + _fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords' ); + _fnMap( oSettings.oLanguage, oLanguage, 'sInfo' ); + _fnMap( oSettings.oLanguage, oLanguage, 'sInfoEmpty' ); + _fnMap( oSettings.oLanguage, oLanguage, 'sInfoFiltered' ); + _fnMap( oSettings.oLanguage, oLanguage, 'sInfoPostFix' ); + _fnMap( oSettings.oLanguage, oLanguage, 'sSearch' ); + + if ( typeof oLanguage.oPaginate != 'undefined' ) + { + _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sFirst' ); + _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sPrevious' ); + _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sNext' ); + _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sLast' ); + } + + if ( bInit ) + { + _fnInitalise( oSettings ); + } + } + + /* + * Function: _fnAddColumn + * Purpose: Add a column to the list used for the table + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * object:oOptions - object with sType, bVisible and bSearchable + * node:nTh - the th element for this column + * Notes: All options in enter column can be over-ridden by the user + * initialisation of dataTables + */ + function _fnAddColumn( oSettings, oOptions, nTh ) + { + oSettings.aoColumns[ oSettings.aoColumns.length++ ] = { + "sType": null, + "_bAutoType": true, + "bVisible": true, + "bSearchable": true, + "bSortable": true, + "sTitle": nTh ? nTh.innerHTML : '', + "sName": '', + "sWidth": null, + "sClass": null, + "fnRender": null, + "bUseRendered": true, + "iDataSort": oSettings.aoColumns.length-1, + "nTh": nTh ? nTh : document.createElement('th'), + "nTf": null + }; + + /* User specified column options */ + var iLength = oSettings.aoColumns.length-1; + if ( typeof oOptions != 'undefined' && oOptions !== null ) + { + var oCol = oSettings.aoColumns[ iLength ]; + + if ( typeof oOptions.sType != 'undefined' ) + { + oCol.sType = oOptions.sType; + oCol._bAutoType = false; + } + + _fnMap( oCol, oOptions, "bVisible" ); + _fnMap( oCol, oOptions, "bSearchable" ); + _fnMap( oCol, oOptions, "bSortable" ); + _fnMap( oCol, oOptions, "sTitle" ); + _fnMap( oCol, oOptions, "sName" ); + _fnMap( oCol, oOptions, "sWidth" ); + _fnMap( oCol, oOptions, "sClass" ); + _fnMap( oCol, oOptions, "fnRender" ); + _fnMap( oCol, oOptions, "bUseRendered" ); + _fnMap( oCol, oOptions, "iDataSort" ); + } + + /* Add a column specific filter */ + if ( typeof oSettings.aoPreSearchCols[ iLength ] == 'undefined' || + oSettings.aoPreSearchCols[ iLength ] === null ) + { + oSettings.aoPreSearchCols[ iLength ] = { + "sSearch": "", + "bEscapeRegex": true + }; + } + else if ( typeof oSettings.aoPreSearchCols[ iLength ].bEscapeRegex == 'undefined' ) + { + /* Don't require that the user must specify bEscapeRegex */ + oSettings.aoPreSearchCols[ iLength ].bEscapeRegex = true; + } + } + + /* + * Function: _fnAddData + * Purpose: Add a data array to the table, creating DOM node etc + * Returns: int: - >=0 if successful (index of new aoData entry), -1 if failed + * Inputs: object:oSettings - dataTables settings object + * array:aData - data array to be added + */ + function _fnAddData ( oSettings, aData ) + { + /* Sanity check the length of the new array */ + if ( aData.length != oSettings.aoColumns.length ) + { + alert( "Warning - added data does not match known number of columns" ); + return -1; + } + + /* Create the object for storing information about this new row */ + var iThisIndex = oSettings.aoData.length; + oSettings.aoData.push( { + "_iId": oSettings.iNextId++, + "_aData": aData.slice(), + "nTr": document.createElement('tr'), + "_anHidden": [] + } ); + + /* Create the cells */ + var nTd; + for ( var i=0 ; i<aData.length ; i++ ) + { + nTd = document.createElement('td'); + + if ( typeof oSettings.aoColumns[i].fnRender == 'function' ) + { + var sRendered = oSettings.aoColumns[i].fnRender( { + "iDataRow": iThisIndex, + "iDataColumn": i, + "aData": aData + } ); + nTd.innerHTML = sRendered; + if ( oSettings.aoColumns[i].bUseRendered ) + { + /* Use the rendered data for filtering/sorting */ + oSettings.aoData[iThisIndex]._aData[i] = sRendered; + } + } + else + { + nTd.innerHTML = aData[i]; + } + + if ( oSettings.aoColumns[i].sClass !== null ) + { + nTd.className = oSettings.aoColumns[i].sClass; + } + + /* See if we should auto-detect the column type */ + if ( oSettings.aoColumns[i]._bAutoType && oSettings.aoColumns[i].sType != 'string' ) + { + /* Attempt to auto detect the type - same as _fnGatherData() */ + if ( oSettings.aoColumns[i].sType === null ) + { + oSettings.aoColumns[i].sType = _fnDetectType( aData[i] ); + } + else if ( oSettings.aoColumns[i].sType == "date" || + oSettings.aoColumns[i].sType == "numeric" ) + { + oSettings.aoColumns[i].sType = _fnDetectType( aData[i] ); + } + } + + if ( oSettings.aoColumns[i].bVisible ) + { + oSettings.aoData[iThisIndex].nTr.appendChild( nTd ); + } + else + { + oSettings.aoData[iThisIndex]._anHidden[i] = nTd; + } + } + + /* Add to the display array */ + oSettings.aiDisplayMaster.push( iThisIndex ); + return iThisIndex; + } + + /* + * Function: _fnGatherData + * Purpose: Read in the data from the target table + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnGatherData( oSettings ) + { + var iLoop; + var i, j; + + /* + * Process by row first + * Add the data object for the whole table - storing the tr node. Note - no point in getting + * DOM based data if we are going to go and replace it with Ajax source data. + */ + if ( oSettings.sAjaxSource === null ) + { + $('tbody:eq(0)>tr', oSettings.nTable).each( function() { + var iThisIndex = oSettings.aoData.length; + oSettings.aoData.push( { + "_iId": oSettings.iNextId++, + "_aData": [], + "nTr": this, + "_anHidden": [] + } ); + + oSettings.aiDisplayMaster.push( iThisIndex ); + + /* Add the data for this column */ + var aLocalData = oSettings.aoData[iThisIndex]._aData; + $('td', this).each( function( i ) { + aLocalData[i] = this.innerHTML; + } ); + } ); + } + + /* + * Now process by column + */ + var iCorrector = 0; + for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) + { + /* Get the title of the column - unless there is a user set one */ + if ( oSettings.aoColumns[i].sTitle === null ) + { + oSettings.aoColumns[i].sTitle = oSettings.aoColumns[i].nTh.innerHTML; + } + + var bAutoType = oSettings.aoColumns[i]._bAutoType; + var bRender = typeof oSettings.aoColumns[i].fnRender == 'function'; + var bClass = oSettings.aoColumns[i].sClass !== null; + var bVisible = oSettings.aoColumns[i].bVisible; + + /* A single loop to rule them all (and be more efficient) */ + if ( bAutoType || bRender || bClass || !bVisible ) + { + iLoop = oSettings.aoData.length; + for ( j=0 ; j<iLoop ; j++ ) + { + var nCellNode = oSettings.aoData[j].nTr.getElementsByTagName('td')[ i-iCorrector ]; + + if ( bAutoType ) + { + if ( oSettings.aoColumns[i].sType === null ) + { + oSettings.aoColumns[i].sType = _fnDetectType( oSettings.aoData[j]._aData[i] ); + } + else if ( oSettings.aoColumns[i].sType == "date" || + oSettings.aoColumns[i].sType == "numeric" ) + { + /* If type is date or numeric - ensure that all collected data + * in the column is of the same type + */ + oSettings.aoColumns[i].sType = _fnDetectType( oSettings.aoData[j]._aData[i] ); + } + /* The else would be 'type = string' we don't want to do anything + * if that is the case + */ + } + + if ( bRender ) + { + var sRendered = oSettings.aoColumns[i].fnRender( { + "iDataRow": j, + "iDataColumn": i, + "aData": oSettings.aoData[j]._aData + } ); + nCellNode.innerHTML = sRendered; + if ( oSettings.aoColumns[i].bUseRendered ) + { + /* Use the rendered data for filtering/sorting */ + oSettings.aoData[j]._aData[i] = sRendered; + } + } + + if ( bClass ) + { + nCellNode.className += ' '+oSettings.aoColumns[i].sClass; + } + + if ( !bVisible ) + { + oSettings.aoData[j]._anHidden[i] = nCellNode; + nCellNode.parentNode.removeChild( nCellNode ); + } + } + + /* Keep an index corrector for the next loop */ + if ( !bVisible ) + { + iCorrector++; + } + } + } + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Drawing functions + */ + + /* + * Function: _fnDrawHead + * Purpose: Create the HTML header for the table + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnDrawHead( oSettings ) + { + var i, nTh, iLen; + var iThs = oSettings.nTable.getElementsByTagName('thead')[0].getElementsByTagName('th').length; + var iCorrector = 0; + + /* If there is a header in place - then use it - otherwise it's going to get nuked... */ + if ( iThs !== 0 ) + { + /* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */ + for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) + { + //oSettings.aoColumns[i].nTh = nThs[i]; + nTh = oSettings.aoColumns[i].nTh; + + if ( oSettings.aoColumns[i].bVisible ) + { + /* Set width */ + if ( oSettings.aoColumns[i].sWidth !== null ) + { + nTh.style.width = oSettings.aoColumns[i].sWidth; + } + + /* Set the title of the column if it is user defined (not what was auto detected) */ + if ( oSettings.aoColumns[i].sTitle != nTh.innerHTML ) + { + nTh.innerHTML = oSettings.aoColumns[i].sTitle; + } + } + else + { + nTh.parentNode.removeChild( nTh ); + iCorrector++; + } + } + } + else + { + /* We don't have a header in the DOM - so we are going to have to create one */ + var nTr = document.createElement( "tr" ); + + for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) + { + if ( oSettings.aoColumns[i].bVisible ) + { + nTh = oSettings.aoColumns[i].nTh; + + if ( oSettings.aoColumns[i].sClass !== null ) + { + nTh.className = oSettings.aoColumns[i].sClass; + } + + if ( oSettings.aoColumns[i].sWidth !== null ) + { + nTh.style.width = oSettings.aoColumns[i].sWidth; + } + + nTh.innerHTML = oSettings.aoColumns[i].sTitle; + nTr.appendChild( nTh ); + } + } + $('thead', oSettings.nTable).html( '' )[0].appendChild( nTr ); + } + + /* Add the extra markup needed by jQuery UI's themes */ + if ( oSettings.bJUI ) + { + for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) + { + var nSpan = document.createElement('span'); + oSettings.aoColumns[i].nTh.appendChild( nSpan ); + } + } + + /* Add sort listener */ + if ( oSettings.oFeatures.bSort ) + { + for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) + { + if ( oSettings.aoColumns[i].bSortable === false ) + { + continue; + } + + $(oSettings.aoColumns[i].nTh).click( function (e) { + var iDataIndex; + /* Find which column we are sorting on - can't use index() due to colspan etc */ + for ( var i=0 ; i<oSettings.aoColumns.length ; i++ ) + { + if ( oSettings.aoColumns[i].nTh == this ) + { + iDataIndex = i; + break; + } + } + + /* If the column is not sortable - don't to anything */ + if ( oSettings.aoColumns[iDataIndex].bSortable === false ) + { + return; + } + + /* + * This is a little bit odd I admit... I declare a temporary function inside the scope of + * _fnDrawHead and the click handler in order that the code presented here can be used + * twice - once for when bProcessing is enabled, and another time for when it is + * disabled, as we need to perform slightly different actions. + * Basically the issue here is that the Javascript engine in modern browsers don't + * appear to allow the rendering engine to update the display while it is still excuting + * it's thread (well - it does but only after long intervals). This means that the + * 'processing' display doesn't appear for a table sort. To break the js thread up a bit + * I force an execution break by using setTimeout - but this breaks the expected + * thread continuation for the end-developer's point of view (their code would execute + * too early), so we on;y do it when we absolutely have to. + */ + var fnInnerSorting = function () { + if ( e.shiftKey ) + { + /* If the shift key is pressed then we are multipe column sorting */ + var bFound = false; + for ( var i=0 ; i<oSettings.aaSorting.length ; i++ ) + { + if ( oSettings.aaSorting[i][0] == iDataIndex ) + { + if ( oSettings.aaSorting[i][1] == "asc" ) + { + oSettings.aaSorting[i][1] = "desc"; + } + else + { + oSettings.aaSorting.splice( i, 1 ); + } + bFound = true; + break; + } + } + + if ( bFound === false ) + { + oSettings.aaSorting.push( [ iDataIndex, "asc" ] ); + } + } + else + { + /* If no shift key then single column sort */ + if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex ) + { + oSettings.aaSorting[0][1] = oSettings.aaSorting[0][1]=="asc" ? "desc" : "asc"; + } + else + { + oSettings.aaSorting.splice( 0, oSettings.aaSorting.length ); + oSettings.aaSorting.push( [ iDataIndex, "asc" ] ); + } + } + + /* Run the sort */ + _fnSort( oSettings ); + }; /* /fnInnerSorting */ + + if ( !oSettings.oFeatures.bProcessing ) + { + fnInnerSorting(); + } + else + { + _fnProcessingDisplay( oSettings, true ); + setTimeout( function() { + fnInnerSorting(); + if ( !oSettings.oFeatures.bServerSide ) + { + _fnProcessingDisplay( oSettings, false ); + } + }, 0 ); + } + } ); /* /click */ + } /* For each column */ + + /* Take the brutal approach to cancelling text selection due to the shift key */ + $('thead th', oSettings.nTable).mousedown( function (e) { + if ( e.shiftKey ) + { + this.onselectstart = function() { return false; }; + return false; + } + } ); + } /* /if feature sort */ + + /* Set an absolute width for the table such that pagination doesn't + * cause the table to resize + */ + if ( oSettings.oFeatures.bAutoWidth ) + { + oSettings.nTable.style.width = oSettings.nTable.offsetWidth+"px"; + } + + /* Cache the footer elements */ + var nTfoot = oSettings.nTable.getElementsByTagName('tfoot'); + if ( nTfoot.length !== 0 ) + { + iCorrector = 0; + var nTfs = nTfoot[0].getElementsByTagName('th'); + for ( i=0, iLen=nTfs.length ; i<iLen ; i++ ) + { + oSettings.aoColumns[i].nTf = nTfs[i-iCorrector]; + if ( !oSettings.aoColumns[i].bVisible ) + { + nTfs[i-iCorrector].parentNode.removeChild( nTfs[i-iCorrector] ); + iCorrector++; + } + } + } + } + + /* + * Function: _fnDraw + * Purpose: Insert the required TR nodes into the table for display + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnDraw( oSettings ) + { + var i; + var anRows = []; + var iRowCount = 0; + var bRowError = false; + var iStrips = oSettings.asStripClasses.length; + var iOpenRows = oSettings.aoOpenRows.length; + + /* If we are dealing with Ajax - do it here */ + if ( oSettings.oFeatures.bServerSide && + !_fnAjaxUpdate( oSettings ) ) + { + return; + } + + if ( oSettings.aiDisplay.length !== 0 ) + { + var iStart = oSettings._iDisplayStart; + var iEnd = oSettings._iDisplayEnd; + + if ( oSettings.oFeatures.bServerSide ) + { + iStart = 0; + iEnd = oSettings.aoData.length; + } + + for ( var j=iStart ; j<iEnd ; j++ ) + { + var nRow = oSettings.aoData[ oSettings.aiDisplay[j] ].nTr; + + /* Remove any old stripping classes and then add the new one */ + if ( iStrips !== 0 ) + { + $(nRow).removeClass( oSettings.asStripClasses.join(' ') ); + $(nRow).addClass( oSettings.asStripClasses[ iRowCount % iStrips ] ); + } + + /* Custom row callback function - might want to manipule the row */ + if ( typeof oSettings.fnRowCallback == "function" ) + { + nRow = oSettings.fnRowCallback( nRow, + oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j ); + if ( !nRow && !bRowError ) + { + alert( "Error: A node was not returned by fnRowCallback" ); + bRowError = true; + } + } + + anRows.push( nRow ); + iRowCount++; + + /* If there is an open row - and it is attached to this parent - attach it on redraw */ + if ( iOpenRows !== 0 ) + { + for ( var k=0 ; k<iOpenRows ; k++ ) + { + if ( nRow == oSettings.aoOpenRows[k].nParent ) + { + anRows.push( oSettings.aoOpenRows[k].nTr ); + } + } + } + } + } + else + { + /* Table is empty - create a row with an empty message in it */ + anRows[ 0 ] = document.createElement( 'tr' ); + + if ( typeof oSettings.asStripClasses[0] != 'undefined' ) + { + anRows[ 0 ].className = oSettings.asStripClasses[0]; + } + + var nTd = document.createElement( 'td' ); + nTd.setAttribute( 'valign', "top" ); + nTd.colSpan = oSettings.aoColumns.length; + nTd.className = oSettings.oClasses.sRowEmpty; + nTd.innerHTML = oSettings.oLanguage.sZeroRecords; + + anRows[ iRowCount ].appendChild( nTd ); + } + + /* Callback the header and footer custom funcation if there is one */ + if ( typeof oSettings.fnHeaderCallback == 'function' ) + { + oSettings.fnHeaderCallback( $('thead tr', oSettings.nTable)[0], + _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), + oSettings.aiDisplay ); + } + + if ( typeof oSettings.fnFooterCallback == 'function' ) + { + oSettings.fnFooterCallback( $('tfoot tr', oSettings.nTable)[0], + _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), + oSettings.aiDisplay ); + } + + /* + * Need to remove any old row from the display - note we can't just empty the tbody using + * .html('') since this will unbind the jQuery event handlers (even although the node still + * exists!) - note the initially odd ':eq(0)>tr' expression. This basically ensures that we + * only get tr elements of the tbody that the data table has been initialised on. If there + * are nested tables then we don't want to remove those elements. + */ + var nTrs = $('tbody:eq(0)>tr', oSettings.nTable); + for ( i=0 ; i<nTrs.length ; i++ ) + { + nTrs[i].parentNode.removeChild( nTrs[i] ); + } + + /* Put the draw table into the dom */ + var nBody = $('tbody:eq(0)', oSettings.nTable); + if ( nBody[0] ) + { + for ( i=0 ; i<anRows.length ; i++ ) + { + nBody[0].appendChild( anRows[i] ); + } + } + + /* Update the pagination display buttons */ + if ( oSettings.oFeatures.bPaginate ) + { + _oExt.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) { + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + } ); + } + + /* Show information about the table */ + if ( oSettings.oFeatures.bInfo && oSettings.anFeatures.i ) + { + /* Update the information */ + if ( oSettings.fnRecordsDisplay() === 0 && + oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() ) + { + oSettings.anFeatures.i.innerHTML = + oSettings.oLanguage.sInfoEmpty+ oSettings.oLanguage.sInfoPostFix; + } + else if ( oSettings.fnRecordsDisplay() === 0 ) + { + oSettings.anFeatures.i.innerHTML = oSettings.oLanguage.sInfoEmpty +' '+ + oSettings.oLanguage.sInfoFiltered.replace('_MAX_', + oSettings.fnRecordsTotal())+ oSettings.oLanguage.sInfoPostFix; + } + else if ( oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() ) + { + oSettings.anFeatures.i.innerHTML = + oSettings.oLanguage.sInfo. + replace('_START_',oSettings._iDisplayStart+1). + replace('_END_',oSettings.fnDisplayEnd()). + replace('_TOTAL_',oSettings.fnRecordsDisplay())+ + oSettings.oLanguage.sInfoPostFix; + } + else + { + oSettings.anFeatures.i.innerHTML = + oSettings.oLanguage.sInfo. + replace('_START_',oSettings._iDisplayStart+1). + replace('_END_',oSettings.fnDisplayEnd()). + replace('_TOTAL_',oSettings.fnRecordsDisplay()) +' '+ + oSettings.oLanguage.sInfoFiltered.replace('_MAX_', oSettings.fnRecordsTotal())+ + oSettings.oLanguage.sInfoPostFix; + } + } + + /* Alter the sorting classes to take account of the changes */ + if ( oSettings.oFeatures.bServerSide && oSettings.oFeatures.bSort ) + { + _fnSortingClasses( oSettings ); + } + + /* Save the table state on each draw */ + _fnSaveState( oSettings ); + + /* Drawing is finished - call the callback if there is one */ + if ( typeof oSettings.fnDrawCallback == 'function' ) + { + oSettings.fnDrawCallback( oSettings ); + } + } + + /* + * Function: _fnReDraw + * Purpose: Redraw the table - taking account of the various features which are enabled + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnReDraw( oSettings ) + { + if ( oSettings.oFeatures.bSort ) + { + /* Sorting will refilter and draw for us */ + _fnSort( oSettings, oSettings.oPreviousSearch ); + } + else if ( oSettings.oFeatures.bFilter ) + { + /* Filtering will redraw for us */ + _fnFilterComplete( oSettings, oSettings.oPreviousSearch ); + } + else + { + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + } + } + + /* + * Function: _fnAjaxUpdate + * Purpose: Update the table using an Ajax call + * Returns: bool: block the table drawing or not + * Inputs: object:oSettings - dataTables settings object + */ + function _fnAjaxUpdate( oSettings ) + { + if ( oSettings.bAjaxDataGet ) + { + _fnProcessingDisplay( oSettings, true ); + var iColumns = oSettings.aoColumns.length; + var aoData = []; + var i; + + /* Paging and general */ + oSettings.iServerDraw++; + aoData.push( { "name": "sEcho", "value": oSettings.iServerDraw } ); + aoData.push( { "name": "iColumns", "value": iColumns } ); + aoData.push( { "name": "sColumns", "value": _fnColumnOrdering(oSettings) } ); + aoData.push( { "name": "iDisplayStart", "value": oSettings._iDisplayStart } ); + aoData.push( { "name": "iDisplayLength", "value": oSettings.oFeatures.bPaginate !== false ? + oSettings._iDisplayLength : -1 } ); + + /* Filtering */ + if ( oSettings.oFeatures.bFilter !== false ) + { + aoData.push( { "name": "sSearch", "value": oSettings.oPreviousSearch.sSearch } ); + aoData.push( { "name": "bEscapeRegex", "value": oSettings.oPreviousSearch.bEscapeRegex } ); + for ( i=0 ; i<iColumns ; i++ ) + { + aoData.push( { "name": "sSearch_"+i, "value": oSettings.aoPreSearchCols[i].sSearch } ); + aoData.push( { "name": "bEscapeRegex_"+i, "value": oSettings.aoPreSearchCols[i].bEscapeRegex } ); + } + } + + /* Sorting */ + if ( oSettings.oFeatures.bSort !== false ) + { + var iFixed = oSettings.aaSortingFixed !== null ? oSettings.aaSortingFixed.length : 0; + var iUser = oSettings.aaSorting.length; + aoData.push( { "name": "iSortingCols", "value": iFixed+iUser } ); + for ( i=0 ; i<iFixed ; i++ ) + { + aoData.push( { "name": "iSortCol_"+i, "value": oSettings.aaSortingFixed[i][0] } ); + aoData.push( { "name": "iSortDir_"+i, "value": oSettings.aaSortingFixed[i][1] } ); + } + + for ( i=0 ; i<iUser ; i++ ) + { + aoData.push( { "name": "iSortCol_"+(i+iFixed), "value": oSettings.aaSorting[i][0] } ); + aoData.push( { "name": "iSortDir_"+(i+iFixed), "value": oSettings.aaSorting[i][1] } ); + } + } + + oSettings.fnServerData( oSettings.sAjaxSource, aoData, function(json) { + _fnAjaxUpdateDraw( oSettings, json ); + } ); + return false; + } + else + { + return true; + } + } + + /* + * Function: _fnAjaxUpdateDraw + * Purpose: Data the data from the server (nuking the old) and redraw the table + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * object:json - json data return from the server. + * The following must be defined: + * iTotalRecords, iTotalDisplayRecords, aaData + * The following may be defined: + * sColumns + */ + function _fnAjaxUpdateDraw ( oSettings, json ) + { + if ( typeof json.sEcho != 'undefined' ) + { + /* Protect against old returns over-writing a new one. Possible when you get + * very fast interaction, and later queires are completed much faster + */ + if ( json.sEcho*1 < oSettings.iServerDraw ) + { + return; + } + else + { + oSettings.iServerDraw = json.sEcho * 1; + } + } + + _fnClearTable( oSettings ); + oSettings._iRecordsTotal = json.iTotalRecords; + oSettings._iRecordsDisplay = json.iTotalDisplayRecords; + + /* Determine if reordering is required */ + var sOrdering = _fnColumnOrdering(oSettings); + var bReOrder = (json.sColumns != 'undefined' && sOrdering !== "" && json.sColumns != sOrdering ); + if ( bReOrder ) + { + var aiIndex = _fnReOrderIndex( oSettings, json.sColumns ); + } + + for ( var i=0, iLen=json.aaData.length ; i<iLen ; i++ ) + { + if ( bReOrder ) + { + /* If we need to re-order, then create a new array with the correct order and add it */ + var aData = []; + for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ ) + { + aData.push( json.aaData[i][ aiIndex[j] ] ); + } + _fnAddData( oSettings, aData ); + } + else + { + /* No re-order required, sever got it "right" - just straight add */ + _fnAddData( oSettings, json.aaData[i] ); + } + } + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + + oSettings.bAjaxDataGet = false; + _fnDraw( oSettings ); + oSettings.bAjaxDataGet = true; + _fnProcessingDisplay( oSettings, false ); + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Options (features) HTML + */ + + /* + * Function: _fnAddOptionsHtml + * Purpose: Add the options to the page HTML for the table + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnAddOptionsHtml ( oSettings ) + { + /* + * Create a temporary, empty, div which we can later on replace with what we have generated + * we do it this way to rendering the 'options' html offline - speed :-) + */ + var nHolding = document.createElement( 'div' ); + oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable ); + + /* + * All DataTables are wrapped in a div - this is not currently optional - backwards + * compatability. It can be removed if you don't want it. + */ + var nWrapper = document.createElement( 'div' ); + nWrapper.className = oSettings.oClasses.sWrapper; + if ( oSettings.sTableId !== '' ) + { + nWrapper.setAttribute( 'id', oSettings.sTableId+'_wrapper' ); + } + + /* Track where we want to insert the option */ + var nInsertNode = nWrapper; + + /* IE don't treat strings as arrays */ + var sDom = oSettings.sDomPositioning.split(''); + + /* Loop over the user set positioning and place the elements as needed */ + var nTmp; + for ( var i=0 ; i<sDom.length ; i++ ) + { + var cOption = sDom[i]; + + if ( cOption == '<' ) + { + /* New container div */ + var nNewNode = document.createElement( 'div' ); + + /* Check to see if we should append a class name to the container */ + var cNext = sDom[i+1]; + if ( cNext == "'" || cNext == '"' ) + { + var sClass = ""; + var j = 2; + while ( sDom[i+j] != cNext ) + { + sClass += sDom[i+j]; + j++; + } + nNewNode.className = sClass; + i += j; /* Move along the position array */ + } + + nInsertNode.appendChild( nNewNode ); + nInsertNode = nNewNode; + } + else if ( cOption == '>' ) + { + /* End container div */ + nInsertNode = nInsertNode.parentNode; + } + else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange ) + { + /* Length */ + nTmp = _fnFeatureHtmlLength( oSettings ); + oSettings.anFeatures[cOption] = nTmp; + nInsertNode.appendChild( nTmp ); + } + else if ( cOption == 'f' && oSettings.oFeatures.bFilter ) + { + /* Filter */ + nTmp = _fnFeatureHtmlFilter( oSettings ); + oSettings.anFeatures[cOption] = nTmp; + nInsertNode.appendChild( nTmp ); + } + else if ( cOption == 'r' && oSettings.oFeatures.bProcessing ) + { + /* pRocessing */ + nTmp = _fnFeatureHtmlProcessing( oSettings ); + oSettings.anFeatures[cOption] = nTmp; + nInsertNode.appendChild( nTmp ); + } + else if ( cOption == 't' ) + { + /* Table */ + oSettings.anFeatures[cOption] = oSettings.nTable; + nInsertNode.appendChild( oSettings.nTable ); + } + else if ( cOption == 'i' && oSettings.oFeatures.bInfo ) + { + /* Info */ + nTmp = _fnFeatureHtmlInfo( oSettings ); + oSettings.anFeatures[cOption] = nTmp; + nInsertNode.appendChild( nTmp ); + } + else if ( cOption == 'p' && oSettings.oFeatures.bPaginate ) + { + /* Pagination */ + nTmp = _fnFeatureHtmlPaginate( oSettings ); + oSettings.anFeatures[cOption] = nTmp; + nInsertNode.appendChild( nTmp ); + } + else if ( _oExt.aoFeatures.length !== 0 ) + { + var aoFeatures = _oExt.aoFeatures; + for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ ) + { + if ( cOption == aoFeatures[k].cFeature ) + { + nTmp = aoFeatures[k].fnInit( oSettings ); + oSettings.anFeatures[cOption] = nTmp; + nInsertNode.appendChild( nTmp ); + break; + } + } + } + } + + /* Built our DOM structure - replace the holding div with what we want */ + nHolding.parentNode.replaceChild( nWrapper, nHolding ); + } + + /* + * Function: _fnFeatureHtmlFilter + * Purpose: Generate the node required for filtering text + * Returns: node + * Inputs: object:oSettings - dataTables settings object + */ + function _fnFeatureHtmlFilter ( oSettings ) + { + var nFilter = document.createElement( 'div' ); + if ( oSettings.sTableId !== '' ) + { + nFilter.setAttribute( 'id', oSettings.sTableId+'_filter' ); + } + nFilter.className = oSettings.oClasses.sFilter; + var sSpace = oSettings.oLanguage.sSearch==="" ? "" : " "; + nFilter.innerHTML = oSettings.oLanguage.sSearch+sSpace+'<input type="text" />'; + + var jqFilter = $("input", nFilter); + jqFilter.val( oSettings.oPreviousSearch.sSearch.replace('"','"') ); + jqFilter.keyup( function(e) { + _fnFilterComplete( oSettings, { + "sSearch": this.value, + "bEscapeRegex": oSettings.oPreviousSearch.bEscapeRegex + } ); + + /* Prevent default */ + return false; + } ); + + return nFilter; + } + + /* + * Function: _fnFeatureHtmlInfo + * Purpose: Generate the node required for the info display + * Returns: node + * Inputs: object:oSettings - dataTables settings object + */ + function _fnFeatureHtmlInfo ( oSettings ) + { + var nInfo = document.createElement( 'div' ); + if ( oSettings.sTableId !== '' ) + { + nInfo.setAttribute( 'id', oSettings.sTableId+'_info' ); + } + nInfo.className = oSettings.oClasses.sInfo; + return nInfo; + } + + /* + * Function: _fnFeatureHtmlPaginate + * Purpose: Generate the node required for default pagination + * Returns: node + * Inputs: object:oSettings - dataTables settings object + */ + function _fnFeatureHtmlPaginate ( oSettings ) + { + var nPaginate = document.createElement( 'div' ); + nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType; + oSettings.anFeatures.p = nPaginate; /* Need this stored in order to call paging plug-ins */ + + _oExt.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, function( oSettings ) { + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + } ); + return nPaginate; + } + + /* + * Function: _fnFeatureHtmlLength + * Purpose: Generate the node required for user display length changing + * Returns: node + * Inputs: object:oSettings - dataTables settings object + */ + function _fnFeatureHtmlLength ( oSettings ) + { + /* This can be overruled by not using the _MENU_ var/macro in the language variable */ + var sName = (oSettings.sTableId === "") ? "" : 'name="'+oSettings.sTableId+'_length"'; + var sStdMenu = + '<select size="1" '+sName+'>'+ + '<option value="10">10</option>'+ + '<option value="25">25</option>'+ + '<option value="50">50</option>'+ + '<option value="100">100</option>'+ + '</select>'; + + var nLength = document.createElement( 'div' ); + if ( oSettings.sTableId !== '' ) + { + nLength.setAttribute( 'id', oSettings.sTableId+'_length' ); + } + nLength.className = oSettings.oClasses.sLength; + nLength.innerHTML = oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu ); + + /* + * Set the length to the current display length - thanks to Andrea Pavlovic for this fix, + * and Stefan Skopnik for fixing the fix! + */ + $('select option[value="'+oSettings._iDisplayLength+'"]',nLength).attr("selected",true); + + $('select', nLength).change( function(e) { + oSettings._iDisplayLength = parseInt($(this).val(), 10); + + _fnCalculateEnd( oSettings ); + + /* If we have space to show extra rows (backing up from the end point - then do so */ + if ( oSettings._iDisplayEnd == oSettings.aiDisplay.length ) + { + oSettings._iDisplayStart = oSettings._iDisplayEnd - oSettings._iDisplayLength; + if ( oSettings._iDisplayStart < 0 ) + { + oSettings._iDisplayStart = 0; + } + } + + if ( oSettings._iDisplayLength == -1 ) + { + oSettings._iDisplayStart = 0; + } + + _fnDraw( oSettings ); + } ); + + return nLength; + } + + /* + * Function: _fnFeatureHtmlProcessing + * Purpose: Generate the node required for the processing node + * Returns: node + * Inputs: object:oSettings - dataTables settings object + */ + function _fnFeatureHtmlProcessing ( oSettings ) + { + var nProcessing = document.createElement( 'div' ); + + if ( oSettings.sTableId !== '' ) + { + nProcessing.setAttribute( 'id', oSettings.sTableId+'_processing' ); + } + nProcessing.innerHTML = oSettings.oLanguage.sProcessing; + nProcessing.className = oSettings.oClasses.sProcessing; + oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable ); + + return nProcessing; + } + + /* + * Function: _fnProcessingDisplay + * Purpose: Display or hide the processing indicator + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * bool: + * true - show the processing indicator + * false - don't show + */ + function _fnProcessingDisplay ( oSettings, bShow ) + { + if ( oSettings.oFeatures.bProcessing ) + { + oSettings.anFeatures.r.style.visibility = bShow ? "visible" : "hidden"; + } + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Filtering + */ + + /* + * Function: _fnFilterComplete + * Purpose: Filter the table using both the global filter and column based filtering + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * object:oSearch: search information + * int:iForce - optional - force a research of the master array (1) or not (undefined or 0) + */ + function _fnFilterComplete ( oSettings, oInput, iForce ) + { + /* Filter on everything */ + _fnFilter( oSettings, oInput.sSearch, iForce, oInput.bEscapeRegex ); + + /* Now do the individual column filter */ + for ( var i=0 ; i<oSettings.aoPreSearchCols.length ; i++ ) + { + _fnFilterColumn( oSettings, oSettings.aoPreSearchCols[i].sSearch, i, + oSettings.aoPreSearchCols[i].bEscapeRegex ); + } + + /* Custom filtering */ + if ( _oExt.afnFiltering.length !== 0 ) + { + _fnFilterCustom( oSettings ); + } + + /* Redraw the table */ + if ( typeof oSettings.iInitDisplayStart != 'undefined' && oSettings.iInitDisplayStart != -1 ) + { + oSettings._iDisplayStart = oSettings.iInitDisplayStart; + oSettings.iInitDisplayStart = -1; + } + else + { + oSettings._iDisplayStart = 0; + } + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + + /* Rebuild search array 'offline' */ + _fnBuildSearchArray( oSettings, 0 ); + } + + /* + * Function: _fnFilterCustom + * Purpose: Apply custom filtering functions + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnFilterCustom( oSettings ) + { + var afnFilters = _oExt.afnFiltering; + for ( var i=0, iLen=afnFilters.length ; i<iLen ; i++ ) + { + var iCorrector = 0; + for ( var j=0, jLen=oSettings.aiDisplay.length ; j<jLen ; j++ ) + { + var iDisIndex = oSettings.aiDisplay[j-iCorrector]; + + /* Check if we should use this row based on the filtering function */ + if ( !afnFilters[i]( oSettings, oSettings.aoData[iDisIndex]._aData, iDisIndex ) ) + { + oSettings.aiDisplay.splice( j-iCorrector, 1 ); + iCorrector++; + } + } + } + } + + /* + * Function: _fnFilterColumn + * Purpose: Filter the table on a per-column basis + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * string:sInput - string to filter on + * int:iColumn - column to filter + * bool:bEscapeRegex - escape regex or not + */ + function _fnFilterColumn ( oSettings, sInput, iColumn, bEscapeRegex ) + { + if ( sInput === "" ) + { + return; + } + + var iIndexCorrector = 0; + var sRegexMatch = bEscapeRegex ? _fnEscapeRegex( sInput ) : sInput; + var rpSearch = new RegExp( sRegexMatch, "i" ); + + for ( var i=oSettings.aiDisplay.length-1 ; i>=0 ; i-- ) + { + var sData = _fnDataToSearch( oSettings.aoData[ oSettings.aiDisplay[i] ]._aData[iColumn], + oSettings.aoColumns[iColumn].sType ); + if ( ! rpSearch.test( sData ) ) + { + oSettings.aiDisplay.splice( i, 1 ); + iIndexCorrector++; + } + } + } + + /* + * Function: _fnFilter + * Purpose: Filter the data table based on user input and draw the table + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * string:sInput - string to filter on + * int:iForce - optional - force a research of the master array (1) or not (undefined or 0) + * bool:bEscapeRegex - escape regex or not + */ + function _fnFilter( oSettings, sInput, iForce, bEscapeRegex ) + { + var i; + + /* Check if we are forcing or not - optional parameter */ + if ( typeof iForce == 'undefined' || iForce === null ) + { + iForce = 0; + } + + /* Need to take account of custom filtering functions always */ + if ( _oExt.afnFiltering.length !== 0 ) + { + iForce = 1; + } + + /* Generate the regular expression to use. Something along the lines of: + * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$ + */ + var asSearch = bEscapeRegex ? + _fnEscapeRegex( sInput ).split( ' ' ) : + sInput.split( ' ' ); + var sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$'; + var rpSearch = new RegExp( sRegExpString, "i" ); /* case insensitive */ + + /* + * If the input is blank - we want the full data set + */ + if ( sInput.length <= 0 ) + { + oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + } + else + { + /* + * We are starting a new search or the new search string is smaller + * then the old one (i.e. delete). Search from the master array + */ + if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length || + oSettings.oPreviousSearch.sSearch.length > sInput.length || iForce == 1 || + sInput.indexOf(oSettings.oPreviousSearch.sSearch) !== 0 ) + { + /* Nuke the old display array - we are going to rebuild it */ + oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); + + /* Force a rebuild of the search array */ + _fnBuildSearchArray( oSettings, 1 ); + + /* Search through all records to populate the search array + * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1 + * mapping + */ + for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ ) + { + if ( rpSearch.test(oSettings.asDataSearch[i]) ) + { + oSettings.aiDisplay.push( oSettings.aiDisplayMaster[i] ); + } + } + } + else + { + /* Using old search array - refine it - do it this way for speed + * Don't have to search the whole master array again + */ + var iIndexCorrector = 0; + + /* Search the current results */ + for ( i=0 ; i<oSettings.asDataSearch.length ; i++ ) + { + if ( ! rpSearch.test(oSettings.asDataSearch[i]) ) + { + oSettings.aiDisplay.splice( i-iIndexCorrector, 1 ); + iIndexCorrector++; + } + } + } + } + oSettings.oPreviousSearch.sSearch = sInput; + oSettings.oPreviousSearch.bEscapeRegex = bEscapeRegex; + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Sorting + */ + + /* + * Function: _fnSort + * Purpose: Change the order of the table + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * bool:bApplyClasses - optional - should we apply classes or not + * Notes: We always sort the master array and then apply a filter again + * if it is needed. This probably isn't optimal - but atm I can't think + * of any other way which is (each has disadvantages) + */ + function _fnSort ( oSettings, bApplyClasses ) + { + /* + * Funny one this - we want to sort aiDisplayMaster - but according to aoData[]._aData + * + * function _fnSortText ( a, b ) + * { + * var iTest; + * var oSort = _oExt.oSort; + * + * iTest = oSort['string-asc']( aoData[ a ]._aData[ COL ], aoData[ b ]._aData[ COL ] ); + * if ( iTest === 0 ) + * ... + * } + */ + + /* Here is what we are looking to achieve here (custom sort functions add complication...) + * function _fnSortText ( a, b ) + * { + * var iTest; + * var oSort = _oExt.oSort; + * iTest = oSort['string-asc']( a[0], b[0] ); + * if ( iTest === 0 ) + * iTest = oSort['string-asc']( a[1], b[1] ); + * if ( iTest === 0 ) + * iTest = oSort['string-asc']( a[2], b[2] ); + * + * return iTest; + * } + */ + var aaSort = []; + var oSort = _oExt.oSort; + var aoData = oSettings.aoData; + var iDataSort; + var iDataType; + var i; + + if ( oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null ) + { + if ( oSettings.aaSortingFixed !== null ) + { + aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ); + } + else + { + aaSort = oSettings.aaSorting.slice(); + } + + if ( !window.runtime ) + { + var fnLocalSorting; + var sDynamicSort = "fnLocalSorting = function(a,b){"+ + "var iTest;"; + + for ( i=0 ; i<aaSort.length-1 ; i++ ) + { + iDataSort = oSettings.aoColumns[ aaSort[i][0] ].iDataSort; + iDataType = oSettings.aoColumns[ iDataSort ].sType; + sDynamicSort += "iTest = oSort['"+iDataType+"-"+aaSort[i][1]+"']"+ + "( aoData[a]._aData["+iDataSort+"], aoData[b]._aData["+iDataSort+"] ); if ( iTest === 0 )"; + } + + iDataSort = oSettings.aoColumns[ aaSort[aaSort.length-1][0] ].iDataSort; + iDataType = oSettings.aoColumns[ iDataSort ].sType; + sDynamicSort += "iTest = oSort['"+iDataType+"-"+aaSort[aaSort.length-1][1]+"']"+ + "( aoData[a]._aData["+iDataSort+"], aoData[b]._aData["+iDataSort+"] ); return iTest;}"; + + /* The eval has to be done to a variable for IE */ + eval( sDynamicSort ); + oSettings.aiDisplayMaster.sort( fnLocalSorting ); + } + else + { + /* + * Support for Adobe AIR - AIR doesn't allow eval with a function + * Note that for reasonable sized data sets this method is around 1.5 times slower than + * the eval above (hence why it is not used all the time). Oddly enough, it is ever so + * slightly faster for very small sets (presumably the eval has overhead). + * Single column (1083 records) - eval: 32mS AIR: 38mS + * Two columns (1083 records) - eval: 55mS AIR: 66mS + */ + + /* Build a cached array so the sort doesn't have to process this stuff on every call */ + var aAirSort = []; + var iLen = aaSort.length; + for ( i=0 ; i<iLen ; i++ ) + { + iDataSort = oSettings.aoColumns[ aaSort[i][0] ].iDataSort; + aAirSort.push( [ + iDataSort, + oSettings.aoColumns[ iDataSort ].sType+'-'+aaSort[i][1] + ] ); + } + + oSettings.aiDisplayMaster.sort( function (a,b) { + var iTest; + for ( var i=0 ; i<iLen ; i++ ) + { + iTest = oSort[ aAirSort[i][1] ]( aoData[a]._aData[aAirSort[i][0]], aoData[b]._aData[aAirSort[i][0]] ); + if ( iTest !== 0 ) + { + return iTest; + } + } + return 0; + } ); + } + } + + /* Alter the sorting classes to take account of the changes */ + if ( typeof bApplyClasses == 'undefined' || bApplyClasses ) + { + _fnSortingClasses( oSettings ); + } + + /* Copy the master data into the draw array and re-draw */ + if ( oSettings.oFeatures.bFilter ) + { + /* _fnFilter() will redraw the table for us */ + _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 ); + } + else + { + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + oSettings._iDisplayStart = 0; /* reset display back to page 0 */ + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + } + } + + /* + * Function: _fnSortingClasses + * Purpose: Set the sortting classes on the header + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnSortingClasses( oSettings ) + { + var i, j, iFound; + var aaSort, sClass; + var iColumns = oSettings.aoColumns.length; + var oClasses = oSettings.oClasses; + + for ( i=0 ; i<iColumns ; i++ ) + { + $(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc + + " "+ oClasses.sSortable ); + } + + if ( oSettings.aaSortingFixed !== null ) + { + aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ); + } + else + { + aaSort = oSettings.aaSorting.slice(); + } + + /* Apply the required classes to the header */ + for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) + { + if ( oSettings.aoColumns[i].bSortable && oSettings.aoColumns[i].bVisible ) + { + sClass = oClasses.sSortable; + iFound = -1; + for ( j=0 ; j<aaSort.length ; j++ ) + { + if ( aaSort[j][0] == i ) + { + sClass = ( aaSort[j][1] == "asc" ) ? + oClasses.sSortAsc : oClasses.sSortDesc; + iFound = j; + break; + } + } + $(oSettings.aoColumns[i].nTh).addClass( sClass ); + + if ( oSettings.bJUI ) + { + /* jQuery UI uses extra markup */ + var jqSpan = $("span", oSettings.aoColumns[i].nTh); + jqSpan.removeClass(oClasses.sSortJUIAsc +" "+ oClasses.sSortJUIDesc +" "+ + oClasses.sSortJUI); + + var sSpanClass; + if ( iFound == -1 ) + { + sSpanClass = oClasses.sSortJUI; + } + else if ( aaSort[iFound][1] == "asc" ) + { + sSpanClass = oClasses.sSortJUIAsc; + } + else + { + sSpanClass = oClasses.sSortJUIDesc; + } + + jqSpan.addClass( sSpanClass ); + } + } + } + + /* + * Apply the required classes to the table body + * Note that this is given as a feature switch since it can significantly slow down a sort + * on large data sets (adding and removing of classes is always slow at the best of times..) + */ + if ( oSettings.oFeatures.bSortClasses ) + { + var nTrs = _fnGetTrNodes( oSettings ); + sClass = oClasses.sSortColumn; + $('td', nTrs).removeClass( sClass+"1 "+sClass+"2 "+sClass+"3" ); + + var iClass = 1; + for ( i=0 ; i<aaSort.length ; i++ ) + { + var iVis = _fnColumnIndexToVisible(oSettings, aaSort[i][0]); + if ( iVis !== null ) + { + /* Limit the number of classes to three */ + if ( iClass <= 2 ) + { + $('td:eq('+iVis+')', nTrs).addClass( sClass+iClass ); + } + else + { + $('td:eq('+iVis+')', nTrs).addClass( sClass+'3' ); + } + iClass++; + } + } + } + } + + /* + * Function: _fnVisibleToColumnIndex + * Purpose: Covert the index of a visible column to the index in the data array (take account + * of hidden columns) + * Returns: int:i - the data index + * Inputs: object:oSettings - dataTables settings object + */ + function _fnVisibleToColumnIndex( oSettings, iMatch ) + { + var iColumn = -1; + + for ( var i=0 ; i<oSettings.aoColumns.length ; i++ ) + { + if ( oSettings.aoColumns[i].bVisible === true ) + { + iColumn++; + } + + if ( iColumn == iMatch ) + { + return i; + } + } + + return null; + } + + /* + * Function: _fnColumnIndexToVisible + * Purpose: Covert the index of an index in the data array and convert it to the visible + * column index (take account of hidden columns) + * Returns: int:i - the data index + * Inputs: object:oSettings - dataTables settings object + */ + function _fnColumnIndexToVisible( oSettings, iMatch ) + { + var iVisible = -1; + for ( var i=0 ; i<oSettings.aoColumns.length ; i++ ) + { + if ( oSettings.aoColumns[i].bVisible === true ) + { + iVisible++; + } + + if ( i == iMatch ) + { + return oSettings.aoColumns[i].bVisible === true ? iVisible : null; + } + } + + return null; + } + + /* + * Function: _fnVisbleColumns + * Purpose: Get the number of visible columns + * Returns: int:i - the number of visible columns + * Inputs: object:oS - dataTables settings object + */ + function _fnVisbleColumns( oS ) + { + var iVis = 0; + for ( var i=0 ; i<oS.aoColumns.length ; i++ ) + { + if ( oS.aoColumns[i].bVisible === true ) + { + iVis++; + } + } + return iVis; + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Support functions + */ + + /* + * Function: _fnBuildSearchArray + * Purpose: Create an array which can be quickly search through + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * int:iMaster - use the master data array - optional + */ + function _fnBuildSearchArray ( oSettings, iMaster ) + { + /* Clear out the old data */ + oSettings.asDataSearch.splice( 0, oSettings.asDataSearch.length ); + + var aArray = (typeof iMaster != 'undefined' && iMaster == 1) ? + oSettings.aiDisplayMaster : oSettings.aiDisplay; + + for ( var i=0, iLen=aArray.length ; i<iLen ; i++ ) + { + oSettings.asDataSearch[i] = ''; + for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ ) + { + if ( oSettings.aoColumns[j].bSearchable ) + { + var sData = oSettings.aoData[ aArray[i] ]._aData[j]; + oSettings.asDataSearch[i] += _fnDataToSearch( sData, oSettings.aoColumns[j].sType )+' '; + } + } + } + } + + /* + * Function: _fnDataToSearch + * Purpose: Convert raw data into something that the user can search on + * Returns: string: - search string + * Inputs: string:sData - data to be modified + * string:sType - data type + */ + function _fnDataToSearch ( sData, sType ) + { + + if ( typeof _oExt.ofnSearch[sType] == "function" ) + { + return _oExt.ofnSearch[sType]( sData ); + } + else if ( sType == "html" ) + { + return sData.replace(/\n/g," ").replace( /<.*?>/g, "" ); + } + else if ( typeof sData == "string" ) + { + return sData.replace(/\n/g," "); + } + return sData; + } + + /* + * Function: _fnCalculateEnd + * Purpose: Rcalculate the end point based on the start point + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnCalculateEnd( oSettings ) + { + if ( oSettings.oFeatures.bPaginate === false ) + { + oSettings._iDisplayEnd = oSettings.aiDisplay.length; + } + else + { + /* Set the end point of the display - based on how many elements there are + * still to display + */ + if ( oSettings._iDisplayStart + oSettings._iDisplayLength > oSettings.aiDisplay.length || + oSettings._iDisplayLength == -1 ) + { + oSettings._iDisplayEnd = oSettings.aiDisplay.length; + } + else + { + oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength; + } + } + } + + /* + * Function: _fnConvertToWidth + * Purpose: Convert a CSS unit width to pixels (e.g. 2em) + * Returns: int:iWidth - width in pixels + * Inputs: string:sWidth - width to be converted + * node:nParent - parent to get the with for (required for + * relative widths) - optional + */ + function _fnConvertToWidth ( sWidth, nParent ) + { + if ( !sWidth || sWidth === null || sWidth === '' ) + { + return 0; + } + + if ( typeof nParent == "undefined" ) + { + nParent = document.getElementsByTagName('body')[0]; + } + + var iWidth; + var nTmp = document.createElement( "div" ); + nTmp.style.width = sWidth; + + nParent.appendChild( nTmp ); + iWidth = nTmp.offsetWidth; + nParent.removeChild( nTmp ); + + return ( iWidth ); + } + + /* + * Function: _fnCalculateColumnWidths + * Purpose: Calculate the width of columns for the table + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnCalculateColumnWidths ( oSettings ) + { + var iTableWidth = oSettings.nTable.offsetWidth; + var iTotalUserIpSize = 0; + var iTmpWidth; + var iVisibleColumns = 0; + var iColums = oSettings.aoColumns.length; + var i; + var oHeaders = $('thead th', oSettings.nTable); + + /* Convert any user input sizes into pixel sizes */ + for ( i=0 ; i<iColums ; i++ ) + { + if ( oSettings.aoColumns[i].bVisible ) + { + iVisibleColumns++; + + if ( oSettings.aoColumns[i].sWidth !== null ) + { + iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidth, + oSettings.nTable.parentNode ); + + /* Total up the user defined widths for later calculations */ + iTotalUserIpSize += iTmpWidth; + + oSettings.aoColumns[i].sWidth = iTmpWidth+"px"; + } + } + } + + /* If the number of columns in the DOM equals the number that we + * have to process in dataTables, then we can use the offsets that are + * created by the web-browser. No custom sizes can be set in order for + * this to happen + */ + if ( iColums == oHeaders.length && iTotalUserIpSize === 0 && iVisibleColumns == iColums ) + { + for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) + { + oSettings.aoColumns[i].sWidth = oHeaders[i].offsetWidth+"px"; + } + } + else + { + /* Otherwise we are going to have to do some calculations to get + * the width of each column. Construct a 1 row table with the maximum + * string sizes in the data, and any user defined widths + */ + var nCalcTmp = oSettings.nTable.cloneNode( false ); + nCalcTmp.setAttribute( "id", '' ); + + var sTableTmp = '<table class="'+nCalcTmp.className+'">'; + var sCalcHead = "<tr>"; + var sCalcHtml = "<tr>"; + + /* Construct a tempory table which we will inject (invisibly) into + * the dom - to let the browser do all the hard word + */ + for ( i=0 ; i<iColums ; i++ ) + { + if ( oSettings.aoColumns[i].bVisible ) + { + sCalcHead += '<th>'+oSettings.aoColumns[i].sTitle+'</th>'; + + if ( oSettings.aoColumns[i].sWidth !== null ) + { + var sWidth = ''; + if ( oSettings.aoColumns[i].sWidth !== null ) + { + sWidth = ' style="width:'+oSettings.aoColumns[i].sWidth+';"'; + } + + sCalcHtml += '<td'+sWidth+' tag_index="'+i+'">'+fnGetMaxLenString( oSettings, i)+'</td>'; + } + else + { + sCalcHtml += '<td tag_index="'+i+'">'+fnGetMaxLenString( oSettings, i)+'</td>'; + } + } + } + + sCalcHead += "</tr>"; + sCalcHtml += "</tr>"; + + /* Create the tmp table node (thank you jQuery) */ + nCalcTmp = $( sTableTmp + sCalcHead + sCalcHtml +'</table>' )[0]; + nCalcTmp.style.width = iTableWidth + "px"; + nCalcTmp.style.visibility = "hidden"; + nCalcTmp.style.position = "absolute"; /* Try to aviod scroll bar */ + + oSettings.nTable.parentNode.appendChild( nCalcTmp ); + + var oNodes = $("td", nCalcTmp); + var iIndex; + + /* Gather in the browser calculated widths for the rows */ + for ( i=0 ; i<oNodes.length ; i++ ) + { + iIndex = oNodes[i].getAttribute('tag_index'); + + oSettings.aoColumns[iIndex].sWidth = $("td", nCalcTmp)[i].offsetWidth +"px"; + } + + oSettings.nTable.parentNode.removeChild( nCalcTmp ); + } + } + + /* + * Function: fnGetMaxLenString + * Purpose: Get the maximum strlen for each data column + * Returns: string: - max strlens for each column + * Inputs: object:oSettings - dataTables settings object + * int:iCol - column of interest + */ + function fnGetMaxLenString( oSettings, iCol ) + { + var iMax = 0; + var iMaxIndex = -1; + + for ( var i=0 ; i<oSettings.aoData.length ; i++ ) + { + if ( oSettings.aoData[i]._aData[iCol].length > iMax ) + { + iMax = oSettings.aoData[i]._aData[iCol].length; + iMaxIndex = i; + } + } + + if ( iMaxIndex >= 0 ) + { + return oSettings.aoData[iMaxIndex]._aData[iCol]; + } + return ''; + } + + /* + * Function: _fnArrayCmp + * Purpose: Compare two arrays + * Returns: 0 if match, 1 if length is different, 2 if no match + * Inputs: array:aArray1 - first array + * array:aArray2 - second array + */ + function _fnArrayCmp( aArray1, aArray2 ) + { + if ( aArray1.length != aArray2.length ) + { + return 1; + } + + for ( var i=0 ; i<aArray1.length ; i++ ) + { + if ( aArray1[i] != aArray2[i] ) + { + return 2; + } + } + + return 0; + } + + /* + * Function: _fnDetectType + * Purpose: Get the sort type based on an input string + * Returns: string: - type (defaults to 'string' if no type can be detected) + * Inputs: string:sData - data we wish to know the type of + * Notes: This function makes use of the DataTables plugin objct _oExt + * (.aTypes) such that new types can easily be added. + */ + function _fnDetectType( sData ) + { + var aTypes = _oExt.aTypes; + var iLen = aTypes.length; + + for ( var i=0 ; i<iLen ; i++ ) + { + var sType = aTypes[i]( sData ); + if ( sType !== null ) + { + return sType; + } + } + + return 'string'; + } + + /* + * Function: _fnSettingsFromNode + * Purpose: Return the settings object for a particular table + * Returns: object: Settings object - or null if not found + * Inputs: node:nTable - table we are using as a dataTable + */ + function _fnSettingsFromNode ( nTable ) + { + for ( var i=0 ; i<_aoSettings.length ; i++ ) + { + if ( _aoSettings[i].nTable == nTable ) + { + return _aoSettings[i]; + } + } + + return null; + } + + /* + * Function: _fnGetDataMaster + * Purpose: Return an array with the full table data + * Returns: array array:aData - Master data array + * Inputs: object:oSettings - dataTables settings object + */ + function _fnGetDataMaster ( oSettings ) + { + var aData = []; + var iLen = oSettings.aoData.length; + for ( var i=0 ; i<iLen; i++ ) + { + if ( oSettings.aoData[i] === null ) + { + aData.push( null ); + } + else + { + aData.push( oSettings.aoData[i]._aData ); + } + } + return aData; + } + + /* + * Function: _fnGetTrNodes + * Purpose: Return an array with the TR nodes for the table + * Returns: array array:aData - TR array + * Inputs: object:oSettings - dataTables settings object + */ + function _fnGetTrNodes ( oSettings ) + { + var aNodes = []; + var iLen = oSettings.aoData.length; + for ( var i=0 ; i<iLen ; i++ ) + { + if ( oSettings.aoData[i] === null ) + { + aNodes.push( null ); + } + else + { + aNodes.push( oSettings.aoData[i].nTr ); + } + } + return aNodes; + } + + /* + * Function: _fnEscapeRegex + * Purpose: scape a string stuch that it can be used in a regular expression + * Returns: string: - escaped string + * Inputs: string:sVal - string to escape + */ + function _fnEscapeRegex ( sVal ) + { + var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^' ]; + var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' ); + return sVal.replace(reReplace, '\\$1'); + } + + /* + * Function: _fnReOrderIndex + * Purpose: Figure out how to reorder a display list + * Returns: array int:aiReturn - index list for reordering + * Inputs: object:oSettings - dataTables settings object + */ + function _fnReOrderIndex ( oSettings, sColumns ) + { + var aColumns = sColumns.split(','); + var aiReturn = []; + + for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) + { + for ( var j=0 ; j<iLen ; j++ ) + { + if ( oSettings.aoColumns[i].sName == aColumns[j] ) + { + aiReturn.push( j ); + break; + } + } + } + + return aiReturn; + } + + /* + * Function: _fnColumnOrdering + * Purpose: Get the column ordering that DataTables expects + * Returns: string: - comma separated list of names + * Inputs: object:oSettings - dataTables settings object + */ + function _fnColumnOrdering ( oSettings ) + { + var sNames = ''; + for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) + { + sNames += oSettings.aoColumns[i].sName+','; + } + if ( sNames.length == iLen ) + { + return ""; + } + return sNames.slice(0, -1); + } + + /* + * Function: _fnClearTable + * Purpose: Nuke the table + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnClearTable( oSettings ) + { + oSettings.aoData.length = 0; + oSettings.aiDisplayMaster.length = 0; + oSettings.aiDisplay.length = 0; + _fnCalculateEnd( oSettings ); + } + + /* + * Function: _fnSaveState + * Purpose: Save the state of a table in a cookie such that the page can be reloaded + * Returns: - + * Inputs: object:oSettings - dataTables settings object + */ + function _fnSaveState ( oSettings ) + { + if ( !oSettings.oFeatures.bStateSave ) + { + return; + } + + /* Store the interesting variables */ + var i; + var sValue = "{"; + sValue += '"iStart": '+oSettings._iDisplayStart+','; + sValue += '"iEnd": '+oSettings._iDisplayEnd+','; + sValue += '"iLength": '+oSettings._iDisplayLength+','; + sValue += '"sFilter": "'+oSettings.oPreviousSearch.sSearch.replace('"','\\"')+'",'; + sValue += '"sFilterEsc": '+oSettings.oPreviousSearch.bEscapeRegex+','; + + sValue += '"aaSorting": [ '; + for ( i=0 ; i<oSettings.aaSorting.length ; i++ ) + { + sValue += "["+oSettings.aaSorting[i][0]+",'"+oSettings.aaSorting[i][1]+"'],"; + } + sValue = sValue.substring(0, sValue.length-1); + sValue += "],"; + + sValue += '"aaSearchCols": [ '; + for ( i=0 ; i<oSettings.aoPreSearchCols.length ; i++ ) + { + sValue += "['"+oSettings.aoPreSearchCols[i].sSearch.replace("'","\'")+ + "',"+oSettings.aoPreSearchCols[i].bEscapeRegex+"],"; + } + sValue = sValue.substring(0, sValue.length-1); + sValue += "],"; + + sValue += '"abVisCols": [ '; + for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) + { + sValue += oSettings.aoColumns[i].bVisible+","; + } + sValue = sValue.substring(0, sValue.length-1); + sValue += "]"; + + sValue += "}"; + _fnCreateCookie( "SpryMedia_DataTables_"+oSettings.sInstance, sValue, + oSettings.iCookieDuration ); + } + + /* + * Function: _fnLoadState + * Purpose: Attempt to load a saved table state from a cookie + * Returns: - + * Inputs: object:oSettings - dataTables settings object + * object:oInit - DataTables init object so we can override settings + */ + function _fnLoadState ( oSettings, oInit ) + { + if ( !oSettings.oFeatures.bStateSave ) + { + return; + } + + var oData; + var sData = _fnReadCookie( "SpryMedia_DataTables_"+oSettings.sInstance ); + if ( sData !== null && sData !== '' ) + { + /* Try/catch the JSON eval - if it is bad then we ignore it */ + try + { + /* Use the JSON library for safety - if it is available */ + if ( typeof JSON == 'object' && typeof JSON.parse == 'function' ) + { + /* DT 1.4.0 used single quotes for a string - JSON.parse doesn't allow this and throws + * an error. So for now we can do this. This can be removed in future it is just to + * allow the tranfrer to 1.4.1+ to occur + */ + oData = JSON.parse( sData.replace(/'/g, '"') ); + } + else + { + oData = eval( '('+sData+')' ); + } + } + catch( e ) + { + return; + } + + /* Restore key features */ + oSettings._iDisplayStart = oData.iStart; + oSettings.iInitDisplayStart = oData.iStart; + oSettings._iDisplayEnd = oData.iEnd; + oSettings._iDisplayLength = oData.iLength; + oSettings.oPreviousSearch.sSearch = oData.sFilter; + oSettings.aaSorting = oData.aaSorting.slice(); + + /* Search filtering - global reference added in 1.4.1 */ + if ( typeof oData.sFilterEsc != 'undefined' ) + { + oSettings.oPreviousSearch.bEscapeRegex = oData.sFilterEsc; + } + + /* Column filtering - added in 1.5.0 beta 6 */ + if ( typeof oData.aaSearchCols != 'undefined' ) + { + for ( var i=0 ; i<oData.aaSearchCols.length ; i++ ) + { + oSettings.aoPreSearchCols[i] = { + "sSearch": oData.aaSearchCols[i][0], + "bEscapeRegex": oData.aaSearchCols[i][1] + }; + } + } + + /* Column visibility state - added in 1.5.0 beta 10 */ + if ( typeof oData.abVisCols != 'undefined' ) + { + /* We need to override the settings in oInit for this */ + if ( typeof oInit.aoColumns == 'undefined' ) + { + oInit.aoColumns = []; + } + + for ( i=0 ; i<oData.abVisCols.length ; i++ ) + { + if ( typeof oInit.aoColumns[i] == 'undefined' || oInit.aoColumns[i] === null ) + { + oInit.aoColumns[i] = {}; + } + + oInit.aoColumns[i].bVisible = oData.abVisCols[i]; + } + } + } + } + + /* + * Function: _fnCreateCookie + * Purpose: Create a new cookie with a value to store the state of a table + * Returns: - + * Inputs: string:sName - name of the cookie to create + * string:sValue - the value the cookie should take + * int:iSecs - duration of the cookie + */ + function _fnCreateCookie ( sName, sValue, iSecs ) + { + var date = new Date(); + date.setTime( date.getTime()+(iSecs*1000) ); + + /* + * Shocking but true - it would appear IE has major issues with having the path being + * set to anything but root. We need the cookie to be available based on the path, so we + * have to append the pathname to the cookie name. Appalling. + */ + sName += '_'+window.location.pathname.replace(/[\/:]/g,"").toLowerCase(); + + document.cookie = sName+"="+sValue+"; expires="+date.toGMTString()+"; path=/"; + } + + /* + * Function: _fnReadCookie + * Purpose: Read an old cookie to get a cookie with an old table state + * Returns: string: - contents of the cookie - or null if no cookie with that name found + * Inputs: string:sName - name of the cookie to read + */ + function _fnReadCookie ( sName ) + { + var sNameEQ = sName +'_'+ window.location.pathname.replace(/[\/:]/g,"").toLowerCase() + "="; + var sCookieContents = document.cookie.split(';'); + + for( var i=0 ; i<sCookieContents.length ; i++ ) + { + var c = sCookieContents[i]; + + while (c.charAt(0)==' ') + { + c = c.substring(1,c.length); + } + + if (c.indexOf(sNameEQ) === 0) + { + return c.substring(sNameEQ.length,c.length); + } + } + return null; + } + + /* + * Function: _fnGetUniqueThs + * Purpose: Get an array of unique th elements, one for each column + * Returns: array node:aReturn - list of unique ths + * Inputs: node:nThead - The thead element for the table + */ + function _fnGetUniqueThs ( nThead ) + { + var nTrs = nThead.getElementsByTagName('tr'); + + /* Nice simple case */ + if ( nTrs.length == 1 ) + { + return nTrs[0].getElementsByTagName('th'); + } + + /* Otherwise we need to figure out the layout array to get the nodes */ + var aLayout = [], aReturn = []; + var ROWSPAN = 2, COLSPAN = 3, TDELEM = 4; + var i, j, k, iLen, jLen, iColumnShifted; + var fnShiftCol = function ( a, i, j ) { + while ( typeof a[i][j] != 'undefined' ) { + j++; + } + return j; + }; + var fnAddRow = function ( i ) { + if ( typeof aLayout[i] == 'undefined' ) { + aLayout[i] = []; + } + }; + + /* Calculate a layout array */ + for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) + { + fnAddRow( i ); + var iColumn = 0; + var nTds = []; + + for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ ) + { + if ( nTrs[i].childNodes[j].nodeName == "TD" || nTrs[i].childNodes[j].nodeName == "TH" ) + { + nTds.push( nTrs[i].childNodes[j] ); + } + } + + for ( j=0, jLen=nTds.length ; j<jLen ; j++ ) + { + var iColspan = nTds[j].getAttribute('colspan') * 1; + var iRowspan = nTds[j].getAttribute('rowspan') * 1; + + if ( !iColspan || iColspan===0 || iColspan===1 ) + { + iColumnShifted = fnShiftCol( aLayout, i, iColumn ); + aLayout[i][iColumnShifted] = (nTds[j].nodeName=="TD") ? TDELEM : nTds[j]; + if ( iRowspan || iRowspan===0 || iRowspan===1 ) + { + for ( k=1 ; k<iRowspan ; k++ ) + { + fnAddRow( i+k ); + aLayout[i+k][iColumnShifted] = ROWSPAN; + } + } + iColumn++; + } + else + { + iColumnShifted = fnShiftCol( aLayout, i, iColumn ); + for ( k=0 ; k<iColspan ; k++ ) + { + aLayout[i][iColumnShifted+k] = COLSPAN; + } + iColumn += iColspan; + } + } + } + + /* Convert the layout array into a node array + * Note the use of aLayout[0] in the outloop, we want the outer loop to occur the same + * number of times as there are columns. Unusual having nested loops this way around + * but is what we need here. + */ + for ( i=0, iLen=aLayout[0].length ; i<iLen ; i++ ) + { + for ( j=0, jLen=aLayout.length ; j<jLen ; j++ ) + { + if ( typeof aLayout[j][i] == 'object' ) + { + aReturn.push( aLayout[j][i] ); + } + } + } + + return aReturn; + } + + /* + * Function: _fnMap + * Purpose: See if a property is defined on one object, if so assign it to the other object + * Returns: - (done by reference) + * Inputs: object:oRet - target object + * object:oSrc - source object + * string:sName - property + * string:sMappedName - name to map too - optional, sName used if not given + */ + function _fnMap( oRet, oSrc, sName, sMappedName ) + { + if ( typeof sMappedName == 'undefined' ) + { + sMappedName = sName; + } + if ( typeof oSrc[sName] != 'undefined' ) + { + oRet[sMappedName] = oSrc[sName]; + } + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * API + * + * I'm not overly happy with this solution - I'd much rather that there was a way of getting + * a list of all the private functions and do what we need to dynamically - but that doesn't + * appear to be possible. Bonkers. A better solution would be to provide a 'bind' type object + * To do - bind type method in DTs 1.6. + */ + this.oApi._fnInitalise = _fnInitalise; + this.oApi._fnLanguageProcess = _fnLanguageProcess; + this.oApi._fnAddColumn = _fnAddColumn; + this.oApi._fnAddData = _fnAddData; + this.oApi._fnGatherData = _fnGatherData; + this.oApi._fnDrawHead = _fnDrawHead; + this.oApi._fnDraw = _fnDraw; + this.oApi._fnAjaxUpdate = _fnAjaxUpdate; + this.oApi._fnAddOptionsHtml = _fnAddOptionsHtml; + this.oApi._fnFeatureHtmlFilter = _fnFeatureHtmlFilter; + this.oApi._fnFeatureHtmlInfo = _fnFeatureHtmlInfo; + this.oApi._fnFeatureHtmlPaginate = _fnFeatureHtmlPaginate; + this.oApi._fnFeatureHtmlLength = _fnFeatureHtmlLength; + this.oApi._fnFeatureHtmlProcessing = _fnFeatureHtmlProcessing; + this.oApi._fnProcessingDisplay = _fnProcessingDisplay; + this.oApi._fnFilterComplete = _fnFilterComplete; + this.oApi._fnFilterColumn = _fnFilterColumn; + this.oApi._fnFilter = _fnFilter; + this.oApi._fnSortingClasses = _fnSortingClasses; + this.oApi._fnVisibleToColumnIndex = _fnVisibleToColumnIndex; + this.oApi._fnColumnIndexToVisible = _fnColumnIndexToVisible; + this.oApi._fnVisbleColumns = _fnVisbleColumns; + this.oApi._fnBuildSearchArray = _fnBuildSearchArray; + this.oApi._fnDataToSearch = _fnDataToSearch; + this.oApi._fnCalculateEnd = _fnCalculateEnd; + this.oApi._fnConvertToWidth = _fnConvertToWidth; + this.oApi._fnCalculateColumnWidths = _fnCalculateColumnWidths; + this.oApi._fnArrayCmp = _fnArrayCmp; + this.oApi._fnDetectType = _fnDetectType; + this.oApi._fnGetDataMaster = _fnGetDataMaster; + this.oApi._fnGetTrNodes = _fnGetTrNodes; + this.oApi._fnEscapeRegex = _fnEscapeRegex; + this.oApi._fnReOrderIndex = _fnReOrderIndex; + this.oApi._fnColumnOrdering = _fnColumnOrdering; + this.oApi._fnClearTable = _fnClearTable; + this.oApi._fnSaveState = _fnSaveState; + this.oApi._fnLoadState = _fnLoadState; + this.oApi._fnCreateCookie = _fnCreateCookie; + this.oApi._fnReadCookie = _fnReadCookie; + this.oApi._fnGetUniqueThs = _fnGetUniqueThs; + + /* Want to be able to reference "this" inside the this.each function */ + var _that = this; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Constructor + */ + return this.each(function() + { + /* Make a complete and independent copy of the settings object */ + var oSettings = new classSettings(); + _aoSettings.push( oSettings ); + + var i=0, iLen; + var bInitHandedOff = false; + var bUsePassedData = false; + + /* Set the id */ + var sId = this.getAttribute( 'id' ); + if ( sId !== null ) + { + oSettings.sTableId = sId; + oSettings.sInstance = sId; + } + else + { + oSettings.sInstance = _oExt._oExternConfig.iNextUnique ++; + } + + /* Set the table node */ + oSettings.nTable = this; + + /* Bind the API functions to the settings, so we can perform actions whenever oSettings is + * available + */ + oSettings.oApi = _that.oApi; + + /* Store the features that we have available */ + if ( typeof oInit != 'undefined' && oInit !== null ) + { + _fnMap( oSettings.oFeatures, oInit, "bPaginate" ); + _fnMap( oSettings.oFeatures, oInit, "bLengthChange" ); + _fnMap( oSettings.oFeatures, oInit, "bFilter" ); + _fnMap( oSettings.oFeatures, oInit, "bSort" ); + _fnMap( oSettings.oFeatures, oInit, "bInfo" ); + _fnMap( oSettings.oFeatures, oInit, "bProcessing" ); + _fnMap( oSettings.oFeatures, oInit, "bAutoWidth" ); + _fnMap( oSettings.oFeatures, oInit, "bSortClasses" ); + _fnMap( oSettings.oFeatures, oInit, "bServerSide" ); + _fnMap( oSettings, oInit, "asStripClasses" ); + _fnMap( oSettings, oInit, "fnRowCallback" ); + _fnMap( oSettings, oInit, "fnHeaderCallback" ); + _fnMap( oSettings, oInit, "fnFooterCallback" ); + _fnMap( oSettings, oInit, "fnDrawCallback" ); + _fnMap( oSettings, oInit, "fnInitComplete" ); + _fnMap( oSettings, oInit, "fnServerData" ); + _fnMap( oSettings, oInit, "aaSorting" ); + _fnMap( oSettings, oInit, "aaSortingFixed" ); + _fnMap( oSettings, oInit, "sPaginationType" ); + _fnMap( oSettings, oInit, "sAjaxSource" ); + _fnMap( oSettings, oInit, "sDom", "sDomPositioning" ); + _fnMap( oSettings, oInit, "oSearch", "oPreviousSearch" ); + _fnMap( oSettings, oInit, "aoSearchCols", "aoPreSearchCols" ); + _fnMap( oSettings, oInit, "iDisplayLength", "_iDisplayLength" ); + _fnMap( oSettings, oInit, "bJQueryUI", "bJUI" ); + + if ( typeof oInit.bJQueryUI != 'undefined' ) + { + /* Use the JUI classes object for display. You could clone the oStdClasses object if + * you want to have multiple tables with multiple independent classes + */ + oSettings.oClasses = _oExt.oJUIClasses; + + if ( typeof oInit.sDom == 'undefined' ) + { + /* Set the DOM to use a layout suitable for jQuery UI's theming */ + oSettings.sDomPositioning = + '<"fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix"lfr>'+ + 't'+ + '<"fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix"ip>'; + } + } + + if ( typeof oInit.iDisplayStart != 'undefined' && + typeof oSettings.iInitDisplayStart == 'undefined' ) { + /* Display start point, taking into account the save saving */ + oSettings.iInitDisplayStart = oInit.iDisplayStart; + oSettings._iDisplayStart = oInit.iDisplayStart; + } + + /* Must be done after everything which can be overridden by a cookie! */ + if ( typeof oInit.bStateSave != 'undefined' ) + { + oSettings.oFeatures.bStateSave = oInit.bStateSave; + _fnLoadState( oSettings, oInit ); + } + + if ( typeof oInit.aaData != 'undefined' ) { + bUsePassedData = true; + } + + /* Backwards compatability */ + /* aoColumns / aoData - remove at some point... */ + if ( typeof oInit != 'undefined' && typeof oInit.aoData != 'undefined' ) + { + oInit.aoColumns = oInit.aoData; + } + + /* Language definitions */ + if ( typeof oInit.oLanguage != 'undefined' ) + { + if ( typeof oInit.oLanguage.sUrl != 'undefined' && oInit.oLanguage.sUrl !== "" ) + { + /* Get the language definitions from a file */ + oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl; + $.getJSON( oSettings.oLanguage.sUrl, null, function( json ) { + _fnLanguageProcess( oSettings, json, true ); } ); + bInitHandedOff = true; + } + else + { + _fnLanguageProcess( oSettings, oInit.oLanguage, false ); + } + } + /* Warning: The _fnLanguageProcess function is async to the remainder of this function due + * to the XHR. We use _bInitialised in _fnLanguageProcess() to check this the processing + * below is complete. The reason for spliting it like this is optimisation - we can fire + * off the XHR (if needed) and then continue processing the data. + */ + } + + /* Add the strip classes now that we know which classes to apply - unless overruled */ + if ( typeof oInit == 'undefined' || typeof oInit.asStripClasses == 'undefined' ) + { + oSettings.asStripClasses.push( oSettings.oClasses.sStripOdd ); + oSettings.asStripClasses.push( oSettings.oClasses.sStripEven ); + } + + /* See if we should load columns automatically or use defined ones - a bit messy this... */ + var nThead = this.getElementsByTagName('thead'); + var nThs = nThead.length===0 ? null : _fnGetUniqueThs( nThead[0] ); + var bUseCols = typeof oInit != 'undefined' && typeof oInit.aoColumns != 'undefined'; + for ( i=0, iLen=bUseCols ? oInit.aoColumns.length : nThs.length ; i<iLen ; i++ ) + { + var col = bUseCols ? oInit.aoColumns[i] : null; + var n = nThs ? nThs[i] : null; + _fnAddColumn( oSettings, col, n ); + } + + /* Sanity check that there is a thead and tfoot. If not let's just create them */ + if ( this.getElementsByTagName('thead').length === 0 ) + { + this.appendChild( document.createElement( 'thead' ) ); + } + + if ( this.getElementsByTagName('tbody').length === 0 ) + { + this.appendChild( document.createElement( 'tbody' ) ); + } + + /* Check if there is data passing into the constructor */ + if ( bUsePassedData ) + { + for ( i=0 ; i<oInit.aaData.length ; i++ ) + { + _fnAddData( oSettings, oInit.aaData[ i ] ); + } + } + else + { + /* Grab the data from the page */ + _fnGatherData( oSettings ); + } + + /* Copy the data index array */ + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + + /* Calculate sizes for columns */ + if ( oSettings.oFeatures.bAutoWidth ) + { + _fnCalculateColumnWidths( oSettings ); + } + + /* Initialisation complete - table can be drawn */ + oSettings.bInitialised = true; + + /* Check if we need to initialise the table (it might not have been handed off to the + * language processor) + */ + if ( bInitHandedOff === false ) + { + _fnInitalise( oSettings ); + } + }); + }; +})(jQuery);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/browser/web/js/sparql.js Thu Oct 08 11:19:11 2009 +0000 @@ -0,0 +1,490 @@ +/********************************************************** + Copyright (c) 2006, 2007 + Lee Feigenbaum ( lee AT thefigtrees DOT net ) + Elias Torres ( elias AT torrez DOT us ) + Wing Yung ( wingerz AT gmail DOT com ) + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +**********************************************************/ + +/** + * Example client interactions + * + + var sparqler = new SPARQL.Service("http://sparql.org/sparql"); + sparqler.addDefaultGraph("http://thefigtrees.net/lee/ldf-card"); // inherited by all (future) queries + sparqler.addNamedGraph("http://torrez.us/elias/foaf.rdf"); + sparqler.setPrefix("foaf", "http://xmlns.com/foaf/0.1/"); // inherited by all (future) queries + sparqler.setPrefix("rdf", "http://xmlns.com/foaf/0.1/"); + + sparqler.setRequestHeader("Authentication", "Basic: " + basicAuthString); + + //sparqler.wantOutputAs("application/json"); // for now we only do JSON + + var query = sparqler.createQuery(); + query.addDefualtGraph(...) query.addNamedGraph(...) query.setPrefix(...) query.setRequestHeader(...) // this query only + + // query forms: + + // passes standard JSON results object to success callback + query.setPrefix("ldf", "http://thefigtrees.net/lee/ldf-card#"); + query.query("SELECT ?who ?mbox WHERE { ldf:LDF foaf:knows ?who . ?who foaf:mbox ?mbox }", + {failure: onFailure, success: function(json) { for (var x in json.head.vars) { ... } ...}} + ); + + // passes boolean value to success callback + query.ask("ASK ?person WHERE { ?person foaf:knows [ foaf:name "Dan Connolly" ] }", + {failure: onFailure, success: function(bool) { if (bool) ... }} + ); + + // passes a single vector (array) of values representing a single column of results to success callback + query.setPrefix("ldf", "http://thefigtrees.net/lee/ldf-card#"); + var addresses = query.selectValues("SELECT ?mbox WHERE { _:someone foaf:mbox ?mbox }", + {failure: onFailure, success: function(values) { for (var i = 0; i < values.length; i++) { ... values[i] ...} } } + ); + + // passes a single value representing a single row of a single column (variable) to success callback + query.setPrefix("ldf", "http://thefigtrees.net/lee/ldf-card#"); + var myAddress = query.selectSingleValue("SELECT ?mbox WHERE {ldf:LDF foaf:mbox ?mbox }", + {failure: onFailure, success: function(value) { alert("value is: " + value); } } + ); + + // shortcuts for all of the above (w/o ability to set any query-specific graphs or prefixes) + sparqler.query(...) sparqler.ask(...) sparqler.selectValues(...) sparqler.selectSingleValue(...) + + + */ + +var SPARQL = {}; // SPARQL namespace + + +/** + * Both SPARQL service objects and SPARQL query objects receive one query utility method + * per entry in this dictionary. The key is the name of the method, and the value is a function + * that transforms the standard JSON output into a more useful form. The return value of a + * transformation function is passed into any 'success' callback function provided when the query + * is issued. The following transformations are included: + * + query -- identity transform; returns the JSON structure unchanged + * + ask -- for ASK queries; returns a boolean value indicating the answer to the query + * + selectValues -- for SELECT queries with a single variable; returns an array containing + * the answers to the query + * + selectSingleValue -- for SELECT queries returning one column with one row; returns the + * value in the first (and presumably, only) cell in the resultset + * + selectValueArrays -- for SELECT queries returning independent columns; returns a hash + * keyed on variable name with values as arrays of answers for that variable. Useful + * for UNION queries. + * Note that all of the transformations that return values directly lose any type information + * and the ability to distinguish between URIs, blank nodes, and literals. + */ +SPARQL._query_transformations = { + query: function (o) { return o; }, + ask: function (o) { return o["boolean"]; }, + selectValues: function (o) { + var v = o.head.vars[0]; // assume one variable + var values = []; + for (var i = 0; i < o.results.bindings.length; i++) + values.push(o.results.bindings[i][v].value); + return values; + }, + selectSingleValue: function(o) { return o.results.bindings[0][o.head.vars[0]].value; }, + selectValueArrays: function(o) { + // factor by value (useful for UNION queries) + var ret = {}; + for (var i = 0; i < o.head.vars.length; i++) + ret[o.head.vars[i]] = []; + for (var i = 0; i < o.results.bindings.length; i++) + for (var v in o.results.bindings[i]) + if (ret[v] instanceof Array) ret[v].push(o.results.bindings[i][v].value); + return ret; + }, + selectValueHashes: function(o) { + var hashes = []; + for (var i = 0; i < o.results.bindings.length; i++) { + var hash = {}; + for (var v in o.results.bindings[i]) + hash[v] = o.results.bindings[i][v].value; + hashes.push(hash); + } + return hashes; + } +}; + +SPARQL.statistics = { + queries_sent : 0, + successes : 0, + failures : 0 +}; + +// A SPARQL service represents a single endpoint which implements the HTTP (GET or POST) +// bindings of the SPARQL Protocol. It provides convenience methods to set dataset and +// prefix options for all queries created for this endpoint. +SPARQL.Service = function(endpoint) { + //--------------- + // private fields + var _endpoint = endpoint; + var _default_graphs = []; + var _named_graphs = []; + var _prefix_map = {}; + var _method = 'POST'; + var _output = 'json'; + var _max_simultaneous = 0; + var _request_headers = {}; + + //---------- + // accessors + this.endpoint = function() { return _endpoint; }; + this.defaultGraphs = function() { return _default_graphs; }; + this.namedGraphs = function() { return _named_graphs; }; + this.prefixes = function() { return _prefix_map; }; + this.method = function() { return _method; }; + this.output = function() { return _output; }; + this.maxSimultaneousQueries = function() { return _max_simultaneous; }; + this.requestHeaders = function() { return _request_headers; }; + + //--------- + // mutators + function _add_graphs(toAdd, arr) { + if (toAdd instanceof Array) + for (var i = 0; i < toAdd.length; i++) arr.push(toAdd[i]); + else + arr.push(toAdd); + } + this.addDefaultGraph = function(g) { _add_graphs(g, this.defaultGraphs()); }; + this.addNamedGraph = function(g) { _add_graphs(g, this.namedGraphs()); }; + this.setPrefix = function(p, u) { this.prefixes()[p] = u; }; + this.createQuery = function(p) { return new SPARQL.Query(this, p); }; + this.setMethod = function(m) { + if (m != 'GET' && m != 'POST') throw("HTTP methods other than GET and POST are not supported."); + _method = m; + }; + this.setOutput = function(o) { _output = o; }; + this.setMaxSimultaneousQueries = function(m) { _max_simultaneous = m; }; + this.setRequestHeader = function(h, v) { _request_headers[h] = v; }; + + //--------------- + // protected methods (should only be called within this module + this._active_queries = 0; + this._queued_queries = []; + this._next_in_queue = 0; + this._canRun = function() { return this.maxSimultaneousQueries() <= 0 || this._active_queries < this.maxSimultaneousQueries();}; + this._queue = function(q,f, p) { + if (!p) p = 0; + if (p > 0) { + for (var i = 0; i < this._queued_queries.length; i++) { + if (this._queued_queries[i] != null && this._queued_queries[i][2] < p) { + this._queued_queries.splice(i, 0, [q, f, p]); + return; + } + } + } + this._queued_queries.push([q,f,p]); + }; + this._markRunning = function(q) { this._active_queries++; }; + this._markDone = function(q) { + this._active_queries--; + //document.getElementById('log').innerHTML+="query done. " + this._active_queries + " queries still active.<br>"; + if (this._queued_queries[this._next_in_queue] != null && this._canRun()) { + var a = this._queued_queries[this._next_in_queue]; + this._queued_queries[this._next_in_queue++] = null; + // a[0] is query object, a[1] is function to run query + //document.getElementById('log').innerHTML += "running query from Q<br>"; + a[1](); + } + }; + + //--------------- + // public methods + + // use our varied transformations to create the various shortcut methods of actually + // issuing queries without explicitly creating a query object + for (var query_form in SPARQL._query_transformations) { + // need the extra function to properly scope query_form (qf) + this[query_form] = (function(qf) { + return function(queryString, callback) { + var q = this.createQuery(); + q._doQuery(queryString, callback, SPARQL._query_transformations[qf]); + }; + })(query_form); + } + + //------------ + // constructor + + if (!_endpoint) + return null; + + return this; +} + +/** + * A SPARQL query object should be created using the createQuery method of a SPARQL + * service object. It allows prefixes and datasets to be defined specifically for + * a single query, and provides introspective methods to see the query string and the + * full (HTTP GET) URL of the query. + */ +SPARQL.Query = function(service, priority) { + //--------------- + // private fields + var _conn = null; + var _service = service; + var _default_graphs = clone_obj(service.defaultGraphs()); // prevent future updates from affecting us + var _named_graphs = clone_obj(service.namedGraphs()); + var _prefix_map = clone_obj(service.prefixes()); + var _user_query = ''; // doesn't include auto-generated prefix declarations + var _method = service.method(); + var _output = service.output(); + var _priority = priority || 0; + var _request_headers = clone_obj(service.requestHeaders()); + + //------------------ + // private functions + function _create_json(text) { + if (!text) + return null; + // make sure this is safe JSON + // see: http://www.ietf.org/internet-drafts/draft-crockford-jsonorg-json-03.txt + + // (1) strip out quoted strings + var no_strings = text.replace(/"(\\.|[^"\\])*"/g, ''); + // (2) make sure that all the characters are explicitly part of the JSON grammar + // (in particular, note as discussed in the IETF submission, there are no assignments + // or function invocations allowed by this reg. exp.) + var hasBadCharacter = /[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(no_strings); + // (3) evaluate the JSON string, returning its contents + if (!hasBadCharacter) { + try { + return eval('(' + text + ')'); + } catch (e) { + return null; + } + } + return null; + } + + function clone_obj(o) { + var o2 = o instanceof Array ? [] : {}; + for(var x in o) {o2[x] = o[x];} + return o2; + } + + //---------------- + // private methods + this._doCallback = function(cb, which, arg) { + //document.getElementById('log').innerHTML += "_doCallback ... <br>"; + var user_data = "argument" in cb ? cb.argument : null; + if (which in cb) { + if (cb.scope) { + cb[which].apply(cb.scope, [arg, user_data]); + } else { + cb[which](arg, user_data); + } + } + } + + this._queryFailure = function(xhr, arg) { + SPARQL.statistics.failures++; + _service._markDone(this); + this._doCallback(arg.callback, 'failure', xhr /* just pass through the connection response object */); + }; + this._querySuccess = function(xhr, arg) { + //alert(xhr.responseText); + SPARQL.statistics.successes++; + _service._markDone(this); + this._doCallback(arg.callback, 'success', arg.transformer( + _output == 'json' ? _create_json(xhr.responseText) : xhr.responseText + )); + }; + + function getXmlHttpRequest(url) { + // right now, this only does Firefox (Opera? Safari?) + return new XMLHttpRequest(); + } + + this._doQuery = function(queryString, callback, transformer) { + _user_query = queryString; + if (_service._canRun()) { + try { + if (_method != 'POST' && _method != 'GET') + throw("HTTP methods other than GET and POST are not supported."); + + var url = _method == 'GET' ? this.queryUrl() : this.service().endpoint(); + var xhr = getXmlHttpRequest(url); + var content = null; + + try { + if (!document.domain || (url.match(/^https?:\/\//) && url.slice(7, document.domain.length + 7) != document.domain && window.netscape && netscape.security && netscape.security.PrivilegeManager)) { + netscape.security.PrivilegeManager.enablePrivilege( "UniversalBrowserRead"); + netscape.security.PrivilegeManager.enablePrivilege( "UniversalXPConnect"); + } + } catch(e) { + alert("Cross-site requests prohibited. You will only be able to SPARQL the origin site: " + e); + return; + } + + xhr.open(_method, url, true /* async */); + + // set the headers, including the content-type for POSTed queries + for (var header in this.requestHeaders()) + if (typeof(this.requestHeaders()[header]) != "function") + xhr.setRequestHeader(header, this.requestHeaders()[header]); + if (_method == 'POST') { + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + content = this.queryParameters(); + } + + SPARQL.statistics.queries_sent++; + _service._markRunning(this); + + var callbackData = { + scope:this, + success: this._querySuccess, + failure: this._queryFailure, + argument: { + transformer: transformer, + callback: callback + } + }; + + // I've seen some strange race-condition behavior (strange since normally + // JS is single-threaded, so synchronization conditions don't occur barring + // reentrancy) with onreadystatechange. Instead, we poll asynchronously to + // determine when the request is done. + var token = window.setInterval( + function () { + if (xhr.readyState == 4) { // ready! + // clear this handler + window.clearInterval(token); + // we check the status to know which callback to call + if (xhr.status >= 200 && xhr.status < 300) + callbackData.success.apply(callbackData.scope, [xhr, callbackData.argument]); + else + callbackData.failure.apply(callbackData.scope, [xhr, callbackData.argument]); + } + }, + 200 /* maybe this should be customizable */ + ); + + xhr.send(content); + } catch (e) { + alert("Error sending SPARQL query: " + e); + } + } else { + var self = this; + _service._queue(self, function() { self._doQuery(queryString, callback, transformer); }, _priority); + } + }; + + + //---------- + // accessors + this.request = function() { return _conn; }; + this.service = function() { return _service; }; + this.defaultGraphs = function() { return _default_graphs; }; + this.namedGraphs = function() { return _named_graphs; }; + this.prefixes = function() { return _prefix_map; }; + this.method = function() { return _method; }; + this.requestHeaders = function() { return _request_headers; }; + + + /** + * Returns the SPARQL query represented by this object. The parameter, which can + * be omitted, determines whether or not auto-generated PREFIX clauses are included + * in the returned query string. + */ + this.queryString = function(excludePrefixes) { + var preamble = ''; + if (!excludePrefixes) { + for (var prefix in this.prefixes()) { + if(typeof(this.prefixes()[prefix]) != 'string') continue; + preamble += 'PREFIX ' + prefix + ': <' + this.prefixes()[prefix] + '> '; + } + } + return preamble + _user_query; + }; + + /** + * Returns the HTTP query parameters to invoke this query. This includes entries for + * all of the default graphs, the named graphs, the SPARQL query itself, and an + * output parameter to specify JSON (or other) output is desired. + */ + this.queryParameters = function () { + var urlQueryString = ''; + var i; + + // add default and named graphs to the protocol invocation + for (i = 0; i < this.defaultGraphs().length; i++) urlQueryString += 'default-graph-uri=' + encodeURIComponent(this.defaultGraphs()[i]) + '&'; + for (i = 0; i < this.namedGraphs().length; i++) urlQueryString += 'named-graph-uri=' + encodeURIComponent(this.namedGraphs()[i]) + '&'; + + // specify JSON output (currently output= supported by latest Joseki) (or other output) + urlQueryString += 'output=' + _output + '&soft-limit=-1&'; + return urlQueryString + 'query=' + encodeURIComponent(this.queryString()); + } + + /** + * Returns the HTTP GET URL to invoke this query. (Note that this returns a full HTTP GET URL + * even if this query is set to actually use POST.) + */ + this.queryUrl = function() { + var url = this.service().endpoint() + '?'; + return url + this.queryParameters(); + }; + + //--------- + // mutators + function _add_graphs(toAdd, arr) { + if (toAdd instanceof Array) + for (var i = 0; i < toAdd.length; i++) arr.push(toAdd[i]); + else + arr.push(toAdd); + } + this.addDefaultGraph = function(g) { _add_graphs(g, this.defaultGraphs()); }; + this.addNamedGraph = function(g) { _add_graphs(g, this.namedGraphs()); }; + this.setPrefix = function(p, u) { this.prefixes()[p] = u; }; + this.setMethod = function(m) { + if (m != 'GET' && m != 'POST') throw("HTTP methods other than GET and POST are not supported."); + _method = m; + }; + this.setRequestHeader = function(h, v) { _request_headers[h] = v; }; + + //--------------- + // public methods + + // use our varied transformations to create the various methods of actually issuing + // queries + for (var query_form in SPARQL._query_transformations) { + // need the extra function to properly scope query_form (qf) + this[query_form] = (function(qf) { + return function(queryString, callback) { + this._doQuery(queryString, callback, SPARQL._query_transformations[qf]); + }; + })(query_form); + } + + + //------------ + // constructor + + return this; +} + +// Nothing to see here, yet. +SPARQL.QueryUtilities = { +}; +