Transposh WordPress Translation - Version 0.2.0

Version Description

Download this release

Release Info

Developer oferwald
Plugin Icon 128x128 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 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
- * Determines position in page marks a transaltable tag in html page or rss feed section.
496
- * Return TRUE if should be translated otherwise FALSE.
497
  */
498
- function is_translatable_section()
499
- {
500
- global $tags_list, $is_in_channel, $is_in_body;
501
- $rc = FALSE;
502
- $current_tag = end($tags_list);
503
-
504
- if($current_tag == 'title')
505
- {
506
- $rc = TRUE;
507
- }
508
- else if($is_in_body && $current_tag != 'style' && $current_tag != 'script')
509
- {
510
- $rc = TRUE;
511
- }
512
- else if($is_in_channel &&
513
- ($current_tag == 'title' || $current_tag == 'description' || $current_tag == 'category'))
514
- {
515
- $rc = TRUE;
516
- }
517
-
518
-
519
- return $rc;
520
- }
521
-
522
- /*
523
- * Determine if the current position in buffer is a sentence breaker, e.g. '.' or ',' .
524
- * Note html markups are not considered sentence breaker within the scope of this function.
525
- * Return TRUE is current position marks a break in sentence otherwise FALSE
526
- */
527
- function is_sentence_breaker($position)
528
- {
529
- global $page;
530
- $rc = FALSE;
531
-
532
- if($page[$position] == '.' || $page[$position] == '-')
533
- {
534
- //Only break if the next character is a white space,
535
- //in order to avoid breaks on cases like this: (hello world.)
536
- if(is_white_space($position + 1) || $page[$position + 1] == '<')
537
- {
538
- $rc = TRUE;
539
- }
540
- }
541
- else if($page[$position] == ',' || $page[$position] == '?' ||
542
- $page[$position] == '(' || $page[$position] == ')' ||
543
- $page[$position] == '[' || $page[$position] == ']' ||
544
- $page[$position] == '"' || $page[$position] == '!' ||
545
- $page[$position] == ':' || $page[$position] == '|' ||
546
- $page[$position] == ';')
547
- {
548
- //break the sentence into segments regardless of the next character.
549
- $rc = TRUE;
550
- }
551
-
552
- return $rc;
553
- }
554
-
555
- /*
556
- * Determines if the current position marks the begining of an html
557
- * entity. E.g &amp;
558
- * Return 0 if not an html entity otherwise return the position past this entity. In addition
559
- * the $is_breaker will be set to TRUE if entity should break translation into a new segment.
560
- *
561
- */
562
- function is_html_entity($position, &$is_breaker)
563
- {
564
- global $page;
565
- $is_breaker = FALSE;
566
- if($page[$position] == "&" )
567
- {
568
- $end_pos = $position + 1;
569
-
570
- while($page[$end_pos] == "#" ||
571
- is_digit($end_pos) || is_a_to_z_character($end_pos))
572
- {
573
- $end_pos++;
574
- }
575
-
576
- if($page[$end_pos] == ';')
577
- {
578
- $entity = substr($page, $position, $end_pos - $position + 1);
579
-
580
- //Don't break on ` so for our use we don't consider it an entity
581
- //e.g. Jack`s apple.
582
- //Exception: don't break when we there is a white space after the apostrophe. e.g. `uncategorized`
583
- if(($entity == "&#8217;" || $entity == "&apos;" || $entity == "&#039;")
584
- && $page[$end_pos + 1] != " ")
585
- {
586
- $is_breaker = FALSE;
587
- }
588
- else
589
- {
590
- $is_breaker = TRUE;
591
- }
592
-
593
-
594
- //It is an html entity.
595
- return $end_pos + 1;
596
- }
597
- }
598
-
599
- return 0;
600
- }
601
-
602
- /*
603
- * Determines if the current position marks the begining of a number, e.g. 123 050-391212232
604
- *
605
- * Return 0 if not a number otherwise return the position past this number.
606
- */
607
- function is_number($position)
608
- {
609
- global $page;
610
- $start = $position;
611
-
612
- while(is_digit($position) || $page[$position] == '-' || $page[$position] == '+' ||
613
- (($page[$position] == ',' || $page[$position] == '.' || $page[$position] == '\\' || $page[$position] == '/') && is_digit($position+1)))
614
- {
615
- $position++;
616
- }
617
-
618
- if($position != $start && (is_white_space($position) || $page[$position] == '<'))
619
- {
620
- return $position;
621
- }
622
-
623
- return 0;
624
- }
625
-
626
- /*
627
- * Determine if the current position in page points to a character in the
628
- * range of a-z (case insensetive).
629
- * Return TRUE if a-z otherwise FALSE
630
- *
631
- */
632
-
633
- function is_a_to_z_character($position)
634
- {
635
- global $page;
636
-
637
- if(($page[$position] >= 'a' && $page[$position] <= 'z') ||
638
- ($page[$position] >= 'A' && $page[$position] <= 'Z'))
639
- {
640
- return TRUE;
641
- }
642
-
643
- return FALSE;
644
- }
645
-
646
- /*
647
- * Determine if the current position is a number.
648
- * Return TRUE if a number otherwise FALSE
649
- */
650
- function is_digit($position)
651
- {
652
- global $page;
653
-
654
- if($page[$position] >= '0' && $page[$position] <= '9')
655
- {
656
- return TRUE;
657
- }
658
-
659
- return FALSE;
660
- }
661
-
662
- /*
663
- * Determine if the current position in buffer is a white space.
664
- * return TRUE if current position marks a white space otherwise FALSE.
665
- */
666
- function is_white_space($position)
667
- {
668
- global $page;
669
-
670
- if($page[$position] == " " || $page[$position] == "" ||
671
- $page[$position] == "\t" || $page[$position] == "\r" ||
672
- $page[$position] == "\n" || $page[$position] == "\x0B" ||
673
- $page[$position] == "\0")
674
- {
675
- return TRUE;
676
- }
677
- }
678
-
679
- /*
680
- * Skip within buffer past white space characters , Staring from the specified
681
- * position going either forward or backward.
682
- * param forward - indicate direction going either backward of forward.
683
- */
684
- function skip_white_space(&$index = NULL, $forward=TRUE)
685
- {
686
- global $page, $pos;
687
-
688
- if(!isset($index))
689
- {
690
- //use $pos as the default position if not specified otherwise
691
- $index = &$pos;
692
- }
693
-
694
- while($index < strlen($page) && $index > 0 && is_white_space($index))
695
- {
696
- ($forward ? $index++ : $index--);
697
- }
698
-
699
- return $index;
700
- }
701
-
702
- /*
703
- * Check within page buffer position for the given word.
704
- * param word The word to look for.
705
- * param index1 Optional position within the page buffer, if not available then the current
706
- * position ($pos) is used.
707
- * Return TRUE if the word matches otherwise FALSE
708
- */
709
- function is_word($word, $index1 = NULL)
710
- {
711
- global $page, $pos;
712
- $rc = FALSE;
713
-
714
- if(!isset($index1))
715
- {
716
- //use $pos as the default position if not specified otherwise
717
- $index1 = $pos;
718
- }
719
-
720
- $index2 = 0; //position within word
721
- $word_len = strlen($word);
722
- $page_length = strlen($page);
723
-
724
- while($index1 < $page_length && $index2 < $word_len)
725
- {
726
- if($page[$index1] == $word[$index2])
727
- {
728
- $index1++;
729
- $index2++;
730
- }
731
- else
732
- {
733
- break;
734
- }
735
- }
736
-
737
- //check if we have full match
738
- if($index2 == $word_len)
739
- {
740
- $rc = TRUE;
741
- }
742
-
743
- return $rc;
744
- }
745
-
746
- /*
747
- * Translate the text between the given start position and the current
748
- * position (pos) within the buffer.
749
- */
750
- function translate_text($start)
751
- {
752
-
753
- global $page, $pos, $lang, $phrase_count, $pharses_by_human, $pharses_by_cat;
754
-
755
- //trim white space from the start position going forward
756
- skip_white_space($start);
757
-
758
- //Set the end position of the string to one back from current position
759
- //(i.e. current position points to '<' or a breaker '.') and then trim
760
- //white space from the right backwards
761
- $end = $pos - 1;
762
- $end = skip_white_space($end, $forward=FALSE);
763
-
764
- if($start >= $end)
765
- {
766
- //empty string - nothing to do
767
- return;
768
- }
769
-
770
- $original_text = substr($page, $start, $end - $start + 1);
771
-
772
- //Cleanup and prepare text
773
- $original_text = scrub_text($original_text);
774
- if($original_text == NULL)
775
- {
776
- //nothing left from the text
777
- return;
778
- }
779
-
780
- list($translated_text, $source) = fetch_translation($original_text, $lang);
781
-
782
- $phrase_count++;
783
- if ($translated_text != NULL) {
784
- if ($source) {
785
- $pharses_by_cat++;
786
- } else {
787
- $pharses_by_human++;
788
- }
789
- }
790
-
791
- insert_translation($original_text, $translated_text, $source, $start, $end);
792
- }
793
-
794
- /*
795
- * Update the translated page with the specified translation at the given position.
796
- * param original_text Text in the original page. Will not be NULL.
797
- * param translated_text The translated text, can be NULL in case no translation is available
798
- * param start Marks the start position of the text to be replaced within the original page
799
- * param end Marks the end position of the text to be replaced within the original page
800
- */
801
- function insert_translation(&$original_text, &$translated_text, $source, $start, $end)
802
- {
803
- global $segment_id, $is_edit_mode, $tags_list, $enable_auto_translate;
804
-
805
- $is_translated = FALSE;
806
-
807
- if(($is_edit_mode || ($enable_auto_translate && $translated_text == NULL)) && in_array('body', $tags_list))
808
- {
809
- //Use base64 encoding to make that when the page is translated (i.e. update_translation) we
810
- //get back exactlly the same string without having the client decode/encode it in anyway.
811
- $token = 'token="' . base64_url_encode($original_text) . '"';
812
-
813
- // We will mark translated text with tr_t class and untranslated with tr_u
814
- $span = '<span id="'.SPAN_PREFIX."{$segment_id}\" $token ";
815
- // those are needed for on the fly image creation
816
- if ($is_edit_mode) {
817
- $span .= "source=\"$source\" ";
818
- //if($translated_text != NULL)
819
- $span .= "orig=\"$original_text\" ";
820
- }
821
- $span .= 'class="'.SPAN_PREFIX;
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 &amp; or &#8220;.
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('&#8217;&apos;&#039;&#39;', $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(f){var g=jQuery(this).attr("id").substr(jQuery(this).attr("id").lastIndexOf("_")+1);jQuery("#"+transposh_params.prefix+g).addClass(transposh_params.prefix+"t").removeClass(transposh_params.prefix+"u");jQuery("#"+transposh_params.prefix+"img_"+g).removeClass("tr-icon-yellow").removeClass("tr-icon-green");if(jQuery.trim(e).length!==0){if(d==1){jQuery("#"+transposh_params.prefix+"img_"+g).addClass("tr-icon-yellow")}else{jQuery("#"+transposh_params.prefix+"img_"+g).addClass("tr-icon-green")}}})}function do_auto_translate(){if(transposh_params.progress){togo=jQuery("."+transposh_params.prefix+"u").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+"u").each(function(d){var c=jQuery(this).attr("id");if(!(b[jQuery(this).text()]==1)){b[jQuery(this).text()]=1;google.language.translate(jQuery(this).text(),"",transposh_params.lang,function(e){if(!e.error){var f=c.substr(c.lastIndexOf("_")+1);fix_page(jQuery("<div>"+e.translation+"</div>").text(),1,f);ajax_translate(jQuery("<div>"+e.translation+"</div>").text(),1,f);if(transposh_params.progress){a=togo-jQuery("."+transposh_params.prefix+"u").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());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+"t,."+transposh_params.prefix+"u").each(function(b){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"/>');jQuery("#"+transposh_params.prefix+"img_"+a).click(function(){translate_dialog(a);return false}).css({border:"0px",margin:"1px",padding:"0px"});if(jQuery(this).hasClass(transposh_params.prefix+"t")){if(jQuery(this).attr("source")=="1"){jQuery("#"+transposh_params.prefix+"img_"+a).addClass("tr-icon-yellow")}else{jQuery("#"+transposh_params.prefix+"img_"+a).addClass("tr-icon-green")}}})}});
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.1.7
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
+ &lt;?php if(function_exists("transposh_widget")) { transposh_widget(array()); } ?&gt;
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.1.7
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 flused. Triggers the
39
  * translation process.
40
  */
41
  function process_page(&$buffer) {
42
 
43
- global $wp_query, $tr_page, $page, $lang, $is_edit_mode;
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
- process_html();
 
 
 
 
 
 
 
75
 
76
  $end_time = microtime(TRUE);
77
-
78
 
79
 
80
- //return the translated page unless it is empty, othewise return the original
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.1.7');
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.1.7');
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 is_url_excluded_from_permalink_rewrite($href)
481
  {
 
482
  $use_params = FALSE;
 
483
 
484
- //don't fix links pointing to real files as it will cause that the
485
- //web server will not be able to locate them
 
 
 
 
 
 
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
- $use_params = TRUE;
492
  }
 
493
 
494
- return $use_params;
 
 
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 '<script type="text/javascript">'.
 
76
  'function chbx_change(lang)'.
77
  '{'.
78
- 'var view = lang + "_view";'.
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
- $columns = 2;
 
 
 
89
 
90
  for($hdr=0; $hdr < $columns; $hdr++)
91
  {
92
- echo '<th>Language</th><th>Viewable</th><th>Translatable</th>'.
93
- '<th>Default</th><th>Auto?</th><th style="padding-right: 80px"></th>';
 
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=\"\"/>&nbsp;$language</td>";
110
- echo '<td align="center"> <input type="checkbox" id="' . $code .'_view" name="' .
111
  $code . '_view" onchange="chbx_change(\'' . $code . '\')" ' . is_viewable($code) . '/></td>';
112
- echo "\n";
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 . '&nbsp;&nbsp;&nbsp;';
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> en</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=\"\"/>&nbsp;$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) . '&nbsp;&nbsp;&nbsp;';
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.1.7 db_version-". DB_VERSION);
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.1.7 db_version-". DB_VERSION);
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.1.7');
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