Version Description
Download this release
Release Info
Developer | oferwald |
Plugin | Transposh WordPress Translation |
Version | 0.2.0 |
Comparing to | |
See all releases |
Code changes from version 0.1.7 to 0.2.0
- core/parser.php +327 -919
- core/shd/simple_html_dom.php +975 -0
- js/transposh.js +1 -1
- readme.txt +13 -1
- transposh.php +33 -20
- transposh_admin.php +30 -24
- transposh_db.php +2 -2
- transposh_widget.php +2 -2
core/parser.php
CHANGED
@@ -16,928 +16,336 @@
|
|
16 |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
17 |
*/
|
18 |
|
19 |
-
|
20 |
-
* Contains the core functionality of the html parser. I.e. break into translation segments,
|
21 |
-
* fetch translation and update the translated page.
|
22 |
-
* This file should include only general purpose parser functionality while using callbacks
|
23 |
-
* to obtain WorkdPress specific capabilities, e.g. db access.
|
24 |
-
*/
|
25 |
-
|
26 |
-
|
27 |
-
require_once("constants.php");
|
28 |
-
require_once("globals.php");
|
29 |
-
require_once("utils.php");
|
30 |
-
|
31 |
-
//The html page which starts contains the content being translated
|
32 |
-
$page;
|
33 |
-
|
34 |
-
//Marks the current position of the translation process within the page
|
35 |
-
$pos = 0;
|
36 |
-
|
37 |
-
//Used for statistics
|
38 |
-
$phrase_count = 0;
|
39 |
-
$pharses_by_human = 0;
|
40 |
-
$pharses_by_cat = 0;
|
41 |
-
|
42 |
-
//Contains the stack of tag in the current position within the page
|
43 |
-
$tags_list = array();
|
44 |
-
|
45 |
-
//The translated html page
|
46 |
-
$tr_page;
|
47 |
-
|
48 |
-
//Points to the last character that have been copied from the original to the translated page.
|
49 |
-
$tr_mark = 0;
|
50 |
-
|
51 |
-
//Segment identifier within tags (span/img) mainly for use by js code on the client
|
52 |
-
$segment_id = 0;
|
53 |
-
|
54 |
-
//Is current position within the body tag
|
55 |
-
$is_in_body = FALSE;
|
56 |
-
|
57 |
-
//Is current position within the channel tag, i.e. RSS feed
|
58 |
-
$is_in_channel = FALSE;
|
59 |
-
|
60 |
-
/*
|
61 |
-
* Parse the html page into tags, identify translateable string which
|
62 |
-
* will be translated.
|
63 |
-
*/
|
64 |
-
function process_html()
|
65 |
-
{
|
66 |
-
|
67 |
-
|
68 |
-
global $page, $tr_page, $pos, $tags_list, $lang, $phrase_count, $pharses_by_human, $pharses_by_cat;
|
69 |
-
$no_translate = 0;
|
70 |
-
$page_length = strlen($page);
|
71 |
-
|
72 |
-
while($pos < $page_length)
|
73 |
-
{
|
74 |
-
//find beginning of next tag
|
75 |
-
$pos = strpos($page, '<', $pos);
|
76 |
-
if($pos === FALSE)
|
77 |
-
{
|
78 |
-
//
|
79 |
-
break;
|
80 |
-
}
|
81 |
-
$pos++;
|
82 |
-
|
83 |
-
//Get the element identifying this tag
|
84 |
-
$element = get_element();
|
85 |
-
|
86 |
-
|
87 |
-
if(should_skip_element($element))
|
88 |
-
{
|
89 |
-
|
90 |
-
//do nothing
|
91 |
-
}
|
92 |
-
if($element == '![CDATA[')
|
93 |
-
{
|
94 |
-
|
95 |
-
process_cdata_section();
|
96 |
-
}
|
97 |
-
else
|
98 |
-
{
|
99 |
-
//Mark tag start position
|
100 |
-
$tag_start = $pos;
|
101 |
-
$pos = strpos($page, '>', $pos);
|
102 |
-
|
103 |
-
//Mark tag end position
|
104 |
-
$tag_end = $pos;
|
105 |
-
|
106 |
-
if($page[$pos-1] == '/')
|
107 |
-
{
|
108 |
-
//single line tag - no need to update tags list
|
109 |
-
process_tag_init($element, $tag_start, $tag_end);
|
110 |
-
}
|
111 |
-
else if($element[0] != '/')
|
112 |
-
{
|
113 |
-
if(!$no_translate)
|
114 |
-
{
|
115 |
-
process_tag_init($element, $tag_start, $tag_end);
|
116 |
-
}
|
117 |
-
|
118 |
-
$tags_list[] = $element;
|
119 |
-
|
120 |
-
//Look for the no translate class
|
121 |
-
if(stripos($element, NO_TRANSLATE_CLASS) !== FALSE)
|
122 |
-
{
|
123 |
-
$no_translate++;
|
124 |
-
}
|
125 |
-
}
|
126 |
-
else
|
127 |
-
{
|
128 |
-
$popped_element = array_pop($tags_list);
|
129 |
-
if(!$no_translate)
|
130 |
-
{
|
131 |
-
process_tag_termination($element);
|
132 |
-
}
|
133 |
-
|
134 |
-
//Look for the no translate class
|
135 |
-
if(stripos($popped_element, NO_TRANSLATE_CLASS) !== FALSE)
|
136 |
-
{
|
137 |
-
$no_translate--;
|
138 |
-
}
|
139 |
-
}
|
140 |
-
|
141 |
-
$pos++;
|
142 |
-
|
143 |
-
//skip processing while enclosed within a tag marked by no_translate
|
144 |
-
if(!$no_translate)
|
145 |
-
{
|
146 |
-
process_current_tag();
|
147 |
-
}
|
148 |
-
|
149 |
-
}
|
150 |
-
}
|
151 |
-
|
152 |
-
if(strlen($tr_page) > 0)
|
153 |
-
{
|
154 |
-
//Some translation has been taken place. Complete the translated
|
155 |
-
//page up to the full contents of the original page.
|
156 |
-
update_translated_page(strlen($page), -1, "");
|
157 |
-
}
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
}
|
162 |
-
|
163 |
-
/*
|
164 |
-
* Determine if the specified element should be skipped. If so the position
|
165 |
-
* is moved past end of tag.
|
166 |
-
* Return TRUE if element is skipped otherwise FALSE.
|
167 |
-
*/
|
168 |
-
function should_skip_element(&$element)
|
169 |
-
{
|
170 |
-
global $page, $pos;
|
171 |
-
$rc = TRUE;
|
172 |
-
|
173 |
-
if(strncmp($element, "!DOCTYPE", 8) == 0)
|
174 |
-
{
|
175 |
-
$pos = strpos($page, '>', $pos);
|
176 |
-
}
|
177 |
-
else if(strncmp($element, "!--", 3) == 0)
|
178 |
-
{
|
179 |
-
// the -3 here is for elements like <!--hello-->
|
180 |
-
$pos = strpos($page, '-->', $pos - 3);
|
181 |
-
|
182 |
-
}
|
183 |
-
else
|
184 |
-
{
|
185 |
-
$rc = FALSE;
|
186 |
-
}
|
187 |
-
|
188 |
-
return $rc;
|
189 |
-
}
|
190 |
-
|
191 |
-
/*
|
192 |
-
* Process tag init for the specified element, with the current start and
|
193 |
-
* end positions within the page buffer.
|
194 |
-
*/
|
195 |
-
function process_tag_init(&$element, $start, $end)
|
196 |
-
{
|
197 |
-
switch ($element)
|
198 |
-
{
|
199 |
-
case 'a':
|
200 |
-
process_anchor_tag($start, $end);
|
201 |
-
break;
|
202 |
-
case 'div' :
|
203 |
-
case 'span':
|
204 |
-
process_span_or_div_tag($element, $start, $end);
|
205 |
-
break;
|
206 |
-
case 'html':
|
207 |
-
process_html_tag($start, $end);
|
208 |
-
break;
|
209 |
-
case 'body':
|
210 |
-
global $is_in_body;
|
211 |
-
$is_in_body = TRUE;
|
212 |
-
break;
|
213 |
-
case 'channel':
|
214 |
-
global $is_in_channel;
|
215 |
-
$is_in_channel = TRUE;
|
216 |
-
break;
|
217 |
-
}
|
218 |
-
|
219 |
-
}
|
220 |
-
|
221 |
-
/*
|
222 |
-
* Handle span tags. Looks for 'no_tranlate' identifier that will disable
|
223 |
-
* translation for the enclosed text.
|
224 |
-
*
|
225 |
-
*/
|
226 |
-
function process_span_or_div_tag(&$element, $start, $end)
|
227 |
-
{
|
228 |
-
|
229 |
-
$cls = get_attribute($start, $end, 'class');
|
230 |
-
|
231 |
-
if($cls == NULL)
|
232 |
-
{
|
233 |
-
return;
|
234 |
-
}
|
235 |
-
|
236 |
-
//Look for the no translate class
|
237 |
-
if(stripos($cls, NO_TRANSLATE_CLASS) === FALSE)
|
238 |
-
{
|
239 |
-
return;
|
240 |
-
}
|
241 |
-
|
242 |
-
//Mark the element as not translatable
|
243 |
-
$element .= "." . NO_TRANSLATE_CLASS;
|
244 |
-
}
|
245 |
-
|
246 |
-
/*
|
247 |
-
* Process html tag. Set the direction for rtl languages.
|
248 |
-
*
|
249 |
-
*/
|
250 |
-
function process_html_tag($start, $end)
|
251 |
-
{
|
252 |
-
global $lang, $rtl_languages;
|
253 |
-
|
254 |
-
if(!(in_array ($lang, $rtl_languages)))
|
255 |
-
{
|
256 |
-
return;
|
257 |
-
}
|
258 |
|
259 |
-
$dir = get_attribute($start, $end, 'dir');
|
260 |
-
|
261 |
-
if($dir == NULL)
|
262 |
-
{
|
263 |
-
|
264 |
-
//attribute does not exist - add it
|
265 |
-
update_translated_page($end, -1, 'dir="rtl"');
|
266 |
-
}
|
267 |
-
else
|
268 |
-
{
|
269 |
-
$dir = 'rtl';
|
270 |
-
|
271 |
-
//rewrite url in translated page
|
272 |
-
update_translated_page($start, $end, $dir);
|
273 |
-
|
274 |
-
}
|
275 |
-
|
276 |
-
}
|
277 |
-
|
278 |
-
|
279 |
-
/*
|
280 |
-
* Process tag termination.
|
281 |
-
* Note: The current position in buffer points to the '>' character
|
282 |
-
*/
|
283 |
-
function process_tag_termination(&$element)
|
284 |
-
{
|
285 |
-
global $pos, $tags_list, $page;
|
286 |
-
|
287 |
-
|
288 |
-
}
|
289 |
-
|
290 |
-
|
291 |
-
/*
|
292 |
-
* Return the element id within the current tag.
|
293 |
-
*/
|
294 |
-
function get_element()
|
295 |
-
{
|
296 |
-
global $page, $pos;
|
297 |
-
|
298 |
-
|
299 |
-
skip_white_space();
|
300 |
-
|
301 |
-
$start = $pos;
|
302 |
-
|
303 |
-
//check first for a character data section - treat it like an element
|
304 |
-
if(is_word('![CDATA['))
|
305 |
-
{
|
306 |
-
$pos += 8; //skip to end of ![CDATA[
|
307 |
-
}
|
308 |
-
else
|
309 |
-
{
|
310 |
-
//keep scanning till the first white space or the '>' mark
|
311 |
-
while($pos < strlen($page) && !is_white_space($pos) && $page[$pos] != '>')
|
312 |
-
{
|
313 |
-
$pos++;
|
314 |
-
}
|
315 |
-
}
|
316 |
-
|
317 |
-
|
318 |
-
return substr($page,$start, $pos - $start);
|
319 |
-
}
|
320 |
-
|
321 |
-
/*
|
322 |
-
* Search for the given attribute within the limit of the start and
|
323 |
-
* end position within the buffer.
|
324 |
-
* Returns the string containing the attribute if available otherwise NULL.
|
325 |
-
* In addition the start and end position are moved to boundaries of the
|
326 |
-
* attribute's value.
|
327 |
-
*/
|
328 |
-
function get_attribute(&$start, &$end, $id)
|
329 |
-
{
|
330 |
-
global $page;
|
331 |
-
|
332 |
-
//look for the id within the given limits.
|
333 |
-
while($start < $end)
|
334 |
-
{
|
335 |
-
$index = 0;
|
336 |
-
|
337 |
-
while($start < $end && $page[$start + $index] == $id[$index]
|
338 |
-
&& $index < strlen($id))
|
339 |
-
{
|
340 |
-
$index++;
|
341 |
-
}
|
342 |
-
|
343 |
-
if($index == strlen($id))
|
344 |
-
{
|
345 |
-
//we have match
|
346 |
-
break;
|
347 |
-
}
|
348 |
-
|
349 |
-
$start++;
|
350 |
-
}
|
351 |
-
|
352 |
-
if($start == $end)
|
353 |
-
{
|
354 |
-
return NULL;
|
355 |
-
}
|
356 |
-
|
357 |
-
//look for the " or ' marking start of attribute's value
|
358 |
-
while($start < $end && $page[$start] != '"' && $page[$start] != "'")
|
359 |
-
{
|
360 |
-
$start++;
|
361 |
-
}
|
362 |
-
|
363 |
-
$start++;
|
364 |
-
if($start >= $end)
|
365 |
-
{
|
366 |
-
return NULL;
|
367 |
-
}
|
368 |
-
|
369 |
-
$tmp = $start + 1;
|
370 |
-
//look for the " or ' marking the end of attribute's value
|
371 |
-
while($tmp < $end && $page[$tmp] != '"' && $page[$tmp] != "'")
|
372 |
-
{
|
373 |
-
$tmp++;
|
374 |
-
}
|
375 |
-
|
376 |
-
$end = $tmp - 1;
|
377 |
-
|
378 |
-
|
379 |
-
return substr($page, $start, $end - $start + 1);
|
380 |
-
}
|
381 |
-
|
382 |
-
/*
|
383 |
-
* Attempt to process the content of the tag (if exists). If the current
|
384 |
-
* is of a type that need translation then translate, otherwise skip.
|
385 |
-
*
|
386 |
-
*/
|
387 |
-
function process_current_tag()
|
388 |
-
{
|
389 |
-
global $page, $pos;
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
//translate only known elements within the body or channel
|
394 |
-
if(is_translatable_section())
|
395 |
-
{
|
396 |
-
skip_white_space();
|
397 |
-
$start = $pos;
|
398 |
-
$page_length = strlen($page);
|
399 |
-
|
400 |
-
// Indicates if the html entity should break into a new translation segment.
|
401 |
-
$is_breaker = FALSE;
|
402 |
-
|
403 |
-
while($pos < $page_length && $page[$pos] != '<')
|
404 |
-
{
|
405 |
-
//will break translation unit when one of the following characters is reached: .,
|
406 |
-
$end_of_entity = is_html_entity($pos, $is_breaker);
|
407 |
-
if($end_of_entity)
|
408 |
-
{
|
409 |
-
//Check if should break - value has been set by the is_html_entity function
|
410 |
-
if($is_breaker)
|
411 |
-
{
|
412 |
-
translate_text($start);
|
413 |
-
$start = $end_of_entity;
|
414 |
-
}
|
415 |
-
|
416 |
-
//skip past entity
|
417 |
-
$pos = $end_of_entity;
|
418 |
-
}
|
419 |
-
else if(is_sentence_breaker($pos))
|
420 |
-
{
|
421 |
-
translate_text($start);
|
422 |
-
$pos++;
|
423 |
-
$start = $pos;
|
424 |
-
}
|
425 |
-
else if(is_number($pos))
|
426 |
-
{
|
427 |
-
$end_of_number = is_number($pos);
|
428 |
-
//numbers will break translations segements and will not be included in the translation
|
429 |
-
translate_text($start);
|
430 |
-
$pos = $start = $end_of_number;
|
431 |
-
}
|
432 |
-
else
|
433 |
-
{
|
434 |
-
$pos++;
|
435 |
-
}
|
436 |
-
}
|
437 |
-
|
438 |
-
if($pos > $start)
|
439 |
-
{
|
440 |
-
translate_text($start);
|
441 |
-
}
|
442 |
-
}
|
443 |
-
|
444 |
-
}
|
445 |
-
|
446 |
-
/*
|
447 |
-
* Translate the content of a cdata section. For now we only expect to handle it
|
448 |
-
* within RSS feeds.
|
449 |
-
*/
|
450 |
-
function process_cdata_section()
|
451 |
-
{
|
452 |
-
global $page, $pos;
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
//translate only known elements within rss feeds
|
457 |
-
if(is_translatable_section())
|
458 |
-
{
|
459 |
-
skip_white_space();
|
460 |
-
$start = $pos;
|
461 |
-
$page_length = strlen($page);
|
462 |
-
|
463 |
-
while($pos < $page_length && !is_word(']]>'))
|
464 |
-
{
|
465 |
-
//will break translation unit when one of the following characters is reached: .,
|
466 |
-
if(is_sentence_breaker($pos) ||
|
467 |
-
$page[$pos] == '<' || $page[$pos] == '>') //only in cdata the < > are valid breakers as well
|
468 |
-
{
|
469 |
-
translate_text($start);
|
470 |
-
$pos++;
|
471 |
-
$start = $pos;
|
472 |
-
}
|
473 |
-
else if(is_number($pos))
|
474 |
-
{
|
475 |
-
$end_of_number = is_number($pos);
|
476 |
-
//numbers will break translations segements and will not be included in the translation
|
477 |
-
translate_text($start);
|
478 |
-
$pos = $start = $end_of_number;
|
479 |
-
}
|
480 |
-
else
|
481 |
-
{
|
482 |
-
$pos++;
|
483 |
-
}
|
484 |
-
}
|
485 |
-
|
486 |
-
if($pos > $start)
|
487 |
-
{
|
488 |
-
translate_text($start);
|
489 |
-
}
|
490 |
-
}
|
491 |
-
|
492 |
-
}
|
493 |
|
494 |
/**
|
495 |
-
*
|
496 |
-
* Return TRUE if should be translated otherwise FALSE.
|
497 |
*/
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
}
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
if($translated_text == NULL)
|
824 |
-
{
|
825 |
-
$span .= 'u">';
|
826 |
-
$span .= $original_text;
|
827 |
-
}
|
828 |
-
else
|
829 |
-
{
|
830 |
-
$span .= 't">';
|
831 |
-
$span .= $translated_text;
|
832 |
-
//// $is_translated = TRUE;
|
833 |
-
}
|
834 |
-
$span .= '</span>';
|
835 |
-
|
836 |
-
//Insert text (either original or translated) marked by a <span>
|
837 |
-
update_translated_page($start, $end, $span);
|
838 |
-
|
839 |
-
//Increment only after both text and image are generated so they
|
840 |
-
//will be the same for each translated segement
|
841 |
-
$segment_id++;
|
842 |
-
|
843 |
-
}
|
844 |
-
else
|
845 |
-
{
|
846 |
-
if($translated_text != NULL)
|
847 |
-
{
|
848 |
-
update_translated_page($start, $end, $translated_text);
|
849 |
-
}
|
850 |
-
}
|
851 |
-
|
852 |
-
|
853 |
-
}
|
854 |
-
|
855 |
-
/*
|
856 |
-
* Scrubs text prior to translation to remove/encode special
|
857 |
-
* characters.
|
858 |
-
* Return the scurbed text, or NULL if nothing left to translate
|
859 |
-
*/
|
860 |
-
function scrub_text(&$text)
|
861 |
-
{
|
862 |
-
//skip strings like without any readable characters (i.e. ".")
|
863 |
-
//Todo: need a broader defintion for non-ascii characters as well
|
864 |
-
if(preg_match("/^[.?!|\(\)\[\],0-9]+$/", $text))
|
865 |
-
{
|
866 |
-
return NULL;
|
867 |
-
}
|
868 |
-
|
869 |
-
//replace multi space chars with a single space
|
870 |
-
$text = preg_replace("/\s\s+/", " ", $text);
|
871 |
-
|
872 |
-
return $text;
|
873 |
-
}
|
874 |
-
|
875 |
-
/**
|
876 |
-
* Insert a translated text to the translated page.
|
877 |
-
* Currentlly assume that we always insert and move forward - not moving
|
878 |
-
* back in buffer.
|
879 |
-
* param start - marks the starting position of the replaced string in the original page.
|
880 |
-
* param end - marks the end position of the replaced string in the original page.
|
881 |
-
Use -1 to do insert instead of replace.
|
882 |
-
* param translated_text - text to be inserted.
|
883 |
-
*/
|
884 |
-
function update_translated_page($start, $end, $translated_text)
|
885 |
-
{
|
886 |
-
global $page, $tr_page, $tr_mark;
|
887 |
-
|
888 |
-
//Bring the translated up to date up to the start position.
|
889 |
-
while($tr_mark < $start)
|
890 |
-
{
|
891 |
-
$tr_page .= $page[$tr_mark++];
|
892 |
-
}
|
893 |
-
|
894 |
-
$tr_page .= $translated_text;
|
895 |
-
|
896 |
-
if($end > $start)
|
897 |
-
{
|
898 |
-
//Move mark to correlate the posistion between the two pages.
|
899 |
-
//Only do this when some content has been replaced, i.e. not
|
900 |
-
//an insert.
|
901 |
-
$tr_mark = $end + 1;
|
902 |
-
}
|
903 |
-
|
904 |
-
}
|
905 |
-
|
906 |
-
|
907 |
-
/*
|
908 |
-
* Fix links on the page. href needs to be modified to include
|
909 |
-
* lang specifier and editing mode.
|
910 |
-
*/
|
911 |
-
function process_anchor_tag($start, $end)
|
912 |
-
{
|
913 |
-
global $home_url, $lang, $is_edit_mode, $enable_permalinks_rewrite;
|
914 |
-
|
915 |
-
$href = get_attribute($start, $end, 'href');
|
916 |
-
|
917 |
-
if($href == NULL)
|
918 |
-
{
|
919 |
-
return;
|
920 |
-
}
|
921 |
-
|
922 |
-
//Ignore urls not from this site
|
923 |
-
if(stripos($href, $home_url) === FALSE)
|
924 |
-
{
|
925 |
-
return;
|
926 |
-
}
|
927 |
-
|
928 |
-
$use_params = !$enable_permalinks_rewrite;
|
929 |
-
|
930 |
-
//Allow specific override for url rewriting .
|
931 |
-
if($enable_permalinks_rewrite && function_exists('is_url_excluded_from_permalink_rewrite') &&
|
932 |
-
is_url_excluded_from_permalink_rewrite($href))
|
933 |
-
{
|
934 |
-
$use_params = TRUE;
|
935 |
-
}
|
936 |
-
|
937 |
-
$href = rewrite_url_lang_param($href, $lang, $is_edit_mode, $use_params);
|
938 |
-
|
939 |
-
//rewrite url in translated page
|
940 |
-
update_translated_page($start, $end, $href);
|
941 |
-
|
942 |
}
|
943 |
?>
|
16 |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
17 |
*/
|
18 |
|
19 |
+
require_once("shd/simple_html_dom.php");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
/**
|
23 |
+
* Parser class - allows phrase marking and translation with callback functions
|
|
|
24 |
*/
|
25 |
+
class parser {
|
26 |
+
public $url_rewrite_func = null;
|
27 |
+
public $fetch_translate_func = null;
|
28 |
+
private $segment_id = 0;
|
29 |
+
private $currentnode;
|
30 |
+
private $html; // the document
|
31 |
+
public $dir_rtl;
|
32 |
+
public $lang;
|
33 |
+
private $inbody = false;
|
34 |
+
public $is_edit_mode;
|
35 |
+
public $is_auto_translate;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Determine if the current position in buffer is a white space.
|
39 |
+
* @param $char
|
40 |
+
* @return bool true if current position marks a white space
|
41 |
+
*/
|
42 |
+
function is_white_space($char)
|
43 |
+
{
|
44 |
+
if (!$char) return TRUE;
|
45 |
+
return strspn($char, " \t\r\n\0\x0B");
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Determine if the current position in page points to a character in the
|
50 |
+
* range of a-z (case insensetive).
|
51 |
+
* @return bool true if a-z
|
52 |
+
*/
|
53 |
+
function is_a_to_z_character($char)
|
54 |
+
{
|
55 |
+
return (($char >= 'a' && $char <= 'z') || ($char >= 'A' && $char <= 'Z')) ? true : false;
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Determine if the current position is a digit.
|
60 |
+
* @return bool true if a digit
|
61 |
+
*/
|
62 |
+
function is_digit($char)
|
63 |
+
{
|
64 |
+
return (($char >= '0' && $char <= '9')) ? true : false;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Determine if the current position is an html entity - such as & or “.
|
69 |
+
* @param $string string to evalute
|
70 |
+
* @param $position where to check for entities
|
71 |
+
* @return int length of entity
|
72 |
+
*/
|
73 |
+
function is_html_entity($string, $position)
|
74 |
+
{
|
75 |
+
if ($string[$position] == "&")
|
76 |
+
{
|
77 |
+
$end_pos = $position + 1;
|
78 |
+
while($string[$end_pos] == "#" || $this->is_digit($string[$end_pos]) || $this->is_a_to_z_character($string[$end_pos])) ++$end_pos;
|
79 |
+
if ($string[$end_pos] == ';') return $end_pos - $position +1;
|
80 |
+
}
|
81 |
+
return 0;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Some entities will not cause a break if they don't have whitespace after them
|
86 |
+
* such as Jack`s apple.
|
87 |
+
* `uncatagorized` will break on the later entity
|
88 |
+
* @param $entity - html entity to check
|
89 |
+
* @return - true if not a breaker (apostrophy)
|
90 |
+
*/
|
91 |
+
function is_entity_breaker($entity) {
|
92 |
+
return !(strpos('’'''', $entity) !== FALSE);
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Determine if the current position in buffer is a sentence breaker, e.g. '.' or ',' .
|
97 |
+
* Note html markups are not considered sentence breaker within the scope of this function.
|
98 |
+
* @param $char charcter checked if breaker
|
99 |
+
* @param $nextchar needed for checking if . or - breaks
|
100 |
+
* @return bool true if current position marks a break in sentence
|
101 |
+
*/
|
102 |
+
function is_sentence_breaker($char, $nextchar)
|
103 |
+
{
|
104 |
+
if (($char == '.' || $char == '-') && ($this->is_white_space($nextchar))) return true;
|
105 |
+
return (strpos(',?()[]"!:|;',$char) !== false) ? true : false;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Determines if the current position marks the begining of a number, e.g. 123 050-391212232
|
110 |
+
* @return length of number.
|
111 |
+
*/
|
112 |
+
function is_number($page, $position)
|
113 |
+
{
|
114 |
+
return strspn($page,'0123456789-+,.\\/',$position);
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Create a pharse tag in the html dom tree
|
119 |
+
* @param int $start - beginning of pharse in element
|
120 |
+
* @param int $end - end of pharse in element
|
121 |
+
*/
|
122 |
+
function tag_phrase($string,$start, $end) {
|
123 |
+
$phrase = trim(substr($string,$start,$end-$start));
|
124 |
+
if ($phrase) {
|
125 |
+
$node = new simple_html_dom_node($this->html);
|
126 |
+
$node->tag = 'phrase';
|
127 |
+
$node->parent = $this->currentnode;
|
128 |
+
$this->currentnode->nodes[] = $node;
|
129 |
+
$node->_[HDOM_INFO_OUTER] = '';
|
130 |
+
$node->phrase = $phrase;
|
131 |
+
$node->inbody = $this->inbody;
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Breaks strings into substring according to some rules and common sense
|
137 |
+
* @param string $string - the string which is "broken" into smaller strings
|
138 |
+
*/
|
139 |
+
function parsetext($string) {
|
140 |
+
$pos = 0;
|
141 |
+
// $pos = skip_white_space($string, $pos);
|
142 |
+
$start = $pos;
|
143 |
+
|
144 |
+
while($pos < strlen($string))
|
145 |
+
{
|
146 |
+
// Some HTML entities make us break, almost all but apostrophies
|
147 |
+
if($len_of_entity = $this->is_html_entity($string,$pos))
|
148 |
+
{
|
149 |
+
if($this->is_white_space($string[$pos+$len_of_entity]) || $this->is_entity_breaker(substr($string,$pos,$len_of_entity)))
|
150 |
+
{
|
151 |
+
$this->tag_phrase($string,$start,$pos);
|
152 |
+
$start = $pos + $len_of_entity;
|
153 |
+
}
|
154 |
+
//skip past entity
|
155 |
+
$pos += $len_of_entity;
|
156 |
+
}
|
157 |
+
// will break translation unit when there's a breaker ",.[]()..."
|
158 |
+
else if($this->is_sentence_breaker($string[$pos],$string[$pos+1]))
|
159 |
+
{
|
160 |
+
$this->tag_phrase($string,$start,$pos);
|
161 |
+
$pos++;
|
162 |
+
$start = $pos;
|
163 |
+
}
|
164 |
+
// Numbers also break, if they are followed by whitespace (don't break 42nd)
|
165 |
+
else if($num_len = $this->is_number($string,$pos))
|
166 |
+
{
|
167 |
+
if ($this->is_white_space($string[$pos+$num_len])) {
|
168 |
+
$this->tag_phrase($string,$start,$pos);
|
169 |
+
$start = $pos + $num_len + 1;
|
170 |
+
}
|
171 |
+
$pos += $num_len + 1;
|
172 |
+
}
|
173 |
+
else
|
174 |
+
{
|
175 |
+
$pos++;
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
// the end is also some breaker
|
180 |
+
if($pos > $start)
|
181 |
+
{
|
182 |
+
$this->tag_phrase($string,$start,$pos);
|
183 |
+
}
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* This recursive function works on the $html dom and adds phrase nodes to translate as needed
|
188 |
+
* it currently also rewrites urls, and should consider if this is smart
|
189 |
+
* @param <type> $node
|
190 |
+
*/
|
191 |
+
function translate_tagging($node) {
|
192 |
+
$this->currentnode = $node;
|
193 |
+
// we don't want to translate non-translatable classes
|
194 |
+
if (stripos($node->class,NO_TRANSLATE_CLASS) !== false) return;
|
195 |
+
if ($node->tag == 'style' || $node->tag == 'script') return;
|
196 |
+
elseif ($node->tag == 'text') {
|
197 |
+
if (trim($node->outertext)) {
|
198 |
+
$this->parsetext($node->outertext);
|
199 |
+
};
|
200 |
+
}
|
201 |
+
// for anchors we will rewrite urls if we can
|
202 |
+
elseif ($node->tag == 'a') {
|
203 |
+
if ($this->url_rewrite_func != null) {
|
204 |
+
$node->href = call_user_func_array($this->url_rewrite_func,array($node->href));
|
205 |
+
}
|
206 |
+
}
|
207 |
+
// same for options, although normally not required (ticket #34)
|
208 |
+
elseif ($node->tag == 'option') {
|
209 |
+
if ($this->url_rewrite_func != null) {
|
210 |
+
$node->value = call_user_func_array($this->url_rewrite_func,array($node->value));
|
211 |
+
}
|
212 |
+
}
|
213 |
+
// we can only do translation for elements which are in the body, not in other places
|
214 |
+
elseif ($node->tag == 'body') {
|
215 |
+
$this->inbody = true;
|
216 |
+
}
|
217 |
+
|
218 |
+
// titles are also good places to translate, exist in a, img, abbr, acronym
|
219 |
+
if ($node->title) $this->parsetext($node->title);
|
220 |
+
|
221 |
+
// recurse
|
222 |
+
foreach($node->nodes as $c) {
|
223 |
+
$this->translate_tagging($c);
|
224 |
+
}
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Creates a span used in translation and editing
|
229 |
+
* @param string $original_text
|
230 |
+
* @param string $translated_text
|
231 |
+
* @param int $source (Either "0" for Human, "1" for Machine or "" for untouched)
|
232 |
+
* @param boolean $for_hidden_element
|
233 |
+
* @return string
|
234 |
+
*/
|
235 |
+
function create_edit_span ($original_text , $translated_text, $source, $for_hidden_element = false) {
|
236 |
+
// Use base64 encoding to make that when the page is translated (i.e. update_translation) we
|
237 |
+
// get back exactlly the same string without having the client decode/encode it in anyway.
|
238 |
+
$span = '<span class ="'.SPAN_PREFIX.'" id="'.SPAN_PREFIX.$this->segment_id.'" token="' . base64_url_encode($original_text)."\" source=\"$source\"";
|
239 |
+
// those are needed for on the fly image creation / hidden elements translations
|
240 |
+
if ($this->is_edit_mode || $for_hidden_element) {
|
241 |
+
$span .= " orig=\"$original_text\"";
|
242 |
+
if ($for_hidden_element) {
|
243 |
+
$span.= ' hidden="y"';
|
244 |
+
// hidden elements currently have issues figuring what they translated in the JS
|
245 |
+
if ($translated_text != null) {
|
246 |
+
$span.= " trans=\"$translated_text\"";
|
247 |
+
}
|
248 |
+
}
|
249 |
+
}
|
250 |
+
$span .= '>';
|
251 |
+
++$this->segment_id;
|
252 |
+
return $span;
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Main function - actually translates a given HTML
|
257 |
+
* @param string $string
|
258 |
+
* @return string Translated content is here
|
259 |
+
*/
|
260 |
+
function fix_html($string) {
|
261 |
+
// create our dom
|
262 |
+
$this->html = str_get_html($string);
|
263 |
+
// mark translateable elements
|
264 |
+
$this->translate_tagging($this->html->root);
|
265 |
+
|
266 |
+
// first fix the html tag itself - we might need to to the same for all such attributes with flipping
|
267 |
+
if ($this->dir_rtl)
|
268 |
+
$this->html->find('html',0)->dir="rtl";
|
269 |
+
|
270 |
+
if ($this->lang)
|
271 |
+
$this->html->find('html',0)->lang=$lang;
|
272 |
+
|
273 |
+
// not much point in further processing if we don't have a function that does it
|
274 |
+
if ($this->fetch_translate_func == null) {
|
275 |
+
return $this->html;
|
276 |
+
}
|
277 |
+
|
278 |
+
// actually translate tags
|
279 |
+
// texts are first
|
280 |
+
foreach ($this->html->find('text') as $e) {
|
281 |
+
$right = '';
|
282 |
+
$newtext = '';
|
283 |
+
foreach ($e->nodes as $ep) {
|
284 |
+
list ($translated_text, $source) = call_user_func_array($this->fetch_translate_func,array($ep->phrase, $this->lang));
|
285 |
+
if (($this->is_edit_mode || ($this->is_auto_translate && $translated_text == null)) && $ep->inbody) {
|
286 |
+
$span = $this->create_edit_span($ep->phrase, $translated_text, $source);
|
287 |
+
$spanend = "</span>";
|
288 |
+
if ($translated_text == null) $translated_text = $ep->phrase;
|
289 |
+
} else {
|
290 |
+
$span = '';
|
291 |
+
$spanend = '';
|
292 |
+
}
|
293 |
+
if ($translated_text) {
|
294 |
+
list ($left, $right) = explode($ep->phrase, $e->outertext, 2);
|
295 |
+
$newtext .= $left.$span.$translated_text.$spanend;
|
296 |
+
$e->outertext = $right;
|
297 |
+
}
|
298 |
+
}
|
299 |
+
if ($newtext) {
|
300 |
+
$e->outertext = $newtext.$right;
|
301 |
+
}
|
302 |
+
}
|
303 |
+
|
304 |
+
// now we handle the title attributes
|
305 |
+
$hidden_phrases = array();
|
306 |
+
foreach ($this->html->find('[title]') as $e) {
|
307 |
+
$span = '';
|
308 |
+
$spanend = '';
|
309 |
+
$right = '';
|
310 |
+
$newtext = '';
|
311 |
+
// when we already have a parent outertext we'll have to update it directly
|
312 |
+
if ($e->parent->_[HDOM_INFO_OUTER]) {
|
313 |
+
$saved_outertext = $e->outertext;
|
314 |
+
}
|
315 |
+
|
316 |
+
foreach ($e->nodes as $ep) {
|
317 |
+
if ($ep->tag == 'phrase') {
|
318 |
+
list ($translated_text, $source) = call_user_func_array($this->fetch_translate_func,array($ep->phrase, $this->lang));
|
319 |
+
if (($this->is_edit_mode || ($this->is_auto_translate && $translated_text == null)) && $ep->inbody) {
|
320 |
+
// prevent duplicate translation (title = text)
|
321 |
+
if (strpos($e->innertext,base64_url_encode($ep->phrase)) === false) {
|
322 |
+
//no need to translate span the same hidden phrase more than once
|
323 |
+
if (!in_array($ep->phrase, $hidden_phrases)) {
|
324 |
+
$span .= $this->create_edit_span($ep->phrase, $translated_text, $source, true)."</span>";
|
325 |
+
//
|
326 |
+
$hidden_phrases[] = $ep->phrase;
|
327 |
+
}
|
328 |
+
}
|
329 |
+
}
|
330 |
+
if ($translated_text) {
|
331 |
+
list ($left, $right) = explode($ep->phrase, $e->title, 2);
|
332 |
+
$newtext .= $left.$translated_text;
|
333 |
+
$e->title = $right;
|
334 |
+
}
|
335 |
+
}
|
336 |
+
}
|
337 |
+
if ($newtext)
|
338 |
+
$e->title = $newtext.$right;
|
339 |
+
|
340 |
+
$e->outertext .= $span;
|
341 |
+
// this is where we update in the outercase issue
|
342 |
+
if ($e->parent->_[HDOM_INFO_OUTER]) {
|
343 |
+
$e->parent->outertext = implode ($e->outertext,explode($saved_outertext,$e->parent->outertext,2));
|
344 |
+
}
|
345 |
+
|
346 |
+
}
|
347 |
+
|
348 |
+
return $this->html;
|
349 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
350 |
}
|
351 |
?>
|
core/shd/simple_html_dom.php
ADDED
@@ -0,0 +1,975 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/*******************************************************************************
|
3 |
+
Version: 1.11 ($Rev: 175 $)
|
4 |
+
Website: http://sourceforge.net/projects/simplehtmldom/
|
5 |
+
Author: S.C. Chen <me578022@gmail.com>
|
6 |
+
Acknowledge: Jose Solorzano (https://sourceforge.net/projects/php-html/)
|
7 |
+
Contributions by:
|
8 |
+
Yousuke Kumakura (Attribute filters)
|
9 |
+
Vadim Voituk (Negative indexes supports of "find" method)
|
10 |
+
Antcs (Constructor with automatically load contents either text or file/url)
|
11 |
+
Licensed under The MIT License
|
12 |
+
Redistributions of files must retain the above copyright notice.
|
13 |
+
*******************************************************************************/
|
14 |
+
|
15 |
+
define('HDOM_TYPE_ELEMENT', 1);
|
16 |
+
define('HDOM_TYPE_COMMENT', 2);
|
17 |
+
define('HDOM_TYPE_TEXT', 3);
|
18 |
+
define('HDOM_TYPE_ENDTAG', 4);
|
19 |
+
define('HDOM_TYPE_ROOT', 5);
|
20 |
+
define('HDOM_TYPE_UNKNOWN', 6);
|
21 |
+
define('HDOM_QUOTE_DOUBLE', 0);
|
22 |
+
define('HDOM_QUOTE_SINGLE', 1);
|
23 |
+
define('HDOM_QUOTE_NO', 3);
|
24 |
+
define('HDOM_INFO_BEGIN', 0);
|
25 |
+
define('HDOM_INFO_END', 1);
|
26 |
+
define('HDOM_INFO_QUOTE', 2);
|
27 |
+
define('HDOM_INFO_SPACE', 3);
|
28 |
+
define('HDOM_INFO_TEXT', 4);
|
29 |
+
define('HDOM_INFO_INNER', 5);
|
30 |
+
define('HDOM_INFO_OUTER', 6);
|
31 |
+
define('HDOM_INFO_ENDSPACE',7);
|
32 |
+
|
33 |
+
// helper functions
|
34 |
+
// -----------------------------------------------------------------------------
|
35 |
+
// get html dom form file
|
36 |
+
function file_get_html() {
|
37 |
+
$dom = new simple_html_dom;
|
38 |
+
$args = func_get_args();
|
39 |
+
$dom->load(call_user_func_array('file_get_contents', $args), true);
|
40 |
+
return $dom;
|
41 |
+
}
|
42 |
+
|
43 |
+
// get html dom form string
|
44 |
+
function str_get_html($str, $lowercase=true) {
|
45 |
+
$dom = new simple_html_dom;
|
46 |
+
$dom->load($str, $lowercase);
|
47 |
+
return $dom;
|
48 |
+
}
|
49 |
+
|
50 |
+
// dump html dom tree
|
51 |
+
function dump_html_tree($node, $show_attr=true, $deep=0) {
|
52 |
+
$lead = str_repeat(' ', $deep);
|
53 |
+
echo $lead.$node->tag;
|
54 |
+
if ($show_attr && count($node->attr)>0) {
|
55 |
+
echo '(';
|
56 |
+
foreach($node->attr as $k=>$v)
|
57 |
+
echo "[$k]=>\"".$node->$k.'", ';
|
58 |
+
echo ')';
|
59 |
+
}
|
60 |
+
echo "\n";
|
61 |
+
|
62 |
+
foreach($node->nodes as $c)
|
63 |
+
dump_html_tree($c, $show_attr, $deep+1);
|
64 |
+
}
|
65 |
+
|
66 |
+
// get dom form file (deprecated)
|
67 |
+
function file_get_dom() {
|
68 |
+
$dom = new simple_html_dom;
|
69 |
+
$args = func_get_args();
|
70 |
+
$dom->load(call_user_func_array('file_get_contents', $args), true);
|
71 |
+
return $dom;
|
72 |
+
}
|
73 |
+
|
74 |
+
// get dom form string (deprecated)
|
75 |
+
function str_get_dom($str, $lowercase=true) {
|
76 |
+
$dom = new simple_html_dom;
|
77 |
+
$dom->load($str, $lowercase);
|
78 |
+
return $dom;
|
79 |
+
}
|
80 |
+
|
81 |
+
// simple html dom node
|
82 |
+
// -----------------------------------------------------------------------------
|
83 |
+
class simple_html_dom_node {
|
84 |
+
public $nodetype = HDOM_TYPE_TEXT;
|
85 |
+
public $tag = 'text';
|
86 |
+
public $attr = array();
|
87 |
+
public $children = array();
|
88 |
+
public $nodes = array();
|
89 |
+
public $parent = null;
|
90 |
+
public $_ = array();
|
91 |
+
private $dom = null;
|
92 |
+
|
93 |
+
function __construct($dom) {
|
94 |
+
$this->dom = $dom;
|
95 |
+
$dom->nodes[] = $this;
|
96 |
+
}
|
97 |
+
|
98 |
+
function __destruct() {
|
99 |
+
$this->clear();
|
100 |
+
}
|
101 |
+
|
102 |
+
function __toString() {
|
103 |
+
return $this->outertext();
|
104 |
+
}
|
105 |
+
|
106 |
+
// clean up memory due to php5 circular references memory leak...
|
107 |
+
function clear() {
|
108 |
+
$this->dom = null;
|
109 |
+
$this->nodes = null;
|
110 |
+
$this->parent = null;
|
111 |
+
$this->children = null;
|
112 |
+
}
|
113 |
+
|
114 |
+
// dump node's tree
|
115 |
+
function dump($show_attr=true) {
|
116 |
+
dump_html_tree($this, $show_attr);
|
117 |
+
}
|
118 |
+
|
119 |
+
// returns the parent of node
|
120 |
+
function parent() {
|
121 |
+
return $this->parent;
|
122 |
+
}
|
123 |
+
|
124 |
+
// returns children of node
|
125 |
+
function children($idx=-1) {
|
126 |
+
if ($idx===-1) return $this->children;
|
127 |
+
if (isset($this->children[$idx])) return $this->children[$idx];
|
128 |
+
return null;
|
129 |
+
}
|
130 |
+
|
131 |
+
// returns the first child of node
|
132 |
+
function first_child() {
|
133 |
+
if (count($this->children)>0) return $this->children[0];
|
134 |
+
return null;
|
135 |
+
}
|
136 |
+
|
137 |
+
// returns the last child of node
|
138 |
+
function last_child() {
|
139 |
+
if (($count=count($this->children))>0) return $this->children[$count-1];
|
140 |
+
return null;
|
141 |
+
}
|
142 |
+
|
143 |
+
// returns the next sibling of node
|
144 |
+
function next_sibling() {
|
145 |
+
if ($this->parent===null) return null;
|
146 |
+
$idx = 0;
|
147 |
+
$count = count($this->parent->children);
|
148 |
+
while ($idx<$count && $this!==$this->parent->children[$idx])
|
149 |
+
++$idx;
|
150 |
+
if (++$idx>=$count) return null;
|
151 |
+
return $this->parent->children[$idx];
|
152 |
+
}
|
153 |
+
|
154 |
+
// returns the previous sibling of node
|
155 |
+
function prev_sibling() {
|
156 |
+
if ($this->parent===null) return null;
|
157 |
+
$idx = 0;
|
158 |
+
$count = count($this->parent->children);
|
159 |
+
while ($idx<$count && $this!==$this->parent->children[$idx])
|
160 |
+
++$idx;
|
161 |
+
if (--$idx<0) return null;
|
162 |
+
return $this->parent->children[$idx];
|
163 |
+
}
|
164 |
+
|
165 |
+
// get dom node's inner html
|
166 |
+
function innertext() {
|
167 |
+
if (isset($this->_[HDOM_INFO_INNER])) return $this->_[HDOM_INFO_INNER];
|
168 |
+
if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
|
169 |
+
|
170 |
+
$ret = '';
|
171 |
+
foreach($this->nodes as $n)
|
172 |
+
$ret .= $n->outertext();
|
173 |
+
return $ret;
|
174 |
+
}
|
175 |
+
|
176 |
+
// get dom node's outer text (with tag)
|
177 |
+
function outertext() {
|
178 |
+
if ($this->tag==='root') return $this->innertext();
|
179 |
+
|
180 |
+
// trigger callback
|
181 |
+
if ($this->dom->callback!==null)
|
182 |
+
call_user_func_array($this->dom->callback, array($this));
|
183 |
+
|
184 |
+
if (isset($this->_[HDOM_INFO_OUTER])) return $this->_[HDOM_INFO_OUTER];
|
185 |
+
if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
|
186 |
+
|
187 |
+
// render begin tag
|
188 |
+
$ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup();
|
189 |
+
|
190 |
+
// render inner text
|
191 |
+
if (isset($this->_[HDOM_INFO_INNER]))
|
192 |
+
$ret .= $this->_[HDOM_INFO_INNER];
|
193 |
+
else {
|
194 |
+
foreach($this->nodes as $n)
|
195 |
+
$ret .= $n->outertext();
|
196 |
+
}
|
197 |
+
|
198 |
+
// render end tag
|
199 |
+
if(isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END]!=0)
|
200 |
+
$ret .= '</'.$this->tag.'>';
|
201 |
+
return $ret;
|
202 |
+
}
|
203 |
+
|
204 |
+
// get dom node's plain text
|
205 |
+
function text() {
|
206 |
+
if (isset($this->_[HDOM_INFO_INNER])) return $this->_[HDOM_INFO_INNER];
|
207 |
+
switch ($this->nodetype) {
|
208 |
+
case HDOM_TYPE_TEXT: return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
|
209 |
+
case HDOM_TYPE_COMMENT: return '';
|
210 |
+
case HDOM_TYPE_UNKNOWN: return '';
|
211 |
+
}
|
212 |
+
if (strcasecmp($this->tag, 'script')===0) return '';
|
213 |
+
if (strcasecmp($this->tag, 'style')===0) return '';
|
214 |
+
|
215 |
+
$ret = '';
|
216 |
+
foreach($this->nodes as $n)
|
217 |
+
$ret .= $n->text();
|
218 |
+
return $ret;
|
219 |
+
}
|
220 |
+
|
221 |
+
function xmltext() {
|
222 |
+
$ret = $this->innertext();
|
223 |
+
$ret = str_ireplace('<![CDATA[', '', $ret);
|
224 |
+
$ret = str_replace(']]>', '', $ret);
|
225 |
+
return $ret;
|
226 |
+
}
|
227 |
+
|
228 |
+
// build node's text with tag
|
229 |
+
function makeup() {
|
230 |
+
// text, comment, unknown
|
231 |
+
if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
|
232 |
+
|
233 |
+
$ret = '<'.$this->tag;
|
234 |
+
$i = -1;
|
235 |
+
|
236 |
+
foreach($this->attr as $key=>$val) {
|
237 |
+
++$i;
|
238 |
+
|
239 |
+
// skip removed attribute
|
240 |
+
if ($val===null || $val===false)
|
241 |
+
continue;
|
242 |
+
|
243 |
+
$ret .= $this->_[HDOM_INFO_SPACE][$i][0];
|
244 |
+
//no value attr: nowrap, checked selected...
|
245 |
+
if ($val===true)
|
246 |
+
$ret .= $key;
|
247 |
+
else {
|
248 |
+
switch($this->_[HDOM_INFO_QUOTE][$i]) {
|
249 |
+
case HDOM_QUOTE_DOUBLE: $quote = '"'; break;
|
250 |
+
case HDOM_QUOTE_SINGLE: $quote = '\''; break;
|
251 |
+
default: $quote = '';
|
252 |
+
}
|
253 |
+
$ret .= $key.$this->_[HDOM_INFO_SPACE][$i][1].'='.$this->_[HDOM_INFO_SPACE][$i][2].$quote.$val.$quote;
|
254 |
+
}
|
255 |
+
}
|
256 |
+
$ret = $this->dom->restore_noise($ret);
|
257 |
+
return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>';
|
258 |
+
}
|
259 |
+
|
260 |
+
// find elements by css selector
|
261 |
+
function find($selector, $idx=null) {
|
262 |
+
$selectors = $this->parse_selector($selector);
|
263 |
+
if (($count=count($selectors))===0) return array();
|
264 |
+
$found_keys = array();
|
265 |
+
|
266 |
+
// find each selector
|
267 |
+
for ($c=0; $c<$count; ++$c) {
|
268 |
+
if (($levle=count($selectors[0]))===0) return array();
|
269 |
+
if (!isset($this->_[HDOM_INFO_BEGIN])) return array();
|
270 |
+
|
271 |
+
$head = array($this->_[HDOM_INFO_BEGIN]=>1);
|
272 |
+
|
273 |
+
// handle descendant selectors, no recursive!
|
274 |
+
for ($l=0; $l<$levle; ++$l) {
|
275 |
+
$ret = array();
|
276 |
+
foreach($head as $k=>$v) {
|
277 |
+
$n = ($k===-1) ? $this->dom->root : $this->dom->nodes[$k];
|
278 |
+
$n->seek($selectors[$c][$l], $ret);
|
279 |
+
}
|
280 |
+
$head = $ret;
|
281 |
+
}
|
282 |
+
|
283 |
+
foreach($head as $k=>$v) {
|
284 |
+
if (!isset($found_keys[$k]))
|
285 |
+
$found_keys[$k] = 1;
|
286 |
+
}
|
287 |
+
}
|
288 |
+
|
289 |
+
// sort keys
|
290 |
+
ksort($found_keys);
|
291 |
+
|
292 |
+
$found = array();
|
293 |
+
foreach($found_keys as $k=>$v)
|
294 |
+
$found[] = $this->dom->nodes[$k];
|
295 |
+
|
296 |
+
// return nth-element or array
|
297 |
+
if (is_null($idx)) return $found;
|
298 |
+
else if ($idx<0) $idx = count($found) + $idx;
|
299 |
+
return (isset($found[$idx])) ? $found[$idx] : null;
|
300 |
+
}
|
301 |
+
|
302 |
+
// seek for given conditions
|
303 |
+
protected function seek($selector, &$ret) {
|
304 |
+
list($tag, $key, $val, $exp, $no_key) = $selector;
|
305 |
+
|
306 |
+
// xpath index
|
307 |
+
if ($tag && $key && is_numeric($key)) {
|
308 |
+
$count = 0;
|
309 |
+
foreach ($this->children as $c) {
|
310 |
+
if ($tag==='*' || $tag===$c->tag) {
|
311 |
+
if (++$count==$key) {
|
312 |
+
$ret[$c->_[HDOM_INFO_BEGIN]] = 1;
|
313 |
+
return;
|
314 |
+
}
|
315 |
+
}
|
316 |
+
}
|
317 |
+
return;
|
318 |
+
}
|
319 |
+
|
320 |
+
$end = (!empty($this->_[HDOM_INFO_END])) ? $this->_[HDOM_INFO_END] : 0;
|
321 |
+
if ($end==0) {
|
322 |
+
$parent = $this->parent;
|
323 |
+
while (!isset($parent->_[HDOM_INFO_END]) && $parent!==null) {
|
324 |
+
$end -= 1;
|
325 |
+
$parent = $parent->parent;
|
326 |
+
}
|
327 |
+
$end += $parent->_[HDOM_INFO_END];
|
328 |
+
}
|
329 |
+
|
330 |
+
for($i=$this->_[HDOM_INFO_BEGIN]+1; $i<$end; ++$i) {
|
331 |
+
$node = $this->dom->nodes[$i];
|
332 |
+
$pass = true;
|
333 |
+
|
334 |
+
if ($tag==='*' && !$key) {
|
335 |
+
if (in_array($node, $this->children, true))
|
336 |
+
$ret[$i] = 1;
|
337 |
+
continue;
|
338 |
+
}
|
339 |
+
|
340 |
+
// compare tag
|
341 |
+
if ($tag && $tag!=$node->tag && $tag!=='*') {$pass=false;}
|
342 |
+
// compare key
|
343 |
+
if ($pass && $key) {
|
344 |
+
if ($no_key) {
|
345 |
+
if (isset($node->attr[$key])) $pass=false;
|
346 |
+
}
|
347 |
+
else if (!isset($node->attr[$key])) $pass=false;
|
348 |
+
}
|
349 |
+
// compare value
|
350 |
+
if ($pass && $key && $val && $val!=='*') {
|
351 |
+
$check = $this->match($exp, $val, $node->attr[$key]);
|
352 |
+
// handle multiple class
|
353 |
+
if (!$check && strcasecmp($key, 'class')===0) {
|
354 |
+
foreach(explode(' ',$node->attr[$key]) as $k) {
|
355 |
+
$check = $this->match($exp, $val, $k);
|
356 |
+
if ($check) break;
|
357 |
+
}
|
358 |
+
}
|
359 |
+
if (!$check) $pass = false;
|
360 |
+
}
|
361 |
+
if ($pass) $ret[$i] = 1;
|
362 |
+
unset($node);
|
363 |
+
}
|
364 |
+
}
|
365 |
+
|
366 |
+
protected function match($exp, $pattern, $value) {
|
367 |
+
switch ($exp) {
|
368 |
+
case '=':
|
369 |
+
return ($value===$pattern);
|
370 |
+
case '!=':
|
371 |
+
return ($value!==$pattern);
|
372 |
+
case '^=':
|
373 |
+
return preg_match("/^".preg_quote($pattern,'/')."/", $value);
|
374 |
+
case '$=':
|
375 |
+
return preg_match("/".preg_quote($pattern,'/')."$/", $value);
|
376 |
+
case '*=':
|
377 |
+
if ($pattern[0]=='/')
|
378 |
+
return preg_match($pattern, $value);
|
379 |
+
return preg_match("/".$pattern."/i", $value);
|
380 |
+
}
|
381 |
+
return false;
|
382 |
+
}
|
383 |
+
|
384 |
+
protected function parse_selector($selector_string) {
|
385 |
+
// pattern of CSS selectors, modified from mootools
|
386 |
+
$pattern = "/([\w-:\*]*)(?:\#([\w-]+)|\.([\w-]+))?(?:\[@?(!?[\w-]+)(?:([!*^$]?=)[\"']?(.*?)[\"']?)?\])?([\/, ]+)/is";
|
387 |
+
preg_match_all($pattern, trim($selector_string).' ', $matches, PREG_SET_ORDER);
|
388 |
+
$selectors = array();
|
389 |
+
$result = array();
|
390 |
+
//print_r($matches);
|
391 |
+
|
392 |
+
foreach ($matches as $m) {
|
393 |
+
$m[0] = trim($m[0]);
|
394 |
+
if ($m[0]==='' || $m[0]==='/' || $m[0]==='//') continue;
|
395 |
+
// for borwser grnreated xpath
|
396 |
+
if ($m[1]==='tbody') continue;
|
397 |
+
|
398 |
+
list($tag, $key, $val, $exp, $no_key) = array($m[1], null, null, '=', false);
|
399 |
+
if(!empty($m[2])) {$key='id'; $val=$m[2];}
|
400 |
+
if(!empty($m[3])) {$key='class'; $val=$m[3];}
|
401 |
+
if(!empty($m[4])) {$key=$m[4];}
|
402 |
+
if(!empty($m[5])) {$exp=$m[5];}
|
403 |
+
if(!empty($m[6])) {$val=$m[6];}
|
404 |
+
|
405 |
+
// convert to lowercase
|
406 |
+
if ($this->dom->lowercase) {$tag=strtolower($tag); $key=strtolower($key);}
|
407 |
+
//elements that do NOT have the specified attribute
|
408 |
+
if (isset($key[0]) && $key[0]==='!') {$key=substr($key, 1); $no_key=true;}
|
409 |
+
|
410 |
+
$result[] = array($tag, $key, $val, $exp, $no_key);
|
411 |
+
if (trim($m[7])===',') {
|
412 |
+
$selectors[] = $result;
|
413 |
+
$result = array();
|
414 |
+
}
|
415 |
+
}
|
416 |
+
if (count($result)>0)
|
417 |
+
$selectors[] = $result;
|
418 |
+
return $selectors;
|
419 |
+
}
|
420 |
+
|
421 |
+
function __get($name) {
|
422 |
+
if (isset($this->attr[$name])) return $this->attr[$name];
|
423 |
+
switch($name) {
|
424 |
+
case 'outertext': return $this->outertext();
|
425 |
+
case 'innertext': return $this->innertext();
|
426 |
+
case 'plaintext': return $this->text();
|
427 |
+
case 'xmltext': return $this->xmltext();
|
428 |
+
default: return array_key_exists($name, $this->attr);
|
429 |
+
}
|
430 |
+
}
|
431 |
+
|
432 |
+
function __set($name, $value) {
|
433 |
+
switch($name) {
|
434 |
+
case 'outertext': return $this->_[HDOM_INFO_OUTER] = $value;
|
435 |
+
case 'innertext':
|
436 |
+
if (isset($this->_[HDOM_INFO_TEXT])) return $this->_[HDOM_INFO_TEXT] = $value;
|
437 |
+
return $this->_[HDOM_INFO_INNER] = $value;
|
438 |
+
}
|
439 |
+
if (!isset($this->attr[$name])) {
|
440 |
+
$this->_[HDOM_INFO_SPACE][] = array(' ', '', '');
|
441 |
+
$this->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
|
442 |
+
}
|
443 |
+
$this->attr[$name] = $value;
|
444 |
+
}
|
445 |
+
|
446 |
+
function __isset($name) {
|
447 |
+
switch($name) {
|
448 |
+
case 'outertext': return true;
|
449 |
+
case 'innertext': return true;
|
450 |
+
case 'plaintext': return true;
|
451 |
+
}
|
452 |
+
//no value attr: nowrap, checked selected...
|
453 |
+
return (array_key_exists($name, $this->attr)) ? true : isset($this->attr[$name]);
|
454 |
+
}
|
455 |
+
|
456 |
+
function __unset($name) {
|
457 |
+
if (isset($this->attr[$name]))
|
458 |
+
unset($this->attr[$name]);
|
459 |
+
}
|
460 |
+
|
461 |
+
// camel naming conventions
|
462 |
+
function getAllAttributes() {return $this->attr;}
|
463 |
+
function getAttribute($name) {return $this->__get($name);}
|
464 |
+
function setAttribute($name, $value) {$this->__set($name, $value);}
|
465 |
+
function hasAttribute($name) {return $this->__isset($name);}
|
466 |
+
function removeAttribute($name) {$this->__set($name, null);}
|
467 |
+
function getElementById($id) {return $this->find("#$id", 0);}
|
468 |
+
function getElementsById($id, $idx=null) {return $this->find("#$id", $idx);}
|
469 |
+
function getElementByTagName($name) {return $this->find($name, 0);}
|
470 |
+
function getElementsByTagName($name, $idx=null) {return $this->find($name, $idx);}
|
471 |
+
function parentNode() {return $this->parent();}
|
472 |
+
function childNodes($idx=-1) {return $this->children($idx);}
|
473 |
+
function firstChild() {return $this->first_child();}
|
474 |
+
function lastChild() {return $this->last_child();}
|
475 |
+
function nextSibling() {return $this->next_sibling();}
|
476 |
+
function previousSibling() {return $this->prev_sibling();}
|
477 |
+
}
|
478 |
+
|
479 |
+
// simple html dom parser
|
480 |
+
// -----------------------------------------------------------------------------
|
481 |
+
class simple_html_dom {
|
482 |
+
public $root = null;
|
483 |
+
public $nodes = array();
|
484 |
+
public $callback = null;
|
485 |
+
public $lowercase = false;
|
486 |
+
protected $pos;
|
487 |
+
protected $doc;
|
488 |
+
protected $char;
|
489 |
+
protected $size;
|
490 |
+
protected $cursor;
|
491 |
+
protected $parent;
|
492 |
+
protected $noise = array();
|
493 |
+
protected $token_blank = " \t\r\n";
|
494 |
+
protected $token_equal = ' =/>';
|
495 |
+
protected $token_slash = " />\r\n\t";
|
496 |
+
protected $token_attr = ' >';
|
497 |
+
// use isset instead of in_array, performance boost about 30%...
|
498 |
+
protected $self_closing_tags = array('img'=>1, 'br'=>1, 'input'=>1, 'meta'=>1, 'link'=>1, 'hr'=>1, 'base'=>1, 'embed'=>1, 'spacer'=>1);
|
499 |
+
protected $block_tags = array('root'=>1, 'body'=>1, 'form'=>1, 'div'=>1, 'span'=>1, 'table'=>1);
|
500 |
+
protected $optional_closing_tags = array(
|
501 |
+
'tr'=>array('tr'=>1, 'td'=>1, 'th'=>1),
|
502 |
+
'th'=>array('th'=>1),
|
503 |
+
'td'=>array('td'=>1),
|
504 |
+
'li'=>array('li'=>1),
|
505 |
+
'dt'=>array('dt'=>1, 'dd'=>1),
|
506 |
+
'dd'=>array('dd'=>1, 'dt'=>1),
|
507 |
+
'dl'=>array('dd'=>1, 'dt'=>1),
|
508 |
+
'p'=>array('p'=>1),
|
509 |
+
'nobr'=>array('nobr'=>1),
|
510 |
+
);
|
511 |
+
|
512 |
+
function __construct($str=null) {
|
513 |
+
if ($str) {
|
514 |
+
if (preg_match("/^http:\/\//i",$str) || is_file($str))
|
515 |
+
$this->load_file($str);
|
516 |
+
else
|
517 |
+
$this->load($str);
|
518 |
+
}
|
519 |
+
}
|
520 |
+
|
521 |
+
function __destruct() {
|
522 |
+
$this->clear();
|
523 |
+
}
|
524 |
+
|
525 |
+
// load html from string
|
526 |
+
function load($str, $lowercase=true) {
|
527 |
+
// prepare
|
528 |
+
$this->prepare($str, $lowercase);
|
529 |
+
// strip out comments
|
530 |
+
$this->remove_noise("'<!--(.*?)-->'is");
|
531 |
+
// strip out cdata
|
532 |
+
$this->remove_noise("'<!\[CDATA\[(.*?)\]\]>'is", true);
|
533 |
+
// strip out <style> tags
|
534 |
+
$this->remove_noise("'<\s*style[^>]*[^/]>(.*?)<\s*/\s*style\s*>'is");
|
535 |
+
$this->remove_noise("'<\s*style\s*>(.*?)<\s*/\s*style\s*>'is");
|
536 |
+
// strip out <script> tags
|
537 |
+
$this->remove_noise("'<\s*script[^>]*[^/]>(.*?)<\s*/\s*script\s*>'is");
|
538 |
+
$this->remove_noise("'<\s*script\s*>(.*?)<\s*/\s*script\s*>'is");
|
539 |
+
// strip out preformatted tags
|
540 |
+
$this->remove_noise("'<\s*(?:code)[^>]*>(.*?)<\s*/\s*(?:code)\s*>'is");
|
541 |
+
// strip out server side scripts
|
542 |
+
$this->remove_noise("'(<\?)(.*?)(\?>)'s", true);
|
543 |
+
// strip smarty scripts
|
544 |
+
$this->remove_noise("'(\{\w)(.*?)(\})'s", true);
|
545 |
+
|
546 |
+
// parsing
|
547 |
+
while ($this->parse());
|
548 |
+
// end
|
549 |
+
$this->root->_[HDOM_INFO_END] = $this->cursor;
|
550 |
+
}
|
551 |
+
|
552 |
+
// load html from file
|
553 |
+
function load_file() {
|
554 |
+
$args = func_get_args();
|
555 |
+
$this->load(call_user_func_array('file_get_contents', $args), true);
|
556 |
+
}
|
557 |
+
|
558 |
+
// set callback function
|
559 |
+
function set_callback($function_name) {
|
560 |
+
$this->callback = $function_name;
|
561 |
+
}
|
562 |
+
|
563 |
+
// remove callback function
|
564 |
+
function remove_callback() {
|
565 |
+
$this->callback = null;
|
566 |
+
}
|
567 |
+
|
568 |
+
// save dom as string
|
569 |
+
function save($filepath='') {
|
570 |
+
$ret = $this->root->innertext();
|
571 |
+
if ($filepath!=='') file_put_contents($filepath, $ret);
|
572 |
+
return $ret;
|
573 |
+
}
|
574 |
+
|
575 |
+
// find dom node by css selector
|
576 |
+
function find($selector, $idx=null) {
|
577 |
+
return $this->root->find($selector, $idx);
|
578 |
+
}
|
579 |
+
|
580 |
+
// clean up memory due to php5 circular references memory leak...
|
581 |
+
function clear() {
|
582 |
+
foreach($this->nodes as $n) {$n->clear(); $n = null;}
|
583 |
+
if (isset($this->parent)) {$this->parent->clear(); unset($this->parent);}
|
584 |
+
if (isset($this->root)) {$this->root->clear(); unset($this->root);}
|
585 |
+
unset($this->doc);
|
586 |
+
unset($this->noise);
|
587 |
+
}
|
588 |
+
|
589 |
+
function dump($show_attr=true) {
|
590 |
+
$this->root->dump($show_attr);
|
591 |
+
}
|
592 |
+
|
593 |
+
// prepare HTML data and init everything
|
594 |
+
protected function prepare($str, $lowercase=true) {
|
595 |
+
$this->clear();
|
596 |
+
$this->doc = $str;
|
597 |
+
$this->pos = 0;
|
598 |
+
$this->cursor = 1;
|
599 |
+
$this->noise = array();
|
600 |
+
$this->nodes = array();
|
601 |
+
$this->lowercase = $lowercase;
|
602 |
+
$this->root = new simple_html_dom_node($this);
|
603 |
+
$this->root->tag = 'root';
|
604 |
+
$this->root->_[HDOM_INFO_BEGIN] = -1;
|
605 |
+
$this->root->nodetype = HDOM_TYPE_ROOT;
|
606 |
+
$this->parent = $this->root;
|
607 |
+
// set the length of content
|
608 |
+
$this->size = strlen($str);
|
609 |
+
if ($this->size>0) $this->char = $this->doc[0];
|
610 |
+
}
|
611 |
+
|
612 |
+
// parse html content
|
613 |
+
protected function parse() {
|
614 |
+
if (($s = $this->copy_until_char('<'))==='')
|
615 |
+
return $this->read_tag();
|
616 |
+
|
617 |
+
// text
|
618 |
+
$node = new simple_html_dom_node($this);
|
619 |
+
++$this->cursor;
|
620 |
+
$node->_[HDOM_INFO_TEXT] = $s;
|
621 |
+
$this->link_nodes($node, false);
|
622 |
+
return true;
|
623 |
+
}
|
624 |
+
|
625 |
+
// read tag info
|
626 |
+
protected function read_tag() {
|
627 |
+
if ($this->char!=='<') {
|
628 |
+
$this->root->_[HDOM_INFO_END] = $this->cursor;
|
629 |
+
return false;
|
630 |
+
}
|
631 |
+
$begin_tag_pos = $this->pos;
|
632 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
633 |
+
|
634 |
+
// end tag
|
635 |
+
if ($this->char==='/') {
|
636 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
637 |
+
$this->skip($this->token_blank_t);
|
638 |
+
$tag = $this->copy_until_char('>');
|
639 |
+
|
640 |
+
// skip attributes in end tag
|
641 |
+
if (($pos = strpos($tag, ' '))!==false)
|
642 |
+
$tag = substr($tag, 0, $pos);
|
643 |
+
|
644 |
+
$parent_lower = strtolower($this->parent->tag);
|
645 |
+
$tag_lower = strtolower($tag);
|
646 |
+
|
647 |
+
if ($parent_lower!==$tag_lower) {
|
648 |
+
if (isset($this->optional_closing_tags[$parent_lower]) && isset($this->block_tags[$tag_lower])) {
|
649 |
+
$this->parent->_[HDOM_INFO_END] = 0;
|
650 |
+
$org_parent = $this->parent;
|
651 |
+
|
652 |
+
while (($this->parent->parent) && strtolower($this->parent->tag)!==$tag_lower)
|
653 |
+
$this->parent = $this->parent->parent;
|
654 |
+
|
655 |
+
if (strtolower($this->parent->tag)!==$tag_lower) {
|
656 |
+
$this->parent = $org_parent; // restore origonal parent
|
657 |
+
if ($this->parent->parent) $this->parent = $this->parent->parent;
|
658 |
+
$this->parent->_[HDOM_INFO_END] = $this->cursor;
|
659 |
+
return $this->as_text_node($tag);
|
660 |
+
}
|
661 |
+
}
|
662 |
+
else if (($this->parent->parent) && isset($this->block_tags[$tag_lower])) {
|
663 |
+
$this->parent->_[HDOM_INFO_END] = 0;
|
664 |
+
$org_parent = $this->parent;
|
665 |
+
|
666 |
+
while (($this->parent->parent) && strtolower($this->parent->tag)!==$tag_lower)
|
667 |
+
$this->parent = $this->parent->parent;
|
668 |
+
|
669 |
+
if (strtolower($this->parent->tag)!==$tag_lower) {
|
670 |
+
$this->parent = $org_parent; // restore origonal parent
|
671 |
+
$this->parent->_[HDOM_INFO_END] = $this->cursor;
|
672 |
+
return $this->as_text_node($tag);
|
673 |
+
}
|
674 |
+
}
|
675 |
+
else if (($this->parent->parent) && strtolower($this->parent->parent->tag)===$tag_lower) {
|
676 |
+
$this->parent->_[HDOM_INFO_END] = 0;
|
677 |
+
$this->parent = $this->parent->parent;
|
678 |
+
}
|
679 |
+
else
|
680 |
+
return $this->as_text_node($tag);
|
681 |
+
}
|
682 |
+
|
683 |
+
$this->parent->_[HDOM_INFO_END] = $this->cursor;
|
684 |
+
if ($this->parent->parent) $this->parent = $this->parent->parent;
|
685 |
+
|
686 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
687 |
+
return true;
|
688 |
+
}
|
689 |
+
|
690 |
+
$node = new simple_html_dom_node($this);
|
691 |
+
$node->_[HDOM_INFO_BEGIN] = $this->cursor;
|
692 |
+
++$this->cursor;
|
693 |
+
$tag = $this->copy_until($this->token_slash);
|
694 |
+
|
695 |
+
// doctype, cdata & comments...
|
696 |
+
if (isset($tag[0]) && $tag[0]==='!') {
|
697 |
+
$node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until_char('>');
|
698 |
+
|
699 |
+
if (isset($tag[2]) && $tag[1]==='-' && $tag[2]==='-') {
|
700 |
+
$node->nodetype = HDOM_TYPE_COMMENT;
|
701 |
+
$node->tag = 'comment';
|
702 |
+
} else {
|
703 |
+
$node->nodetype = HDOM_TYPE_UNKNOWN;
|
704 |
+
$node->tag = 'unknown';
|
705 |
+
}
|
706 |
+
|
707 |
+
if ($this->char==='>') $node->_[HDOM_INFO_TEXT].='>';
|
708 |
+
$this->link_nodes($node, true);
|
709 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
710 |
+
return true;
|
711 |
+
}
|
712 |
+
|
713 |
+
// text
|
714 |
+
if ($pos=strpos($tag, '<')!==false) {
|
715 |
+
$tag = '<' . substr($tag, 0, -1);
|
716 |
+
$node->_[HDOM_INFO_TEXT] = $tag;
|
717 |
+
$this->link_nodes($node, false);
|
718 |
+
$this->char = $this->doc[--$this->pos]; // prev
|
719 |
+
return true;
|
720 |
+
}
|
721 |
+
|
722 |
+
if (!preg_match("/^[\w-:]+$/", $tag)) {
|
723 |
+
$node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until('<>');
|
724 |
+
if ($this->char==='<') {
|
725 |
+
$this->link_nodes($node, false);
|
726 |
+
return true;
|
727 |
+
}
|
728 |
+
|
729 |
+
if ($this->char==='>') $node->_[HDOM_INFO_TEXT].='>';
|
730 |
+
$this->link_nodes($node, false);
|
731 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
732 |
+
return true;
|
733 |
+
}
|
734 |
+
|
735 |
+
// begin tag
|
736 |
+
$node->nodetype = HDOM_TYPE_ELEMENT;
|
737 |
+
$tag_lower = strtolower($tag);
|
738 |
+
$node->tag = ($this->lowercase) ? $tag_lower : $tag;
|
739 |
+
|
740 |
+
// handle optional closing tags
|
741 |
+
if (isset($this->optional_closing_tags[$tag_lower]) ) {
|
742 |
+
while (isset($this->optional_closing_tags[$tag_lower][strtolower($this->parent->tag)])) {
|
743 |
+
$this->parent->_[HDOM_INFO_END] = 0;
|
744 |
+
$this->parent = $this->parent->parent;
|
745 |
+
}
|
746 |
+
$node->parent = $this->parent;
|
747 |
+
}
|
748 |
+
|
749 |
+
$guard = 0; // prevent infinity loop
|
750 |
+
$space = array($this->copy_skip($this->token_blank), '', '');
|
751 |
+
|
752 |
+
// attributes
|
753 |
+
do {
|
754 |
+
if ($this->char!==null && $space[0]==='') break;
|
755 |
+
$name = $this->copy_until($this->token_equal);
|
756 |
+
if($guard===$this->pos) {
|
757 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
758 |
+
continue;
|
759 |
+
}
|
760 |
+
$guard = $this->pos;
|
761 |
+
|
762 |
+
// handle endless '<'
|
763 |
+
if($this->pos>=$this->size-1 && $this->char!=='>') {
|
764 |
+
$node->nodetype = HDOM_TYPE_TEXT;
|
765 |
+
$node->_[HDOM_INFO_END] = 0;
|
766 |
+
$node->_[HDOM_INFO_TEXT] = '<'.$tag . $space[0] . $name;
|
767 |
+
$node->tag = 'text';
|
768 |
+
$this->link_nodes($node, false);
|
769 |
+
return true;
|
770 |
+
}
|
771 |
+
|
772 |
+
// handle mismatch '<'
|
773 |
+
if($this->doc[$this->pos-1]=='<') {
|
774 |
+
$node->nodetype = HDOM_TYPE_TEXT;
|
775 |
+
$node->tag = 'text';
|
776 |
+
$node->attr = array();
|
777 |
+
$node->_[HDOM_INFO_END] = 0;
|
778 |
+
$node->_[HDOM_INFO_TEXT] = substr($this->doc, $begin_tag_pos, $this->pos-$begin_tag_pos-1);
|
779 |
+
$this->pos -= 2;
|
780 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
781 |
+
$this->link_nodes($node, false);
|
782 |
+
return true;
|
783 |
+
}
|
784 |
+
|
785 |
+
if ($name!=='/' && $name!=='') {
|
786 |
+
$space[1] = $this->copy_skip($this->token_blank);
|
787 |
+
$name = $this->restore_noise($name);
|
788 |
+
if ($this->lowercase) $name = strtolower($name);
|
789 |
+
if ($this->char==='=') {
|
790 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
791 |
+
$this->parse_attr($node, $name, $space);
|
792 |
+
}
|
793 |
+
else {
|
794 |
+
//no value attr: nowrap, checked selected...
|
795 |
+
$node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO;
|
796 |
+
$node->attr[$name] = true;
|
797 |
+
if ($this->char!='>') $this->char = $this->doc[--$this->pos]; // prev
|
798 |
+
}
|
799 |
+
$node->_[HDOM_INFO_SPACE][] = $space;
|
800 |
+
$space = array($this->copy_skip($this->token_blank), '', '');
|
801 |
+
}
|
802 |
+
else
|
803 |
+
break;
|
804 |
+
} while($this->char!=='>' && $this->char!=='/');
|
805 |
+
|
806 |
+
$this->link_nodes($node, true);
|
807 |
+
$node->_[HDOM_INFO_ENDSPACE] = $space[0];
|
808 |
+
|
809 |
+
// check self closing
|
810 |
+
if ($this->copy_until_char_escape('>')==='/') {
|
811 |
+
$node->_[HDOM_INFO_ENDSPACE] .= '/';
|
812 |
+
$node->_[HDOM_INFO_END] = 0;
|
813 |
+
}
|
814 |
+
else {
|
815 |
+
// reset parent
|
816 |
+
if (!isset($this->self_closing_tags[strtolower($node->tag)])) $this->parent = $node;
|
817 |
+
}
|
818 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
819 |
+
return true;
|
820 |
+
}
|
821 |
+
|
822 |
+
// parse attributes
|
823 |
+
protected function parse_attr($node, $name, &$space) {
|
824 |
+
$space[2] = $this->copy_skip($this->token_blank);
|
825 |
+
switch($this->char) {
|
826 |
+
case '"':
|
827 |
+
$node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
|
828 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
829 |
+
$node->attr[$name] = $this->restore_noise($this->copy_until_char_escape('"'));
|
830 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
831 |
+
break;
|
832 |
+
case '\'':
|
833 |
+
$node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_SINGLE;
|
834 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
835 |
+
$node->attr[$name] = $this->restore_noise($this->copy_until_char_escape('\''));
|
836 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
837 |
+
break;
|
838 |
+
default:
|
839 |
+
$node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO;
|
840 |
+
$node->attr[$name] = $this->restore_noise($this->copy_until($this->token_attr));
|
841 |
+
}
|
842 |
+
}
|
843 |
+
|
844 |
+
// link node's parent
|
845 |
+
protected function link_nodes(&$node, $is_child) {
|
846 |
+
$node->parent = $this->parent;
|
847 |
+
$this->parent->nodes[] = $node;
|
848 |
+
if ($is_child)
|
849 |
+
$this->parent->children[] = $node;
|
850 |
+
}
|
851 |
+
|
852 |
+
// as a text node
|
853 |
+
protected function as_text_node($tag) {
|
854 |
+
$node = new simple_html_dom_node($this);
|
855 |
+
++$this->cursor;
|
856 |
+
$node->_[HDOM_INFO_TEXT] = '</' . $tag . '>';
|
857 |
+
$this->link_nodes($node, false);
|
858 |
+
$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
859 |
+
return true;
|
860 |
+
}
|
861 |
+
|
862 |
+
protected function skip($chars) {
|
863 |
+
$this->pos += strspn($this->doc, $chars, $this->pos);
|
864 |
+
$this->char = ($this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
865 |
+
}
|
866 |
+
|
867 |
+
protected function copy_skip($chars) {
|
868 |
+
$pos = $this->pos;
|
869 |
+
$len = strspn($this->doc, $chars, $pos);
|
870 |
+
$this->pos += $len;
|
871 |
+
$this->char = ($this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
872 |
+
if ($len===0) return '';
|
873 |
+
return substr($this->doc, $pos, $len);
|
874 |
+
}
|
875 |
+
|
876 |
+
protected function copy_until($chars) {
|
877 |
+
$pos = $this->pos;
|
878 |
+
$len = strcspn($this->doc, $chars, $pos);
|
879 |
+
$this->pos += $len;
|
880 |
+
$this->char = ($this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
|
881 |
+
return substr($this->doc, $pos, $len);
|
882 |
+
}
|
883 |
+
|
884 |
+
protected function copy_until_char($char) {
|
885 |
+
if ($this->char===null) return '';
|
886 |
+
|
887 |
+
if (($pos = strpos($this->doc, $char, $this->pos))===false) {
|
888 |
+
$ret = substr($this->doc, $this->pos, $this->size-$this->pos);
|
889 |
+
$this->char = null;
|
890 |
+
$this->pos = $this->size;
|
891 |
+
return $ret;
|
892 |
+
}
|
893 |
+
|
894 |
+
if ($pos===$this->pos) return '';
|
895 |
+
$pos_old = $this->pos;
|
896 |
+
$this->char = $this->doc[$pos];
|
897 |
+
$this->pos = $pos;
|
898 |
+
return substr($this->doc, $pos_old, $pos-$pos_old);
|
899 |
+
}
|
900 |
+
|
901 |
+
protected function copy_until_char_escape($char) {
|
902 |
+
if ($this->char===null) return '';
|
903 |
+
|
904 |
+
$start = $this->pos;
|
905 |
+
while(1) {
|
906 |
+
if (($pos = strpos($this->doc, $char, $start))===false) {
|
907 |
+
$ret = substr($this->doc, $this->pos, $this->size-$this->pos);
|
908 |
+
$this->char = null;
|
909 |
+
$this->pos = $this->size;
|
910 |
+
return $ret;
|
911 |
+
}
|
912 |
+
|
913 |
+
if ($pos===$this->pos) return '';
|
914 |
+
|
915 |
+
if ($this->doc[$pos-1]==='\\') {
|
916 |
+
$start = $pos+1;
|
917 |
+
continue;
|
918 |
+
}
|
919 |
+
|
920 |
+
$pos_old = $this->pos;
|
921 |
+
$this->char = $this->doc[$pos];
|
922 |
+
$this->pos = $pos;
|
923 |
+
return substr($this->doc, $pos_old, $pos-$pos_old);
|
924 |
+
}
|
925 |
+
}
|
926 |
+
|
927 |
+
// remove noise from html content
|
928 |
+
protected function remove_noise($pattern, $remove_tag=false) {
|
929 |
+
$count = preg_match_all($pattern, $this->doc, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
|
930 |
+
|
931 |
+
for ($i=$count-1; $i>-1; --$i) {
|
932 |
+
$key = '___noise___'.sprintf('% 3d', count($this->noise)+100);
|
933 |
+
$idx = ($remove_tag) ? 0 : 1;
|
934 |
+
$this->noise[$key] = $matches[$i][$idx][0];
|
935 |
+
$this->doc = substr_replace($this->doc, $key, $matches[$i][$idx][1], strlen($matches[$i][$idx][0]));
|
936 |
+
}
|
937 |
+
|
938 |
+
// reset the length of content
|
939 |
+
$this->size = strlen($this->doc);
|
940 |
+
if ($this->size>0) $this->char = $this->doc[0];
|
941 |
+
}
|
942 |
+
|
943 |
+
// restore noise to html content
|
944 |
+
function restore_noise($text) {
|
945 |
+
while(($pos=strpos($text, '___noise___'))!==false) {
|
946 |
+
$key = '___noise___'.$text[$pos+11].$text[$pos+12].$text[$pos+13];
|
947 |
+
if (isset($this->noise[$key]))
|
948 |
+
$text = substr($text, 0, $pos).$this->noise[$key].substr($text, $pos+14);
|
949 |
+
}
|
950 |
+
return $text;
|
951 |
+
}
|
952 |
+
|
953 |
+
function __toString() {
|
954 |
+
return $this->root->innertext();
|
955 |
+
}
|
956 |
+
|
957 |
+
function __get($name) {
|
958 |
+
switch($name) {
|
959 |
+
case 'outertext': return $this->root->innertext();
|
960 |
+
case 'innertext': return $this->root->innertext();
|
961 |
+
case 'plaintext': return $this->root->text();
|
962 |
+
}
|
963 |
+
}
|
964 |
+
|
965 |
+
// camel naming conventions
|
966 |
+
function childNodes($idx=-1) {return $this->root->childNodes($idx);}
|
967 |
+
function firstChild() {return $this->root->first_child();}
|
968 |
+
function lastChild() {return $this->root->last_child();}
|
969 |
+
function getElementById($id) {return $this->find("#$id", 0);}
|
970 |
+
function getElementsById($id, $idx=null) {return $this->find("#$id", $idx);}
|
971 |
+
function getElementByTagName($name) {return $this->find($name, 0);}
|
972 |
+
function getElementsByTagName($name, $idx=-1) {return $this->find($name, $idx);}
|
973 |
+
function loadFile() {$args = func_get_args();$this->load(call_user_func_array('file_get_contents', $args), true);}
|
974 |
+
}
|
975 |
+
?>
|
js/transposh.js
CHANGED
@@ -1 +1 @@
|
|
1 |
-
function getgt(){jQuery(":button:contains('Suggest - Google')").attr("disabled","disabled").addClass("ui-state-disabled");google.language.translate(jQuery("#"+transposh_params.prefix+"original").val(),"",transposh_params.lang,function(a){if(!a.error){jQuery("#"+transposh_params.prefix+"translation").val(jQuery("<div>"+a.translation+"</div>").text()).keyup()}})}var done_p=0;var togo=0;function ajax_translate(c,b,a){jQuery.ajax({type:"POST",url:transposh_params.post_url,data:{token:jQuery("#"+transposh_params.prefix+a).attr("token"),translation:c,lang:transposh_params.lang,source:b,translation_posted:"1"},success:function(e){fix_page(c,b,a);if(transposh_params.progress){var d=jQuery("#"+transposh_params.prefix+a).attr("token");done_p+=jQuery("*[token='"+d+"']").size();if(togo>4){jQuery("#progress_bar2").progressbar("value",done_p/togo*100)}}},error:function(d){if(b==0){alert("Error !!! failed to translate.\n\nServer's message: "+d.statusText)}}})}function fix_page(e,d,c){var b=jQuery("#"+transposh_params.prefix+c).attr("token");var a=e;if(jQuery.trim(e).length===0){a=jQuery("#"+transposh_params.prefix+c).attr("orig")}jQuery("*[token='"+b+"']").html(a).each(function(
|
1 |
+
function getgt(){jQuery(":button:contains('Suggest - Google')").attr("disabled","disabled").addClass("ui-state-disabled");google.language.translate(jQuery("#"+transposh_params.prefix+"original").val(),"",transposh_params.lang,function(a){if(!a.error){jQuery("#"+transposh_params.prefix+"translation").val(jQuery("<div>"+a.translation+"</div>").text()).keyup()}})}var done_p=0;var togo=0;function ajax_translate(c,b,a){jQuery.ajax({type:"POST",url:transposh_params.post_url,data:{token:jQuery("#"+transposh_params.prefix+a).attr("token"),translation:c,lang:transposh_params.lang,source:b,translation_posted:"1"},success:function(e){fix_page(c,b,a);if(transposh_params.progress){var d=jQuery("#"+transposh_params.prefix+a).attr("token");done_p+=jQuery("*[token='"+d+"']").size();if(togo>4){jQuery("#progress_bar2").progressbar("value",done_p/togo*100)}}},error:function(d){if(b==0){alert("Error !!! failed to translate.\n\nServer's message: "+d.statusText)}}})}function fix_page(e,d,c){var b=jQuery("#"+transposh_params.prefix+c).attr("token");var a=e;if(jQuery.trim(e).length===0){a=jQuery("#"+transposh_params.prefix+c).attr("orig")}jQuery("*[token='"+b+"'][hidden!='y']").html(a).each(function(g){var h=jQuery(this).attr("id").substr(jQuery(this).attr("id").lastIndexOf("_")+1);jQuery("#"+transposh_params.prefix+h).attr("source",d);var f=jQuery("#"+transposh_params.prefix+"img_"+h);f.removeClass("tr-icon-yellow").removeClass("tr-icon-green");if(jQuery.trim(e).length!==0){if(d==1){f.addClass("tr-icon-yellow")}else{f.addClass("tr-icon-green")}}});jQuery("*[token='"+b+"'][hidden='y']").attr("trans",a).each(function(g){var h=jQuery(this).attr("id").substr(jQuery(this).attr("id").lastIndexOf("_")+1);jQuery("#"+transposh_params.prefix+h).attr("source",d);var f=jQuery("#"+transposh_params.prefix+"img_"+h);f.removeClass("tr-icon-yellow").removeClass("tr-icon-green");if(jQuery.trim(e).length!==0){if(d==1){f.addClass("tr-icon-yellow")}else{f.addClass("tr-icon-green")}}})}function do_auto_translate(){if(transposh_params.progress){togo=jQuery("."+transposh_params.prefix+'[source=""]').size();if(togo>4){jQuery("#"+transposh_params.prefix+"credit").append('<div style="float: left;width: 90%;height: 10px" id="progress_bar"/><div style="margin-bottom:10px;float:left;width: 90%;height: 10px" id="progress_bar2"/>');jQuery("#progress_bar").progressbar({value:0});jQuery("#progress_bar2").progressbar({value:0});jQuery("#progress_bar2 > div").css({background:"#28F828",border:"#08A908 1px solid"})}var a=0}var b=new Array();jQuery("."+transposh_params.prefix+'[source=""]').each(function(d){var c=jQuery(this).attr("id");var e=jQuery(this).attr("orig");if(e==undefined){e=jQuery(this).html()}if(!(b[e]==1)){b[e]=1;google.language.translate(e,"",transposh_params.lang,function(f){if(!f.error){var g=c.substr(c.lastIndexOf("_")+1);fix_page(jQuery("<div>"+f.translation+"</div>").text(),1,g);ajax_translate(jQuery("<div>"+f.translation+"</div>").text(),1,g);if(transposh_params.progress){a=togo-jQuery("."+transposh_params.prefix+'[source=""]').size();if(togo>4){jQuery("#progress_bar").progressbar("value",a/togo*100)}}}})}})}function confirm_close(){jQuery('<div id="dial" title="Close without saving?"><p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>You have made a change to the translation. Are you sure you want to discard it?</p></div>').appendTo("body").dialog({bgiframe:true,resizable:false,height:140,modal:true,overlay:{backgroundColor:"#000",opacity:0.5},buttons:{Discard:function(){jQuery("#"+transposh_params.prefix+"translation").data("edit",{changed:false});jQuery(this).dialog("close");jQuery("#"+transposh_params.prefix+"d-tabs").dialog("close")},Cancel:function(){jQuery(this).dialog("close")}}})}function translate_dialog(b){jQuery("#"+transposh_params.prefix+"d-tabs").remove();jQuery('<div id="'+transposh_params.prefix+'d-tabs" title="Edit Translation"/>').appendTo("body");jQuery("#"+transposh_params.prefix+"d-tabs").append("<ul/>").tabs({cache:true}).tabs("add","#"+transposh_params.prefix+"d-tabs-1","Translate").tabs("add",transposh_params.post_url+"?tr_token_hist="+jQuery("#"+transposh_params.prefix+b).attr("token")+"&lang="+transposh_params.lang,"History").css("text-align","left").css("padding",0).bind("tabsload",function(d,e){jQuery("table",e.panel).addClass("ui-widget ui-widget-content").css({width:"95%",padding:"0"});jQuery("table thead tr",e.panel).addClass("ui-widget-header");jQuery("table tbody td[source='1']",e.panel).append('<img size="16x16" src="'+transposh_params.post_url+'?tp_gif=y" title="computer" style="display: inline; margin-right: 0.3em;" class="ui-icon ui-icon-gear"/>');jQuery("table tbody td[source='0']",e.panel).append('<img size="16x16" src="'+transposh_params.post_url+'?tp_gif=y" title="human" style="display: inline; margin-right: 0.3em;" class="ui-icon ui-icon-person"/>')}).bind("tabsselect",function(d,e){if(jQuery(e.tab).text()=="Translate"){jQuery("#"+transposh_params.prefix+"d-tabs").dialog("option","buttons",a)}else{jQuery("#"+transposh_params.prefix+"d-tabs").dialog("option","buttons",c)}}).bind("dialogbeforeclose",function(d,e){if(jQuery("#"+transposh_params.prefix+"translation").data("edit").changed){confirm_close();return false}return true});jQuery("#"+transposh_params.prefix+"d-tabs li").css("list-style-type","none").css("list-style-position","outside");jQuery("#"+transposh_params.prefix+"d-tabs-1").append('<form id="'+transposh_params.prefix+'form"><fieldset><label for="original">Original Text</label><textarea cols="80" row="3" name="original" id="'+transposh_params.prefix+'original" class="text ui-widget-content ui-corner-all" readonly="y"/><label for="translation">Translate To</label><textarea cols="80" row="3" name="translation" id="'+transposh_params.prefix+'translation" value="" class="text ui-widget-content ui-corner-all"/></fieldset></form>');jQuery("#"+transposh_params.prefix+"d-tabs-1 label").css("display","block");jQuery("#"+transposh_params.prefix+"d-tabs-1 textarea.text").css({"margin-bottom":"12px",width:"95%",padding:".4em"});jQuery("#"+transposh_params.prefix+"original").val(jQuery("#"+transposh_params.prefix+b).attr("orig"));jQuery("#"+transposh_params.prefix+"translation").val(jQuery("#"+transposh_params.prefix+b).html());if(jQuery("#"+transposh_params.prefix+b).attr("trans")){jQuery("#"+transposh_params.prefix+"translation").val(jQuery("#"+transposh_params.prefix+b).attr("trans"))}jQuery("#"+transposh_params.prefix+"translation").data("edit",{changed:false});jQuery("#"+transposh_params.prefix+"translation").keyup(function(d){if(jQuery("#"+transposh_params.prefix+b).text()!=jQuery(this).val()){jQuery(this).css("background","yellow");jQuery(this).data("edit",{changed:true})}else{jQuery(this).css("background","");jQuery(this).data("edit",{changed:false})}});var a;if(google.language.isTranslatable(transposh_params.lang)||"he|zh-tw|pt".indexOf(transposh_params.lang)>-1){a={"Suggest - Google":function(){getgt()},Ok:function(){var d=jQuery("#"+transposh_params.prefix+"translation").val();if(jQuery("#"+transposh_params.prefix+"translation").data("edit").changed){ajax_translate(d,0,b);jQuery("#"+transposh_params.prefix+"translation").data("edit",{changed:false})}jQuery(this).dialog("close")}}}else{a={Ok:function(){var d=jQuery("#"+transposh_params.prefix+"translation").val();if(jQuery("#"+transposh_params.prefix+"translation").data("edit").changed){ajax_translate(d,0,b);jQuery("#"+transposh_params.prefix+"translation").data("edit",{changed:false})}jQuery(this).dialog("close")}}}var c={Close:function(){jQuery(this).dialog("close")}};jQuery("#"+transposh_params.prefix+"d-tabs").dialog({bgiframe:true,modal:true,width:500,buttons:a})}jQuery.noConflict();var transposh_params=new Array();jQuery("script[src*='transposh.js']").each(function(a){var e=unescape(this.src.substring(this.src.indexOf("?")+1));var d=e.split("&");for(var c=0;c<d.length;c++){var g=d[c].indexOf("=");if(g>0){var b=d[c].substring(0,g);var f=d[c].substring(g+1);transposh_params[b]=f}}});google.load("language","1");jQuery(document).ready(function(){if(typeof(jQuery().progressbar)!="undefined"){transposh_params.progress=true}if(google.language.isTranslatable(transposh_params.lang)||"he|zh-tw|pt".indexOf(transposh_params.lang)>-1){do_auto_translate()}if(transposh_params.edit){jQuery("."+transposh_params.prefix).each(function(c){var a=jQuery(this).attr("id").substr(jQuery(this).attr("id").lastIndexOf("_")+1);jQuery(this).after('<img id="'+transposh_params.prefix+"img_"+a+'" class="tr-icon" size="12x12" title="'+jQuery(this).attr("orig")+'" src="'+transposh_params.post_url+'?tp_gif=y"/>');var b=jQuery("#"+transposh_params.prefix+"img_"+a);b.click(function(){translate_dialog(a);return false}).css({border:"0px",margin:"1px",padding:"0px"});if(jQuery(this).attr("source")=="1"){b.addClass("tr-icon-yellow")}else{if(jQuery(this).attr("source")=="0"){b.addClass("tr-icon-green")}}if(jQuery(this).attr("hidden")=="y"){b.css({opacity:"0.6"})}})}});
|
readme.txt
CHANGED
@@ -4,7 +4,7 @@ Donate link: http://transposh.org/
|
|
4 |
Tags: translation, widget, filter, bilingual, multilingual, transposh, language, crowdsourcing, context, wiki, RTL, Hebrew, Spanish, French, Russian, English, Arabic, Portuguese
|
5 |
Requires at least: 2.7
|
6 |
Tested up to: 2.7.1
|
7 |
-
Stable tag: 0.
|
8 |
|
9 |
Transposh filter allows in context quick translation of websites, it allows you to crowd-source the translation to your users
|
10 |
|
@@ -82,6 +82,12 @@ Please be reminded of the following “rules”
|
|
82 |
|
83 |
Also - please take extra care to validate your html, adding extra tags that are unclosed in the template may lead to our parser breaking. Use the w3c validator service for more details. If everything is setup correctly and still nothing happens, please contact us.
|
84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
== Screenshots ==
|
86 |
|
87 |
1. This is a screen shot of a site using Transposh widget on the sidebar
|
@@ -91,6 +97,12 @@ Also - please take extra care to validate your html, adding extra tags that are
|
|
91 |
5. Widget style selection
|
92 |
|
93 |
== Release notes ==
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
* 2009/05/07 - 0.1.7
|
95 |
* Fix issues with IIS/Windows/Non standard installations (thanks [Fabrizio](http://www.sulmare.it/))
|
96 |
* Fixed namespace conflict with more plugins (For example - Lazyest Gallery)
|
4 |
Tags: translation, widget, filter, bilingual, multilingual, transposh, language, crowdsourcing, context, wiki, RTL, Hebrew, Spanish, French, Russian, English, Arabic, Portuguese
|
5 |
Requires at least: 2.7
|
6 |
Tested up to: 2.7.1
|
7 |
+
Stable tag: 0.2.0
|
8 |
|
9 |
Transposh filter allows in context quick translation of websites, it allows you to crowd-source the translation to your users
|
10 |
|
82 |
|
83 |
Also - please take extra care to validate your html, adding extra tags that are unclosed in the template may lead to our parser breaking. Use the w3c validator service for more details. If everything is setup correctly and still nothing happens, please contact us.
|
84 |
|
85 |
+
= How can I add the plugin interface without using the sidebar widget? =
|
86 |
+
|
87 |
+
Just add the following line to your template:
|
88 |
+
|
89 |
+
<?php if(function_exists("transposh_widget")) { transposh_widget(array()); } ?>
|
90 |
+
|
91 |
== Screenshots ==
|
92 |
|
93 |
1. This is a screen shot of a site using Transposh widget on the sidebar
|
97 |
5. Widget style selection
|
98 |
|
99 |
== Release notes ==
|
100 |
+
* 2009/05/18 - 0.2.0
|
101 |
+
* Faster parser - 50% faster parsing than previous engine (thanks [Simple Html DOM](http://simplehtmldom.sourceforge.net/))
|
102 |
+
* Hidden elements translation (mainly tooltips specified by title attribute)
|
103 |
+
* Make sure viewable languages are translateable
|
104 |
+
* Simplify setting page
|
105 |
+
* Fixed various bugs (thanks [Mike](http://www.nostate.com/))
|
106 |
* 2009/05/07 - 0.1.7
|
107 |
* Fix issues with IIS/Windows/Non standard installations (thanks [Fabrizio](http://www.sulmare.it/))
|
108 |
* Fixed namespace conflict with more plugins (For example - Lazyest Gallery)
|
transposh.php
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
Plugin URI: http://transposh.org/
|
5 |
Description: Translation filter for WordPress, After enabling please set languages at the <a href="options-general.php?page=Transposh">the options page</a> Want to help? visit our development site at <a href="http://trac.transposh.org/">trac.transposh.org</a>.
|
6 |
Author: Team Transposh
|
7 |
-
Version: 0.
|
8 |
Author URI: http://transposh.org/
|
9 |
License: GPL (http://www.gnu.org/licenses/gpl.txt)
|
10 |
*/
|
@@ -35,50 +35,52 @@ require_once("transposh_admin.php");
|
|
35 |
$admin_msg;
|
36 |
|
37 |
/*
|
38 |
-
* Called when the buffer containing the original page is
|
39 |
* translation process.
|
40 |
*/
|
41 |
function process_page(&$buffer) {
|
42 |
|
43 |
-
global $wp_query, $
|
44 |
|
|
|
45 |
$start_time = microtime(TRUE);
|
46 |
|
|
|
47 |
if (!isset($wp_query->query_vars[LANG_PARAM]))
|
48 |
{
|
49 |
-
//No language code - avoid further processing.
|
50 |
return $buffer;
|
51 |
-
|
52 |
}
|
53 |
|
54 |
$lang = $wp_query->query_vars[LANG_PARAM];
|
55 |
$default_lang = get_default_lang();
|
|
|
56 |
if($lang == $default_lang)
|
57 |
{
|
58 |
-
//Don't translate the default language
|
59 |
|
60 |
return $buffer;
|
61 |
}
|
62 |
|
63 |
-
$page = $buffer;
|
64 |
-
|
65 |
if (($wp_query->query_vars[EDIT_PARAM] == "1" || $wp_query->query_vars[EDIT_PARAM] == "true") &&
|
66 |
is_editing_permitted())
|
67 |
{
|
68 |
$is_edit_mode = TRUE;
|
69 |
}
|
70 |
|
71 |
-
|
72 |
|
73 |
//translate the entire page
|
74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
|
76 |
$end_time = microtime(TRUE);
|
77 |
-
|
78 |
|
79 |
|
80 |
-
|
81 |
-
return (strlen($tr_page) > 0 ? $tr_page : $page);
|
82 |
}
|
83 |
|
84 |
/*
|
@@ -355,7 +357,7 @@ function add_transposh_css() {
|
|
355 |
return;
|
356 |
}
|
357 |
//include the transposh.css
|
358 |
-
wp_enqueue_style("transposh","$tr_plugin_url/css/transposh.css",array(),'0.
|
359 |
wp_enqueue_style("jquery","http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.1/themes/ui-lightness/jquery-ui.css",array(),'1.0');
|
360 |
|
361 |
}
|
@@ -399,7 +401,7 @@ function add_transposh_js() {
|
|
399 |
wp_deregister_script('jquery');
|
400 |
wp_enqueue_script("jquery","http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js",array(),'1.3.2');
|
401 |
wp_enqueue_script("google","http://www.google.com/jsapi",array(),'1');
|
402 |
-
wp_enqueue_script("transposh","$tr_plugin_url/js/transposh.js?post_url=$post_url{$edit_mode}&lang={$lang}&prefix=".SPAN_PREFIX,array("jquery"),'0.
|
403 |
}
|
404 |
}
|
405 |
|
@@ -477,21 +479,32 @@ function is_auto_translate_permitted()
|
|
477 |
* @param $href
|
478 |
* @return TRUE if parameters should be used instead of rewriting as a permalink
|
479 |
*/
|
480 |
-
function
|
481 |
{
|
|
|
482 |
$use_params = FALSE;
|
|
|
483 |
|
484 |
-
|
485 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
486 |
if(stripos($href, '/wp-admin') !== FALSE ||
|
487 |
stripos($href, '/wp-content') !== FALSE ||
|
488 |
stripos($href, '/wp-login') !== FALSE ||
|
489 |
stripos($href, '/.php') !== FALSE)
|
490 |
{
|
491 |
-
$
|
492 |
}
|
|
|
493 |
|
494 |
-
|
|
|
|
|
495 |
}
|
496 |
|
497 |
//Register callbacks
|
4 |
Plugin URI: http://transposh.org/
|
5 |
Description: Translation filter for WordPress, After enabling please set languages at the <a href="options-general.php?page=Transposh">the options page</a> Want to help? visit our development site at <a href="http://trac.transposh.org/">trac.transposh.org</a>.
|
6 |
Author: Team Transposh
|
7 |
+
Version: 0.2.0
|
8 |
Author URI: http://transposh.org/
|
9 |
License: GPL (http://www.gnu.org/licenses/gpl.txt)
|
10 |
*/
|
35 |
$admin_msg;
|
36 |
|
37 |
/*
|
38 |
+
* Called when the buffer containing the original page is flushed. Triggers the
|
39 |
* translation process.
|
40 |
*/
|
41 |
function process_page(&$buffer) {
|
42 |
|
43 |
+
global $wp_query, $lang, $is_edit_mode, $rtl_languages, $enable_auto_translate;
|
44 |
|
45 |
+
|
46 |
$start_time = microtime(TRUE);
|
47 |
|
48 |
+
// No language code - avoid further processing.
|
49 |
if (!isset($wp_query->query_vars[LANG_PARAM]))
|
50 |
{
|
|
|
51 |
return $buffer;
|
|
|
52 |
}
|
53 |
|
54 |
$lang = $wp_query->query_vars[LANG_PARAM];
|
55 |
$default_lang = get_default_lang();
|
56 |
+
// Don't translate the default language
|
57 |
if($lang == $default_lang)
|
58 |
{
|
|
|
59 |
|
60 |
return $buffer;
|
61 |
}
|
62 |
|
|
|
|
|
63 |
if (($wp_query->query_vars[EDIT_PARAM] == "1" || $wp_query->query_vars[EDIT_PARAM] == "true") &&
|
64 |
is_editing_permitted())
|
65 |
{
|
66 |
$is_edit_mode = TRUE;
|
67 |
}
|
68 |
|
|
|
69 |
|
70 |
//translate the entire page
|
71 |
+
$parse = new parser();
|
72 |
+
$parse->fetch_translate_func = 'fetch_translation';
|
73 |
+
$parse->url_rewrite_func = 'rewrite_url';
|
74 |
+
$parse->dir_rtl = (in_array ($lang, $rtl_languages));
|
75 |
+
$parse->lang = $lang;
|
76 |
+
$parse->is_edit_mode = $is_edit_mode;
|
77 |
+
$parse->is_auto_translate = $enable_auto_translate;
|
78 |
+
$buffer = $parse->fix_html($buffer);
|
79 |
|
80 |
$end_time = microtime(TRUE);
|
|
|
81 |
|
82 |
|
83 |
+
return $buffer;
|
|
|
84 |
}
|
85 |
|
86 |
/*
|
357 |
return;
|
358 |
}
|
359 |
//include the transposh.css
|
360 |
+
wp_enqueue_style("transposh","$tr_plugin_url/css/transposh.css",array(),'0.2.0');
|
361 |
wp_enqueue_style("jquery","http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.1/themes/ui-lightness/jquery-ui.css",array(),'1.0');
|
362 |
|
363 |
}
|
401 |
wp_deregister_script('jquery');
|
402 |
wp_enqueue_script("jquery","http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js",array(),'1.3.2');
|
403 |
wp_enqueue_script("google","http://www.google.com/jsapi",array(),'1');
|
404 |
+
wp_enqueue_script("transposh","$tr_plugin_url/js/transposh.js?post_url=$post_url{$edit_mode}&lang={$lang}&prefix=".SPAN_PREFIX,array("jquery"),'0.2.0');
|
405 |
}
|
406 |
}
|
407 |
|
479 |
* @param $href
|
480 |
* @return TRUE if parameters should be used instead of rewriting as a permalink
|
481 |
*/
|
482 |
+
function rewrite_url($href)
|
483 |
{
|
484 |
+
global $lang, $is_edit_mode, $enable_permalinks_rewrite, $home_url;
|
485 |
$use_params = FALSE;
|
486 |
+
|
487 |
|
488 |
+
// Ignore urls not from this site
|
489 |
+
if(stripos($href, $home_url) === FALSE)
|
490 |
+
{
|
491 |
+
return $href;
|
492 |
+
}
|
493 |
+
|
494 |
+
// don't fix links pointing to real files as it will cause that the
|
495 |
+
// web server will not be able to locate them
|
496 |
if(stripos($href, '/wp-admin') !== FALSE ||
|
497 |
stripos($href, '/wp-content') !== FALSE ||
|
498 |
stripos($href, '/wp-login') !== FALSE ||
|
499 |
stripos($href, '/.php') !== FALSE)
|
500 |
{
|
501 |
+
return $href;
|
502 |
}
|
503 |
+
$use_params = !$enable_permalinks_rewrite;
|
504 |
|
505 |
+
$href = rewrite_url_lang_param($href, $lang, $is_edit_mode, $use_params);
|
506 |
+
|
507 |
+
return $href;
|
508 |
}
|
509 |
|
510 |
//Register callbacks
|
transposh_admin.php
CHANGED
@@ -72,25 +72,34 @@ function insert_supported_langs()
|
|
72 |
{
|
73 |
global $languages, $tr_plugin_url;
|
74 |
|
75 |
-
echo
|
|
|
76 |
'function chbx_change(lang)'.
|
77 |
'{'.
|
78 |
-
'
|
79 |
-
'if(document.getElementById(view).checked)'.
|
80 |
-
'{'.
|
81 |
-
'var edit = lang + "_edit";'.
|
82 |
-
'document.getElementById(edit).checked = true;'.
|
83 |
-
'}'.
|
84 |
'}'.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
'</script>';
|
86 |
echo '<table><tr>';
|
87 |
|
88 |
-
|
|
|
|
|
|
|
89 |
|
90 |
for($hdr=0; $hdr < $columns; $hdr++)
|
91 |
{
|
92 |
-
echo '<th>Language</th><th>Viewable</th
|
93 |
-
|
|
|
94 |
}
|
95 |
|
96 |
echo '</tr>';
|
@@ -102,17 +111,13 @@ function insert_supported_langs()
|
|
102 |
{
|
103 |
echo '<tr>';
|
104 |
}
|
105 |
-
echo "\n";
|
106 |
-
|
107 |
$i++;
|
108 |
|
109 |
echo "<td><img src=\"$tr_plugin_url/img/flags/$flag.png\" alt=\"\"/> $language</td>";
|
110 |
-
echo '<td align="center"
|
111 |
$code . '_view" onchange="chbx_change(\'' . $code . '\')" ' . is_viewable($code) . '/></td>';
|
112 |
-
echo "
|
113 |
-
echo '<td align="center"> <input type="checkbox" id="' . $code . '_edit" name="' .
|
114 |
$code . '_edit" ' . is_editable($code). '/></td>';
|
115 |
-
echo "\n";
|
116 |
echo "<td align=\"center\"><input type=\"radio\" name=\"default_lang\" value=\"$code\" " .
|
117 |
is_default_lang($code). "/></td>";
|
118 |
// TODO: Add icons?
|
@@ -130,7 +135,6 @@ function insert_supported_langs()
|
|
130 |
{
|
131 |
echo "<td style=\"padding-right: 60px\"></td>";
|
132 |
}
|
133 |
-
echo "\n";
|
134 |
}
|
135 |
|
136 |
echo '</table>';
|
@@ -202,11 +206,11 @@ function insert_permissions()
|
|
202 |
foreach($wp_roles->get_names() as $role_name => $something)
|
203 |
{
|
204 |
echo '<input type="checkbox" value="1" name="' . $role_name . '" ' . can_translate($role_name) .
|
205 |
-
'/>' . $role_name . ' ';
|
206 |
}
|
207 |
|
208 |
//Add our own custom role
|
209 |
-
echo '<input type="checkbox" value="1" name="anonymous" '.
|
210 |
can_translate('anonymous') . '/> Anonymous';
|
211 |
}
|
212 |
|
@@ -223,9 +227,9 @@ function insert_permalink_rewrite_option()
|
|
223 |
$checked = 'checked="checked"';
|
224 |
}
|
225 |
|
226 |
-
echo '<input type="checkbox" value="1" name="enable_permalinks" '. $checked . '/>'.
|
227 |
'Rewrite URLs to be search engine friendly, '.
|
228 |
-
'e.g. (http://wordpress.org/<strong>
|
229 |
'Requires that permalinks will be enabled.';
|
230 |
}
|
231 |
|
@@ -241,7 +245,7 @@ function insert_auto_translate_option()
|
|
241 |
$checked = 'checked="checked"';
|
242 |
}
|
243 |
|
244 |
-
echo '<input type="checkbox" value="1" name="enable_autotranslate" '.$checked.'/>'.
|
245 |
'Allow automatic translation of pages (currently using Google Translate)';
|
246 |
}
|
247 |
|
@@ -312,12 +316,14 @@ function update_admin_options()
|
|
312 |
{
|
313 |
if($_POST[$code . '_view'])
|
314 |
{
|
315 |
-
$viewable_langs[] = $code;
|
|
|
|
|
316 |
}
|
317 |
|
318 |
if($_POST[$code . '_edit'])
|
319 |
{
|
320 |
-
$editable_langs[] = $code;
|
321 |
}
|
322 |
}
|
323 |
|
72 |
{
|
73 |
global $languages, $tr_plugin_url;
|
74 |
|
75 |
+
echo
|
76 |
+
'<script type="text/javascript">'.
|
77 |
'function chbx_change(lang)'.
|
78 |
'{'.
|
79 |
+
'jQuery("#"+lang+"_edit").attr("checked",jQuery("#"+lang+"_view").attr("checked"))'.
|
|
|
|
|
|
|
|
|
|
|
80 |
'}'.
|
81 |
+
'jQuery(document).ready(function() {'.
|
82 |
+
'jQuery("#tr_anon").click(function() {'.
|
83 |
+
'if (jQuery("#tr_anon").attr("checked")) {'.
|
84 |
+
'jQuery(".tr_editable").css("display","none");'.
|
85 |
+
'} else {'.
|
86 |
+
'jQuery(".tr_editable").css("display","");'.
|
87 |
+
'}'.
|
88 |
+
'});'.
|
89 |
+
'});'.
|
90 |
'</script>';
|
91 |
echo '<table><tr>';
|
92 |
|
93 |
+
// we will hide the translatable column if anonymous can translate anyway
|
94 |
+
if (can_translate('anonymous')) $extrastyle = ' style ="display:none"';
|
95 |
+
|
96 |
+
$columns = 2;
|
97 |
|
98 |
for($hdr=0; $hdr < $columns; $hdr++)
|
99 |
{
|
100 |
+
echo '<th>Language</th><th title="Is this language user selectable?">Viewable</th>'.
|
101 |
+
'<th title="Is this language visible for translators?"'.$extrastyle.' class="tr_editable">Translatable</th>'.
|
102 |
+
'<th>Default</th><th title="Can we auto-translate this language?">Auto?</th><th style="padding-right: 80px"></th>';
|
103 |
}
|
104 |
|
105 |
echo '</tr>';
|
111 |
{
|
112 |
echo '<tr>';
|
113 |
}
|
|
|
|
|
114 |
$i++;
|
115 |
|
116 |
echo "<td><img src=\"$tr_plugin_url/img/flags/$flag.png\" alt=\"\"/> $language</td>";
|
117 |
+
echo '<td align="center"><input type="checkbox" id="' . $code .'_view" name="' .
|
118 |
$code . '_view" onchange="chbx_change(\'' . $code . '\')" ' . is_viewable($code) . '/></td>';
|
119 |
+
echo '<td class="tr_editable"'.$extrastyle.' align="center"><input type="checkbox" id="' . $code . '_edit" name="' .
|
|
|
120 |
$code . '_edit" ' . is_editable($code). '/></td>';
|
|
|
121 |
echo "<td align=\"center\"><input type=\"radio\" name=\"default_lang\" value=\"$code\" " .
|
122 |
is_default_lang($code). "/></td>";
|
123 |
// TODO: Add icons?
|
135 |
{
|
136 |
echo "<td style=\"padding-right: 60px\"></td>";
|
137 |
}
|
|
|
138 |
}
|
139 |
|
140 |
echo '</table>';
|
206 |
foreach($wp_roles->get_names() as $role_name => $something)
|
207 |
{
|
208 |
echo '<input type="checkbox" value="1" name="' . $role_name . '" ' . can_translate($role_name) .
|
209 |
+
'/> ' . ucfirst($role_name) . ' ';
|
210 |
}
|
211 |
|
212 |
//Add our own custom role
|
213 |
+
echo '<input id="tr_anon" type="checkbox" value="1" name="anonymous" '.
|
214 |
can_translate('anonymous') . '/> Anonymous';
|
215 |
}
|
216 |
|
227 |
$checked = 'checked="checked"';
|
228 |
}
|
229 |
|
230 |
+
echo '<input type="checkbox" value="1" name="enable_permalinks" '. $checked . '/> '.
|
231 |
'Rewrite URLs to be search engine friendly, '.
|
232 |
+
'e.g. (http://wordpress.org/<strong>en</strong>). '.
|
233 |
'Requires that permalinks will be enabled.';
|
234 |
}
|
235 |
|
245 |
$checked = 'checked="checked"';
|
246 |
}
|
247 |
|
248 |
+
echo '<input type="checkbox" value="1" name="enable_autotranslate" '.$checked.'/> '.
|
249 |
'Allow automatic translation of pages (currently using Google Translate)';
|
250 |
}
|
251 |
|
316 |
{
|
317 |
if($_POST[$code . '_view'])
|
318 |
{
|
319 |
+
$viewable_langs[$code] = $code;
|
320 |
+
// force that every viewable lang is editable
|
321 |
+
$editable_langs[$code] = $code;
|
322 |
}
|
323 |
|
324 |
if($_POST[$code . '_edit'])
|
325 |
{
|
326 |
+
$editable_langs[$code] = $code;
|
327 |
}
|
328 |
}
|
329 |
|
transposh_db.php
CHANGED
@@ -139,7 +139,7 @@ function update_translation()
|
|
139 |
$original = $wpdb->escape(html_entity_decode($original, ENT_NOQUOTES, 'UTF-8'));
|
140 |
|
141 |
//add our own custom header - so we will know that we got here
|
142 |
-
header("Transposh: ver-0.
|
143 |
|
144 |
list($translated_text, $old_source) = fetch_translation($original, $lang);
|
145 |
if ($translated_text) {
|
@@ -243,7 +243,7 @@ function get_translation_history($token, $lang)
|
|
243 |
$original = $wpdb->escape(html_entity_decode($original, ENT_NOQUOTES, 'UTF-8'));
|
244 |
|
245 |
//add our own custom header - so we will know that we got here
|
246 |
-
header("Transposh: ver-0.
|
247 |
|
248 |
$query = "SELECT translated, translated_by, timestamp, source, user_login ".
|
249 |
"FROM $table_name ".
|
139 |
$original = $wpdb->escape(html_entity_decode($original, ENT_NOQUOTES, 'UTF-8'));
|
140 |
|
141 |
//add our own custom header - so we will know that we got here
|
142 |
+
header("Transposh: ver-0.2.0 db_version-". DB_VERSION);
|
143 |
|
144 |
list($translated_text, $old_source) = fetch_translation($original, $lang);
|
145 |
if ($translated_text) {
|
243 |
$original = $wpdb->escape(html_entity_decode($original, ENT_NOQUOTES, 'UTF-8'));
|
244 |
|
245 |
//add our own custom header - so we will know that we got here
|
246 |
+
header("Transposh: ver-0.2.0 db_version-". DB_VERSION);
|
247 |
|
248 |
$query = "SELECT translated, translated_by, timestamp, source, user_login ".
|
249 |
"FROM $table_name ".
|
transposh_widget.php
CHANGED
@@ -85,7 +85,7 @@ function add_transposh_widget_css() {
|
|
85 |
global $tr_plugin_url;
|
86 |
|
87 |
//include the transposh_widget.css
|
88 |
-
wp_enqueue_style("transposh_widget","$tr_plugin_url/css/transposh_widget.css",array(),'0.
|
89 |
|
90 |
}
|
91 |
|
@@ -204,7 +204,7 @@ function transposh_widget($args)
|
|
204 |
|
205 |
echo "</form>";
|
206 |
//echo "<button onClick=\"do_auto_translate();\">translate all</button>";
|
207 |
-
echo "<div id=\"".SPAN_PREFIX."credit\">by <a href=\"http://transposh.org\"><img src=\"$plugpath/img/tplogo.png\" style=\"padding:1px;border:0px\" title=\"Transposh\" alt=\"Transposh\"/></a></div>";
|
208 |
echo $after_widget;
|
209 |
}
|
210 |
|
85 |
global $tr_plugin_url;
|
86 |
|
87 |
//include the transposh_widget.css
|
88 |
+
wp_enqueue_style("transposh_widget","$tr_plugin_url/css/transposh_widget.css",array(),'0.2.0');
|
89 |
|
90 |
}
|
91 |
|
204 |
|
205 |
echo "</form>";
|
206 |
//echo "<button onClick=\"do_auto_translate();\">translate all</button>";
|
207 |
+
echo "<div id=\"".SPAN_PREFIX."credit\">by <a href=\"http://transposh.org\"><img class=".NO_TRANSLATE_CLASS." src=\"$plugpath/img/tplogo.png\" style=\"padding:1px;border:0px\" title=\"Transposh\" alt=\"Transposh\"/></a></div>";
|
208 |
echo $after_widget;
|
209 |
}
|
210 |
|