Changeset 30480


Ignore:
Timestamp:
2016-04-20T22:59:50+12:00 (8 years ago)
Author:
davidb
Message:

Next iteration in the development of this file. There are still some DL pages that need some porting to work with the new system, but the majority of them now work, and it tends to be ones that make use of AJAX calls the need addressing.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/web/client-side-xslt.js

    r30468 r30480  
    77
    88@author     Steven McTainsh
    9 @date       14/02/2011
     9@author     David Bainbridge
     10@date       2011-2016
    1011
    1112*/
    1213
    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 */
    18 var keyUrl = ''; // Used across methods to build up query string for text retrieval (client-side transformed version)
    19 var on = true; // Set to false to disable operation
    20 var debug = true;
    21 
    22 var index = 0; // Used for query array (to keep track of number of elements)
    23 var deferredEls = new Array(); // Elements to defer text retrieval for until later
    24 var queryArr = new Array(); // Text to query for (the text for the corresponding deferred element)
     14
     15function 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
     27
     28    return true;
     29}
     30
     31function notSupportedCookie() {
     32    document.cookie = 'supportsXSLT=false; expires=0; path=/';
     33}
    2534
    2635function 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");
     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);
    4443   
    45     if (isSupported()) {       
    46     transform(false);
    47     }
    48     else {
    49     notSupported();
    50     }
    51 });
    52 */
     44    window.location = new_location_href;
     45}
     46
     47function getUrlParameterHashmap() {
     48    var sPageURL = decodeURIComponent(window.location.search.substring(1));
     49    var sURLVariables = sPageURL.split('&');
     50
     51    var paramHashmap = {};
     52   
     53    for (var i = 0; i < sURLVariables.length; i++) {
     54        var sParameterName = sURLVariables[i].split('=');
     55    paramHashmap[sParameterName[0]] = sParameterName[1];
     56    }
     57
     58    return paramHashmap;
     59}
     60
     61// From:
     62//   http://stackoverflow.com/questions/298750/how-do-i-select-text-nodes-with-jquery
     63function 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        }
     77    }
     78
     79    getTextNodes(node);
     80    return textNodes;
     81}
     82
     83function 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
    5395
    5496
     
    5698
    5799    try {
     100    var paramHashmap = getUrlParameterHashmap();
     101   
    58102    var rooturl = window.location.pathname;
    59103    var queryStr = window.location.search.substring(1);
     
    63107    }
    64108    queryStr += "o=clientside";
    65        
    66     console.log("*** rooturl = " + rooturl);
    67     console.log("*** queryStr = " + queryStr);
     109
     110    paramHashmap['o']="clientside";
     111   
     112    //console.log("*** rooturl = " + rooturl);
     113    //console.log("*** queryStr = " + queryStr);
    68114
    69115    var skindoc = "";
    70116    var xmldoc = "";   
    71117   
    72     $.get(rooturl + "?" + queryStr, function(data) {
    73 
    74         //dataStr = dataStr.replace(/gs3:id/g, "gs3id");
    75        
    76         //data = parseFromString(dataStr, "text/xml");
    77        
     118    $.get(rooturl, paramHashmap, function(data) {
     119
    78120        var toplevel_children = $(data).children().eq(0).children();
    79121        var skindoc = toplevel_children[0];
    80122        var xmldoc = toplevel_children[1];
    81        
    82        
    83         var site_name = $('xsltparams>param[name=site_name]', xmldoc).text();
     123           
    84124        var library_name = $('xsltparams>param[name=library_name]', xmldoc).text();
    85125        var interface_name = $('xsltparams>param[name=interface_name]', xmldoc).text();
     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();
    86128       
    87129        //// Convert temporarily to text here       
     
    89131        xmldoc = convertToString(xmldoc);
    90132       
    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 
    100133        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"
    102134        skindoc = skindoc.replace(/extension-element-prefixes="(.*?)"/,"extension-element-prefixes=\"$1 ixsl\"");
    103         //skindoc = skindoc.replace(/exclude-result-prefixes="(.*?)"/,"exclude-result-prefixes=\"$1 js\"");
    104        
    105135       
    106136        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        
     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");
     142       
     143        skindoc = skindoc.replace(/util:getDetailFromDate\((.+?),.+?,.+?\)/g, "'getDetailFromDate $1'"); // ****
     144
     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
    118151        // Convert to XML
    119152        xmldoc = parseFromString(xmldoc, "text/xml");           
    120153        skindoc = parseFromString(skindoc, "text/xml");
    121        
     154
     155        console.log("Applying client-side XSLT");       
    122156        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        
     157
     158        //var proc = Saxon.newXSLT20Processor(skindoc);
     159        var proc = Saxon.newXSLT20Processor();
     160       
    138161        proc.setParameter(null, 'library_name', library_name);
    139162        proc.setParameter(null, 'interface_name', interface_name);
    140         proc.setParameter(null, 'site_name', site_name);       
    141 
     163        proc.setParameter(null, 'site_name', site_name);
     164        proc.setParameter(null, 'use_client_side_xslt', use_client_side_xslt);     
     165
     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       
    142172        result = proc.transformToDocument(xmldoc);
     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);
    143184        xmlSer = new XMLSerializer();
    144185        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 
    169 function 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            
     186       
     187        if (excerptid) {
     188        var callback = paramHashmap['callback'];
     189        parent[callback](output);       
     190        }
     191        else {
    281192        var doc = document.open();
    282193        doc.write(output);
    283194        doc.close();
     195
     196        // ****
     197        document.cookie = 'supportsXSLT=true; expires=0; path=/';
     198        }
    284199           
    285         document.cookie = 'supportsXSLT=true; expires=0; path=/';
    286        
    287         }, 'text');
    288     }
     200       
     201    }, 'xml');
     202
     203    }
    289204    catch (e) {
    290     if(trial) {
    291         notSupportedCookie();
    292     }
    293     else {
    294         notSupported();
    295     }
    296     }
    297 }
    298 
    299 function notSupportedCookie() {
    300     document.cookie = 'supportsXSLT=false; expires=0; path=/';
    301 }
    302 
    303 
    304 // Method equivalent to PHP's in_array method
    305 function 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
    315 function 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 }
     205    alert("Error occured:" + e.message + "\n======\nSee web browser console for more details");
     206    notSupported();
     207   
     208    }
     209}
     210
    325211
    326212function 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     }
     213    try {
     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    }       
     220    }
    335221    catch (e) {
    336222    notSupported();
     
    361247}
    362248
    363 function 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 }
    388249
    389250/* Simulating web storage for browsers that don't support it */
Note: See TracChangeset for help on using the changeset viewer.