source: main/trunk/greenstone3/web/client-side-xslt.js@ 30468

Last change on this file since 30468 was 30468, checked in by davidb, 8 years ago

New work on supporting client-side XSLT in the browser

  • Property svn:executable set to *
File size: 13.9 KB
Line 
1/*
2
3Greenstone 3 'Client-side transformer'
4Performs client-side transformations of XSLT, using HTML5 local storage (simulated if the browser doesn't support it).
5
6Currently 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
9@date 14/02/2011
10
11*/
12
13/* These URLs and file paths are fetched dynamically */
14//var gsweb = ""; // The file path to the Greenstone 3 web directory
15//var gsurl = ""; // The root URL for this Greenstone 3 installation
16
17/* Misc. switches and paths */
18var keyUrl = ''; // Used across methods to build up query string for text retrieval (client-side transformed version)
19var on = true; // Set to false to disable operation
20var debug = true;
21
22var index = 0; // Used for query array (to keep track of number of elements)
23var deferredEls = new Array(); // Elements to defer text retrieval for until later
24var queryArr = new Array(); // Text to query for (the text for the corresponding deferred element)
25
26function notSupported() {
27 // Set not supported cookie here
28 document.cookie = 'supportsXSLT=false; expires=0; path=/';
29 // Fall back to server version
30 var location = window.location.search.substring(1);
31 if(location == '') {
32 // Start with a question mark
33 location = window.location + "?o=server";
34 } else {
35 // Start with an ampersand
36 location = window.location + "&o=server";
37 }
38 window.location = location;
39}
40
41/*
42$(document).ready(function() {
43 console.log("Client-side XSLT: Document is ready");
44
45 if (isSupported()) {
46 transform(false);
47 }
48 else {
49 notSupported();
50 }
51});
52*/
53
54
55var onSaxonLoad = function() {
56
57 try {
58 var rooturl = window.location.pathname;
59 var queryStr = window.location.search.substring(1);
60 queryStr = queryStr.replace(/o=.*?(&|$)/g,"");
61 if (queryStr != '') {
62 queryStr += "&";
63 }
64 queryStr += "o=clientside";
65
66 console.log("*** rooturl = " + rooturl);
67 console.log("*** queryStr = " + queryStr);
68
69 var skindoc = "";
70 var xmldoc = "";
71
72 $.get(rooturl + "?" + queryStr, function(data) {
73
74 //dataStr = dataStr.replace(/gs3:id/g, "gs3id");
75
76 //data = parseFromString(dataStr, "text/xml");
77
78 var toplevel_children = $(data).children().eq(0).children();
79 var skindoc = toplevel_children[0];
80 var xmldoc = toplevel_children[1];
81
82
83 var site_name = $('xsltparams>param[name=site_name]', xmldoc).text();
84 var library_name = $('xsltparams>param[name=library_name]', xmldoc).text();
85 var interface_name = $('xsltparams>param[name=interface_name]', xmldoc).text();
86
87 //// Convert temporarily to text here
88 skindoc = convertToString(skindoc);
89 xmldoc = convertToString(xmldoc);
90
91 //// This could just be done on the server (later)
92 ////data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" /*+ "<?xml-stylesheet type=\"text/xsl\" href=\"" + skinurl + "\"?>\r\n"*/ + data;
93 //// replace all!
94 //// Be careful with regex syntax and the use of special characters!
95 //skindoc = skindoc.replace(/util\:exists\(\$meta, ''\)/g, "$meta!=''"); // For now - use regex instead
96 //skindoc = skindoc.replace(/%3A/g, ":"); // undo colon escaping
97
98 //skindoc = skindoc.replace(/util\:.+?\(\.*?\)/g, "true");
99
100 skindoc = skindoc.replace(/<xslt:stylesheet\s+/,"<xslt:stylesheet xmlns:ixsl=\"http://saxonica.com/ns/interactiveXSLT\" xmlns:js=\"http://saxonica.com/ns/globalJS\" ");
101 //extension-element-prefixes="ixsl" exclude-result-prefixes="xs xd d js"
102 skindoc = skindoc.replace(/extension-element-prefixes="(.*?)"/,"extension-element-prefixes=\"$1 ixsl\"");
103 //skindoc = skindoc.replace(/exclude-result-prefixes="(.*?)"/,"exclude-result-prefixes=\"$1 js\"");
104
105
106 skindoc = skindoc.replace(/util\:exists\(\$meta, ''\)/g, "$meta!=''"); // For now - use regex instead
107 skindoc = skindoc.replace(/util:replace\((.*?)\)/g, "replace($1)"); // exists in XSLT 2.0
108 skindoc = skindoc.replace(/util:storeString\((.+?),(.+)?\)/g, "$1=$2");
109 skindoc = skindoc.replace(/util:getString\((.+?)\)/g, "$1");
110 skindoc = skindoc.replace(/util:getDetailFromDate\((.+?),.+?,.+?\)/g, "$1");
111
112 //skindoc = skindoc.replace(/util:getNumberedItem\(([^,]+),([^,]+)\)$/,"js:getNumberedItem($1,$2)"); // never tested
113
114 //skindoc = skindoc.replace(/\{\{/,"\\{");
115 //skindoc = skindoc.replace(/\}\}/,"\\}");
116 //skindoc = skindoc.replace(/"js:[^"]+"/g, "js:console.log('foo')");
117
118 // Convert to XML
119 xmldoc = parseFromString(xmldoc, "text/xml");
120 skindoc = parseFromString(skindoc, "text/xml");
121
122 var output = '';
123 /*
124 var params = { 'library_name': library_name, 'interface_name': interface_name };
125 var proc = Saxon.run( {
126 stylesheet: skindoc,
127 source: xmldoc,
128 parameters: params
129 });
130*/
131
132 /*
133 var xml = Saxon.requestXML("http://localhost:8383/greenstone3/mozarts-library/collection/digital-music-stand/page/about?o=xml");
134 var xsl = Saxon.requestXML("http://localhost:8383/greenstone3/mozarts-library/collection/digital-music-stand/page/about?o=skinandlibdocfinal");
135*/
136 var proc = Saxon.newXSLT20Processor(skindoc);
137
138 proc.setParameter(null, 'library_name', library_name);
139 proc.setParameter(null, 'interface_name', interface_name);
140 proc.setParameter(null, 'site_name', site_name);
141
142 result = proc.transformToDocument(xmldoc);
143 xmlSer = new XMLSerializer();
144 output = xmlSer.serializeToString(result);
145
146 var doc = document.open();
147 doc.write(output);
148 doc.close();
149
150 document.cookie = 'supportsXSLT=true; expires=0; path=/';
151
152
153
154 }, 'xml');
155 }
156 catch (e) {
157 if(trial) {
158 notSupportedCookie();
159 }
160 else {
161 notSupported();
162 }
163 }
164}
165
166
167
168
169function transform(trial) {
170
171 //alert("transform(): trial = " + trial);
172
173 try {
174 var rooturl = window.location.pathname;
175 var queryStr = window.location.search.substring(1);
176 queryStr = queryStr.replace(/o=.*?(&|$)/g,"");
177 if (queryStr != '') {
178 queryStr += "&";
179 }
180 queryStr += "o=clientside";
181
182 console.log("*** rooturl = " + rooturl);
183 console.log("*** queryStr = " + queryStr);
184
185 var skindoc = "";
186 var xmldoc = "";
187
188 $.get(rooturl + "?" + queryStr, function(dataStr) {
189 //dataStr = dataStr.trim();
190 dataStr = dataStr.replace(/gs3:id/g, "gs3id");
191 //dataStr = dataStr.replace(/&/g,"&amp;");
192 //dataStr = dataStr.replace(/&amp;quot;/g,"&quot;");
193 //dataStr = dataStr.replace(/&amp;lt;/g,"&lt;");
194 //dataStr = dataStr.replace(/&amp;gt;/g,"&gt;");
195 //dataStr = dataStr.replace(/&amp;amp;/g,"&amp;");
196
197 data = parseFromString(dataStr, "text/xml");
198
199 var toplevel_children = $(data).children().eq(0).children();
200 var skindoc = toplevel_children[0];
201 var xmldoc = toplevel_children[1];
202
203
204 //gsurl = $($(xmldoc).find("metadata[name=siteURL]")[0]).text();
205 //gsurl = $(xmldoc).find("pageRequest").attr("baseURL");
206 var site_name = $('xsltparams>param[name=site_name]', xmldoc).text();
207 var library_name = $('xsltparams>param[name=library_name]', xmldoc).text();
208 var interface_name = $('xsltparams>param[name=interface_name]', xmldoc).text();
209
210 //gsurl = $(xmldoc).find("pageRequest").attr("baseURL") + site_name;
211 //gsweb = new RegExp($($(xmldoc).find("param[name=filepath]")[0]).text().replace(/\\/g, "\\\\"), "g");
212
213 //// Convert temporarily to text here
214 skindoc = convertToString(skindoc);
215 xmldoc = convertToString(xmldoc);
216
217 //// This could just be done on the server (later)
218 ////data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" /*+ "<?xml-stylesheet type=\"text/xsl\" href=\"" + skinurl + "\"?>\r\n"*/ + data;
219 //// replace all!
220 //// Be careful with regex syntax and the use of special characters!
221 //skindoc = skindoc.replace(/util\:exists\(\$meta, ''\)/g, "$meta!=''"); // For now - use regex instead
222 //skindoc = skindoc.replace(/%3A/g, ":"); // undo colon escaping
223
224 //skindoc = skindoc.replace(/util\:.+?\(\.*?\)/g, "true");
225
226 skindoc = skindoc.replace(/util\:exists\(\$meta, ''\)/g, "$meta!=''"); // For now - use regex instead
227 skindoc = skindoc.replace(/util:storeString\((.+?),(.+)?\)/g, "$1=$2");
228 skindoc = skindoc.replace(/util:getString\((.+?)\)/g, "$1");
229 skindoc = skindoc.replace(/util:getDetailFromDate\((.+?),.+?,.+?\)/g, "$1");
230
231
232 // Convert to XML
233 xmldoc = parseFromString(xmldoc, "text/xml");
234 skindoc = parseFromString(skindoc, "text/xml");
235
236 var output = '';
237
238 // And post-process...
239 if(window.ActiveXObject) {
240 // IE
241 var procFactory = new ActiveXObject("MSXML2.XSLTemplate");
242 procFactory.stylesheet = skindoc;
243 var proc = procFactory.createProcessor();
244 proc.input = xmldoc;
245
246 proc.addParameter('library_name', library_name);
247 proc.addParameter('interface_name', interface_name);
248
249 proc.transform();
250 output = proc.output;
251 }
252 else {
253 // Firefox, Chrome ...
254 xsltProc = new XSLTProcessor();
255 // The leading slash is oh-so important here
256 xsltProc.setParameter(null, 'library_name', library_name);
257 xsltProc.setParameter(null, 'interface_name', interface_name);
258 try {
259 xsltProc.importStylesheet(skindoc);
260 }
261 catch(e) {
262 alert("Malformed importStyle sheet:" + e.message + "\n======\nSee web browser console for more details");
263 // Look for util: of java:
264 if (debug) {
265 console.error("Stylesheet:\n" + skindoc.documentElement.outerHTML);
266 }
267 }
268
269 //
270 try {
271 result = xsltProc.transformToDocument(xmldoc);
272 xmlSer = new XMLSerializer();
273 output = xmlSer.serializeToString(result);
274 }
275 catch(e) {
276 alert("XSL Transform failed:" + e.message + "\n======\nSee web browser console for more details");
277 console.error("doc:\n" + convertToString(xmldoc) + "\n========\nStylesheet:\n" + convertToString(skindoc));
278 }
279 }
280
281 var doc = document.open();
282 doc.write(output);
283 doc.close();
284
285 document.cookie = 'supportsXSLT=true; expires=0; path=/';
286
287 }, 'text');
288 }
289 catch (e) {
290 if(trial) {
291 notSupportedCookie();
292 }
293 else {
294 notSupported();
295 }
296 }
297}
298
299function notSupportedCookie() {
300 document.cookie = 'supportsXSLT=false; expires=0; path=/';
301}
302
303
304// Method equivalent to PHP's in_array method
305function contains(array, value) {
306
307 for(var val in array)
308 if(array[val] == value)
309 return true;
310
311 return false;
312}
313
314// Method equivalent to PHP's own
315function print_r(arr) {
316
317 var result = "";
318
319 for(var a in arr) {
320 result += a + " => " + arr[a] + "\r\n";
321 }
322
323 return result;
324}
325
326function convertToString(content) {
327 try {
328 // If this fails, it's another indication that the browser doesn't have the support we need
329 if(typeof XMLSerializer != 'undefined') {
330 return (new XMLSerializer()).serializeToString(content);
331 } else {
332 return content.xml;
333 }
334 }
335 catch (e) {
336 notSupported();
337 }
338}
339
340function parseFromString(content, contentType) {
341 try {
342 var retobj;
343
344 if(typeof window.DOMParser != 'undefined') {
345 // Firefox, Chrome
346 retobj = (new DOMParser()).parseFromString(content, contentType);
347 } else {
348 // IE
349 var retobj = new ActiveXObject("Microsoft.XMLDOM");
350 retobj.async = "false";
351 retobj.loadXML(content);
352 }
353
354 return retobj;
355 }
356 catch(e) {
357 var obj = new ActiveXObject('MSXML.DomDocument');
358 obj.async = false; obj.loadXML(content);
359 return obj;
360 }
361}
362
363function isSupported() {
364
365 return true;
366 // change test below to simple XML + XSL Transform
367
368 /*
369 // Are cookies enabled?
370 if(navigator.cookieEnabled && typeof navigator.cookieEnabled != 'undefined') {
371 // Is there a cookie?
372 if(document.cookie.indexOf('supportsXSLT=') > -1) {
373 // Cookie exists - don't try the transformation, as the server will
374 // read the cookie and determine which version to serve up.
375 // If it happens to be client-side, allow transformation to proceed
376 return (document.cookie.indexOf('supportsXSLT=true') > -1);
377 } else {
378 // Cookie doesn't exist - test!
379 transform(true);
380 applyText(true);
381 return (document.cookie.indexOf('supportsXSLT=true') > -1);
382 }
383 } else {
384 return false;
385 }
386*/
387}
388
389/* Simulating web storage for browsers that don't support it */
390/* Credit: http://www.thewojogroup.com/2010/01/simulating-html5-local-storage/ */
391(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+
392c++);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))},
393getItem: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];
394f.length=f.a.length};o()}})();
Note: See TracBrowser for help on using the repository browser.