Commit 47bad912c6c0c2d727556c79de5b65e0c3d4de71
1 parent
e249362a
Exists in
master
and in
7 other branches
movido cpaint para pacotes
Showing
12 changed files
with
5260 additions
and
0 deletions
Show diff stats
| ... | ... | @@ -0,0 +1,377 @@ |
| 1 | +/* | |
| 2 | +Copyright (c) 2005 JSON.org | |
| 3 | + | |
| 4 | +Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | +of this software and associated documentation files (the "Software"), to deal | |
| 6 | +in the Software without restriction, including without limitation the rights | |
| 7 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | +copies of the Software, and to permit persons to whom the Software is | |
| 9 | +furnished to do so, subject to the following conditions: | |
| 10 | + | |
| 11 | +The Software shall be used for Good, not Evil. | |
| 12 | + | |
| 13 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 14 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 15 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 16 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 17 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 18 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 19 | +SOFTWARE. | |
| 20 | +*/ | |
| 21 | + | |
| 22 | +Array.prototype.______array = '______array'; | |
| 23 | + | |
| 24 | +var JSON = { | |
| 25 | + org: 'http://www.JSON.org', | |
| 26 | + copyright: '(c)2005 JSON.org', | |
| 27 | + license: 'http://www.crockford.com/JSON/license.html', | |
| 28 | + | |
| 29 | + stringify: function (arg) { | |
| 30 | + var c, i, l, s = '', v; | |
| 31 | + var numeric = true; | |
| 32 | + | |
| 33 | + switch (typeof arg) { | |
| 34 | + case 'object': | |
| 35 | + if (arg) { | |
| 36 | + if (arg.______array == '______array') { | |
| 37 | + // do a test whether all array keys are numeric | |
| 38 | + for (i in arg) { | |
| 39 | + if (i != '______array' | |
| 40 | + && (isNaN(i) | |
| 41 | + || !isFinite(i))) { | |
| 42 | + numeric = false; | |
| 43 | + break; | |
| 44 | + } | |
| 45 | + } | |
| 46 | + | |
| 47 | + if (numeric == true) { | |
| 48 | + for (i = 0; i < arg.length; ++i) { | |
| 49 | + if (typeof arg[i] != 'undefined') { | |
| 50 | + v = this.stringify(arg[i]); | |
| 51 | + if (s) { | |
| 52 | + s += ','; | |
| 53 | + } | |
| 54 | + s += v; | |
| 55 | + } else { | |
| 56 | + s += ',null'; | |
| 57 | + } | |
| 58 | + } | |
| 59 | + return '[' + s + ']'; | |
| 60 | + } else { | |
| 61 | + for (i in arg) { | |
| 62 | + if (i != '______array') { | |
| 63 | + v = arg[i]; | |
| 64 | + if (typeof v != 'undefined' && typeof v != 'function') { | |
| 65 | + v = this.stringify(v); | |
| 66 | + if (s) { | |
| 67 | + s += ','; | |
| 68 | + } | |
| 69 | + s += this.stringify(i) + ':' + v; | |
| 70 | + } | |
| 71 | + } | |
| 72 | + } | |
| 73 | + // return as object | |
| 74 | + return '{' + s + '}'; | |
| 75 | + } | |
| 76 | + } else if (typeof arg.toString != 'undefined') { | |
| 77 | + for (i in arg) { | |
| 78 | + v = arg[i]; | |
| 79 | + if (typeof v != 'undefined' && typeof v != 'function') { | |
| 80 | + v = this.stringify(v); | |
| 81 | + if (s) { | |
| 82 | + s += ','; | |
| 83 | + } | |
| 84 | + s += this.stringify(i) + ':' + v; | |
| 85 | + } | |
| 86 | + } | |
| 87 | + return '{' + s + '}'; | |
| 88 | + } | |
| 89 | + } | |
| 90 | + return 'null'; | |
| 91 | + case 'number': | |
| 92 | + return isFinite(arg) ? String(arg) : 'null'; | |
| 93 | + case 'string': | |
| 94 | + l = arg.length; | |
| 95 | + s = '"'; | |
| 96 | + for (i = 0; i < l; i += 1) { | |
| 97 | + c = arg.charAt(i); | |
| 98 | + if (c >= ' ') { | |
| 99 | + if (c == '\\' || c == '"') { | |
| 100 | + s += '\\'; | |
| 101 | + } | |
| 102 | + s += c; | |
| 103 | + } else { | |
| 104 | + switch (c) { | |
| 105 | + case '\b': | |
| 106 | + s += '\\b'; | |
| 107 | + break; | |
| 108 | + case '\f': | |
| 109 | + s += '\\f'; | |
| 110 | + break; | |
| 111 | + case '\n': | |
| 112 | + s += '\\n'; | |
| 113 | + break; | |
| 114 | + case '\r': | |
| 115 | + s += '\\r'; | |
| 116 | + break; | |
| 117 | + case '\t': | |
| 118 | + s += '\\t'; | |
| 119 | + break; | |
| 120 | + default: | |
| 121 | + c = c.charCodeAt(); | |
| 122 | + s += '\\u00' + Math.floor(c / 16).toString(16) + | |
| 123 | + (c % 16).toString(16); | |
| 124 | + } | |
| 125 | + } | |
| 126 | + } | |
| 127 | + return s + '"'; | |
| 128 | + case 'boolean': | |
| 129 | + return String(arg); | |
| 130 | + default: | |
| 131 | + return 'null'; | |
| 132 | + } | |
| 133 | + }, | |
| 134 | + parse: function (text) { | |
| 135 | + var at = 0; | |
| 136 | + var ch = ' '; | |
| 137 | + | |
| 138 | + function error(m) { | |
| 139 | + throw { | |
| 140 | + name: 'JSONError', | |
| 141 | + message: m, | |
| 142 | + at: at - 1, | |
| 143 | + text: text | |
| 144 | + }; | |
| 145 | + } | |
| 146 | + | |
| 147 | + function next() { | |
| 148 | + ch = text.charAt(at); | |
| 149 | + at += 1; | |
| 150 | + return ch; | |
| 151 | + } | |
| 152 | + | |
| 153 | + function white() { | |
| 154 | + while (ch != '' && ch <= ' ') { | |
| 155 | + next(); | |
| 156 | + } | |
| 157 | + } | |
| 158 | + | |
| 159 | + function str() { | |
| 160 | + var i, s = '', t, u; | |
| 161 | + | |
| 162 | + if (ch == '"') { | |
| 163 | +outer: while (next()) { | |
| 164 | + if (ch == '"') { | |
| 165 | + next(); | |
| 166 | + return s; | |
| 167 | + } else if (ch == '\\') { | |
| 168 | + switch (next()) { | |
| 169 | + case 'b': | |
| 170 | + s += '\b'; | |
| 171 | + break; | |
| 172 | + case 'f': | |
| 173 | + s += '\f'; | |
| 174 | + break; | |
| 175 | + case 'n': | |
| 176 | + s += '\n'; | |
| 177 | + break; | |
| 178 | + case 'r': | |
| 179 | + s += '\r'; | |
| 180 | + break; | |
| 181 | + case 't': | |
| 182 | + s += '\t'; | |
| 183 | + break; | |
| 184 | + case 'u': | |
| 185 | + u = 0; | |
| 186 | + for (i = 0; i < 4; i += 1) { | |
| 187 | + t = parseInt(next(), 16); | |
| 188 | + if (!isFinite(t)) { | |
| 189 | + break outer; | |
| 190 | + } | |
| 191 | + u = u * 16 + t; | |
| 192 | + } | |
| 193 | + s += String.fromCharCode(u); | |
| 194 | + break; | |
| 195 | + default: | |
| 196 | + s += ch; | |
| 197 | + } | |
| 198 | + } else { | |
| 199 | + s += ch; | |
| 200 | + } | |
| 201 | + } | |
| 202 | + } | |
| 203 | + error("Bad string"); | |
| 204 | + } | |
| 205 | + | |
| 206 | + function arr() { | |
| 207 | + var a = []; | |
| 208 | + | |
| 209 | + if (ch == '[') { | |
| 210 | + next(); | |
| 211 | + white(); | |
| 212 | + if (ch == ']') { | |
| 213 | + next(); | |
| 214 | + return a; | |
| 215 | + } | |
| 216 | + while (ch) { | |
| 217 | + a.push(val()); | |
| 218 | + white(); | |
| 219 | + if (ch == ']') { | |
| 220 | + next(); | |
| 221 | + return a; | |
| 222 | + } else if (ch != ',') { | |
| 223 | + break; | |
| 224 | + } | |
| 225 | + next(); | |
| 226 | + white(); | |
| 227 | + } | |
| 228 | + } | |
| 229 | + error("Bad array"); | |
| 230 | + } | |
| 231 | + | |
| 232 | + function obj() { | |
| 233 | + var k, o = {}; | |
| 234 | + | |
| 235 | + if (ch == '{') { | |
| 236 | + next(); | |
| 237 | + white(); | |
| 238 | + if (ch == '}') { | |
| 239 | + next(); | |
| 240 | + return o; | |
| 241 | + } | |
| 242 | + while (ch) { | |
| 243 | + k = str(); | |
| 244 | + white(); | |
| 245 | + if (ch != ':') { | |
| 246 | + break; | |
| 247 | + } | |
| 248 | + next(); | |
| 249 | + o[k] = val(); | |
| 250 | + white(); | |
| 251 | + if (ch == '}') { | |
| 252 | + next(); | |
| 253 | + return o; | |
| 254 | + } else if (ch != ',') { | |
| 255 | + break; | |
| 256 | + } | |
| 257 | + next(); | |
| 258 | + white(); | |
| 259 | + } | |
| 260 | + } | |
| 261 | + error("Bad object"); | |
| 262 | + } | |
| 263 | + | |
| 264 | + function assoc() { | |
| 265 | + var k, a = []; | |
| 266 | + | |
| 267 | + if (ch == '<') { | |
| 268 | + next(); | |
| 269 | + white(); | |
| 270 | + if (ch == '>') { | |
| 271 | + next(); | |
| 272 | + return a; | |
| 273 | + } | |
| 274 | + while (ch) { | |
| 275 | + k = str(); | |
| 276 | + white(); | |
| 277 | + if (ch != ':') { | |
| 278 | + break; | |
| 279 | + } | |
| 280 | + next(); | |
| 281 | + a[k] = val(); | |
| 282 | + white(); | |
| 283 | + if (ch == '>') { | |
| 284 | + next(); | |
| 285 | + return a; | |
| 286 | + } else if (ch != ',') { | |
| 287 | + break; | |
| 288 | + } | |
| 289 | + next(); | |
| 290 | + white(); | |
| 291 | + } | |
| 292 | + } | |
| 293 | + error("Bad associative array"); | |
| 294 | + } | |
| 295 | + | |
| 296 | + function num() { | |
| 297 | + var n = '', v; | |
| 298 | + if (ch == '-') { | |
| 299 | + n = '-'; | |
| 300 | + next(); | |
| 301 | + } | |
| 302 | + while (ch >= '0' && ch <= '9') { | |
| 303 | + n += ch; | |
| 304 | + next(); | |
| 305 | + } | |
| 306 | + if (ch == '.') { | |
| 307 | + n += '.'; | |
| 308 | + while (next() && ch >= '0' && ch <= '9') { | |
| 309 | + n += ch; | |
| 310 | + } | |
| 311 | + } | |
| 312 | + if (ch == 'e' || ch == 'E') { | |
| 313 | + n += 'e'; | |
| 314 | + next(); | |
| 315 | + if (ch == '-' || ch == '+') { | |
| 316 | + n += ch; | |
| 317 | + next(); | |
| 318 | + } | |
| 319 | + while (ch >= '0' && ch <= '9') { | |
| 320 | + n += ch; | |
| 321 | + next(); | |
| 322 | + } | |
| 323 | + } | |
| 324 | + v = +n; | |
| 325 | + if (!isFinite(v)) { | |
| 326 | + error("Bad number"); | |
| 327 | + } else { | |
| 328 | + return v; | |
| 329 | + } | |
| 330 | + } | |
| 331 | + | |
| 332 | + function word() { | |
| 333 | + switch (ch) { | |
| 334 | + case 't': | |
| 335 | + if (next() == 'r' && next() == 'u' && next() == 'e') { | |
| 336 | + next(); | |
| 337 | + return true; | |
| 338 | + } | |
| 339 | + break; | |
| 340 | + case 'f': | |
| 341 | + if (next() == 'a' && next() == 'l' && next() == 's' && | |
| 342 | + next() == 'e') { | |
| 343 | + next(); | |
| 344 | + return false; | |
| 345 | + } | |
| 346 | + break; | |
| 347 | + case 'n': | |
| 348 | + if (next() == 'u' && next() == 'l' && next() == 'l') { | |
| 349 | + next(); | |
| 350 | + return null; | |
| 351 | + } | |
| 352 | + break; | |
| 353 | + } | |
| 354 | + error("Syntax error"); | |
| 355 | + } | |
| 356 | + | |
| 357 | + function val() { | |
| 358 | + white(); | |
| 359 | + switch (ch) { | |
| 360 | + case '{': | |
| 361 | + return obj(); | |
| 362 | + case '[': | |
| 363 | + return arr(); | |
| 364 | + case '<': | |
| 365 | + return assoc(); | |
| 366 | + case '"': | |
| 367 | + return str(); | |
| 368 | + case '-': | |
| 369 | + return num(); | |
| 370 | + default: | |
| 371 | + return ch >= '0' && ch <= '9' ? num() : word(); | |
| 372 | + } | |
| 373 | + } | |
| 374 | + | |
| 375 | + return val(); | |
| 376 | + } | |
| 377 | +}; | ... | ... |
| ... | ... | @@ -0,0 +1,663 @@ |
| 1 | +<?php | |
| 2 | +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ | |
| 3 | + | |
| 4 | +/** | |
| 5 | +* Converts to and from JSON format. | |
| 6 | +* | |
| 7 | +* JSON (JavaScript Object Notation) is a lightweight data-interchange | |
| 8 | +* format. It is easy for humans to read and write. It is easy for machines | |
| 9 | +* to parse and generate. It is based on a subset of the JavaScript | |
| 10 | +* Programming Language, Standard ECMA-262 3rd Edition - December 1999. | |
| 11 | +* This feature can also be found in Python. JSON is a text format that is | |
| 12 | +* completely language independent but uses conventions that are familiar | |
| 13 | +* to programmers of the C-family of languages, including C, C++, C#, Java, | |
| 14 | +* JavaScript, Perl, TCL, and many others. These properties make JSON an | |
| 15 | +* ideal data-interchange language. | |
| 16 | +* | |
| 17 | +* This package provides a simple encoder and decoder for JSON notation. It | |
| 18 | +* is intended for use with client-side Javascript applications that make | |
| 19 | +* use of HTTPRequest to perform server communication functions - data can | |
| 20 | +* be encoded into JSON notation for use in a client-side javascript, or | |
| 21 | +* decoded from incoming Javascript requests. JSON format is native to | |
| 22 | +* Javascript, and can be directly eval()'ed with no further parsing | |
| 23 | +* overhead | |
| 24 | +* | |
| 25 | +* All strings should be in ASCII or UTF-8 format! | |
| 26 | +* | |
| 27 | +* LICENSE: Redistribution and use in source and binary forms, with or | |
| 28 | +* without modification, are permitted provided that the following | |
| 29 | +* conditions are met: Redistributions of source code must retain the | |
| 30 | +* above copyright notice, this list of conditions and the following | |
| 31 | +* disclaimer. Redistributions in binary form must reproduce the above | |
| 32 | +* copyright notice, this list of conditions and the following disclaimer | |
| 33 | +* in the documentation and/or other materials provided with the | |
| 34 | +* distribution. | |
| 35 | +* | |
| 36 | +* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
| 37 | +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| 38 | +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | |
| 39 | +* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 40 | +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
| 41 | +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | |
| 42 | +* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
| 43 | +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | |
| 44 | +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | |
| 45 | +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | |
| 46 | +* DAMAGE. | |
| 47 | +* | |
| 48 | +* @category | |
| 49 | +* @package Services_JSON | |
| 50 | +* @author Michal Migurski <mike-json@teczno.com> | |
| 51 | +* @author Matt Knapp <mdknapp[at]gmail[dot]com> | |
| 52 | +* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> | |
| 53 | +* @copyright 2005 Michal Migurski | |
| 54 | +* @license http://www.opensource.org/licenses/bsd-license.php | |
| 55 | +* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 | |
| 56 | +*/ | |
| 57 | + | |
| 58 | +/** | |
| 59 | +* Marker constant for Services_JSON::decode(), used to flag stack state | |
| 60 | +*/ | |
| 61 | +define('SERVICES_JSON_SLICE', 1); | |
| 62 | + | |
| 63 | +/** | |
| 64 | +* Marker constant for Services_JSON::decode(), used to flag stack state | |
| 65 | +*/ | |
| 66 | +define('SERVICES_JSON_IN_STR', 2); | |
| 67 | + | |
| 68 | +/** | |
| 69 | +* Marker constant for Services_JSON::decode(), used to flag stack state | |
| 70 | +*/ | |
| 71 | +define('SERVICES_JSON_IN_ARR', 4); | |
| 72 | + | |
| 73 | +/** | |
| 74 | +* Marker constant for Services_JSON::decode(), used to flag stack state | |
| 75 | +*/ | |
| 76 | +define('SERVICES_JSON_IN_OBJ', 8); | |
| 77 | + | |
| 78 | +/** | |
| 79 | +* Marker constant for Services_JSON::decode(), used to flag stack state | |
| 80 | +*/ | |
| 81 | +define('SERVICES_JSON_IN_CMT', 16); | |
| 82 | + | |
| 83 | +/** | |
| 84 | +* Behavior switch for Services_JSON::decode() | |
| 85 | +*/ | |
| 86 | +define('SERVICES_JSON_LOOSE_TYPE', 10); | |
| 87 | + | |
| 88 | +/** | |
| 89 | +* Behavior switch for Services_JSON::decode() | |
| 90 | +*/ | |
| 91 | +define('SERVICES_JSON_STRICT_TYPE', 11); | |
| 92 | + | |
| 93 | +/** | |
| 94 | +* Converts to and from JSON format. | |
| 95 | +* | |
| 96 | +* Brief example of use: | |
| 97 | +* | |
| 98 | +* <code> | |
| 99 | +* // create a new instance of Services_JSON | |
| 100 | +* $json = new Services_JSON(); | |
| 101 | +* | |
| 102 | +* // convert a complexe value to JSON notation, and send it to the browser | |
| 103 | +* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); | |
| 104 | +* $output = $json->encode($value); | |
| 105 | +* | |
| 106 | +* print($output); | |
| 107 | +* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] | |
| 108 | +* | |
| 109 | +* // accept incoming POST data, assumed to be in JSON notation | |
| 110 | +* $input = file_get_contents('php://input', 1000000); | |
| 111 | +* $value = $json->decode($input); | |
| 112 | +* </code> | |
| 113 | +*/ | |
| 114 | +class Services_JSON | |
| 115 | +{ | |
| 116 | + /** | |
| 117 | + * constructs a new JSON instance | |
| 118 | + * | |
| 119 | + * @param int $use object behavior: when encoding or decoding, | |
| 120 | + * be loose or strict about object/array usage | |
| 121 | + * | |
| 122 | + * possible values: | |
| 123 | + * - SERVICES_JSON_STRICT_TYPE: strict typing, default. | |
| 124 | + * "{...}" syntax creates objects in decode(). | |
| 125 | + * - SERVICES_JSON_LOOSE_TYPE: loose typing. | |
| 126 | + * "{...}" syntax creates associative arrays in decode(). | |
| 127 | + */ | |
| 128 | + function Services_JSON($use = SERVICES_JSON_STRICT_TYPE) | |
| 129 | + { | |
| 130 | + $this->use = $use; | |
| 131 | + } | |
| 132 | + | |
| 133 | + /** | |
| 134 | + * convert a string from one UTF-16 char to one UTF-8 char | |
| 135 | + * | |
| 136 | + * Normally should be handled by mb_convert_encoding, but | |
| 137 | + * provides a slower PHP-only method for installations | |
| 138 | + * that lack the multibye string extension. | |
| 139 | + * | |
| 140 | + * @param string $utf16 UTF-16 character | |
| 141 | + * @return string UTF-8 character | |
| 142 | + * @access private | |
| 143 | + */ | |
| 144 | + function utf162utf8($utf16) | |
| 145 | + { | |
| 146 | + // oh please oh please oh please oh please oh please | |
| 147 | + if(function_exists('mb_convert_encoding')) | |
| 148 | + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); | |
| 149 | + | |
| 150 | + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); | |
| 151 | + | |
| 152 | + switch(true) { | |
| 153 | + case ((0x7F & $bytes) == $bytes): | |
| 154 | + // this case should never be reached, because we are in ASCII range | |
| 155 | + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |
| 156 | + return chr(0x7F & $bytes); | |
| 157 | + | |
| 158 | + case (0x07FF & $bytes) == $bytes: | |
| 159 | + // return a 2-byte UTF-8 character | |
| 160 | + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |
| 161 | + return chr(0xC0 | (($bytes >> 6) & 0x1F)) | |
| 162 | + . chr(0x80 | ($bytes & 0x3F)); | |
| 163 | + | |
| 164 | + case (0xFFFF & $bytes) == $bytes: | |
| 165 | + // return a 3-byte UTF-8 character | |
| 166 | + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |
| 167 | + return chr(0xE0 | (($bytes >> 12) & 0x0F)) | |
| 168 | + . chr(0x80 | (($bytes >> 6) & 0x3F)) | |
| 169 | + . chr(0x80 | ($bytes & 0x3F)); | |
| 170 | + } | |
| 171 | + | |
| 172 | + // ignoring UTF-32 for now, sorry | |
| 173 | + return ''; | |
| 174 | + } | |
| 175 | + | |
| 176 | + /** | |
| 177 | + * convert a string from one UTF-8 char to one UTF-16 char | |
| 178 | + * | |
| 179 | + * Normally should be handled by mb_convert_encoding, but | |
| 180 | + * provides a slower PHP-only method for installations | |
| 181 | + * that lack the multibye string extension. | |
| 182 | + * | |
| 183 | + * @param string $utf8 UTF-8 character | |
| 184 | + * @return string UTF-16 character | |
| 185 | + * @access private | |
| 186 | + */ | |
| 187 | + function utf82utf16($utf8) | |
| 188 | + { | |
| 189 | + // oh please oh please oh please oh please oh please | |
| 190 | + if(function_exists('mb_convert_encoding')) | |
| 191 | + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); | |
| 192 | + | |
| 193 | + switch(strlen($utf8)) { | |
| 194 | + case 1: | |
| 195 | + // this case should never be reached, because we are in ASCII range | |
| 196 | + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |
| 197 | + return $utf8; //$ut8 | |
| 198 | + | |
| 199 | + case 2: | |
| 200 | + // return a UTF-16 character from a 2-byte UTF-8 char | |
| 201 | + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |
| 202 | + return chr(0x07 & (ord($utf8{0}) >> 2)) | |
| 203 | + . chr((0xC0 & (ord($utf8{0}) << 6)) | |
| 204 | + | (0x3F & ord($utf8{1}))); | |
| 205 | + | |
| 206 | + case 3: | |
| 207 | + // return a UTF-16 character from a 3-byte UTF-8 char | |
| 208 | + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |
| 209 | + return chr((0xF0 & (ord($utf8{0}) << 4)) | |
| 210 | + | (0x0F & (ord($utf8{1}) >> 2))) | |
| 211 | + . chr((0xC0 & (ord($utf8{1}) << 6)) | |
| 212 | + | (0x7F & ord($utf8{2}))); | |
| 213 | + } | |
| 214 | + | |
| 215 | + // ignoring UTF-32 for now, sorry | |
| 216 | + return ''; | |
| 217 | + } | |
| 218 | + | |
| 219 | + /** | |
| 220 | + * encodes an arbitrary variable into JSON format | |
| 221 | + * | |
| 222 | + * @param mixed $var any number, boolean, string, array, or object to be encoded. | |
| 223 | + * see argument 1 to Services_JSON() above for array-parsing behavior. | |
| 224 | + * if var is a strng, note that encode() always expects it | |
| 225 | + * to be in ASCII or UTF-8 format! | |
| 226 | + * | |
| 227 | + * @return string JSON string representation of input var | |
| 228 | + * @access public | |
| 229 | + */ | |
| 230 | + function encode($var) | |
| 231 | + { | |
| 232 | + switch (gettype($var)) { | |
| 233 | + case 'boolean': | |
| 234 | + return $var ? 'true' : 'false'; | |
| 235 | + | |
| 236 | + case 'NULL': | |
| 237 | + return 'null'; | |
| 238 | + | |
| 239 | + case 'integer': | |
| 240 | + return (int) $var; | |
| 241 | + | |
| 242 | + case 'double': | |
| 243 | + case 'float': | |
| 244 | + return (float) $var; | |
| 245 | + | |
| 246 | + case 'string': | |
| 247 | + $arg = $var; | |
| 248 | + $l = strlen($var); | |
| 249 | + $s = '"'; | |
| 250 | + | |
| 251 | + for ($i = 0; $i < $l; $i++) { | |
| 252 | + $c = $var{$i}; | |
| 253 | + | |
| 254 | + if (ord($c) >= ord(' ')) { | |
| 255 | + | |
| 256 | + if ($c == '\\' | |
| 257 | + || $c == '"') { | |
| 258 | + | |
| 259 | + $s .= '\\'; | |
| 260 | + } | |
| 261 | + $s .= $c; | |
| 262 | + | |
| 263 | + } else { | |
| 264 | + | |
| 265 | + switch ($c) { | |
| 266 | + | |
| 267 | + case '\b': | |
| 268 | + $s .= '\\b'; | |
| 269 | + break; | |
| 270 | + | |
| 271 | + case '\f': | |
| 272 | + $s .= '\\f'; | |
| 273 | + break; | |
| 274 | + | |
| 275 | + case '\n': | |
| 276 | + $s .= '\\n'; | |
| 277 | + break; | |
| 278 | + | |
| 279 | + case '\r': | |
| 280 | + $s .= '\\r'; | |
| 281 | + break; | |
| 282 | + | |
| 283 | + case '\t': | |
| 284 | + $s .= '\\t'; | |
| 285 | + break; | |
| 286 | + | |
| 287 | + default: | |
| 288 | + $s .= '\u00' . sprintf('%02x', ord($c)); | |
| 289 | + } | |
| 290 | + } | |
| 291 | + } | |
| 292 | + return $s . '"'; | |
| 293 | + break; | |
| 294 | + | |
| 295 | + | |
| 296 | + case 'array': | |
| 297 | + /* | |
| 298 | + * As per JSON spec if any array key is not an integer | |
| 299 | + * we must treat the the whole array as an object. We | |
| 300 | + * also try to catch a sparsely populated associative | |
| 301 | + * array with numeric keys here because some JS engines | |
| 302 | + * will create an array with empty indexes up to | |
| 303 | + * max_index which can cause memory issues and because | |
| 304 | + * the keys, which may be relevant, will be remapped | |
| 305 | + * otherwise. | |
| 306 | + * | |
| 307 | + * As per the ECMA and JSON specification an object may | |
| 308 | + * have any string as a property. Unfortunately due to | |
| 309 | + * a hole in the ECMA specification if the key is a | |
| 310 | + * ECMA reserved word or starts with a digit the | |
| 311 | + * parameter is only accessible using ECMAScript's | |
| 312 | + * bracket notation. | |
| 313 | + */ | |
| 314 | + | |
| 315 | + // treat as a JSON object | |
| 316 | + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { | |
| 317 | + return "{/*Array*/" . | |
| 318 | + join(',', array_map(array($this, 'name_value'), | |
| 319 | + array_keys($var), | |
| 320 | + array_values($var))) | |
| 321 | + . '}'; | |
| 322 | + } | |
| 323 | + | |
| 324 | + // treat it like a regular array | |
| 325 | + return '[' . join(',', array_map(array($this, 'encode'), $var)) . ']'; | |
| 326 | + | |
| 327 | + case 'object': | |
| 328 | + $vars = get_object_vars($var); | |
| 329 | + return '{' . | |
| 330 | + join(',', array_map(array($this, 'name_value'), | |
| 331 | + array_keys($vars), | |
| 332 | + array_values($vars))) | |
| 333 | + . '}'; | |
| 334 | + | |
| 335 | + default: | |
| 336 | + return ''; | |
| 337 | + } | |
| 338 | + } | |
| 339 | + | |
| 340 | + /** | |
| 341 | + * array-walking function for use in generating JSON-formatted name-value pairs | |
| 342 | + * | |
| 343 | + * @param string $name name of key to use | |
| 344 | + * @param mixed $value reference to an array element to be encoded | |
| 345 | + * | |
| 346 | + * @return string JSON-formatted name-value pair, like '"name":value' | |
| 347 | + * @access private | |
| 348 | + */ | |
| 349 | + function name_value($name, $value) | |
| 350 | + { | |
| 351 | + return $this->encode(strval($name)) . ':' . $this->encode($value); | |
| 352 | + } | |
| 353 | + | |
| 354 | + /** | |
| 355 | + * reduce a string by removing leading and trailing comments and whitespace | |
| 356 | + * | |
| 357 | + * @param $str string string value to strip of comments and whitespace | |
| 358 | + * | |
| 359 | + * @return string string value stripped of comments and whitespace | |
| 360 | + * @access private | |
| 361 | + */ | |
| 362 | + function reduce_string($str) | |
| 363 | + { | |
| 364 | + $str = preg_replace(array( | |
| 365 | + | |
| 366 | + // eliminate single line comments in '// ...' form | |
| 367 | + '#^\s*//(.+)$#m', | |
| 368 | + | |
| 369 | + // eliminate multi-line comments in '/* ... */' form, at start of string | |
| 370 | + '#^\s*/\*(.+)\*/#Us', | |
| 371 | + | |
| 372 | + // eliminate multi-line comments in '/* ... */' form, at end of string | |
| 373 | + '#/\*(.+)\*/\s*$#Us' | |
| 374 | + | |
| 375 | + ), '', $str); | |
| 376 | + | |
| 377 | + // eliminate extraneous space | |
| 378 | + return trim($str); | |
| 379 | + } | |
| 380 | + | |
| 381 | + /** | |
| 382 | + * decodes a JSON string into appropriate variable | |
| 383 | + * | |
| 384 | + * @param string $str JSON-formatted string | |
| 385 | + * | |
| 386 | + * @return mixed number, boolean, string, array, or object | |
| 387 | + * corresponding to given JSON input string. | |
| 388 | + * See argument 1 to Services_JSON() above for object-output behavior. | |
| 389 | + * Note that decode() always returns strings | |
| 390 | + * in ASCII or UTF-8 format! | |
| 391 | + * @access public | |
| 392 | + */ | |
| 393 | + function decode($str) | |
| 394 | + { | |
| 395 | + $imarray = false; | |
| 396 | + $str = $this->reduce_string($str); | |
| 397 | + | |
| 398 | + switch (strtolower($str)) { | |
| 399 | + case 'true': | |
| 400 | + return true; | |
| 401 | + | |
| 402 | + case 'false': | |
| 403 | + return false; | |
| 404 | + | |
| 405 | + case 'null': | |
| 406 | + return null; | |
| 407 | + | |
| 408 | + default: | |
| 409 | + if (is_numeric($str)) { | |
| 410 | + // Lookie-loo, it's a number | |
| 411 | + | |
| 412 | + // This would work on its own, but I'm trying to be | |
| 413 | + // good about returning integers where appropriate: | |
| 414 | + // return (float)$str; | |
| 415 | + | |
| 416 | + // Return float or int, as appropriate | |
| 417 | + return ((float)$str == (integer)$str) | |
| 418 | + ? (integer)$str | |
| 419 | + : (float)$str; | |
| 420 | + | |
| 421 | + } elseif (preg_match('/^("|\').+(\1)$/s', $str, $m) && $m[1] == $m[2]) { | |
| 422 | + // STRINGS RETURNED IN UTF-8 FORMAT | |
| 423 | + $delim = substr($str, 0, 1); | |
| 424 | + $chrs = substr($str, 1, -1); | |
| 425 | + $utf8 = ''; | |
| 426 | + $strlen_chrs = strlen($chrs); | |
| 427 | + | |
| 428 | + for ($c = 0; $c < $strlen_chrs; ++$c) { | |
| 429 | + | |
| 430 | + $substr_chrs_c_2 = substr($chrs, $c, 2); | |
| 431 | + $ord_chrs_c = ord($chrs{$c}); | |
| 432 | + | |
| 433 | + switch (true) { | |
| 434 | + case $substr_chrs_c_2 == '\b': | |
| 435 | + $utf8 .= chr(0x08); | |
| 436 | + ++$c; | |
| 437 | + break; | |
| 438 | + case $substr_chrs_c_2 == '\t': | |
| 439 | + $utf8 .= chr(0x09); | |
| 440 | + ++$c; | |
| 441 | + break; | |
| 442 | + case $substr_chrs_c_2 == '\n': | |
| 443 | + $utf8 .= chr(0x0A); | |
| 444 | + ++$c; | |
| 445 | + break; | |
| 446 | + case $substr_chrs_c_2 == '\f': | |
| 447 | + $utf8 .= chr(0x0C); | |
| 448 | + ++$c; | |
| 449 | + break; | |
| 450 | + case $substr_chrs_c_2 == '\r': | |
| 451 | + $utf8 .= chr(0x0D); | |
| 452 | + ++$c; | |
| 453 | + break; | |
| 454 | + | |
| 455 | + case $substr_chrs_c_2 == '\\"': | |
| 456 | + case $substr_chrs_c_2 == '\\\'': | |
| 457 | + case $substr_chrs_c_2 == '\\\\': | |
| 458 | + case $substr_chrs_c_2 == '\\/': | |
| 459 | + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || | |
| 460 | + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { | |
| 461 | + $utf8 .= $chrs{++$c}; | |
| 462 | + } | |
| 463 | + break; | |
| 464 | + | |
| 465 | + case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): | |
| 466 | + // single, escaped unicode character | |
| 467 | + $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) | |
| 468 | + . chr(hexdec(substr($chrs, ($c + 4), 2))); | |
| 469 | + $utf8 .= $this->utf162utf8($utf16); | |
| 470 | + $c += 5; | |
| 471 | + break; | |
| 472 | + | |
| 473 | + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): | |
| 474 | + $utf8 .= $chrs{$c}; | |
| 475 | + break; | |
| 476 | + | |
| 477 | + case ($ord_chrs_c & 0xE0) == 0xC0: | |
| 478 | + // characters U-00000080 - U-000007FF, mask 110XXXXX | |
| 479 | + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |
| 480 | + $utf8 .= substr($chrs, $c, 2); | |
| 481 | + ++$c; | |
| 482 | + break; | |
| 483 | + | |
| 484 | + case ($ord_chrs_c & 0xF0) == 0xE0: | |
| 485 | + // characters U-00000800 - U-0000FFFF, mask 1110XXXX | |
| 486 | + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |
| 487 | + $utf8 .= substr($chrs, $c, 3); | |
| 488 | + $c += 2; | |
| 489 | + break; | |
| 490 | + | |
| 491 | + case ($ord_chrs_c & 0xF8) == 0xF0: | |
| 492 | + // characters U-00010000 - U-001FFFFF, mask 11110XXX | |
| 493 | + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |
| 494 | + $utf8 .= substr($chrs, $c, 4); | |
| 495 | + $c += 3; | |
| 496 | + break; | |
| 497 | + | |
| 498 | + case ($ord_chrs_c & 0xFC) == 0xF8: | |
| 499 | + // characters U-00200000 - U-03FFFFFF, mask 111110XX | |
| 500 | + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |
| 501 | + $utf8 .= substr($chrs, $c, 5); | |
| 502 | + $c += 4; | |
| 503 | + break; | |
| 504 | + | |
| 505 | + case ($ord_chrs_c & 0xFE) == 0xFC: | |
| 506 | + // characters U-04000000 - U-7FFFFFFF, mask 1111110X | |
| 507 | + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |
| 508 | + $utf8 .= substr($chrs, $c, 6); | |
| 509 | + $c += 5; | |
| 510 | + break; | |
| 511 | + | |
| 512 | + } | |
| 513 | + | |
| 514 | + } | |
| 515 | + | |
| 516 | + return $utf8; | |
| 517 | + | |
| 518 | + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { | |
| 519 | + // array, or object notation | |
| 520 | + | |
| 521 | + if ($str{0} == '[') { | |
| 522 | + $stk = array(SERVICES_JSON_IN_ARR); | |
| 523 | + $arr = array(); | |
| 524 | + } else { | |
| 525 | + if ($str{1} == '/' && $str{2} == '*' && $str{3} == 'A') | |
| 526 | + { | |
| 527 | + $stk = array(SERVICES_JSON_IN_OBJ); | |
| 528 | + $obj = array(); | |
| 529 | + | |
| 530 | + } | |
| 531 | + else | |
| 532 | + { | |
| 533 | + if ($this->use == SERVICES_JSON_LOOSE_TYPE) { | |
| 534 | + $stk = array(SERVICES_JSON_IN_OBJ); | |
| 535 | + $obj = array(); | |
| 536 | + | |
| 537 | + } else { | |
| 538 | + $stk = array(SERVICES_JSON_IN_OBJ); | |
| 539 | + $obj = new stdClass(); | |
| 540 | + } | |
| 541 | + } | |
| 542 | + } | |
| 543 | + | |
| 544 | + array_push($stk, array('what' => SERVICES_JSON_SLICE, | |
| 545 | + 'where' => 0, | |
| 546 | + 'delim' => false)); | |
| 547 | + | |
| 548 | + $chrs = substr($str, 1, -1); | |
| 549 | + $chrs = $this->reduce_string($chrs); | |
| 550 | + | |
| 551 | + if ($chrs == '') { | |
| 552 | + if (reset($stk) == SERVICES_JSON_IN_ARR) { | |
| 553 | + return $arr; | |
| 554 | + | |
| 555 | + } else { | |
| 556 | + return $obj; | |
| 557 | + | |
| 558 | + } | |
| 559 | + } | |
| 560 | + | |
| 561 | + //print("\nparsing {$chrs}\n"); | |
| 562 | + | |
| 563 | + $strlen_chrs = strlen($chrs); | |
| 564 | + | |
| 565 | + for ($c = 0; $c <= $strlen_chrs; ++$c) { | |
| 566 | + | |
| 567 | + $top = end($stk); | |
| 568 | + $substr_chrs_c_2 = substr($chrs, $c, 2); | |
| 569 | + | |
| 570 | + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { | |
| 571 | + // found a comma that is not inside a string, array, etc., | |
| 572 | + // OR we've reached the end of the character list | |
| 573 | + $slice = substr($chrs, $top['where'], ($c - $top['where'])); | |
| 574 | + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); | |
| 575 | + //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); | |
| 576 | + | |
| 577 | + if (reset($stk) == SERVICES_JSON_IN_ARR) { | |
| 578 | + // we are in an array, so just push an element onto the stack | |
| 579 | + array_push($arr, $this->decode($slice)); | |
| 580 | + | |
| 581 | + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { | |
| 582 | + // we are in an object, so figure | |
| 583 | + // out the property name and set an | |
| 584 | + // element in an associative array, | |
| 585 | + // for now | |
| 586 | + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { | |
| 587 | + // "name":value pair | |
| 588 | + $key = $this->decode($parts[1]); | |
| 589 | + $val = $this->decode($parts[2]); | |
| 590 | + | |
| 591 | + if ($this->use == SERVICES_JSON_LOOSE_TYPE || is_array($obj)) { | |
| 592 | + $obj[$key] = $val; | |
| 593 | + } else { | |
| 594 | + $obj->$key = $val; | |
| 595 | + } | |
| 596 | + } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { | |
| 597 | + // name:value pair, where name is unquoted | |
| 598 | + $key = $parts[1]; | |
| 599 | + $val = $this->decode($parts[2]); | |
| 600 | + if ($this->use == SERVICES_JSON_LOOSE_TYPE|| is_array($obj)) { | |
| 601 | + $obj[$key] = $val; | |
| 602 | + } else { | |
| 603 | + $obj->$key = $val; | |
| 604 | + } | |
| 605 | + } | |
| 606 | + | |
| 607 | + } | |
| 608 | + | |
| 609 | + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { | |
| 610 | + // found a quote, and we are not inside a string | |
| 611 | + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); | |
| 612 | + //print("Found start of string at {$c}\n"); | |
| 613 | + | |
| 614 | + } elseif (($chrs{$c} == $top['delim']) && | |
| 615 | + ($top['what'] == SERVICES_JSON_IN_STR) && | |
| 616 | + (($chrs{$c - 1} != '\\') || | |
| 617 | + ($chrs{$c - 1} == '\\' && $chrs{$c - 2} == '\\'))) { | |
| 618 | + // found a quote, we're in a string, and it's not escaped | |
| 619 | + array_pop($stk); | |
| 620 | + //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); | |
| 621 | + | |
| 622 | + } elseif (($chrs{$c} == '[') && | |
| 623 | + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { | |
| 624 | + // found a left-bracket, and we are in an array, object, or slice | |
| 625 | + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); | |
| 626 | + //print("Found start of array at {$c}\n"); | |
| 627 | + | |
| 628 | + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { | |
| 629 | + // found a right-bracket, and we're in an array | |
| 630 | + array_pop($stk); | |
| 631 | + //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); | |
| 632 | + | |
| 633 | + } elseif (($chrs{$c} == '{') && | |
| 634 | + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { | |
| 635 | + // found a left-brace, and we are in an array, object, or slice | |
| 636 | + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); | |
| 637 | + //print("Found start of object at {$c}\n"); | |
| 638 | + | |
| 639 | + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { | |
| 640 | + // found a right-brace, and we're in an object | |
| 641 | + array_pop($stk); | |
| 642 | + | |
| 643 | + //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); | |
| 644 | + | |
| 645 | + } | |
| 646 | + | |
| 647 | + } | |
| 648 | + | |
| 649 | + if (reset($stk) == SERVICES_JSON_IN_ARR) { | |
| 650 | + return $arr; | |
| 651 | + | |
| 652 | + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { | |
| 653 | + return $obj; | |
| 654 | + | |
| 655 | + } | |
| 656 | + | |
| 657 | + } | |
| 658 | + } | |
| 659 | + } | |
| 660 | + | |
| 661 | +} | |
| 662 | + | |
| 663 | +?> | |
| 0 | 664 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,351 @@ |
| 1 | +<?php | |
| 2 | +/** | |
| 3 | +* The content of this file is (c) 2003-2005 digital media center GmbH | |
| 4 | +* All rights reserved | |
| 5 | +* | |
| 6 | +* JSON parser / generator test | |
| 7 | +* | |
| 8 | +* @package JSON | |
| 9 | +* @access public | |
| 10 | +* @copyright (c) 2003-2005 digital media center GmbH, all rights reserved | |
| 11 | +* @author Dominique Stender <dst@dmc.de> | |
| 12 | +*/ | |
| 13 | + | |
| 14 | +//---- includes ---------------------------------------------------------------- | |
| 15 | + /** | |
| 16 | + * @include JSON | |
| 17 | + */ | |
| 18 | + require_once('../json.php'); | |
| 19 | + | |
| 20 | +//---- class ------------------------------------------------------------------- | |
| 21 | + class MyObj { | |
| 22 | + var $id = ''; | |
| 23 | + var $name = ''; | |
| 24 | + var $attribs = array("first",'4'); | |
| 25 | + | |
| 26 | + function setId($id) { | |
| 27 | + $this->id = $id; | |
| 28 | + } | |
| 29 | + | |
| 30 | + function getId() { | |
| 31 | + return $this->id; | |
| 32 | + } | |
| 33 | + | |
| 34 | + function setName($name) { | |
| 35 | + $this->name = $name; | |
| 36 | + } | |
| 37 | + | |
| 38 | + function getName() { | |
| 39 | + return $this->name; | |
| 40 | + } | |
| 41 | + } | |
| 42 | + | |
| 43 | +//---- variables --------------------------------------------------------------- | |
| 44 | + $JSON = new JSON(); | |
| 45 | + $myObj = new MyObj(); | |
| 46 | + | |
| 47 | +//---- logic ------------------------------------------------------------------- | |
| 48 | + /* initialize object */ | |
| 49 | + $myObj->setId('Äl' . chr(18) . "ie\nn"); | |
| 50 | + $myObj->setName('objectName'); | |
| 51 | + array_push($myObj->attribs, 'øfirst'); | |
| 52 | + array_push($myObj->attribs, 'second'); | |
| 53 | + array_push($myObj->attribs, 3); | |
| 54 | + | |
| 55 | + /* create JSON representation */ | |
| 56 | + $jsonStr = $JSON->stringify($myObj); | |
| 57 | + | |
| 58 | +//---- clean-up ---------------------------------------------------------------- | |
| 59 | +//---- content ----------------------------------------------------------------- | |
| 60 | + | |
| 61 | +?><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
| 62 | +<head> | |
| 63 | + <title>JSON parser test</title> | |
| 64 | + <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> | |
| 65 | + <script type="text/javascript" src="json.js"></script> | |
| 66 | + <script type="text/javascript"> | |
| 67 | +JSON.arrayObj = new Array(); | |
| 68 | +JSON.parse2 = function (text) | |
| 69 | +{ | |
| 70 | + var p = /^\s*(([,:{}\[\]])|"(\\.|[^\x00-\x1f"\\])*"|-?\d+(\.\d*)?([eE][+-]?\d+)?|true|false|null|\/\*Array\*\/)\s*/, | |
| 71 | + token, | |
| 72 | + operator; | |
| 73 | + | |
| 74 | + function error(m, t) { | |
| 75 | + throw { | |
| 76 | + name: 'JSONError', | |
| 77 | + message: m, | |
| 78 | + text: t || operator || token | |
| 79 | + }; | |
| 80 | + } | |
| 81 | + | |
| 82 | + function next() { | |
| 83 | + if (text) { | |
| 84 | + var t = p.exec(text); | |
| 85 | + if (t) { | |
| 86 | + | |
| 87 | + if (t[2]) { | |
| 88 | + token = null; | |
| 89 | + operator = t[2]; | |
| 90 | + } else { | |
| 91 | + operator = null; | |
| 92 | + if(t[1].search (/^\/\*Array\*\/$/g) > -1) | |
| 93 | + { | |
| 94 | + token = function(){}; | |
| 95 | + } | |
| 96 | + else | |
| 97 | + { | |
| 98 | + try { | |
| 99 | + token = eval(t[1]); | |
| 100 | + } catch (e) { | |
| 101 | + error("Bad token", t[1]); | |
| 102 | + } | |
| 103 | + } | |
| 104 | + } | |
| 105 | + text = text.substring(t[0].length); | |
| 106 | + } else { | |
| 107 | + error("Unrecognized token", text); | |
| 108 | + } | |
| 109 | + } else { | |
| 110 | + token = operator = undefined; | |
| 111 | + } | |
| 112 | + } | |
| 113 | + | |
| 114 | + | |
| 115 | + function arr() { | |
| 116 | + var a = []; | |
| 117 | + | |
| 118 | + next(); | |
| 119 | + if (operator == ']') { | |
| 120 | + next(); | |
| 121 | + return a; | |
| 122 | + } | |
| 123 | + for (;;) { | |
| 124 | + a.push(val()); | |
| 125 | + switch (operator) { | |
| 126 | + case ']': | |
| 127 | + next(); | |
| 128 | + return a; | |
| 129 | + case ',': | |
| 130 | + next(); | |
| 131 | + break; | |
| 132 | + default: | |
| 133 | + error("Missing ']'"); | |
| 134 | + } | |
| 135 | + } | |
| 136 | + } | |
| 137 | + | |
| 138 | + function obj() { | |
| 139 | + var k, o = {}; | |
| 140 | + | |
| 141 | + next(); | |
| 142 | + if (operator == '}') { | |
| 143 | + next(); | |
| 144 | + return o; | |
| 145 | + } | |
| 146 | + if (typeof(token) == 'function') | |
| 147 | + { | |
| 148 | + o = []; | |
| 149 | + next(); | |
| 150 | + } | |
| 151 | + for (;;) | |
| 152 | + { | |
| 153 | + | |
| 154 | + | |
| 155 | + if (operator ||typeof token != 'string') { | |
| 156 | + error("Missing key"); | |
| 157 | + } | |
| 158 | + k = token; | |
| 159 | + next(); | |
| 160 | + | |
| 161 | + if (operator != ':') { | |
| 162 | + error("Missing ':'"); | |
| 163 | + } | |
| 164 | + | |
| 165 | + next(); | |
| 166 | + o[k] = val(); | |
| 167 | + | |
| 168 | + switch (operator) { | |
| 169 | + case '}': | |
| 170 | + next(); | |
| 171 | + return o; | |
| 172 | + case ',': | |
| 173 | + next(); | |
| 174 | + break; | |
| 175 | + case '#': | |
| 176 | + next(); | |
| 177 | + break; | |
| 178 | + default: | |
| 179 | + error("Missing '}'"); | |
| 180 | + } | |
| 181 | + } | |
| 182 | + } | |
| 183 | + | |
| 184 | + function val() { | |
| 185 | + switch (operator) { | |
| 186 | + case '{': | |
| 187 | + return obj(); | |
| 188 | + case '[': | |
| 189 | + return arr(); | |
| 190 | + default: | |
| 191 | + if (operator !== null) { | |
| 192 | + error("Missing value"); | |
| 193 | + } | |
| 194 | + var t = token; | |
| 195 | + next(); | |
| 196 | + return t; | |
| 197 | + } | |
| 198 | + } | |
| 199 | + next(); | |
| 200 | + return val(); | |
| 201 | +}; | |
| 202 | + | |
| 203 | +JSON.stringify2 = function(arg) | |
| 204 | +{ | |
| 205 | + var jsonstring = []; | |
| 206 | + var b = this; | |
| 207 | + function add(str) | |
| 208 | + { | |
| 209 | + jsonstring[jsonstring.length] = str; | |
| 210 | + } | |
| 211 | + function validObj(obj) | |
| 212 | + { | |
| 213 | + if (typeof(obj) != 'undefined' && typeof(obj) != 'function') | |
| 214 | + { | |
| 215 | + return true; | |
| 216 | + } | |
| 217 | + return false; | |
| 218 | + } | |
| 219 | + function recurse(obj, prefix) | |
| 220 | + { | |
| 221 | + switch (typeof(obj)) | |
| 222 | + { | |
| 223 | + case 'object': | |
| 224 | + if (obj) | |
| 225 | + { | |
| 226 | + var first = true; | |
| 227 | + if (obj.constructor == b.arrayObj.constructor) | |
| 228 | + { | |
| 229 | + add('{/*Array*/'); | |
| 230 | + //add ('{'); | |
| 231 | + } | |
| 232 | + else | |
| 233 | + { | |
| 234 | + add ('{'); | |
| 235 | + } | |
| 236 | + for (prop in obj) | |
| 237 | + { | |
| 238 | + if (validObj(obj[prop])) | |
| 239 | + { | |
| 240 | + if (!first) | |
| 241 | + { | |
| 242 | + add(','); | |
| 243 | + } | |
| 244 | + recurse(prop); | |
| 245 | + add(':'); | |
| 246 | + recurse (obj[prop]); | |
| 247 | + first =false; | |
| 248 | + } | |
| 249 | + } | |
| 250 | + return add ('}'); | |
| 251 | + } | |
| 252 | + return add('null'); | |
| 253 | + break; | |
| 254 | + case 'number': | |
| 255 | + return add(isFinite(obj) ? String(obj) : 'null'); | |
| 256 | + break; | |
| 257 | + case 'string': | |
| 258 | + l = obj.length; | |
| 259 | + add('"'); | |
| 260 | + for (i = 0; i < l; i += 1) { | |
| 261 | + c = obj.charAt(i); | |
| 262 | + if (c >= ' ') { | |
| 263 | + if (c == '\\' || c == '"') { | |
| 264 | + add('\\'); | |
| 265 | + } | |
| 266 | + add(c); | |
| 267 | + } else { | |
| 268 | + switch (c) { | |
| 269 | + case '\b': | |
| 270 | + add('\\b'); | |
| 271 | + break; | |
| 272 | + case '\f': | |
| 273 | + add('\\f'); | |
| 274 | + break; | |
| 275 | + case '\n': | |
| 276 | + add('\\n'); | |
| 277 | + break; | |
| 278 | + case '\r': | |
| 279 | + add('\\r'); | |
| 280 | + break; | |
| 281 | + case '\t': | |
| 282 | + add('\\t'); | |
| 283 | + break; | |
| 284 | + default: | |
| 285 | + c = c.charCodeAt(); | |
| 286 | + add('\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16)); | |
| 287 | + } | |
| 288 | + } | |
| 289 | + } | |
| 290 | + return add('"'); | |
| 291 | + break; | |
| 292 | + case 'boolean': | |
| 293 | + return add(String(obj)); | |
| 294 | + break; | |
| 295 | + default: | |
| 296 | + return add('null'); | |
| 297 | + break; | |
| 298 | + } | |
| 299 | + } | |
| 300 | + recurse(arg); | |
| 301 | + return jsonstring.join(''); | |
| 302 | +}; | |
| 303 | + </script> | |
| 304 | +</head> | |
| 305 | +<body> | |
| 306 | + <?php echo 'php JSONstr: ' . $jsonStr . "<br />\n"; ?> | |
| 307 | + <script type="text/javascript"> | |
| 308 | + <!-- | |
| 309 | + /* print JSON representation */ | |
| 310 | + var jsonStr = '<?php echo addslashes($jsonStr); ?>'; | |
| 311 | + | |
| 312 | + document.write('JavaScript JSONstr: ' + jsonStr); | |
| 313 | + | |
| 314 | + /* create another object from jsonStr */ | |
| 315 | + try | |
| 316 | + { | |
| 317 | + var myObj2 = JSON.parse(jsonStr); | |
| 318 | + | |
| 319 | + var myObj3 = JSON.parse2(jsonStr); | |
| 320 | + | |
| 321 | + | |
| 322 | + /* test the clone */ | |
| 323 | + document.write('<br /><br />Original JSON implementation'); | |
| 324 | + document.write('<br />id: ' + myObj2.id); | |
| 325 | + document.write('<br />name: ' + myObj2.name); | |
| 326 | + | |
| 327 | + for (var i = 0; i < myObj2.attribs.length; i++) { | |
| 328 | + document.write('<br />attrib[' + i + ']: ' + myObj2.attribs[i]); | |
| 329 | + } | |
| 330 | + document.write('<br /><br />new JSON implementation'); | |
| 331 | + document.write('<br />id: ' + myObj3.id); | |
| 332 | + document.write('<br />name: ' + myObj3.name); | |
| 333 | + | |
| 334 | + for (var i = 0; i < myObj3.attribs.length; i++) { | |
| 335 | + document.write('<br />attrib[' + i + ']: ' + myObj3.attribs[i]); | |
| 336 | + } | |
| 337 | + | |
| 338 | + /* convert newline to JSON */ | |
| 339 | + var nl = "\n" + String.fromCharCode(18); | |
| 340 | + document.write('<br /><br /> Original JSON <br />newline as JSON: ' + JSON.stringify(nl)); | |
| 341 | + document.write('<br /><br /> New JSON <br />newline as JSON: ' + JSON.stringify2(nl)); | |
| 342 | + | |
| 343 | + //--> | |
| 344 | +} | |
| 345 | + catch(e) | |
| 346 | + { | |
| 347 | + alert(e.message); | |
| 348 | + } | |
| 349 | + </script> | |
| 350 | +</body> | |
| 351 | +</html> | |
| 0 | 352 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,413 @@ |
| 1 | +<?php | |
| 2 | + | |
| 3 | +// New TestSuite | |
| 4 | +//---- includes ---------------------------------------------------------------- | |
| 5 | + /** | |
| 6 | + * @include JSON | |
| 7 | + */ | |
| 8 | + require_once('../json.php'); | |
| 9 | + require_once('json2.php'); | |
| 10 | + | |
| 11 | +// ----- PHP Test Objects | |
| 12 | +class object1 | |
| 13 | +{ | |
| 14 | + var $firstattr = 'firstvalue'; | |
| 15 | + var $foreignchars = "Äø\r\n"; | |
| 16 | + var $regarray = array("item1", "item2"); | |
| 17 | + var $assocarray = array("item1"=>"value1", "item2"=>"value2"); | |
| 18 | + var $boolean = true; | |
| 19 | + var $integer = 5; | |
| 20 | + var $float = 1.12; | |
| 21 | + var $innerobject; | |
| 22 | + function object1() | |
| 23 | + { | |
| 24 | + $this->innerobject = new object2(); | |
| 25 | + } | |
| 26 | +} | |
| 27 | +class object2 | |
| 28 | +{ | |
| 29 | + var $firstattr = 'firstvalue'; | |
| 30 | + var $secondattr = 'secondvalue'; | |
| 31 | +} | |
| 32 | +//---- variables --------------------------------------------------------------- | |
| 33 | + $JSON = new JSON(); | |
| 34 | + $object1 = new object1(); | |
| 35 | + $JSON2 = new Services_JSON(); | |
| 36 | + $submitted = false; | |
| 37 | +//---- logic ------------------------------------------------------------------- | |
| 38 | + $origjsonstring = $JSON->stringify($object1); | |
| 39 | + $newjsonstring = $JSON2->encode($object1); | |
| 40 | + $origjson = $_REQUEST["origjson"]; | |
| 41 | + if (!empty($origjson)) | |
| 42 | + { | |
| 43 | + $origjsonprintr = $JSON->parse(stripslashes($origjson)); | |
| 44 | + $submitted = true; | |
| 45 | + } | |
| 46 | + else | |
| 47 | + { | |
| 48 | + $origjsonprintr = "Not submitted"; | |
| 49 | + } | |
| 50 | + $newjson = $_REQUEST["newjson"]; | |
| 51 | + if (!empty($newjson)) | |
| 52 | + { | |
| 53 | + $newjsonprintr = $JSON2->decode(stripslashes($newjson)); | |
| 54 | + } | |
| 55 | + else | |
| 56 | + { | |
| 57 | + $newjsonprintr = "Not submitted"; | |
| 58 | + } | |
| 59 | + | |
| 60 | +//---- content ----------------------------------------------------------------- | |
| 61 | +?> | |
| 62 | +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
| 63 | +<head> | |
| 64 | + <title>JSON parser test</title> | |
| 65 | + <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> | |
| 66 | + <script type="text/javascript" src="json.js"></script> | |
| 67 | + <script type="text/javascript"> | |
| 68 | +JSON.arrayObj = new Array(); | |
| 69 | +JSON.parse2 = function (text) | |
| 70 | +{ | |
| 71 | + var p = /^\s*(([,:{}\[\]])|"(\\.|[^\x00-\x1f"\\])*"|-?\d+(\.\d*)?([eE][+-]?\d+)?|true|false|null|\/\*Array\*\/)\s*/, | |
| 72 | + token, | |
| 73 | + operator; | |
| 74 | + | |
| 75 | + function error(m, t) { | |
| 76 | + throw { | |
| 77 | + name: 'JSONError', | |
| 78 | + message: m, | |
| 79 | + text: t || operator || token | |
| 80 | + }; | |
| 81 | + } | |
| 82 | + | |
| 83 | + function next() { | |
| 84 | + if (text) { | |
| 85 | + var t = p.exec(text); | |
| 86 | + if (t) { | |
| 87 | + | |
| 88 | + if (t[2]) { | |
| 89 | + token = null; | |
| 90 | + operator = t[2]; | |
| 91 | + } else { | |
| 92 | + operator = null; | |
| 93 | + if(t[1].search (/^\/\*Array\*\/$/g) > -1) | |
| 94 | + { | |
| 95 | + token = function(){}; | |
| 96 | + } | |
| 97 | + else | |
| 98 | + { | |
| 99 | + try { | |
| 100 | + token = eval(t[1]); | |
| 101 | + } catch (e) { | |
| 102 | + error("Bad token", t[1]); | |
| 103 | + } | |
| 104 | + } | |
| 105 | + } | |
| 106 | + text = text.substring(t[0].length); | |
| 107 | + } else { | |
| 108 | + error("Unrecognized token", text); | |
| 109 | + } | |
| 110 | + } else { | |
| 111 | + token = operator = undefined; | |
| 112 | + } | |
| 113 | + } | |
| 114 | + | |
| 115 | + | |
| 116 | + function arr() { | |
| 117 | + var a = []; | |
| 118 | + | |
| 119 | + next(); | |
| 120 | + if (operator == ']') { | |
| 121 | + next(); | |
| 122 | + return a; | |
| 123 | + } | |
| 124 | + for (;;) { | |
| 125 | + a.push(val()); | |
| 126 | + switch (operator) { | |
| 127 | + case ']': | |
| 128 | + next(); | |
| 129 | + return a; | |
| 130 | + case ',': | |
| 131 | + next(); | |
| 132 | + break; | |
| 133 | + default: | |
| 134 | + error("Missing ']'"); | |
| 135 | + } | |
| 136 | + } | |
| 137 | + } | |
| 138 | + | |
| 139 | + function obj() { | |
| 140 | + var k, o = {}; | |
| 141 | + | |
| 142 | + next(); | |
| 143 | + if (operator == '}') { | |
| 144 | + next(); | |
| 145 | + return o; | |
| 146 | + } | |
| 147 | + if (typeof(token) == 'function') | |
| 148 | + { | |
| 149 | + o = []; | |
| 150 | + next(); | |
| 151 | + } | |
| 152 | + for (;;) | |
| 153 | + { | |
| 154 | + | |
| 155 | + | |
| 156 | + if (operator ||typeof token != 'string') { | |
| 157 | + error("Missing key"); | |
| 158 | + } | |
| 159 | + k = token; | |
| 160 | + next(); | |
| 161 | + | |
| 162 | + if (operator != ':') { | |
| 163 | + error("Missing ':'"); | |
| 164 | + } | |
| 165 | + | |
| 166 | + next(); | |
| 167 | + o[k] = val(); | |
| 168 | + | |
| 169 | + switch (operator) { | |
| 170 | + case '}': | |
| 171 | + next(); | |
| 172 | + return o; | |
| 173 | + case ',': | |
| 174 | + next(); | |
| 175 | + break; | |
| 176 | + case '#': | |
| 177 | + next(); | |
| 178 | + break; | |
| 179 | + default: | |
| 180 | + error("Missing '}'"); | |
| 181 | + } | |
| 182 | + } | |
| 183 | + } | |
| 184 | + | |
| 185 | + function val() { | |
| 186 | + switch (operator) { | |
| 187 | + case '{': | |
| 188 | + return obj(); | |
| 189 | + case '[': | |
| 190 | + return arr(); | |
| 191 | + default: | |
| 192 | + if (operator !== null) { | |
| 193 | + error("Missing value"); | |
| 194 | + } | |
| 195 | + var t = token; | |
| 196 | + next(); | |
| 197 | + return t; | |
| 198 | + } | |
| 199 | + } | |
| 200 | + next(); | |
| 201 | + return val(); | |
| 202 | +}; | |
| 203 | + | |
| 204 | +JSON.stringify2 = function(arg) | |
| 205 | +{ | |
| 206 | + var jsonstring = []; | |
| 207 | + var b = this; | |
| 208 | + function add(str) | |
| 209 | + { | |
| 210 | + jsonstring[jsonstring.length] = str; | |
| 211 | + } | |
| 212 | + function validObj(obj) | |
| 213 | + { | |
| 214 | + if (typeof(obj) != 'undefined' && typeof(obj) != 'function') | |
| 215 | + { | |
| 216 | + return true; | |
| 217 | + } | |
| 218 | + return false; | |
| 219 | + } | |
| 220 | + function recurse(obj, prefix) | |
| 221 | + { | |
| 222 | + switch (typeof(obj)) | |
| 223 | + { | |
| 224 | + case 'object': | |
| 225 | + if (obj) | |
| 226 | + { | |
| 227 | + var first = true; | |
| 228 | + if (obj.constructor == b.arrayObj.constructor) | |
| 229 | + { | |
| 230 | + add('{/*Array*/'); | |
| 231 | + //add ('{'); | |
| 232 | + } | |
| 233 | + else | |
| 234 | + { | |
| 235 | + add ('{'); | |
| 236 | + } | |
| 237 | + for (prop in obj) | |
| 238 | + { | |
| 239 | + if (validObj(obj[prop])) | |
| 240 | + { | |
| 241 | + if (!first) | |
| 242 | + { | |
| 243 | + add(','); | |
| 244 | + } | |
| 245 | + recurse(prop); | |
| 246 | + add(':'); | |
| 247 | + recurse (obj[prop]); | |
| 248 | + first =false; | |
| 249 | + } | |
| 250 | + } | |
| 251 | + return add ('}'); | |
| 252 | + } | |
| 253 | + return add('null'); | |
| 254 | + break; | |
| 255 | + case 'number': | |
| 256 | + return add(isFinite(obj) ? String(obj) : 'null'); | |
| 257 | + break; | |
| 258 | + case 'string': | |
| 259 | + l = obj.length; | |
| 260 | + add('"'); | |
| 261 | + for (i = 0; i < l; i += 1) { | |
| 262 | + c = obj.charAt(i); | |
| 263 | + if (c >= ' ') { | |
| 264 | + if (c == '\\' || c == '"') { | |
| 265 | + add('\\'); | |
| 266 | + } | |
| 267 | + add(c); | |
| 268 | + } else { | |
| 269 | + switch (c) { | |
| 270 | + case '\b': | |
| 271 | + add('\\b'); | |
| 272 | + break; | |
| 273 | + case '\f': | |
| 274 | + add('\\f'); | |
| 275 | + break; | |
| 276 | + case '\n': | |
| 277 | + add('\\n'); | |
| 278 | + break; | |
| 279 | + case '\r': | |
| 280 | + add('\\r'); | |
| 281 | + break; | |
| 282 | + case '\t': | |
| 283 | + add('\\t'); | |
| 284 | + break; | |
| 285 | + default: | |
| 286 | + c = c.charCodeAt(); | |
| 287 | + add('\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16)); | |
| 288 | + } | |
| 289 | + } | |
| 290 | + } | |
| 291 | + return add('"'); | |
| 292 | + break; | |
| 293 | + case 'boolean': | |
| 294 | + return add(String(obj)); | |
| 295 | + break; | |
| 296 | + default: | |
| 297 | + return add('null'); | |
| 298 | + break; | |
| 299 | + } | |
| 300 | + } | |
| 301 | + recurse(arg); | |
| 302 | + return jsonstring.join(''); | |
| 303 | +}; | |
| 304 | + </script> | |
| 305 | +</head> | |
| 306 | +<body> | |
| 307 | + <table width="100%"> | |
| 308 | + <tr> | |
| 309 | + <td> | |
| 310 | + <table width="100%"> | |
| 311 | + <caption>Original JSON PHP/Javascript</caption> | |
| 312 | + <tr> | |
| 313 | + <td> | |
| 314 | + Original PHP object (print_r) | |
| 315 | + <textarea rows="5" cols="50"><?php echo print_r($object1); ?></textarea> | |
| 316 | + </td> | |
| 317 | + </tr> | |
| 318 | + <tr> | |
| 319 | + <td>Result of PHP Stringify<br /><textarea rows="5" cols="50"><?php echo ($origjsonstring); ?></textarea></td> | |
| 320 | + </tr> | |
| 321 | + <tr> | |
| 322 | + <td>Did it Convert to a Javascript Object origjsobj ?<br /> | |
| 323 | + <script> | |
| 324 | + try | |
| 325 | + { | |
| 326 | + var origjsobj = JSON.parse('<?php echo addslashes($origjsonstring); ?>'); | |
| 327 | + document.write('Successful'); | |
| 328 | + } | |
| 329 | + catch(e) | |
| 330 | + { | |
| 331 | + document.write('Failed'); | |
| 332 | + document.write(' Original Parse Failed with this error "' + e.message + '"'); | |
| 333 | + } | |
| 334 | + </script> | |
| 335 | + <br /><button onclick="s=document.body.appendChild(document.createElement('script'));s.id='sst';s.language='javascript';void(s.src='http://snmp-dev.cableaz.com/beta/print_r_0_3.js');">Invoke Print_R</button> | |
| 336 | + </td> | |
| 337 | + </tr> | |
| 338 | + <tr> | |
| 339 | + <td>Result of Javascript Stringify<br /> | |
| 340 | + <script> | |
| 341 | + var origjsstr = JSON.stringify(origjsobj); | |
| 342 | + document.write('<textarea rows="5" cols="50">' + origjsstr + '</textarea>'); | |
| 343 | + </script> | |
| 344 | + </td> | |
| 345 | + </tr> | |
| 346 | + <tr> | |
| 347 | + <td> | |
| 348 | + Result of Conversion Back to PHP object (print_r) | |
| 349 | + <textarea rows="5" cols="50"><?php echo print_r($origjsonprintr); ?></textarea> | |
| 350 | + </td> | |
| 351 | + </tr> | |
| 352 | + </table> | |
| 353 | + </td> | |
| 354 | + <td> | |
| 355 | + <table width="100%"> | |
| 356 | + <caption>New JSON PHP/Javascript</caption> | |
| 357 | + <tr> | |
| 358 | + <td> | |
| 359 | + Original PHP object (print_r) | |
| 360 | + <textarea rows="5" cols="50"><?php echo print_r($object1); ?></textarea> | |
| 361 | + </td> | |
| 362 | + </tr> | |
| 363 | + <tr> | |
| 364 | + <td>Result of PHP Stringify<br /><textarea rows="5" cols="50"><?php echo ($newjsonstring); ?></textarea></td> | |
| 365 | + </tr> | |
| 366 | + <tr> | |
| 367 | + <td>Did it Convert to a Javascript Object newjsobj ?<br /> | |
| 368 | + <script> | |
| 369 | + try | |
| 370 | + { | |
| 371 | + var newjsobj = JSON.parse2('<?php echo addslashes($newjsonstring); ?>'); | |
| 372 | + document.write('Successful'); | |
| 373 | + } | |
| 374 | + catch(e) | |
| 375 | + { | |
| 376 | + document.write('Failed'); | |
| 377 | + document.write(' New Parse Failed with this error "' + e.message + '"'); | |
| 378 | + } | |
| 379 | + </script> | |
| 380 | + <br /><button onclick="s=document.body.appendChild(document.createElement('script'));s.id='sst';s.language='javascript';void(s.src='http://snmp-dev.cableaz.com/beta/print_r_0_3.js');">Invoke Print_R</button> | |
| 381 | + </td> | |
| 382 | + </tr> | |
| 383 | + <tr> | |
| 384 | + <td>Result of Javascript Stringify<br /> | |
| 385 | + <script> | |
| 386 | + var newjsstr = JSON.stringify2(newjsobj); | |
| 387 | + document.write('<textarea rows="5" cols="50">' + newjsstr + '</textarea>'); | |
| 388 | + </script> | |
| 389 | + </td> | |
| 390 | + </tr> | |
| 391 | + <tr> | |
| 392 | + <td> | |
| 393 | + Result of Conversion Back to PHP object (print_r) | |
| 394 | + <textarea rows="5" cols="50"><?php echo print_r($newjsonprintr); ?></textarea> | |
| 395 | + </td> | |
| 396 | + </tr> | |
| 397 | + </table> | |
| 398 | + </td> | |
| 399 | + </table> | |
| 400 | +<form method="GET" action="testsuite2.php" id="hiddenform"> | |
| 401 | + <script> | |
| 402 | + document.write ('<input name="origjson" type="hidden" value=\'' + origjsstr + '\' />'); | |
| 403 | + document.write ('<input name="newjson" type="hidden" value=\'' + newjsstr + '\' />'); | |
| 404 | + <?php | |
| 405 | + if (!$submitted) | |
| 406 | + { | |
| 407 | + echo "document.getElementById('hiddenform').submit();"; | |
| 408 | + } | |
| 409 | + ?> | |
| 410 | + </script> | |
| 411 | +</form> | |
| 412 | +</body> | |
| 413 | +</html> | |
| 0 | 414 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,134 @@ |
| 1 | +CPAINT changelog | |
| 2 | + | |
| 3 | +//---- v2.0.3 (Released Feburary 8, 2006) -------------------------- | |
| 4 | +- PHP: nixed potential security hole with invalid response types | |
| 5 | + (Thanks to James Bercegay) | |
| 6 | + | |
| 7 | +//---- v2.0.2 (Released January 20, 2006) -------------------------- | |
| 8 | +- JS: check HTTP response code and throw an error if request was | |
| 9 | + not accepted by server. (thanks gloomyandy) | |
| 10 | +- JS: allowing the underscore in node names. | |
| 11 | +- JS: added support for the passing of arrays and objects to the | |
| 12 | + backend. | |
| 13 | +- JS: added X-Powered-By request header. | |
| 14 | +- JS: node names, attribute names and -values from backend are now | |
| 15 | + expected to be valid ISO-8859-1. | |
| 16 | +- JS: fixed wrong initialization of the data property to int zero in | |
| 17 | + class cpaint_result_object. | |
| 18 | +- JS: removed unneccessary setter() methods from internal classes | |
| 19 | + to reduce size. | |
| 20 | +- JS: added debugging level cp.set_debug(-1) to suppress errors. | |
| 21 | +- JS: fixed type conversion issue where non-empty, pure whitespace | |
| 22 | + strings where interpreted as numbers. | |
| 23 | +- JS: fixed problem with callback being called twice. | |
| 24 | + (thanks to Lance Rushing) | |
| 25 | +- JS: fixed problem with URLs with existing GET parameters. | |
| 26 | + (thanks to Nelson Antunes) | |
| 27 | +- JS / PHP: added support for JSON as response type. | |
| 28 | +- JS / PHP: added support for E4X as response type. | |
| 29 | +- PHP: added cpaint::unregister() | |
| 30 | +- PHP: added cpaint::get_response_type(); | |
| 31 | +- PHP: removed a bug in JSON library that prevented the generation of valid JSON | |
| 32 | + when including a leading zero character. | |
| 33 | +- PHP: Removed unneccessary unescaping of parameters in proxy script. | |
| 34 | +- PHP: added support for array and object parameters from JavaScript (JSON!). | |
| 35 | +- PHP: added X-Powered-By response header. | |
| 36 | +- PHP: removed encoding of node names, attribute names and attribute | |
| 37 | + values to UTF-8 if possible. those values must now be valid | |
| 38 | + ISO-8859-1. | |
| 39 | +- PHP: removed output buffer to make it possible to include backend | |
| 40 | + and frontend in the same file. | |
| 41 | +- PHP: fixed error output (a function was called...) when no | |
| 42 | + parameters where sent to CPAINT indicating a function call, thus | |
| 43 | + making it possible to include backend and frontend in the same file | |
| 44 | + (thanks S. Alexandre M. Lemaire) | |
| 45 | +- PHP: added backend debugger code to main source tree & documentation. | |
| 46 | +- PHP: secured proxy utility (see documentation). | |
| 47 | +- PHP: added backend configuration file (for proxy security and future use) | |
| 48 | +- ASP: removed from distribution to focus on PHP and other ports. | |
| 49 | + | |
| 50 | +//---- v2.0.1 (Released September 5, 2005) -------------------------- | |
| 51 | +- JS: added the returning of plain-text response when using XML|OBJECT | |
| 52 | + response type as second parameter to user-defined callback function | |
| 53 | + (thanks Gunther) | |
| 54 | +- JS: added cpaint.capable property to determine AJAX-compatible | |
| 55 | + browsers | |
| 56 | +- JS: cleaned up CPAINT parameters when not using CPAINT API w/ proxy | |
| 57 | +- PHP: fixed callability of object methods (thanks Markus Wolf) | |
| 58 | +- PHP: fixed HTTP authentication in proxy file | |
| 59 | +- ASP: improved security regarding incoming function arguments | |
| 60 | + | |
| 61 | +//---- v2.0 (Released August 20, 2005) ------------------------------ | |
| 62 | +- completely rewrote the JavaScript frontend. | |
| 63 | + - 100% OOP approach. | |
| 64 | + - completely configurable through setter methods. | |
| 65 | + - support for syncronized calls (SJAX?) ;-). | |
| 66 | + - automatic XML parsing into JavaScript document structure if | |
| 67 | + response_type='object'. | |
| 68 | + - support for arbitrary character sets, including non-printable | |
| 69 | + characters. | |
| 70 | + - automatic detection and conversion of numeric data into | |
| 71 | + integers / floats. | |
| 72 | + - unified use of call() for local and remote targets. | |
| 73 | + - can now use frontend to retrieve static files or backend | |
| 74 | + functions not implemented with CPAINT | |
| 75 | + - use of a single (persistent) connection for multiple calls. | |
| 76 | + - XML to JavaScript parser is able to parse any XML structure now. | |
| 77 | + - support for XML attributes. | |
| 78 | + - added find_item_by_type() to cpaint_result_object. | |
| 79 | + - added get_attribute(), set_attribute() to cpaint_result_object. | |
| 80 | + - improved debugging, added debuglevels 1, 2. see set_debug() | |
| 81 | + | |
| 82 | +- completely rewrote the PHP and ASP backends. | |
| 83 | + - 100% OOP approach. | |
| 84 | + - protection against remote code-execution. | |
| 85 | + - support for object methods as callable functions. | |
| 86 | + - nested XML support through composite pattern. | |
| 87 | + - arbitrary XML structure creation support. | |
| 88 | + - support for arbitrary character sets. | |
| 89 | + - replaced remote code-execution blacklisting approach of v1.3-SP | |
| 90 | + with whitelisting through cpaint.register(). | |
| 91 | + - changed XML name convention. Attributes and tagnames are send | |
| 92 | + as-is, not in uppercase. | |
| 93 | + | |
| 94 | +- improved readability of the ASP and PHP proxy utilities. | |
| 95 | + - fixed GET requests. | |
| 96 | + - proxy now uses attributenames identical to the CPAINT backend | |
| 97 | + itself whenever possible. | |
| 98 | + - added support for target ports other than 80. | |
| 99 | + - added support for Basic Authentication. | |
| 100 | + - completely redesigned and rewritten documentation! | |
| 101 | + | |
| 102 | +//---- v1.3-SP2 (Released August 16, 2005) -------------------------- | |
| 103 | +- improved the remote-code execution fix. | |
| 104 | + | |
| 105 | +//---- v1.3-SP (Released August 15, 2005) --------------------------- | |
| 106 | +- fixed a remote code-execution vulnerability on the backend by | |
| 107 | + adding a blacklist test on the function name passed by the | |
| 108 | + frontend. | |
| 109 | + | |
| 110 | +//---- v1.3 (Released July 10, 2005) -------------------------------- | |
| 111 | +- rewrote cpaint_parse_ajax_xml() to be able to parse arbitrary XML. | |
| 112 | +- added bugfix from v1.01 to cpaint_get_remote_file() as well. | |
| 113 | + | |
| 114 | +//---- v1.2 (Released June 20, 2005) -------------------------------- | |
| 115 | +- added method cpaint_parse_ajax_xml() to the JavaScript frontend for | |
| 116 | + primitive XML to JavaScript transformation of response data. | |
| 117 | + | |
| 118 | +//---- v1.01 (Released June 16, 2005) ------------------------------- | |
| 119 | +- bugfix regarding Unicode character encoding in cpaint_call(). | |
| 120 | + | |
| 121 | +//---- v1.0 (Released June 13, 2005) -------------------------------- | |
| 122 | +- added support for XML/DOM document object returns. | |
| 123 | +- added support for XML on the backend. | |
| 124 | +- added proxy files for accessing remote functions & data. | |
| 125 | +- greatly improved documentation & examples. | |
| 126 | +- code stablized | |
| 127 | +- development effort moved to SourceForge.Net | |
| 128 | + | |
| 129 | +//---- Pre-1.0 Releases and Dates ----------------------------------- | |
| 130 | +- v0.7 - released June 8, 2005 | |
| 131 | +- v0.5 & 0.6 - released June 6, 2005 | |
| 132 | +- v0.4 - released June 3, 2005 | |
| 133 | +- v0.2 & 0.3 - released June 2, 2005 | |
| 134 | +- v0.1 - released May 31, 2005 (first release) | ... | ... |
| ... | ... | @@ -0,0 +1,104 @@ |
| 1 | +<? | |
| 2 | +/* | |
| 3 | + CPAINT Backend Debug Interface | |
| 4 | + | |
| 5 | + released under the terms of the GPL | |
| 6 | + see http://www.fsf.org/licensing/licenses/gpl.txt for details | |
| 7 | + | |
| 8 | + @package CPAINT | |
| 9 | + @access public | |
| 10 | + @author Paul Sullivan <wiley14@gmail.com> | |
| 11 | + @author Stephan Tijink <stijink@googlemail.com> | |
| 12 | + @copyright Copyright (c) 2005-2006 Paul Sullivan - http://sf.net/projects/cpaint | |
| 13 | + @version 2.0.3 | |
| 14 | +*/ | |
| 15 | + | |
| 16 | +if (!(isset($_REQUEST["cpaint_function"]))) { | |
| 17 | + $debug_html_start = ' | |
| 18 | + <html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> | |
| 19 | + <title>CPAINT Debug Interface</title> | |
| 20 | + <style type="text/css">body { background-color: #9999CC; margin-top: 0px; } | |
| 21 | + .style3 { font-family: Arial, Helvetica, sans-serif; font-size: 12px; } | |
| 22 | + .style4 { font-size: 20px; color: #FFFFFF; font-family: Arial, Helvetica, sans-serif;} | |
| 23 | + .style5 { font-size: 16px; color: #FFFFFF; font-family: Arial, Helvetica, sans-serif; font-weight: bold; } | |
| 24 | + .style6 { font-size: 12px; color: #FFFFFF; font-family: Arial, Helvetica, sans-serif; font-weight: bold; } | |
| 25 | + div, iframe { margin: 0px; border: 1px solid #9999CC; } | |
| 26 | + </style> | |
| 27 | + <script type="text/javascript"> | |
| 28 | + function showForm(divId) { | |
| 29 | + if (document.getElementById(divId).style.display == "block") { | |
| 30 | + document.getElementById(divId).style.display = "none"; | |
| 31 | + } else { | |
| 32 | + document.getElementById(divId).style.display = "block"; | |
| 33 | + } | |
| 34 | + } | |
| 35 | + </script> | |
| 36 | + </head> | |
| 37 | + <body> | |
| 38 | + <table width="100%" border="0" cellspacing="0" cellpadding="0"> | |
| 39 | + <tr><td bgcolor="#9999CC"><div align="justify"><span class="style4">CPAINT Debug Interface</span></div></td></tr> | |
| 40 | + <tr><td bgcolor="#9999CC"><div align="justify"><span class="style6" style="color:#FFFFFF;">backend filename: '.$_SERVER["SCRIPT_NAME"].'</span></div></td></tr> | |
| 41 | + <tr><td height="10" bgcolor="#9999CC" class="style3"></td></tr> | |
| 42 | + <tr><td height="10" bgcolor="#9999CC" class="style3"></td></tr> | |
| 43 | + <tr><td bgcolor="#FFFFFF" class="style3"><blockquote>'; | |
| 44 | + $debug_html_end = "</blockquote></td></tr></table><br><iframe name=\"results\" class=\"style3\" width=\"100%\" height=\"100%\" scrolling=\"yes\" allowtransparency=\"false\" style=\"background-color: #FFFFFF\"></iframe></body></html>"; | |
| 45 | + | |
| 46 | + // get function names and function variables/values | |
| 47 | + $functionArray = getCallableCode(); | |
| 48 | + | |
| 49 | + $debug_body = ""; | |
| 50 | + if (count($functionArray) > 0) { | |
| 51 | + foreach ($functionArray as $func_name=>$func_variables) { | |
| 52 | + $debug_body = $debug_body . "<form method=\"get\" target=\"results\"><a href=\"javascript:showForm('" . $func_name . "_form');\">" . $func_name . "</a><div id=\"" . $func_name . "_form\" style=\"display: none;\">"; | |
| 53 | + | |
| 54 | + $debug_body = $debug_body . '<table border="0">'; | |
| 55 | + if ( count($func_variables) > 0) { | |
| 56 | + foreach ($func_variables as $var_name=>$var_preset) { | |
| 57 | + $debug_body = $debug_body . '<tr><td class="style3">Parameter (<strong>$'.$var_name.'</strong>):</td><td><input type="text" name="cpaint_argument[]"></td>'; | |
| 58 | + if (strlen($var_preset) > 0) { $debug_body = $debug_body . "<td class=\"style3\"> default value is <strong>".$var_preset." </strong></td>"; } | |
| 59 | + $debug_body = $debug_body . '</tr>'; | |
| 60 | + } | |
| 61 | + } | |
| 62 | + $debug_body = $debug_body . "<tr><td class=\"style3\">Response Type:</td><td><select name=\"cpaint_response_type\"><option>TEXT</option><option>XML</option><option>OBJECT</option></select></td></tr>"; | |
| 63 | + $debug_body = $debug_body . "<tr><td colspan=\"3\"><input type=\"hidden\" name=\"cpaint_function\" value=\"" . $func_name . "\"><input type=\"submit\"></td></tr></table></div></form>"; | |
| 64 | + } | |
| 65 | + } | |
| 66 | + | |
| 67 | + print($debug_html_start . $debug_body . $debug_html_end); | |
| 68 | + die(); | |
| 69 | +} | |
| 70 | + | |
| 71 | +function getCallableCode() { | |
| 72 | + $scriptName = $_SERVER['SCRIPT_FILENAME']; | |
| 73 | + $fileLines = file($scriptName); | |
| 74 | + for ($i=0; $i < sizeof($fileLines); $i++) { | |
| 75 | + $line = trim($fileLines[$i]); | |
| 76 | + if (substr($line, 0, 9) == "FUNCTION " || substr($line,0,9) == "function ") { | |
| 77 | + $match[] = $line; | |
| 78 | + } | |
| 79 | + } | |
| 80 | + for ($i = 0; $i < sizeof($match); $i++) { | |
| 81 | + $line = str_replace("function ", "", $match[$i]); | |
| 82 | + $line = str_replace("FUNCTION ", "", $line); | |
| 83 | + $line = str_replace("{", "", $line); | |
| 84 | + $parts = explode("(", $line); | |
| 85 | + $func_name = trim($parts[0]); | |
| 86 | + | |
| 87 | + $Tempargs = explode(")", $parts[1]); | |
| 88 | + $args = explode(",", $Tempargs[0]); | |
| 89 | + $argSize = sizeof($args); | |
| 90 | + | |
| 91 | + // check args for preset values | |
| 92 | + if ($argSize > 0) { | |
| 93 | + foreach ($args as $arg) { | |
| 94 | + $arg = trim ($arg); | |
| 95 | + $varArray = explode ("=", $arg); | |
| 96 | + $var_name = trim (str_replace ("$", "", $varArray["0"])); | |
| 97 | + $var_value = trim ($varArray["1"]); | |
| 98 | + $resultArray[$func_name][$var_name] = $var_value; | |
| 99 | + } | |
| 100 | + } | |
| 101 | + } | |
| 102 | + return $resultArray; | |
| 103 | +} | |
| 104 | +?> | |
| 0 | 105 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,35 @@ |
| 1 | +<? | |
| 2 | +/** | |
| 3 | +* CPAINT (Cross-Platform Asynchronous INterface Toolkit) | |
| 4 | +* | |
| 5 | +* http://sf.net/projects/cpaint | |
| 6 | +* | |
| 7 | +* released under the terms of the GPL | |
| 8 | +* see http://www.fsf.org/licensing/licenses/gpl.txt for details | |
| 9 | +* | |
| 10 | +* $Id$ | |
| 11 | +* $Log$ | |
| 12 | +* | |
| 13 | +* Configuration file for backend scripts, including proxy | |
| 14 | +* | |
| 15 | +* @package CPAINT | |
| 16 | +* @author Paul Sullivan <wiley14@gmail.com> | |
| 17 | +* @author Dominique Stender <dstender@st-webdevelopment.de> | |
| 18 | +* @copyright Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint | |
| 19 | +* @version 2.0.3 | |
| 20 | +*/ | |
| 21 | + | |
| 22 | +//---- proxy settings ---------------------------------------------------------- | |
| 23 | + $cpaint2_config["proxy.security.use_whitelist"] = true; | |
| 24 | + // Use the whitelist for allowed URLs? | |
| 25 | + | |
| 26 | +//---- proxy security whitelist ------------------------------------------------ | |
| 27 | + /* whitelist data should be added to the variable $cpaint2_proxy_whitelist[] | |
| 28 | + example: $cpaint2_proxy_whitelist[] = "example.com/test.php"; | |
| 29 | + - or - | |
| 30 | + example: $cpaint2_proxy_whitelist[] = "example.com"; | |
| 31 | + ** Omit http:// and https:// from the URL ** | |
| 32 | + */ | |
| 33 | + $cpaint2_proxy_whitelist[] = $_SERVER['HTTP_HOST']; // this server | |
| 34 | + | |
| 35 | +?> | |
| 0 | 36 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,116 @@ |
| 1 | +function cpaint(){this.version='2.0.3';var config=new Array();config['debugging']=-1;config['proxy_url']='';config['transfer_mode']='GET';config['async']=true;config['response_type']='OBJECT';config['persistent_connection']=false;config['use_cpaint_api']=true;var stack_count=0;this.capable=test_ajax_capability();this.set_debug=function(){if(typeof arguments[0]=='boolean'){if(arguments[0]===true){config['debugging']=1;}else{config['debugging']=0;}}else if(typeof arguments[0]=='number'){config['debugging']=Math.round(arguments[0]);}} | |
| 2 | +this.set_proxy_url=function(){if(typeof arguments[0]=='string'){config['proxy_url']=arguments[0];}} | |
| 3 | +this.set_transfer_mode=function(){if(arguments[0].toUpperCase()=='GET'||arguments[0].toUpperCase()=='POST'){config['transfer_mode']=arguments[0].toUpperCase();}} | |
| 4 | +this.set_async=function(){if(typeof arguments[0]=='boolean'){config['async']=arguments[0];}} | |
| 5 | +this.set_response_type=function(){if(arguments[0].toUpperCase()=='TEXT'||arguments[0].toUpperCase()=='XML'||arguments[0].toUpperCase()=='OBJECT'||arguments[0].toUpperCase()=='E4X'||arguments[0].toUpperCase()=='JSON'){config['response_type']=arguments[0].toUpperCase();}} | |
| 6 | +this.set_persistent_connection=function(){if(typeof arguments[0]=='boolean'){config['persistent_connection']=arguments[0];}} | |
| 7 | +this.set_use_cpaint_api=function(){if(typeof arguments[0]=='boolean'){config['use_cpaint_api']=arguments[0];}} | |
| 8 | +function test_ajax_capability(){var cpc=new cpaint_call(0,config,this.version);return cpc.test_ajax_capability();} | |
| 9 | +this.call=function(){var use_stack=-1;if(config['persistent_connection']==true&&__cpaint_stack[0]!=null){switch(__cpaint_stack[0].get_http_state()){case-1:use_stack=0;debug('no XMLHttpObject object to re-use for persistence, creating new one later',2);break;case 4:use_stack=0 | |
| 10 | +debug('re-using the persistent connection',2);break;default:debug('the persistent connection is in use - skipping this request',2);}}else if(config['persistent_connection']==true){use_stack=0;__cpaint_stack[use_stack]=new cpaint_call(use_stack,config,this.version);debug('no cpaint_call object available for re-use, created new one',2);}else{use_stack=stack_count;__cpaint_stack[use_stack]=new cpaint_call(use_stack,config,this.version);debug('no cpaint_call object created new one',2);} | |
| 11 | +if(use_stack!=-1){__cpaint_stack[use_stack].set_client_callback(arguments[2]);if(config['proxy_url']!=''){__cpaint_stack[use_stack].call_proxy(arguments);}else{__cpaint_stack[use_stack].call_direct(arguments);} | |
| 12 | +stack_count++;debug('stack size: '+__cpaint_stack.length,2);}} | |
| 13 | +var debug=function(message,debug_level){var prefix='[CPAINT Debug] ';if(debug_level<1){prefix='[CPAINT Error] ';} | |
| 14 | +if(config['debugging']>=debug_level){alert(prefix+message);}if (message.search("error") > 1){client_callback("", message);}}} | |
| 15 | +var __cpaint_stack=new Array();var __cpaint_transformer=new cpaint_transformer();function cpaint_call(){var version=arguments[2];var config=new Array();config['debugging']=arguments[1]['debugging'];config['proxy_url']=arguments[1]['proxy_url'];config['transfer_mode']=arguments[1]['transfer_mode'];config['async']=arguments[1]['async'];config['response_type']=arguments[1]['response_type'];config['persistent_connection']=arguments[1]['persistent_connection'];config['use_cpaint_api']=arguments[1]['use_cpaint_api'];var httpobj=false;var client_callback;var stack_id=arguments[0];this.set_client_callback=function(){if(typeof arguments[0]=='function'){client_callback=arguments[0];}} | |
| 16 | +this.get_http_state=function(){var return_value=-1;if(typeof httpobj=='object'){return_value=httpobj.readyState;} | |
| 17 | +return return_value;} | |
| 18 | +this.call_direct=function(call_arguments){var url=call_arguments[0];var remote_method=call_arguments[1];var querystring='';var i=0;if(url=='SELF'){url=document.location.href;} | |
| 19 | +if(config['use_cpaint_api']==true){for(i=3;i<call_arguments.length;i++){if((typeof call_arguments[i]=='string'&&call_arguments[i]!=''&&call_arguments[i].search(/^\s+$/g)==-1)&&!isNaN(call_arguments[i])&&isFinite(call_arguments[i])){querystring+='&cpaint_argument[]='+encodeURIComponent(JSON.stringify(Number(call_arguments[i])));}else{querystring+='&cpaint_argument[]='+encodeURIComponent(JSON.stringify(call_arguments[i]));}} | |
| 20 | +querystring+='&cpaint_response_type='+config['response_type'];if(config['transfer_mode']=='GET'){if(url.indexOf('?')!=-1){url=url+'&cpaint_function='+remote_method+querystring;}else{url=url+'?cpaint_function='+remote_method+querystring;}}else{querystring='cpaint_function='+remote_method+querystring;}}else{for(i=3;i<call_arguments.length;i++){if(i==3){querystring+=encodeURIComponent(call_arguments[i]);}else{querystring+='&'+encodeURIComponent(call_arguments[i]);}} | |
| 21 | +if(config['transfer_mode']=='GET'){url=url+querystring;}} | |
| 22 | +get_connection_object();debug('opening connection to "'+url+'"',1);httpobj.open(config['transfer_mode'],url,config['async']);if(config['transfer_mode']=='POST'){try{httpobj.setRequestHeader('Content-Type','application/x-www-form-urlencoded');}catch(cp_err){debug('POST cannot be completed due to incompatible browser. Use GET as your request method.',0);}} | |
| 23 | +httpobj.setRequestHeader('X-Powered-By','CPAINT v'+version+' :: http://sf.net/projects/cpaint');httpobj.onreadystatechange=callback;if(config['transfer_mode']=='GET'){httpobj.send(null);}else{debug('sending query: '+querystring,1);httpobj.send(querystring);} | |
| 24 | +if(config['async']==true){callback();}} | |
| 25 | +this.call_proxy=function(call_arguments){var proxyscript=config['proxy_url'];var url=call_arguments[0];var remote_method=call_arguments[1];var querystring='';var i=0;var querystring_argument_prefix='cpaint_argument[]=';if(config['use_cpaint_api']==false){querystring_argument_prefix='';} | |
| 26 | +for(i=3;i<call_arguments.length;i++){if(config['use_cpaint_api']==true){if((typeof call_arguments[i]=='string'&&call_arguments[i]!=''&&call_arguments[i].search(/^\s+$/g)==-1)&&!isNaN(call_arguments[i])&&isFinite(call_arguments[i])){querystring+=encodeURIComponent(querystring_argument_prefix+JSON.stringify(Number(call_arguments[i]))+'&');}else{querystring+=encodeURIComponent(querystring_argument_prefix+JSON.stringify(call_arguments[i])+'&');}}else{querystring+=encodeURIComponent(querystring_argument_prefix+call_arguments[i]+'&');}} | |
| 27 | +if(config['use_cpaint_api']==true){querystring+=encodeURIComponent('&cpaint_function='+remote_method);querystring+=encodeURIComponent('&cpaint_responsetype='+config['response_type']);} | |
| 28 | +if(config['transfer_mode']=='GET'){proxyscript+='?cpaint_remote_url='+encodeURIComponent(url) | |
| 29 | ++'&cpaint_remote_query='+querystring | |
| 30 | ++'&cpaint_remote_method='+config['transfer_mode'] | |
| 31 | ++'&cpaint_response_type='+config['response_type'];}else{querystring='cpaint_remote_url='+encodeURIComponent(url) | |
| 32 | ++'&cpaint_remote_query='+querystring | |
| 33 | ++'&cpaint_remote_method='+config['transfer_mode'] | |
| 34 | ++'&cpaint_response_type='+config['response_type'];} | |
| 35 | +get_connection_object();debug('opening connection to proxy "'+proxyscript+'"',1);httpobj.open(config['transfer_mode'],proxyscript,config['async']);if(config['transfer_mode']=='POST'){try{httpobj.setRequestHeader('Content-Type','application/x-www-form-urlencoded');}catch(cp_err){debug('POST cannot be completed due to incompatible browser. Use GET as your request method.',0);}} | |
| 36 | +httpobj.setRequestHeader('X-Powered-By','CPAINT v'+version);httpobj.onreadystatechange=callback;if(config['transfer_mode']=='GET'){httpobj.send(null);}else{debug('sending query: '+querystring,1);httpobj.send(querystring);} | |
| 37 | +if(config['async']==false){callback();}} | |
| 38 | +this.test_ajax_capability=function(){return get_connection_object();} | |
| 39 | +var get_connection_object=function(){var return_value=false;var new_connection=false;if(config['persistent_connection']==false){debug('Using new connection object',1);new_connection=true;}else{debug('Using shared connection object.',1);if(typeof httpobj!='object'){debug('Getting new persistent connection object.',1);new_connection=true;}} | |
| 40 | +if(new_connection==true){try{httpobj=new XMLHttpRequest();}catch(e1){try{httpobj=new ActiveXObject('Msxml2.XMLHTTP');}catch(e){try{httpobj=new ActiveXObject('Microsoft.XMLHTTP');}catch(oc){httpobj=null;}}} | |
| 41 | +if(!httpobj){debug('Could not create connection object',0);}else{return_value=true;}} | |
| 42 | +if(httpobj.readyState!=4){httpobj.abort();} | |
| 43 | +return return_value;} | |
| 44 | +var callback=function(){var response=null;if(httpobj.readyState==4&&httpobj.status==200){debug(httpobj.responseText,1);debug('using response type '+config['response_type'],2);switch(config['response_type']){case'XML':debug(httpobj.responseXML,2);response=__cpaint_transformer.xml_conversion(httpobj.responseXML);break;case'OBJECT':response=__cpaint_transformer.object_conversion(httpobj.responseXML);break;case'TEXT':response=__cpaint_transformer.text_conversion(httpobj.responseText);break;case'E4X':response=__cpaint_transformer.e4x_conversion(httpobj.responseText);break;case'JSON':response=__cpaint_transformer.json_conversion(httpobj.responseText);break;default:debug('invalid response type \''+response_type+'\'',0);} | |
| 45 | +if(response!=null&&typeof client_callback=='function'){client_callback(response,httpobj.responseText);} | |
| 46 | +remove_from_stack();}else if(httpobj.readyState==4&&httpobj.status!=200){debug('invalid HTTP response code \''+Number(httpobj.status)+'\'',0);client_callback("", "erro");}} | |
| 47 | +var remove_from_stack=function(){if(typeof stack_id=='number'&&__cpaint_stack[stack_id]&&config['persistent_connection']==false){__cpaint_stack[stack_id]=null;}} | |
| 48 | +var debug=function(message,debug_level){var prefix='[CPAINT Debug] ';if(config['debugging']<1){prefix='[CPAINT Error] ';if (message.search("error") > 1){client_callback("", message);}} | |
| 49 | +if(config['debugging']>=debug_level){alert(prefix+message);}}} | |
| 50 | +function cpaint_transformer(){this.object_conversion=function(xml_document){var return_value=new cpaint_result_object();var i=0;var firstNodeName='';if(typeof xml_document=='object'&&xml_document!=null){for(i=0;i<xml_document.childNodes.length;i++){if(xml_document.childNodes[i].nodeType==1){firstNodeName=xml_document.childNodes[i].nodeName;break;}} | |
| 51 | +var ajax_response=xml_document.getElementsByTagName(firstNodeName);return_value[firstNodeName]=new Array();for(i=0;i<ajax_response.length;i++){var tmp_node=create_object_structure(ajax_response[i]);tmp_node.id=ajax_response[i].getAttribute('id') | |
| 52 | +return_value[firstNodeName].push(tmp_node);}}else{debug('received invalid XML response',0);} | |
| 53 | +return return_value;} | |
| 54 | +this.xml_conversion=function(xml_document){return xml_document;} | |
| 55 | +this.text_conversion=function(text){return decode(text);} | |
| 56 | +this.e4x_conversion=function(text){text=text.replace(/^\<\?xml[^>]+\>/,'');return new XML(text);} | |
| 57 | +this.json_conversion=function(text){return JSON.parse(text);} | |
| 58 | +var create_object_structure=function(stream){var return_value=new cpaint_result_object();var node_name='';var i=0;var attrib=0;if(stream.hasChildNodes()==true){for(i=0;i<stream.childNodes.length;i++){node_name=stream.childNodes[i].nodeName;node_name=node_name.replace(/[^a-zA-Z0-9_]*/g,'');if(typeof return_value[node_name]!='object'){return_value[node_name]=new Array();} | |
| 59 | +if(stream.childNodes[i].nodeType==1){var tmp_node=create_object_structure(stream.childNodes[i]);for(attrib=0;attrib<stream.childNodes[i].attributes.length;attrib++){tmp_node.set_attribute(stream.childNodes[i].attributes[attrib].nodeName,stream.childNodes[i].attributes[attrib].nodeValue);} | |
| 60 | +return_value[node_name].push(tmp_node);}else if(stream.childNodes[i].nodeType==3){return_value.data=decode(String(stream.firstChild.data));}}} | |
| 61 | +return return_value;} | |
| 62 | +var decode=function(rawtext){var plaintext='';var i=0;var c1=0;var c2=0;var c3=0;var u=0;var t=0;while(i<rawtext.length){if(rawtext.charAt(i)=='\\'&&rawtext.charAt(i+1)=='u'){u=0;for(j=2;j<6;j+=1){t=parseInt(rawtext.charAt(i+j),16);if(!isFinite(t)){break;} | |
| 63 | +u=u*16+t;} | |
| 64 | +plaintext+=String.fromCharCode(u);i+=6;}else{plaintext+=rawtext.charAt(i);i++;}} | |
| 65 | +if(plaintext!=''&&plaintext.search(/^\s+$/g)==-1&&!isNaN(plaintext)&&isFinite(plaintext)){plaintext=Number(plaintext);} | |
| 66 | +return plaintext;}} | |
| 67 | +function cpaint_result_object(){this.id=0;this.data='';var __attributes=new Array();this.find_item_by_id=function(){var return_value=null;var type=arguments[0];var id=arguments[1];var i=0;if(this[type]){for(i=0;i<this[type].length;i++){if(this[type][i].get_attribute('id')==id){return_value=this[type][i];break;}}} | |
| 68 | +return return_value;} | |
| 69 | +this.get_attribute=function(){var return_value=null;var id=arguments[0];if(typeof __attributes[id]!='undefined'){return_value=__attributes[id];} | |
| 70 | +return return_value;} | |
| 71 | +this.set_attribute=function(){__attributes[arguments[0]]=arguments[1];}} | |
| 72 | +Array.prototype.______array='______array';var JSON={org:'http://www.JSON.org',copyright:'(c)2005 JSON.org',license:'http://www.crockford.com/JSON/license.html',stringify:function(arg){var c,i,l,s='',v;var numeric=true;switch(typeof arg){case'object':if(arg){if(arg.______array=='______array'){for(i in arg){if(i!='______array'&&(isNaN(i)||!isFinite(i))){numeric=false;break;}} | |
| 73 | +if(numeric==true){for(i=0;i<arg.length;++i){if(typeof arg[i]!='undefined'){v=this.stringify(arg[i]);if(s){s+=',';} | |
| 74 | +s+=v;}else{s+=',null';}} | |
| 75 | +return'['+s+']';}else{for(i in arg){if(i!='______array'){v=arg[i];if(typeof v!='undefined'&&typeof v!='function'){v=this.stringify(v);if(s){s+=',';} | |
| 76 | +s+=this.stringify(i)+':'+v;}}} | |
| 77 | +return'{'+s+'}';}}else if(typeof arg.toString!='undefined'){for(i in arg){v=arg[i];if(typeof v!='undefined'&&typeof v!='function'){v=this.stringify(v);if(s){s+=',';} | |
| 78 | +s+=this.stringify(i)+':'+v;}} | |
| 79 | +return'{'+s+'}';}} | |
| 80 | +return'null';case'number':return isFinite(arg)?String(arg):'null';case'string':l=arg.length;s='"';for(i=0;i<l;i+=1){c=arg.charAt(i);if(c>=' '){if(c=='\\'||c=='"'){s+='\\';} | |
| 81 | +s+=c;}else{switch(c){case'\b':s+='\\b';break;case'\f':s+='\\f';break;case'\n':s+='\\n';break;case'\r':s+='\\r';break;case'\t':s+='\\t';break;default:c=c.charCodeAt();s+='\\u00'+Math.floor(c/16).toString(16)+ | |
| 82 | +(c%16).toString(16);}}} | |
| 83 | +return s+'"';case'boolean':return String(arg);default:return'null';}},parse:function(text){var at=0;var ch=' ';function error(m){throw{name:'JSONError',message:m,at:at-1,text:text};} | |
| 84 | +function next(){ch=text.charAt(at);at+=1;return ch;} | |
| 85 | +function white(){while(ch!=''&&ch<=' '){next();}} | |
| 86 | +function str(){var i,s='',t,u;if(ch=='"'){outer:while(next()){if(ch=='"'){next();return s;}else if(ch=='\\'){switch(next()){case'b':s+='\b';break;case'f':s+='\f';break;case'n':s+='\n';break;case'r':s+='\r';break;case't':s+='\t';break;case'u':u=0;for(i=0;i<4;i+=1){t=parseInt(next(),16);if(!isFinite(t)){break outer;} | |
| 87 | +u=u*16+t;} | |
| 88 | +s+=String.fromCharCode(u);break;default:s+=ch;}}else{s+=ch;}}} | |
| 89 | +error("Bad string");} | |
| 90 | +function arr(){var a=[];if(ch=='['){next();white();if(ch==']'){next();return a;} | |
| 91 | +while(ch){a.push(val());white();if(ch==']'){next();return a;}else if(ch!=','){break;} | |
| 92 | +next();white();}} | |
| 93 | +error("Bad array");} | |
| 94 | +function obj(){var k,o={};if(ch=='{'){next();white();if(ch=='}'){next();return o;} | |
| 95 | +while(ch){k=str();white();if(ch!=':'){break;} | |
| 96 | +next();o[k]=val();white();if(ch=='}'){next();return o;}else if(ch!=','){break;} | |
| 97 | +next();white();}} | |
| 98 | +error("Bad object");} | |
| 99 | +function assoc(){var k,a=[];if(ch=='<'){next();white();if(ch=='>'){next();return a;} | |
| 100 | +while(ch){k=str();white();if(ch!=':'){break;} | |
| 101 | +next();a[k]=val();white();if(ch=='>'){next();return a;}else if(ch!=','){break;} | |
| 102 | +next();white();}} | |
| 103 | +error("Bad associative array");} | |
| 104 | +function num(){var n='',v;if(ch=='-'){n='-';next();} | |
| 105 | +while(ch>='0'&&ch<='9'){n+=ch;next();} | |
| 106 | +if(ch=='.'){n+='.';while(next()&&ch>='0'&&ch<='9'){n+=ch;}} | |
| 107 | +if(ch=='e'||ch=='E'){n+='e';next();if(ch=='-'||ch=='+'){n+=ch;next();} | |
| 108 | +while(ch>='0'&&ch<='9'){n+=ch;next();}} | |
| 109 | +v=+n;if(!isFinite(v)){error("Bad number");}else{return v;}} | |
| 110 | +function word(){switch(ch){case't':if(next()=='r'&&next()=='u'&&next()=='e'){next();return true;} | |
| 111 | +break;case'f':if(next()=='a'&&next()=='l'&&next()=='s'&&next()=='e'){next();return false;} | |
| 112 | +break;case'n':if(next()=='u'&&next()=='l'&&next()=='l'){next();return null;} | |
| 113 | +break;} | |
| 114 | +error("Syntax error");} | |
| 115 | +function val(){white();switch(ch){case'{':return obj();case'[':return arr();case'<':return assoc();case'"':return str();case'-':return num();default:return ch>='0'&&ch<='9'?num():word();}} | |
| 116 | +return val();}}; | |
| 0 | 117 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,1429 @@ |
| 1 | +/** | |
| 2 | +* CPAINT - Cross-Platform Asynchronous INterface Toolkit | |
| 3 | +* | |
| 4 | +* http://sf.net/projects/cpaint | |
| 5 | +* | |
| 6 | +* released under the terms of the LGPL | |
| 7 | +* see http://www.fsf.org/licensing/licenses/lgpl.txt for details | |
| 8 | +* | |
| 9 | +* @package CPAINT | |
| 10 | +* @access public | |
| 11 | +* @copyright Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint | |
| 12 | +* @author Paul Sullivan <wiley14@gmail.com> | |
| 13 | +* @author Dominique Stender <dstender@st-webdevelopment.de> | |
| 14 | +* @author Stephan Tijink <stijink@googlemail.com> | |
| 15 | +* @version 2.0.3 | |
| 16 | +*/ | |
| 17 | +function cpaint() { | |
| 18 | + /** | |
| 19 | + * CPAINT version | |
| 20 | + * | |
| 21 | + * @access protected | |
| 22 | + * @var string version | |
| 23 | + */ | |
| 24 | + this.version = '2.0.3'; | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * configuration options both for this class but also for the cpaint_call() objects. | |
| 28 | + * | |
| 29 | + * @access protected | |
| 30 | + * @var array config | |
| 31 | + */ | |
| 32 | + var config = new Array(); | |
| 33 | + config['debugging'] = -1; | |
| 34 | + config['proxy_url'] = ''; | |
| 35 | + config['transfer_mode'] = 'GET'; | |
| 36 | + config['async'] = true; | |
| 37 | + config['response_type'] = 'OBJECT'; | |
| 38 | + config['persistent_connection'] = false; | |
| 39 | + config['use_cpaint_api'] = true; | |
| 40 | + | |
| 41 | + /** | |
| 42 | + * maintains the next free index in the stack | |
| 43 | + * | |
| 44 | + * @access protected | |
| 45 | + * @var integer stack_count | |
| 46 | + */ | |
| 47 | + var stack_count = 0; | |
| 48 | + | |
| 49 | + /** | |
| 50 | + * property returns whether or not the browser is AJAX capable | |
| 51 | + * | |
| 52 | + * @access public | |
| 53 | + * @return boolean | |
| 54 | + */ | |
| 55 | + this.capable = test_ajax_capability(); | |
| 56 | + | |
| 57 | + /** | |
| 58 | + * switches debug mode on/off. | |
| 59 | + * | |
| 60 | + * @access public | |
| 61 | + * @param boolean debug debug flag | |
| 62 | + * @return void | |
| 63 | + */ | |
| 64 | + this.set_debug = function() { | |
| 65 | + | |
| 66 | + if (typeof arguments[0] == 'boolean') { | |
| 67 | + if (arguments[0] === true) { | |
| 68 | + config['debugging'] = 1; | |
| 69 | + | |
| 70 | + } else { | |
| 71 | + config['debugging'] = 0; | |
| 72 | + } | |
| 73 | + | |
| 74 | + } else if (typeof arguments[0] == 'number') { | |
| 75 | + config['debugging'] = Math.round(arguments[0]); | |
| 76 | + } | |
| 77 | + } | |
| 78 | + | |
| 79 | + /** | |
| 80 | + * defines the URL of the proxy script. | |
| 81 | + * | |
| 82 | + * @access public | |
| 83 | + * @param string proxy_url URL of the proxyscript to connect | |
| 84 | + * @return void | |
| 85 | + */ | |
| 86 | + this.set_proxy_url = function() { | |
| 87 | + | |
| 88 | + if (typeof arguments[0] == 'string') { | |
| 89 | + | |
| 90 | + config['proxy_url'] = arguments[0]; | |
| 91 | + } | |
| 92 | + } | |
| 93 | + | |
| 94 | + /** | |
| 95 | + * sets the transfer_mode (GET|POST). | |
| 96 | + * | |
| 97 | + * @access public | |
| 98 | + * @param string transfer_mode transfer_mode | |
| 99 | + * @return void | |
| 100 | + */ | |
| 101 | + this.set_transfer_mode = function() { | |
| 102 | + | |
| 103 | + if (arguments[0].toUpperCase() == 'GET' | |
| 104 | + || arguments[0].toUpperCase() == 'POST') { | |
| 105 | + | |
| 106 | + config['transfer_mode'] = arguments[0].toUpperCase(); | |
| 107 | + } | |
| 108 | + } | |
| 109 | + | |
| 110 | + /** | |
| 111 | + * sets the flag whether or not to use asynchronous calls. | |
| 112 | + * | |
| 113 | + * @access public | |
| 114 | + * @param boolean async syncronization flag | |
| 115 | + * @return void | |
| 116 | + */ | |
| 117 | + this.set_async = function() { | |
| 118 | + | |
| 119 | + if (typeof arguments[0] == 'boolean') { | |
| 120 | + config['async'] = arguments[0]; | |
| 121 | + } | |
| 122 | + } | |
| 123 | + | |
| 124 | + /** | |
| 125 | + * defines the response type. | |
| 126 | + * | |
| 127 | + * allowed values are: | |
| 128 | + * TEXT = raw text response | |
| 129 | + * XML = raw XMLHttpObject | |
| 130 | + * OBJECT = parsed JavaScript object structure from XMLHttpObject | |
| 131 | + * | |
| 132 | + * the default is OBJECT. | |
| 133 | + * | |
| 134 | + * @access public | |
| 135 | + * @param string response_type response type | |
| 136 | + * @return void | |
| 137 | + */ | |
| 138 | + this.set_response_type = function() { | |
| 139 | + | |
| 140 | + if (arguments[0].toUpperCase() == 'TEXT' | |
| 141 | + || arguments[0].toUpperCase() == 'XML' | |
| 142 | + || arguments[0].toUpperCase() == 'OBJECT' | |
| 143 | + || arguments[0].toUpperCase() == 'E4X' | |
| 144 | + || arguments[0].toUpperCase() == 'JSON') { | |
| 145 | + | |
| 146 | + config['response_type'] = arguments[0].toUpperCase(); | |
| 147 | + } | |
| 148 | + } | |
| 149 | + | |
| 150 | + /** | |
| 151 | + * sets the flag whether or not to use a persistent connection. | |
| 152 | + * | |
| 153 | + * @access public | |
| 154 | + * @param boolean persistent_connection persistance flag | |
| 155 | + * @return void | |
| 156 | + */ | |
| 157 | + this.set_persistent_connection = function() { | |
| 158 | + | |
| 159 | + if (typeof arguments[0] == 'boolean') { | |
| 160 | + config['persistent_connection'] = arguments[0]; | |
| 161 | + } | |
| 162 | + } | |
| 163 | + | |
| 164 | + | |
| 165 | + /** | |
| 166 | + * sets the flag whether or not to use the cpaint api on the backend. | |
| 167 | + * | |
| 168 | + * @access public | |
| 169 | + * @param boolean cpaint_api api_flag | |
| 170 | + * @return void | |
| 171 | + */ | |
| 172 | + this.set_use_cpaint_api = function() { | |
| 173 | + if (typeof arguments[0] == 'boolean') { | |
| 174 | + config['use_cpaint_api'] = arguments[0]; | |
| 175 | + } | |
| 176 | + } | |
| 177 | + | |
| 178 | + /** | |
| 179 | + * tests whether one of the necessary implementations | |
| 180 | + * of the XMLHttpRequest class are available | |
| 181 | + * | |
| 182 | + * @access protected | |
| 183 | + * @return boolean | |
| 184 | + */ | |
| 185 | + function test_ajax_capability() { | |
| 186 | + var cpc = new cpaint_call(0, config, this.version); | |
| 187 | + return cpc.test_ajax_capability(); | |
| 188 | + } | |
| 189 | + | |
| 190 | + /** | |
| 191 | + * takes the arguments supplied and triggers a call to the CPAINT backend | |
| 192 | + * based on the settings. | |
| 193 | + * | |
| 194 | + * upon response cpaint_call.callback() will automatically be called | |
| 195 | + * to perform post-processing operations. | |
| 196 | + * | |
| 197 | + * @access public | |
| 198 | + * @param string url remote URL to call | |
| 199 | + * @param string remote_method remote method to call | |
| 200 | + * @param object client_callback client side callback method to deliver the remote response to. do NOT supply a string! | |
| 201 | + * @param mixed argN remote parameters from now on | |
| 202 | + * @return void | |
| 203 | + */ | |
| 204 | + this.call = function() { | |
| 205 | + var use_stack = -1; | |
| 206 | + | |
| 207 | + if (config['persistent_connection'] == true | |
| 208 | + && __cpaint_stack[0] != null) { | |
| 209 | + | |
| 210 | + switch (__cpaint_stack[0].get_http_state()) { | |
| 211 | + case -1: | |
| 212 | + // no XMLHttpObject object has already been instanciated | |
| 213 | + // create new object and configure it | |
| 214 | + use_stack = 0; | |
| 215 | + debug('no XMLHttpObject object to re-use for persistence, creating new one later', 2); | |
| 216 | + break; | |
| 217 | + | |
| 218 | + case 4: | |
| 219 | + // object is ready for a new request, no need to do anything | |
| 220 | + use_stack = 0 | |
| 221 | + debug('re-using the persistent connection', 2); | |
| 222 | + break; | |
| 223 | + | |
| 224 | + default: | |
| 225 | + // connection is currently in use, don't do anything | |
| 226 | + debug('the persistent connection is in use - skipping this request', 2); | |
| 227 | + } | |
| 228 | + | |
| 229 | + } else if (config['persistent_connection'] == true) { | |
| 230 | + // persistent connection is active, but no object has been instanciated | |
| 231 | + use_stack = 0; | |
| 232 | + __cpaint_stack[use_stack] = new cpaint_call(use_stack, config, this.version); | |
| 233 | + debug('no cpaint_call object available for re-use, created new one', 2); | |
| 234 | + | |
| 235 | + } else { | |
| 236 | + // no connection persistance | |
| 237 | + use_stack = stack_count; | |
| 238 | + __cpaint_stack[use_stack] = new cpaint_call(use_stack, config, this.version); | |
| 239 | + debug('no cpaint_call object created new one', 2); | |
| 240 | + } | |
| 241 | + | |
| 242 | + // configure cpaint_call if allowed to | |
| 243 | + if (use_stack != -1) { | |
| 244 | + __cpaint_stack[use_stack].set_client_callback(arguments[2]); | |
| 245 | + | |
| 246 | + // distribute according to proxy use | |
| 247 | + if (config['proxy_url'] != '') { | |
| 248 | + __cpaint_stack[use_stack].call_proxy(arguments); | |
| 249 | + | |
| 250 | + } else { | |
| 251 | + __cpaint_stack[use_stack].call_direct(arguments); | |
| 252 | + } | |
| 253 | + | |
| 254 | + // increase stack counter | |
| 255 | + stack_count++; | |
| 256 | + debug('stack size: ' + __cpaint_stack.length, 2); | |
| 257 | + } | |
| 258 | + } | |
| 259 | + | |
| 260 | + /** | |
| 261 | + * debug method | |
| 262 | + * | |
| 263 | + * @access protected | |
| 264 | + * @param string message the message to debug | |
| 265 | + * @param integer debug_level debug level at which the message appears | |
| 266 | + * @return void | |
| 267 | + */ | |
| 268 | + var debug = function(message, debug_level) { | |
| 269 | + var prefix = '[CPAINT Debug] '; | |
| 270 | + | |
| 271 | + if (debug_level < 1) { | |
| 272 | + prefix = '[CPAINT Error] '; | |
| 273 | + } | |
| 274 | + | |
| 275 | + if (config['debugging'] >= debug_level) { | |
| 276 | + alert(prefix + message); | |
| 277 | + } | |
| 278 | + } | |
| 279 | +} | |
| 280 | + | |
| 281 | +/** | |
| 282 | +* internal FIFO stack of cpaint_call() objects. | |
| 283 | +* | |
| 284 | +* @access protected | |
| 285 | +* @var array __cpaint_stack | |
| 286 | +*/ | |
| 287 | +var __cpaint_stack = new Array(); | |
| 288 | + | |
| 289 | +/** | |
| 290 | +* local instance of cpaint_transformer | |
| 291 | +* MSIE is unable to handle static classes... sheesh. | |
| 292 | +* | |
| 293 | +* @access public | |
| 294 | +* @var object __cpaint_transformer | |
| 295 | +*/ | |
| 296 | +var __cpaint_transformer = new cpaint_transformer(); | |
| 297 | + | |
| 298 | +/** | |
| 299 | +* transport agent class | |
| 300 | +* | |
| 301 | +* creates the request object, takes care of the response, handles the | |
| 302 | +* client callback. Is configured by the cpaint() object. | |
| 303 | +* | |
| 304 | +* @package CPAINT | |
| 305 | +* @access public | |
| 306 | +* @copyright Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint | |
| 307 | +* @author Dominique Stender <dstender@st-webdevelopment.de> | |
| 308 | +* @author Paul Sullivan <wiley14@gmail.com> | |
| 309 | +* @param integer stack_id stack Id in cpaint | |
| 310 | +* @param array config configuration array for this call | |
| 311 | +* @param string version CPAINT API version | |
| 312 | +*/ | |
| 313 | +function cpaint_call() { | |
| 314 | + /** | |
| 315 | + * CPAINT version | |
| 316 | + * | |
| 317 | + * @access protected | |
| 318 | + * @var string version | |
| 319 | + */ | |
| 320 | + var version = arguments[2]; | |
| 321 | + | |
| 322 | + /** | |
| 323 | + * configuration options both for this class objects. | |
| 324 | + * | |
| 325 | + * @access protected | |
| 326 | + * @var array config | |
| 327 | + */ | |
| 328 | + var config = new Array(); | |
| 329 | + config['debugging'] = arguments[1]['debugging']; | |
| 330 | + config['proxy_url'] = arguments[1]['proxy_url']; | |
| 331 | + config['transfer_mode'] = arguments[1]['transfer_mode']; | |
| 332 | + config['async'] = arguments[1]['async']; | |
| 333 | + config['response_type'] = arguments[1]['response_type']; | |
| 334 | + config['persistent_connection'] = arguments[1]['persistent_connection']; | |
| 335 | + config['use_cpaint_api'] = arguments[1]['use_cpaint_api']; | |
| 336 | + | |
| 337 | + /** | |
| 338 | + * XMLHttpObject used for this request. | |
| 339 | + * | |
| 340 | + * @access protected | |
| 341 | + * @var object httpobj | |
| 342 | + */ | |
| 343 | + var httpobj = false; | |
| 344 | + | |
| 345 | + /** | |
| 346 | + * client callback function. | |
| 347 | + * | |
| 348 | + * @access public | |
| 349 | + * @var function client_callback | |
| 350 | + */ | |
| 351 | + var client_callback; | |
| 352 | + | |
| 353 | + /** | |
| 354 | + * stores the stack Id within the cpaint object | |
| 355 | + * | |
| 356 | + * @access protected | |
| 357 | + * @var stack_id | |
| 358 | + */ | |
| 359 | + var stack_id = arguments[0]; | |
| 360 | + | |
| 361 | + /** | |
| 362 | + * sets the client callback function. | |
| 363 | + * | |
| 364 | + * @access public | |
| 365 | + * @param function client_callback the client callback function | |
| 366 | + * @return void | |
| 367 | + */ | |
| 368 | + this.set_client_callback = function() { | |
| 369 | + | |
| 370 | + if (typeof arguments[0] == 'function') { | |
| 371 | + client_callback = arguments[0]; | |
| 372 | + } | |
| 373 | + } | |
| 374 | + | |
| 375 | + /** | |
| 376 | + * returns the ready state of the internal XMLHttpObject | |
| 377 | + * | |
| 378 | + * if no such object was set up already, -1 is returned | |
| 379 | + * | |
| 380 | + * @access public | |
| 381 | + * @return integer | |
| 382 | + */ | |
| 383 | + this.get_http_state = function() { | |
| 384 | + var return_value = -1; | |
| 385 | + | |
| 386 | + if (typeof httpobj == 'object') { | |
| 387 | + return_value = httpobj.readyState; | |
| 388 | + } | |
| 389 | + | |
| 390 | + return return_value; | |
| 391 | + } | |
| 392 | + | |
| 393 | + /** | |
| 394 | + * internal method for remote calls to the local server without use of the proxy script. | |
| 395 | + * | |
| 396 | + * @access public | |
| 397 | + * @param array call_arguments array of arguments initially passed to cpaint.call() | |
| 398 | + * @return void | |
| 399 | + */ | |
| 400 | + this.call_direct = function(call_arguments) { | |
| 401 | + var url = call_arguments[0]; | |
| 402 | + var remote_method = call_arguments[1]; | |
| 403 | + var querystring = ''; | |
| 404 | + var i = 0; | |
| 405 | + | |
| 406 | + // correct link to self | |
| 407 | + if (url == 'SELF') { | |
| 408 | + url = document.location.href; | |
| 409 | + } | |
| 410 | + | |
| 411 | + if (config['use_cpaint_api'] == true) { | |
| 412 | + // backend uses cpaint api | |
| 413 | + // pass parameters to remote method | |
| 414 | + for (i = 3; i < call_arguments.length; i++) { | |
| 415 | + | |
| 416 | + if ((typeof call_arguments[i] == 'string' | |
| 417 | + && call_arguments[i] != '' | |
| 418 | + && call_arguments[i].search(/^\s+$/g) == -1) | |
| 419 | + && !isNaN(call_arguments[i]) | |
| 420 | + && isFinite(call_arguments[i])) { | |
| 421 | + // numerical value, convert it first | |
| 422 | + querystring += '&cpaint_argument[]=' + encodeURIComponent(JSON.stringify(Number(call_arguments[i]))); | |
| 423 | + | |
| 424 | + } else { | |
| 425 | + querystring += '&cpaint_argument[]=' + encodeURIComponent(JSON.stringify(call_arguments[i])); | |
| 426 | + } | |
| 427 | + } | |
| 428 | + | |
| 429 | + // add response type to querystring | |
| 430 | + querystring += '&cpaint_response_type=' + config['response_type']; | |
| 431 | + | |
| 432 | + // build header | |
| 433 | + if (config['transfer_mode'] == 'GET') { | |
| 434 | + | |
| 435 | + if(url.indexOf('?') != -1) { | |
| 436 | + url = url + '&cpaint_function=' + remote_method + querystring; | |
| 437 | + | |
| 438 | + } else { | |
| 439 | + url = url + '?cpaint_function=' + remote_method + querystring; | |
| 440 | + } | |
| 441 | + | |
| 442 | + } else { | |
| 443 | + querystring = 'cpaint_function=' + remote_method + querystring; | |
| 444 | + } | |
| 445 | + | |
| 446 | + } else { | |
| 447 | + // backend does not use cpaint api | |
| 448 | + // pass parameters to remote method | |
| 449 | + for (i = 3; i < call_arguments.length; i++) { | |
| 450 | + | |
| 451 | + if (i == 3) { | |
| 452 | + querystring += encodeURIComponent(call_arguments[i]); | |
| 453 | + | |
| 454 | + } else { | |
| 455 | + querystring += '&' + encodeURIComponent(call_arguments[i]); | |
| 456 | + } | |
| 457 | + } | |
| 458 | + | |
| 459 | + // build header | |
| 460 | + if (config['transfer_mode'] == 'GET') { | |
| 461 | + url = url + querystring; | |
| 462 | + } | |
| 463 | + } | |
| 464 | + | |
| 465 | + // open connection | |
| 466 | + get_connection_object(); | |
| 467 | + | |
| 468 | + // open connection to remote target | |
| 469 | + debug('opening connection to "' + url + '"', 1); | |
| 470 | + httpobj.open(config['transfer_mode'], url, config['async']); | |
| 471 | + | |
| 472 | + // send "urlencoded" header if necessary (if POST) | |
| 473 | + if (config['transfer_mode'] == 'POST') { | |
| 474 | + | |
| 475 | + try { | |
| 476 | + httpobj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); | |
| 477 | + | |
| 478 | + } catch (cp_err) { | |
| 479 | + debug('POST cannot be completed due to incompatible browser. Use GET as your request method.', 0); | |
| 480 | + } | |
| 481 | + } | |
| 482 | + | |
| 483 | + // make ourselves known | |
| 484 | + httpobj.setRequestHeader('X-Powered-By', 'CPAINT v' + version + ' :: http://sf.net/projects/cpaint'); | |
| 485 | + | |
| 486 | + // callback handling for asynchronous calls | |
| 487 | + httpobj.onreadystatechange = callback; | |
| 488 | + | |
| 489 | + // send content | |
| 490 | + if (config['transfer_mode'] == 'GET') { | |
| 491 | + httpobj.send(null); | |
| 492 | + | |
| 493 | + } else { | |
| 494 | + debug('sending query: ' + querystring, 1); | |
| 495 | + httpobj.send(querystring); | |
| 496 | + } | |
| 497 | + | |
| 498 | + if (config['async'] == true) { | |
| 499 | + // manual callback handling for synchronized calls | |
| 500 | + callback(); | |
| 501 | + } | |
| 502 | + } | |
| 503 | + | |
| 504 | + /** | |
| 505 | + * internal method for calls to remote servers through the proxy script. | |
| 506 | + * | |
| 507 | + * @access public | |
| 508 | + * @param array call_arguments array of arguments passed to cpaint.call() | |
| 509 | + * @return void | |
| 510 | + */ | |
| 511 | + this.call_proxy = function(call_arguments) { | |
| 512 | + var proxyscript = config['proxy_url']; | |
| 513 | + var url = call_arguments[0]; | |
| 514 | + var remote_method = call_arguments[1]; | |
| 515 | + var querystring = ''; | |
| 516 | + var i = 0; | |
| 517 | + | |
| 518 | + var querystring_argument_prefix = 'cpaint_argument[]='; | |
| 519 | + | |
| 520 | + // pass parameters to remote method | |
| 521 | + if (config['use_cpaint_api'] == false) { | |
| 522 | + // when not talking to a CPAINT backend, don't prefix arguments | |
| 523 | + querystring_argument_prefix = ''; | |
| 524 | + } | |
| 525 | + | |
| 526 | + for (i = 3; i < call_arguments.length; i++) { | |
| 527 | + | |
| 528 | + if (config['use_cpaint_api'] == true) { | |
| 529 | + | |
| 530 | + if ((typeof call_arguments[i] == 'string' | |
| 531 | + && call_arguments[i] != '' | |
| 532 | + && call_arguments[i].search(/^\s+$/g) == -1) | |
| 533 | + && !isNaN(call_arguments[i]) | |
| 534 | + && isFinite(call_arguments[i])) { | |
| 535 | + // numerical value, convert it first | |
| 536 | + querystring += encodeURIComponent(querystring_argument_prefix + JSON.stringify(Number(call_arguments[i])) + '&'); | |
| 537 | + | |
| 538 | + } else { | |
| 539 | + querystring += encodeURIComponent(querystring_argument_prefix + JSON.stringify(call_arguments[i]) + '&'); | |
| 540 | + } | |
| 541 | + | |
| 542 | + } else { | |
| 543 | + // no CPAINT in the backend | |
| 544 | + querystring += encodeURIComponent(querystring_argument_prefix + call_arguments[i] + '&'); | |
| 545 | + } | |
| 546 | + } | |
| 547 | + | |
| 548 | + if (config['use_cpaint_api'] == true) { | |
| 549 | + // add remote function name to querystring | |
| 550 | + querystring += encodeURIComponent('&cpaint_function=' + remote_method); | |
| 551 | + | |
| 552 | + // add response type to querystring | |
| 553 | + querystring += encodeURIComponent('&cpaint_responsetype=' + config['response_type']); | |
| 554 | + } | |
| 555 | + | |
| 556 | + // build header | |
| 557 | + if (config['transfer_mode'] == 'GET') { | |
| 558 | + proxyscript += '?cpaint_remote_url=' + encodeURIComponent(url) | |
| 559 | + + '&cpaint_remote_query=' + querystring | |
| 560 | + + '&cpaint_remote_method=' + config['transfer_mode'] | |
| 561 | + + '&cpaint_response_type=' + config['response_type']; | |
| 562 | + | |
| 563 | + } else { | |
| 564 | + querystring = 'cpaint_remote_url=' + encodeURIComponent(url) | |
| 565 | + + '&cpaint_remote_query=' + querystring | |
| 566 | + + '&cpaint_remote_method=' + config['transfer_mode'] | |
| 567 | + + '&cpaint_response_type=' + config['response_type']; | |
| 568 | + } | |
| 569 | + | |
| 570 | + // open connection | |
| 571 | + get_connection_object(); | |
| 572 | + | |
| 573 | + // open connection to remote target | |
| 574 | + debug('opening connection to proxy "' + proxyscript + '"', 1); | |
| 575 | + httpobj.open(config['transfer_mode'], proxyscript, config['async']); | |
| 576 | + | |
| 577 | + // send "urlencoded" header if necessary (if POST) | |
| 578 | + if (config['transfer_mode'] == 'POST') { | |
| 579 | + | |
| 580 | + try { | |
| 581 | + httpobj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); | |
| 582 | + | |
| 583 | + } catch (cp_err) { | |
| 584 | + debug('POST cannot be completed due to incompatible browser. Use GET as your request method.', 0); | |
| 585 | + } | |
| 586 | + } | |
| 587 | + | |
| 588 | + httpobj.setRequestHeader('X-Powered-By', 'CPAINT v' + version); | |
| 589 | + | |
| 590 | + // callback handling for asynchronous calls | |
| 591 | + httpobj.onreadystatechange = callback; | |
| 592 | + | |
| 593 | + // send content | |
| 594 | + if (config['transfer_mode'] == 'GET') { | |
| 595 | + httpobj.send(null); | |
| 596 | + | |
| 597 | + } else { | |
| 598 | + debug('sending query: ' + querystring, 1); | |
| 599 | + httpobj.send(querystring); | |
| 600 | + } | |
| 601 | + | |
| 602 | + if (config['async'] == false) { | |
| 603 | + // manual callback handling for synchronized calls | |
| 604 | + callback(); | |
| 605 | + } | |
| 606 | + } | |
| 607 | + | |
| 608 | + this.test_ajax_capability = function() { | |
| 609 | + return get_connection_object(); | |
| 610 | + } | |
| 611 | + | |
| 612 | + /** | |
| 613 | + * creates a new connection object. | |
| 614 | + * | |
| 615 | + * @access protected | |
| 616 | + * @return boolean | |
| 617 | + */ | |
| 618 | + var get_connection_object = function() { | |
| 619 | + var return_value = false; | |
| 620 | + var new_connection = false; | |
| 621 | + | |
| 622 | + // open new connection only if necessary | |
| 623 | + if (config['persistent_connection'] == false) { | |
| 624 | + // no persistance, create a new object every time | |
| 625 | + debug('Using new connection object', 1); | |
| 626 | + new_connection = true; | |
| 627 | + | |
| 628 | + } else { | |
| 629 | + // persistent connection object, only open one if no object exists | |
| 630 | + debug('Using shared connection object.', 1); | |
| 631 | + | |
| 632 | + if (typeof httpobj != 'object') { | |
| 633 | + debug('Getting new persistent connection object.', 1); | |
| 634 | + new_connection = true; | |
| 635 | + } | |
| 636 | + } | |
| 637 | + | |
| 638 | + if (new_connection == true) { | |
| 639 | + | |
| 640 | + try { | |
| 641 | + httpobj = new XMLHttpRequest(); | |
| 642 | + } catch (e1) { | |
| 643 | + | |
| 644 | + try { | |
| 645 | + httpobj = new ActiveXObject('Msxml2.XMLHTTP'); | |
| 646 | + | |
| 647 | + } catch (e) { | |
| 648 | + | |
| 649 | + try { | |
| 650 | + httpobj = new ActiveXObject('Microsoft.XMLHTTP'); | |
| 651 | + | |
| 652 | + } catch (oc) { | |
| 653 | + httpobj = null; | |
| 654 | + } | |
| 655 | + } | |
| 656 | + } | |
| 657 | + | |
| 658 | + | |
| 659 | + if (!httpobj) { | |
| 660 | + debug('Could not create connection object', 0); | |
| 661 | + | |
| 662 | + } else { | |
| 663 | + return_value = true; | |
| 664 | + } | |
| 665 | + } | |
| 666 | + | |
| 667 | + if (httpobj.readyState != 4) { | |
| 668 | + httpobj.abort(); | |
| 669 | + } | |
| 670 | + | |
| 671 | + return return_value; | |
| 672 | + } | |
| 673 | + | |
| 674 | + /** | |
| 675 | + * internal callback function. | |
| 676 | + * | |
| 677 | + * will perform some consistency checks (response code, NULL value testing) | |
| 678 | + * and if response_type = 'OBJECT' it will automatically call | |
| 679 | + * cpaint_call.parse_ajax_xml() to have a JavaScript object structure generated. | |
| 680 | + * | |
| 681 | + * after all that is done the client side callback function will be called | |
| 682 | + * with the generated response as single value. | |
| 683 | + * | |
| 684 | + * @access protected | |
| 685 | + * @return void | |
| 686 | + */ | |
| 687 | + var callback = function() { | |
| 688 | + var response = null; | |
| 689 | + | |
| 690 | + if (httpobj.readyState == 4 | |
| 691 | + && httpobj.status == 200) { | |
| 692 | + | |
| 693 | + debug(httpobj.responseText, 1); | |
| 694 | + debug('using response type ' + config['response_type'], 2); | |
| 695 | + | |
| 696 | + // fetch correct response | |
| 697 | + switch (config['response_type']) { | |
| 698 | + case 'XML': | |
| 699 | + debug(httpobj.responseXML, 2); | |
| 700 | + response = __cpaint_transformer.xml_conversion(httpobj.responseXML); | |
| 701 | + break; | |
| 702 | + | |
| 703 | + case 'OBJECT': | |
| 704 | + response = __cpaint_transformer.object_conversion(httpobj.responseXML); | |
| 705 | + break; | |
| 706 | + | |
| 707 | + case 'TEXT': | |
| 708 | + response = __cpaint_transformer.text_conversion(httpobj.responseText); | |
| 709 | + break; | |
| 710 | + | |
| 711 | + case 'E4X': | |
| 712 | + response = __cpaint_transformer.e4x_conversion(httpobj.responseText); | |
| 713 | + break; | |
| 714 | + | |
| 715 | + case 'JSON': | |
| 716 | + response = __cpaint_transformer.json_conversion(httpobj.responseText); | |
| 717 | + break; | |
| 718 | + | |
| 719 | + default: | |
| 720 | + debug('invalid response type \'' + response_type + '\'', 0); | |
| 721 | + } | |
| 722 | + | |
| 723 | + // call client side callback | |
| 724 | + if (response != null | |
| 725 | + && typeof client_callback == 'function') { | |
| 726 | + client_callback(response, httpobj.responseText); | |
| 727 | + } | |
| 728 | + // remove ourselves from the stack | |
| 729 | + remove_from_stack(); | |
| 730 | + | |
| 731 | + } else if (httpobj.readyState == 4 | |
| 732 | + && httpobj.status != 200) { | |
| 733 | + // HTTP error of some kind | |
| 734 | + debug('invalid HTTP response code \'' + Number(httpobj.status) + '\'', 0); | |
| 735 | + //client_callback("", "erro"); | |
| 736 | + } | |
| 737 | + } | |
| 738 | + | |
| 739 | + /** | |
| 740 | + * removes an entry from the stack | |
| 741 | + * | |
| 742 | + * @access protected | |
| 743 | + * @return void | |
| 744 | + */ | |
| 745 | + var remove_from_stack = function() { | |
| 746 | + // remove only if everything is okay and we're not configured as persistent connection | |
| 747 | + if (typeof stack_id == 'number' | |
| 748 | + && __cpaint_stack[stack_id] | |
| 749 | + && config['persistent_connection'] == false) { | |
| 750 | + | |
| 751 | + __cpaint_stack[stack_id] = null; | |
| 752 | + } | |
| 753 | + } | |
| 754 | + | |
| 755 | + /** | |
| 756 | + * debug method | |
| 757 | + * | |
| 758 | + * @access protected | |
| 759 | + * @param string message the message to debug | |
| 760 | + * @param integer debug_level debug level at which the message appears | |
| 761 | + * @return void | |
| 762 | + */ | |
| 763 | + var debug = function(message, debug_level) { | |
| 764 | + var prefix = '[CPAINT Debug] '; | |
| 765 | + | |
| 766 | + if (config['debugging'] < 1) { | |
| 767 | + prefix = '[CPAINT Error] '; | |
| 768 | + } | |
| 769 | + | |
| 770 | + if (config['debugging'] >= debug_level) { | |
| 771 | + alert(prefix + message); | |
| 772 | + } | |
| 773 | + if (message.search("error") > 1){client_callback("", message);} | |
| 774 | + } | |
| 775 | +} | |
| 776 | + | |
| 777 | +/** | |
| 778 | +* CPAINT transformation object | |
| 779 | +* | |
| 780 | +* @package CPAINT | |
| 781 | +* @access public | |
| 782 | +* @copyright Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint | |
| 783 | +* @author Paul Sullivan <wiley14@gmail.com> | |
| 784 | +* @author Dominique Stender <dstender@st-webdevelopment.de> | |
| 785 | +*/ | |
| 786 | +function cpaint_transformer() { | |
| 787 | + | |
| 788 | + /** | |
| 789 | + * will take a XMLHttpObject and generate a JavaScript | |
| 790 | + * object structure from it. | |
| 791 | + * | |
| 792 | + * is internally called by cpaint_call.callback() if necessary. | |
| 793 | + * will call cpaint_call.create_object_structure() to create nested object structures. | |
| 794 | + * | |
| 795 | + * @access public | |
| 796 | + * @param object xml_document a XMLHttpObject | |
| 797 | + * @return object | |
| 798 | + */ | |
| 799 | + this.object_conversion = function(xml_document) { | |
| 800 | + var return_value = new cpaint_result_object(); | |
| 801 | + var i = 0; | |
| 802 | + var firstNodeName = ''; | |
| 803 | + | |
| 804 | + if (typeof xml_document == 'object' | |
| 805 | + && xml_document != null) { | |
| 806 | + | |
| 807 | + // find the first element node - for MSIE the <?xml?> node is the very first... | |
| 808 | + for (i = 0; i < xml_document.childNodes.length; i++) { | |
| 809 | + | |
| 810 | + if (xml_document.childNodes[i].nodeType == 1) { | |
| 811 | + firstNodeName = xml_document.childNodes[i].nodeName; | |
| 812 | + break; | |
| 813 | + } | |
| 814 | + } | |
| 815 | + | |
| 816 | + var ajax_response = xml_document.getElementsByTagName(firstNodeName); | |
| 817 | + | |
| 818 | + return_value[firstNodeName] = new Array(); | |
| 819 | + | |
| 820 | + for (i = 0; i < ajax_response.length; i++) { | |
| 821 | + var tmp_node = create_object_structure(ajax_response[i]); | |
| 822 | + tmp_node.id = ajax_response[i].getAttribute('id') | |
| 823 | + return_value[firstNodeName].push(tmp_node); | |
| 824 | + } | |
| 825 | + | |
| 826 | + } else { | |
| 827 | + debug('received invalid XML response', 0); | |
| 828 | + } | |
| 829 | + | |
| 830 | + return return_value; | |
| 831 | + } | |
| 832 | + | |
| 833 | + /** | |
| 834 | + * performs the necessary conversions for the XML response type | |
| 835 | + * | |
| 836 | + * @access public | |
| 837 | + * @param object xml_document a XMLHttpObject | |
| 838 | + * @return object | |
| 839 | + */ | |
| 840 | + this.xml_conversion = function(xml_document) { | |
| 841 | + return xml_document; | |
| 842 | + } | |
| 843 | + | |
| 844 | + /** | |
| 845 | + * performs the necessary conversions for the TEXT response type | |
| 846 | + * | |
| 847 | + * @access public | |
| 848 | + * @param string text the response text | |
| 849 | + * @return string | |
| 850 | + */ | |
| 851 | + this.text_conversion = function(text) { | |
| 852 | + return decode(text); | |
| 853 | + } | |
| 854 | + | |
| 855 | + /** | |
| 856 | + * performs the necessary conversions for the E4X response type | |
| 857 | + * | |
| 858 | + * @access public | |
| 859 | + * @param string text the response text | |
| 860 | + * @return string | |
| 861 | + */ | |
| 862 | + this.e4x_conversion = function(text) { | |
| 863 | + // remove <?xml ?>tag | |
| 864 | + text = text.replace(/^\<\?xml[^>]+\>/, ''); | |
| 865 | + return new XML(text); | |
| 866 | + } | |
| 867 | + | |
| 868 | + /** | |
| 869 | + * performs the necessary conversions for the JSON response type | |
| 870 | + * | |
| 871 | + * @access public | |
| 872 | + * @param string text the response text | |
| 873 | + * @return string | |
| 874 | + */ | |
| 875 | + this.json_conversion = function(text) { | |
| 876 | + return JSON.parse(text); | |
| 877 | + } | |
| 878 | + | |
| 879 | + /** | |
| 880 | + * this method takes a HTML / XML node object and creates a | |
| 881 | + * JavaScript object structure from it. | |
| 882 | + * | |
| 883 | + * @access public | |
| 884 | + * @param object stream a node in the XML structure | |
| 885 | + * @return object | |
| 886 | + */ | |
| 887 | + var create_object_structure = function(stream) { | |
| 888 | + var return_value = new cpaint_result_object(); | |
| 889 | + var node_name = ''; | |
| 890 | + var i = 0; | |
| 891 | + var attrib = 0; | |
| 892 | + | |
| 893 | + if (stream.hasChildNodes() == true) { | |
| 894 | + for (i = 0; i < stream.childNodes.length; i++) { | |
| 895 | + | |
| 896 | + node_name = stream.childNodes[i].nodeName; | |
| 897 | + node_name = node_name.replace(/[^a-zA-Z0-9_]*/g, ''); | |
| 898 | + | |
| 899 | + // reset / create subnode | |
| 900 | + if (typeof return_value[node_name] != 'object') { | |
| 901 | + return_value[node_name] = new Array(); | |
| 902 | + } | |
| 903 | + | |
| 904 | + if (stream.childNodes[i].nodeType == 1) { | |
| 905 | + var tmp_node = create_object_structure(stream.childNodes[i]); | |
| 906 | + | |
| 907 | + for (attrib = 0; attrib < stream.childNodes[i].attributes.length; attrib++) { | |
| 908 | + tmp_node.set_attribute(stream.childNodes[i].attributes[attrib].nodeName, stream.childNodes[i].attributes[attrib].nodeValue); | |
| 909 | + } | |
| 910 | + | |
| 911 | + return_value[node_name].push(tmp_node); | |
| 912 | + | |
| 913 | + } else if (stream.childNodes[i].nodeType == 3) { | |
| 914 | + return_value.data = decode(String(stream.firstChild.data)); | |
| 915 | + } | |
| 916 | + } | |
| 917 | + } | |
| 918 | + | |
| 919 | + return return_value; | |
| 920 | + } | |
| 921 | + | |
| 922 | + /** | |
| 923 | + * converts an encoded text back to viewable characters. | |
| 924 | + * | |
| 925 | + * @access public | |
| 926 | + * @param string rawtext raw text as provided by the backend | |
| 927 | + * @return mixed | |
| 928 | + */ | |
| 929 | + var decode = function(rawtext) { | |
| 930 | + var plaintext = ''; | |
| 931 | + var i = 0; | |
| 932 | + var c1 = 0; | |
| 933 | + var c2 = 0; | |
| 934 | + var c3 = 0; | |
| 935 | + var u = 0; | |
| 936 | + var t = 0; | |
| 937 | + | |
| 938 | + // remove special JavaScript encoded non-printable characters | |
| 939 | + while (i < rawtext.length) { | |
| 940 | + if (rawtext.charAt(i) == '\\' | |
| 941 | + && rawtext.charAt(i + 1) == 'u') { | |
| 942 | + | |
| 943 | + u = 0; | |
| 944 | + | |
| 945 | + for (j = 2; j < 6; j += 1) { | |
| 946 | + t = parseInt(rawtext.charAt(i + j), 16); | |
| 947 | + | |
| 948 | + if (!isFinite(t)) { | |
| 949 | + break; | |
| 950 | + } | |
| 951 | + u = u * 16 + t; | |
| 952 | + } | |
| 953 | + | |
| 954 | + plaintext += String.fromCharCode(u); | |
| 955 | + i += 6; | |
| 956 | + | |
| 957 | + } else { | |
| 958 | + plaintext += rawtext.charAt(i); | |
| 959 | + i++; | |
| 960 | + } | |
| 961 | + } | |
| 962 | + | |
| 963 | + // convert numeric data to number type | |
| 964 | + if (plaintext != '' | |
| 965 | + && plaintext.search(/^\s+$/g) == -1 | |
| 966 | + && !isNaN(plaintext) | |
| 967 | + && isFinite(plaintext)) { | |
| 968 | + | |
| 969 | + plaintext = Number(plaintext); | |
| 970 | + } | |
| 971 | + | |
| 972 | + return plaintext; | |
| 973 | + } | |
| 974 | +} | |
| 975 | + | |
| 976 | +/** | |
| 977 | +* this is the basic prototype for a cpaint node object | |
| 978 | +* as used in cpaint_call.parse_ajax_xml() | |
| 979 | +* | |
| 980 | +* @package CPAINT | |
| 981 | +* @access public | |
| 982 | +* @copyright Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint | |
| 983 | +* @author Paul Sullivan <wiley14@gmail.com> | |
| 984 | +* @author Dominique Stender <dstender@st-webdevelopment.de> | |
| 985 | +*/ | |
| 986 | +function cpaint_result_object() { | |
| 987 | + this.id = 0; | |
| 988 | + this.data = ''; | |
| 989 | + var __attributes = new Array(); | |
| 990 | + | |
| 991 | + /** | |
| 992 | + * Returns a subnode with the given type and id. | |
| 993 | + * | |
| 994 | + * @access public | |
| 995 | + * @param string type The type of the subnode. Equivalent to the XML tag name. | |
| 996 | + * @param string id The id of the subnode. Equivalent to the XML tag names id attribute. | |
| 997 | + * @return object | |
| 998 | + */ | |
| 999 | + this.find_item_by_id = function() { | |
| 1000 | + var return_value = null; | |
| 1001 | + var type = arguments[0]; | |
| 1002 | + var id = arguments[1]; | |
| 1003 | + var i = 0; | |
| 1004 | + | |
| 1005 | + if (this[type]) { | |
| 1006 | + | |
| 1007 | + for (i = 0; i < this[type].length; i++) { | |
| 1008 | + | |
| 1009 | + if (this[type][i].get_attribute('id') == id) { | |
| 1010 | + return_value = this[type][i]; | |
| 1011 | + break; | |
| 1012 | + } | |
| 1013 | + } | |
| 1014 | + } | |
| 1015 | + | |
| 1016 | + return return_value; | |
| 1017 | + } | |
| 1018 | + | |
| 1019 | + /** | |
| 1020 | + * retrieves the value of an attribute. | |
| 1021 | + * | |
| 1022 | + * @access public | |
| 1023 | + * @param string name name of the attribute | |
| 1024 | + * @return mixed | |
| 1025 | + */ | |
| 1026 | + this.get_attribute = function() { | |
| 1027 | + var return_value = null; | |
| 1028 | + var id = arguments[0]; | |
| 1029 | + | |
| 1030 | + if (typeof __attributes[id] != 'undefined') { | |
| 1031 | + return_value = __attributes[id]; | |
| 1032 | + } | |
| 1033 | + | |
| 1034 | + return return_value; | |
| 1035 | + } | |
| 1036 | + | |
| 1037 | + /** | |
| 1038 | + * assigns a value to an attribute. | |
| 1039 | + * | |
| 1040 | + * if that attribute does not exist it will be created. | |
| 1041 | + * | |
| 1042 | + * @access public | |
| 1043 | + * @param string name name of the attribute | |
| 1044 | + * @param string value value of the attribute | |
| 1045 | + * @return void | |
| 1046 | + */ | |
| 1047 | + this.set_attribute = function() { | |
| 1048 | + __attributes[arguments[0]] = arguments[1]; | |
| 1049 | + } | |
| 1050 | +} | |
| 1051 | + | |
| 1052 | + | |
| 1053 | +/* | |
| 1054 | +Copyright (c) 2005 JSON.org | |
| 1055 | + | |
| 1056 | +Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 1057 | +of this software and associated documentation files (the "Software"), to deal | |
| 1058 | +in the Software without restriction, including without limitation the rights | |
| 1059 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 1060 | +copies of the Software, and to permit persons to whom the Software is | |
| 1061 | +furnished to do so, subject to the following conditions: | |
| 1062 | + | |
| 1063 | +The Software shall be used for Good, not Evil. | |
| 1064 | + | |
| 1065 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 1066 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 1067 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 1068 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 1069 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 1070 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 1071 | +SOFTWARE. | |
| 1072 | +*/ | |
| 1073 | + | |
| 1074 | +Array.prototype.______array = '______array'; | |
| 1075 | + | |
| 1076 | +var JSON = { | |
| 1077 | + org: 'http://www.JSON.org', | |
| 1078 | + copyright: '(c)2005 JSON.org', | |
| 1079 | + license: 'http://www.crockford.com/JSON/license.html', | |
| 1080 | + | |
| 1081 | + stringify: function (arg) { | |
| 1082 | + var c, i, l, s = '', v; | |
| 1083 | + var numeric = true; | |
| 1084 | + | |
| 1085 | + switch (typeof arg) { | |
| 1086 | + case 'object': | |
| 1087 | + if (arg) { | |
| 1088 | + if (arg.______array == '______array') { | |
| 1089 | + // do a test whether all array keys are numeric | |
| 1090 | + for (i in arg) { | |
| 1091 | + if (i != '______array' | |
| 1092 | + && (isNaN(i) | |
| 1093 | + || !isFinite(i))) { | |
| 1094 | + numeric = false; | |
| 1095 | + break; | |
| 1096 | + } | |
| 1097 | + } | |
| 1098 | + | |
| 1099 | + if (numeric == true) { | |
| 1100 | + for (i = 0; i < arg.length; ++i) { | |
| 1101 | + if (typeof arg[i] != 'undefined') { | |
| 1102 | + v = this.stringify(arg[i]); | |
| 1103 | + if (s) { | |
| 1104 | + s += ','; | |
| 1105 | + } | |
| 1106 | + s += v; | |
| 1107 | + } else { | |
| 1108 | + s += ',null'; | |
| 1109 | + } | |
| 1110 | + } | |
| 1111 | + return '[' + s + ']'; | |
| 1112 | + } else { | |
| 1113 | + for (i in arg) { | |
| 1114 | + if (i != '______array') { | |
| 1115 | + v = arg[i]; | |
| 1116 | + if (typeof v != 'undefined' && typeof v != 'function') { | |
| 1117 | + v = this.stringify(v); | |
| 1118 | + if (s) { | |
| 1119 | + s += ','; | |
| 1120 | + } | |
| 1121 | + s += this.stringify(i) + ':' + v; | |
| 1122 | + } | |
| 1123 | + } | |
| 1124 | + } | |
| 1125 | + // return as object | |
| 1126 | + return '{' + s + '}'; | |
| 1127 | + } | |
| 1128 | + } else if (typeof arg.toString != 'undefined') { | |
| 1129 | + for (i in arg) { | |
| 1130 | + v = arg[i]; | |
| 1131 | + if (typeof v != 'undefined' && typeof v != 'function') { | |
| 1132 | + v = this.stringify(v); | |
| 1133 | + if (s) { | |
| 1134 | + s += ','; | |
| 1135 | + } | |
| 1136 | + s += this.stringify(i) + ':' + v; | |
| 1137 | + } | |
| 1138 | + } | |
| 1139 | + return '{' + s + '}'; | |
| 1140 | + } | |
| 1141 | + } | |
| 1142 | + return 'null'; | |
| 1143 | + case 'number': | |
| 1144 | + return isFinite(arg) ? String(arg) : 'null'; | |
| 1145 | + case 'string': | |
| 1146 | + l = arg.length; | |
| 1147 | + s = '"'; | |
| 1148 | + for (i = 0; i < l; i += 1) { | |
| 1149 | + c = arg.charAt(i); | |
| 1150 | + if (c >= ' ') { | |
| 1151 | + if (c == '\\' || c == '"') { | |
| 1152 | + s += '\\'; | |
| 1153 | + } | |
| 1154 | + s += c; | |
| 1155 | + } else { | |
| 1156 | + switch (c) { | |
| 1157 | + case '\b': | |
| 1158 | + s += '\\b'; | |
| 1159 | + break; | |
| 1160 | + case '\f': | |
| 1161 | + s += '\\f'; | |
| 1162 | + break; | |
| 1163 | + case '\n': | |
| 1164 | + s += '\\n'; | |
| 1165 | + break; | |
| 1166 | + case '\r': | |
| 1167 | + s += '\\r'; | |
| 1168 | + break; | |
| 1169 | + case '\t': | |
| 1170 | + s += '\\t'; | |
| 1171 | + break; | |
| 1172 | + default: | |
| 1173 | + c = c.charCodeAt(); | |
| 1174 | + s += '\\u00' + Math.floor(c / 16).toString(16) + | |
| 1175 | + (c % 16).toString(16); | |
| 1176 | + } | |
| 1177 | + } | |
| 1178 | + } | |
| 1179 | + return s + '"'; | |
| 1180 | + case 'boolean': | |
| 1181 | + return String(arg); | |
| 1182 | + default: | |
| 1183 | + return 'null'; | |
| 1184 | + } | |
| 1185 | + }, | |
| 1186 | + parse: function (text) { | |
| 1187 | + var at = 0; | |
| 1188 | + var ch = ' '; | |
| 1189 | + | |
| 1190 | + function error(m) { | |
| 1191 | + throw { | |
| 1192 | + name: 'JSONError', | |
| 1193 | + message: m, | |
| 1194 | + at: at - 1, | |
| 1195 | + text: text | |
| 1196 | + }; | |
| 1197 | + } | |
| 1198 | + | |
| 1199 | + function next() { | |
| 1200 | + ch = text.charAt(at); | |
| 1201 | + at += 1; | |
| 1202 | + return ch; | |
| 1203 | + } | |
| 1204 | + | |
| 1205 | + function white() { | |
| 1206 | + while (ch != '' && ch <= ' ') { | |
| 1207 | + next(); | |
| 1208 | + } | |
| 1209 | + } | |
| 1210 | + | |
| 1211 | + function str() { | |
| 1212 | + var i, s = '', t, u; | |
| 1213 | + | |
| 1214 | + if (ch == '"') { | |
| 1215 | +outer: while (next()) { | |
| 1216 | + if (ch == '"') { | |
| 1217 | + next(); | |
| 1218 | + return s; | |
| 1219 | + } else if (ch == '\\') { | |
| 1220 | + switch (next()) { | |
| 1221 | + case 'b': | |
| 1222 | + s += '\b'; | |
| 1223 | + break; | |
| 1224 | + case 'f': | |
| 1225 | + s += '\f'; | |
| 1226 | + break; | |
| 1227 | + case 'n': | |
| 1228 | + s += '\n'; | |
| 1229 | + break; | |
| 1230 | + case 'r': | |
| 1231 | + s += '\r'; | |
| 1232 | + break; | |
| 1233 | + case 't': | |
| 1234 | + s += '\t'; | |
| 1235 | + break; | |
| 1236 | + case 'u': | |
| 1237 | + u = 0; | |
| 1238 | + for (i = 0; i < 4; i += 1) { | |
| 1239 | + t = parseInt(next(), 16); | |
| 1240 | + if (!isFinite(t)) { | |
| 1241 | + break outer; | |
| 1242 | + } | |
| 1243 | + u = u * 16 + t; | |
| 1244 | + } | |
| 1245 | + s += String.fromCharCode(u); | |
| 1246 | + break; | |
| 1247 | + default: | |
| 1248 | + s += ch; | |
| 1249 | + } | |
| 1250 | + } else { | |
| 1251 | + s += ch; | |
| 1252 | + } | |
| 1253 | + } | |
| 1254 | + } | |
| 1255 | + error("Bad string"); | |
| 1256 | + } | |
| 1257 | + | |
| 1258 | + function arr() { | |
| 1259 | + var a = []; | |
| 1260 | + | |
| 1261 | + if (ch == '[') { | |
| 1262 | + next(); | |
| 1263 | + white(); | |
| 1264 | + if (ch == ']') { | |
| 1265 | + next(); | |
| 1266 | + return a; | |
| 1267 | + } | |
| 1268 | + while (ch) { | |
| 1269 | + a.push(val()); | |
| 1270 | + white(); | |
| 1271 | + if (ch == ']') { | |
| 1272 | + next(); | |
| 1273 | + return a; | |
| 1274 | + } else if (ch != ',') { | |
| 1275 | + break; | |
| 1276 | + } | |
| 1277 | + next(); | |
| 1278 | + white(); | |
| 1279 | + } | |
| 1280 | + } | |
| 1281 | + error("Bad array"); | |
| 1282 | + } | |
| 1283 | + | |
| 1284 | + function obj() { | |
| 1285 | + var k, o = {}; | |
| 1286 | + | |
| 1287 | + if (ch == '{') { | |
| 1288 | + next(); | |
| 1289 | + white(); | |
| 1290 | + if (ch == '}') { | |
| 1291 | + next(); | |
| 1292 | + return o; | |
| 1293 | + } | |
| 1294 | + while (ch) { | |
| 1295 | + k = str(); | |
| 1296 | + white(); | |
| 1297 | + if (ch != ':') { | |
| 1298 | + break; | |
| 1299 | + } | |
| 1300 | + next(); | |
| 1301 | + o[k] = val(); | |
| 1302 | + white(); | |
| 1303 | + if (ch == '}') { | |
| 1304 | + next(); | |
| 1305 | + return o; | |
| 1306 | + } else if (ch != ',') { | |
| 1307 | + break; | |
| 1308 | + } | |
| 1309 | + next(); | |
| 1310 | + white(); | |
| 1311 | + } | |
| 1312 | + } | |
| 1313 | + error("Bad object"); | |
| 1314 | + } | |
| 1315 | + | |
| 1316 | + function assoc() { | |
| 1317 | + var k, a = []; | |
| 1318 | + | |
| 1319 | + if (ch == '<') { | |
| 1320 | + next(); | |
| 1321 | + white(); | |
| 1322 | + if (ch == '>') { | |
| 1323 | + next(); | |
| 1324 | + return a; | |
| 1325 | + } | |
| 1326 | + while (ch) { | |
| 1327 | + k = str(); | |
| 1328 | + white(); | |
| 1329 | + if (ch != ':') { | |
| 1330 | + break; | |
| 1331 | + } | |
| 1332 | + next(); | |
| 1333 | + a[k] = val(); | |
| 1334 | + white(); | |
| 1335 | + if (ch == '>') { | |
| 1336 | + next(); | |
| 1337 | + return a; | |
| 1338 | + } else if (ch != ',') { | |
| 1339 | + break; | |
| 1340 | + } | |
| 1341 | + next(); | |
| 1342 | + white(); | |
| 1343 | + } | |
| 1344 | + } | |
| 1345 | + error("Bad associative array"); | |
| 1346 | + } | |
| 1347 | + | |
| 1348 | + function num() { | |
| 1349 | + var n = '', v; | |
| 1350 | + if (ch == '-') { | |
| 1351 | + n = '-'; | |
| 1352 | + next(); | |
| 1353 | + } | |
| 1354 | + while (ch >= '0' && ch <= '9') { | |
| 1355 | + n += ch; | |
| 1356 | + next(); | |
| 1357 | + } | |
| 1358 | + if (ch == '.') { | |
| 1359 | + n += '.'; | |
| 1360 | + while (next() && ch >= '0' && ch <= '9') { | |
| 1361 | + n += ch; | |
| 1362 | + } | |
| 1363 | + } | |
| 1364 | + if (ch == 'e' || ch == 'E') { | |
| 1365 | + n += 'e'; | |
| 1366 | + next(); | |
| 1367 | + if (ch == '-' || ch == '+') { | |
| 1368 | + n += ch; | |
| 1369 | + next(); | |
| 1370 | + } | |
| 1371 | + while (ch >= '0' && ch <= '9') { | |
| 1372 | + n += ch; | |
| 1373 | + next(); | |
| 1374 | + } | |
| 1375 | + } | |
| 1376 | + v = +n; | |
| 1377 | + if (!isFinite(v)) { | |
| 1378 | + error("Bad number"); | |
| 1379 | + } else { | |
| 1380 | + return v; | |
| 1381 | + } | |
| 1382 | + } | |
| 1383 | + | |
| 1384 | + function word() { | |
| 1385 | + switch (ch) { | |
| 1386 | + case 't': | |
| 1387 | + if (next() == 'r' && next() == 'u' && next() == 'e') { | |
| 1388 | + next(); | |
| 1389 | + return true; | |
| 1390 | + } | |
| 1391 | + break; | |
| 1392 | + case 'f': | |
| 1393 | + if (next() == 'a' && next() == 'l' && next() == 's' && | |
| 1394 | + next() == 'e') { | |
| 1395 | + next(); | |
| 1396 | + return false; | |
| 1397 | + } | |
| 1398 | + break; | |
| 1399 | + case 'n': | |
| 1400 | + if (next() == 'u' && next() == 'l' && next() == 'l') { | |
| 1401 | + next(); | |
| 1402 | + return null; | |
| 1403 | + } | |
| 1404 | + break; | |
| 1405 | + } | |
| 1406 | + error("Syntax error"); | |
| 1407 | + } | |
| 1408 | + | |
| 1409 | + function val() { | |
| 1410 | + white(); | |
| 1411 | + switch (ch) { | |
| 1412 | + case '{': | |
| 1413 | + return obj(); | |
| 1414 | + case '[': | |
| 1415 | + return arr(); | |
| 1416 | + case '<': | |
| 1417 | + return assoc(); | |
| 1418 | + case '"': | |
| 1419 | + return str(); | |
| 1420 | + case '-': | |
| 1421 | + return num(); | |
| 1422 | + default: | |
| 1423 | + return ch >= '0' && ch <= '9' ? num() : word(); | |
| 1424 | + } | |
| 1425 | + } | |
| 1426 | + | |
| 1427 | + return val(); | |
| 1428 | + } | |
| 1429 | +}; | ... | ... |
| ... | ... | @@ -0,0 +1,941 @@ |
| 1 | +<?php | |
| 2 | +/** | |
| 3 | +* CPAINT - Cross-Platform Asynchronous INterface Toolkit | |
| 4 | +* | |
| 5 | +* http://sf.net/projects/cpaint | |
| 6 | +* | |
| 7 | +* released under the terms of the LGPL | |
| 8 | +* see http://www.fsf.org/licensing/licenses/lgpl.txt for details | |
| 9 | +* | |
| 10 | +* @package CPAINT | |
| 11 | +* @author Paul Sullivan <wiley14@gmail.com> | |
| 12 | +* @author Dominique Stender <dstender@st-webdevelopment.de> | |
| 13 | +* @copyright Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint | |
| 14 | +* @version 2.0.3 | |
| 15 | +*/ | |
| 16 | + | |
| 17 | +//---- includes ---------------------------------------------------------------- | |
| 18 | + /** | |
| 19 | + * @include JSON | |
| 20 | + */ | |
| 21 | + require_once(dirname(__FILE__) . '/json.php'); | |
| 22 | + | |
| 23 | + /** | |
| 24 | + * @include config | |
| 25 | + */ | |
| 26 | + require_once("cpaint2.config.php"); | |
| 27 | + | |
| 28 | +//---- variables --------------------------------------------------------------- | |
| 29 | + $GLOBALS['__cpaint_json'] = new JSON(); | |
| 30 | + | |
| 31 | +//---- error reporting --------------------------------------------------------- | |
| 32 | + error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING); | |
| 33 | + | |
| 34 | +//---- classes ----------------------------------------------------------------- | |
| 35 | + /** | |
| 36 | + * cpaint base class. | |
| 37 | + * | |
| 38 | + * @package CPAINT | |
| 39 | + * @access public | |
| 40 | + * @author Paul Sullivan <wiley14@gmail.com> | |
| 41 | + * @author Dominique Stender <dstender@st-webdevelopment.de> | |
| 42 | + * @copyright Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint | |
| 43 | + * @version 2.0.3 | |
| 44 | + */ | |
| 45 | + class cpaint { | |
| 46 | + /** | |
| 47 | + * version number | |
| 48 | + * | |
| 49 | + * @access private | |
| 50 | + * @var string $version | |
| 51 | + */ | |
| 52 | + var $version = '2.0.3'; | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * response type. | |
| 56 | + * | |
| 57 | + * @access protected | |
| 58 | + * @var string $response_type | |
| 59 | + */ | |
| 60 | + var $response_type; | |
| 61 | + | |
| 62 | + /** | |
| 63 | + * the basenode ajaxResponse. | |
| 64 | + * | |
| 65 | + * @access protected | |
| 66 | + * @var object $basenode | |
| 67 | + */ | |
| 68 | + var $basenode; | |
| 69 | + | |
| 70 | + /** | |
| 71 | + * list of registered methods available through the CPAINT API | |
| 72 | + * | |
| 73 | + * @access protected | |
| 74 | + * @var array $api_functions | |
| 75 | + */ | |
| 76 | + var $api_functions; | |
| 77 | + | |
| 78 | + /** | |
| 79 | + * list of registered complex types used in the CPAINT API | |
| 80 | + * | |
| 81 | + * @access protected | |
| 82 | + * @var array $api_datatypes | |
| 83 | + */ | |
| 84 | + var $api_datatypes; | |
| 85 | + | |
| 86 | + /** | |
| 87 | + * whether or not the CPAINT API generates a WSDL when called with ?wsdl querystring | |
| 88 | + * | |
| 89 | + * @access private | |
| 90 | + * @var boolean $use_wsdl | |
| 91 | + */ | |
| 92 | + var $use_wsdl; | |
| 93 | + | |
| 94 | + /** | |
| 95 | + * PHP4 constructor. | |
| 96 | + * | |
| 97 | + * @access public | |
| 98 | + * @return void | |
| 99 | + */ | |
| 100 | + function cpaint() { | |
| 101 | + $this->__construct(); | |
| 102 | + } | |
| 103 | + | |
| 104 | + /** | |
| 105 | + * PHP 5 constructor. | |
| 106 | + * | |
| 107 | + * @access public | |
| 108 | + * @return void | |
| 109 | + * @todo -o"Dominique Stender" -ccpaint implement a better debugging | |
| 110 | + */ | |
| 111 | + function __construct() { | |
| 112 | + // initialize properties | |
| 113 | + $this->basenode = new cpaint_node(); | |
| 114 | + $this->basenode->set_name('ajaxResponse'); | |
| 115 | + $this->basenode->set_attribute('id', ''); | |
| 116 | + $this->basenode->set_encoding('UTF-8'); | |
| 117 | + | |
| 118 | + $this->response_type = 'TEXT'; | |
| 119 | + $this->api_functions = array(); | |
| 120 | + $this->api_datatypes = array(); | |
| 121 | + $this->use_wsdl = true; | |
| 122 | + | |
| 123 | + $this->complex_type(array( | |
| 124 | + 'name' => 'cpaintResponseType', | |
| 125 | + 'type' => 'restriction', // (restriction|complex|list) | |
| 126 | + 'base_type' => 'string', // scalar type of all values, e.g. 'string', for type = (restriction|list) only | |
| 127 | + 'values' => array( // for type = 'restriction' only: list of allowed values | |
| 128 | + 'XML', 'TEXT', 'OBJECT', 'E4X', 'JSON', | |
| 129 | + ), | |
| 130 | + ) | |
| 131 | + ); | |
| 132 | + $this->complex_type(array( | |
| 133 | + 'name' => 'cpaintDebugLevel', | |
| 134 | + 'type' => 'restriction', | |
| 135 | + 'base_type' => 'long', | |
| 136 | + 'values' => array( | |
| 137 | + -1, 0, 1, 2 | |
| 138 | + ), | |
| 139 | + ) | |
| 140 | + ); | |
| 141 | + $this->complex_type(array( | |
| 142 | + 'name' => 'cpaintDebugMessage', | |
| 143 | + 'type' => 'list', | |
| 144 | + 'base_type' => 'string', | |
| 145 | + ) | |
| 146 | + ); | |
| 147 | + | |
| 148 | + $this->complex_type(array( | |
| 149 | + 'name' => 'cpaintRequestHead', | |
| 150 | + 'type' => 'complex', | |
| 151 | + 'struct' => array( | |
| 152 | + 0 => array('name' => 'functionName', 'type' => 'string'), | |
| 153 | + 1 => array('name' => 'responseType', 'type' => 'cpaintResponseType'), | |
| 154 | + 2 => array('name' => 'debugLevel', 'type' => 'cpaintDebugLevel'), | |
| 155 | + ), | |
| 156 | + ) | |
| 157 | + ); | |
| 158 | + | |
| 159 | + $this->complex_type(array( | |
| 160 | + 'name' => 'cpaintResponseHead', | |
| 161 | + 'type' => 'complex', | |
| 162 | + 'struct' => array( | |
| 163 | + 0 => array('name' => 'success', 'type' => 'boolean'), | |
| 164 | + 1 => array('name' => 'debugger', 'type' => 'cpaintDebugMessage'), | |
| 165 | + ), | |
| 166 | + ) | |
| 167 | + ); | |
| 168 | + | |
| 169 | + // determine response type | |
| 170 | + if (isset($_REQUEST['cpaint_response_type'])) { | |
| 171 | + $this->response_type = htmlentities(strip_tags(strtoupper((string) $_REQUEST['cpaint_response_type']))); | |
| 172 | + } // end: if | |
| 173 | + } | |
| 174 | + | |
| 175 | + /** | |
| 176 | + * calls the user function responsible for this specific call. | |
| 177 | + * | |
| 178 | + * @access public | |
| 179 | + * @param string $input_encoding input data character encoding, default is UTF-8 | |
| 180 | + * @return void | |
| 181 | + */ | |
| 182 | + function start($input_encoding = 'UTF-8') { | |
| 183 | + $user_function = ''; | |
| 184 | + $arguments = array(); | |
| 185 | + | |
| 186 | + // work only if there is no API version request | |
| 187 | + if (!isset($_REQUEST['api_query']) | |
| 188 | + && !isset($_REQUEST['wsdl'])) { | |
| 189 | + $this->basenode->set_encoding($input_encoding); | |
| 190 | + | |
| 191 | + if ($_REQUEST['cpaint_function'] != '') { | |
| 192 | + $user_function = $_REQUEST['cpaint_function']; | |
| 193 | + if(@$_REQUEST['cpaint_argument']) | |
| 194 | + $arguments = $_REQUEST['cpaint_argument']; | |
| 195 | + else | |
| 196 | + $arguments = array(); | |
| 197 | + } | |
| 198 | + | |
| 199 | + // perform character conversion on every argument | |
| 200 | + foreach ($arguments as $key => $value) { | |
| 201 | + | |
| 202 | + if (get_magic_quotes_gpc() == true) { | |
| 203 | + $value = stripslashes($value); | |
| 204 | + } // end: if | |
| 205 | + | |
| 206 | + // convert from JSON string | |
| 207 | + $arguments[$key] = $GLOBALS['__cpaint_json']->parse($value); | |
| 208 | + } // end: foreach | |
| 209 | + | |
| 210 | + $arguments = cpaint_transformer::decode_array($arguments, $this->basenode->get_encoding()); | |
| 211 | + | |
| 212 | + if (is_array($this->api_functions[$user_function]) | |
| 213 | + && is_callable($this->api_functions[$user_function]['call'])) { | |
| 214 | + // a valid API function is to be called | |
| 215 | + call_user_func_array($this->api_functions[$user_function]['call'], $arguments); | |
| 216 | + } else if ($user_function != '') { | |
| 217 | + // desired function is not registered as API function | |
| 218 | + $this->basenode->set_data('[CPAINT] A function name was passed that is not allowed to execute on this server.'); | |
| 219 | + } | |
| 220 | + } // end: if | |
| 221 | + } | |
| 222 | + | |
| 223 | + /** | |
| 224 | + * generates and prints the response based on response type supplied by the frontend. | |
| 225 | + * | |
| 226 | + * @access public | |
| 227 | + * @return void | |
| 228 | + */ | |
| 229 | + function return_data() { | |
| 230 | + // send appropriate headers to avoid caching | |
| 231 | + header('Expires: Fri, 14 Mar 1980 20:53:00 GMT'); | |
| 232 | + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); | |
| 233 | + header('Cache-Control: no-cache, must-revalidate'); | |
| 234 | + header('Pragma: no-cache'); | |
| 235 | + header('X-Powered-By: CPAINT v' . $this->version . '/PHP v' . phpversion()); | |
| 236 | + | |
| 237 | + // work only if there is no API version request | |
| 238 | + | |
| 239 | + if (!isset($_REQUEST['api_query']) | |
| 240 | + && !isset($_REQUEST['wsdl'])) { | |
| 241 | + // trigger generation of response | |
| 242 | + switch (trim($this->response_type)) { | |
| 243 | + | |
| 244 | + case 'TEXT': | |
| 245 | + header('Content-type: text/plain; charset=' . cpaint_transformer::find_output_charset($this->basenode->get_encoding())); | |
| 246 | + echo cpaint_transformer::toString($this->basenode); | |
| 247 | + break; | |
| 248 | + | |
| 249 | + case 'JSON': | |
| 250 | + header('Content-type: text/plain; charset=' . cpaint_transformer::find_output_charset($this->basenode->get_encoding())); | |
| 251 | + echo cpaint_transformer::toJSON($this->basenode); | |
| 252 | + break; | |
| 253 | + | |
| 254 | + case 'OBJECT': | |
| 255 | + case 'E4X': | |
| 256 | + case 'XML': | |
| 257 | + header('Content-type: text/xml; charset=' . cpaint_transformer::find_output_charset($this->basenode->get_encoding())); | |
| 258 | + echo '<?xml version="1.0" encoding="' . cpaint_transformer::find_output_charset($this->basenode->get_encoding()) . '"?>' | |
| 259 | + . cpaint_transformer::toXML($this->basenode); | |
| 260 | + break; | |
| 261 | + | |
| 262 | + default: | |
| 263 | + echo 'ERROR: invalid response type \'' . $this->response_type . '\''; | |
| 264 | + } // end: switch | |
| 265 | + | |
| 266 | + } elseif (isset($_REQUEST['api_query'])) { | |
| 267 | + // API version request | |
| 268 | + header('Content-type: text/plain; charset=ISO-8859-1'); | |
| 269 | + echo 'CPAINT v' . $this->version . '/PHP v' . phpversion(); | |
| 270 | + | |
| 271 | + } elseif ($this->use_wsdl == true | |
| 272 | + && isset($_REQUEST['wsdl'])) { | |
| 273 | + | |
| 274 | + if (is_file(dirname(__FILE__) . '/cpaint2.wsdl.php') | |
| 275 | + && is_readable(dirname(__FILE__) . '/cpaint2.wsdl.php')) { | |
| 276 | + | |
| 277 | + require_once(dirname(__FILE__) . '/cpaint2.wsdl.php'); | |
| 278 | + | |
| 279 | + if (class_exists('cpaint_wsdl')) { | |
| 280 | + // create new instance of WSDL library | |
| 281 | + $wsdl = new cpaint_wsdl(); | |
| 282 | + | |
| 283 | + // build WSDL info | |
| 284 | + header('Content-type: text/xml; charset=UTF-8'); | |
| 285 | + echo $wsdl->generate($this->api_functions, $this->api_datatypes); | |
| 286 | + | |
| 287 | + } else { | |
| 288 | + header('Content-type: text/plain; charset=ISO-8859-1'); | |
| 289 | + echo 'WSDL generator is unavailable'; | |
| 290 | + } // end: if | |
| 291 | + | |
| 292 | + } else { | |
| 293 | + header('Content-type: text/plain; charset=ISO-8859-1'); | |
| 294 | + echo 'WSDL generator is unavailable'; | |
| 295 | + } // end: if | |
| 296 | + } // end: if | |
| 297 | + } | |
| 298 | + | |
| 299 | + /** | |
| 300 | + * registers a new function or method as part of the CPAINT API | |
| 301 | + * | |
| 302 | + * @access public | |
| 303 | + * @param mixed $func function name, array(&$object, 'function_name') or array('class', 'function_name') | |
| 304 | + * @param array $input function input parameters (not yet used by CPAINT and subject to change) | |
| 305 | + * @param array $output function output format (not yed used by CPAINT and subject to change) | |
| 306 | + * @param string $comment description of the functionality | |
| 307 | + * @return boolean | |
| 308 | + */ | |
| 309 | + function register($func, $input = array(), $output = array(), $comment = '') { | |
| 310 | + $return_value = false; | |
| 311 | + $input = (array) $input; | |
| 312 | + $output = (array) $output; | |
| 313 | + $comment = (string) $comment; | |
| 314 | + | |
| 315 | + if (is_array($func) | |
| 316 | + && (is_object($func[0]) || is_string($func[0])) | |
| 317 | + && is_string($func[1]) | |
| 318 | + && is_callable($func)) { | |
| 319 | + | |
| 320 | + // calling a method of an object | |
| 321 | + $this->api_functions[$func[1]] = array( | |
| 322 | + 'call' => $func, | |
| 323 | + 'input' => $input, | |
| 324 | + 'output' => $output, | |
| 325 | + 'comment' => $comment, | |
| 326 | + ); | |
| 327 | + $return_value = true; | |
| 328 | + | |
| 329 | + } elseif (is_string($func)) { | |
| 330 | + // calling a standalone function | |
| 331 | + $this->api_functions[$func] = array( | |
| 332 | + 'call' => $func, | |
| 333 | + 'input' => $input, | |
| 334 | + 'output' => $output, | |
| 335 | + 'comment' => $comment, | |
| 336 | + ); | |
| 337 | + $return_value = true; | |
| 338 | + } // end: if | |
| 339 | + | |
| 340 | + return $return_value; | |
| 341 | + } | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + /** | |
| 346 | + * unregisters a function that is currently part of the CPAINT API. | |
| 347 | + * | |
| 348 | + * proves useful when the same set of functions is to be used in the | |
| 349 | + * frontend and in some kind of administration environment. you might | |
| 350 | + * want to unregister a few (admin) functions for the frontend in this | |
| 351 | + * case. | |
| 352 | + * | |
| 353 | + * @access public | |
| 354 | + * @param string $func function name | |
| 355 | + * @return boolean | |
| 356 | + */ | |
| 357 | + function unregister($func) { | |
| 358 | + $retval = false; | |
| 359 | + | |
| 360 | + if (is_array($this->api_functions[$func])) { | |
| 361 | + unset($this->api_functions[$func]); | |
| 362 | + } // end: if | |
| 363 | + | |
| 364 | + return $retval; | |
| 365 | + } | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + /** | |
| 370 | + * registers a complex data type | |
| 371 | + * | |
| 372 | + * @access public | |
| 373 | + * @param array $schema schema definition for the complex type | |
| 374 | + * @return boolean | |
| 375 | + */ | |
| 376 | + function complex_type($schema) { | |
| 377 | + $return_value = false; | |
| 378 | + $schema = (array) $schema; | |
| 379 | + | |
| 380 | + if ($schema['name'] != '' | |
| 381 | + && in_array($schema['type'], array('restriction', 'complex', 'list'))) { | |
| 382 | + | |
| 383 | + $this->api_datatypes[] = $schema; | |
| 384 | + $return_value = true; | |
| 385 | + } // end: if | |
| 386 | + | |
| 387 | + return $return_value; | |
| 388 | + } | |
| 389 | + | |
| 390 | + /** | |
| 391 | + * switches the generation of WSDL on/off. default is on | |
| 392 | + * | |
| 393 | + * @access public | |
| 394 | + * @param boolean $state state of WSDL generation | |
| 395 | + * @return void | |
| 396 | + */ | |
| 397 | + function use_wsdl($state) { | |
| 398 | + $this->use_wsdl = (boolean) $state; | |
| 399 | + } | |
| 400 | + | |
| 401 | + /** | |
| 402 | + * adds a new subnode to the basenode. | |
| 403 | + * | |
| 404 | + * will return a reference to it for further processing. | |
| 405 | + * | |
| 406 | + * @access public | |
| 407 | + * @param string $nodename name of the new node | |
| 408 | + * @param string $id id of the new node | |
| 409 | + * @return object | |
| 410 | + */ | |
| 411 | + function &add_node($nodename, $id = '') { | |
| 412 | + return $this->basenode->add_node($nodename, $id); | |
| 413 | + } | |
| 414 | + | |
| 415 | + /** | |
| 416 | + * assigns textual data to the basenode. | |
| 417 | + * | |
| 418 | + * @access public | |
| 419 | + * @param mixed $data data to assign to this node | |
| 420 | + * @return void | |
| 421 | + */ | |
| 422 | + function set_data($data) { | |
| 423 | + $this->basenode->set_data($data); | |
| 424 | + } | |
| 425 | + | |
| 426 | + /** | |
| 427 | + * returns the data assigned to the basenode. | |
| 428 | + * | |
| 429 | + * @access public | |
| 430 | + * @return mixed | |
| 431 | + */ | |
| 432 | + function get_data() { | |
| 433 | + return $this->basenode->get_data(); | |
| 434 | + } | |
| 435 | + | |
| 436 | + /** | |
| 437 | + * sets the id property of the basenode. | |
| 438 | + * | |
| 439 | + * @deprecated deprecated since version 2.0.0 | |
| 440 | + * @access public | |
| 441 | + * @param string $id the id | |
| 442 | + * @return void | |
| 443 | + */ | |
| 444 | + function set_id($id) { | |
| 445 | + $this->basenode->set_attribute('id', $id); | |
| 446 | + } | |
| 447 | + | |
| 448 | + /** | |
| 449 | + * gets the id property of the basenode. | |
| 450 | + * | |
| 451 | + * @deprecated deprecated since version 2.0.0 | |
| 452 | + * @access public | |
| 453 | + * @return string | |
| 454 | + */ | |
| 455 | + function get_id() { | |
| 456 | + return $this->basenode->get_attribute('id'); | |
| 457 | + } | |
| 458 | + | |
| 459 | + /** | |
| 460 | + * adds a new attribute to the basenode. | |
| 461 | + * | |
| 462 | + * @access public | |
| 463 | + * @param string $name attribute name | |
| 464 | + * @param mixed $value attribute value | |
| 465 | + * @return void | |
| 466 | + */ | |
| 467 | + function set_attribute($name, $value) { | |
| 468 | + $this->basenode->set_attribute($name, $value); | |
| 469 | + } | |
| 470 | + | |
| 471 | + /** | |
| 472 | + * retrieves an attribute of the basenode by name. | |
| 473 | + * | |
| 474 | + * @access public | |
| 475 | + * @param string $name attribute name | |
| 476 | + * @return string | |
| 477 | + */ | |
| 478 | + function get_attribute($name) { | |
| 479 | + return $this->basenode->get_attributes($name); | |
| 480 | + } | |
| 481 | + | |
| 482 | + /** | |
| 483 | + * set name property of the basenode. | |
| 484 | + * | |
| 485 | + * @access public | |
| 486 | + * @param string $name the name | |
| 487 | + * @return void | |
| 488 | + */ | |
| 489 | + function set_name($name) { | |
| 490 | + $this->basenode->set_name($name); | |
| 491 | + } | |
| 492 | + | |
| 493 | + /** | |
| 494 | + * get name property of the basenode. | |
| 495 | + * | |
| 496 | + * @access public | |
| 497 | + * @return string | |
| 498 | + */ | |
| 499 | + function get_name() { | |
| 500 | + return $this->basenode->get_name(); | |
| 501 | + } | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + /** | |
| 506 | + * returns the response type as requested by the client | |
| 507 | + * | |
| 508 | + * @access public | |
| 509 | + * @return string | |
| 510 | + */ | |
| 511 | + function get_response_type() { | |
| 512 | + return $this->response_type; | |
| 513 | + } | |
| 514 | + | |
| 515 | + } | |
| 516 | + | |
| 517 | + /** | |
| 518 | + * a cpaint data node. Data nodes are used to build up the response. | |
| 519 | + * | |
| 520 | + * @package CPAINT | |
| 521 | + * @access public | |
| 522 | + * @author Dominique Stender <dstender@st-webdevelopment.de> | |
| 523 | + * @copyright 2005-2006 (Dominique Stender); All rights reserved | |
| 524 | + * @version 2.0.3 | |
| 525 | + */ | |
| 526 | + class cpaint_node { | |
| 527 | + /** | |
| 528 | + * array of subnodes. | |
| 529 | + * | |
| 530 | + * @access public | |
| 531 | + * @var array $composites | |
| 532 | + */ | |
| 533 | + var $composites; | |
| 534 | + | |
| 535 | + /** | |
| 536 | + * node attributes. | |
| 537 | + * | |
| 538 | + * @access public | |
| 539 | + * @var array $attributes | |
| 540 | + */ | |
| 541 | + var $attributes; | |
| 542 | + | |
| 543 | + /** | |
| 544 | + * name of this node. | |
| 545 | + * | |
| 546 | + * @access public | |
| 547 | + * @var string $nodename | |
| 548 | + */ | |
| 549 | + var $nodename; | |
| 550 | + | |
| 551 | + /** | |
| 552 | + * textual data of this node. | |
| 553 | + * | |
| 554 | + * @access public | |
| 555 | + * @var string $data | |
| 556 | + */ | |
| 557 | + var $data; | |
| 558 | + | |
| 559 | + /** | |
| 560 | + * character encoding for input data | |
| 561 | + * | |
| 562 | + * @access private | |
| 563 | + * @var $input_encoding | |
| 564 | + */ | |
| 565 | + var $input_encoding; | |
| 566 | + | |
| 567 | + /** | |
| 568 | + * PHP4 constructor. | |
| 569 | + * | |
| 570 | + * @package CPAINT | |
| 571 | + * @access public | |
| 572 | + * @return void | |
| 573 | + */ | |
| 574 | + function cpaint_node() { | |
| 575 | + $this->__construct(); | |
| 576 | + } | |
| 577 | + | |
| 578 | + /** | |
| 579 | + * PHP 5 constructor. | |
| 580 | + * | |
| 581 | + * @access public | |
| 582 | + * @return void | |
| 583 | + */ | |
| 584 | + function __construct() { | |
| 585 | + // initialize properties | |
| 586 | + $this->composites = array(); | |
| 587 | + $this->attributes = array(); | |
| 588 | + $this->data = ''; | |
| 589 | + | |
| 590 | + $this->set_encoding('UTF-8'); | |
| 591 | + $this->set_name(''); | |
| 592 | + $this->set_attribute('id', ''); | |
| 593 | + } | |
| 594 | + | |
| 595 | + /** | |
| 596 | + * adds a new subnode to this node. | |
| 597 | + * | |
| 598 | + * will return a reference to it for further processing. | |
| 599 | + * | |
| 600 | + * @access public | |
| 601 | + * @param string $nodename name of the new node | |
| 602 | + * @param string $id id of the new node | |
| 603 | + * @return object | |
| 604 | + */ | |
| 605 | + function &add_node($nodename, $id = '') { | |
| 606 | + $composites = count($this->composites); | |
| 607 | + | |
| 608 | + // create new node | |
| 609 | + $this->composites[$composites] =& new cpaint_node(); | |
| 610 | + $this->composites[$composites]->set_name($nodename); | |
| 611 | + $this->composites[$composites]->set_attribute('id', $id); | |
| 612 | + $this->composites[$composites]->set_encoding($this->input_encoding); | |
| 613 | + | |
| 614 | + return $this->composites[$composites]; | |
| 615 | + } | |
| 616 | + | |
| 617 | + /** | |
| 618 | + * assigns textual data to this node. | |
| 619 | + * | |
| 620 | + * @access public | |
| 621 | + * @param mixed $data data to assign to this node | |
| 622 | + * @return void | |
| 623 | + */ | |
| 624 | + function set_data($data) { | |
| 625 | + $this->data = $data; | |
| 626 | + } | |
| 627 | + | |
| 628 | + /** | |
| 629 | + * returns the textual data assigned to this node. | |
| 630 | + * | |
| 631 | + * @access public | |
| 632 | + * @return mixed | |
| 633 | + */ | |
| 634 | + function get_data() { | |
| 635 | + return $this->data; | |
| 636 | + } | |
| 637 | + | |
| 638 | + /** | |
| 639 | + * sets the id property of this node. | |
| 640 | + * | |
| 641 | + * @deprecated deprecated since version 2.0.0 | |
| 642 | + * @access public | |
| 643 | + * @param string id the id | |
| 644 | + * @return void | |
| 645 | + */ | |
| 646 | + function set_id($id) { | |
| 647 | + if ($id != '') { | |
| 648 | + $this->set_attribute('id', $id); | |
| 649 | + } // end: if | |
| 650 | + } | |
| 651 | + | |
| 652 | + /** | |
| 653 | + * returns the id property if this node. | |
| 654 | + * | |
| 655 | + * @deprecated deprecated since version 2.0.0 | |
| 656 | + * @access public | |
| 657 | + * @return string | |
| 658 | + */ | |
| 659 | + function get_id() { | |
| 660 | + return $this->get_attribute('id'); | |
| 661 | + } | |
| 662 | + | |
| 663 | + /** | |
| 664 | + * adds a new attribute to this node. | |
| 665 | + * | |
| 666 | + * @access public | |
| 667 | + * @param string $name attribute name | |
| 668 | + * @param mixed $value attribute value | |
| 669 | + * @return void | |
| 670 | + */ | |
| 671 | + function set_attribute($name, $value) { | |
| 672 | + $this->attributes[$name] = (string) $value; | |
| 673 | + } | |
| 674 | + | |
| 675 | + /** | |
| 676 | + * retrieves an attribute by name. | |
| 677 | + * | |
| 678 | + * @access public | |
| 679 | + * @param string $name attribute name | |
| 680 | + * @return string | |
| 681 | + */ | |
| 682 | + function get_attribute($name) { | |
| 683 | + return $this->attributes[$name]; | |
| 684 | + } | |
| 685 | + | |
| 686 | + /** | |
| 687 | + * set name property. | |
| 688 | + * | |
| 689 | + * @access public | |
| 690 | + * @param string $name the name | |
| 691 | + * @return void | |
| 692 | + */ | |
| 693 | + function set_name($name) { | |
| 694 | + $this->nodename = (string) $name; | |
| 695 | + } | |
| 696 | + | |
| 697 | + /** | |
| 698 | + * get name property. | |
| 699 | + * | |
| 700 | + * @access public | |
| 701 | + * @return string | |
| 702 | + */ | |
| 703 | + function get_name() { | |
| 704 | + return $this->nodename; | |
| 705 | + } | |
| 706 | + | |
| 707 | + /** | |
| 708 | + * sets the character encoding for this node | |
| 709 | + * | |
| 710 | + * @access public | |
| 711 | + * @param string $encoding character encoding | |
| 712 | + * @return void | |
| 713 | + */ | |
| 714 | + function set_encoding($encoding) { | |
| 715 | + $this->input_encoding = strtoupper((string) $encoding); | |
| 716 | + } | |
| 717 | + | |
| 718 | + /** | |
| 719 | + * returns the character encoding for this node | |
| 720 | + * | |
| 721 | + * @access public | |
| 722 | + * @return string | |
| 723 | + */ | |
| 724 | + function get_encoding() { | |
| 725 | + return $this->input_encoding; | |
| 726 | + } | |
| 727 | + } | |
| 728 | + | |
| 729 | + /** | |
| 730 | + * static class of output transformers. | |
| 731 | + * | |
| 732 | + * @package CPAINT | |
| 733 | + * @access public | |
| 734 | + * @author Dominique Stender <dstender@st-webdevelopment.de> | |
| 735 | + * @copyright 2003-2006 (Dominique Stender); All rights reserved | |
| 736 | + * @version 2.0.3 | |
| 737 | + */ | |
| 738 | + class cpaint_transformer { | |
| 739 | + /** | |
| 740 | + * toString method, used to generate response of type TEXT. | |
| 741 | + * will perform character transformation according to parameters. | |
| 742 | + * | |
| 743 | + * @access public | |
| 744 | + * @param object $node a cpaint_node object | |
| 745 | + * @return string | |
| 746 | + */ | |
| 747 | + function toString(&$node) { | |
| 748 | + $return_value = ''; | |
| 749 | + | |
| 750 | + foreach ($node->composites as $composite) { | |
| 751 | + $return_value .= cpaint_transformer::toString($composite); | |
| 752 | + } | |
| 753 | + | |
| 754 | + $return_value .= cpaint_transformer::encode($node->get_data(), $node->get_encoding()); | |
| 755 | + | |
| 756 | + return $return_value; | |
| 757 | + } | |
| 758 | + | |
| 759 | + /** | |
| 760 | + * XML response generator. | |
| 761 | + * will perform character transformation according to parameters. | |
| 762 | + * | |
| 763 | + * @access public | |
| 764 | + * @param object $node a cpaint_node object | |
| 765 | + * @return string | |
| 766 | + */ | |
| 767 | + function toXML(&$node) { | |
| 768 | + $return_value = '<' . $node->get_name(); | |
| 769 | + | |
| 770 | + // handle attributes | |
| 771 | + foreach ($node->attributes as $name => $value) { | |
| 772 | + if ($value != '') { | |
| 773 | + $return_value .= ' ' . $name . '="' . $node->get_attribute($name) . '"'; | |
| 774 | + } | |
| 775 | + } // end: foreach | |
| 776 | + | |
| 777 | + $return_value .= '>'; | |
| 778 | + | |
| 779 | + // handle subnodes | |
| 780 | + foreach ($node->composites as $composite) { | |
| 781 | + $return_value .= cpaint_transformer::toXML($composite); | |
| 782 | + } | |
| 783 | + | |
| 784 | + $return_value .= cpaint_transformer::encode($node->get_data(), $node->get_encoding()) | |
| 785 | + . '</' . $node->get_name() . '>'; | |
| 786 | + | |
| 787 | + return $return_value; | |
| 788 | + } | |
| 789 | + | |
| 790 | + /** | |
| 791 | + * JSON response generator. | |
| 792 | + * will perform character transformation according to parameters. | |
| 793 | + * | |
| 794 | + * @access public | |
| 795 | + * @param object $node a cpaint_node object | |
| 796 | + * @return string | |
| 797 | + */ | |
| 798 | + function toJSON($node) { | |
| 799 | + $return_value = ''; | |
| 800 | + $JSON_node = new stdClass(); | |
| 801 | + | |
| 802 | + // handle attributes | |
| 803 | + $JSON_node->attributes = $node->attributes; | |
| 804 | + | |
| 805 | + // handle subnodes | |
| 806 | + foreach ($node->composites as $composite) { | |
| 807 | + | |
| 808 | + if (!is_array($JSON_node->{$composite->nodename})) { | |
| 809 | + $JSON_node->{$composite->nodename} = array(); | |
| 810 | + } // end: if | |
| 811 | + | |
| 812 | + // we need to parse the JSON object again to avoid multiple encoding | |
| 813 | + $JSON_node->{$composite->nodename}[] = $GLOBALS['__cpaint_json']->parse(cpaint_transformer::toJSON($composite)); | |
| 814 | + } | |
| 815 | + | |
| 816 | + // handle data | |
| 817 | + $JSON_node->data = $node->data; | |
| 818 | + | |
| 819 | + return $GLOBALS['__cpaint_json']->stringify($JSON_node); | |
| 820 | + } | |
| 821 | + | |
| 822 | + /** | |
| 823 | + * performs conversion to JavaScript-safe UTF-8 characters | |
| 824 | + * | |
| 825 | + * @access public | |
| 826 | + * @param string $data data to convert | |
| 827 | + * @param string $encoding character encoding | |
| 828 | + * @return string | |
| 829 | + */ | |
| 830 | + function encode($data, $encoding) { | |
| 831 | + // convert string | |
| 832 | + if (function_exists('iconv')) { | |
| 833 | + // iconv is by far the most flexible approach, try this first | |
| 834 | + $return_value = iconv($encoding, 'UTF-8', $data); | |
| 835 | + | |
| 836 | + } elseif ($encoding == 'ISO-8859-1') { | |
| 837 | + // for ISO-8859-1 we can use utf8-encode() | |
| 838 | + $return_value = utf8_encode($data); | |
| 839 | + | |
| 840 | + } else { | |
| 841 | + // give up. if UTF-8 data was supplied everything is fine! | |
| 842 | + $return_value = $data; | |
| 843 | + } /* end: if */ | |
| 844 | + | |
| 845 | + // now encode non-printable characters | |
| 846 | + for ($i = 0; $i < 32; $i++) { | |
| 847 | + $return_value = str_replace(chr($i), '\u00' . sprintf('%02x', $i), $return_value); | |
| 848 | + } // end: for | |
| 849 | + | |
| 850 | + // encode <, >, and & respectively for XML sanity | |
| 851 | + $return_value = str_replace(chr(0x26), '\u0026', $return_value); | |
| 852 | + $return_value = str_replace(chr(0x3c), '\u003c', $return_value); | |
| 853 | + $return_value = str_replace(chr(0x3e), '\u003e', $return_value); | |
| 854 | + | |
| 855 | + return $return_value; | |
| 856 | + } | |
| 857 | + | |
| 858 | + /** | |
| 859 | + * performs conversion from JavaScript encodeURIComponent() string (UTF-8) to | |
| 860 | + * the charset in use. | |
| 861 | + * | |
| 862 | + * @access public | |
| 863 | + * @param string $data data to convert | |
| 864 | + * @param string $encoding character encoding | |
| 865 | + * @return string | |
| 866 | + */ | |
| 867 | + function decode($data, $encoding) { | |
| 868 | + // convert string | |
| 869 | + | |
| 870 | + if (is_string($data)) { | |
| 871 | + if (function_exists('iconv')) { | |
| 872 | + // iconv is by far the most flexible approach, try this first | |
| 873 | + $return_value = iconv('UTF-8', $encoding, $data); | |
| 874 | + | |
| 875 | + } elseif ($encoding == 'ISO-8859-1') { | |
| 876 | + // for ISO-8859-1 we can use utf8-decode() | |
| 877 | + $return_value = utf8_decode($data); | |
| 878 | + | |
| 879 | + } else { | |
| 880 | + // give up. if data was supplied in the correct format everything is fine! | |
| 881 | + $return_value = $data; | |
| 882 | + } // end: if | |
| 883 | + | |
| 884 | + } else { | |
| 885 | + // non-string value | |
| 886 | + $return_value = $data; | |
| 887 | + } // end: if | |
| 888 | + | |
| 889 | + return $return_value; | |
| 890 | + } | |
| 891 | + | |
| 892 | + /** | |
| 893 | + * decodes a (nested) array of data from UTF-8 into the configured character set | |
| 894 | + * | |
| 895 | + * @access public | |
| 896 | + * @param array $data data to convert | |
| 897 | + * @param string $encoding character encoding | |
| 898 | + * @return array | |
| 899 | + */ | |
| 900 | + function decode_array($data, $encoding) { | |
| 901 | + $return_value = array(); | |
| 902 | + | |
| 903 | + foreach ($data as $key => $value) { | |
| 904 | + | |
| 905 | + if (!is_array($value)) { | |
| 906 | + $return_value[$key] = cpaint_transformer::decode($value, $encoding); | |
| 907 | + | |
| 908 | + } else { | |
| 909 | + $return_value[$key] = cpaint_transformer::decode_array($value, $encoding); | |
| 910 | + } | |
| 911 | + } | |
| 912 | + | |
| 913 | + return $return_value; | |
| 914 | + } | |
| 915 | + | |
| 916 | + /** | |
| 917 | + * determines the output character set | |
| 918 | + * based on input character set | |
| 919 | + * | |
| 920 | + * @access public | |
| 921 | + * @param string $encoding character encoding | |
| 922 | + * @return string | |
| 923 | + */ | |
| 924 | + function find_output_charset($encoding) { | |
| 925 | + $return_value = 'UTF-8'; | |
| 926 | + | |
| 927 | + if (function_exists('iconv') | |
| 928 | + || $encoding == 'UTF-8' | |
| 929 | + || $encoding == 'ISO-8859-1') { | |
| 930 | + | |
| 931 | + $return_value = 'UTF-8'; | |
| 932 | + | |
| 933 | + } else { | |
| 934 | + $return_value = $encoding; | |
| 935 | + } // end: if | |
| 936 | + | |
| 937 | + return $return_value; | |
| 938 | + } | |
| 939 | + } | |
| 940 | + | |
| 941 | +?> | |
| 0 | 942 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,117 @@ |
| 1 | +<?php | |
| 2 | +/** | |
| 3 | +* CPAINT (Cross-Platform Asynchronous INterface Toolkit) | |
| 4 | +* | |
| 5 | +* http://sf.net/projects/cpaint | |
| 6 | +* | |
| 7 | +* released under the terms of the GPL | |
| 8 | +* see http://www.fsf.org/licensing/licenses/gpl.txt for details | |
| 9 | +* | |
| 10 | +* | |
| 11 | +* proxy script to pass request on to remote servers | |
| 12 | +* | |
| 13 | +* @package CPAINT | |
| 14 | +* @author Paul Sullivan <wiley14@gmail.com> | |
| 15 | +* @author Dominique Stender <dstender@st-webdevelopment.de> | |
| 16 | +* @copyright Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint | |
| 17 | +* @version 2.0.3 | |
| 18 | +*/ | |
| 19 | + | |
| 20 | +//---- includes ---------------------------------------------------------------- | |
| 21 | + /** | |
| 22 | + * @include config | |
| 23 | + */ | |
| 24 | + require_once("cpaint2.config.php"); | |
| 25 | + | |
| 26 | +//---- main code --------------------------------------------------------------- | |
| 27 | + | |
| 28 | + error_reporting (E_ALL ^ E_NOTICE ^ E_WARNING); | |
| 29 | + set_time_limit(0); | |
| 30 | + | |
| 31 | + if ($_REQUEST['cpaint_remote_url'] != "") { | |
| 32 | + $cp_remote_url = urldecode($_REQUEST['cpaint_remote_url']); | |
| 33 | + $cp_remote_method = urldecode($_REQUEST['cpaint_remote_method']); | |
| 34 | + $cp_remote_query = urldecode($_REQUEST['cpaint_remote_query']); | |
| 35 | + $cp_response_type = strtoupper($_REQUEST['cpaint_response_type']); | |
| 36 | + } | |
| 37 | + | |
| 38 | + // propagate XML header if necessary | |
| 39 | + if ($cp_response_type == 'XML' | |
| 40 | + || $cp_response_type == 'OBJECT') { | |
| 41 | + header("Content-type: text/xml"); | |
| 42 | + } | |
| 43 | + | |
| 44 | + // transfer mode specifics | |
| 45 | + if ($cp_remote_method == 'GET') { | |
| 46 | + $cp_remote_url .= '?' . $cp_remote_query; | |
| 47 | + $cp_request_body = ''; | |
| 48 | + | |
| 49 | + // prepare parameters | |
| 50 | + $url_parts = parse_url($cp_remote_url); | |
| 51 | + | |
| 52 | + // build basic header | |
| 53 | + $cp_request_header = 'GET ' . $url_parts['path'] . '?' . str_replace(' ', '+', $url_parts['query']) . " HTTP/1.0\r\n" | |
| 54 | + . "Host: " . $url_parts['host'] . "\r\n"; | |
| 55 | + | |
| 56 | + } elseif ($cp_remote_method == 'POST') { | |
| 57 | + $cp_request_body = '&' . $cp_remote_query; | |
| 58 | + | |
| 59 | + // prepare parameters | |
| 60 | + $url_parts = parse_url($cp_remote_url); | |
| 61 | + | |
| 62 | + // check against whitelist | |
| 63 | + if ($cpaint2_config["proxy.security.use_whitelist"] == true) { | |
| 64 | + $url_allowed = false; | |
| 65 | + foreach($cpaint2_proxy_whitelist as $whitelistURL) { | |
| 66 | + $whiteList_parts = parse_url("http://" . $whitelistURL); | |
| 67 | + $url_parts_temp = parse_url("http://" . $cp_remote_url); | |
| 68 | + if (array_key_exists("path", $whiteList_parts)) { | |
| 69 | + if ((strtolower($whiteList_parts["path"]) == strtolower($url_parts_temp["path"])) && (strtolower($whiteList_parts["host"]) == strtolower($url_parts_temp["host"]))) $url_allowed = true; | |
| 70 | + } else { // no path, check only host | |
| 71 | + if (strtolower($whiteList_parts["host"]) == strtolower($url_parts_temp["host"])) $url_allowed = true; | |
| 72 | + } | |
| 73 | + } | |
| 74 | + if ($url_allowed == false) die("[CPAINT] The host or script cannot be accessed through this proxy."); | |
| 75 | + } | |
| 76 | + | |
| 77 | + // build basic header | |
| 78 | + $cp_request_header = 'POST ' . $url_parts['path'] . " HTTP/1.0\r\n" | |
| 79 | + . "Host: " . $url_parts['host'] . "\r\n" | |
| 80 | + . "Content-Type: application/x-www-form-urlencoded\r\n"; | |
| 81 | + } | |
| 82 | + | |
| 83 | + // add port if none exists | |
| 84 | + if (!isset($url_parts['port'])) { | |
| 85 | + $url_parts['port'] = 80; | |
| 86 | + } | |
| 87 | + | |
| 88 | + // add content-length header | |
| 89 | + $cp_request_header .= "Content-Length: " . strlen($cp_request_body) . "\r\n"; | |
| 90 | + | |
| 91 | + // add authentication to header if necessary | |
| 92 | + if ($url_parts['user'] != '') { | |
| 93 | + $cp_request_header .= 'Authorization: Basic ' . base64_encode($url_parts['user'] . ':' . $url_parts['pass']) . "\r\n"; | |
| 94 | + } | |
| 95 | + | |
| 96 | + // open connection | |
| 97 | + $cp_socket = @fsockopen($url_parts['host'], $url_parts['port'], $error, $errstr, 10); | |
| 98 | + | |
| 99 | + if ($cp_socket !== false) { | |
| 100 | + // send headers | |
| 101 | + @fwrite($cp_socket, $cp_request_header . "\r\n\r\n"); | |
| 102 | + | |
| 103 | + // send body if necessary | |
| 104 | + if ($cp_request_body != '') { | |
| 105 | + @fwrite($cp_socket, $cp_request_body . "\r\n"); | |
| 106 | + } | |
| 107 | + | |
| 108 | + while (!feof($cp_socket)) { | |
| 109 | + $http_data = $http_data . fgets($cp_socket); | |
| 110 | + } | |
| 111 | + | |
| 112 | + list($http_headers, $http_body) = split("\r\n\r\n", $http_data, 2); | |
| 113 | + echo($http_body); | |
| 114 | + @fclose($cp_socket); | |
| 115 | + } | |
| 116 | + | |
| 117 | +?> | |
| 0 | 118 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,580 @@ |
| 1 | +<?php | |
| 2 | +/** | |
| 3 | +* The content of this file is (c) 2003-2005 digital media center GmbH | |
| 4 | +* All rights reserved | |
| 5 | +* | |
| 6 | +* a JSON parser / generator. | |
| 7 | +* implemented as 100% compatible port of the original JSON parser | |
| 8 | +* by Douglas Crockford on http://www.JSON.org. | |
| 9 | +* | |
| 10 | +* @package JSON | |
| 11 | +* @access public | |
| 12 | +* @copyright Copyright (c) 2005-2006 Dominique Stender - http://sf.net/projects/cpaint | |
| 13 | +* @license http://www.crockford.com/JSON/license.html | |
| 14 | +* @author Dominique Stender <dstender@st-webdevelopment.de> | |
| 15 | +* @version 1.0.1 | |
| 16 | +*/ | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + /** | |
| 21 | + * a JSON parser / generator | |
| 22 | + * | |
| 23 | + * @access public | |
| 24 | + * @author Dominique Stender <dst@dmc.de> | |
| 25 | + * @version 1.0.1 | |
| 26 | + */ | |
| 27 | + class JSON { | |
| 28 | + /** | |
| 29 | + * line counter | |
| 30 | + * | |
| 31 | + * @access private | |
| 32 | + * @var integer $at | |
| 33 | + */ | |
| 34 | + var $at = 0; | |
| 35 | + | |
| 36 | + /** | |
| 37 | + * | |
| 38 | + * @access private | |
| 39 | + * @var string $ch | |
| 40 | + */ | |
| 41 | + var $ch = ' '; | |
| 42 | + | |
| 43 | + /** | |
| 44 | + * JSON representation | |
| 45 | + * | |
| 46 | + * @access private | |
| 47 | + * @var string $text | |
| 48 | + */ | |
| 49 | + var $text = ''; | |
| 50 | + | |
| 51 | + /** | |
| 52 | + * takes an arbitrary PHP data structure and generates a valid JSON representation from it | |
| 53 | + * | |
| 54 | + * @param mixed $arg an arbitrary PHP data structure | |
| 55 | + * @access public | |
| 56 | + * @return string | |
| 57 | + * @version 1.0.1 fixed tests whether $s has content by using strlen(). this enables the user to use 0 as very first character | |
| 58 | + */ | |
| 59 | + function stringify($arg) { | |
| 60 | + $returnValue = ''; | |
| 61 | + $c = ''; | |
| 62 | + $i = ''; | |
| 63 | + $l = ''; | |
| 64 | + $s = ''; | |
| 65 | + $v = ''; | |
| 66 | + $numeric = true; | |
| 67 | + | |
| 68 | + switch (gettype($arg)) { | |
| 69 | + | |
| 70 | + case 'array': | |
| 71 | + // do a test whether all array keys are numeric | |
| 72 | + foreach ($arg as $i => $v) { | |
| 73 | + if (!is_numeric($i)) { | |
| 74 | + $numeric = false; | |
| 75 | + break; | |
| 76 | + } | |
| 77 | + } | |
| 78 | + | |
| 79 | + if ($numeric) { | |
| 80 | + | |
| 81 | + foreach ($arg as $i => $v) { | |
| 82 | + if (strlen($s) > 0) { | |
| 83 | + $s .= ','; | |
| 84 | + } | |
| 85 | + | |
| 86 | + $s .= $this->stringify($arg[$i]); | |
| 87 | + } // end: foreach | |
| 88 | + | |
| 89 | + $returnValue = '[' . $s . ']'; | |
| 90 | + | |
| 91 | + } else { | |
| 92 | + // associative array | |
| 93 | + foreach ($arg as $i => $v) { | |
| 94 | + if (strlen($s) > 0) { | |
| 95 | + $s .= ','; | |
| 96 | + } | |
| 97 | + $s .= $this->stringify($i) . ':' . $this->stringify($arg[$i]); | |
| 98 | + } | |
| 99 | + // return as object | |
| 100 | + $returnValue = '{' . $s . '}'; | |
| 101 | + } | |
| 102 | + break; | |
| 103 | + | |
| 104 | + case 'object': | |
| 105 | + | |
| 106 | + foreach (get_object_vars($arg) as $i => $v) { | |
| 107 | + $v = $this->stringify($v); | |
| 108 | + | |
| 109 | + if (strlen($s) > 0) { | |
| 110 | + $s .= ','; | |
| 111 | + } | |
| 112 | + | |
| 113 | + $s .= $this->stringify($i) . ':' . $v; | |
| 114 | + } | |
| 115 | + | |
| 116 | + $returnValue = '{' . $s . '}'; | |
| 117 | + break; | |
| 118 | + | |
| 119 | + case 'integer': | |
| 120 | + case 'double': | |
| 121 | + $returnValue = is_numeric($arg) ? (string) $arg : 'null'; | |
| 122 | + break; | |
| 123 | + | |
| 124 | + case 'string': | |
| 125 | + $l = strlen($arg); | |
| 126 | + $s = '"'; | |
| 127 | + | |
| 128 | + for ($i = 0; $i < $l; $i++) { | |
| 129 | + $c = $arg{$i}; | |
| 130 | + | |
| 131 | + if (ord($c) >= ord(' ')) { | |
| 132 | + | |
| 133 | + if ($c == '\\' | |
| 134 | + || $c == '"') { | |
| 135 | + | |
| 136 | + $s .= '\\'; | |
| 137 | + } | |
| 138 | + $s .= $c; | |
| 139 | + | |
| 140 | + } else { | |
| 141 | + | |
| 142 | + switch ($c) { | |
| 143 | + | |
| 144 | + case '\b': | |
| 145 | + $s .= '\\b'; | |
| 146 | + break; | |
| 147 | + | |
| 148 | + case '\f': | |
| 149 | + $s .= '\\f'; | |
| 150 | + break; | |
| 151 | + | |
| 152 | + case '\n': | |
| 153 | + $s .= '\\n'; | |
| 154 | + break; | |
| 155 | + | |
| 156 | + case '\r': | |
| 157 | + $s .= '\\r'; | |
| 158 | + break; | |
| 159 | + | |
| 160 | + case '\t': | |
| 161 | + $s .= '\\t'; | |
| 162 | + break; | |
| 163 | + | |
| 164 | + default: | |
| 165 | + $s .= '\u00' . sprintf('%02x', ord($c)); | |
| 166 | + } | |
| 167 | + } | |
| 168 | + } | |
| 169 | + $returnValue = $s . '"'; | |
| 170 | + break; | |
| 171 | + | |
| 172 | + case 'boolean': | |
| 173 | + $returnValue = (string) $arg; | |
| 174 | + break; | |
| 175 | + | |
| 176 | + default: | |
| 177 | + $returnValue = 'null'; | |
| 178 | + } | |
| 179 | + | |
| 180 | + return $returnValue; | |
| 181 | + } | |
| 182 | + | |
| 183 | + | |
| 184 | + /** | |
| 185 | + * parses the given string into a PHP data structure | |
| 186 | + * | |
| 187 | + * @param string $text JSON data representation | |
| 188 | + * @access public | |
| 189 | + * @return mixed | |
| 190 | + */ | |
| 191 | + function parse($text) { | |
| 192 | + $this->at = 0; | |
| 193 | + $this->ch = ' '; | |
| 194 | + $this->text = $text; | |
| 195 | + | |
| 196 | + return $this->val(); | |
| 197 | + } | |
| 198 | + | |
| 199 | + /** | |
| 200 | + * triggers a PHP_ERROR | |
| 201 | + * | |
| 202 | + * @access private | |
| 203 | + * @param string $m error message | |
| 204 | + * @return void | |
| 205 | + */ | |
| 206 | + function error($m) { | |
| 207 | + trigger_error($m . ' at offset ' . $this->at . ': ' . $this->text, E_USER_ERROR); | |
| 208 | + } | |
| 209 | + | |
| 210 | + /** | |
| 211 | + * returns the next character of a JSON string | |
| 212 | + * | |
| 213 | + * @access private | |
| 214 | + * @return string | |
| 215 | + */ | |
| 216 | + function next() { | |
| 217 | + $this->ch = $this->text{$this->at}; | |
| 218 | + $this->at++; | |
| 219 | + return $this->ch; | |
| 220 | + } | |
| 221 | + | |
| 222 | + /** | |
| 223 | + * handles whitespace and comments | |
| 224 | + * | |
| 225 | + * @access private | |
| 226 | + * @return void | |
| 227 | + */ | |
| 228 | + function white() { | |
| 229 | + | |
| 230 | + while ($this->ch != '' | |
| 231 | + && ord($this->ch) <= ord(' ')) { | |
| 232 | + | |
| 233 | + $this->next(); | |
| 234 | + } | |
| 235 | + } | |
| 236 | + | |
| 237 | + /** | |
| 238 | + * handles strings | |
| 239 | + * | |
| 240 | + * @access private | |
| 241 | + * @return void | |
| 242 | + */ | |
| 243 | + function str() { | |
| 244 | + $i = ''; | |
| 245 | + $s = ''; | |
| 246 | + $t = ''; | |
| 247 | + $u = ''; | |
| 248 | + | |
| 249 | + if ($this->ch == '"') { | |
| 250 | + | |
| 251 | + while ($this->next() !== null) { | |
| 252 | + | |
| 253 | + if ($this->ch == '"') { | |
| 254 | + $this->next(); | |
| 255 | + return $s; | |
| 256 | + | |
| 257 | + } elseif ($this->ch == '\\') { | |
| 258 | + | |
| 259 | + switch ($this->next()) { | |
| 260 | + case 'b': | |
| 261 | + $s .= '\b'; | |
| 262 | + break; | |
| 263 | + | |
| 264 | + case 'f': | |
| 265 | + $s .= '\f'; | |
| 266 | + break; | |
| 267 | + | |
| 268 | + case 'n': | |
| 269 | + $s .= '\n'; | |
| 270 | + break; | |
| 271 | + | |
| 272 | + case 'r': | |
| 273 | + $s .= '\r'; | |
| 274 | + break; | |
| 275 | + | |
| 276 | + case 't': | |
| 277 | + $s .= '\t'; | |
| 278 | + break; | |
| 279 | + | |
| 280 | + case 'u': | |
| 281 | + $u = 0; | |
| 282 | + | |
| 283 | + for ($i = 0; $i < 4; $i += 1) { | |
| 284 | + $t = (integer) sprintf('%01c', hexdec($this->next())); | |
| 285 | + | |
| 286 | + if (!is_numeric($t)) { | |
| 287 | + break 2; | |
| 288 | + } | |
| 289 | + $u = $u * 16 + $t; | |
| 290 | + } | |
| 291 | + | |
| 292 | + $s .= chr($u); | |
| 293 | + break; | |
| 294 | + | |
| 295 | + default: | |
| 296 | + $s .= $this->ch; | |
| 297 | + } | |
| 298 | + } else { | |
| 299 | + $s .= $this->ch; | |
| 300 | + } | |
| 301 | + } | |
| 302 | + } | |
| 303 | + | |
| 304 | + $this->error('Bad string'); | |
| 305 | + } | |
| 306 | + | |
| 307 | + /** | |
| 308 | + * handless arrays | |
| 309 | + * | |
| 310 | + * @access private | |
| 311 | + * @return void | |
| 312 | + */ | |
| 313 | + function arr() { | |
| 314 | + $a = array(); | |
| 315 | + | |
| 316 | + if ($this->ch == '[') { | |
| 317 | + $this->next(); | |
| 318 | + $this->white(); | |
| 319 | + | |
| 320 | + if ($this->ch == ']') { | |
| 321 | + $this->next(); | |
| 322 | + return $a; | |
| 323 | + } | |
| 324 | + | |
| 325 | + while ($this->ch) { | |
| 326 | + array_push($a, $this->val()); | |
| 327 | + $this->white(); | |
| 328 | + | |
| 329 | + if ($this->ch == ']') { | |
| 330 | + $this->next(); | |
| 331 | + return $a; | |
| 332 | + | |
| 333 | + } elseif ($this->ch != ',') { | |
| 334 | + break; | |
| 335 | + } | |
| 336 | + | |
| 337 | + $this->next(); | |
| 338 | + $this->white(); | |
| 339 | + } | |
| 340 | + | |
| 341 | + $this->error('Bad array'); | |
| 342 | + } | |
| 343 | + } | |
| 344 | + | |
| 345 | + /** | |
| 346 | + * handles objects | |
| 347 | + * | |
| 348 | + * @access public | |
| 349 | + * @return void | |
| 350 | + */ | |
| 351 | + function obj() { | |
| 352 | + $k = ''; | |
| 353 | + $o = new stdClass(); | |
| 354 | + | |
| 355 | + if ($this->ch == '{') { | |
| 356 | + $this->next(); | |
| 357 | + $this->white(); | |
| 358 | + | |
| 359 | + if ($this->ch == '}') { | |
| 360 | + $this->next(); | |
| 361 | + return $o; | |
| 362 | + } | |
| 363 | + | |
| 364 | + while ($this->ch) { | |
| 365 | + $k = $this->str(); | |
| 366 | + $this->white(); | |
| 367 | + | |
| 368 | + if ($this->ch != ':') { | |
| 369 | + break; | |
| 370 | + } | |
| 371 | + | |
| 372 | + $this->next(); | |
| 373 | + $o->$k = $this->val(); | |
| 374 | + $this->white(); | |
| 375 | + | |
| 376 | + if ($this->ch == '}') { | |
| 377 | + $this->next(); | |
| 378 | + return $o; | |
| 379 | + | |
| 380 | + } elseif ($this->ch != ',') { | |
| 381 | + break; | |
| 382 | + } | |
| 383 | + | |
| 384 | + $this->next(); | |
| 385 | + $this->white(); | |
| 386 | + } | |
| 387 | + } | |
| 388 | + | |
| 389 | + $this->error('Bad object'); | |
| 390 | + } | |
| 391 | + | |
| 392 | + /** | |
| 393 | + * handles objects | |
| 394 | + * | |
| 395 | + * @access public | |
| 396 | + * @return void | |
| 397 | + */ | |
| 398 | + function assoc() { | |
| 399 | + $k = ''; | |
| 400 | + $a = array(); | |
| 401 | + | |
| 402 | + if ($this->ch == '<') { | |
| 403 | + $this->next(); | |
| 404 | + $this->white(); | |
| 405 | + | |
| 406 | + if ($this->ch == '>') { | |
| 407 | + $this->next(); | |
| 408 | + return $a; | |
| 409 | + } | |
| 410 | + | |
| 411 | + while ($this->ch) { | |
| 412 | + $k = $this->str(); | |
| 413 | + $this->white(); | |
| 414 | + | |
| 415 | + if ($this->ch != ':') { | |
| 416 | + break; | |
| 417 | + } | |
| 418 | + | |
| 419 | + $this->next(); | |
| 420 | + $a[$k] = $this->val(); | |
| 421 | + $this->white(); | |
| 422 | + | |
| 423 | + if ($this->ch == '>') { | |
| 424 | + $this->next(); | |
| 425 | + return $a; | |
| 426 | + | |
| 427 | + } elseif ($this->ch != ',') { | |
| 428 | + break; | |
| 429 | + } | |
| 430 | + | |
| 431 | + $this->next(); | |
| 432 | + $this->white(); | |
| 433 | + } | |
| 434 | + } | |
| 435 | + | |
| 436 | + $this->error('Bad associative array'); | |
| 437 | + } | |
| 438 | + | |
| 439 | + /** | |
| 440 | + * handles numbers | |
| 441 | + * | |
| 442 | + * @access private | |
| 443 | + * @return void | |
| 444 | + */ | |
| 445 | + function num() { | |
| 446 | + $n = ''; | |
| 447 | + $v = ''; | |
| 448 | + | |
| 449 | + if ($this->ch == '-') { | |
| 450 | + $n = '-'; | |
| 451 | + $this->next(); | |
| 452 | + } | |
| 453 | + | |
| 454 | + while ($this->ch >= '0' | |
| 455 | + && $this->ch <= '9') { | |
| 456 | + | |
| 457 | + $n .= $this->ch; | |
| 458 | + $this->next(); | |
| 459 | + } | |
| 460 | + | |
| 461 | + if ($this->ch == '.') { | |
| 462 | + $n .= '.'; | |
| 463 | + | |
| 464 | + while ($this->next() | |
| 465 | + && $this->ch >= '0' | |
| 466 | + && $this->ch <= '9') { | |
| 467 | + | |
| 468 | + $n .= $this->ch; | |
| 469 | + } | |
| 470 | + } | |
| 471 | + | |
| 472 | + if ($this->ch == 'e' | |
| 473 | + || $this->ch == 'E') { | |
| 474 | + | |
| 475 | + $n .= 'e'; | |
| 476 | + $this->next(); | |
| 477 | + | |
| 478 | + if ($this->ch == '-' | |
| 479 | + || $this->ch == '+') { | |
| 480 | + | |
| 481 | + $n .= $this->ch; | |
| 482 | + $this->next(); | |
| 483 | + } | |
| 484 | + | |
| 485 | + while ($this->ch >= '0' | |
| 486 | + && $this->ch <= '9') { | |
| 487 | + | |
| 488 | + $n .= $this->ch; | |
| 489 | + $this->next(); | |
| 490 | + } | |
| 491 | + } | |
| 492 | + | |
| 493 | + $v += $n; | |
| 494 | + | |
| 495 | + if (!is_numeric($v)) { | |
| 496 | + $this->error('Bad number'); | |
| 497 | + | |
| 498 | + } else { | |
| 499 | + return $v; | |
| 500 | + } // end: if | |
| 501 | + } | |
| 502 | + | |
| 503 | + /** | |
| 504 | + * handles words | |
| 505 | + * | |
| 506 | + * @access private | |
| 507 | + * @return mixed | |
| 508 | + */ | |
| 509 | + function word() { | |
| 510 | + switch ($this->ch) { | |
| 511 | + | |
| 512 | + case 't': | |
| 513 | + | |
| 514 | + if ($this->next() == 'r' | |
| 515 | + && $this->next() == 'u' | |
| 516 | + && $this->next() == 'e') { | |
| 517 | + | |
| 518 | + $this->next(); | |
| 519 | + return true; | |
| 520 | + } | |
| 521 | + break; | |
| 522 | + | |
| 523 | + case 'f': | |
| 524 | + if ($this->next() == 'a' | |
| 525 | + && $this->next() == 'l' | |
| 526 | + && $this->next() == 's' | |
| 527 | + && $this->next() == 'e') { | |
| 528 | + | |
| 529 | + $this->next(); | |
| 530 | + return false; | |
| 531 | + } | |
| 532 | + break; | |
| 533 | + | |
| 534 | + case 'n': | |
| 535 | + if ($this->next() == 'u' | |
| 536 | + && $this->next() == 'l' | |
| 537 | + && $this->next() == 'l') { | |
| 538 | + | |
| 539 | + $this->next(); | |
| 540 | + return null; | |
| 541 | + } | |
| 542 | + break; | |
| 543 | + } | |
| 544 | + | |
| 545 | + $this->error('Syntax error'); | |
| 546 | + } | |
| 547 | + | |
| 548 | + /** | |
| 549 | + * generic value handler | |
| 550 | + * | |
| 551 | + * @access private | |
| 552 | + * @return mixed | |
| 553 | + */ | |
| 554 | + function val() { | |
| 555 | + $this->white(); | |
| 556 | + | |
| 557 | + switch ($this->ch) { | |
| 558 | + | |
| 559 | + case '{': | |
| 560 | + return $this->obj(); | |
| 561 | + | |
| 562 | + case '[': | |
| 563 | + return $this->arr(); | |
| 564 | + | |
| 565 | + case '<': | |
| 566 | + return $this->assoc(); | |
| 567 | + | |
| 568 | + case '"': | |
| 569 | + return $this->str(); | |
| 570 | + | |
| 571 | + case '-': | |
| 572 | + return $this->num(); | |
| 573 | + | |
| 574 | + default: | |
| 575 | + return ($this->ch >= '0' && $this->ch <= '9') ? $this->num() : $this->word(); | |
| 576 | + } | |
| 577 | + } | |
| 578 | + } | |
| 579 | + | |
| 580 | +?> | |
| 0 | 581 | \ No newline at end of file | ... | ... |