Index: trunk/extensions/WikiSync/WikiSyncBasic.php |
— | — | @@ -1,216 +0,0 @@ |
2 | | -<?php |
3 | | -/** |
4 | | - * ***** BEGIN LICENSE BLOCK ***** |
5 | | - * This file is part of WikiSync. |
6 | | - * |
7 | | - * WikiSync is free software; you can redistribute it and/or modify |
8 | | - * it under the terms of the GNU General Public License as published by |
9 | | - * the Free Software Foundation; either version 2 of the License, or |
10 | | - * (at your option) any later version. |
11 | | - * |
12 | | - * WikiSync is distributed in the hope that it will be useful, |
13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | - * GNU General Public License for more details. |
16 | | - * |
17 | | - * You should have received a copy of the GNU General Public License |
18 | | - * along with WikiSync; if not, write to the Free Software |
19 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | | - * |
21 | | - * ***** END LICENSE BLOCK ***** |
22 | | - * |
23 | | - * WikiSync allows an AJAX-based synchronization of revisions and files between |
24 | | - * global wiki site and it's local mirror. |
25 | | - * |
26 | | - * To activate this extension : |
27 | | - * * Create a new directory named WikiSync into the directory "extensions" of MediaWiki. |
28 | | - * * Place the files from the extension archive there. |
29 | | - * * Add this line at the end of your LocalSettings.php file : |
30 | | - * require_once "$IP/extensions/WikiSync/WikiSync.php"; |
31 | | - * |
32 | | - * @version 0.3.1 |
33 | | - * @link http://www.mediawiki.org/wiki/Extension:WikiSync |
34 | | - * @author Dmitriy Sintsov <questpc@rambler.ru> |
35 | | - * @addtogroup Extensions |
36 | | - */ |
37 | | - |
38 | | -if ( !defined( 'MEDIAWIKI' ) ) { |
39 | | - die( "This file is a part of MediaWiki extension.\n" ); |
40 | | -} |
41 | | - |
42 | | -/* render output data */ |
43 | | -class _QXML { |
44 | | - // the stucture of $tag is like this: |
45 | | - // array( "__tag"=>"td", "class"=>"myclass", 0=>"text before li", 1=>array( "__tag"=>"li", 0=>"text inside li" ), 2=>"text after li" ) |
46 | | - // both tagged and tagless lists are supported |
47 | | - static function toText( &$tag ) { |
48 | | - $tag_open = ""; |
49 | | - $tag_close = ""; |
50 | | - $tag_val = null; |
51 | | - if ( is_array( $tag ) ) { |
52 | | - ksort( $tag ); |
53 | | - if ( array_key_exists( '__tag', $tag ) ) { |
54 | | - # list inside of tag |
55 | | - $tag_open .= "<" . $tag[ '__tag' ]; |
56 | | - foreach ( $tag as $attr_key => &$attr_val ) { |
57 | | - if ( is_int( $attr_key ) ) { |
58 | | - if ( $tag_val === null ) |
59 | | - $tag_val = ""; |
60 | | - if ( is_array( $attr_val ) ) { |
61 | | - # recursive tags |
62 | | - $tag_val .= self::toText( $attr_val ); |
63 | | - } else { |
64 | | - # text |
65 | | - $tag_val .= $attr_val; |
66 | | - } |
67 | | - } else { |
68 | | - # string keys are for tag attributes |
69 | | - if ( substr( $attr_key, 0, 2 ) != "__" ) { |
70 | | - # include only non-reserved attributes |
71 | | - $tag_open .= " $attr_key=\"" . $attr_val . "\""; |
72 | | - } |
73 | | - } |
74 | | - } |
75 | | - if ( $tag_val !== null ) { |
76 | | - $tag_open .= ">"; |
77 | | - $tag_close .= "</" . $tag[ '__tag' ] . ">"; |
78 | | - } else { |
79 | | - $tag_open .= " />"; |
80 | | - } |
81 | | - if ( array_key_exists( '__end', $tag ) ) { |
82 | | - $tag_close .= $tag[ '__end' ]; |
83 | | - } |
84 | | - } else { |
85 | | - # tagless list |
86 | | - $tag_val = ""; |
87 | | - foreach ( $tag as $attr_key => &$attr_val ) { |
88 | | - if ( is_int( $attr_key ) ) { |
89 | | - if ( is_array( $attr_val ) ) { |
90 | | - # recursive tags |
91 | | - $tag_val .= self::toText( $attr_val ); |
92 | | - } else { |
93 | | - # text |
94 | | - $tag_val .= $attr_val; |
95 | | - } |
96 | | - } else { |
97 | | - ob_start(); |
98 | | - var_dump( $tag ); |
99 | | - $tagdump = ob_get_contents(); |
100 | | - ob_end_clean(); |
101 | | - $tag_val = "invalid argument: tagless list cannot have tag attribute values in key=$attr_key, $tagdump"; |
102 | | - } |
103 | | - } |
104 | | - } |
105 | | - } else { |
106 | | - # just a text |
107 | | - $tag_val = $tag; |
108 | | - } |
109 | | - return $tag_open . $tag_val . $tag_close; |
110 | | - } |
111 | | - |
112 | | - # creates one "htmlobject" row of the table |
113 | | - # elements of $row can be either a string/number value of cell or an array( "count"=>colspannum, "attribute"=>value, 0=>html_inside_tag ) |
114 | | - # attribute maps can be like this: ("name"=>0, "count"=>colspan" ) |
115 | | - static function newRow( $row, $rowattrs = "", $celltag = "td", $attribute_maps = null ) { |
116 | | - $result = ""; |
117 | | - if ( count( $row ) > 0 ) { |
118 | | - foreach ( $row as &$cell ) { |
119 | | - if ( !is_array( $cell ) ) { |
120 | | - $cell = array( 0 => $cell ); |
121 | | - } |
122 | | - $cell[ '__tag' ] = $celltag; |
123 | | - $cell[ '__end' ] = "\n"; |
124 | | - if ( is_array( $attribute_maps ) ) { |
125 | | - # converts ("count"=>3) to ("colspan"=>3) in table headers - don't use frequently |
126 | | - foreach ( $attribute_maps as $key => $val ) { |
127 | | - if ( array_key_exists( $key, $cell ) ) { |
128 | | - $cell[ $val ] = $cell[ $key ]; |
129 | | - unset( $cell[ $key ] ); |
130 | | - } |
131 | | - } |
132 | | - } |
133 | | - } |
134 | | - $result = array( '__tag' => 'tr', 0 => $row, '__end' => "\n" ); |
135 | | - if ( is_array( $rowattrs ) ) { |
136 | | - $result = array_merge( $rowattrs, $result ); |
137 | | - } elseif ( $rowattrs !== "" ) { |
138 | | - $result[0][] = __METHOD__ . ':invalid rowattrs supplied'; |
139 | | - } |
140 | | - } |
141 | | - return $result; |
142 | | - } |
143 | | - |
144 | | - # add row to the table |
145 | | - static function addRow( &$table, $row, $rowattrs = "", $celltag = "td", $attribute_maps = null ) { |
146 | | - $table[] = self::newRow( $row, $rowattrs, $celltag, $attribute_maps ); |
147 | | - } |
148 | | - |
149 | | - # add column to the table |
150 | | - static function addColumn( &$table, $column, $rowattrs = "", $celltag = "td", $attribute_maps = null ) { |
151 | | - if ( count( $column ) > 0 ) { |
152 | | - $row = 0; |
153 | | - foreach ( $column as &$cell ) { |
154 | | - if ( !is_array( $cell ) ) { |
155 | | - $cell = array( 0 => $cell ); |
156 | | - } |
157 | | - $cell[ '__tag' ] = $celltag; |
158 | | - $cell[ '__end' ] = "\n"; |
159 | | - if ( is_array( $attribute_maps ) ) { |
160 | | - # converts ("count"=>3) to ("rowspan"=>3) in table headers - don't use frequently |
161 | | - foreach ( $attribute_maps as $key => $val ) { |
162 | | - if ( array_key_exists( $key, $cell ) ) { |
163 | | - $cell[ $val ] = $cell[ $key ]; |
164 | | - unset( $cell[ $key ] ); |
165 | | - } |
166 | | - } |
167 | | - } |
168 | | - if ( is_array( $rowattrs ) ) { |
169 | | - $cell = array_merge( $rowattrs, $cell ); |
170 | | - } elseif ( $rowattrs !== "" ) { |
171 | | - $cell[ 0 ] = __METHOD__ . ':invalid rowattrs supplied'; |
172 | | - } |
173 | | - if ( !array_key_exists( $row, $table ) ) { |
174 | | - $table[ $row ] = array( '__tag' => 'tr', '__end' => "\n" ); |
175 | | - } |
176 | | - $table[ $row ][] = $cell; |
177 | | - if ( array_key_exists( 'rowspan', $cell ) ) { |
178 | | - $row += intval( $cell[ 'rowspan' ] ); |
179 | | - } else { |
180 | | - $row++; |
181 | | - } |
182 | | - } |
183 | | - $result = array( '__tag' => 'tr', 0 => $column, '__end' => "\n" ); |
184 | | - } |
185 | | - } |
186 | | - |
187 | | - static function displayRow( $row, $rowattrs = "", $celltag = "td", $attribute_maps = null ) { |
188 | | - return self::toText( self::newRow( $row, $rowattrs, $celltag, $attribute_maps ) ); |
189 | | - } |
190 | | - |
191 | | - // use newRow() or addColumn() to add resulting row/column to the table |
192 | | - // if you want to use the resulting row with toText(), don't forget to apply attrs=array('__tag'=>'td') |
193 | | - static function applyAttrsToRow( &$row, $attrs ) { |
194 | | - if ( is_array( $attrs ) && count( $attrs > 0 ) ) { |
195 | | - foreach ( $row as &$cell ) { |
196 | | - if ( !is_array( $cell ) ) { |
197 | | - $cell = array_merge( $attrs, array( $cell ) ); |
198 | | - } else { |
199 | | - foreach ( $attrs as $attr_key => $attr_val ) { |
200 | | - if ( !array_key_exists( $attr_key, $cell ) ) { |
201 | | - $cell[ $attr_key ] = $attr_val; |
202 | | - } |
203 | | - } |
204 | | - } |
205 | | - } |
206 | | - } |
207 | | - } |
208 | | - |
209 | | - static function entities( $s ) { |
210 | | - return htmlentities( $s, ENT_COMPAT, 'UTF-8' ); |
211 | | - } |
212 | | - |
213 | | - static function specialchars( $s ) { |
214 | | - return htmlspecialchars( $s, ENT_COMPAT, 'UTF-8' ); |
215 | | - } |
216 | | - |
217 | | -} /* end of _QXML class */ |
Index: trunk/extensions/WikiSync/WikiSyncExporter.php |
— | — | @@ -73,7 +73,7 @@ |
74 | 74 | class WikiSyncImportReporter extends ImportReporter { |
75 | 75 | private $mResultArr = array(); |
76 | 76 | |
77 | | - function reportPage( $title, $origTitle, $revisionCount, $successCount ) { |
| 77 | + function reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo = '' ) { |
78 | 78 | // Add a result entry |
79 | 79 | $r = array(); |
80 | 80 | ApiQueryBase::addTitleInfo($r, $title); |
— | — | @@ -84,7 +84,7 @@ |
85 | 85 | # avoid bug in 1.15.4 Special:Import (new file page text without the file uploaded) |
86 | 86 | # PHP Fatal error: Call to a member function insertOn() on a non-object in E:\www\psychologos\includes\specials\SpecialImport.php on line 334 |
87 | 87 | if ( $title->getArticleId() !== 0 ) { |
88 | | - parent::reportPage( $title, $origTitle, $revisionCount, $successCount ); |
| 88 | + parent::reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ); |
89 | 89 | } |
90 | 90 | } |
91 | 91 | |
Index: trunk/extensions/WikiSync/WikiSyncPage.php |
— | — | @@ -157,7 +157,7 @@ |
158 | 158 | // progress explanation hint |
159 | 159 | array( '__tag'=>'td', 'style'=>'font-size:9pt; ', 'colspan'=>'2', '' ) |
160 | 160 | ), |
161 | | - array( '__tag'=>'tr', 'style'=>'border:1px solid gray; ', |
| 161 | + array( '__tag'=>'tr', 'style'=>'border:1px solid gray; height:12px; ', |
162 | 162 | array( '__tag'=>'td', 'style'=>'width:0%; background-color:Gold; display: none; ', '' ), |
163 | 163 | array( '__tag'=>'td', 'style'=>'width:100%;', '' ) |
164 | 164 | ) |
— | — | @@ -200,7 +200,6 @@ |
201 | 201 | array( '__tag'=>'td', 'colspan'=>'2', |
202 | 202 | // Have to explicitly set empty contents for the iframe, or we'll produce |
203 | 203 | // <iframe /> which browsers consider an unclosed tag |
204 | | - // todo: fix in _QXML class |
205 | 204 | array( '__tag'=> 'iframe', 'id'=>'wikisync_iframe', 'style' => 'width:100%; height:200px; display:none; ', '' ) |
206 | 205 | ) |
207 | 206 | ) |
— | — | @@ -232,7 +231,8 @@ |
233 | 232 | WikiSyncSetup::headScripts( $wgOut, $wgContLang->isRTL() ); |
234 | 233 | $wgOut->setPagetitle( wfMsgHtml( 'wikisync' ) ); |
235 | 234 | $this->initPageTpl(); |
236 | | - $wgOut->addHTML( _QXML::toText( $this->page_tpl ) ); |
| 235 | + $wgOut->addHTML( "\n" ); |
| 236 | + $wgOut->addHTML( _QXML::toText( $this->page_tpl, 4 ) ); |
237 | 237 | } |
238 | 238 | |
239 | 239 | } /* end of WikiSyncPage class */ |
Index: trunk/extensions/WikiSync/WikiSync.php |
— | — | @@ -52,6 +52,57 @@ |
53 | 53 | $wgSpecialPages['WikiSync'] = array( 'WikiSyncPage' ); |
54 | 54 | $wgSpecialPageGroups['WikiSync'] = 'pagetools'; |
55 | 55 | |
| 56 | +if ( !isset( $wgAutoloadClasses['FormatJson'] ) ) { |
| 57 | + // for MediaWiki 1.15.5 |
| 58 | + class FormatJson { |
| 59 | + |
| 60 | + /** |
| 61 | + * Returns the JSON representation of a value. |
| 62 | + * |
| 63 | + * @param $value Mixed: the value being encoded. Can be any type except a resource. |
| 64 | + * @param $isHtml Boolean |
| 65 | + * |
| 66 | + * @return string |
| 67 | + */ |
| 68 | + public static function encode( $value, $isHtml = false ) { |
| 69 | + // Some versions of PHP have a broken json_encode, see PHP bug |
| 70 | + // 46944. Test encoding an affected character (U+20000) to |
| 71 | + // avoid this. |
| 72 | + if ( !function_exists( 'json_encode' ) || $isHtml || strtolower( json_encode( "\xf0\xa0\x80\x80" ) ) != '\ud840\udc00' ) { |
| 73 | + $json = new Services_JSON(); |
| 74 | + return $json->encode( $value, $isHtml ); |
| 75 | + } else { |
| 76 | + return json_encode( $value ); |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + /** |
| 81 | + * Decodes a JSON string. |
| 82 | + * |
| 83 | + * @param $value String: the json string being decoded. |
| 84 | + * @param $assoc Boolean: when true, returned objects will be converted into associative arrays. |
| 85 | + * |
| 86 | + * @return Mixed: the value encoded in json in appropriate PHP type. |
| 87 | + * Values true, false and null (case-insensitive) are returned as true, false |
| 88 | + * and &null; respectively. &null; is returned if the json cannot be |
| 89 | + * decoded or if the encoded data is deeper than the recursion limit. |
| 90 | + */ |
| 91 | + public static function decode( $value, $assoc = false ) { |
| 92 | + if ( !function_exists( 'json_decode' ) ) { |
| 93 | + $json = new Services_JSON(); |
| 94 | + $jsonDec = $json->decode( $value ); |
| 95 | + if( $assoc ) { |
| 96 | + $jsonDec = wfObjectToArray( $jsonDec ); |
| 97 | + } |
| 98 | + return $jsonDec; |
| 99 | + } else { |
| 100 | + return json_decode( $value, $assoc ); |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + } |
| 105 | +} |
| 106 | + |
56 | 107 | WikiSyncSetup::init(); |
57 | 108 | |
58 | 109 | class WikiSyncSetup { |
— | — | @@ -114,8 +165,11 @@ |
115 | 166 | self::$ScriptPath = $wgScriptPath . '/extensions' . ( ( $top_dir == 'extensions' ) ? '' : '/' . $top_dir ); |
116 | 167 | |
117 | 168 | if ( !isset( $wgAutoloadClasses['_QXML'] ) ) { |
118 | | - $wgAutoloadClasses['_QXML'] = self::$ExtDir . '/WikiSyncBasic.php'; |
| 169 | + $wgAutoloadClasses['_QXML'] = self::$ExtDir . '/WikiSyncQXML.php'; |
119 | 170 | } |
| 171 | + if ( !isset( $wgAutoloadClasses['FormatJson'] ) ) { |
| 172 | + $wgAutoloadClasses['FormatJson'] = self::$ExtDir . '/WikiSync.php'; |
| 173 | + } |
120 | 174 | $wgAutoloadClasses['Snoopy'] = self::$ExtDir . '/Snoopy/Snoopy.class.php'; |
121 | 175 | $wgAutoloadClasses['WikiSyncSetup'] = self::$ExtDir . '/WikiSync.php'; |
122 | 176 | $wgAutoloadClasses['WikiSnoopy'] = |
— | — | @@ -168,7 +222,7 @@ |
169 | 223 | array( |
170 | 224 | 'scripts' => array( 'md5.js', 'WikiSync_utils.js', 'WikiSync.js' ), |
171 | 225 | 'styles' => 'WikiSync.css', |
172 | | - 'messages' => array_map( 'self::setJSprefix', self::$jsMessages ) |
| 226 | + 'messages' => array_map( array( 'self', 'setJSprefix' ), self::$jsMessages ) |
173 | 227 | ), |
174 | 228 | $localpath, |
175 | 229 | $remotepath |
Index: trunk/extensions/WikiSync/WikiSyncQXML.php |
— | — | @@ -0,0 +1,352 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * ***** BEGIN LICENSE BLOCK ***** |
| 5 | + * This file is part of WikiSync. |
| 6 | + * |
| 7 | + * WikiSync is free software; you can redistribute it and/or modify |
| 8 | + * it under the terms of the GNU General Public License as published by |
| 9 | + * the Free Software Foundation; either version 2 of the License, or |
| 10 | + * (at your option) any later version. |
| 11 | + * |
| 12 | + * WikiSync is distributed in the hope that it will be useful, |
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | + * GNU General Public License for more details. |
| 16 | + * |
| 17 | + * You should have received a copy of the GNU General Public License |
| 18 | + * along with WikiSync; if not, write to the Free Software |
| 19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 20 | + * |
| 21 | + * ***** END LICENSE BLOCK ***** |
| 22 | + * |
| 23 | + * WikiSync allows an AJAX-based synchronization of revisions and files between |
| 24 | + * global wiki site and it's local mirror. |
| 25 | + * |
| 26 | + * To activate this extension : |
| 27 | + * * Create a new directory named WikiSync into the directory "extensions" of MediaWiki. |
| 28 | + * * Place the files from the extension archive there. |
| 29 | + * * Add this line at the end of your LocalSettings.php file : |
| 30 | + * require_once "$IP/extensions/WikiSync/WikiSync.php"; |
| 31 | + * |
| 32 | + * @version 0.3.1 |
| 33 | + * @link http://www.mediawiki.org/wiki/Extension:WikiSync |
| 34 | + * @author Dmitriy Sintsov <questpc@rambler.ru> |
| 35 | + * @addtogroup Extensions |
| 36 | + */ |
| 37 | + |
| 38 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 39 | + die( "This file is a part of MediaWiki extension.\n" ); |
| 40 | +} |
| 41 | + |
| 42 | +/** |
| 43 | + * render output data v0.2 |
| 44 | + */ |
| 45 | +class _QXML { |
| 46 | + |
| 47 | + /** |
| 48 | + * The sample stucture of $tag array is like this: |
| 49 | + * array( '__tag'=>'td', 'class'=>'myclass', 0=>'text before li', 1=>array( '__tag'=>'li', 0=>'text inside li' ), 2=>'text after li' ) |
| 50 | + * |
| 51 | + * '__tag' key specifies node name |
| 52 | + * associative keys specify node attributes |
| 53 | + * numeric keys specify inner nodes of node |
| 54 | + * |
| 55 | + * both tagged (with '__tag' attribute) and tagless lists are supported |
| 56 | + * |
| 57 | + * tagless lists cannot have associative keys (node attributes) |
| 58 | + * |
| 59 | + */ |
| 60 | + |
| 61 | + # next tags ignore text padding on opening (safe indent) |
| 62 | + static $inner_indent_tags = array( 'table', 'tbody', 'tr' ); |
| 63 | + # next tags ignore text padding on closing (safe indent) |
| 64 | + static $outer_indent_tags = array( 'table', 'tbody', 'tr', 'th', 'td', 'p' ); |
| 65 | + # next tags can be self-closed, according to |
| 66 | + # http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd |
| 67 | + # otherwise, simple switching of Content-Type / DOCTYPE may make generated tree invalid |
| 68 | + # see also |
| 69 | + # http://stackoverflow.com/questions/97522/what-are-all-the-valid-self-closing-tags-in-xhtml-as-implemented-by-the-major-br |
| 70 | + static $self_closed_tags = array( 'base', 'meta', 'link', 'hr', 'br', 'basefont', 'param', 'img', 'area', 'input', 'isindex', 'col' ); |
| 71 | + |
| 72 | + # indent types |
| 73 | + # initial caller |
| 74 | + const TOP_NODE = -1; |
| 75 | + # text node |
| 76 | + const NODE_TEXT = 0; |
| 77 | + # tag without indentation |
| 78 | + const NO_INDENT = 1; |
| 79 | + # tag with outer indent |
| 80 | + const OUTER_INDENT = 2; |
| 81 | + # tag with inner indent |
| 82 | + const INNER_INDENT = 3; |
| 83 | + |
| 84 | + # used for detection of indent in non-tagged list of nodes |
| 85 | + static $prev_indent_type; |
| 86 | + |
| 87 | + /** |
| 88 | + * used for erroneous $tag content reporting |
| 89 | + */ |
| 90 | + static function getTagDump( &$tag ) { |
| 91 | + ob_start(); |
| 92 | + var_dump( $tag ); |
| 93 | + $tagdump = ob_get_contents(); |
| 94 | + ob_end_clean(); |
| 95 | + return $tagdump; |
| 96 | + } |
| 97 | + |
| 98 | + /** |
| 99 | + * recursive tags generator |
| 100 | + * @param $tag nested associative array of tag nodes (see an example above) |
| 101 | + * @param $indent level of indentation (negative to completely suppress indent) |
| 102 | + */ |
| 103 | + static function toText( &$tag, $indent = -1 ) { |
| 104 | + self::$prev_indent_type = self::TOP_NODE; |
| 105 | + return self::_toText( $tag, $indent, self::TOP_NODE ); |
| 106 | + } |
| 107 | + |
| 108 | + /** |
| 109 | + * recursive tags generator |
| 110 | + * @param $tag nested associative array of tag nodes (see an example above) |
| 111 | + * @param $indent level of indentation (negative to completely suppress indent) |
| 112 | + * @param $caller_indent_type indent type of lower level (caller) |
| 113 | + */ |
| 114 | + static private function _toText( &$tag, $indent = -1, $caller_indent_type ) { |
| 115 | + $tag_open = |
| 116 | + $tag_close = ''; |
| 117 | + # $tag_val is a recusively concatenated inner content of tag |
| 118 | + # by default, null value indicates a self-closing tag |
| 119 | + $tag_val = null; |
| 120 | + # current and nested indent levels |
| 121 | + $nested_indent = $indent; |
| 122 | + if ( is_array( $tag ) ) { |
| 123 | + ksort( $tag ); |
| 124 | + $current_indent_type = self::NODE_TEXT; |
| 125 | + if ( isset( $tag['__tag'] ) ) { |
| 126 | + $tag_name = strtolower( $tag['__tag'] ); |
| 127 | + $current_indent_type = self::NO_INDENT; |
| 128 | + if ( $indent >= 0 ) { |
| 129 | + # inner has predecense (outer contains inner inside) |
| 130 | + if ( in_array( $tag_name, self::$inner_indent_tags ) ) { |
| 131 | + $current_indent_type = self::INNER_INDENT; |
| 132 | + # also indent every indented tag that is inside |
| 133 | + $nested_indent++; |
| 134 | + } elseif ( in_array( $tag_name, self::$outer_indent_tags ) ) { |
| 135 | + $current_indent_type = self::OUTER_INDENT; |
| 136 | + # also indent every indented tag that is inside |
| 137 | + $nested_indent++; |
| 138 | + } |
| 139 | + } |
| 140 | + # list inside tag |
| 141 | + $tag_open .= '<' . $tag['__tag']; |
| 142 | + foreach ( $tag as $attr_key => &$attr_val ) { |
| 143 | + if ( is_int( $attr_key ) ) { |
| 144 | + # numeric node values are going into $tag_val |
| 145 | + if ( $tag_val === null ) { |
| 146 | + $tag_val = ''; |
| 147 | + } |
| 148 | + if ( is_array( $attr_val ) ) { |
| 149 | + # recursive list |
| 150 | + $tag_val .= self::_toText( $attr_val, $nested_indent, $current_indent_type ); |
| 151 | + } else { |
| 152 | + # text node inside tag |
| 153 | + self::$prev_indent_type = self::NODE_TEXT; |
| 154 | + # use the following format for the debug printouts: "!$attr_val!" |
| 155 | + $tag_val .= $attr_val; |
| 156 | + } |
| 157 | + } else { |
| 158 | + # string keys are for tag attributes |
| 159 | + if ( substr( $attr_key, 0, 2 ) !== '__' ) { |
| 160 | + # include only non-reserved attributes |
| 161 | + $tag_open .= " $attr_key=\"$attr_val\""; |
| 162 | + } |
| 163 | + } |
| 164 | + } |
| 165 | + if ( $tag_val === null && !in_array( $tag_name, self::$self_closed_tags ) ) { |
| 166 | + $tag_val = ''; |
| 167 | + } |
| 168 | + if ( $tag_val === null ) { |
| 169 | + $tag_open .= " />"; |
| 170 | + } else { |
| 171 | + $tag_open .= '>'; |
| 172 | + $tag_close .= '</' . $tag['__tag'] . '>'; |
| 173 | + } |
| 174 | + } else { |
| 175 | + # tagless list |
| 176 | + $tag_val = ''; |
| 177 | + foreach ( $tag as $attr_key => &$attr_val ) { |
| 178 | + if ( is_int( $attr_key ) ) { |
| 179 | + if ( is_array( $attr_val ) ) { |
| 180 | + # recursive tags |
| 181 | + $tag_val .= self::_toText( $attr_val, $indent, $caller_indent_type ); |
| 182 | + } else { |
| 183 | + # text |
| 184 | + if ( self::$prev_indent_type === self::INNER_INDENT ) { |
| 185 | + $attr_val = "\n$attr_val"; |
| 186 | + } |
| 187 | + $caller_indent_type = self::NODE_TEXT; |
| 188 | + # use for debug printout |
| 189 | + # $tag_val .= '~' . $attr_val . self::$prev_indent_type . '~'; |
| 190 | + $tag_val .= $attr_val; |
| 191 | + } |
| 192 | + } else { |
| 193 | + $tag_val = "Invalid argument: tagless list cannot have tag attribute values in key=$attr_key, " . self::getTagDump( $tag ); |
| 194 | + } |
| 195 | + } |
| 196 | + return $tag_val; |
| 197 | + } |
| 198 | + } else { |
| 199 | + # just a text; use "?$tag?" for debug printout |
| 200 | + return $tag; |
| 201 | + } |
| 202 | + # uncomment for the debug printout |
| 203 | + # $tag_close .= "($current_indent_type,$caller_indent_type," . self::$prev_indent_type . ")"; |
| 204 | + if ( $current_indent_type === self::INNER_INDENT ) { |
| 205 | + $tag_open = str_repeat( "\t", $indent ) . "$tag_open\n"; |
| 206 | + $tag_close = str_repeat( "\t", $indent ) . $tag_close; |
| 207 | + if ( in_array( $caller_indent_type, array( self::TOP_NODE, self::INNER_INDENT ) ) ) { |
| 208 | + $tag_close = "$tag_close\n"; |
| 209 | + if ( self::$prev_indent_type === self::NODE_TEXT ) { |
| 210 | + $tag_open = "\n$tag_open"; |
| 211 | + } |
| 212 | + } else { |
| 213 | + $tag_open = "\n$tag_open"; |
| 214 | + } |
| 215 | + } elseif ( $current_indent_type === self::OUTER_INDENT ) { |
| 216 | + if ( $caller_indent_type === self::INNER_INDENT ) { |
| 217 | + $tag_open = str_repeat( "\t", $indent ) . $tag_open; |
| 218 | + $tag_close = "$tag_close\n"; |
| 219 | + } |
| 220 | + if ( self::$prev_indent_type === self::INNER_INDENT ) { |
| 221 | + $tag_close = "\n" . str_repeat( "\t", $indent ) . $tag_close; |
| 222 | + } |
| 223 | + } elseif ( $current_indent_type === self::NO_INDENT ) { |
| 224 | + if ( $indent >= 0 && $caller_indent_type === self::INNER_INDENT ) { |
| 225 | + $tag_close .= "\n"; |
| 226 | + if ( self::$prev_indent_type === self::INNER_INDENT ) { |
| 227 | + $tag_close = "\n$tag_close"; |
| 228 | + } |
| 229 | + } |
| 230 | + } elseif ( $current_indent_type === self::NODE_TEXT ) { |
| 231 | + if ( self::$prev_indent_type === self::INNER_INDENT ) { |
| 232 | + $tag_close = "\n$tag_close"; |
| 233 | + } |
| 234 | + } |
| 235 | + # we support __end only for compatibility to older versions |
| 236 | + # it's deprecated and the usage is discouraged |
| 237 | + if ( isset( $tag['__end'] ) ) { |
| 238 | + $end = $tag['__end']; |
| 239 | + if ( $end === "\n" ) { |
| 240 | + if ( substr( $tag_close, -1 ) === "\n" ) { |
| 241 | + $end = ''; |
| 242 | + } |
| 243 | + } |
| 244 | + $tag_close .= $end; |
| 245 | + } |
| 246 | + self::$prev_indent_type = $current_indent_type; |
| 247 | + return $tag_open . $tag_val . $tag_close; |
| 248 | + } |
| 249 | + |
| 250 | + # creates one "htmlobject" row of the table |
| 251 | + # elements of $row can be either a string/number value of cell or an array( "count"=>colspannum, "attribute"=>value, 0=>html_inside_tag ) |
| 252 | + # attribute maps can be like this: ("name"=>0, "count"=>colspan" ) |
| 253 | + static function newRow( $row, $rowattrs = "", $celltag = "td", $attribute_maps = null ) { |
| 254 | + $result = ""; |
| 255 | + if ( count( $row ) > 0 ) { |
| 256 | + foreach ( $row as &$cell ) { |
| 257 | + if ( !is_array( $cell ) ) { |
| 258 | + $cell = array( 0 => $cell ); |
| 259 | + } |
| 260 | + $cell[ '__tag' ] = $celltag; |
| 261 | + if ( is_array( $attribute_maps ) ) { |
| 262 | + # converts ("count"=>3) to ("colspan"=>3) in table headers - don't use frequently |
| 263 | + foreach ( $attribute_maps as $key => $val ) { |
| 264 | + if ( isset( $cell[$key] ) ) { |
| 265 | + $cell[ $val ] = $cell[ $key ]; |
| 266 | + unset( $cell[ $key ] ); |
| 267 | + } |
| 268 | + } |
| 269 | + } |
| 270 | + } |
| 271 | + $result = array( '__tag' => 'tr', 0 => $row ); |
| 272 | + if ( is_array( $rowattrs ) ) { |
| 273 | + $result = array_merge( $rowattrs, $result ); |
| 274 | + } elseif ( $rowattrs !== "" ) { |
| 275 | + $result[0][] = __METHOD__ . ':invalid rowattrs supplied'; |
| 276 | + } |
| 277 | + } |
| 278 | + return $result; |
| 279 | + } |
| 280 | + |
| 281 | + # add row to the table |
| 282 | + static function addRow( &$table, $row, $rowattrs = "", $celltag = "td", $attribute_maps = null ) { |
| 283 | + $table[] = self::newRow( $row, $rowattrs, $celltag, $attribute_maps ); |
| 284 | + } |
| 285 | + |
| 286 | + # add column to the table |
| 287 | + static function addColumn( &$table, $column, $rowattrs = "", $celltag = "td", $attribute_maps = null ) { |
| 288 | + if ( count( $column ) > 0 ) { |
| 289 | + $row = 0; |
| 290 | + foreach ( $column as &$cell ) { |
| 291 | + if ( !is_array( $cell ) ) { |
| 292 | + $cell = array( 0 => $cell ); |
| 293 | + } |
| 294 | + $cell[ '__tag' ] = $celltag; |
| 295 | + if ( is_array( $attribute_maps ) ) { |
| 296 | + # converts ("count"=>3) to ("rowspan"=>3) in table headers - don't use frequently |
| 297 | + foreach ( $attribute_maps as $key => $val ) { |
| 298 | + if ( isset( $cell[$key] ) ) { |
| 299 | + $cell[ $val ] = $cell[ $key ]; |
| 300 | + unset( $cell[ $key ] ); |
| 301 | + } |
| 302 | + } |
| 303 | + } |
| 304 | + if ( is_array( $rowattrs ) ) { |
| 305 | + $cell = array_merge( $rowattrs, $cell ); |
| 306 | + } elseif ( $rowattrs !== "" ) { |
| 307 | + $cell[ 0 ] = __METHOD__ . ':invalid rowattrs supplied'; |
| 308 | + } |
| 309 | + if ( !isset( $table[$row] ) ) { |
| 310 | + $table[ $row ] = array( '__tag' => 'tr' ); |
| 311 | + } |
| 312 | + $table[ $row ][] = $cell; |
| 313 | + if ( isset( $cell['rowspan'] ) ) { |
| 314 | + $row += intval( $cell[ 'rowspan' ] ); |
| 315 | + } else { |
| 316 | + $row++; |
| 317 | + } |
| 318 | + } |
| 319 | + $result = array( '__tag' => 'tr', 0 => $column ); |
| 320 | + } |
| 321 | + } |
| 322 | + |
| 323 | + static function displayRow( $row, $rowattrs = "", $celltag = "td", $attribute_maps = null ) { |
| 324 | + return self::toText( self::newRow( $row, $rowattrs, $celltag, $attribute_maps ) ); |
| 325 | + } |
| 326 | + |
| 327 | + // use newRow() or addColumn() to add resulting row/column to the table |
| 328 | + // if you want to use the resulting row with toText(), don't forget to apply attrs=array('__tag'=>'td') |
| 329 | + static function applyAttrsToRow( &$row, $attrs ) { |
| 330 | + if ( is_array( $attrs ) && count( $attrs > 0 ) ) { |
| 331 | + foreach ( $row as &$cell ) { |
| 332 | + if ( !is_array( $cell ) ) { |
| 333 | + $cell = array_merge( $attrs, array( $cell ) ); |
| 334 | + } else { |
| 335 | + foreach ( $attrs as $attr_key => $attr_val ) { |
| 336 | + if ( !isset( $cell[$attr_key] ) ) { |
| 337 | + $cell[ $attr_key ] = $attr_val; |
| 338 | + } |
| 339 | + } |
| 340 | + } |
| 341 | + } |
| 342 | + } |
| 343 | + } |
| 344 | + |
| 345 | + static function entities( $s ) { |
| 346 | + return htmlentities( $s, ENT_COMPAT, 'UTF-8' ); |
| 347 | + } |
| 348 | + |
| 349 | + static function specialchars( $s ) { |
| 350 | + return htmlspecialchars( $s, ENT_COMPAT, 'UTF-8' ); |
| 351 | + } |
| 352 | + |
| 353 | +} /* end of _QXML class */ |
Property changes on: trunk/extensions/WikiSync/WikiSyncQXML.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 354 | + native |
Index: trunk/extensions/WikiSync/WikiSync_utils.js |
— | — | @@ -132,13 +132,15 @@ |
133 | 133 | */ |
134 | 134 | window.WikiSyncPercentsIndicator = function( id ) { |
135 | 135 | this.topElement = document.getElementById( id ); |
136 | | - var tr1 = this.topElement.firstChild.firstChild; |
| 136 | + // cannot use .firstChild here, because in FF indentation text nodes are inserted |
| 137 | + // between TABLE / TR / TD |
| 138 | + // (in IE8 the indentation is ignored and .firstChild worked) |
| 139 | + var elements = this.topElement.getElementsByTagName( 'TD' ); |
137 | 140 | // description line will be stored there |
138 | | - this.descriptionContainer = tr1.firstChild; |
139 | | - var tr2 = tr1.nextSibling; |
| 141 | + this.descriptionContainer = elements[0]; |
140 | 142 | // td1 and td2 are used together as percent indicators |
141 | | - this.td1 = tr2.firstChild; |
142 | | - this.td2 = this.td1.nextSibling; |
| 143 | + this.td1 = elements[1]; |
| 144 | + this.td2 = elements[2]; |
143 | 145 | this.reset(); |
144 | 146 | } |
145 | 147 | |