%D \module
%D   [       file=lxml-ini,
%D        version=2007.08.17,
%D          title=\CONTEXT\ \XML\ Support,
%D       subtitle=Initialization,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA / Hans Hagen \& Ton Otten}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\writestatus{loading}{ConTeXt XML Support / Initialization}

\registerctxluafile{lxml-tab}{1.001}
\registerctxluafile{lxml-pth}{1.001}
\registerctxluafile{lxml-ent}{1.001}
\registerctxluafile{lxml-mis}{1.001}
\registerctxluafile{lxml-ini}{1.001}

\unprotect

\def\xmlmain               #1{\ctxlua{lxml.main("#1")}}
\def\xmlall              #1#2{\ctxlua{lxml.all("#1","#2")}}
\def\xmlatt              #1#2{\ctxlua{lxml.attribute("#1","/","#2")}}
\def\xmlattdef         #1#2#3{\ctxlua{lxml.attribute("#1","/","#2","#3")}}
\def\xmlattribute      #1#2#3{\ctxlua{lxml.attribute("#1","#2","#3")}}
\def\xmlattributedef #1#2#3#4{\ctxlua{lxml.attribute("#1","#2","#3","#4")}}
\def\xmlcommand        #1#2#3{\ctxlua{lxml.command("#1","#2","#3")}}
\def\xmlconcat         #1#2#3{\ctxlua{lxml.concat("#1","#2",[[\detokenize{#3}]])}}
\def\xmlconcatrange#1#2#3#4#5{\ctxlua{lxml.concatrange("#1","#2","#3","#4",[[\detokenize{#5}]])}}
\def\xmlcount            #1#2{\ctxlua{lxml.count("#1","#2")}}
\def\xmldelete           #1#2{\ctxlua{xml.delete(lxml.id("#1"),"#2")}}
\def\xmldirectives         #1{\ctxlua{lxml.directives.setup("#1")}}
\def\xmldirectivesbefore   #1{\ctxlua{lxml.directives.before("#1")}}
\def\xmldirectivesafter    #1{\ctxlua{lxml.directives.after("#1")}}
\def\xmlfilter           #1#2{\ctxlua{lxml.filter("#1",\!!bs#2\!!es)}}
\def\xmlfirst            #1#2{\ctxlua{lxml.first("#1","#2")}}
\def\xmlflush              #1{\ctxlua{lxml.flush("#1")}}
% \def\xmlcontent            #1{\ctxlua{lxml.content("#1")}}
% \def\xmlstrip              #1{\ctxlua{lxml.strip("#1")}}
% \def\xmlflushstripped      #1{\ctxlua{lxml.strip("#1",true)}}
\def\xmldirect             #1{\ctxlua{lxml.direct("#1")}} % in loops, not dt but root
\def\xmlidx            #1#2#3{\ctxlua{lxml.idx("#1","#2",\number#3)}}
\def\xmlinclude        #1#2#3{\ctxlua{lxml.include("#1","#2","#3",true)}}
\def\xmlindex          #1#2#3{\ctxlua{lxml.index("#1","#2",\number#3)}}
\def\xmlinfo               #1{\hbox{\ttxx[\ctxlua{lxml.info("#1")}]}}
\def\xmlshow               #1{\startpacked\ttx\xmlverbatim{#1}\stoppacked}
\def\xmllast             #1#2{\ctxlua{lxml.last("#1","#2")}}
\def\xmlload             #1#2{\ctxlua{lxml.load("#1","#2")}}
\def\xmlloadbuffer       #1#2{\ctxlua{lxml.loadbuffer("#1","#2")}}
\def\xmlloaddata         #1#2{\ctxlua{lxml.loaddata("#1",\!!bs#2\!!es)}}
\def\xmlloadregistered   #1#2{\ctxlua{lxml.loadregistered("#1")}}
\def\xmlloaddirectives     #1{\ctxlua{lxml.directives.load("#1")}}
\def\xmlname               #1{\ctxlua{lxml.name("#1")}}
\def\xmlnamespace          #1{\ctxlua{lxml.namespace("#1")}}
\def\xmlnonspace         #1#2{\ctxlua{lxml.nonspace("#1","#2")}}
\def\xmlraw              #1#2{\ctxlua{lxml.raw("#1","#2")}}
\def\xmlsnippet          #1#2{\ctxlua{lxml.snippet("#1",#2)}}
\def\xmlelement          #1#2{\ctxlua{lxml.element("#1",#2)}}
\def\xmlnofelements        #1{\ctxlua{lxml.nofelements("#1")}}
\def\xmlregisterns       #1#2{\ctxlua{xml.registerns("#1","#2")}}                    % document
\def\xmlremapname    #1#2#3#4{\ctxlua{xml.remapname(lxml.id("#1"),"#2","#3","#4")}}  % element
\def\xmlremapnamespace #1#2#3{\ctxlua{xml.rename_space(lxml.id("#1"),"#2","#3")}}    % document
\def\xmlchecknamespace #1#2#3{\ctxlua{xml.check_namespace(lxml.id("#1"),"#2","#3")}} % element
\def\xmlsetfunction    #1#2#3{\ctxlua{lxml.setaction("#1","#2",#3)}}
\def\xmlsetsetup       #1#2#3{\ctxlua{lxml.setsetup("#1","#2","#3")}}
\def\xmlstrip            #1#2{\ctxlua{lxml.strip("#1","#2")}}
\def\xmlstripped         #1#2{\ctxlua{lxml.stripped("#1","#2")}}
\def\xmlstripnolines     #1#2{\ctxlua{lxml.strip("#1","#2",true)}}
\def\xmlstrippednolines  #1#2{\ctxlua{lxml.stripped("#1","#2",true)}}
\def\xmltag                #1{\ctxlua{lxml.tag("#1")}}
\def\xmltext             #1#2{\ctxlua{lxml.text("#1","#2")}}
\def\xmltags             #1#2{\ctxlua{lxml.tags("#1","#2")}}
\def\xmlutfize             #1{\ctxlua{lxml.utfize("#1")}}
\def\xmlverbatim           #1{\ctxlua{lxml.verbatim("#1")}}
\def\xmldisplayverbatim    #1{\ctxlua{lxml.displayverbatim("#1")}}
\def\xmlinlineverbatim     #1{\ctxlua{lxml.inlineverbatim("#1")}}

%def\xmldoifelse         #1#2{\ctxlua{cs.testcase(xml.found(lxml.id("#1"),"#2",false))}}
%def\xmldoifelsetext     #1#2{\ctxlua{cs.testcase(xml.found(lxml.id("#1"),"#2",true ))}}

% kind of special:

\def\xmlstartraw{\ctxlua{lxml.startraw()}}
\def\xmlstopraw {\ctxlua{lxml.stopraw()}}

% todo: \xmldoifelseattribute

\def\xmldoif             #1#2{\ctxlua{lxml.doif("#1","#2")}}
\def\xmldoifnot          #1#2{\ctxlua{lxml.doifnot("#1","#2")}}
\def\xmldoifelse         #1#2{\ctxlua{lxml.doifelse("#1","#2")}}
\def\xmldoiftext         #1#2{\ctxlua{lxml.doiftext("#1","#2")}}
\def\xmldoifnottext      #1#2{\ctxlua{lxml.doifnottext("#1","#2")}}
\def\xmldoifelsetext     #1#2{\ctxlua{lxml.doifelsetext("#1","#2")}}
\def\xmldoifelseempty    #1#2{\ctxlua{lxml.doifelseempty("#1","#2")}} % #2, "*" or "" == self not yet implemented
\def\xmldoifelseselfempty  #1{\ctxlua{lxml.doifelseempty("#1")}}

\def\xmldefaulttotext      #1{\ifcase\xmlprocessingmode\or\or   \ctxlua{lxml.set_command_to_text("#1")}\fi}
\def\xmldefaulttonone      #1{\ifcase\xmlprocessingmode\or\or\or\ctxlua{lxml.set_command_to_none("#1")}\fi}

% \startxmlsetups xml:include
%     \xmlinclude{main}{include}{filename|href}
% \stopxmlsetups
%
% \xmlprependsetup{xml:include}

\let\xmlgrab\xmlsetsetup

\def\xmlsetup#1#2%              % we pass the reference so that we have access to the frozen number
  {\def\xmlself{#1}%            % by using #1 inside a setup
   \setupwithargument{#2}{#1}}

\newtoks \registeredxmlsetups

% todo: 1:xml:whatever alwas before 3:xml:something

\def\xmlprependsetup           #1{\ctxlua{lxml.installsetup(1,"*","#1")}}
\def\xmlappendsetup            #1{\ctxlua{lxml.installsetup(2,"*","#1")}}
\def\xmlbeforesetup          #1#2{\ctxlua{lxml.installsetup(3,"*","#1","#2"))}}
\def\xmlaftersetup           #1#2{\ctxlua{lxml.installsetup(4,"*","#1","#2"))}}

\def\xmlprependdocumentsetup #1#2{\ctxlua{lxml.installsetup(1,"#1","#2")}}
\def\xmlappenddocumentsetup  #1#2{\ctxlua{lxml.installsetup(2,"#1","#2")}}
\def\xmlbeforedocumentsetup#1#2#3{\ctxlua{lxml.installsetup(3,"#1","#2","#3"))}}
\def\xmlafterdocumentsetup #1#2#3{\ctxlua{lxml.installsetup(4,"#1","#2","#3"))}}

\def\xmlremovesetup            #1{\ctxlua{lxml.removesetup("*","#1")}}
\def\xmlremovedocumentsetup  #1#2{\ctxlua{lxml.removesetup("#1","#2")}}

\def\xmlflushdocumentsetups    #1{\ctxlua{lxml.flushsetups("*","#1")}}
\def\xmlresetdocumentsetups    #1{\ctxlua{lxml.resetsetups("#1")}}

\let\xmlregistersetup        \xmlappendsetup
\let\xmlregisterdocumentsetup\xmlappenddocumentsetup

\def\xmldocument{main}

\def\xmlregisteredsetups
  {\xmlstarttiming
   \xmlflushsetups
   \xmldefaulttotext\xmldocument % after include
   \xmlstoptiming}

\def\xmlregistereddocumentsetups#1%
  {\xmlstarttiming
   % todo: test for duplicates !
   \xmlflushdocumentsetups{#1}%
   \xmldefaulttotext{#1}% after include
   \xmlstoptiming}

\def\xmlstarttiming{\ctxlua{statistics.starttiming(lxml)}}
\def\xmlstoptiming {\ctxlua{statistics.stoptiming (lxml)}}

\def\doxmlprocess#1#2#3#4%
  {\begingroup
   \edef\xmldocument{#2}% #2 can be \xmldocument and set as such
   #1{#2}{#3}%
   \ifcase\xmlprocessingmode
     \enableXML
   \else
     \setcatcodetable\notcatcodes
   \fi
   \doifelsenothing{#4}
     {\directsetup{xml:process}}
     {\directsetup{#4}}%
   \endgroup}

\def\xmlprocessfile      {\doxmlprocess\xmlload}
\def\xmlprocessdata      {\doxmlprocess\xmlloaddata}
\def\xmlprocessbuffer    {\doxmlprocess\xmlloadbuffer}
\def\xmlprocessregistered{\doxmlprocess\xmlloadregistered}
\let\xmlprocess           \xmlprocessfile

% beware: \xmlmain takes the real root, so also processing
% instructions preceding the root element; well, in some
% sense that is the root

\startsetups xml:process
    \xmlregistereddocumentsetups\xmldocument
    \xmlmain\xmldocument
\stopsetups

\long\def\xmlloop#1#2#3%
  {\def\xmli##1##2{\xmlidx{#1}{#2/##1}{##2}}%
   \dorecurse{\xmlcount{#1}{#2}}{#3}}

\long\def\xmlconnect#1#2#3% inefficient
  {\scratchcounter\xmlcount{#1}{#2}\relax
   \ifcase\scratchcounter \or
       \xmlall{#1}{#2}%
   \else
       \dorecurse \scratchcounter {
           \ifnum\recurselevel>\plusone#3\fi
           \xmlidx{#1}{#2}{\recurselevel}
       }
   \fi}

\def\xmlcdataobeyedline {\obeyedline}
\def\xmlcdataobeyedspace{\strut\obeyedspace}
\def\xmlcdatabefore     {\bgroup\tt}
\def\xmlcdataafter      {\egroup}

% verbatim (dodo:pre/post whitespace, maybe splot verbatim and
% cdata commands), experimental:
%
% \xmlsetfunction{main}{verbatim}{lxml.displayverbatim}
% \xmlsetfunction{main}{verb}    {lxml.inlineverbatim}

% \def\startxmldisplayverbatim[#1]{}
% \def\stopxmldisplayverbatim     {}
% \def\startxmlinlineverbatim [#1]{}
% \def\stopxmlinlineverbatim      {}

% we use an xml: namespace so one has to define a suitable verbatim, say
%
% \definetyping[xml:verbatim][typing]
%
% this is experimental!

\def\startxmldisplayverbatim[#1]%
  {\startpacked % \begingroup
   \let\currenttypingclass\??tp
   \edef\currenttyping{xml:#1}%
   \def\stopxmldisplayverbatim
     {\endofverbatimlines
      \stoppacked} % \endgroup
   \mkinitializeverbatim
   \beginofverbatimlines}

\def\startxmlinlineverbatim[#1]%
  {\begingroup
   \let\currenttypingclass\??ty
   \edef\currenttyping{xml:#1}%
   \let\stopxmldisplayverbatim\endgroup
   \mkinitializeverbatim}

% will move but is developed for xml

\newtoks \collectingtoks

\def\startcollect#1\stopcollect
  {\collectingtoks\@EA{\the\collectingtoks#1}}

\def\startexpandedcollect#1\stopexpandedcollect
  {\expanded{\collectingtoks{\the\collectingtoks#1}}}

\def\startcollecting{\collectingtoks\emptytoks}
\def\stopcollecting {\the\collectingtoks}

\def\inlinemessage #1{\dontleavehmode{\tttf#1}}
\def\displaymessage#1{\blank\inlinemessage{#1}\blank}

\def\xmltraceentities
  {\ctxlua{xml.set_text_cleanup(lxml.trace_text_entities)}%
   \appendtoks\ctxlua{lxml.show_text_entities()}\to\everygoodbye}

% processing instructions

\def\xmlcontextdirective#1% kind class key value
  {\executeifdefined{xml#1directive}\gobblethreearguments}

% setting up xml:
%
% \setupxml[\c!method=mkii]                    % mixed mkiv and mkii
% \setupxml[\c!method=mkiv,\c!default=]        % mkiv only
% \setupxml[\c!method=mkiv,\c!default=\v!none] % mkiv only, undefined -> hidden
% \setupxml[\c!method=mkiv,\c!default=\v!text] % mkiv only, undefined -> text

% \def\xmlctxdirective#1#2#3{\doif{#1}{clue}{\doif{#2}{page}}{\page[#3]}}

\chardef\xmlprocessingmode=0 % 0=mixed, 1=mkivonly, 2=mkivonly-default-text, 3=mkivonly-default-none

% \setupxml[method=mkiv,strip=yes,entities=yes,default=text]

\newtoks\everysetupxml

\def\setupxml[#1]{\getparameters[\??xm][#1]\the\everysetupxml}

\def\c!entities{entities} % to be internationalized
\def\s!mkiv    {mkiv}
\def\s!mkii    {mkii}

% entities

\newif\ifautoXMLentities

\def\xmlkeepentities{\ctxlua{lxml.reset_entityhandler()}}
\def\xmlmkiientities{\ctxlua{lxml.set_mkii_entityhandler()}\autoXMLentitiestrue}
\def\xmlmkiventities{\ctxlua{lxml.set_mkiv_entityhandler()}}

\let\xmlresolveentities\xmlmkiventities % will become \xmlmkiventities

\letvalue{\??xm:1:\s!mkii  }\zerocount
\letvalue{\??xm:1:\s!mkiv  }\plusone

\letvalue{\??xm:2:\v!none  }\plusone
\letvalue{\??xm:2:\v!text  }\plustwo
\letvalue{\??xm:2:\v!hidden}\plusthree

\letvalue{\??xm:ii:\v!yes }\xmlresolveentities
\letvalue{\??xm:ii:\v!no  }\xmlkeepentities
\letvalue{\??xm:ii:\s!mkii}\xmlmkiientities
\letvalue{\??xm:ii:\s!mkiv}\xmlmkiventities

\letvalue{\??xm:iv:\v!yes }\xmlresolveentities
\letvalue{\??xm:iv:\v!no  }\xmlkeepentities
\letvalue{\??xm:iv:\s!mkii}\xmlmkiventities
\letvalue{\??xm:iv:\s!mkiv}\xmlmkiventities

\appendtoks
    \chardef\xmlprocessingmode\executeifdefined{\??xm:1:\@@xmmethod}\zerocount
    \ifcase\xmlprocessingmode
        % mkii, permits both methods
        \executeifdefined{\??xm:ii:\@@xmentities}\xmlkeepentities
    \or
        % mkiv, mkiv exclusively
        \chardef\xmlprocessingmode\executeifdefined{\??xm:2:\@@xmdefault}\plusone
        \executeifdefined{\??xm:iv:\@@xmentities}\xmlresolveentities
    \else
        % unset
    \fi
    \ifcase\xmlprocessingmode
        \ctxlua{characters.setmkiientities()}%
    \else
        \ctxlua{characters.setmkiventities()}%
    \fi
    \doifelse\@@xmcompress\v!yes % this key may change, maybe compress=yes|no
      {\ctxlua{xml.strip_cm_and_dt=true}}
      {\ctxlua{xml.strip_cm_and_dt=false}}%
\to \everysetupxml

\def\xmlinitialize{\the\everysetupxml}

\newcount\charactersactiveoffset \charactersactiveoffset="10000

\startextendcatcodetable\ctxcatcodes
    \catcode\numexpr\charactersactiveoffset+`<\relax=13
    \catcode\numexpr\charactersactiveoffset+`&\relax=13
    \catcode\numexpr\charactersactiveoffset+`>\relax=13
\stopextendcatcodetable
\startextendcatcodetable\xmlcatcodes
    \catcode\numexpr\charactersactiveoffset+`<\relax=13
    \catcode\numexpr\charactersactiveoffset+`&\relax=13
    \catcode\numexpr\charactersactiveoffset+`>\relax=13
\stopextendcatcodetable

\ctxlua { % entities are remembered in the format
    characters.remapentity("<",characters.active_offset + utf.byte("<"))
    characters.remapentity("&",characters.active_offset + utf.byte("&"))
    characters.remapentity(">",characters.active_offset + utf.byte(">"))
}

\setupxml
  [\c!method=mkii,       % mixed mode
   \c!default=\v!hidden, % ignore elements that are not defined
   \c!compress=\v!no,    % strip comment
   \c!entities=\v!yes]   % replace entities

% \defineXMLenvironment[y]{(}{)}
%
% \startxmlsetups x
%     /\xmlflush{#1}/
% \stopxmlsetups
%
% \startxmlsetups xx
%     \xmlsetsetup{main}{x}{*}
% \stopxmlsetups
%
% \xmlregistersetup{xx}
%
% \startbuffer
% <x>a &lt;&amp;&gt;<y>{b}</y> c</x>
% \stopbuffer
%
% mkii: [\processXMLbuffer]\quad mkiv: [\xmlprocessbuffer{main}{}{}]

% some mkii commands, but mkiv-ified

\def\xmlmapvalue#1#2#3{\setvalue{\??xm:v:#1:#2}{#3}} % keep #3 to grab spaces
\def\xmlvalue   #1#2#3{\executeifdefined{\??xm:v:#1:#2}{#3}}

\let\xmlmapval\xmlmapvalue
\let\xmlval   \xmlvalue

% brrrr, give this at the top of a style that needs to stub mkiv loading

\def\remapXMLtoMKIV
  {\ifx\xmldocument\undefined\def\xmldocument{main}\fi
   \def\processXMLfile          ##1{\xmlprocessfile  \xmldocument{##1}{}}%
   \def\processXMLfilegrouped   ##1{\xmlprocessfile  \xmldocument{##1}{}}% maybe still grouped?
   \def\processXMLbuffer           {\dosingleempty\doprocessXMLbuffer}%
   \def\doprocessXMLbuffer    [##1]{\xmlprocessbuffer\xmldocument{##1}{}}%
   \def\XMLdata                 ##1{\xmlprocessdata  \xmldocument{##1}{}}%
   \def\startXMLdata##1\stopXMLdata{\xmlprocessdata  \xmldocument{##1}{}}}

\let\normalprocessXMLfilegrouped\processXMLfilegrouped

\def\processXMLfileMKIV % one-shot, will become obsolete, personal hack
  {\dosingleempty\doprocessXMLfileMKIV}

\def\doprocessXMLfileMKIV[#1]%
  {\def\processXMLfilegrouped##1%
     {\let\processXMLfilegrouped\normalprocessXMLfilegrouped
      \doifelsenothing{#1}{\xmlprocess{main}{##1}{}}{\xmlprocess{#1}{##1}{}}}}

\protect \endinput
