All rules from html2wl are now implemented.
[redakcja.git] / platforma / static / js / xslt.js
1 /*
2  * 
3  * XSLT STUFF
4  * 
5  */
6 function createXSLT(xsl) {
7     var p = new XSLTProcessor();
8     p.importStylesheet(xsl);
9     return p;
10 }
11
12 var xml2htmlStylesheet = null;
13
14 // Wykonuje block z załadowanymi arkuszami stylów
15 function withStylesheets(code_block, onError) 
16 {       
17     if (!xml2htmlStylesheet) {
18         $.blockUI({message: 'Ładowanie arkuszy stylów...'});
19         $.ajax({
20                 url: STATIC_URL + 'xsl/wl2html_client.xsl',
21                 dataType: 'xml',
22                 success: function(data) {
23                 xml2htmlStylesheet = createXSLT(data);
24                 $.unblockUI();
25                                 code_block();
26                  
27             },
28                         error: onError
29         })
30     }
31         else {
32                 code_block();
33         }
34 }
35
36
37 function xml2html(options) {
38     withStylesheets(function() {
39         var xml = options.xml.replace(/\/\s+/g, '<br />');                              
40         var parser = new DOMParser();
41         var serializer = new XMLSerializer();
42         var doc = parser.parseFromString(xml, 'text/xml');              
43         var error = $('parsererror', doc);
44         
45         if (error.length == 0) {
46             doc = xml2htmlStylesheet.transformToDocument(doc);
47                         console.log(doc);
48             error = $('parsererror', doc);
49         }
50         
51         if (error.length > 0 && options.error) {
52             options.error(error.text());
53         } else {                        
54             options.success(document.importNode(doc.documentElement, true));
55         }
56     }, function() { options.error && options.error('Nie udało się załadować XSLT'); });
57 }
58
59 /* USEFULL CONSTANTS */
60 const ELEMENT_NODE                                       = 1;
61 const ATTRIBUTE_NODE                 = 2;
62 const TEXT_NODE                      = 3;
63 const CDATA_SECTION_NODE             = 4;
64 const ENTITY_REFERENCE_NODE          = 5;
65 const ENTITY_NODE                    = 6;
66 const PROCESSING_INSTRUCTION_NODE    = 7;
67 const COMMENT_NODE                   = 8;
68 const DOCUMENT_NODE                  = 9;
69 const DOCUMENT_TYPE_NODE             = 10;
70 const DOCUMENT_FRAGMENT_NODE         = 11;
71 const NOTATION_NODE                  = 12;
72 const XATTR_RE = /^x-attr-name-(.*)$/;
73
74 const ELEM_START = 1;
75 const ELEM_END = 2;
76 const NS_END = 3;
77
78 const NAMESPACES = {
79         // namespaces not listed here will be assigned random names
80         "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
81         "http://purl.org/dc/elements/1.1/": "dc",
82         "http://www.w3.org/XML/1998/namespace": "xml"
83 };
84
85 /*
86  * PADDING for pretty-printing
87  */
88 const PADDING = {
89     dramat_wierszowany_l: 4,
90     dramat_wierszowany_lp: 4,
91     dramat_wspolczesny: 4,
92     wywiad: 4,
93     opowiadanie: 4,
94     powiesc: 4,
95     liryka_l: 4,
96     liryka_lp: 4,
97     naglowek_czesc: 4,
98     naglowek_akt: 4,
99     naglowek_rozdzial: 4,
100     naglowek_osoba: 4,
101     lista_osob: 4,
102     
103     akap: 3,
104     akap_cd: 3,
105     akap_dialog: 3,
106     strofa: 3,
107     motto: 3, 
108     miejsce_czas: 3,
109         
110     autor_utworu: 2,
111     nazwa_utworu: 2,
112     dzielo_nadrzedne: 2,
113         
114     didaskalia: 2,
115     motto_podpis: 2,
116     naglowek_listy: 2,
117     kwestia: 1,
118     lista_osoba: 1,
119         
120         "podpis": 1,    
121         "wers": 1,
122         "wers_cd": 1,
123         "wers_akap": 1,
124         "wers_wciety": 1,       
125         
126         "rdf:RDF": 3,   
127 }
128
129 function HTMLSerializer() {     
130         // empty constructor
131 }
132
133 HTMLSerializer.prototype._prepare = function() {
134         this.stack = [];
135         
136         // XML namespace is implicit
137         this.nsMap = {"http://www.w3.org/XML/1998/namespace": "xml"};
138                 
139         this.result = "";
140         this.nsCounter = 1;     
141 }
142
143 HTMLSerializer.prototype._pushElement = function(element) {
144         this.stack.push({
145                 "type": ELEM_START,
146                 "node": element
147         });     
148 }
149
150 HTMLSerializer.prototype._pushChildren = function(element) {
151         for(var i = element.childNodes.length-1; i >= 0; i--)
152                 this._pushElement(element.childNodes.item(i));                                  
153 }
154
155 HTMLSerializer.prototype._pushTagEnd = function(tagName) {
156         this.stack.push({
157                 "type": ELEM_END,
158                 "tagName": tagName
159         });     
160 }
161
162 HTMLSerializer.prototype._verseBefore = function(node) {
163         var prev = node.previousSibling;
164         
165         while(prev) {
166                 if(prev.nodeType == ELEMENT_NODE && prev.hasAttribute('x-verse'))
167                         return true;
168         }
169         
170         return false;
171 }
172
173 HTMLSerializer.prototype.serialize = function(rootElement) 
174 {
175         var self = this;
176         self._prepare();
177         self._pushElement(rootElement); 
178         
179         while(self.stack.length > 0) {
180                 var token = self.stack.pop();
181                                                 
182                 if(token.type === ELEM_END) {
183                         self.result += "</" + token.tagName + ">";
184                         if (PADDING[token.tagName]) {
185                                 for(var n=0; n < PADDING[token.tagName]; n++)                   
186                                         self.result += "\n";
187                         }
188                         continue;
189                 };
190                 
191                 if(token.type === NS_END) {
192                         self._unassignNamespace(token.namespace);
193                         continue;
194                 } 
195                 
196                 
197                 switch(token.node.nodeType) {
198                         case ELEMENT_NODE:
199                                 if(token.node.hasAttribute('x-pass-thru')) {
200                                         self._pushChildren(token.node);
201                                         break;
202                                 }
203                                 
204                                 if(!token.node.hasAttribute('x-node'))
205                                         break;
206                                         
207                                 var xnode = token.node.getAttribute('x-node');
208                                 
209                                 if(xnode === 'wers') {
210                                         /* push children */
211                                         if(self._verseBefore(token.node))
212                                                 self.result += '/\n';
213                                         self._pushChildren(token.node);
214                                         break;                                  
215                                 };                                      
216                                 
217                                 if(token.node.hasAttribute('x-verse') && self._verseBefore(token.node)) {
218                                         self.result += '/\n';                                   
219                                 };
220                                         
221                                 self._serializeElement(token.node);
222                                 break;
223                         case TEXT_NODE:
224                                 self.result += token.node.nodeValue;
225                                 break;
226                 };
227         };
228         
229         return this.result;
230 }
231
232 /*
233  * TODO: this doesn't support prefix redefinitions
234  */
235 HTMLSerializer.prototype._unassignNamespace = function(nsData) {
236         this.nsMap[nsData.uri] = undefined;
237 };
238         
239 HTMLSerializer.prototype._assignNamespace = function(uri) {
240         if(uri === null) {
241                 // default namespace
242                 return ({"prefix": "", "uri": "", "fresh": false});
243         }
244         
245         if(this.nsMap[uri] === undefined) {
246                 // this prefix hasn't been defined yet in current context       
247                 var prefix = NAMESPACES[uri];
248                 
249                 if (prefix === undefined) { // not predefined
250                         prefix = "ns" + this.nsCounter;
251                         this.nsCounter += 1;
252                 }
253                 
254                 this.nsMap[uri] = prefix;               
255                 return ({
256                         "prefix": prefix,
257                         "uri": uri,
258                         "fresh": true
259                 });                     
260         }       
261                 
262         return ({"prefix": this.nsMap[uri], "uri": uri, "fresh": false});               
263 };
264
265 HTMLSerializer.prototype._join = function(prefix, name) {
266         if(!!prefix) 
267                 return prefix + ":" + name;
268         return name;    
269 };
270
271 HTMLSerializer.prototype._rjoin = function(prefix, name) {
272         if(!!name) 
273                 return prefix + ":" + name;
274         return prefix;  
275 };
276                                         
277 HTMLSerializer.prototype._serializeElement = function(node) {
278         var self = this;
279                 
280         var ns = node.getAttribute('x-ns');
281         var nsPrefix = null;
282         var newNamespaces = [];
283         
284         var nsData = self._assignNamespace(node.getAttribute('x-ns'));
285         
286         if(nsData.fresh) {
287                 newNamespaces.push(nsData);
288                 self.stack.push({
289                         "type": NS_END,
290                         "namespace": nsData
291                 });
292         }                                       
293         
294         var tagName = self._join(nsData.prefix, node.getAttribute('x-node'));
295                                 
296         /* retrieve attributes */
297         var attributeIDs = [];
298         for (var i = 0; i < node.attributes.length; i++) {
299                 var attr = node.attributes.item(i);
300                 
301                 // check if name starts with "x-attr-name"
302                 var m = attr.name.match(XATTR_RE);
303                 if (m !== null) 
304                         attributeIDs.push(m[1]);                                
305         };
306                                 
307         /* print out */
308         self.result += '<' + tagName;   
309                                         
310         $.each(attributeIDs, function() {
311                 var nsData = self._assignNamespace(node.getAttribute('x-attr-ns-'+this));
312         
313                 if(nsData.fresh) {
314                         newNamespaces.push(nsData);
315                         self.stack.push({
316                                 "type": NS_END,
317                                 "namespace": nsData
318                         });
319                 };
320                                                                                                                         
321                 self.result += ' ' + self._join(nsData.prefix, node.getAttribute('x-attr-name-'+this));
322                 self.result += '="'+node.getAttribute('x-attr-value-'+this) +'"';
323         });
324         
325         /* print new namespace declarations */
326         $.each(newNamespaces, function() {
327                 self.result += " " + self._rjoin("xmlns", this.prefix);
328                 self.result += '="' + this.uri + '"';           
329         });                                     
330         
331         if (node.childNodes.length > 0) {
332                 self.result += ">";
333                 self._pushTagEnd(tagName);
334                 self._pushChildren(node);
335         }
336         else {
337                 self.result += "/>";
338         };
339 };
340
341 function html2text(params) {
342         if (params.stripOuter) {
343                 // ...
344         };
345         
346         try {
347                 var s = new HTMLSerializer();
348                 params.success( s.serialize(params.element) );
349         } catch(e) {
350                 params.error("Nie udało się zserializować tekstu:" + e)
351         }       
352 }