1<?php
2if (! class_exists('syntax_plugin_lang')) {
3	if (! defined('DOKU_PLUGIN')) {
4		if (! defined('DOKU_INC')) {
5			define('DOKU_INC', realpath(dirname(__FILE__) . '/../../') . '/');
6		} // if
7		define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/');
8	} // if
9	// include parent class
10	require_once(DOKU_PLUGIN . 'syntax.php');
11
12/**
13 * <tt>syntax_plugin_lang.php </tt>- A PHP4 class that implements
14 * a <tt>DokuWiki</tt> plugin to specify an area using a different
15 * language than the remaining document.
16 *
17 * <p>
18 * Markup a section of text to be using a different language,
19 * <tt>lang 2-letter-lang-code</tt>
20 * </p><pre>
21 *	Copyright (C) 2005, 2007 DFG/M.Watermann, D-10247 Berlin, FRG
22 *			All rights reserved
23 *		EMail : &lt;support@mwat.de&gt;
24 * </pre>
25 * <div class="disclaimer">
26 * This program is free software; you can redistribute it and/or modify
27 * it under the terms of the GNU General Public License as published by
28 * the Free Software Foundation; either
29 * <a href="http://www.gnu.org/licenses/gpl.html">version 3</a> of the
30 * License, or (at your option) any later version.<br>
31 * This software is distributed in the hope that it will be useful,
32 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
34 * General Public License for more details.
35 * </div>
36 * @author <a href="mailto:support@mwat.de">Matthias Watermann</a>
37 * @version <tt>$Id: syntax_plugin_lang.php,v 1.4 2007/08/15 12:36:19 matthias Exp $</tt>
38 * @since created 1-Sep-2005
39 */
40class syntax_plugin_lang extends DokuWiki_Syntax_Plugin {
41
42	/**
43	 * @publicsection
44	 */
45	//@{
46
47	/**
48	 * Tell the parser whether the plugin accepts syntax mode
49	 * <tt>$aMode</tt> within its own markup.
50	 *
51	 * @param $aMode String The requested syntaxmode.
52	 * @return Boolean <tt>TRUE</tt> unless <tt>$aMode</tt> is
53	 * <tt>plugin_lang</tt> (which would result in a
54	 * <tt>FALSE</tt> method result).
55	 * @public
56	 * @see getAllowedTypes()
57	 * @static
58	 */
59	function accepts($aMode) {
60		return ('plugin_lang' != $aMode);
61	} // accepts()
62
63	/**
64	 * Connect lookup pattern to lexer.
65	 *
66	 * @param $aMode String The desired rendermode.
67	 * @public
68	 * @see render()
69	 */
70	function connectTo($aMode) {
71		// See http://www.w3.org/TR/html401/struct/dirlang.html#h-8.1.1;
72		// better (specialized) REs are used in 'handle()' method.
73		$this->Lexer->addEntryPattern(
74			'\x3Clang\s+[a-z\-A-Z0-9]{2,})?\s*\x3E\s*(?=(?s).*?\x3C\x2Flang\x3E)',
75			$aMode, 'plugin_lang');
76	} // connectTo()
77
78	/**
79	 * Get an associative array with plugin info.
80	 *
81	 * <p>
82	 * The returned array holds the following fields:
83	 * <dl>
84	 * <dt>author</dt><dd>Author of the plugin</dd>
85	 * <dt>email</dt><dd>Email address to contact the author</dd>
86	 * <dt>date</dt><dd>Last modified date of the plugin in
87	 * <tt>YYYY-MM-DD</tt> format</dd>
88	 * <dt>name</dt><dd>Name of the plugin</dd>
89	 * <dt>desc</dt><dd>Short description of the plugin (Text only)</dd>
90	 * <dt>url</dt><dd>Website with more information on the plugin
91	 * (eg. syntax description)</dd>
92	 * </dl>
93	 * @return Array Information about this plugin class.
94	 * @public
95	 * @static
96	 */
97	function getInfo() {
98		return array(
99			'author' =>	'Matthias Watermann',
100			'email' =>	'support@mwat.de',
101			'date' =>	'2007-08-15',
102			'name' =>	'LANGuage Syntax Plugin',
103			'desc' =>	'Markup a text area using another language',
104			'url' =>	'http://wiki.splitbrain.org/plugin:lang');
105	} // getInfo()
106
107	/**
108	 * Where to sort in?
109	 *
110	 * @return Integer <tt>498</tt> (doesn't really matter).
111	 * @public
112	 * @static
113	 */
114	function getSort() {
115		return 498;
116	} // getSort()
117
118	/**
119	 * Get the type of syntax this plugin defines.
120	 *
121	 * @return String <tt>'formatting'</tt>.
122	 * @public
123	 * @static
124	 */
125	function getType() {
126		return 'formatting';
127	} // getType()
128
129	/**
130	 * Handler to prepare matched data for the rendering process.
131	 *
132	 * <p>
133	 * The <tt>$aState</tt> parameter gives the type of pattern
134	 * which triggered the call to this method:
135	 * </p>
136	 * <dl>
137	 * <dt>DOKU_LEXER_ENTER</dt>
138	 * <dd>a pattern set by <tt>addEntryPattern()</tt></dd>
139	 * <dt>DOKU_LEXER_MATCHED</dt>
140	 * <dd>a pattern set by <tt>addPattern()</tt></dd>
141	 * <dt>DOKU_LEXER_EXIT</dt>
142	 * <dd> a pattern set by <tt>addExitPattern()</tt></dd>
143	 * <dt>DOKU_LEXER_SPECIAL</dt>
144	 * <dd>a pattern set by <tt>addSpecialPattern()</tt></dd>
145	 * <dt>DOKU_LEXER_UNMATCHED</dt>
146	 * <dd>ordinary text encountered within the plugin's syntax mode
147	 * which doesn't match any pattern.</dd>
148	 * </dl>
149	 * @param $aMatch String The text matched by the patterns.
150	 * @param $aState Integer The lexer state for the match.
151	 * @param $aPos Integer The character position of the matched text.
152	 * @param $aHandler Object Reference to the Doku_Handler object.
153	 * @return Array Index <tt>[0]</tt> holds the current
154	 * <tt>$aState</tt>, index <tt>[1]</tt> the match prepared for
155	 * the <tt>render()</tt> method.
156	 * @public
157	 * @see render()
158	 * @static
159	 */
160	function handle($aMatch, $aState, $aPos, &$aHandler) {
161		if (DOKU_LEXER_ENTER == $aState) {
162			$hits = array();
163			// RFC 3066, "2. The Language tag", p. 2f.
164			// Language-Tag = Primary-subtag *( "-" Subtag )
165			if (preg_match('|\s+([a-z]{2,3})\s*>|i', $aMatch, $hits)) {
166				// primary _only_ (most likely to be used)
167				return array($aState, $hits[1]);
168			} // if
169			if (preg_match('|\s+([a-z]{2,3}\-[a-z0-9]{2,})\s*>|i',
170			$aMatch, $hits)) {
171				// primary _and_ subtag
172				return array($aState, $hits[1]);
173			} // if
174			if (preg_match('|\s+([ix]\-[a-z0-9]{2,})\s*>|i', $aMatch, $hits)) {
175				// 1-letter primary _and_ subtag
176				return array($aState, $hits[1]);
177			} // if
178			if (preg_match('|\s+([a-z]{2,3})\-.*\s*>|i', $aMatch, $hits)) {
179				// convenience: accept primary with empty subtag
180				return array($aState, $hits[1]);
181			} // if
182			// invalid language specification
183			return array($aState, FALSE);
184		} // if
185		return array($aState, $aMatch);
186	} // handle()
187
188	/**
189	 * Add exit pattern to lexer.
190	 *
191	 * @public
192	 */
193	function postConnect() {
194		$this->Lexer->addExitPattern('\x3C\x2Flang\x3E', 'plugin_lang');
195	} // postConnect()
196
197	/**
198	 * Handle the actual output creation.
199	 *
200	 * <p>
201	 * The method checks for the given <tt>$aFormat</tt> and returns
202	 * <tt>FALSE</tt> when a format isn't supported. <tt>$aRenderer</tt>
203	 * contains a reference to the renderer object which is currently
204	 * handling the rendering. The contents of <tt>$aData</tt> is the
205	 * return value of the <tt>handle()</tt> method.
206	 * </p>
207	 * @param $aFormat String The output format to generate.
208	 * @param $aRenderer Object A reference to the renderer object.
209	 * @param $aData Array The data created by the <tt>handle()</tt>
210	 * method.
211	 * @return Boolean <tt>TRUE</tt> if rendered successfully, or
212	 * <tt>FALSE</tt> otherwise.
213	 * @public
214	 * @see handle()
215	 *
216	 */
217	function render($aFormat, &$aRenderer, &$aData) {
218		if ('xhtml' != $aFormat) {
219			return FALSE;
220		} // if
221		static $VALID = TRUE;	// flag to notice invalid markup
222		switch ($aData[0]) {
223			case DOKU_LEXER_ENTER:
224				if ($aData[1]) {
225					$aRenderer->doc .= '<span lang="' . $aData[1]
226						. '" xml:lang="' . $aData[1] . '">';
227				} else {
228					$VALID = FALSE;
229				} // if
230				return TRUE;
231			case DOKU_LEXER_UNMATCHED:
232				$aRenderer->doc .= str_replace(array('&','<', '>'),
233					array('&#38;', '&#60;', '&#62;'), $aData[1]);
234				return TRUE;
235			case DOKU_LEXER_EXIT:
236				if ($VALID) {
237					$aRenderer->doc .= '</span>';
238				} else {
239					$VALID = TRUE;
240				} // if
241			default:
242				return TRUE;
243		} // switch
244	} // render()
245
246	//@}
247} // class syntax_plugin_lang
248} // if
249?>
250