1if not modules then modules = { } end modules ['lxml-css'] = {
2 version = 1.001,
3 comment = "companion to lxml-css.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local tonumber, rawset, type, select = tonumber, rawset, type, select
10local lower, format, find, gmatch = string.lower, string.format, string.find, string.gmatch
11local topattern, is_empty = string.topattern, string.is_empty
12local P, S, C, R, Cb, Cg, Carg, Ct, Cc, Cs = lpeg.P, lpeg.S, lpeg.C, lpeg.R, lpeg.Cb, lpeg.Cg, lpeg.Carg, lpeg.Ct, lpeg.Cc, lpeg.Cs
13
14local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
15local sort = table.sort
16local setmetatableindex = table.setmetatableindex
17
18xml.css = xml.css or { }
19local css = xml.css
20
21local report_css = logs and logs.reporter("xml","css") or function(...) print(string.format(...)) end
22
23local getid = lxml and lxml.getid
24
25if not number.dimenfactors then
26 require("util-dim.lua")
27end
28
29local dimenfactors = number.dimenfactors
30local bpf = 1/dimenfactors.bp
31local cmf = 1/dimenfactors.cm
32local mmf = 1/dimenfactors.mm
33local inf = 1/dimenfactors["in"]
34
35local whitespace = lpegpatterns.whitespace
36local skipspace = whitespace^0
37
38local percentage, exheight, emwidth, pixels
39
40if tex then
41
42 local exheights = fonts.hashes.exheights
43 local emwidths = fonts.hashes.emwidths
44 local texget = tex.get
45
46 percentage = function(s,pcf) return tonumber(s) * (pcf or texget("hsize")) end
47 exheight = function(s,exf) return tonumber(s) * (exf or exheights[true]) end
48 emwidth = function(s,emf) return tonumber(s) * (emf or emwidths[true]) end
49 pixels = function(s,pxf) return tonumber(s) * (pxf or emwidths[true]/300) end
50
51else
52
53 local function generic(s,unit) return tonumber(s) * unit end
54
55 percentage = generic
56 exheight = generic
57 emwidth = generic
58 pixels = generic
59
60end
61
62local validdimen = Cg(lpegpatterns.number,'a') * (
63 Cb('a') * P("pt") / function(s) return tonumber(s) * bpf end
64 + Cb('a') * P("cm") / function(s) return tonumber(s) * cmf end
65 + Cb('a') * P("mm") / function(s) return tonumber(s) * mmf end
66 + Cb('a') * P("in") / function(s) return tonumber(s) * inf end
67 + Cb('a') * P("px") * Carg(1) / pixels
68 + Cb('a') * P("%") * Carg(2) / percentage
69 + Cb('a') * P("ex") * Carg(3) / exheight
70 + Cb('a') * P("em") * Carg(4) / emwidth
71 + Cb('a') * Carg(1) / pixels
72 )
73
74local pattern = (validdimen * skipspace)^1
75
76
77
78local function dimension(str,pixel,percent,exheight,emwidth)
79 return (lpegmatch(pattern,str,1,pixel,percent,exheight,emwidth))
80end
81
82local function padding(str,pixel,percent,exheight,emwidth)
83 local top, bottom, left, right = lpegmatch(pattern,str,1,pixel,percent,exheight,emwidth)
84 if not bottom then
85 bottom, left, right = top, top, top
86 elseif not left then
87 bottom, left, right = top, bottom, bottom
88 elseif not right then
89 bottom, left, right = left, bottom, bottom
90 end
91 return top, bottom, left, right
92end
93
94css.dimension = dimension
95css.padding = padding
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124local context = context
125
126if context then
127
128 local currentfont = font.current
129 local texget = tex.get
130 local hashes = fonts.hashes
131 local quads = hashes.quads
132 local xheights = hashes.xheights
133
134 local function todimension(str)
135 local font = currentfont()
136 local exheight = xheights[font]
137 local emwidth = quads[font]
138 local hsize = texget("hsize")/100
139 local pixel = emwidth/100
140 return dimension(str,pixel,hsize,exheight,emwidth)
141 end
142
143 css.todimension = todimension
144
145 function context.cssdimension(str)
146
147 context(todimension(str) .. "sp")
148 end
149
150end
151
152
153do
154
155 local p_digit = lpegpatterns.digit
156 local p_unquoted = Cs(lpegpatterns.unquoted)
157 local p_size = (S("+-")^0 * (p_digit^0 * P(".") * p_digit^1 + p_digit^1 * P(".") + p_digit^1)) / tonumber
158 * C(P("p") * S("txc") + P("e") * S("xm") + S("mc") * P("m") + P("in") + P("%"))
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183 local pattern = Ct("") * (
184 Cg(
185 Cc("style") * (
186 C("italic")
187 + C("oblique")
188 + C("slanted") / "oblique"
189 )
190 + Cc("variant") * (
191 (C("smallcaps") + C("caps")) / "small-caps"
192 )
193 + Cc("weight") * (
194 C("bold")
195 )
196 + Cc("family") * (
197 (C("mono") + C("type")) / "monospace"
198 + (C("sansserif") + C("sans")) / "sans-serif"
199 + C("serif")
200 )
201 + Cc("size") * Ct(p_size)
202 ) % rawset
203 + P(1)
204 )^0
205
206 function css.fontspecification(str)
207 return str and lpegmatch(pattern,lower(str))
208 end
209
210
211
212 function css.style(str)
213 if str and str ~= "" then
214 str = lower(str)
215 if str == "italic" then
216 return "italic"
217 elseif str == "slanted" or str == "oblique" then
218 return "slanted"
219 end
220 end
221 return "normal"
222 end
223
224 function css.variant(str)
225 if str and str ~= "" then
226 str = lower(str)
227 if str == "small-caps" or str == "caps" or str == "smallcaps" then
228 return "caps"
229 end
230 end
231 return "normal"
232 end
233
234 function css.weight(str)
235 if str and str ~= "" then
236 str = lower(str)
237 if str == "bold" then
238 return "bold"
239 end
240 end
241 return "normal"
242 end
243
244 function css.family(str)
245 if str and str ~= "" then
246 str = lower(str)
247 if str == "mono" or str == "type" or str == "monospace" then
248 return "mono"
249 elseif str == "sansserif" or str == "sans" then
250 return "sans"
251 elseif str == "serif" then
252 return "serif"
253 else
254
255 return lpegmatch(p_unquoted,str) or str
256 end
257 end
258 end
259
260 function css.size(str,factors, pct)
261 local size, unit
262 if type(str) == "table" then
263 size, unit = str[1], str[2]
264 elseif str and str ~= "" then
265 size, unit = lpegmatch(p_size,lower(str))
266 end
267 if size and unit then
268 if unit == "%" and pct then
269 return size * pct
270 elseif factors then
271 return (factors[unit] or 1) * size
272 else
273 return size, unit
274 end
275 end
276 end
277
278 function css.colorspecification(str)
279 if str and str ~= "" then
280 local c = attributes.colors.values[tonumber(str)]
281 if c then
282 return format("rgb(%s%%,%s%%,%s%%)",c[3]*100,c[4]*100,c[5]*100)
283 end
284 end
285 end
286
287end
288
289
290
291
292
293
294
295local function s_element_a(list,collected,c,negate,str,dummy,dummy,n)
296 local all = str == "*"
297 for l=1,#list do
298 local ll = list[l]
299 local dt = ll.dt
300 if dt then
301 local ok = all or ll.tg == str
302 if negate then
303 ok = not ok
304 end
305 if ok then
306 c = c + 1
307 collected[c] = ll
308 end
309 if (not n or n > 1) and dt then
310 c = s_element_a(dt,collected,c,negate,str,dummy,dummy,n and n+1 or 1)
311 end
312 end
313 end
314 return c
315end
316
317
318
319local function s_element_b(list,collected,c,negate,str)
320 local all = str == "*"
321 for l=1,#list do
322 local ll = list[l]
323 local pp = ll.__p__
324 if pp then
325 local dd = pp.dt
326 if dd then
327 local ni = ll.ni
328 local d = dd[ni+1]
329 local dt = d and d.dt
330 if not dt then
331 d = dd[ni+2]
332 dt = d and d.dt
333 end
334 if dt then
335 local ok = all or d.tg == str
336 if negate then
337 ok = not ok
338 end
339 if ok then
340 c = c + 1
341 collected[c] = d
342 end
343 end
344 end
345 end
346 end
347 return c
348end
349
350
351
352local function s_element_c(list,collected,c,negate,str)
353 local all = str == "*"
354 for l=1,#list do
355 local ll = list[l]
356 local pp = ll.__p__
357 if pp then
358 local dt = pp.dt
359 if dt then
360 local ni = ll.ni
361 for i=ni+1,#dt do
362 local d = dt[i]
363 local dt = d.dt
364 if dt then
365 local ok = all or d.tg == str
366 if negate then
367 ok = not ok
368 end
369 if ok then
370 c = c + 1
371 collected[c] = d
372 end
373 end
374 end
375 end
376 end
377 end
378 return c
379end
380
381
382
383
384local function s_element_d(list,collected,c,negate,str)
385 if str == "*" then
386 if not negate then
387 for l=1,#list do
388 local ll = list[l]
389 local dt = ll.dt
390 if dt then
391 if not ll.special then
392 c = c + 1
393 collected[c] = ll
394 end
395 c = s_element_d(dt,collected,c,negate,str)
396 end
397 end
398 end
399 else
400 for l=1,#list do
401 local ll = list[l]
402 local dt = ll.dt
403 if dt then
404 if not ll.special then
405 local ok = ll.tg == str
406 if negate then
407 ok = not ok
408 end
409 if ok then
410 c = c + 1
411 collected[c] = ll
412 end
413 end
414 c = s_element_d(dt,collected,c,negate,str)
415 end
416 end
417 end
418 return c
419end
420
421
422
423
424
425
426
427
428
429
430
431local function s_attribute(list,collected,c,negate,str,what,value)
432 for l=1,#list do
433 local ll = list[l]
434 local dt = ll.dt
435 if dt then
436 local at = ll.at
437 if at then
438 local v = at[str]
439 local ok = negate
440 if v then
441 if not what then
442 ok = not negate
443 elseif what == 1 then
444 if v == value then
445 ok = not negate
446 end
447 elseif what == 2 then
448
449 if find(v,value) then
450 ok = not negate
451 end
452 elseif what == 3 then
453
454 if find(v," ",1,true) then
455 for s in gmatch(v,"[^ ]+") do
456 if s == value then
457 ok = not negate
458 break
459 end
460 end
461 elseif v == value then
462 ok = not negate
463 end
464 end
465 end
466 if ok then
467 c = c + 1
468 collected[c] = ll
469 end
470 end
471 c = s_attribute(dt,collected,c,negate,str,what,value)
472 end
473 end
474 return c
475end
476
477
478
479
480
481
482local function filter_down(collected,c,negate,dt,a,b)
483 local t = { }
484 local n = 0
485 for i=1,#dt do
486 local d = dt[i]
487 if type(d) == "table" then
488 n = n + 1
489 t[n] = i
490 end
491 end
492 if n == 0 then
493 return 0
494 end
495 local m = a
496 while true do
497 if m > n then
498 break
499 end
500 if m > 0 then
501 t[m] = -t[m]
502 end
503 m = m + b
504 end
505 if negate then
506 for i=n,1-1 do
507 local ti = t[i]
508 if ti > 0 then
509 local di = dt[ti]
510 c = c + 1
511 collected[c] = di
512 end
513 end
514 else
515 for i=n,1,-1 do
516 local ti = t[i]
517 if ti < 0 then
518 ti = - ti
519 local di = dt[ti]
520 c = c + 1
521 collected[c] = di
522 end
523 end
524 end
525 return c
526end
527
528local function filter_up(collected,c,negate,dt,a,b)
529 local t = { }
530 local n = 0
531 for i=1,#dt do
532 local d = dt[i]
533 if type(d) == "table" then
534 n = n + 1
535 t[n] = i
536 end
537 end
538 if n == 0 then
539 return 0
540 end
541 if not b then
542 b = 0
543 end
544 local m = n - a
545 while true do
546 if m < 1 then
547 break
548 end
549 if m < n then
550 t[m] = -t[m]
551 end
552 m = m - b
553 end
554 if negate then
555 for i=1,n do
556 local ti = t[i]
557 if ti > 0 then
558 local di = dt[ti]
559 c = c + 1
560 collected[c] = di
561 end
562 end
563 else
564 for i=1,n do
565 local ti = t[i]
566 if ti < 0 then
567 ti = - ti
568 local di = dt[ti]
569 c = c + 1
570 collected[c] = di
571 end
572 end
573 end
574 return c
575end
576
577local function just(collected,c,negate,dt,a,start,stop,step)
578 local m = 0
579 for i=start,stop,step do
580 local d = dt[i]
581 if type(d) == "table" then
582 m = m + 1
583 if negate then
584 if a ~= m then
585 c = c + 1
586 collected[c] = d
587 end
588 else
589 if a == m then
590 c = c + 1
591 collected[c] = d
592 break
593 end
594 end
595 end
596 end
597 return c
598end
599
600local function s_nth_child(list,collected,c,negate,a,n,b)
601 if n == "n" then
602 for l=1,#list do
603 local ll = list[l]
604 local dt = ll.dt
605 if dt then
606 c = filter_up(collected,c,negate,dt,a,b)
607 end
608 end
609 else
610 for l=1,#list do
611 local ll = list[l]
612 local dt = ll.dt
613 if dt then
614 c = just(collected,c,negate,dt,a,1,#dt,1)
615 end
616 end
617 end
618 return c
619end
620
621local function s_nth_last_child(list,collected,c,negate,a,n,b)
622 if n == "n" then
623 for l=1,#list do
624 local ll = list[l]
625 local dt = ll.dt
626 if dt then
627 c = filter_down(collected,c,negate,dt,a,b)
628 end
629 end
630 else
631 for l=1,#list do
632 local ll = list[l]
633 local dt = ll.dt
634 if dt then
635 c = just(collected,c,negate,dt,a,#dt,1,-1)
636 end
637 end
638 end
639 return c
640end
641
642
643
644
645
646
647local function s_nth_of_type(list,collected,c,negate,a,n,b)
648 if n == "n" then
649 return filter_up(collected,c,negate,list,a,b)
650 else
651 return just(collected,c,negate,list,a,1,#list,1)
652 end
653end
654
655local function s_nth_last_of_type(list,collected,c,negate,a,n,b)
656 if n == "n" then
657 return filter_down(collected,c,negate,list,a,b)
658 else
659 return just(collected,c,negate,list,a,#list,1,-1)
660 end
661end
662
663
664
665local function s_only_of_type(list,collected,c,negate)
666 if negate then
667 for i=1,#list do
668 c = c + 1
669 collected[c] = list[i]
670 end
671 else
672 if #list == 1 then
673 c = c + 1
674 collected[c] = list[1]
675 end
676 end
677 return c
678end
679
680
681
682local function s_only_child(list,collected,c,negate)
683 if negate then
684 for l=1,#list do
685 local ll = list[l]
686 local dt = ll.dt
687 if dt then
688 for i=1,#dt do
689 local di = dt[i]
690 if type(di) == "table" then
691 c = c + 1
692 collected[c] = di
693 end
694 end
695 end
696 end
697 else
698 for l=1,#list do
699 local ll = list[l]
700 local dt = ll.dt
701 if dt and #dt == 1 then
702 local di = dt[1]
703 if type(di) == "table" then
704 c = c + 1
705 collected[c] = di
706 end
707 end
708 end
709 end
710 return c
711end
712
713
714
715local function s_empty(list,collected,c,negate)
716 for l=1,#list do
717 local ll = list[l]
718 local dt = ll.dt
719 if dt then
720 local dn = #dt
721 local ok = dn == 0
722 if not ok and dn == 1 then
723 local d = dt[1]
724 if type(d) == "string" and is_empty(d) then
725 ok = true
726 end
727 end
728 if negate then
729 ok = not ok
730 end
731 if ok then
732 c = c + 1
733 collected[c] = ll
734 end
735 end
736 end
737 return c
738end
739
740
741
742local function s_root(list,collected,c,negate)
743 for l=1,#list do
744 local ll = list[l]
745 if type(ll) == "table" then
746 local r = xml.root(ll)
747 if r then
748 if r.special and r.tg == "@rt@" then
749 r = r.dt[r.ri]
750 end
751 c = c + 1
752 collected[c] = r
753 break
754 end
755 end
756 end
757 return c
758end
759
760local P, R, S, C, Cs, Ct, Cc, Carg, lpegmatch = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.Cc, lpeg.Carg, lpeg.match
761
762local p_number = lpegpatterns.integer / tonumber
763
764local p_key = C((R("az","AZ","09") + S("_-"))^1)
765local p_left = S("#.[],:()")
766local p_right = S("#.[],:() ")
767local p_tag = C((1-p_left) * (1-p_right)^0)
768local p_value = C((1-P("]"))^0)
769local p_unquoted = (P('"')/"") * C((1-P('"'))^0) * (P('"')/"")
770 + (1-P("]"))^1
771local p_element = Ct( (
772 P(">") * skipspace * Cc(s_element_a) +
773 P("+") * skipspace * Cc(s_element_b) +
774 P("~") * skipspace * Cc(s_element_c) +
775 Cc(s_element_d)
776 ) * p_tag )
777local p_attribute = P("[") * Ct(Cc(s_attribute) * p_key * (
778 P("=" ) * Cc(1) * Cs( p_unquoted)
779 + P("^=") * Cc(2) * Cs(Cc("^") * (p_unquoted / topattern))
780 + P("$=") * Cc(2) * Cs( p_unquoted / topattern * Cc("$"))
781 + P("*=") * Cc(2) * Cs( p_unquoted / topattern)
782 + P("~=") * Cc(3) * Cs( p_unquoted)
783 )^0 * P("]"))
784
785local p_separator = skipspace * P(",") * skipspace
786
787local p_formula = skipspace * P("(")
788 * skipspace
789 * (
790 p_number * skipspace * (C("n") * skipspace * (p_number + Cc(0)))^-1
791 + P("even") * Cc(0) * Cc("n") * Cc(2)
792 + P("odd") * Cc(-1) * Cc("n") * Cc(2)
793 )
794 * skipspace
795 * P(")")
796
797local p_step = P(".") * Ct(Cc(s_attribute) * Cc("class") * Cc(3) * p_tag)
798 + P("#") * Ct(Cc(s_attribute) * Cc("id") * Cc(1) * p_tag)
799 + p_attribute
800 + p_element
801 + P(":nth-child") * Ct(Cc(s_nth_child) * p_formula)
802 + P(":nth-last-child") * Ct(Cc(s_nth_last_child) * p_formula)
803 + P(":first-child") * Ct(Cc(s_nth_child) * Cc(1))
804 + P(":last-child") * Ct(Cc(s_nth_last_child) * Cc(1))
805 + P(":only-child") * Ct(Cc(s_only_child) )
806 + P(":nth-of-type") * Ct(Cc(s_nth_of_type) * p_formula)
807 + P(":nth-last-of-type") * Ct(Cc(s_nth_last_of_type) * p_formula)
808 + P(":first-of-type") * Ct(Cc(s_nth_of_type) * Cc(1))
809 + P(":last-of-type") * Ct(Cc(s_nth_last_of_type) * Cc(1))
810 + P(":only-of-type") * Ct(Cc(s_only_of_type) )
811 + P(":empty") * Ct(Cc(s_empty) )
812 + P(":root") * Ct(Cc(s_root) )
813
814local p_not = P(":not") * Cc(true) * skipspace * P("(") * skipspace * p_step * skipspace * P(")")
815local p_yes = Cc(false) * skipspace * p_step
816
817local p_stepper = Ct((skipspace * (p_not+p_yes))^1)
818local p_steps = Ct((p_stepper * p_separator^0)^1) * skipspace * (P(-1) + function() report_css("recovering from error") end)
819
820local cache = setmetatableindex(function(t,k)
821 local v = lpegmatch(p_steps,k) or false
822 t[k] = v
823 return v
824end)
825
826local function selector(root,s)
827
828 local steps = cache[s]
829 if steps then
830 local done = { }
831 local collected = { }
832 local nofcollected = 0
833 local nofsteps = #steps
834 for i=1,nofsteps do
835 local step = steps[i]
836 local n = #step
837 if n > 0 then
838 local r = root
839 local m = 0
840 local c = { }
841 for i=1,n,2 do
842 local s = step[i+1]
843 m = s[1](r,c,0,step[i],s[2],s[3],s[4])
844 if m == 0 then
845 break
846 else
847 r = c
848 c = { }
849 end
850 end
851 if m > 0 then
852 if nofsteps > 1 then
853 for i=1,m do
854 local ri = r[i]
855 if done[ri] then
856
857
858
859 else
860 nofcollected = nofcollected + 1
861 collected[nofcollected] = ri
862 done[ri] = true
863 end
864 end
865 else
866 return r
867 end
868 end
869 end
870 end
871 if nofcollected > 1 then
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890 local n = 0
891 local function traverse(dt)
892 for i=1,#dt do
893 local e = dt[i]
894 if done[e] then
895 n = n + 1
896 done[e] = n
897 if n == nofcollected then
898 return
899 end
900 end
901 local d = e.dt
902 if d then
903 traverse(d)
904 if n == nofcollected then
905 return
906 end
907 end
908 end
909 end
910 local r = root[1]
911 if done[r] then
912 n = n + 1
913 done[r] = n
914 end
915 traverse(r.dt)
916
917 sort(collected,function(a,b) return done[a] < done[b] end)
918 end
919 return collected
920 else
921 return { }
922 end
923end
924
925xml.applyselector= selector
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994function css.applyselector(x,str)
995
996 return applyselector({ x },str)
997end
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042local ctx_sprint = context.sprint
1043local ctx_xmlvalue = context.xmlvalue
1044
1045local colon = P(":")
1046local semicolon = P(";")
1047local eos = P(-1)
1048local somevalue = (1 - (skipspace * (semicolon + eos)))^1
1049local someaction = skipspace * colon * skipspace * (somevalue/ctx_sprint)
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060local patterns = setmetatableindex(function(t,k)
1061 local v = P(k * someaction + 1)^0
1062 t[k] = v
1063 return v
1064end)
1065
1066function css.stylevalue(root,name)
1067 local list = getid(root).at.style
1068 if list then
1069 lpegmatch(patterns[name],list)
1070 end
1071end
1072
1073local somevalue = (1 - whitespace - semicolon - eos)^1
1074local someaction = skipspace * colon * (skipspace * Carg(1) * C(somevalue)/function(m,s)
1075 ctx_xmlvalue(m,s,"")
1076end)^1
1077
1078local patterns= setmetatableindex(function(t,k)
1079 local v = P(k * someaction + 1)^0
1080 t[k] = v
1081 return v
1082end)
1083
1084function css.mappedstylevalue(root,map,name)
1085 local list = getid(root).at.style
1086 if list then
1087 lpegmatch(patterns[name],list,1,map)
1088 end
1089end
1090
1091
1092
1093interfaces.implement {
1094 name = "xmlcssstylevalue",
1095 public = true,
1096 actions = css.stylevalue,
1097 arguments = "2 strings",
1098}
1099
1100interfaces.implement {
1101 name = "xmlcssmappedstylevalue",
1102 public = true,
1103 actions = css.mappedstylevalue,
1104 arguments = "3 strings",
1105}
1106
1107
1108
1109local containsws = string.containsws
1110local classsplitter = lpeg.tsplitat(whitespace^1)
1111
1112function xml.functions.classes(e,class)
1113 if class then
1114 local at = e.at
1115 local data = at[class] or at.class
1116 if data then
1117 return lpegmatch(classsplitter,data) or { }
1118 end
1119 end
1120 return { }
1121end
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141function xml.functions.hasclass(e,class,name,more,...)
1142 if class and name then
1143 local at = e.at
1144 local data = at[class] or at.class
1145 if not data or data == "" then
1146 return false
1147 end
1148 if data == name or data == more then
1149 return true
1150 end
1151 if containsws(data,name) then
1152 return true
1153 end
1154 if not more then
1155 return false
1156 end
1157 if containsws(data,more) then
1158 return true
1159 end
1160 for i=1,select("#",...) do
1161 if containsws(data,select(i,...)) then
1162 return true
1163 end
1164 end
1165 end
1166 return false
1167end
1168
1169function xml.expressions.hasclass(data,name,more,...)
1170 if data then
1171 if not data or data == "" then
1172 return false
1173 end
1174 if data == name or data == more then
1175 return true
1176 end
1177 if containsws(data,name) then
1178 return true
1179 end
1180 if not more then
1181 return false
1182 end
1183 if containsws(data,more) then
1184 return true
1185 end
1186 for i=1,select("#",...) do
1187 if containsws(data,select(i,...)) then
1188 return true
1189 end
1190 end
1191 end
1192 return false
1193end
1194 |