]
>
What's new in XQuery 3.0?
hello mum");
doc.close();
}
// find current slide based upon location
// first find target anchor and then look
// for associated div element enclosing it
// finally map that to slide number
function findSlideNumber(uri)
{
// first get anchor from page location
var i = uri.indexOf("#");
// check if anchor is entire page
if (i < 0)
return 0; // yes
var anchor = unescape(uri.substr(i+1));
// now use anchor as XML ID to find target
var target = document.getElementById(anchor);
if (!target)
{
// does anchor look like "(2)" for slide 2 ??
// where first slide is (1)
var re = /\((\d)+\)/;
if (anchor.match(re))
{
var num = parseInt(anchor.substring(1, anchor.length-1));
if (num > slides.length)
num = 1;
if (--num < 0)
num = 0;
return num;
}
// accept [2] for backwards compatibility
re = /\[(\d)+\]/;
if (anchor.match(re))
{
var num = parseInt(anchor.substring(1, anchor.length-1));
if (num > slides.length)
num = 1;
if (--num < 0)
num = 0;
return num;
}
// oh dear unknown anchor
return 0;
}
// search for enclosing slide
while (true)
{
// browser coerces html elements to uppercase!
if (target.nodeName.toLowerCase() == "div" &&
hasClass(target, "slide"))
{
// found the slide element
break;
}
// otherwise try parent element if any
target = target.parentNode;
if (!target)
{
return 0; // no luck!
}
};
for (i = 0; i < slides.length; ++i)
{
if (slides[i] == target)
return i; // success
}
// oh dear still no luck
return 0;
}
// find slide name from first h1 element
// default to document title + slide number
function slideName(index)
{
var name = null;
var slide = slides[index];
var heading = findHeading(slide);
if (heading)
name = extractText(heading);
if (!name)
name = title + "(" + (index + 1) + ")";
name.replace(/\&/g, "&");
name.replace(/\/g, ">");
return name;
}
// find first h1 element in DOM tree
function findHeading(node)
{ if (!node || node.nodeType != 1)
return null;
if (node.nodeName == "H1" || node.nodeName == "h1")
return node;
var child = node.firstChild;
while (child)
{
node = findHeading(child);
if (node)
return node;
child = child.nextSibling;
}
return null;
}
// recursively extract text from DOM tree
function extractText(node)
{
if (!node)
return "";
// text nodes
if (node.nodeType == 3)
return node.nodeValue;
// elements
if (node.nodeType == 1)
{
node = node.firstChild;
var text = "";
while (node)
{
text = text + extractText(node);
node = node.nextSibling;
}
return text;
}
return "";
}
// find copyright text from meta element
function findCopyright()
{
var name, content;
var meta = document.getElementsByTagName("meta");
for (var i = 0; i < meta.length; ++i)
{
name = meta[i].getAttribute("name");
content = meta[i].getAttribute("content");
if (name == "copyright")
return content;
}
return null;
}
function findSizeAdjust()
{
var name, content, offset;
var meta = document.getElementsByTagName("meta");
for (var i = 0; i < meta.length; ++i)
{
name = meta[i].getAttribute("name");
content = meta[i].getAttribute("content");
if (name == "font-size-adjustment")
return 1 * content;
}
return 1;
}
function addToolbar()
{
var slideCounter, page;
var toolbar = createElement("div");
toolbar.setAttribute("class", "toolbar");
if (ns_pos) // a reasonably behaved browser
{
var right = document.createElement("div");
right.setAttribute("style", "float: right; text-align: right");
slideCounter = document.createElement("div")
slideCounter.innerHTML = "slide".localize() + " n/m";
right.appendChild(slideCounter);
toolbar.appendChild(right);
var left = document.createElement("div");
left.setAttribute("style", "text-align: left");
// global end of slide indicator
eos = document.createElement("span");
eos.innerHTML = "* ";
left.appendChild(eos);
var help = document.createElement("a");
help.setAttribute("href", helpPage);
help.setAttribute("title", helpText.localize());
help.innerHTML = " ".localize();
left.appendChild(help);
helpAnchor = help; // save for focus hack
var gap1 = document.createTextNode(" ");
left.appendChild(gap1);
var contents = document.createElement("a");
contents.setAttribute("href", "javascript:toggleTableOfContents()");
contents.setAttribute("title", "table of contents".localize());
contents.innerHTML = "Table of Contents ".localize();
left.appendChild(contents);
var gap2 = document.createTextNode("..............................................");
left.appendChild(gap2);
var start = document.createElement("a");
start.setAttribute("href", "javascript:firstSlide()");
start.setAttribute("title", "restart presentation".localize());
start.innerHTML = " ".localize();
// start.setAttribute("href", "javascript:printSlides()");
// start.setAttribute("title", "print all slides".localize());
// start.innerHTML = "print!".localize();
left.appendChild(start);
// var identity = document.createTextNode("");
// left.appendChild(identity);
var copyright = findCopyright();
if (copyright)
{
var span = document.createElement("span");
span.innerHTML = copyright;
span.style.color = "black";
span.style.marginLeft = "4em";
left.appendChild(span);
}
toolbar.appendChild(left);
}
else // IE so need to work around its poor CSS support
{
toolbar.style.position = (ie7 ? "fixed" : "absolute");
toolbar.style.zIndex = "200";
toolbar.style.width = "99.9%";
toolbar.style.height = "1.2em";
toolbar.style.top = "auto";
toolbar.style.bottom = "0";
toolbar.style.left = "0";
toolbar.style.right = "0";
toolbar.style.textAlign = "left";
toolbar.style.fontSize = "60%";
toolbar.style.color = "red";
toolbar.borderWidth = 0;
toolbar.style.background = "rgb(240,240,240)";
// would like to have help text left aligned
// and page counter right aligned, floating
// div's don't work, so instead use nested
// absolutely positioned div's.
var sp = document.createElement("span");
sp.innerHTML = " * ";
toolbar.appendChild(sp);
eos = sp; // end of slide indicator
var help = document.createElement("a");
help.setAttribute("href", helpPage);
help.setAttribute("title", helpText.localize());
help.innerHTML = "help?".localize();
toolbar.appendChild(help);
helpAnchor = help; // save for focus hack
var gap1 = document.createTextNode(" ");
toolbar.appendChild(gap1);
var contents = document.createElement("a");
contents.setAttribute("href", "javascript:toggleTableOfContents()");
contents.setAttribute("title", "table of contents".localize());
contents.innerHTML = "contents?".localize();
toolbar.appendChild(contents);
var gap2 = document.createTextNode(" ");
toolbar.appendChild(gap2);
var start = document.createElement("a");
start.setAttribute("href", "javascript:firstSlide()");
start.setAttribute("title", "restart presentation".localize());
start.innerHTML = "restart?".localize();
// start.setAttribute("href", "javascript:printSlides()");
// start.setAttribute("title", "print all slides".localize());
// start.innerHTML = "print!".localize();
toolbar.appendChild(start);
var copyright = findCopyright();
if (copyright)
{
var span = document.createElement("span");
span.innerHTML = copyright;
span.style.color = "black";
span.style.marginLeft = "2em";
toolbar.appendChild(span);
}
slideCounter = document.createElement("div")
slideCounter.style.position = "absolute";
slideCounter.style.width = "auto"; //"20%";
slideCounter.style.height = "1.2em";
slideCounter.style.top = "auto";
slideCounter.style.bottom = 0;
slideCounter.style.right = "0";
slideCounter.style.textAlign = "right";
slideCounter.style.color = "red";
slideCounter.style.background = "rgb(240,240,240)";
slideCounter.innerHTML = "slide".localize() + " n/m";
toolbar.appendChild(slideCounter);
}
// ensure that click isn't passed through to the page
toolbar.onclick = stopPropagation;
document.body.appendChild(toolbar);
slideNumElement = slideCounter;
setEosStatus(false);
return toolbar;
}
function isShownToc()
{
if (toc && toc.style.visible == "visible")
return true;
return false;
}
function showTableOfContents()
{
if (toc)
{
if (toc.style.visibility != "visible")
{
toc.style.visibility = "visible";
toc.style.display = "block";
toc.focus();
if (ie7 && slidenum == 0)
setTimeout("ieHack()", 100);
}
else
hideTableOfContents();
}
}
function hideTableOfContents()
{
if (toc && toc.style.visibility != "hidden")
{
toc.style.visibility = "hidden";
toc.style.display = "none";
try
{
if (!opera)
helpAnchor.focus();
}
catch (e)
{
}
}
}
function toggleTableOfContents()
{
if (toc)
{
if (toc.style.visible != "visible")
showTableOfContents();
else
hideTableOfContents();
}
}
// called on clicking toc entry
function gotoEntry(e)
{
var target;
if (!e)
var e = window.event;
if (e.target)
target = e.target;
else if (e.srcElement)
target = e.srcElement;
// work around Safari bug
if (target.nodeType == 3)
target = target.parentNode;
if (target && target.nodeType == 1)
{
var uri = target.getAttribute("href");
if (uri)
{
//alert("going to " + uri);
var slide = slides[slidenum];
hideSlide(slide);
slidenum = findSlideNumber(uri);
slide = slides[slidenum];
lastShown = null;
setLocation();
setVisibilityAllIncremental("hidden");
setEosStatus(!nextIncrementalItem(lastShown));
showSlide(slide);
//target.focus();
try
{
if (!opera)
helpAnchor.focus();
}
catch (e)
{
}
}
}
hideTableOfContents(e);
if (ie7) ieHack();
stopPropagation(e);
return cancel(e);
}
// called onkeydown for toc entry
function gotoTocEntry(event)
{
var key;
if (!event)
var event = window.event;
// kludge around NS/IE differences
if (window.event)
key = window.event.keyCode;
else if (event.which)
key = event.which;
else
return true; // Yikes! unknown browser
// ignore event if key value is zero
// as for alt on Opera and Konqueror
if (!key)
return true;
// check for concurrent control/command/alt key
// but are these only present on mouse events?
if (event.ctrlKey || event.altKey)
return true;
if (key == 13)
{
var uri = this.getAttribute("href");
if (uri)
{
//alert("going to " + uri);
var slide = slides[slidenum];
hideSlide(slide);
slidenum = findSlideNumber(uri);
slide = slides[slidenum];
lastShown = null;
setLocation();
setVisibilityAllIncremental("hidden");
setEosStatus(!nextIncrementalItem(lastShown));
showSlide(slide);
//target.focus();
try
{
if (!opera)
helpAnchor.focus();
}
catch (e)
{
}
}
hideTableOfContents();
if (ie7) ieHack();
return cancel(event);
}
if (key == 40 && this.next)
{
this.next.focus();
return cancel(event);
}
if (key == 38 && this.previous)
{
this.previous.focus();
return cancel(event);
}
return true;
}
function isTitleSlide(slide)
{
return hasClass(slide, "title");
}
// create div element with links to each slide
function tableOfContents()
{
var toc = document.createElement("div");
addClass(toc, "toc");
//toc.setAttribute("tabindex", "0");
var heading = document.createElement("div");
addClass(heading, "toc-heading");
heading.innerHTML = "Table of Contents".localize();
heading.style.textAlign = "center";
heading.style.width = "100%";
heading.style.margin = "0";
heading.style.marginBottom = "1em";
heading.style.borderBottomStyle = "solid";
heading.style.borderBottomColor = "rgb(180,180,180)";
heading.style.borderBottomWidth = "1px";
toc.appendChild(heading);
var previous = null;
for (var i = 0; i < slides.length; ++i)
{
var title = hasClass(slides[i], "title");
var num = document.createTextNode((i + 1) + ". ");
toc.appendChild(num);
var a = document.createElement("a");
a.setAttribute("href", "#(" + (i+1) + ")");
if (title)
addClass(a, "titleslide");
var name = document.createTextNode(slideName(i));
a.appendChild(name);
a.onclick = gotoEntry;
a.onkeydown = gotoTocEntry;
a.previous = previous;
if (previous)
previous.next = a;
toc.appendChild(a);
if (i == 0)
toc.first = a;
if (i < slides.length - 1)
{
var br = document.createElement("br");
toc.appendChild(br);
}
previous = a;
}
toc.focus = function () {
if (this.first)
this.first.focus();
}
toc.onmouseup = mouseButtonUp;
toc.onclick = function (e) {
e||(e=window.event);
if (selectedTextLen <= 0)
hideTableOfContents();
stopPropagation(e);
if (e.cancel != undefined)
e.cancel = true;
if (e.returnValue != undefined)
e.returnValue = false;
return false;
};
toc.style.position = "absolute";
toc.style.zIndex = "300";
toc.style.width = "60%";
toc.style.maxWidth = "30em";
toc.style.height = "30em";
toc.style.overflow = "auto";
toc.style.top = "auto";
toc.style.right = "auto";
toc.style.left = "4em";
toc.style.bottom = "4em";
toc.style.padding = "1em";
toc.style.background = "rgb(240,240,240)";
toc.style.borderStyle = "solid";
toc.style.borderWidth = "2px";
toc.style.fontSize = "60%";
document.body.insertBefore(toc, document.body.firstChild);
return toc;
}
function replaceByNonBreakingSpace(str)
{
for (var i = 0; i < str.length; ++i)
str[i] = 160;
}
function initOutliner()
{
var items = document.getElementsByTagName("LI");
for (var i = 0; i < items.length; ++i)
{
var target = items[i];
if (!hasClass(target.parentNode, "outline"))
continue;
target.onclick = outlineClick;
if (!ns_pos)
{
target.onmouseover = hoverOutline;
target.onmouseout = unhoverOutline;
}
if (foldable(target))
{
target.foldable = true;
target.onfocus = function () {outline = this;};
target.onblur = function () {outline = null;};
if (!target.getAttribute("tabindex"))
target.setAttribute("tabindex", "0");
if (hasClass(target, "expand"))
unfold(target);
else
fold(target);
}
else
{
addClass(target, "nofold");
target.visible = true;
target.foldable = false;
}
}
}
function foldable(item)
{
if (!item || item.nodeType != 1)
return false;
var node = item.firstChild;
while (node)
{
if (node.nodeType == 1 && isBlock(node))
return true;
node = node.nextSibling;
}
return false;
}
function fold(item)
{
if (item)
{
removeClass(item, "unfolded");
addClass(item, "folded");
}
var node = item ? item.firstChild : null;
while (node)
{
if (node.nodeType == 1 && isBlock(node)) // element
{
// note that getElementStyle won't work for Safari 1.3
node.display = getElementStyle(node, "display", "display");
node.style.display = "none";
node.style.visibility = "hidden";
}
node = node.nextSibling;
}
item.visible = false;
}
function unfold(item)
{
if (item)
{
addClass(item, "unfolded");
removeClass(item, "folded");
}
var node = item ? item.firstChild : null;
while (node)
{
if (node.nodeType == 1 && isBlock(node)) // element
{
// with fallback for Safari, see above
node.style.display = (node.display ? node.display : "block");
node.style.visibility = "visible";
}
node = node.nextSibling;
}
item.visible = true;
}
function outlineClick(e)
{
var rightclick = false;
var target;
if (!e)
var e = window.event;
if (e.target)
target = e.target;
else if (e.srcElement)
target = e.srcElement;
// work around Safari bug
if (target.nodeType == 3)
target = target.parentNode;
while (target && target.visible == undefined)
target = target.parentNode;
if (!target)
return true;
if (e.which)
rightclick = (e.which == 3);
else if (e.button)
rightclick = (e.button == 2);
if (!rightclick && target.visible != undefined)
{
if (target.foldable)
{
if (target.visible)
fold(target);
else
unfold(target);
}
stopPropagation(e);
e.cancel = true;
e.returnValue = false;
}
return false;
}
function hoverOutline(e)
{
var target;
if (!e)
var e = window.event;
if (e.target)
target = e.target;
else if (e.srcElement)
target = e.srcElement;
// work around Safari bug
if (target.nodeType == 3)
target = target.parentNode;
while (target && target.visible == undefined)
target = target.parentNode;
if (target && target.foldable)
target.style.cursor = "pointer";
return true;
}
function unhoverOutline(e)
{
var target;
if (!e)
var e = window.event;
if (e.target)
target = e.target;
else if (e.srcElement)
target = e.srcElement;
// work around Safari bug
if (target.nodeType == 3)
target = target.parentNode;
while (target && target.visible == undefined)
target = target.parentNode;
if (target)
target.style.cursor = "default";
return true;
}
function stopPropagation(e)
{
if (window.event)
{
window.event.cancelBubble = true;
//window.event.returnValue = false;
}
else if (e)
{
e.cancelBubble = true;
e.stopPropagation();
//e.preventDefault();
}
}
/* can't rely on display since we set that to none to hide things */
function isBlock(elem)
{
var tag = elem.nodeName;
return tag == "OL" || tag == "UL" || tag == "P" ||
tag == "LI" || tag == "TABLE" || tag == "PRE" ||
tag == "H1" || tag == "H2" || tag == "H3" ||
tag == "H4" || tag == "H5" || tag == "H6" ||
tag == "BLOCKQUOTE" || tag == "ADDRESS";
}
function getElementStyle(elem, IEStyleProp, CSSStyleProp)
{
if (elem.currentStyle)
{
return elem.currentStyle[IEStyleProp];
}
else if (window.getComputedStyle)
{
var compStyle = window.getComputedStyle(elem, "");
return compStyle.getPropertyValue(CSSStyleProp);
}
return "";
}
// works with text/html and text/xhtml+xml with thanks to Simon Willison
function createElement(element)
{
if (typeof document.createElementNS != 'undefined')
{
return document.createElementNS('http://www.w3.org/1999/xhtml', element);
}
if (typeof document.createElement != 'undefined')
{
return document.createElement(element);
}
return false;
}
// designed to work with both text/html and text/xhtml+xml
function getElementsByTagName(name)
{
if (typeof document.getElementsByTagNameNS != 'undefined')
{
return document.getElementsByTagNameNS('http://www.w3.org/1999/xhtml', name);
}
if (typeof document.getElementsByTagName != 'undefined')
{
return document.getElementsByTagName(name);
}
return null;
}
/*
// clean alternative to innerHTML method, but on IE6
// it doesn't work with named entities like
// which need to be replaced by numeric entities
function insertText(element, text)
{
try
{
element.textContent = text; // DOM3 only
}
catch (e)
{
if (element.firstChild)
{
// remove current children
while (element.firstChild)
element.removeChild(element.firstChild);
}
element.appendChild(document.createTextNode(text));
}
}
// as above, but as method of all element nodes
// doesn't work in IE6 which doesn't allow you to
// add methods to the HTMLElement prototype
if (HTMLElement != undefined)
{
HTMLElement.prototype.insertText = function(text) {
var element = this;
try
{
element.textContent = text; // DOM3 only
}
catch (e)
{
if (element.firstChild)
{
// remove current children
while (element.firstChild)
element.removeChild(element.firstChild);
}
element.appendChild(document.createTextNode(text));
}
};
}
*/
function getSelectedText()
{
try
{
if (window.getSelection)
return window.getSelection().toString();
if (document.getSelection)
return document.getSelection().toString();
if (document.selection)
return document.selection.createRange().text;
}
catch (e)
{
return "";
}
return "";
}
//]]>
XML Prague 2012
Focus of this talk
Status of specifications
-
List all significant new features briefly
Describe the really simple features quickly
Cover more complex features in depth
Mike Kay will handle higher order functions
Mike Kay and I will discuss maps/JSON in a separate presentation
Status: XQuery 3.0, XPath 3.0
Six documents progressing together
XQuery 3.0, XPath 3.0,
XQueryX 3.0,
Data Model 3.0,
Functions and Operators 3.0,
Serialization 3.0
Status: Last Call Working Draft
Comments period ended yesterday (10 Feb 2012)
Next step: Candidate Recommendation
Call for Recommendations
Status: XQuery 3.0, XPath 3.0 (2)
XSLT 3.0, XQuery Update Facility 3.0, XQuery Full Text 3.0 are not yet in Last Call
Coordination among Working Groups
Maps/JSON?
Features needed in development of XSLT 3.0
New work in XQuery after 3.0
Shared features in XPath 3.0 / XQuery 3.0
Functions
Dynamic function calls
Inline function expressions
Private functions
-
String concatenation operator
"con" || "cat" || "enate"
-
Support for XML Schema 1.1
-
Support for union types in function arguments
-
Literal URIs in EQNames (like Clark notation)
Syntax in flux ...
Specified use of "unknown" types, including types from documents and
types from other modules.
Cleaner separation of Static Base URI (query analysis time), Dynamic Base URI(run time)
XQuery-only Features
FLWOR Expressions
group by clause
tumbling window and sliding window
count clause
allowing empty in for clause (outer joins)
try/catch expressions
switch expressions
Computed namespace constructors
Annotations
An extensible mechanism for declaring properties of functions or variables
Examples: %private, %public
Annotation assertions in function tests
An extensible mechanism for testing characteristics of functions
Output declarations to set serialization parameters
FLWOR: group by clause
Input tuple stream
S101, $itemno = P78395)
($storeno = S102, $itemno = P94738)
($storeno = S101, $itemno = P41653)
($storeno = S102, $itemno = P70421)
]]>
group by clause
Output tuple stream
P78395, P41653))
($storeno = S102, $itemno = (P94738, P70421))
]]>
Note: value of $storeno changed from current published spec.
Grouping sales by product
$sales is bound to a sequence of sales elements, e.g.:
S101
P78395
125
]]>
$sales is bound to a sequence of products elements, e.g.:
P78395
25.00
Men's Wear
]]>
Query:
]]>
Result:
]]>
Grouping sales by product and category
Query
{for $s2 in $s1,
$p in $products[itemno = $s2/itemno]
let $category := $p/category,
$revenue := $s2/qty * $p/price
group by $category
let $group-revenue := sum($revenue)
where $group-revenue > 10000
order by $group-revenue descending
return
}
]]>
Result:
]]>
FLWOR: tumbling window and sliding window
A window identifies a range of items in a binding sequence
Conditions identify start / end of window
One tuple per sequence
Variables in the tuple identify window start, window end, etc.
Tumbling windows cannot overlap
Sliding windows can overlap
FLWOR: tumbling window and sliding window - example
Query
Result
4 6 8 10 12
Use cases for tumbling and sliding windows
Arrange a sequence of items as an HTML table with three columns (using as many rows as necessary).
Convert sequences of elements to hierarchical structures
Conditions identify start, end of a section
Moving averages, exponential smoothing, outliers
Sequences of events that signal a condition
Data analytics
FLWOR: count clause
Input tuple stream
($name = "Bob", $age = 21)
($name = "Carol", $age = 19)
($name = "Ted", $age = 20)
($name = "Alice", $age = 22)
count clause
count $counter
Output tuple stream:
($name = "Bob", $age = 21, $counter = 1)
($name = "Carol", $age = 19, $counter = 2)
($name = "Ted", $age = 20, $counter = 3)
($name = "Alice", $age = 22, $counter = 4)
Example: Top N queries
{$p/name, $p/sales}
]]>
FLWOR: allowing empty in for clause
Used for outer joins
Binds a variable to () if evaluates to empty sequence
Query fragment:
for $x in (100, 200, 300),
$y allowing empty in ()
Output tuple stream
($x = 100, $y = ()),
($x = 200, $y = ()),
($x = 300, $y = ())
try / catch expressions
Provides run-time error handling
try {
$x cast as xs:integer
}
catch err:FORG0001 | err:XPTY0004 {
0
}
Variables are implicitly bound in catch clause to identify error codes, descriptions, modules, line numbers, etc.
try {
fn:error(fn:QName('http://www.w3.org/2005/xqt-errors', 'err:FOER0000'))
}
catch * {
$err:code, $err:value, " module: ",
$err:module, "(", $err:line-number, ",", $err:column-number, ")"
}
switch expressions
As in most languages you know ...
switch ($animal)
case "Cow" return "Moo"
case "Cat" return "Meow"
case "Duck" return "Quack"
default return "What's that odd noise?"
Computed namespace constructors
Allow namespace bindings to be created explicitly
Useful for namespaces in content
{
namespace xs {"http://www.w3.org/2001/XMLSchema"},
attribute xsi:type {"xs:integer"},
23
}]]>
Literal URIs in EQNames
Extends QNames so the entire name can be specified as a literal
BNF
URIQualifiedName ::= URILiteral ":" NCName
Example
"http://www.w3.org/2005/xpath-functions/math":pi
Output declarations
Output declarations declare serialization parameters
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare option output:method "xml";
declare option output:encoding "iso-8859-1";
declare option output:indent "yes";
Can also put in a file
declare option output:parameter-document "file:///home/me/serialization-parameters.xml";
]]>
Files are the only way to do character maps
]]>