[30468] | 1 | /*
|
---|
| 2 |
|
---|
| 3 | Greenstone 3 'Client-side transformer'
|
---|
| 4 | Performs client-side transformations of XSLT, using HTML5 local storage (simulated if the browser doesn't support it).
|
---|
| 5 |
|
---|
| 6 | Currently only supports Firefox 3 and greater, since it's the only browser (at the time of writing) that can do this properly.
|
---|
| 7 |
|
---|
| 8 | @author Steven McTainsh
|
---|
[30480] | 9 | @author David Bainbridge
|
---|
| 10 | @date 2011-2016
|
---|
[30468] | 11 |
|
---|
| 12 | */
|
---|
| 13 |
|
---|
| 14 |
|
---|
[30480] | 15 | function isSupported()
|
---|
| 16 | {
|
---|
| 17 | // The whole script currently assumes SaxonCE is used to provide XSLT
|
---|
| 18 | // which means it is always supported => return true
|
---|
| 19 | //
|
---|
| 20 | // Could consider testing for native XSLT (using simple
|
---|
| 21 | // hardwired XML + XSL Tranform?) and using that
|
---|
| 22 | // if successful (presumably faster)
|
---|
| 23 | //
|
---|
| 24 | // Or even try to transform the given transform natively,
|
---|
| 25 | // and store 'isNativelySupported' as true/false in cookie
|
---|
| 26 | // accordingly
|
---|
[30468] | 27 |
|
---|
[30480] | 28 | return true;
|
---|
| 29 | }
|
---|
[30468] | 30 |
|
---|
[30480] | 31 | function notSupportedCookie() {
|
---|
| 32 | document.cookie = 'supportsXSLT=false; expires=0; path=/';
|
---|
| 33 | }
|
---|
| 34 |
|
---|
[30468] | 35 | function notSupported() {
|
---|
[30480] | 36 | notSupportedCookie();
|
---|
| 37 |
|
---|
| 38 | // knock out 'client-' part from URL
|
---|
| 39 | var location_href = window.location.href;
|
---|
| 40 |
|
---|
| 41 | var new_location_href = location_href.replace(/^(.*)\/(client-)?([^\?\#]+)(.*)$/,"$1/$3$4");
|
---|
| 42 | console.log("Client-side XSLT not supported. Redirecting to: " + new_location_href);
|
---|
| 43 |
|
---|
| 44 | window.location = new_location_href;
|
---|
[30468] | 45 | }
|
---|
| 46 |
|
---|
[30480] | 47 | function getUrlParameterHashmap() {
|
---|
| 48 | var sPageURL = decodeURIComponent(window.location.search.substring(1));
|
---|
| 49 | var sURLVariables = sPageURL.split('&');
|
---|
| 50 |
|
---|
| 51 | var paramHashmap = {};
|
---|
[30468] | 52 |
|
---|
[30480] | 53 | for (var i = 0; i < sURLVariables.length; i++) {
|
---|
| 54 | var sParameterName = sURLVariables[i].split('=');
|
---|
| 55 | paramHashmap[sParameterName[0]] = sParameterName[1];
|
---|
[30468] | 56 | }
|
---|
[30480] | 57 |
|
---|
| 58 | return paramHashmap;
|
---|
| 59 | }
|
---|
| 60 |
|
---|
| 61 | // From:
|
---|
| 62 | // http://stackoverflow.com/questions/298750/how-do-i-select-text-nodes-with-jquery
|
---|
| 63 | function getTextNodesIn(node, includeWhitespaceNodes) {
|
---|
| 64 | var textNodes = [], nonWhitespaceMatcher = /\S/;
|
---|
| 65 |
|
---|
| 66 | function getTextNodes(node) {
|
---|
| 67 | if (node.nodeType == 3) {
|
---|
| 68 | if (includeWhitespaceNodes || nonWhitespaceMatcher.test(node.nodeValue)) {
|
---|
| 69 | textNodes.push(node);
|
---|
| 70 | }
|
---|
| 71 | }
|
---|
| 72 | else {
|
---|
| 73 | for (var i = 0, len = node.childNodes.length; i < len; ++i) {
|
---|
| 74 | getTextNodes(node.childNodes[i]);
|
---|
| 75 | }
|
---|
| 76 | }
|
---|
[30468] | 77 | }
|
---|
| 78 |
|
---|
[30480] | 79 | getTextNodes(node);
|
---|
| 80 | return textNodes;
|
---|
| 81 | }
|
---|
[30468] | 82 |
|
---|
[30480] | 83 | function applyDisableEscapingToTextNodes(elem)
|
---|
| 84 | {
|
---|
| 85 | var textNodes = getTextNodesIn(elem,false); // ignore whitespace
|
---|
| 86 |
|
---|
| 87 | for (var i=textNodes.length-1; i>=0; i--) {
|
---|
| 88 | var text_node = textNodes[i];
|
---|
| 89 | var text = text_node.nodeValue;
|
---|
| 90 | var html = $.parseHTML(text);
|
---|
| 91 | $(text_node).replaceWith(html);
|
---|
| 92 | }
|
---|
| 93 | }
|
---|
| 94 |
|
---|
| 95 |
|
---|
| 96 |
|
---|
[30468] | 97 | var onSaxonLoad = function() {
|
---|
| 98 |
|
---|
| 99 | try {
|
---|
[30480] | 100 | var paramHashmap = getUrlParameterHashmap();
|
---|
| 101 |
|
---|
[30468] | 102 | var rooturl = window.location.pathname;
|
---|
| 103 | var queryStr = window.location.search.substring(1);
|
---|
| 104 | queryStr = queryStr.replace(/o=.*?(&|$)/g,"");
|
---|
| 105 | if (queryStr != '') {
|
---|
| 106 | queryStr += "&";
|
---|
| 107 | }
|
---|
| 108 | queryStr += "o=clientside";
|
---|
| 109 |
|
---|
[30480] | 110 | paramHashmap['o']="clientside";
|
---|
| 111 |
|
---|
| 112 | //console.log("*** rooturl = " + rooturl);
|
---|
| 113 | //console.log("*** queryStr = " + queryStr);
|
---|
| 114 |
|
---|
[30468] | 115 | var skindoc = "";
|
---|
| 116 | var xmldoc = "";
|
---|
| 117 |
|
---|
[30480] | 118 | $.get(rooturl, paramHashmap, function(data) {
|
---|
[30468] | 119 |
|
---|
| 120 | var toplevel_children = $(data).children().eq(0).children();
|
---|
| 121 | var skindoc = toplevel_children[0];
|
---|
| 122 | var xmldoc = toplevel_children[1];
|
---|
[30480] | 123 |
|
---|
[30468] | 124 | var library_name = $('xsltparams>param[name=library_name]', xmldoc).text();
|
---|
| 125 | var interface_name = $('xsltparams>param[name=interface_name]', xmldoc).text();
|
---|
[30480] | 126 | var site_name = $('xsltparams>param[name=site_name]', xmldoc).text();
|
---|
| 127 | var use_client_side_xslt = $('xsltparams>param[name=use_client_side_xslt]', xmldoc).text();
|
---|
[30468] | 128 |
|
---|
| 129 | //// Convert temporarily to text here
|
---|
| 130 | skindoc = convertToString(skindoc);
|
---|
| 131 | xmldoc = convertToString(xmldoc);
|
---|
| 132 |
|
---|
| 133 | skindoc = skindoc.replace(/<xslt:stylesheet\s+/,"<xslt:stylesheet xmlns:ixsl=\"http://saxonica.com/ns/interactiveXSLT\" xmlns:js=\"http://saxonica.com/ns/globalJS\" ");
|
---|
| 134 | skindoc = skindoc.replace(/extension-element-prefixes="(.*?)"/,"extension-element-prefixes=\"$1 ixsl\"");
|
---|
| 135 |
|
---|
[30480] | 136 | skindoc = skindoc.replace(/util\:exists\(\$meta, ''\)/g, "$meta!=''"); // For now - use regex instead
|
---|
| 137 | skindoc = skindoc.replace(/util:replace\((.*?)\)/g, "replace($1)"); // 'replace()' exists in XSLT 2.0
|
---|
| 138 | skindoc = skindoc.replace(/util:storeString\(\s*'(.+?)'\s*,\s*'(.*?)'\s*\)/g, "js:storeString(string($1),string($2))");
|
---|
| 139 | skindoc = skindoc.replace(/util:getString\('(.+?)'\)/g, "js:getString(string($1))");
|
---|
| 140 | skindoc = skindoc.replace(/util:escapeNewLinesAndQuotes\(([^)]+)\)/g, "js:escapeNewLinesAndQuotes(string($1))");
|
---|
| 141 | //attr_val = attr_val.replaceAll("util:escapeNewLinesAndQuotes\\(\\s*(.+?)\\s*\\)","$1");
|
---|
[30468] | 142 |
|
---|
[30480] | 143 | skindoc = skindoc.replace(/util:getDetailFromDate\((.+?),.+?,.+?\)/g, "'getDetailFromDate $1'"); // ****
|
---|
[30468] | 144 |
|
---|
[30480] | 145 | skindoc = skindoc.replace(/util:oidIsMatchOrParent\(([^,]+),([^)]+)\)/g,"js:oidIsMatchOrParent(string($1),string($2))");
|
---|
| 146 | skindoc = skindoc.replace(/util:hashToDepthClass\(([^)]+)\)/g,"js:hashToDepthClass(string($1))");
|
---|
| 147 | skindoc = skindoc.replace(/util:hashToSectionId\(([^)]+)\)/g,"js:hashToSectionId(string($1))");
|
---|
| 148 |
|
---|
| 149 | skindoc = skindoc.replace(/java:.*?getNumberedItem\(([^,]+),([^)]+)\)/g,"js:getNumberedItem(string($1),string($2))");
|
---|
| 150 |
|
---|
[30468] | 151 | // Convert to XML
|
---|
| 152 | xmldoc = parseFromString(xmldoc, "text/xml");
|
---|
| 153 | skindoc = parseFromString(skindoc, "text/xml");
|
---|
[30480] | 154 |
|
---|
| 155 | console.log("Applying client-side XSLT");
|
---|
| 156 | var output = '';
|
---|
| 157 |
|
---|
| 158 | //var proc = Saxon.newXSLT20Processor(skindoc);
|
---|
| 159 | var proc = Saxon.newXSLT20Processor();
|
---|
[30468] | 160 |
|
---|
| 161 | proc.setParameter(null, 'library_name', library_name);
|
---|
| 162 | proc.setParameter(null, 'interface_name', interface_name);
|
---|
[30480] | 163 | proc.setParameter(null, 'site_name', site_name);
|
---|
| 164 | proc.setParameter(null, 'use_client_side_xslt', use_client_side_xslt);
|
---|
[30468] | 165 |
|
---|
[30480] | 166 | // Consider making above XSLT20Porcessor constructor take no arguments,
|
---|
| 167 | // and specify transform through importStylesheet(), this combination
|
---|
| 168 | // of code is more consistent with other XSLT systems
|
---|
| 169 | //
|
---|
| 170 | proc.importStylesheet(skindoc);
|
---|
| 171 |
|
---|
[30468] | 172 | result = proc.transformToDocument(xmldoc);
|
---|
[30480] | 173 | //result = proc.transformToFragment(xmldoc);
|
---|
| 174 |
|
---|
| 175 | //proc.updateHTMLDocument(xmldoc);
|
---|
| 176 | //return;
|
---|
| 177 |
|
---|
| 178 | var excerptid = paramHashmap['excerptid'];
|
---|
| 179 | if (excerptid) {
|
---|
| 180 | result = result.getElementById(excerptid);
|
---|
| 181 | }
|
---|
| 182 |
|
---|
| 183 | applyDisableEscapingToTextNodes(result);
|
---|
[30468] | 184 | xmlSer = new XMLSerializer();
|
---|
| 185 | output = xmlSer.serializeToString(result);
|
---|
[30480] | 186 |
|
---|
| 187 | if (excerptid) {
|
---|
| 188 | var callback = paramHashmap['callback'];
|
---|
| 189 | parent[callback](output);
|
---|
| 190 | }
|
---|
| 191 | else {
|
---|
| 192 | var doc = document.open();
|
---|
| 193 | doc.write(output);
|
---|
| 194 | doc.close();
|
---|
[30468] | 195 |
|
---|
[30480] | 196 | // ****
|
---|
| 197 | document.cookie = 'supportsXSLT=true; expires=0; path=/';
|
---|
| 198 | }
|
---|
[30468] | 199 |
|
---|
| 200 |
|
---|
| 201 | }, 'xml');
|
---|
[30480] | 202 |
|
---|
[30468] | 203 | }
|
---|
| 204 | catch (e) {
|
---|
[30480] | 205 | alert("Error occured:" + e.message + "\n======\nSee web browser console for more details");
|
---|
| 206 | notSupported();
|
---|
| 207 |
|
---|
[30468] | 208 | }
|
---|
| 209 | }
|
---|
| 210 |
|
---|
| 211 |
|
---|
[30480] | 212 | function convertToString(content) {
|
---|
[30468] | 213 | try {
|
---|
[30480] | 214 | // If this fails, it's another indication that the browser doesn't have the support we need
|
---|
| 215 | if(typeof XMLSerializer != 'undefined') {
|
---|
| 216 | return (new XMLSerializer()).serializeToString(content);
|
---|
| 217 | } else {
|
---|
| 218 | return content.xml;
|
---|
| 219 | }
|
---|
[30468] | 220 | }
|
---|
| 221 | catch (e) {
|
---|
| 222 | notSupported();
|
---|
| 223 | }
|
---|
| 224 | }
|
---|
| 225 |
|
---|
| 226 | function parseFromString(content, contentType) {
|
---|
| 227 | try {
|
---|
| 228 | var retobj;
|
---|
| 229 |
|
---|
| 230 | if(typeof window.DOMParser != 'undefined') {
|
---|
| 231 | // Firefox, Chrome
|
---|
| 232 | retobj = (new DOMParser()).parseFromString(content, contentType);
|
---|
| 233 | } else {
|
---|
| 234 | // IE
|
---|
| 235 | var retobj = new ActiveXObject("Microsoft.XMLDOM");
|
---|
| 236 | retobj.async = "false";
|
---|
| 237 | retobj.loadXML(content);
|
---|
| 238 | }
|
---|
| 239 |
|
---|
| 240 | return retobj;
|
---|
| 241 | }
|
---|
| 242 | catch(e) {
|
---|
| 243 | var obj = new ActiveXObject('MSXML.DomDocument');
|
---|
| 244 | obj.async = false; obj.loadXML(content);
|
---|
| 245 | return obj;
|
---|
| 246 | }
|
---|
| 247 | }
|
---|
| 248 |
|
---|
| 249 |
|
---|
| 250 | /* Simulating web storage for browsers that don't support it */
|
---|
| 251 | /* Credit: http://www.thewojogroup.com/2010/01/simulating-html5-local-storage/ */
|
---|
| 252 | (function(){var k=this;if(!k.localStorage&&navigator.cookieEnabled){var x="storageData_",y="++",z="::",l=function(a,c,b){var e=new Date;e.setTime(e.getTime()+b);b="; expires="+e.toGMTString();document.cookie=a+"="+c+b+"; path=/"},h=function(a){a=a+"=";for(var c=document.cookie.split(";"),b=0,e=c.length;b<e;b++){for(var d=c[b];d.charAt(0)==" ";)d=d.substring(1,d.length);if(d.indexOf(a)===0)return d.substring(a.length,d.length)}return null},m=function(a){l(a,"",-1)},i=function(){for(var a="",c=0;h(y+c)!==null;)a+=h(y+
|
---|
| 253 | c++);return a==""?[]:a.split(y)},n=function(a){for(var c=Math.ceil(a.length/4E3),b=0;b<c||h(y+b)!==null;){b<c?l(y+b,a.substr(b*4E3,(b+1)*4E3>a.length?a.length-b*4E3:4E3),2592E3):m(y+b);b++}},f=k.localStorage={length:0,setItem:function(a,c){var b=i(),e=0,d=b.length,g=false;for(e=0;e<d;e++){var j=b[e].split(z);if(j[0]==a){j[1]=c;g=true;b[e]=j.join(z);e=d}}if(!g){b.push(a+z+c.replace(/::/g,": :").replace(/\+\+/g,"+ +"));this.a.push(a);this.length++}n(b.join(y))},
|
---|
| 254 | getItem:function(a){var c=i(),b=0,e=c.length;for(b=0;b<e;b++){var d=c[b].split(z);if(d[0]==a&&d[1])return d[1]}return null},removeItem:function(a){var c=i(),b=0,e=c.length,d=false,g=[];for(b=0;b<e;b++)if(c[b].split(z)[0]!=a)g.push(c[b]);else d=true;if(d){n(g.join(y));o()}},clear:function(){for(var a=0;h(y+a)!==null;)m(y+a++);this.a=[];this.length=0},key:function(a){return a<this.length?this.a[a]:null},a:[]},o=function(){f.a=i();for(var a=0;f.a[a];)f.a[a]=f.a[a++].split(z)[0];
|
---|
| 255 | f.length=f.a.length};o()}})();
|
---|