├── .bumpversion.cfg ├── .flake8 ├── .gitattributes ├── .github └── workflows │ ├── checks.yml │ ├── publish.yml │ └── tests.yml ├── .gitignore ├── LICENSE ├── README.md ├── SCHEMA.rst ├── js2xml ├── __init__.py ├── __main__.py ├── jsonlike.py ├── utils │ ├── __init__.py │ ├── objects.py │ └── vars.py └── xmlvisitor.py ├── pyproject.toml ├── setup.cfg ├── setup.py ├── test.py ├── tests ├── __init__.py ├── profile.py ├── run_profile.sh ├── samples │ ├── array001.js │ ├── array002.js │ ├── array003.js │ ├── comments001.js │ ├── const001.js │ ├── const002.js │ ├── continue001.js │ ├── continue002.js │ ├── debugger001.js │ ├── dowhile001.js │ ├── dowhile002.js │ ├── for001.js │ ├── for002.js │ ├── forin001.js │ ├── forin002.js │ ├── fullcalendar.js │ ├── fullcalendar.min.js │ ├── function001.js │ ├── function002.js │ ├── function003.js │ ├── gcal.js │ ├── get001.js │ ├── html │ │ └── map.html │ ├── ifelse001.js │ ├── jquery-ui.custom.min.js │ ├── jquery.min.js │ ├── labeled001.js │ ├── let001.js │ ├── misc001.js │ ├── misc002.js │ ├── misc003.js │ ├── misc004.js │ ├── misc005.js │ ├── misc006.js │ ├── quotes001.js │ ├── return001.js │ ├── return002.js │ ├── switch001.js │ ├── switch002.js │ ├── this001.js │ ├── this002.js │ ├── throw001.js │ ├── trycatch001.js │ ├── trycatch002.js │ ├── var001.js │ ├── wikibooks │ │ ├── complextypes-array001.js │ │ ├── complextypes-array002.js │ │ ├── complextypes-array003.js │ │ ├── complextypes-object001.js │ │ ├── complextypes-object002.js │ │ ├── functions-expr-anonymous.js │ │ ├── functions-expr-names.js │ │ ├── functions001.js │ │ ├── functions002.js │ │ ├── functions003.js │ │ ├── functions004.js │ │ ├── functions005.js │ │ ├── functions006.js │ │ ├── primitivetypes-boolean.js │ │ ├── primitivetypes-numeric.js │ │ ├── primitivetypes-string.js │ │ ├── variable001.js │ │ ├── variable002.js │ │ └── variable003.js │ └── with001.js ├── test_json.py ├── test_parse.py ├── test_schema.py ├── test_syntax.py ├── test_utils_objects.py └── test_utils_vars.py └── tox.ini /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.5.0 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | 8 | [bumpversion:file:js2xml/__init__.py] 9 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 99 3 | ignore = W503 4 | exclude = 5 | .git 6 | .tox 7 | venv* 8 | 9 | tests/test_parse.py E501 10 | 11 | # pending revision 12 | js2xml/__init__.py F403 13 | js2xml/__init__.py F401 14 | js2xml/jsonlike.py F401 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | tests/samples/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/workflows/checks.yml: -------------------------------------------------------------------------------- 1 | name: Checks 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | checks: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | include: 11 | - python-version: 3 12 | env: 13 | TOXENV: black 14 | - python-version: 3 15 | env: 16 | TOXENV: flake8 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v2 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | 26 | - name: Run check 27 | env: ${{ matrix.env }} 28 | run: | 29 | pip install -U pip 30 | pip install -U tox 31 | tox 32 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: 3 | release: 4 | types: [published] 5 | 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - name: Set up Python 3.10 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: '3.10' 17 | 18 | - name: Publish to PyPI 19 | run: | 20 | pip install --upgrade pip 21 | pip install --upgrade setuptools wheel twine 22 | python setup.py sdist bdist_wheel 23 | export TWINE_USERNAME=__token__ 24 | export TWINE_PASSWORD=${{ secrets.PYPI_TOKEN }} 25 | twine upload dist/* 26 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | tests-ubuntu: 6 | name: "Test: py${{ matrix.python-version }}, Ubuntu" 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: ['3.5', '3.6', '3.7', '3.8', '3.9', '3.10'] 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Set up Python ${{ matrix.python-version }} 16 | uses: actions/setup-python@v2 17 | with: 18 | python-version: ${{ matrix.python-version }} 19 | 20 | - name: Install tox 21 | run: pip install tox 22 | 23 | - name: Run tests 24 | run: tox -e py 25 | 26 | - name: Upload coverage report 27 | run: bash <(curl -s https://codecov.io/bash) 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # Installer logs 26 | pip-log.txt 27 | pip-delete-this-directory.txt 28 | 29 | # Unit test / coverage reports 30 | htmlcov/ 31 | .tox/ 32 | .coverage 33 | .cache 34 | nosetests.xml 35 | coverage.xml 36 | 37 | # Translations 38 | *.mo 39 | 40 | # Mr Developer 41 | .mr.developer.cfg 42 | .project 43 | .pydevproject 44 | 45 | # Rope 46 | .ropeproject 47 | 48 | # Django stuff: 49 | *.log 50 | *.pot 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Paul Tremberth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | js2xml 2 | ====== 3 | 4 | [![Tests](https://github.com/scrapinghub/js2xml/actions/workflows/tests.yml/badge.svg)](https://github.com/scrapinghub/js2xml/actions/workflows/tests.yml) 5 | [![codecov](https://codecov.io/gh/scrapinghub/js2xml/branch/master/graph/badge.svg)](https://codecov.io/gh/scrapinghub/js2xml) 6 | 7 | Convert Javascript code to an XML document. 8 | 9 | This makes it easy to extract data embedded in JavaScript code using XPath 10 | in a way more robust than just using regular expressions. 11 | 12 | 13 | # Install: 14 | 15 | You can install js2xml via [PyPI](https://pypi.python.org/pypi/js2xml): 16 | 17 | pip install js2xml 18 | 19 | 20 | # Example: 21 | 22 | ```python 23 | >>> import js2xml 24 | >>> 25 | >>> jscode = """function factorial(n) { 26 | ... if (n === 0) { 27 | ... return 1; 28 | ... } 29 | ... return n * factorial(n - 1); 30 | ... }""" 31 | >>> parsed = js2xml.parse(jscode) 32 | >>> 33 | >>> parsed.xpath("//funcdecl/@name") # extracts function name 34 | ['factorial'] 35 | >>> 36 | >>> print(js2xml.pretty_print(parsed)) # pretty-print generated XML 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | >>> 91 | ``` 92 | 93 | 94 | # Changelog 95 | 96 | ## v0.5.0 (2022-03-14) 97 | 98 | - Add official Python 3.9 and 3.10 support, drop Python 2.7 support, remove 99 | `six` dependency 100 | 101 | - Support a class call without parameters (e.g. `new Map`) 102 | 103 | - Updated the CI setup 104 | 105 | ## v0.4.0 (2020-06-04) 106 | 107 | - Add Python 3.7 and 3.8 support, drop Python 3.4 support 108 | 109 | - Use `calmjs.parse` instead of `slimit` for JavaScript parsing 110 | 111 | [`calmjs.parse`](https://github.com/calmjs/calmjs.parse) is a well-maintained 112 | fork of [`slimit`](https://github.com/rspivak/slimit) which solves some of 113 | its shortcomings, such as support for JavaScript keywords being used as 114 | object keys. 115 | 116 | However, `calmjs.parse` also introduces slight changes to the output of 117 | js2xml, making this change backward-incompatible. 118 | 119 | - Fix unicode surrogate pair handling 120 | 121 | - Code cleanup for Python 3 122 | 123 | ## v0.3.1 (2017-08-03) 124 | 125 | - Fix packaging 126 | 127 | ## v0.3.0 (2017-08-03) 128 | 129 | - Add Python 3.6 support 130 | - Deprecate `js2xml.jsonlike` 131 | - Introduce `js2xml.utils.objects` module: 132 | 133 | - `js2xml.utils.objects.make(node)`: takes a node in the js2xml-parsed 134 | tree and converts to a suitable Python object 135 | - `js2xml.utils.objects.findall(tree, types)`: used to find the 136 | top-most nodes in the js2xml-parsed tree that can be converted to 137 | a `dict`, `list`, `str`, `bool`, `int` or `float` 138 | - `js2xml.utils.objects.getall(tree, types)`: same as `.findall()` 139 | except that it converts what was found to the corresponding Python 140 | object, using `js2xml.utils.objects.make()` 141 | 142 | - Introduce `js2xml.utils.vars` module: 143 | 144 | - `js2xml.utils.vars.get_vars(tree)` can be used to turn a JS snippet 145 | into a python object where you can access JavaScript variables 146 | by name and get the parsed values 147 | 148 | ## v0.2.3 (2017-05-30) 149 | 150 | - Regenerate lextab.py and yacctab.py files with PLY 3.10 151 | - Properly set logger level to ERROR 152 | 153 | ## v0.2.2 (2016-12-01) 154 | 155 | - Include lextab.py and yacctab.py files to (hopefully) remove write 156 | permission warnings (see issue #16) 157 | - Run tests with tox (locally and on Travis CI) 158 | - Add code coverage reports (+ codecov.io for Travis CI builds) 159 | - Run tests with Python 3.6 160 | - Automatic PyPI deploys from Travis CI 161 | 162 | ## v0.2.1 (2016-06-10) 163 | 164 | - Distribute as universal wheel 165 | 166 | ## v0.2.0 (2016-06-10) 167 | 168 | - Python 3 support (tested with 3.4 and 3.5) 169 | - Use logger to suppress Yacc warnings 170 | - require PLY > 3.6 171 | - Use bumpversion for versioning 172 | - Pretty-print output is now a Unicode string 173 | 174 | ## v0.1.2 (2015-05-11) 175 | 176 | - Profiling scripts added 177 | - Updated notes with use-case, installing via pip 178 | - Force PLY 3.4 (3.6 has issues with slimit) 179 | 180 | ## v0.1.1 (2014-08-13) 181 | 182 | - Fix parsing of objects with integer keys 183 | - Fix try/catch/finally and named function expressions 184 | - Add download URL in setup file (for PyPI) 185 | 186 | ## v0.1 (2014-08-12) 187 | 188 | Initial release 189 | -------------------------------------------------------------------------------- /SCHEMA.rst: -------------------------------------------------------------------------------- 1 | js2xml XML schema by example 2 | ============================ 3 | 4 | All examples are taken from https://en.wikibooks.org/wiki/JavaScript/Variables_and_Types 5 | 6 | Variable declaration 7 | -------------------- 8 | 9 | Explicit declaration 10 | ******************** 11 | 12 | .. code:: javascript 13 | 14 | var c; 15 | 16 | becomes 17 | 18 | .. code:: xml 19 | 20 | 21 | 22 | c 23 | 24 | 25 | 26 | 27 | Initialized declaration 28 | *********************** 29 | 30 | .. code:: javascript 31 | 32 | var c = 0; 33 | 34 | becomes 35 | 36 | .. code:: xml 37 | 38 | 39 | 40 | 41 | c 42 | 43 | 0 44 | 45 | 46 | 47 | 48 | 49 | Assigning a value 50 | ***************** 51 | 52 | .. code:: javascript 53 | 54 | c = 1; 55 | 56 | becomes 57 | 58 | 59 | .. code:: xml 60 | 61 | 62 | 63 | 64 | c 65 | 66 | = 67 | 68 | 1 69 | 70 | 71 | 72 | 73 | 74 | Primitive types 75 | --------------- 76 | 77 | Boolean type 78 | ************ 79 | 80 | .. code:: javascript 81 | 82 | var mayday = false; 83 | var birthday = true; 84 | 85 | becomes 86 | 87 | .. code:: xml 88 | 89 | 90 | 91 | mayday 92 | 93 | false 94 | 95 | 96 | 97 | 98 | 99 | birthday 100 | 101 | true 102 | 103 | 104 | 105 | 106 | 107 | Numeric types 108 | ************* 109 | 110 | .. code:: javascript 111 | 112 | var sal = 20; 113 | var pal = 12.1; 114 | 115 | becomes 116 | 117 | .. code:: xml 118 | 119 | 120 | 121 | sal 122 | 123 | 20 124 | 125 | 126 | 127 | 128 | 129 | pal 130 | 131 | 12.1 132 | 133 | 134 | 135 | 136 | 137 | String type 138 | *********** 139 | 140 | .. code:: javascript 141 | 142 | var myName = "Some Name"; 143 | var myChar = 'f'; 144 | 145 | becomes 146 | 147 | .. code:: xml 148 | 149 | 150 | 151 | myName 152 | 153 | Some Name 154 | 155 | 156 | 157 | 158 | 159 | myChar 160 | 161 | f 162 | 163 | 164 | 165 | 166 | 167 | Complex types 168 | ------------- 169 | 170 | Array Type 171 | ********** 172 | 173 | Using the statement new followed by ``Array``: 174 | 175 | .. code:: javascript 176 | 177 | var myArray = new Array(0, 2, 4); 178 | var myOtherArray = new Array(); 179 | 180 | becomes 181 | 182 | .. code:: xml 183 | 184 | 185 | 186 | myArray 187 | 188 | 189 | Array 190 | 191 | 0 192 | 2 193 | 4 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | myOtherArray 202 | 203 | 204 | Array 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | Arrays can also be created with the array notation, which uses square brackets: 213 | 214 | .. code:: javascript 215 | 216 | var myArray = [0, 2, 4]; 217 | var myOtherArray = []; 218 | 219 | becomes 220 | 221 | .. code:: xml 222 | 223 | 224 | 225 | myArray 226 | 227 | 228 | 0 229 | 2 230 | 4 231 | 232 | 233 | 234 | 235 | 236 | 237 | myOtherArray 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | Arrays are accessed using the square brackets: 246 | 247 | .. code:: javascript 248 | 249 | myArray[2] = "Hello"; 250 | var text = myArray[2]; 251 | 252 | becomes 253 | 254 | .. code:: xml 255 | 256 | 257 | 258 | 259 | 260 | myArray 261 | 262 | 263 | 2 264 | 265 | 266 | 267 | = 268 | 269 | Hello 270 | 271 | 272 | 273 | 274 | text 275 | 276 | 277 | 278 | myArray 279 | 280 | 281 | 2 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | Object Types 290 | ************ 291 | 292 | Using the ``new`` operator: 293 | 294 | .. code:: javascript 295 | 296 | var myObject = new Object(); 297 | 298 | becomes 299 | 300 | .. code:: xml 301 | 302 | 303 | 304 | myObject 305 | 306 | 307 | Object 308 | 309 | 310 | 311 | 312 | 313 | 314 | Using curly braces notation: 315 | 316 | .. code:: javascript 317 | 318 | var myObject = {}; 319 | 320 | becomes 321 | 322 | .. code:: xml 323 | 324 | 325 | 326 | myObject 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | Functions 335 | --------- 336 | 337 | Function declaration 338 | ******************** 339 | 340 | .. code:: javascript 341 | 342 | function hello() { 343 | alert("Hello, World!"); 344 | } 345 | 346 | becomes 347 | 348 | .. code:: xml 349 | 350 | 351 | hello 352 | 353 | 354 | 355 | 356 | alert 357 | 358 | 359 | Hello, World! 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | With arguments 368 | ************** 369 | 370 | .. code:: javascript 371 | 372 | function hello(who) { 373 | alert("Hello, " + who + "!"); 374 | } 375 | 376 | becomes 377 | 378 | .. code:: xml 379 | 380 | 381 | hello 382 | 383 | who 384 | 385 | 386 | 387 | 388 | alert 389 | 390 | 391 | 392 | 393 | 394 | 395 | Hello, 396 | 397 | + 398 | 399 | who 400 | 401 | 402 | 403 | + 404 | 405 | ! 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | Function expression 415 | ******************* 416 | 417 | Anonymous: 418 | 419 | .. code:: javascript 420 | 421 | var square = function(number) {return number * number}; 422 | 423 | becomes 424 | 425 | .. code:: xml 426 | 427 | 428 | 429 | square 430 | 431 | 432 | 433 | 434 | number 435 | 436 | 437 | 438 | 439 | 440 | number 441 | 442 | * 443 | 444 | number 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | Named function expression: 455 | 456 | .. code:: javascript 457 | 458 | var factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)}; 459 | 460 | becomes 461 | 462 | .. code:: xml 463 | 464 | 465 | 466 | factorial 467 | 468 | 469 | fac 470 | 471 | n 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | n 480 | 481 | < 482 | 483 | 2 484 | 485 | 486 | 487 | 488 | 1 489 | 490 | 491 | 492 | 493 | n 494 | 495 | * 496 | 497 | 498 | 499 | fac 500 | 501 | 502 | 503 | 504 | n 505 | 506 | - 507 | 508 | 1 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | -------------------------------------------------------------------------------- /js2xml/__init__.py: -------------------------------------------------------------------------------- 1 | import lxml.etree 2 | from calmjs.parse.parsers.es5 import Parser 3 | 4 | from .jsonlike import * 5 | from .xmlvisitor import XmlVisitor 6 | 7 | 8 | __version__ = "0.5.0" 9 | 10 | _parser = Parser() 11 | _visitor = XmlVisitor() 12 | 13 | 14 | def parse(text, encoding="utf8", debug=False): 15 | if not isinstance(text, str): 16 | text = text.decode(encoding) 17 | tree = _parser.parse(text, debug=debug) 18 | xml = _visitor.visit(tree) 19 | return xml 20 | 21 | 22 | def pretty_print(tree): 23 | return lxml.etree.tostring(tree, pretty_print=True, encoding="unicode") 24 | -------------------------------------------------------------------------------- /js2xml/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | from argparse import ArgumentParser 4 | 5 | import js2xml 6 | 7 | 8 | def main(): 9 | ap = ArgumentParser() 10 | ap.add_argument("--debug", action="store_true") 11 | ap.add_argument("filenames", nargs="*", default=["-"]) 12 | args = ap.parse_args() 13 | 14 | for fn in args.filenames: 15 | fo = sys.stdin if fn == "-" else open(fn, "r") 16 | parsed = js2xml.parse(fo.read()) 17 | print(js2xml.pretty_print(parsed)) 18 | 19 | 20 | if __name__ == "__main__": 21 | sys.exit(main()) 22 | -------------------------------------------------------------------------------- /js2xml/jsonlike.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | from js2xml.utils.objects import findall, getall, is_instance, make 4 | 5 | 6 | _jsonlike_types = (dict, list) 7 | 8 | findall = partial(findall, types=_jsonlike_types) 9 | getall = partial(getall, types=_jsonlike_types) 10 | is_jsonlike = partial(findall, types=_jsonlike_types) 11 | make_dict = make 12 | -------------------------------------------------------------------------------- /js2xml/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrapinghub/js2xml/3d8cf7f9ec024b251e1720a206c19bad1ef2d4d3/js2xml/utils/__init__.py -------------------------------------------------------------------------------- /js2xml/utils/objects.py: -------------------------------------------------------------------------------- 1 | from lxml.etree import XPath 2 | 3 | 4 | # base built-in objects (e.g. not functions definitions within Objects or Arrays) 5 | _elements = [ 6 | "object[property or not(node())]", # an Object with a property, or an empty Object 7 | "array", 8 | "property[@name]", 9 | "identifier[parent::property]", 10 | "string", 11 | "number", 12 | "boolean", 13 | "null", 14 | "undefined", 15 | ] 16 | 17 | _tagmap = { 18 | list: ["array"], 19 | dict: ["object[property or not(node())]"], 20 | str: ["string"], 21 | int: ["number"], 22 | float: ["number"], 23 | bool: ["boolean"], 24 | None: ["null", "undefined"], 25 | } 26 | 27 | 28 | def one_of_xpath(elements): 29 | return " or ".join("self::{}".format(el) for el in elements) 30 | 31 | 32 | def is_instance_xpath(types): 33 | _tags = {m for t in types for m in _tagmap[t]} 34 | xp = """ 35 | ({mapped}) 36 | and 37 | not(./descendant::*[not( 38 | {elements} 39 | )])""".format( 40 | mapped=one_of_xpath(_tags), elements=one_of_xpath(_elements) 41 | ) 42 | return xp 43 | 44 | 45 | def _xp_all_of(types): 46 | xp = is_instance_xpath(types) 47 | return XPath( 48 | """./descendant-or-self::*[ 49 | {predicate} 50 | ]""".format( 51 | predicate=xp 52 | ) 53 | ) 54 | 55 | 56 | def make(tree): 57 | if tree.tag == "array": 58 | return [make(child) for child in tree.iterchildren()] 59 | elif tree.tag == "object": 60 | return dict([make(child) for child in tree.iterchildren()]) 61 | elif tree.tag == "property": 62 | return (tree.get("name"), make(tree.find("./*"))) 63 | elif tree.tag == "string": 64 | return tree.text 65 | elif tree.tag == "identifier": 66 | return tree.get("name") 67 | elif tree.tag == "boolean": 68 | return tree.text == "true" 69 | elif tree.tag == "number": 70 | try: 71 | return int(tree.get("value")) 72 | except ValueError: 73 | return float(tree.get("value")) 74 | elif tree.tag == "undefined": 75 | return tree.tag 76 | elif tree.tag == "null": 77 | return None 78 | 79 | 80 | def findall(tree, types=None): 81 | if types is None: 82 | types = (dict, list) 83 | candidates = _xp_all_of(types)(tree) 84 | out = [] 85 | if candidates is not None: 86 | test_set = set(candidates) 87 | for candidate in candidates: 88 | # check if ancestors are also candidates ; 89 | # if so, this is not a top-level object 90 | if set(candidate.iterancestors()) & test_set: 91 | continue 92 | out.append(candidate) 93 | return out 94 | 95 | 96 | def getall(tree, types=None): 97 | return [make(obj) for obj in findall(tree, types=types)] 98 | 99 | 100 | def is_instance(tree, types=None): 101 | if types is None: 102 | types = (dict, list) 103 | return XPath(is_instance_xpath(types))(tree) 104 | -------------------------------------------------------------------------------- /js2xml/utils/vars.py: -------------------------------------------------------------------------------- 1 | from js2xml.utils.objects import make as objects_make 2 | 3 | 4 | MAKE_OBJECTS_TYPES = ( 5 | # Types that can be handled by make_dict. 6 | "array", 7 | "object", 8 | "property", 9 | "string", 10 | "boolean", 11 | "number", 12 | "undefined", 13 | ) 14 | 15 | 16 | def make_obj(tree, ignore_tags=(), _ignore=object()): 17 | if tree.tag == "null": 18 | return 19 | 20 | if tree.tag == "identifier": 21 | return tree.get("name") 22 | 23 | if tree.tag in MAKE_OBJECTS_TYPES: 24 | return objects_make(tree) 25 | 26 | if tree.tag == "var": 27 | obj = {} 28 | children = tree.getchildren() 29 | # var can have no children if there is no value. 30 | if children: 31 | assert len(children) == 1 32 | value = make_obj(children[0], ignore_tags=ignore_tags, _ignore=_ignore) 33 | if value is not _ignore: 34 | obj[tree.attrib["name"]] = value 35 | return obj 36 | 37 | if tree.tag == "assign": 38 | assert tree.attrib["operator"] == "=", "only = operator supported" 39 | left_element = _xpath_one(tree, "left/*") 40 | right_element = _xpath_one(tree, "right/*") 41 | obj = {} 42 | name = make_varname(left_element) 43 | value = make_obj(right_element, ignore_tags=ignore_tags, _ignore=_ignore) 44 | if value is not _ignore: 45 | obj[name] = value 46 | return obj 47 | 48 | if "*" in ignore_tags or tree.tag in ignore_tags: 49 | return _ignore 50 | 51 | raise ValueError("Unknown tag: %s" % tree.tag) 52 | 53 | 54 | def make_varname(tree): 55 | """ 56 | tree 57 | """ 58 | if tree.tag == "identifier": 59 | return tree.attrib["name"] 60 | 61 | if tree.tag in ("string", "boolean"): 62 | return tree.text 63 | 64 | if tree.tag == "number": 65 | return tree.attrib["value"] 66 | 67 | if tree.tag in ("property", "object"): 68 | return make_varname(_xpath_one(tree, "*")) 69 | 70 | if tree.tag.endswith("accessor"): 71 | kind = tree.tag[: -len("accessor")] 72 | obj = make_varname(_xpath_one(tree, "object")) 73 | prop = make_varname(_xpath_one(tree, "property")) 74 | if kind == "dot": 75 | fmt = "%s.%s" 76 | elif kind == "bracket": 77 | fmt = "%s[%s]" 78 | else: 79 | raise ValueError("Unknown accessor: %s" % tree.tag) 80 | return fmt % (obj, prop) 81 | 82 | raise ValueError("Unknown tag: %s" % tree.tag) 83 | 84 | 85 | def get_vars(tree, doseq=False, ignore_tags=()): 86 | """ 87 | 88 | >>> get_vars(parse('myobj.a = 42;')) 89 | {'myobj.a': 32} 90 | >>> get_vars(parse('''otherobj = [{a: 2, 3: "test"}];''')) 91 | {'otherobj': [{'a': 2, '3': 'test'}]} 92 | 93 | """ 94 | vars = {} 95 | elements = tree.xpath('/program/*[self::var[@name] or self::assign[@operator="="]]') 96 | for el in elements: 97 | obj = make_obj(el, ignore_tags=ignore_tags) 98 | assert isinstance(obj, dict) 99 | if doseq: 100 | for key, val in obj.items(): 101 | vars.setdefault(key, []).append(val) 102 | else: 103 | vars.update(obj) 104 | return vars 105 | 106 | 107 | def _xpath_one(tree, xpath): 108 | elements = tree.xpath(xpath) 109 | if not elements: 110 | raise ValueError("no matching element") 111 | if len(elements) > 1: 112 | raise ValueError("more than one matching element") 113 | return elements[0] 114 | -------------------------------------------------------------------------------- /js2xml/xmlvisitor.py: -------------------------------------------------------------------------------- 1 | import ast as pyast 2 | import re 3 | 4 | import lxml.etree as ET 5 | from calmjs.parse import asttypes as ast 6 | from lxml.builder import E 7 | 8 | 9 | invalid_unicode_re = re.compile(u"""[\u0001-\u0008\u000b\u000e-\u001f\u007f]""", re.U) 10 | surrogate_unicode_re = re.compile(u"[\\ud800-\\udbff][\\udc00-\\udfff]", re.U) 11 | 12 | 13 | def unescape_string(input_string): 14 | input_string = invalid_unicode_re.sub(u"\ufffd", input_string) 15 | return input_string.replace(r"\/", "/") 16 | 17 | 18 | class XmlVisitor(object): 19 | def visit(self, node): 20 | method = "visit_%s" % node.__class__.__name__ 21 | return getattr(self, method, self.generic_visit)(node) 22 | 23 | def generic_visit(self, node): 24 | return "GEN: %r" % node 25 | 26 | def visit_ES5Program(self, node): 27 | program = E.program() 28 | for child in node: 29 | program.extend(self.visit(child)) 30 | return program 31 | 32 | def visit_Block(self, node): 33 | block = E.block() 34 | for child in node: 35 | block.extend(self.visit(child)) 36 | return [block] 37 | 38 | def visit_VarStatement(self, node): 39 | return [el for child in node for el in self.visit(child)] 40 | 41 | def visit_VarDecl(self, node): 42 | identifier = self.visit(node.identifier)[0] 43 | varel = E.var(name=identifier.get("name")) 44 | if node.initializer is not None: 45 | varel.extend(self.visit(node.initializer)) 46 | return [varel] 47 | 48 | def visit_VarDeclNoIn(self, node): 49 | return self.visit_VarDecl(node) 50 | 51 | def visit_Identifier(self, node): 52 | if isinstance(node.value, (int, float)): 53 | return [E.identifier(node.value)] 54 | elif isinstance(node.value, str): 55 | if node.value == "undefined": 56 | return [E.undefined()] 57 | idel = E.identifier() 58 | idel.attrib["name"] = node.value 59 | return [idel] 60 | 61 | def visit_PropIdentifier(self, node): 62 | return self.visit_Identifier(node) 63 | 64 | def visit_Assign(self, node): 65 | if node.op == ":": 66 | propname = self.visit(node.left)[0] 67 | if isinstance(node.left, ast.String): 68 | propel = E.property(name=propname.text) 69 | elif isinstance(node.left, ast.Identifier): 70 | propel = E.property(name=propname.get("name")) 71 | elif isinstance(node.left, ast.Number): 72 | propel = E.property(name=propname.get("value")) 73 | else: 74 | print(type(node.left), type(propname)) 75 | raise RuntimeError 76 | 77 | propel.extend(self.visit(node.right)) 78 | return [propel] 79 | else: 80 | assign = E.assign(operator=node.op) 81 | left = E.left() 82 | left.extend(self.visit(node.left)) 83 | right = E.right() 84 | right.extend(self.visit(node.right)) 85 | assign.append(left) 86 | assign.append(right) 87 | return [assign] 88 | 89 | def visit_GetPropAssign(self, node): 90 | propel = E.property() 91 | propel.extend(self.visit(node.prop_name)) 92 | getel = E.get() 93 | getel.append(propel) 94 | body = E.body() 95 | for el in node.elements: 96 | body.extend(self.visit(el)) 97 | getel.append(body) 98 | return [getel] 99 | 100 | def visit_SetPropAssign(self, node): 101 | propel = E.property() 102 | propel.extend(self.visit(node.prop_name)) 103 | setel = ET.Element("set") 104 | params = E.params() 105 | params.extend(self.visit(node.parameter)) 106 | body = E.body() 107 | for el in node.elements: 108 | body.extend(self.visit(el)) 109 | setel.append(body) 110 | return [setel] 111 | 112 | def visit_Number(self, node): 113 | return [E.number(value=node.value)] 114 | 115 | def visit_Comma(self, node): 116 | comma = E.comma(E.left(self.visit(node.left)[0]), E.right(self.visit(node.right)[0])) 117 | return [comma] 118 | 119 | def visit_EmptyStatement(self, node): 120 | return [E.empty(node.value)] 121 | 122 | def visit_If(self, node): 123 | ifel = ET.Element("if") 124 | if node.predicate is not None: 125 | predicate = E.predicate() 126 | predicate.extend(self.visit(node.predicate)) 127 | ifel.append(predicate) 128 | 129 | then = E.then() 130 | then.extend(self.visit(node.consequent)) 131 | ifel.append(then) 132 | 133 | if node.alternative is not None: 134 | elseel = ET.Element("else") 135 | elseel.extend(self.visit(node.alternative)) 136 | ifel.append(elseel) 137 | 138 | return [ifel] 139 | 140 | def visit_Boolean(self, node): 141 | return [E.boolean(node.value)] 142 | 143 | def visit_For(self, node): 144 | forel = ET.Element("for") 145 | if node.init is not None: 146 | initel = E.init() 147 | initel.extend(self.visit(node.init)) 148 | forel.append(initel) 149 | if node.init is None: 150 | forel.extend(E.init()) 151 | if node.cond is not None: 152 | condition = E.condition() 153 | condition.extend(self.visit(node.cond)) 154 | forel.append(condition) 155 | 156 | if node.count is not None: 157 | post = E.post() 158 | post.extend(self.visit(node.count)) 159 | forel.append(post) 160 | 161 | statement = E.statement() 162 | statement.extend(self.visit(node.statement)) 163 | forel.append(statement) 164 | return [forel] 165 | 166 | def visit_ForIn(self, node): 167 | variable = E.variable() 168 | variable.extend(self.visit(node.item)) 169 | 170 | objel = ET.Element("object") 171 | objel.extend(self.visit(node.iterable)) 172 | 173 | forel = ET.Element("forin") 174 | forel.append(variable) 175 | forel.append(objel) 176 | 177 | statement = E.statement() 178 | statement.extend(self.visit(node.statement)) 179 | forel.append(statement) 180 | 181 | return [forel] 182 | 183 | def visit_BinOp(self, node): 184 | binop = E.binaryoperation(operation=node.op) 185 | leftpart = E.left() 186 | leftpart.extend(self.visit(node.left)) 187 | binop.append(leftpart) 188 | rightpart = E.right(*self.visit(node.right)) 189 | binop.append(rightpart) 190 | return [binop] 191 | 192 | def visit_PostfixExpr(self, node): 193 | postfix = E.postfix(operation=node.op) 194 | postfix.extend(self.visit(node.value)) 195 | return [postfix] 196 | 197 | def visit_UnaryExpr(self, node): 198 | if node.op in ("delete", "void", "typeof"): 199 | unary = E.unaryoperation(operation=node.op) 200 | unary.extend(self.visit(node.value)) 201 | else: 202 | # convert things like "+3.14" and "-22" 203 | if node.op in ("-", "+") and isinstance(node.value, ast.Number): 204 | node.value.value = "%s%s" % (node.op, node.value.value) 205 | return self.visit(node.value) 206 | else: 207 | unary = E.unaryoperation(operation=node.op) 208 | unary.extend(self.visit(node.value)) 209 | return [unary] 210 | 211 | def visit_ExprStatement(self, node): 212 | return self.visit(node.expr) 213 | 214 | def visit_DoWhile(self, node): 215 | dowhile = E.dowhile() 216 | statement = E.statement() 217 | statement.extend(self.visit(node.statement)) 218 | dowhile.append(statement) 219 | whileel = ET.Element("while") 220 | whileel.extend(self.visit(node.predicate)) 221 | dowhile.append(whileel) 222 | return dowhile 223 | 224 | def visit_While(self, node): 225 | whileel = ET.Element("while") 226 | predicate = E.predicate() 227 | predicate.extend(self.visit(node.predicate)) 228 | whileel.append(predicate) 229 | statement = E.statement() 230 | statement.extend(self.visit(node.statement)) 231 | whileel.append(statement) 232 | return [whileel] 233 | 234 | def visit_Null(self, node): 235 | return [E.null()] 236 | 237 | def visit_Operator(self, node): 238 | try: 239 | return node.value 240 | except Exception: 241 | return node 242 | 243 | def visit_str(self, node): 244 | return node 245 | 246 | def visit_String(self, node): 247 | str_value = pyast.literal_eval("u" + node.value) 248 | if surrogate_unicode_re.search(str_value): 249 | in_utf16 = str_value.encode("utf16", "surrogatepass") 250 | str_value = in_utf16.decode("utf16") 251 | return [E.string(unescape_string(str_value))] 252 | 253 | def visit_Continue(self, node): 254 | continueel = ET.Element("continue") 255 | if node.identifier is not None: 256 | continueel.extend(self.visit_Identifier(node.identifier)) 257 | return [continueel] 258 | 259 | def visit_Break(self, node): 260 | breakel = ET.Element("break") 261 | if node.identifier is not None: 262 | breakel.extend(self.visit_Identifier(node.identifier)) 263 | return [breakel] 264 | 265 | def visit_Return(self, node): 266 | ret = ET.Element("return") 267 | if node.expr is not None: 268 | ret.extend(self.visit(node.expr)) 269 | return [ret] 270 | 271 | def visit_With(self, node): 272 | withel = ET.Element("with") 273 | withel.extend(self.visit(node.expr)) 274 | statement = E.statement() 275 | statement.extend(self.visit(node.statement)) 276 | withel.append(statement) 277 | return [withel] 278 | 279 | def visit_Label(self, node): 280 | identifier = self.visit(node.identifier)[0] 281 | label = E.label(name=identifier.get("name")) 282 | statement = E.statement() 283 | statement.extend(self.visit(node.statement)) 284 | label.append(statement) 285 | return [label] 286 | 287 | def visit_Switch(self, node): 288 | expression = E.expression() 289 | expression.extend(self.visit(node.expr)) 290 | switch = E.switch() 291 | switch.append(expression) 292 | for child in node.case_block.children(): 293 | switch.extend(self.visit(child)) 294 | return [switch] 295 | 296 | def visit_Case(self, node): 297 | expression = E.expression() 298 | expression.extend(self.visit(node.expr)) 299 | case = E.case() 300 | case.append(expression) 301 | for element in node.elements: 302 | case.extend(self.visit(element)) 303 | return [case] 304 | 305 | def visit_Default(self, node): 306 | default = E.default() 307 | for element in node.elements: 308 | default.extend(self.visit(element)) 309 | return [default] 310 | 311 | def visit_Throw(self, node): 312 | throwel = E.throw() 313 | throwel.extend(self.visit(node.expr)) 314 | return [throwel] 315 | 316 | def visit_Debugger(self, node): 317 | return [E.debugger(node.value)] 318 | 319 | def visit_Try(self, node): 320 | tryel = ET.Element("try") 321 | statements = E.statements() 322 | statements.extend(self.visit(node.statements)) 323 | tryel.append(statements) 324 | if node.catch is not None: 325 | tryel.extend(self.visit(node.catch)) 326 | if node.fin is not None: 327 | tryel.extend(self.visit(node.fin)) 328 | return [tryel] 329 | 330 | def visit_Catch(self, node): 331 | expression = E.expression() 332 | expression.extend(self.visit(node.identifier)) 333 | body = E.body() 334 | body.extend(self.visit(node.elements)) 335 | return [E.catch(expression, body)] 336 | 337 | def visit_Finally(self, node): 338 | finallyel = ET.Element("finally") 339 | finallyel.extend(self.visit(node.elements)) 340 | return [finallyel] 341 | 342 | def visit_FuncDecl(self, node): 343 | funcdecl = E.funcdecl() 344 | 345 | if node.identifier is not None: 346 | identifier = self.visit(node.identifier)[0] 347 | funcdecl.attrib["name"] = identifier.get("name") 348 | 349 | parameters = E.parameters() 350 | for param in node.parameters: 351 | parameters.extend(self.visit(param)) 352 | funcdecl.append(parameters) 353 | funcbody = E.body() 354 | for element in node.elements: 355 | funcbody.extend(self.visit(element)) 356 | funcdecl.append(funcbody) 357 | return [funcdecl] 358 | 359 | def visit_FuncExpr(self, node): 360 | funcexpr = E.funcexpr() 361 | if node.identifier is not None: 362 | funcexpr.extend(self.visit(node.identifier)) 363 | else: 364 | funcexpr.append(E.identifier()) 365 | parameters = E.parameters() 366 | for param in node.parameters: 367 | parameters.extend(self.visit(param)) 368 | funcexpr.append(parameters) 369 | body = E.body() 370 | for element in node.elements: 371 | body.extend(self.visit(element)) 372 | funcexpr.append(body) 373 | return [funcexpr] 374 | 375 | def visit_Conditional(self, node): 376 | conditional = E.conditional() 377 | 378 | condition = E.condition() 379 | condition.extend(self.visit(node.predicate)) 380 | 381 | value1 = E.value1() 382 | value1.extend(self.visit(node.consequent)) 383 | value2 = E.value2() 384 | value2.extend(self.visit(node.alternative)) 385 | 386 | conditional.append(condition) 387 | conditional.append(value1) 388 | conditional.append(value2) 389 | 390 | return [conditional] 391 | 392 | def visit_Regex(self, node): 393 | return [E.regex(node.value)] 394 | 395 | def visit_NewExpr(self, node): 396 | newel = E.new() 397 | newel.extend(self.visit(node.identifier)) 398 | arguments = E.arguments() 399 | for arg in node.args or (): 400 | arguments.extend(self.visit(arg)) 401 | newel.append(arguments) 402 | return [newel] 403 | 404 | def visit_DotAccessor(self, node): 405 | dot = E.dotaccessor() 406 | objel = E.object() 407 | objel.extend(self.visit(node.node)) 408 | propel = E.property() 409 | propel.extend(self.visit(node.identifier)) 410 | dot.append(objel) 411 | dot.append(propel) 412 | return [dot] 413 | 414 | def visit_BracketAccessor(self, node): 415 | objel = E.object() 416 | objel.extend(self.visit(node.node)) 417 | propel = E.property() 418 | propel.extend(self.visit(node.expr)) 419 | bracket = E.bracketaccessor(objel, propel) 420 | return [bracket] 421 | 422 | def visit_FunctionCall(self, node): 423 | function = E.function() 424 | function.extend(self.visit(node.identifier)) 425 | funccall = E.functioncall(function) 426 | arguments = E.arguments() 427 | for arg in node.args: 428 | arguments.extend(self.visit(arg)) 429 | funccall.append(arguments) 430 | return [funccall] 431 | 432 | def visit_GroupingOp(self, node): 433 | op = E.groupingoperator() 434 | op.extend(self.visit(node.expr)) 435 | return [op] 436 | 437 | def visit_Object(self, node): 438 | obj = ET.Element("object") 439 | for prop in node.properties: 440 | obj.extend(self.visit(prop)) 441 | return [obj] 442 | 443 | def visit_Array(self, node): 444 | array = E.array() 445 | for index, item in enumerate(node.items): 446 | if isinstance(item, ast.Elision): 447 | for _ in range(item.value): 448 | array.append(E.undefined()) 449 | else: 450 | array.extend(self.visit(item)) 451 | return [array] 452 | 453 | def visit_This(self, node): 454 | return [E.identifier("this")] 455 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 99 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup, find_packages 4 | 5 | 6 | setup( 7 | name="js2xml", 8 | version="0.5.0", 9 | description="Convert Javascript code to XML document", 10 | long_description=""" 11 | ====== 12 | js2xml 13 | ====== 14 | 15 | js2xml is a simple helper to parse Javascript code 16 | by representing a parse tree in XML. 17 | 18 | You can then use XPath for example to find interesting 19 | bits in Javascript instructions (strings, IDs, function parameters...) 20 | 21 | """, 22 | author="Paul Tremberth", 23 | author_email="paul.tremberth@gmail.com", 24 | packages=find_packages(exclude=["tests"]), 25 | requires=["calmjs.parse", "lxml"], 26 | install_requires=[ 27 | "calmjs.parse", 28 | "lxml", 29 | ], 30 | python_requires=">=3.5", 31 | classifiers=[ 32 | "Topic :: Software Development :: Libraries :: Python Modules", 33 | "Topic :: Text Processing :: Markup :: XML", 34 | "Operating System :: POSIX :: Linux", 35 | "Programming Language :: Python :: 3.5", 36 | "Programming Language :: Python :: 3.6", 37 | "Programming Language :: Python :: 3.7", 38 | "Programming Language :: Python :: 3.8", 39 | "Programming Language :: Python :: 3.9", 40 | "Programming Language :: Python :: 3.10", 41 | "License :: OSI Approved :: MIT License", 42 | "Development Status :: 3 - Alpha", 43 | "Intended Audience :: Developers", 44 | ], 45 | url="https://github.com/scrapinghub/js2xml", 46 | download_url="https://github.com/scrapinghub/js2xml/archive/v0.5.0.tar.gz", 47 | ) 48 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import js2xml 4 | 5 | 6 | text = sys.stdin.read() 7 | if not text: 8 | text = """ 9 | var x = { 10 | "key1": "value1", 11 | "key2": "value2", 12 | "key3": 1, 13 | "key4": false 14 | }; 15 | """ 16 | print(text) 17 | tree = js2xml.parse(text, debug=False) 18 | print(js2xml.pretty_print(tree)) 19 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrapinghub/js2xml/3d8cf7f9ec024b251e1720a206c19bad1ef2d4d3/tests/__init__.py -------------------------------------------------------------------------------- /tests/profile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import js2xml 5 | import os 6 | 7 | TEST_DIR = os.path.dirname(__file__) 8 | 9 | files = [ 10 | os.path.join(TEST_DIR, "samples/fullcalendar.js"), 11 | os.path.join(TEST_DIR, "samples/fullcalendar.min.js"), 12 | os.path.join(TEST_DIR, "samples/jquery.min.js"), 13 | ] 14 | for filename in files: 15 | with open(filename) as f: 16 | jscode = f.read() 17 | 18 | tree = js2xml.parse(jscode) 19 | -------------------------------------------------------------------------------- /tests/run_profile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | abort() { 4 | echo "$*"; exit 1; 5 | } 6 | 7 | usage() { 8 | abort """Usage: $(basename $0) [PROFILE_NAME] 9 | 10 | Typical profiling workflow is: 11 | 12 | 1) change code in profile.py 13 | 2) (re-)run profiler passing a different name to use for file 14 | """ 15 | } 16 | 17 | require() { 18 | type $1 >/dev/null 2>/dev/null 19 | } 20 | 21 | realpath(){ 22 | if echo "$1" | grep -q '^/' 23 | then 24 | echo "$1" 25 | else 26 | echo $(cd $(dirname "$1") && pwd)/$(basename "$1") 27 | fi 28 | } 29 | 30 | while [ "${1#-}" != "$1" ]; do 31 | case "$1" in 32 | -h) usage;; 33 | *) usage;; 34 | esac 35 | shift 36 | done 37 | profile_name="${1:-output}" 38 | 39 | require dot || abort "Please install graphviz first: sudo apt-get install graphviz" 40 | require gprof2dot || abort "Please install gprof2dot first: pip install gprof2dot" 41 | 42 | # stop script if some command fails 43 | set -e 44 | 45 | proj_dir=$(dirname $(dirname $(realpath $0))) 46 | 47 | profile_py=$proj_dir/tests/profile.py 48 | 49 | file_prefix=/tmp/profile-${profile_name} 50 | 51 | echo "Running profile.py (output on ${file_prefix}.pstats)..." 52 | PYTHONPATH=$proj_dir python -m cProfile -o ${file_prefix}.pstats $profile_py 53 | 54 | echo "Generating image with profiling results..." 55 | gprof2dot -f pstats ${file_prefix}.pstats | dot -Tpng -o ${file_prefix}.png 56 | 57 | echo "Generated ${file_prefix}.png" 58 | -------------------------------------------------------------------------------- /tests/samples/array001.js: -------------------------------------------------------------------------------- 1 | var my_array = new Array(); 2 | for (i = 0; i < 10; i++) { 3 | my_array[i] = i; 4 | } 5 | x = my_array[4]; 6 | document.write(x); 7 | -------------------------------------------------------------------------------- /tests/samples/array002.js: -------------------------------------------------------------------------------- 1 | var arr = new Array(10); 2 | document.write(arr.length) 3 | 4 | // Output: 10 5 | 6 | // Don't do this 7 | var arr = new Array(-1); 8 | arr = new Array(1.50); 9 | -------------------------------------------------------------------------------- /tests/samples/array003.js: -------------------------------------------------------------------------------- 1 | var my_array = new Array(); 2 | for (i = 0; i < 10; i++) { 3 | my_array[i].value = i; 4 | } 5 | x = my_array[4]; 6 | document.write(x); 7 | -------------------------------------------------------------------------------- /tests/samples/comments001.js: -------------------------------------------------------------------------------- 1 | /* This is a multiline comment that 2 | can span as many lines as necessary. */ 3 | function myfunction(arg1, arg2){ 4 | var r; 5 | // This is a single line comment. 6 | r = arg1 + arg2 7 | return(r); 8 | } 9 | -------------------------------------------------------------------------------- /tests/samples/const001.js: -------------------------------------------------------------------------------- 1 | var c = 10; 2 | { 3 | const c = 2; 4 | // At this point, c = 2. 5 | } 6 | // At this point, c = 10. 7 | 8 | // Additional ways to declare a variable using const. 9 | const name = "Thomas Jefferson"; 10 | const answer = 42, numpages = 10; 11 | const myarray = new Array(); 12 | -------------------------------------------------------------------------------- /tests/samples/const002.js: -------------------------------------------------------------------------------- 1 | var const a = 7; 2 | console.log("a is " + a + "."); 3 | -------------------------------------------------------------------------------- /tests/samples/continue001.js: -------------------------------------------------------------------------------- 1 | for (var i = 1; i < 10; i++) { 2 | if (i < 5) { 3 | continue; 4 | } 5 | document.write (i); 6 | document.write (" "); 7 | } 8 | -------------------------------------------------------------------------------- /tests/samples/continue002.js: -------------------------------------------------------------------------------- 1 | Outer: 2 | for (var i = 1; i <= 10; i++) { 3 | document.write ("
"); 4 | document.write ("i: " + i); 5 | document.write (" j: "); 6 | 7 | Inner: 8 | for (var j = 21; j <= 30; j++) { 9 | if (j == 24) { 10 | continue Inner; 11 | } 12 | document.write (j + " "); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/samples/debugger001.js: -------------------------------------------------------------------------------- 1 | for(i = 1; i<5; i++) { 2 | // Print i to the Output window. 3 | Debug.write("loop index is " + i); 4 | // Wait for user to resume. 5 | debugger 6 | } 7 | -------------------------------------------------------------------------------- /tests/samples/dowhile001.js: -------------------------------------------------------------------------------- 1 | var i = 0; 2 | do { 3 | document.write(i + " "); 4 | i++; 5 | } while (i < 10); 6 | -------------------------------------------------------------------------------- /tests/samples/dowhile002.js: -------------------------------------------------------------------------------- 1 | var i = 10; 2 | do { 3 | document.write(i + " "); 4 | i++; 5 | } while (i < 10); 6 | -------------------------------------------------------------------------------- /tests/samples/for001.js: -------------------------------------------------------------------------------- 1 | // i is set to 0 at the start and is incremented by 1 at the 2 | // end of each iteration. 3 | // The loop terminates when i is not less than or equal to 4 | // 9 before a loop iteration. 5 | for (var i = 0; i <= 9; i++) { 6 | document.write (i); 7 | document.write (" "); 8 | } 9 | 10 | // Output: 0 1 2 3 4 5 6 7 8 9 11 | -------------------------------------------------------------------------------- /tests/samples/for002.js: -------------------------------------------------------------------------------- 1 | var j = 0; 2 | for (;;) { 3 | if (j >= 5) { 4 | break; 5 | } 6 | j++; 7 | document.write (j + " "); 8 | } 9 | 10 | // Output: 1 2 3 4 5 11 | -------------------------------------------------------------------------------- /tests/samples/forin001.js: -------------------------------------------------------------------------------- 1 | // Initialize object. 2 | a = {"a" : "Athens" , "b" : "Belgrade", "c" : "Cairo"} 3 | 4 | // Iterate over the properties. 5 | var s = "" 6 | for (var key in a) { 7 | s += key + ": " + a[key]; 8 | s += "
"; 9 | } 10 | document.write (s); 11 | 12 | // Output: 13 | // a: Athens 14 | // b: Belgrade 15 | // c: Cairo 16 | -------------------------------------------------------------------------------- /tests/samples/forin002.js: -------------------------------------------------------------------------------- 1 | // Initialize the array. 2 | var arr = new Array("zero","one","two"); 3 | 4 | // Add a few expando properties to the array. 5 | arr["orange"] = "fruit"; 6 | arr["carrot"] = "vegetable"; 7 | 8 | // Iterate over the properties and elements. 9 | var s = ""; 10 | for (var key in arr) { 11 | s += key + ": " + arr[key]; 12 | s += "
"; 13 | } 14 | 15 | document.write (s); 16 | 17 | // Output: 18 | // 0: zero 19 | // 1: one 20 | // 2: two 21 | // orange: fruit 22 | // carrot: vegetable 23 | -------------------------------------------------------------------------------- /tests/samples/function001.js: -------------------------------------------------------------------------------- 1 | function myfunction (arg1, arg2) { 2 | var r = arg1 * arg2; 3 | return(r); 4 | } 5 | -------------------------------------------------------------------------------- /tests/samples/function002.js: -------------------------------------------------------------------------------- 1 | function AddFive(x) { 2 | return x + 5; 3 | } 4 | 5 | function AddTen(x) { 6 | return x + 10; 7 | } 8 | 9 | var condition = false; 10 | 11 | var MyFunc; 12 | if (condition) { 13 | MyFunc = AddFive; 14 | } 15 | else { 16 | MyFunc = AddTen; 17 | } 18 | 19 | var result = MyFunc(123); 20 | // Output: 133 21 | -------------------------------------------------------------------------------- /tests/samples/function003.js: -------------------------------------------------------------------------------- 1 | function (x) { 2 | return x + 5; 3 | } 4 | -------------------------------------------------------------------------------- /tests/samples/gcal.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * FullCalendar v1.6.4 Google Calendar Plugin 3 | * Docs & License: http://arshaw.com/fullcalendar/ 4 | * (c) 2013 Adam Shaw 5 | */ 6 | 7 | (function($) { 8 | 9 | 10 | var fc = $.fullCalendar; 11 | var formatDate = fc.formatDate; 12 | var parseISO8601 = fc.parseISO8601; 13 | var addDays = fc.addDays; 14 | var applyAll = fc.applyAll; 15 | 16 | 17 | fc.sourceNormalizers.push(function(sourceOptions) { 18 | if (sourceOptions.dataType == 'gcal' || 19 | sourceOptions.dataType === undefined && 20 | (sourceOptions.url || '').match(/^(http|https):\/\/www.google.com\/calendar\/feeds\//)) { 21 | sourceOptions.dataType = 'gcal'; 22 | if (sourceOptions.editable === undefined) { 23 | sourceOptions.editable = false; 24 | } 25 | } 26 | }); 27 | 28 | 29 | fc.sourceFetchers.push(function(sourceOptions, start, end) { 30 | if (sourceOptions.dataType == 'gcal') { 31 | return transformOptions(sourceOptions, start, end); 32 | } 33 | }); 34 | 35 | 36 | function transformOptions(sourceOptions, start, end) { 37 | 38 | var success = sourceOptions.success; 39 | var data = $.extend({}, sourceOptions.data || {}, { 40 | 'start-min': formatDate(start, 'u'), 41 | 'start-max': formatDate(end, 'u'), 42 | 'singleevents': true, 43 | 'max-results': 9999 44 | }); 45 | 46 | var ctz = sourceOptions.currentTimezone; 47 | if (ctz) { 48 | data.ctz = ctz = ctz.replace(' ', '_'); 49 | } 50 | 51 | return $.extend({}, sourceOptions, { 52 | url: sourceOptions.url.replace(/\/basic$/, '/full') + '?alt=json-in-script&callback=?', 53 | dataType: 'jsonp', 54 | data: data, 55 | startParam: false, 56 | endParam: false, 57 | success: function(data) { 58 | var events = []; 59 | if (data.feed.entry) { 60 | $.each(data.feed.entry, function(i, entry) { 61 | var startStr = entry['gd$when'][0]['startTime']; 62 | var start = parseISO8601(startStr, true); 63 | var end = parseISO8601(entry['gd$when'][0]['endTime'], true); 64 | var allDay = startStr.indexOf('T') == -1; 65 | var url; 66 | $.each(entry.link, function(i, link) { 67 | if (link.type == 'text/html') { 68 | url = link.href; 69 | if (ctz) { 70 | url += (url.indexOf('?') == -1 ? '?' : '&') + 'ctz=' + ctz; 71 | } 72 | } 73 | }); 74 | if (allDay) { 75 | addDays(end, -1); // make inclusive 76 | } 77 | events.push({ 78 | id: entry['gCal$uid']['value'], 79 | title: entry['title']['$t'], 80 | url: url, 81 | start: start, 82 | end: end, 83 | allDay: allDay, 84 | location: entry['gd$where'][0]['valueString'], 85 | description: entry['content']['$t'] 86 | }); 87 | }); 88 | } 89 | var args = [events].concat(Array.prototype.slice.call(arguments, 1)); 90 | var res = applyAll(success, this, args); 91 | if ($.isArray(res)) { 92 | return res; 93 | } 94 | return events; 95 | } 96 | }); 97 | 98 | } 99 | 100 | 101 | // legacy 102 | fc.gcalFeed = function(url, sourceOptions) { 103 | return $.extend({}, sourceOptions, { url: url, dataType: 'gcal' }); 104 | }; 105 | 106 | 107 | })(jQuery); 108 | -------------------------------------------------------------------------------- /tests/samples/get001.js: -------------------------------------------------------------------------------- 1 | var log = ['test']; 2 | var obj = { 3 | get latest () { 4 | if (log.length == 0) return undefined; 5 | return log[log.length - 1] 6 | } 7 | } 8 | console.log (obj.latest); // Will return "test". 9 | -------------------------------------------------------------------------------- /tests/samples/html/map.html: -------------------------------------------------------------------------------- 1 | Scrapinghub team 4 | 5 | 26 | <xmp>.</xmp> 27 | <div id=gbar><nobr><a target=_blank class=gb1 href="https://www.google.fr/webhp">Recherche</a> <a target=_blank class=gb1 href="http://www.google.fr/imghp?hl=fr">Images</a> <a target=_blank class=gb1 href="https://maps.google.fr/maps?hl=fr">Maps</a> <a target=_blank class=gb1 href="https://play.google.com/?hl=fr">Play</a> <a target=_blank class=gb1 href="https://www.youtube.com/">YouTube</a> <a target=_blank class=gb1 href="https://news.google.fr/nwshp?hl=fr">Actualités</a> <a target=_blank class=gb1 href="https://mail.google.com/mail/">Gmail</a> <a target=_blank class=gb1 href="https://drive.google.com/">Drive</a> <a target=_blank class=gb1 style="text-decoration:none" href="http://www.google.fr/intl/fr/options/"><u>Plus</u> &raquo;</a></nobr></div><div class=gbh style=left:0></div><div class=gbh style=right:0></div><div id="oneGoogleNextElement" style="clear:both"></div><div id="docs-editor" class="HB1eCd-L9AdLc c4YZDc"><div id="map-container" tabindex=0><div id="butterbar-container"><div id='butterbar-wrap'><div class="tk3N6e-cXJiPb tk3N6e-cXJiPb-GMvhG tk3N6e-cXJiPb-TSZdd" aria-live="assertive" aria-atomic="true"><span class="X3SwIb-bN97Pc" contentid="unsupported-browser">You are running an unsupported browser, some features may not work.&nbsp;<a href="http://support.google.com/mapsengine/?hl=en&p=lite_browsers" target="_blank"><span class="X3SwIb-NXKGoe">Learn more</span></a></span></div></div></div><div id="zoomToolbar" class="VIpgJd-INgbqf"><div id="zoomInButton" class="r08add-LgbsSe VIpgJd-INgbqf-LgbsSe VIpgJd-TzA9Ye-eEGnhe"><div class="VIpgJd-INgbqf-LgbsSe-n0tgWb-Q4BLdf VIpgJd-TzA9Ye-eEGnhe"><div class="VIpgJd-INgbqf-LgbsSe-SmKAyb-Q4BLdf VIpgJd-TzA9Ye-eEGnhe"><div class="HB1eCd-Bz112c VIpgJd-TzA9Ye-eEGnhe "><div class="Ensave-nJjxad-bEDTcc">&nbsp;</div></div></div></div></div><div id="zoomOutButton" class="E6eRQd-LgbsSe VIpgJd-INgbqf-LgbsSe VIpgJd-TzA9Ye-eEGnhe"><div class="VIpgJd-INgbqf-LgbsSe-n0tgWb-Q4BLdf VIpgJd-TzA9Ye-eEGnhe"><div class="VIpgJd-INgbqf-LgbsSe-SmKAyb-Q4BLdf VIpgJd-TzA9Ye-eEGnhe"><div class="HB1eCd-Bz112c VIpgJd-TzA9Ye-eEGnhe "><div class="Ensave-nJjxad-m9bMae">&nbsp;</div></div></div></div></div></div><div id="map-pane"></div></div></div><script type="text/javascript"> 28 | function _DumpException(e) { 29 | if (window.console) { 30 | window.console.error(e.stack); 31 | } 32 | } 33 | var _pageData = {"version":"8ak-UNEpyAE.fr.","viewportJson":"[,[-112.033531665802,139.69170639999993,64.5333333,-34.91998290805409]\n]\n","pathPrefix":"/map","language":"fr","countryCode":"FR","mapdataJson":"[[\"zJncfB8N3x1E.k9O5ry_AkXs8\",\"Scrapinghub team\",\"Scrapinghubbers around the world!\",,[\"zJncfB8N3x1E.k2j6mzyMNKwk\",\"zJncfB8N3x1E.kRGUjVhV496Y\"]\n,[,1384546570639,,1398795917675]\n,1,[\"Light Political\",\"[{\\\"featureType\\\":\\\"water\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"on\\\"},{\\\"saturation\\\":2},{\\\"hue\\\":\\\"#004cff\\\"},{\\\"lightness\\\":40}]},{\\\"featureType\\\":\\\"administrative\\\",\\\"elementType\\\":\\\"geometry\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"off\\\"}]},{\\\"featureType\\\":\\\"landscape\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"on\\\"},{\\\"lightness\\\":45}]},{\\\"featureType\\\":\\\"transit\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"off\\\"}]},{\\\"featureType\\\":\\\"poi.government\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"off\\\"}]},{\\\"featureType\\\":\\\"poi\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"off\\\"}]},{\\\"featureType\\\":\\\"road\\\",\\\"elementType\\\":\\\"labels\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"off\\\"}]},{\\\"featureType\\\":\\\"road\\\",\\\"elementType\\\":\\\"geometry\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"on\\\"},{\\\"saturation\\\":-99},{\\\"lightness\\\":60}]},{\\\"featureType\\\":\\\"administrative.country\\\",\\\"elementType\\\":\\\"geometry\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"on\\\"},{\\\"lightness\\\":50}]},{\\\"featureType\\\":\\\"administrative.province\\\",\\\"elementType\\\":\\\"geometry\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"on\\\"},{\\\"lightness\\\":50}]},{\\\"featureType\\\":\\\"administrative.country\\\",\\\"elementType\\\":\\\"labels\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"on\\\"},{\\\"lightness\\\":50}]},{\\\"featureType\\\":\\\"administrative.locality\\\",\\\"elementType\\\":\\\"labels\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"on\\\"},{\\\"lightness\\\":60}]},{\\\"featureType\\\":\\\"administrative.neighborhood\\\",\\\"elementType\\\":\\\"labels\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"on\\\"},{\\\"lightness\\\":65}]},{\\\"featureType\\\":\\\"administrative.province\\\",\\\"elementType\\\":\\\"label\\\",\\\"stylers\\\":[{\\\"visibility\\\":\\\"on\\\"},{\\\"lightness\\\":55}]}]\"]\n,[]\n,0]\n,[[\"zJncfB8N3x1E.k2j6mzyMNKwk\",\"Team Members\",[]\n,[\"Style1\"]\n,[\"zJncfB8N3x1E.kQ1Z2LevGmk8\"]\n,[\"name\",\"description\",,[\"gme_geometry_\",\"place_ref\",\"feature_order\",\"gx_metafeatureid\",\"gx_routeinfo\",\"gx_image_links\"]\n]\n,[5,[4000,4000,0.3,\"FF7C2F\",\"FF7C2F\",,\"FF7C2F\",503]\n,\"name\",,[]\n,[]\n,[0,1]\n,,,[\"name\",\"gx_image_links\",\"description\"]\n,,,,,[]\n]\n,[,1384546570639,,1398795917675]\n,\"o:ANVwnM36T7TqEbrN1Bd5StW__e78Hr8j-8fqiN-XKWd9kZuYh2KsUDYNM95aLCG8QzcjPApfJRB1CMZlgKz49asOO3xwkiMqs-WcPQb9NQ-H5Q5CZ4Kkz_PK_6yQp6RZ5g\",4]\n,[\"zJncfB8N3x1E.kRGUjVhV496Y\",\"Untitled layer\",[]\n,[\"Style2\"]\n,[\"zJncfB8N3x1E.kxBWX97j7MBk\"]\n,[\"name\",\"description\",,[\"gme_geometry_\",\"place_ref\",\"feature_order\",\"gx_metafeatureid\",\"gx_routeinfo\",\"gx_image_links\"]\n]\n,[6,[4000,4000,0.3,\"000000\",\"FF8277\",,\"000000\",503]\n,,,[]\n,[]\n,,,,[\"name\",\"gx_image_links\",\"description\"]\n,,,,,[]\n]\n,[,1398795881610,,1398795917675]\n,\"o:ANVwnM3CDTbofCQVIx2snQFjojv8cdvu_4_jIagUntnAtIKYn7oDewf2LFu_v4KR54hprE86n4Ial7hIXkUGQ6kbc7AuOHfAYa615U34P8Sx0YIgH5tROW5FclyQ4_G5rw\",4]\n]\n,[[\"zJncfB8N3x1E.kQ1Z2LevGmk8\",,[[[\"gme_geometry_\",6]\n,[\"name\",3]\n,[\"description\",3]\n,[\"place_ref\",3]\n,[\"feature_order\",3]\n,[\"gx_metafeatureid\",3]\n,[\"gx_routeinfo\",3]\n,[\"gx_image_links\",3]\n]\n]\n,[]\n,,,,,\"gme_geometry_\",\"place_ref\",,,,,\"feature_order\",,\"gx_metafeatureid\",\"gx_routeinfo\",\"gx_image_links\",[\"place_ref\",\"feature_order\",\"gx_metafeatureid\",\"gx_routeinfo\",\"gx_image_links\"]\n,,[]\n]\n,[\"zJncfB8N3x1E.kxBWX97j7MBk\",,[[[\"gme_geometry_\",6]\n,[\"name\",3]\n,[\"description\",3]\n,[\"place_ref\",3]\n,[\"feature_order\",3]\n,[\"gx_metafeatureid\",3]\n,[\"gx_routeinfo\",3]\n,[\"gx_image_links\",3]\n]\n]\n,[]\n,,,,,\"gme_geometry_\",\"place_ref\",,,,,\"feature_order\",,\"gx_metafeatureid\",\"gx_routeinfo\",\"gx_image_links\",[\"place_ref\",\"feature_order\",\"gx_metafeatureid\",\"gx_routeinfo\",\"gx_image_links\"]\n,,[]\n]\n]\n,[[\"Style1\",,[[,[[503,,,[\"FF7C2F\",1.0]\n,[[\"name\"]\n,\"\",11.0,[\"000000\",1.0]\n,[\"FFFFFF\",1.0]\n,[]\n,0]\n,[[\"name\",\"gx_image_links\",\"description\"]\n,\"{name|title}\\u003cbr\\u003e{gx_image_links|images}\\u003cbr\\u003e{description|paragraph}\\u003cbr\\u003e\"]\n]\n,[4000,[\"FF7C2F\",1.0]\n,[[\"name\"]\n,\"\",11.0,[\"000000\",1.0]\n,[\"FFFFFF\",1.0]\n,[]\n,0]\n,[[\"name\",\"gx_image_links\",\"description\"]\n,\"{name|title}\\u003cbr\\u003e{gx_image_links|images}\\u003cbr\\u003e{description|paragraph}\\u003cbr\\u003e\"]\n,,,[]\n]\n,[[\"FF7C2F\",0.2980392156862745]\n,4000,[\"FF7C2F\",1.0]\n,[[\"name\"]\n,\"\",11.0,[\"000000\",1.0]\n,[\"FFFFFF\",1.0]\n,[]\n,0]\n,[[\"name\",\"gx_image_links\",\"description\"]\n,\"{name|title}\\u003cbr\\u003e{gx_image_links|images}\\u003cbr\\u003e{description|paragraph}\\u003cbr\\u003e\"]\n,0]\n]\n]\n]\n]\n,[\"Style2\",,[[,[[503,,,[\"FF8277\",1.0]\n,,[[\"name\",\"gx_image_links\",\"description\"]\n,\"{name|title}\\u003cbr\\u003e{gx_image_links|images}\\u003cbr\\u003e{description|paragraph}\\u003cbr\\u003e\"]\n]\n,[4000,[\"000000\",1.0]\n,,[[\"name\",\"gx_image_links\",\"description\"]\n,\"{name|title}\\u003cbr\\u003e{gx_image_links|images}\\u003cbr\\u003e{description|paragraph}\\u003cbr\\u003e\"]\n,,,[]\n]\n,[[\"000000\",0.2980392156862745]\n,4000,[\"000000\",1.0]\n,,[[\"name\",\"gx_image_links\",\"description\"]\n,\"{name|title}\\u003cbr\\u003e{gx_image_links|images}\\u003cbr\\u003e{description|paragraph}\\u003cbr\\u003e\"]\n,0]\n]\n]\n]\n]\n]\n,[[\"zJncfB8N3x1E.kQ1Z2LevGmk8\",[,[-112.033531665802,139.69170639999993,64.5333333,-34.91998290805409]\n]\n,[[\"5c9ecc034_a1\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-34.905783106306444,-56.13763689994812,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Pablo Hoffman\",,,0]\n,[\"description\",,,,\"Director, Co-Founder\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,\"http://en.gravatar.com/userimage/9551557/66c172620df70ccc797793f6d303e51a.jpg?size\\u003d200\",,,0]\n]\n]\n,[\"5c9f7a9ae_a2\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-34.88789349171672,-56.154019832611084,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Natalia Tirado\",,,0]\n,[\"description\",,,,\"Project Manager\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,\"http://goo.gl/w0FOhW\",,,0]\n]\n]\n,[\"5c9fac612_a3\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-34.86259561970002,-56.12916631111449,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Rocio Aramberri\",,,0]\n,[\"description\",,,,\"Software Developer\",,,0]\n,[\"place_ref\",,,,\"CpQBjgAAABt8oUI8fF-lwN48-rQhvZD2iqqpXBNh8r6dLCMyb2l08ScW-zSJbOwU62sNNFrZvqOuFyN78kNvxQPv5QkI9odZMTaK8wmxO1WOIDBzhLPp72rAIK8cGGwP7sVQW3WsxVGTiGHGHk4jX-sQD4Lalrq48UHKIOX3IsbwROik4AmrcZh9xbdc8uA_IgWtc3sMoRIQzTZEidBJYMoNyx035hbDwBoUi4jqDOSGzxFnBTMPX3e7ZyBjRrk|fc12e1da5b47baa89e26222247cf231c0cbdbe38|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,\"http://schegel.net/static/foto.jpg\",,,0]\n]\n]\n,[\"5c9fccd8b_a4\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-34.891668850311525,-56.10536456108093,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Andrés Moreira\",,,0]\n,[\"description\",,,,\"Project Manager\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5ca0a07a5_a6\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-22.906597758919766,-47.060387134552,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Ricardo Panaggio\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,\"https://pbs.twimg.com/profile_images/378800000140739184/9d77504e46e21f96159f33d36911e0e4.jpeg\",,,0]\n]\n]\n,[\"5ca10f2bc_a8\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-34.652251622627915,-54.189666509628296,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Daniel Graña\",,,0]\n,[\"description\",,,,\"Software Developer\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5ca184e03_a9\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-34.850667445905735,-55.986682176589966,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Martin Olveyra\\n\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5cbb776fe_a5\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[56.84026427909907,60.63652753829956,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Mikhail Korobov\\n\",,,0]\n,[\"description\",,,,\"Software Developer\\n\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d7c33da7_b10\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-34.82166012434302,-55.94654560089111,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Lucia Cawen\",,,0]\n,[\"description\",,,,\"Executive Assistant\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,\"https://www.facebook.com/photo.php?fbid\\u003d528135727204696\\u0026set\\u003dpb.100000248205048.-2207520000.1384786734.\\u0026type\\u003d3\\u0026theater\",,,0]\n]\n]\n,[\"5d7c5f801_b11\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-34.91998290805409,-56.163954734802246,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Marcos Campal\",,,0]\n,[\"description\",,,,\"Software Developer\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d83d93ec_b13\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-34.88056228833752,-56.17363214492798,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Nicolás Ramírez\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d84a905d_b15\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-34.87209494681131,-56.12321197986597,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Ismael Carnales\",,,0]\n,[\"description\",,,,\"Sofware Developer\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d8e025cc_b12\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[48.894772166723506,2.100834846496582,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Paul Tremberth\",,,0]\n,[\"description\",,,,\"Sofware Developer\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d8e5c830_b20\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-34.89798712759517,-56.14286184310913,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Luar Roji\",,,0]\n,[\"description\",,,,\"SysAdmin / Developer\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d919fb25_b13\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[47.97103377353228,37.69810438156128,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Olga Chebotaryova\\n\",,,0]\n,[\"description\",,,,\"Software Developer\\n\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d91f77c9_b14\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[46.44238163923079,30.714833736419678,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Illarion Kovalchuk\\n\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d9302f48_b18\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[50.064150413737764,19.994666576385498,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Jan Wrobel\\n\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d94a5ae8_b17\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[50.429476939759034,30.541541576385498,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Pavel Kuripko\\n\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d981c07e_b19\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[46.44238163923079,31.110341548919678,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Sergey Sinkovskiy\",,,0]\n,[\"description\",,,,\"Odessa\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d9c36284_b20\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[40.97246219535385,29.120700359344482,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Sinan Nalkaya\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d9c38b6f_b14\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[24.854337871583358,67.01055854558945,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Umair Ashraf\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5d9ca4bed_b15\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[31.503574420868407,74.4429087638855,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Akhter Wahab\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5da4eb3c7_b16\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[13.490400943502696,100.51483869552612,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Opp Lieamsiriwong\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5dce0b81a_b21\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[51.878650192936654,-8.393189907073975,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Shane Evans\",,,0]\n,[\"description\",,,,\"Director and Co-Founder\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,\"https://lh6.googleusercontent.com/-ltucWXaSfuw/UBtT9ZP5zHI/AAAAAAAAAFw/HJ0mWJtfD_w/w140-h140-p/profile.jpg\",,,0]\n]\n]\n,[\"5dd8295e8_b22\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[51.51556643060583,31.32622718811035,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Oleksandr Leshchynskyi\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"5e2e63a14_b23\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[30.6000361563382,100.5903697013855,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Terry Peng\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"6202e19d2_b25\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[19.35255015861537,-96.8119740486145,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Gilberto Portillo\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"622e12e60_b26\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-17.551633585905506,-66.09153985977173,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Rolando Espinoza\",,,0]\n,[\"description\",,,,\"Software Developer\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,\"http://i.imgur.com/j45Rkqv.png\",,,0]\n]\n]\n,[\"6253f972c_b28\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[33.30293237960872,-112.033531665802,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Joshua Odmark\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"6258d15dd_b29\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[52.04822823417389,4.5375895500183105,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Alex Cepoi\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"6258d15dd_b30\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[18.35316099908346,73.89771223068237,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Arijit Chakraborty\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"6258d15dd_b31\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[49.88032042370056,36.27387770138557,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Artem Bogomyagkov\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CpQBgwAAAOJFIVQWqYq7zXCatn_Dfd6Mu9IwUlvA7RwLqo_7uQZDBdvqWHgEP32BFPxL5aUzsD4jx65P6yKfTc4c9iFDCL0ytxrH5ZkxutaNJl_RT1Bmn1U-v15aVwscTY2g6JA_te-yfEMkoe6bLbCKgM9NPYjHSvx1GuxjNpqysM2Mg98MuV4qvDKXMgTg6IKt8gPHURIQiNBDorgHEZl62Tcb2bG6WRoUQmBQDALQh_Nq2K2fViqa76FK2c0|fc121d72e13a3dcf140fc83d0807d1a4d5d3f224|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"6259534ef_b32\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[44.306798024135325,26.147383601385513,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Ionut Dragulin\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CoQBdQAAABWsxRC_iQSWzy1rc4wj7ETS8Jkky79K7nVYDKRsv1wXRCcOGM-y5ScgEmZVoE-2s3hII7S9b__XYNql9gsz2h-VV8orNtJ9T-YdIoczq_hhRkv7DLySLCeugtyRbPhKHWBg_cHsdUIuuS2c65-Uc-92dTLzxyZPLRBMimoRtDKnEhA-Sx0OFPdm-CBj877y__bkGhR7iuQf9tIT3gOMyfoU5fMbICpEuQ|7a7b9f3da4c3091248e95bb4555cb6d09e040f50|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"6259534ef_b34\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[50.337997779102786,30.566894701385536,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Oleg Tarasenko\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CoQBewAAAEtaXvou6dANseYBVxMWbfLP73dJgEuZsZfKbByYTZ_KoE9NXsMi-bP9b3TQ-TRSrwmENE1p197ct8LV-fZ38n1yFBbKzpk9IXsMNHjio28psqqT3TFEc4Uvt3z2eiC1mj2hmINKgVarZEO-lpDKrEYCGfb52423cD_zYVxv59njEhCzl_Miu4H1ArZLIlptcgNBGhQxeJpbv0Oh9gc-caE4r5RvUFJhDQ|e720ea1d8de0bf884832349361239d9eb1542685|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"6259534ef_b35\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[47.89812134814171,37.84634470138553,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Alexander Chekunkov\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CpQBgwAAAE75-5A_Ya_ggCDnSHP1uVYkRMxVqc9jlMuX9T1103HgiUYi7bgJuABTpxQ_1Fmnp1XkH4Z1QSuK0x8-H9cFvPc93cd9Azi6xb4kwxUSZCzn8ZWcvHbOZFtnIHrwA7b0ADXQ0UlbS2v3ICvhuCaDN71HckPTQaaWtdrvjzR2NrTZkrNzNUM7m7XW960B9OVeVRIQw_vpPDJtwv67xpeXoStMQRoUTHfO3tkeK4vWNXPmJRTgiT-wKdo|b1e5f9ff379a335886960c471fddb179d0036e30|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"626d7d4a8_b27\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[13.490400943502696,100.51483869552612,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Claudio Salazar\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"788c6b71a_b37\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[50.506254,30.438233999999966,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Surge Yasko\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CpQBjQAAAGMF13hGuKBHeR7fn-_T44AVT5GAVoli31JWh99CL6OJhxQaxDDV7lEBFyZW1yz8GlS42xJe7YC3m7ijpI9-uOh37qnwTj5rRHqOOx_-xc8eJxmEg8RKwNd-ygK-kdwfgvPESpUUsYNccdPG9pFdIZDeJEJUvktu_xi_KT7qCJ2DJj6Gzg3ti3uiz0PGBwaggxIQZzhz31aDZAjIfiJknF658RoUvpJiPW3nrqWypDR-XTOYA282CVw|92e2c81a45d762680d444a96a1d3c284bec853dc|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"78a2da8d0_b39\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-25.269846567908072,-57.498836517333984,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Matias Insaurralde\\n\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,\"https://matias.insaurral.de/\",,,0]\n]\n]\n,[\"78e226218_b40\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[48.792716,2.359279000000015,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Alexis Svinartchouk\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CoQBdAAAAPohn3FO1m2l9SLDePHdO4QX5cyPdOBCJ4xE7Sh3GD0id-yuHVac0_tFIK8Z2skJFHB4d1-6sl_Mh_0Ragcl2ExEdEnle1k-kqGWYERg9WQyVq4r5D43KKPJrdnxaXi3i30DzZKcAczcrhu73GqIU9S17RmQbC_p0QR8G5Xv1Z4jEhDBxBDsGlUeEAmcF-KjX3x0GhQQoSi4wQrBv9yKfNQOhpUI9DmxRw|1fa664fae3856ba6434e1b7b2faa11774ea73cfd|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"78e226218_b41\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[64.5333333,40.53333329999998,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Kirill Zaborsky \",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CpQBiwAAAHluo6jZ6roKgJroy6YiWf2OEmAS1tSgHlx8D450EUk0NMxcWDlyRIywc3V6og2VwXcqWLvi0hNloEJdZA-vEUp5RAJbIxKQLIbHpeEXsNagyKNfkV1VarRwYKlWYtRmQOPpDF1CrQ0sdFDpehXTdqlIb_J1x99_SQN8r3bV4nzmTBbKkL3nBPLbBTRJRxINURIQLFGyg8C3xl3-YTzttOGIoRoUOHR2hjudYf6o7j4hhO-NT0nB1rQ|dd222251f05117a4c3249b03fec661cf167bb580|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"78e226218_b42\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-12.0779798,-76.87209610000002,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Breno Colom\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CnRtAAAAAF_P9nXktVWPSIffkki_VSTYbZ_Qm4USocFbTyg8rFUx8JJIzk9G7z8fSm_6rRQIysGqW4UdOkGVpnJBK9EawWhAd5aFbjkEald53vnfSf6D9leXPDTb_ZoxYdO2aUr_O5yIecPMYp2CPCdGx_HzlBIQMhAKLu_LjdK6d6htrLimNRoUvVuSAbK9x4ANTuan50W-7t7KyFU|c24b60a818e63bc29b8ae0da23349f4b7c7d2e39|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"78e226218_b43\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-7.875384899999999,110.42620880000004,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Sigit Dewanto\\n\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CoQBeAAAADbTM4tZIY6Ce8WepQivQdAnfXyfrltrClv0OzPea066FGbcS2JfWxFf8Vy29zyXF8I5cuIhSQKp5ykNKh4cGvC5vYF66eLdRMqaL_xUzqJQXvVM3Szg03zszXForoDoAOjN435H-mqMA1XAgchq0PeeSQE_SfbhePaBoq8WOV_TEhAtiEMv2QZOma72bh2nivOUGhTGg8IFPEqaAf4UyT3b2yqNGwMCtQ|d09aac111e7be330b9ac566fddb0d12dc2f0c621|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"78e226218_b44\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[53.44163363864001,14.530838943750041,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Mateusz Golewski\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CoQBcwAAAODRFX0t2x7kStZ99PkfH42GXVMlnU1IeWKHjWM06Jn4Uvn52rBwB3_iq4EOBzfAGYRMZXPuKXJPMTVcknv41nWJ5u2xcbbJMmrwn_pqoy-fpKQEr8Xj-lDVJKs6feX3vpPSuo_0vDIvW-Y02F06--Q5iauiYVBlbmeUXoGX1cApEhCLHgl8YhH6yr6blT9pr8mbGhQQV15J29t73dZPlVM8wW89rUpiCw|916222f05021a72eff0f79f5a33e56aca1edc597|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"78e226218_b45\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[45.253783,-69.44546889999998,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Jon Duckworth\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CnRtAAAAq7TnzbfdDYQ8CU70TDXZIRN8qmJj_-G5IWhgYyGAL5AvzPiU1W4kP4k3fF3pGoaEeCN55Fl5w9AmaQ7kQwIhrKYsL08wvBAGc9TEUdBfLlDE83I30zVOpTSOtcphdGGb5tDsWJN4WCNjD_dYscgq8hIQ0eefCu8A9b_sbUtQm_TbBBoUC-mBhe5awnSrkvsC1R9Tmic2F1g|bf6dc489a219a172a00afdd6620842f991c05f97|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"78e226218_b46\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[44.19026071028947,26.001532162499984,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Sonia Stan\",,,0]\n,[\"description\",,,,\"Bucharest\",,,0]\n,[\"place_ref\",,,,\"CoQBfAAAAPLq290-E8dMoEihJq_7nbXEsStePMSPqO5XFtUQvU06COr6Np0RSXMujSOQ7_b8orNmqm-0Fx-izajDEfWMl_2U0v2AB2HZ8mbTwGW6vN4H7_H94hjeiOAfafheDIbXx3eEPUeUKv6J9NFs20GFG_OIRTyRaYgeIE3QM51ZBO6hEhD7JHF7SsKuF2GDhFHUoU6jGhSQjadxkx0pxPN-wircP5G6Z--pvw|7860aa68594bb78627cc41d32cf261bd66a3dbff|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,\"https://lh3.googleusercontent.com/-Z2Cxb7myIGw/UNI9055eLhI/AAAAAAAAIOw/dxYI4eY8CMQ/w458-h689-no/CRI_2604.jpg\",,,0]\n]\n]\n,[\"78e226218_b47\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-21.3,-55.94999999999999,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Rogerio Hilbert\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CpQBjwAAAPVygXWUKf8FcStsJC0uKvb60sQHWR1v577G70veIshMAuCnLqg1KLtD2YT0z0JPe_HBpEVvv7n1jWWH06OKZ68pNc43V3Ad8o1TFxQjPcdyeMh7HcQFhifjuWDwEC_rENRWT2mZvwz5gfc58HAjU9S58pyg4rWZ4Rjp9puH5QO7fxn-E6Bgw5Sb2HfXZxrdZBIQoaLYTaXapWrD65jl50GaDxoUqK3mQqTtrE400RwHZtDGayHJCV8|aeb05062da015f0a2be3cfd9992ab63e26f5bbeb|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"7e0609535_b48\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[51.6180165487737,-0.087890625,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Pawel Miech\\n\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"7eac51636_b49\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[55.953252,-3.188266999999996,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Alexey Bezhan\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CpQBgwAAACHtB_Gy5ic1PpQVQ8CixwfM6Wpy2b5r7BAIK_mNBs8MfEyc3w-FzbPJ-x0sxpolm3atbn7EZfgZH6qpKFicNKcE7FnJqmujb5nAJUc94sqYu8Y_AnST_TDXHSX5iReZksF1g1vqQEk9RWTWjdQIQr_S8BYJ74-1cjbwZwcFPcG0O6PEFg8tJ9TOmcDDtxztQBIQTP_vRoIr5iMHG4Qa33jErRoU9ll3-aw-iYe_WUeOHrAsAbI52ZM|bbd76ac472f384bf95e692ad86e7e99d2370eb45|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"7eac51636_b50\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-16.4090474,-71.53745099999998,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Raul Gallegos\",,,0]\n,[\"description\",,,,\"Software Engineer\",,,0]\n,[\"place_ref\",,,,\"CoQBegAAAAjuWCVriiuoeTU9cwdjUT8oycXfCkdgYb1kRsGe61J5inl0G5A8TUMtJnEYxl2X-I4EaHha82PP1VEsgDknj-8zIVZc1WhLSU7kstP0wp4FAYNZDjjqt_zL3XIgwQTaaQUq8LzMuviyGQG3JJmXu6US4BehzphrUSkTL5_Mhn6oEhDjcxeSIZAbcZKswJTloPJdGhQvDb4CEhMvxlZX_cOm2cN4FbUVgg|0c2fe2621ef64c70bc4ca16f9c493aa07d82833b|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"7eac51636_b51\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[52.2296756,21.012228700000037,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Pepesha Peregud\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CoQBcQAAAK2Zdh7nZcHeIOG7eekU7_QwcF_TEvidt1e_g2UhK1jMWMR1SoofWy6XHuniUwSwZOIjGFcDNoZ4XuGP7icvJ51s_m612xh-2MhrijcXyUjw1XOIZfwxkoAhdrvd_yI7qczlqAKl-GSoYX9yFQ5MR8YmutfQ8J6XcSrw54qYkRi0EhCXKUym4nTHOGs6vfSmbgqaGhQMMgN-sCRtcQlwOUUAS-wTAD8jEQ|6b893c3d05bf5fb2caa4b033c9aa27a41fab6fc3|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"7eac51636_b52\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[31.55460609999999,74.35715809999999,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Waqas Shabbir\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CoQBcwAAAD75CMnjpYLwTklRca9fzB89hK84Jg3UEgIydiFXeU1Vfwq84p_kfGNDdc_IHkQi46SpNMCWX913kemfXUqViF0r-35d_Inogh6M1E7A7FCarBJ6ZMjpKcjLhAiX4lAnES1SRlNp4Lk0RBOmQlmwc-MRDxdmw8g_TMl7QUlwNLflEhCK7rBhRAnl6N-h7DAmLXkgGhTjbGLS-XT23FOfOoLoSXZxRcy9ew|57588c12ca0d041f39b7b057af775b0f51b54abc|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"7eac51636_b53\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[53.479324,-2.248485100000039,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"David Kendal\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CoQBcQAAADAFUwuSuBCaYzIZ_1BDLhbzOku1zqejcVoGqStdsEFRW2NmRErNSaPs10H1D1EPWKc6Qf6Qmt2C3wZjH-U0KGmJORNXFJPG24XsB7v25Kl2HwstYg-BvDextY8AOUF8FntzLorCeNuDlGVqBkc34STrmR7N67fM2czsO2KZkzXyEhBZSaIMo3-U7D5iyZa1ELPgGhRScDpvjlCqBKMV5bnQ0XmgGT8dPw|61c34247dcf1be30d9aba564a005e77749d1dbaf|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"8186dd514_b54\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[14.604847155053898,121.11328125,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Ogi Aquino\\n\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"8519bb1b6_b55\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[35.6894875,139.69170639999993,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Andrew Baxter\\n\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CnRvAAAA-sxXA-ZSrO5tq1RARcsyqQRZ-GVmAjw1F91P3BYm1OHqK2XztJhWH5ub4UU_gww8608TLug3EoXLp9GzgxchA6hlbS6KKwRZ9Jr8cunA0C2Eo24fOYk2Z78CDkEHdtywymmgSpAtwiZjYso8XPQ1VhIQ40YDQ9fFXQlJzhgh61ytxBoUy9d8nvcyDSHstBu2dz3NgbRmKpI|acd0103fbdf13b8e2dedc7e9bf75da02274d18ed|Y\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"88a08f2f4_b56\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[39.791702800558824,-86.02500915527344,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Sam Hart\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"88b6240c9_b57\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[-29.7549941,-51.150283,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Fábio Bertinatto\\n\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CpQBjQAAAJqF8Shk6oaNzizHiCYG_BjAEpSUz9HSBFO7uNZ17HjGdfcYZ1lJ-Y6MZGScdFgr1JdaOckboVGISI5hH3NXoeYS0ZTO6SGsvXGjnnf-Ez_jLX1smGK8PwKefeYzOB2dmxFl4QHqjSi23nKkiyO_x95nfZ3HZJSD2MBwwarjz4yxSz7_tj4XBem7Se-g43AE-hIQLp6uGKeSu4ecYKk4n6XHjBoUt6ZbrfbxYu8OspYERFrhPYg579Y|6ac09e9b0112435266664cfef3b4610e7c840fdc|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"89fbb1c1f_b58\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[14.638482792318786,121.07433557510376,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Mikko Gozalo\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"8afe3d09f_b59\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[53.95850961504583,-1.083526611328125,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Tom Wardill\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"938a14a49_b60\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[42.90816007196054,74.5751953125,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Nargiza Sarkulova\",,,0]\n,[\"description\",,,,\"\",,,0]\n,[\"place_ref\",,,,,,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"943b1fecb_b61\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[30.267153,-97.74306079999997,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Stella Tigre\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CoQBcgAAAItIcFAb47BFNsydjckb0yW9K7v0LQ6j25po2fD7RgzF1GHUyEsty8PS7vXeWgvTA_imu3AnZbGdg4lAMFQ3gkGuI5cgewa_eTMDZXcz52InDkCLfSN6BZ62PRYhRb3xBPn6Yq0XcaQGe_m0-PsSsqhoOb6Pq3apt9W_4I-nHLMTEhCU4nJ2mXm92g38lDWwVn31GhQtWGQJjID6q8wADEGxx7MS2sQ4pA|c5153b48ff062dcbd5e6bbb77bcaa3afb7458147|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n,[\"943b1fecb_b62\",,,,,,,,,,,[[\"gme_geometry_\",,,,,,[[[4.210484,101.97576600000002,0.0]\n]\n,[]\n,[]\n]\n,0]\n,[\"name\",,,,\"Ogi Aquino\",,,0]\n,[\"description\",,,,,,,0]\n,[\"place_ref\",,,,\"CoQBcwAAANpbF9oexRRYbmyfA9aK80jtBK-OxAGBKl0TfwD78Uwqpb6y-Hh1mB2l5dygHFZCLEzbKfR8yt41jHdRWGJunIDWN042It647cfOZ7SbhdaebtQL9DdfZZWDTbHNOEafBkjhrUS_Xp4qXZ49hWuvBV82jcAZCQEIcu7GXYsue5uxEhDx2vI_Lnl6N7sNZZtI4Rb8GhQbqyMETQIgue8YQPsuTAE2KsQ4FQ|abc401b83e6d1e02b14acd8b99f62d7d17a74a3c|N\",,,0]\n,[\"feature_order\",,,,,,,0]\n,[\"gx_metafeatureid\",,,,,,,0]\n,[\"gx_routeinfo\",,,,,,,0]\n,[\"gx_image_links\",,,,,,,0]\n]\n]\n]\n,,[]\n,0]\n,[\"zJncfB8N3x1E.kxBWX97j7MBk\",,[]\n,,[]\n,0]\n]\n,0,,1,0]\n","gapiClientId":"304432081137.apps.googleusercontent.com","userAgentData":{"isMobile":false},"userIndex":"","areOnePickViewsEnabled":true,"guidedHelpTourIsEnabled":true,"applicationName":"Maps Engine Pro","supportsPagination":false,"proUpgradeEnabled":true,"directionsEnabled":true,"routeTraceEnabled":false,"enabledClientFeatures":[0,3,5,8,9],"billingType":0,"isAppsDomain":false,"devMode":false,"geodesicNotificationIsEnabled":false,"onepickBasePath":"https://docs.google.com/picker","proLimits":"[2,10,2000,200,2000,1000,50,40,10,2000,10]\n","isConsumerAndOverLimit":false};</script><script type="text/javascript" src="//maps.googleapis.com/maps/api/js?v=3.15&libraries=drawing,places,geometry,visualization&sensor=false&language=fr&region=FR&client=google-maps-pro"></script><script></script><script src="https://www.gstatic.com/mapspro/_/js/k=mapspro.mpvid.fr.8ak-UNEpyAE.O/m=mp_base,mp_view/rt=j/d=0/rs=AItRSTOkrn14gPenhJwO8rlDuDmQioDLrw"></script><script>_startApp();</script></body></html> -------------------------------------------------------------------------------- /tests/samples/ifelse001.js: -------------------------------------------------------------------------------- 1 | var z = 3; 2 | if (x == 5) { 3 | z = 10; 4 | } 5 | else if (x == 10) { 6 | z = 15; 7 | } 8 | else { 9 | z = 20; 10 | } 11 | -------------------------------------------------------------------------------- /tests/samples/jquery-ui.custom.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.3 - 2013-05-03 2 | * http://jqueryui.com 3 | * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */ 4 | (function(e,t){function i(t,i){var a,n,r,o=t.nodeName.toLowerCase();return"area"===o?(a=t.parentNode,n=a.name,t.href&&n&&"map"===a.nodeName.toLowerCase()?(r=e("img[usemap=#"+n+"]")[0],!!r&&s(r)):!1):(/input|select|textarea|button|object/.test(o)?!t.disabled:"a"===o?t.href||i:i)&&s(t)}function s(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}var a=0,n=/^ui-id-\d+$/;e.ui=e.ui||{},e.extend(e.ui,{version:"1.10.3",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),scrollParent:function(){var t;return t=e.ui.ie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(e.css(this,"position"))&&/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0),/fixed/.test(this.css("position"))||!t.length?e(document):t},zIndex:function(i){if(i!==t)return this.css("zIndex",i);if(this.length)for(var s,a,n=e(this[0]);n.length&&n[0]!==document;){if(s=n.css("position"),("absolute"===s||"relative"===s||"fixed"===s)&&(a=parseInt(n.css("zIndex"),10),!isNaN(a)&&0!==a))return a;n=n.parent()}return 0},uniqueId:function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++a)})},removeUniqueId:function(){return this.each(function(){n.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(t){return i(t,!isNaN(e.attr(t,"tabindex")))},tabbable:function(t){var s=e.attr(t,"tabindex"),a=isNaN(s);return(a||s>=0)&&i(t,!a)}}),e("<a>").outerWidth(1).jquery||e.each(["Width","Height"],function(i,s){function a(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===s?["Left","Right"]:["Top","Bottom"],r=s.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+s]=function(i){return i===t?o["inner"+s].call(this):this.each(function(){e(this).css(r,a(this,i)+"px")})},e.fn["outer"+s]=function(t,i){return"number"!=typeof t?o["outer"+s].call(this,t):this.each(function(){e(this).css(r,a(this,t,!0,i)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.support.selectstart="onselectstart"in document.createElement("div"),e.fn.extend({disableSelection:function(){return this.bind((e.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),e.extend(e.ui,{plugin:{add:function(t,i,s){var a,n=e.ui[t].prototype;for(a in s)n.plugins[a]=n.plugins[a]||[],n.plugins[a].push([i,s[a]])},call:function(e,t,i){var s,a=e.plugins[t];if(a&&e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType)for(s=0;a.length>s;s++)e.options[a[s][0]]&&a[s][1].apply(e.element,i)}},hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",a=!1;return t[s]>0?!0:(t[s]=1,a=t[s]>0,t[s]=0,a)}})})(jQuery); 5 | /*! jQuery UI - v1.10.3 - 2013-05-03 6 | * http://jqueryui.com 7 | * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */ 8 | (function(e,t){var i=0,s=Array.prototype.slice,n=e.cleanData;e.cleanData=function(t){for(var i,s=0;null!=(i=t[s]);s++)try{e(i).triggerHandler("remove")}catch(a){}n(t)},e.widget=function(i,s,n){var a,r,o,h,l={},u=i.split(".")[0];i=i.split(".")[1],a=u+"-"+i,n||(n=s,s=e.Widget),e.expr[":"][a.toLowerCase()]=function(t){return!!e.data(t,a)},e[u]=e[u]||{},r=e[u][i],o=e[u][i]=function(e,i){return this._createWidget?(arguments.length&&this._createWidget(e,i),t):new o(e,i)},e.extend(o,r,{version:n.version,_proto:e.extend({},n),_childConstructors:[]}),h=new s,h.options=e.widget.extend({},h.options),e.each(n,function(i,n){return e.isFunction(n)?(l[i]=function(){var e=function(){return s.prototype[i].apply(this,arguments)},t=function(e){return s.prototype[i].apply(this,e)};return function(){var i,s=this._super,a=this._superApply;return this._super=e,this._superApply=t,i=n.apply(this,arguments),this._super=s,this._superApply=a,i}}(),t):(l[i]=n,t)}),o.prototype=e.widget.extend(h,{widgetEventPrefix:r?h.widgetEventPrefix:i},l,{constructor:o,namespace:u,widgetName:i,widgetFullName:a}),r?(e.each(r._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete r._childConstructors):s._childConstructors.push(o),e.widget.bridge(i,o)},e.widget.extend=function(i){for(var n,a,r=s.call(arguments,1),o=0,h=r.length;h>o;o++)for(n in r[o])a=r[o][n],r[o].hasOwnProperty(n)&&a!==t&&(i[n]=e.isPlainObject(a)?e.isPlainObject(i[n])?e.widget.extend({},i[n],a):e.widget.extend({},a):a);return i},e.widget.bridge=function(i,n){var a=n.prototype.widgetFullName||i;e.fn[i]=function(r){var o="string"==typeof r,h=s.call(arguments,1),l=this;return r=!o&&h.length?e.widget.extend.apply(null,[r].concat(h)):r,o?this.each(function(){var s,n=e.data(this,a);return n?e.isFunction(n[r])&&"_"!==r.charAt(0)?(s=n[r].apply(n,h),s!==n&&s!==t?(l=s&&s.jquery?l.pushStack(s.get()):s,!1):t):e.error("no such method '"+r+"' for "+i+" widget instance"):e.error("cannot call methods on "+i+" prior to initialization; "+"attempted to call method '"+r+"'")}):this.each(function(){var t=e.data(this,a);t?t.option(r||{})._init():e.data(this,a,new n(r,this))}),l}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(t,s){s=e(s||this.defaultElement||this)[0],this.element=e(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),s!==this&&(e.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===s&&this.destroy()}}),this.document=e(s.style?s.ownerDocument:s.document||s),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(i,s){var n,a,r,o=i;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof i)if(o={},n=i.split("."),i=n.shift(),n.length){for(a=o[i]=e.widget.extend({},this.options[i]),r=0;n.length-1>r;r++)a[n[r]]=a[n[r]]||{},a=a[n[r]];if(i=n.pop(),s===t)return a[i]===t?null:a[i];a[i]=s}else{if(s===t)return this.options[i]===t?null:this.options[i];o[i]=s}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!t).attr("aria-disabled",t),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(i,s,n){var a,r=this;"boolean"!=typeof i&&(n=s,s=i,i=!1),n?(s=a=e(s),this.bindings=this.bindings.add(s)):(n=s,s=this.element,a=this.widget()),e.each(n,function(n,o){function h(){return i||r.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?r[o]:o).apply(r,arguments):t}"string"!=typeof o&&(h.guid=o.guid=o.guid||h.guid||e.guid++);var l=n.match(/^(\w+)\s*(.*)$/),u=l[1]+r.eventNamespace,c=l[2];c?a.delegate(c,u,h):s.bind(u,h)})},_off:function(e,t){t=(t||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,r=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(r)&&r.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var r,o=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),r=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),r&&e.effects&&e.effects.effect[o]?s[t](n):o!==t&&s[o]?s[o](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}})})(jQuery); 9 | /*! jQuery UI - v1.10.3 - 2013-05-03 10 | * http://jqueryui.com 11 | * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */ 12 | (function(e){var t=!1;e(document).mouseup(function(){t=!1}),e.widget("ui.mouse",{version:"1.10.3",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+".preventClickEvent")?(e.removeData(i.target,t.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):undefined}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&e(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(i){if(!t){this._mouseStarted&&this._mouseUp(i),this._mouseDownEvent=i;var s=this,n=1===i.which,a="string"==typeof this.options.cancel&&i.target.nodeName?e(i.target).closest(this.options.cancel).length:!1;return n&&!a&&this._mouseCapture(i)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){s.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(i)&&this._mouseDelayMet(i)&&(this._mouseStarted=this._mouseStart(i)!==!1,!this._mouseStarted)?(i.preventDefault(),!0):(!0===e.data(i.target,this.widgetName+".preventClickEvent")&&e.removeData(i.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return s._mouseMove(e)},this._mouseUpDelegate=function(e){return s._mouseUp(e)},e(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),i.preventDefault(),t=!0,!0)):!0}},_mouseMove:function(t){return e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button?this._mouseUp(t):this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){return e(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})})(jQuery); 13 | /*! jQuery UI - v1.10.3 - 2013-05-03 14 | * http://jqueryui.com 15 | * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */ 16 | (function(e){e.widget("ui.draggable",e.ui.mouse,{version:"1.10.3",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"!==this.options.helper||/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},_destroy:function(){this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy()},_mouseCapture:function(t){var i=this.options;return this.helper||i.disabled||e(t.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(t),this.handle?(e(i.iframeFix===!0?"iframe":i.iframeFix).each(function(){e("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>").css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(e(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(t){var i=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offsetParent=this.helper.offsetParent(),this.offsetParentCssPosition=this.offsetParent.css("position"),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.offset.scroll=!1,e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!i.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_mouseDrag:function(t,i){if("fixed"===this.offsetParentCssPosition&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",t,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var i=this,s=!1;return e.ui.ddmanager&&!this.options.dropBehaviour&&(s=e.ui.ddmanager.drop(this,t)),this.dropped&&(s=this.dropped,this.dropped=!1),"original"!==this.options.helper||e.contains(this.element[0].ownerDocument,this.element[0])?("invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",t)!==!1&&i._clear()}):this._trigger("stop",t)!==!1&&this._clear(),!1):!1},_mouseUp:function(t){return e("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){return this.options.handle?!!e(t.target).closest(this.element.find(this.options.handle)).length:!0},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper)?e(i.helper.apply(this.element[0],[t])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return s.parents("body").length||s.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s[0]===this.element[0]||/(fixed|absolute)/.test(s.css("position"))||s.css("position","absolute"),s},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){var t=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&e.ui.ie)&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var e=this.element.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options;return n.containment?"window"===n.containment?(this.containment=[e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,e(window).scrollLeft()+e(window).width()-this.helperProportions.width-this.margins.left,e(window).scrollTop()+(e(window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):"document"===n.containment?(this.containment=[0,0,e(document).width()-this.helperProportions.width-this.margins.left,(e(document).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):n.containment.constructor===Array?(this.containment=n.containment,undefined):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=e(n.containment),s=i[0],s&&(t="hidden"!==i.css("overflow"),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(t?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(t?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=i),undefined):(this.containment=null,undefined)},_convertPositionTo:function(t,i){i||(i=this.position);var s="absolute"===t?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent;return this.offset.scroll||(this.offset.scroll={top:n.scrollTop(),left:n.scrollLeft()}),{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top)*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)*s}},_generatePosition:function(t){var i,s,n,a,o=this.options,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=t.pageX,l=t.pageY;return this.offset.scroll||(this.offset.scroll={top:r.scrollTop(),left:r.scrollLeft()}),this.originalPosition&&(this.containment&&(this.relative_container?(s=this.relative_container.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,t.pageX-this.offset.click.left<i[0]&&(h=i[0]+this.offset.click.left),t.pageY-this.offset.click.top<i[1]&&(l=i[1]+this.offset.click.top),t.pageX-this.offset.click.left>i[2]&&(h=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,l=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,h=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(t,i,s){return s=s||this._uiHash(),e.ui.plugin.call(this,t,[i,s]),"drag"===t&&(this.positionAbs=this._convertPositionTo("absolute")),e.Widget.prototype._trigger.call(this,t,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),e.ui.plugin.add("draggable","connectToSortable",{start:function(t,i){var s=e(this).data("ui-draggable"),n=s.options,a=e.extend({},i,{item:s.element});s.sortables=[],e(n.connectToSortable).each(function(){var i=e.data(this,"ui-sortable");i&&!i.options.disabled&&(s.sortables.push({instance:i,shouldRevert:i.options.revert}),i.refreshPositions(),i._trigger("activate",t,a))})},stop:function(t,i){var s=e(this).data("ui-draggable"),n=e.extend({},i,{item:s.element});e.each(s.sortables,function(){this.instance.isOver?(this.instance.isOver=0,s.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=this.shouldRevert),this.instance._mouseStop(t),this.instance.options.helper=this.instance.options._helper,"original"===s.options.helper&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",t,n))})},drag:function(t,i){var s=e(this).data("ui-draggable"),n=this;e.each(s.sortables,function(){var a=!1,o=this;this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this.instance._intersectsWith(this.instance.containerCache)&&(a=!0,e.each(s.sortables,function(){return this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this!==o&&this.instance._intersectsWith(this.instance.containerCache)&&e.contains(o.instance.element[0],this.instance.element[0])&&(a=!1),a})),a?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=e(n).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return i.helper[0]},t.target=this.instance.currentItem[0],this.instance._mouseCapture(t,!0),this.instance._mouseStart(t,!0,!0),this.instance.offset.click.top=s.offset.click.top,this.instance.offset.click.left=s.offset.click.left,this.instance.offset.parent.left-=s.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=s.offset.parent.top-this.instance.offset.parent.top,s._trigger("toSortable",t),s.dropped=this.instance.element,s.currentItem=s.element,this.instance.fromOutside=s),this.instance.currentItem&&this.instance._mouseDrag(t)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",t,this.instance._uiHash(this.instance)),this.instance._mouseStop(t,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),s._trigger("fromSortable",t),s.dropped=!1)})}}),e.ui.plugin.add("draggable","cursor",{start:function(){var t=e("body"),i=e(this).data("ui-draggable").options;t.css("cursor")&&(i._cursor=t.css("cursor")),t.css("cursor",i.cursor)},stop:function(){var t=e(this).data("ui-draggable").options;t._cursor&&e("body").css("cursor",t._cursor)}}),e.ui.plugin.add("draggable","opacity",{start:function(t,i){var s=e(i.helper),n=e(this).data("ui-draggable").options;s.css("opacity")&&(n._opacity=s.css("opacity")),s.css("opacity",n.opacity)},stop:function(t,i){var s=e(this).data("ui-draggable").options;s._opacity&&e(i.helper).css("opacity",s._opacity)}}),e.ui.plugin.add("draggable","scroll",{start:function(){var t=e(this).data("ui-draggable");t.scrollParent[0]!==document&&"HTML"!==t.scrollParent[0].tagName&&(t.overflowOffset=t.scrollParent.offset())},drag:function(t){var i=e(this).data("ui-draggable"),s=i.options,n=!1;i.scrollParent[0]!==document&&"HTML"!==i.scrollParent[0].tagName?(s.axis&&"x"===s.axis||(i.overflowOffset.top+i.scrollParent[0].offsetHeight-t.pageY<s.scrollSensitivity?i.scrollParent[0].scrollTop=n=i.scrollParent[0].scrollTop+s.scrollSpeed:t.pageY-i.overflowOffset.top<s.scrollSensitivity&&(i.scrollParent[0].scrollTop=n=i.scrollParent[0].scrollTop-s.scrollSpeed)),s.axis&&"y"===s.axis||(i.overflowOffset.left+i.scrollParent[0].offsetWidth-t.pageX<s.scrollSensitivity?i.scrollParent[0].scrollLeft=n=i.scrollParent[0].scrollLeft+s.scrollSpeed:t.pageX-i.overflowOffset.left<s.scrollSensitivity&&(i.scrollParent[0].scrollLeft=n=i.scrollParent[0].scrollLeft-s.scrollSpeed))):(s.axis&&"x"===s.axis||(t.pageY-e(document).scrollTop()<s.scrollSensitivity?n=e(document).scrollTop(e(document).scrollTop()-s.scrollSpeed):e(window).height()-(t.pageY-e(document).scrollTop())<s.scrollSensitivity&&(n=e(document).scrollTop(e(document).scrollTop()+s.scrollSpeed))),s.axis&&"y"===s.axis||(t.pageX-e(document).scrollLeft()<s.scrollSensitivity?n=e(document).scrollLeft(e(document).scrollLeft()-s.scrollSpeed):e(window).width()-(t.pageX-e(document).scrollLeft())<s.scrollSensitivity&&(n=e(document).scrollLeft(e(document).scrollLeft()+s.scrollSpeed)))),n!==!1&&e.ui.ddmanager&&!s.dropBehaviour&&e.ui.ddmanager.prepareOffsets(i,t)}}),e.ui.plugin.add("draggable","snap",{start:function(){var t=e(this).data("ui-draggable"),i=t.options;t.snapElements=[],e(i.snap.constructor!==String?i.snap.items||":data(ui-draggable)":i.snap).each(function(){var i=e(this),s=i.offset();this!==t.element[0]&&t.snapElements.push({item:this,width:i.outerWidth(),height:i.outerHeight(),top:s.top,left:s.left})})},drag:function(t,i){var s,n,a,o,r,h,l,u,c,d,p=e(this).data("ui-draggable"),f=p.options,m=f.snapTolerance,g=i.offset.left,v=g+p.helperProportions.width,b=i.offset.top,y=b+p.helperProportions.height;for(c=p.snapElements.length-1;c>=0;c--)r=p.snapElements[c].left,h=r+p.snapElements[c].width,l=p.snapElements[c].top,u=l+p.snapElements[c].height,r-m>v||g>h+m||l-m>y||b>u+m||!e.contains(p.snapElements[c].item.ownerDocument,p.snapElements[c].item)?(p.snapElements[c].snapping&&p.options.snap.release&&p.options.snap.release.call(p.element,t,e.extend(p._uiHash(),{snapItem:p.snapElements[c].item})),p.snapElements[c].snapping=!1):("inner"!==f.snapMode&&(s=m>=Math.abs(l-y),n=m>=Math.abs(u-b),a=m>=Math.abs(r-v),o=m>=Math.abs(h-g),s&&(i.position.top=p._convertPositionTo("relative",{top:l-p.helperProportions.height,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:u,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r-p.helperProportions.width}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:h}).left-p.margins.left)),d=s||n||a||o,"outer"!==f.snapMode&&(s=m>=Math.abs(l-b),n=m>=Math.abs(u-y),a=m>=Math.abs(r-g),o=m>=Math.abs(h-v),s&&(i.position.top=p._convertPositionTo("relative",{top:l,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:u-p.helperProportions.height,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:h-p.helperProportions.width}).left-p.margins.left)),!p.snapElements[c].snapping&&(s||n||a||o||d)&&p.options.snap.snap&&p.options.snap.snap.call(p.element,t,e.extend(p._uiHash(),{snapItem:p.snapElements[c].item})),p.snapElements[c].snapping=s||n||a||o||d)}}),e.ui.plugin.add("draggable","stack",{start:function(){var t,i=this.data("ui-draggable").options,s=e.makeArray(e(i.stack)).sort(function(t,i){return(parseInt(e(t).css("zIndex"),10)||0)-(parseInt(e(i).css("zIndex"),10)||0)});s.length&&(t=parseInt(e(s[0]).css("zIndex"),10)||0,e(s).each(function(i){e(this).css("zIndex",t+i)}),this.css("zIndex",t+s.length))}}),e.ui.plugin.add("draggable","zIndex",{start:function(t,i){var s=e(i.helper),n=e(this).data("ui-draggable").options;s.css("zIndex")&&(n._zIndex=s.css("zIndex")),s.css("zIndex",n.zIndex)},stop:function(t,i){var s=e(this).data("ui-draggable").options;s._zIndex&&e(i.helper).css("zIndex",s._zIndex)}})})(jQuery); 17 | /*! jQuery UI - v1.10.3 - 2013-05-03 18 | * http://jqueryui.com 19 | * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */ 20 | (function(e){function t(e){return parseInt(e,10)||0}function i(e){return!isNaN(parseInt(e,10))}e.widget("ui.resizable",e.ui.mouse,{version:"1.10.3",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_create:function(){var t,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),e.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(e("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.data("ui-resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(e(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),t=this.handles.split(","),this.handles={},i=0;t.length>i;i++)s=e.trim(t[i]),a="ui-resizable-"+s,n=e("<div class='ui-resizable-handle "+a+"'></div>"),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(t){var i,s,n,a;t=t||this.element;for(i in this.handles)this.handles[i].constructor===String&&(this.handles[i]=e(this.handles[i],this.element).show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(s=e(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),t.css(n,a),this._proportionallyResize()),e(this.handles[i]).length},this._renderAxis(this.element),this._handles=e(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),e(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(e(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(e(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t,i=function(t){e(t).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),t=this.element,this.originalElement.css({position:t.css("position"),width:t.outerWidth(),height:t.outerHeight(),top:t.css("top"),left:t.css("left")}).insertAfter(t),t.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(t){var i,s,n=!1;for(i in this.handles)s=e(this.handles[i])[0],(s===t.target||e.contains(s,t.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(i){var s,n,a,o=this.options,r=this.element.position(),h=this.element;return this.resizing=!0,/absolute/.test(h.css("position"))?h.css({position:"absolute",top:h.css("top"),left:h.css("left")}):h.is(".ui-draggable")&&h.css({position:"absolute",top:r.top,left:r.left}),this._renderProxy(),s=t(this.helper.css("left")),n=t(this.helper.css("top")),o.containment&&(s+=e(o.containment).scrollLeft()||0,n+=e(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:s,top:n},this.size=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalSize=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalPosition={left:s,top:n},this.sizeDiff={width:h.outerWidth()-h.width(),height:h.outerHeight()-h.height()},this.originalMousePosition={left:i.pageX,top:i.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,a=e(".ui-resizable-"+this.axis).css("cursor"),e("body").css("cursor","auto"===a?this.axis+"-resize":a),h.addClass("ui-resizable-resizing"),this._propagate("start",i),!0},_mouseDrag:function(t){var i,s=this.helper,n={},a=this.originalMousePosition,o=this.axis,r=this.position.top,h=this.position.left,l=this.size.width,u=this.size.height,c=t.pageX-a.left||0,d=t.pageY-a.top||0,p=this._change[o];return p?(i=p.apply(this,[t,c,d]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(i=this._updateRatio(i,t)),i=this._respectSize(i,t),this._updateCache(i),this._propagate("resize",t),this.position.top!==r&&(n.top=this.position.top+"px"),this.position.left!==h&&(n.left=this.position.left+"px"),this.size.width!==l&&(n.width=this.size.width+"px"),this.size.height!==u&&(n.height=this.size.height+"px"),s.css(n),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),e.isEmptyObject(n)||this._trigger("resize",t,this.ui()),!1):!1},_mouseStop:function(t){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,u=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&e.ui.hasScroll(i[0],"left")?0:u.sizeDiff.height,a=s?0:u.sizeDiff.width,o={width:u.helper.width()-a,height:u.helper.height()-n},r=parseInt(u.element.css("left"),10)+(u.position.left-u.originalPosition.left)||null,h=parseInt(u.element.css("top"),10)+(u.position.top-u.originalPosition.top)||null,l.animate||this.element.css(e.extend(o,{top:h,left:r})),u.helper.height(u.size.height),u.helper.width(u.size.width),this._helper&&!l.animate&&this._proportionallyResize()),e("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(e){var t,s,n,a,o,r=this.options;o={minWidth:i(r.minWidth)?r.minWidth:0,maxWidth:i(r.maxWidth)?r.maxWidth:1/0,minHeight:i(r.minHeight)?r.minHeight:0,maxHeight:i(r.maxHeight)?r.maxHeight:1/0},(this._aspectRatio||e)&&(t=o.minHeight*this.aspectRatio,n=o.minWidth/this.aspectRatio,s=o.maxHeight*this.aspectRatio,a=o.maxWidth/this.aspectRatio,t>o.minWidth&&(o.minWidth=t),n>o.minHeight&&(o.minHeight=n),o.maxWidth>s&&(o.maxWidth=s),o.maxHeight>a&&(o.maxHeight=a)),this._vBoundaries=o},_updateCache:function(e){this.offset=this.helper.offset(),i(e.left)&&(this.position.left=e.left),i(e.top)&&(this.position.top=e.top),i(e.height)&&(this.size.height=e.height),i(e.width)&&(this.size.width=e.width)},_updateRatio:function(e){var t=this.position,s=this.size,n=this.axis;return i(e.height)?e.width=e.height*this.aspectRatio:i(e.width)&&(e.height=e.width/this.aspectRatio),"sw"===n&&(e.left=t.left+(s.width-e.width),e.top=null),"nw"===n&&(e.top=t.top+(s.height-e.height),e.left=t.left+(s.width-e.width)),e},_respectSize:function(e){var t=this._vBoundaries,s=this.axis,n=i(e.width)&&t.maxWidth&&t.maxWidth<e.width,a=i(e.height)&&t.maxHeight&&t.maxHeight<e.height,o=i(e.width)&&t.minWidth&&t.minWidth>e.width,r=i(e.height)&&t.minHeight&&t.minHeight>e.height,h=this.originalPosition.left+this.originalSize.width,l=this.position.top+this.size.height,u=/sw|nw|w/.test(s),c=/nw|ne|n/.test(s);return o&&(e.width=t.minWidth),r&&(e.height=t.minHeight),n&&(e.width=t.maxWidth),a&&(e.height=t.maxHeight),o&&u&&(e.left=h-t.minWidth),n&&u&&(e.left=h-t.maxWidth),r&&c&&(e.top=l-t.minHeight),a&&c&&(e.top=l-t.maxHeight),e.width||e.height||e.left||!e.top?e.width||e.height||e.top||!e.left||(e.left=null):e.top=null,e},_proportionallyResize:function(){if(this._proportionallyResizeElements.length){var e,t,i,s,n,a=this.helper||this.element;for(e=0;this._proportionallyResizeElements.length>e;e++){if(n=this._proportionallyResizeElements[e],!this.borderDif)for(this.borderDif=[],i=[n.css("borderTopWidth"),n.css("borderRightWidth"),n.css("borderBottomWidth"),n.css("borderLeftWidth")],s=[n.css("paddingTop"),n.css("paddingRight"),n.css("paddingBottom"),n.css("paddingLeft")],t=0;i.length>t;t++)this.borderDif[t]=(parseInt(i[t],10)||0)+(parseInt(s[t],10)||0);n.css({height:a.height()-this.borderDif[0]-this.borderDif[2]||0,width:a.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var t=this.element,i=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||e("<div style='overflow:hidden;'></div>"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(e,t){return{width:this.originalSize.width+t}},w:function(e,t){var i=this.originalSize,s=this.originalPosition;return{left:s.left+t,width:i.width-t}},n:function(e,t,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(e,t,i){return{height:this.originalSize.height+i}},se:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},sw:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,i,s]))},ne:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},nw:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,i,s]))}},_propagate:function(t,i){e.ui.plugin.call(this,t,[i,this.ui()]),"resize"!==t&&this._trigger(t,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),e.ui.plugin.add("resizable","animate",{stop:function(t){var i=e(this).data("ui-resizable"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&e.ui.hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,u=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(e.extend(h,u&&l?{top:u,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&e(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",t)}})}}),e.ui.plugin.add("resizable","containment",{start:function(){var i,s,n,a,o,r,h,l=e(this).data("ui-resizable"),u=l.options,c=l.element,d=u.containment,p=d instanceof e?d.get(0):/parent/.test(d)?c.parent().get(0):d;p&&(l.containerElement=e(p),/document/.test(d)||d===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}):(i=e(p),s=[],e(["Top","Right","Left","Bottom"]).each(function(e,n){s[e]=t(i.css("padding"+n))}),l.containerOffset=i.offset(),l.containerPosition=i.position(),l.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},n=l.containerOffset,a=l.containerSize.height,o=l.containerSize.width,r=e.ui.hasScroll(p,"left")?p.scrollWidth:o,h=e.ui.hasScroll(p)?p.scrollHeight:a,l.parentData={element:p,left:n.left,top:n.top,width:r,height:h}))},resize:function(t){var i,s,n,a,o=e(this).data("ui-resizable"),r=o.options,h=o.containerOffset,l=o.position,u=o._aspectRatio||t.shiftKey,c={top:0,left:0},d=o.containerElement;d[0]!==document&&/static/.test(d.css("position"))&&(c=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-c.left),u&&(o.size.height=o.size.width/o.aspectRatio),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),u&&(o.size.width=o.size.height*o.aspectRatio),o.position.top=o._helper?h.top:0),o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top,i=Math.abs((o._helper?o.offset.left-c.left:o.offset.left-c.left)+o.sizeDiff.width),s=Math.abs((o._helper?o.offset.top-c.top:o.offset.top-h.top)+o.sizeDiff.height),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css("position")),n&&a&&(i-=o.parentData.left),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,u&&(o.size.height=o.size.width/o.aspectRatio)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,u&&(o.size.width=o.size.height*o.aspectRatio))},stop:function(){var t=e(this).data("ui-resizable"),i=t.options,s=t.containerOffset,n=t.containerPosition,a=t.containerElement,o=e(t.helper),r=o.offset(),h=o.outerWidth()-t.sizeDiff.width,l=o.outerHeight()-t.sizeDiff.height;t._helper&&!i.animate&&/relative/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l}),t._helper&&!i.animate&&/static/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),e.ui.plugin.add("resizable","alsoResize",{start:function(){var t=e(this).data("ui-resizable"),i=t.options,s=function(t){e(t).each(function(){var t=e(this);t.data("ui-resizable-alsoresize",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)})})};"object"!=typeof i.alsoResize||i.alsoResize.parentNode?s(i.alsoResize):i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):e.each(i.alsoResize,function(e){s(e)})},resize:function(t,i){var s=e(this).data("ui-resizable"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0},h=function(t,s){e(t).each(function(){var t=e(this),n=e(this).data("ui-resizable-alsoresize"),a={},o=s&&s.length?s:t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(o,function(e,t){var i=(n[t]||0)+(r[t]||0);i&&i>=0&&(a[t]=i||null)}),t.css(a)})};"object"!=typeof n.alsoResize||n.alsoResize.nodeType?h(n.alsoResize):e.each(n.alsoResize,function(e,t){h(e,t)})},stop:function(){e(this).removeData("resizable-alsoresize")}}),e.ui.plugin.add("resizable","ghost",{start:function(){var t=e(this).data("ui-resizable"),i=t.options,s=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),t.ghost.appendTo(t.helper)},resize:function(){var t=e(this).data("ui-resizable");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=e(this).data("ui-resizable");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),e.ui.plugin.add("resizable","grid",{resize:function(){var t=e(this).data("ui-resizable"),i=t.options,s=t.size,n=t.originalSize,a=t.originalPosition,o=t.axis,r="number"==typeof i.grid?[i.grid,i.grid]:i.grid,h=r[0]||1,l=r[1]||1,u=Math.round((s.width-n.width)/h)*h,c=Math.round((s.height-n.height)/l)*l,d=n.width+u,p=n.height+c,f=i.maxWidth&&d>i.maxWidth,m=i.maxHeight&&p>i.maxHeight,g=i.minWidth&&i.minWidth>d,v=i.minHeight&&i.minHeight>p;i.grid=r,g&&(d+=h),v&&(p+=l),f&&(d-=h),m&&(p-=l),/^(se|s|e)$/.test(o)?(t.size.width=d,t.size.height=p):/^(ne)$/.test(o)?(t.size.width=d,t.size.height=p,t.position.top=a.top-c):/^(sw)$/.test(o)?(t.size.width=d,t.size.height=p,t.position.left=a.left-u):(t.size.width=d,t.size.height=p,t.position.top=a.top-c,t.position.left=a.left-u)}})})(jQuery); -------------------------------------------------------------------------------- /tests/samples/labeled001.js: -------------------------------------------------------------------------------- 1 | Outer: 2 | for (i = 1; i <= 10; i++) { 3 | document.write ("<br />"); 4 | document.write ("i: " + i); 5 | document.write (" j: "); 6 | 7 | Inner: 8 | for (j = 21; j <= 30; j++) { 9 | if (j == 24) 10 | { 11 | continue Inner; 12 | } 13 | document.write (j + " "); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/samples/let001.js: -------------------------------------------------------------------------------- 1 | var l = 10; 2 | { 3 | let l = 2; 4 | // At this point, l = 2. 5 | } 6 | // At this point, l = 10. 7 | 8 | // Additional ways to declare a variable using let. 9 | let index; 10 | let name = "Thomas Jefferson"; 11 | let answer = 42, counter, numpages = 10; 12 | let myarray = new Array(); 13 | -------------------------------------------------------------------------------- /tests/samples/misc001.js: -------------------------------------------------------------------------------- 1 | var testObj = {}; 2 | testObj[":"] = undefined; // Breaks 3 | testObj["::"] = undefined; // Breaks 4 | testObj["a:"] = undefined; // Breaks 5 | testObj["."] = undefined; // OK 6 | testObj["{"] = undefined; // OK 7 | testObj["}"] = undefined; // OK 8 | testObj["["] = undefined; // Breaks 9 | testObj["]"] = undefined; // Breaks 10 | testObj["("] = undefined; // OK 11 | testObj[")"] = undefined; // OK 12 | testObj["="] = undefined; // Breaks 13 | testObj["-"] = undefined; // OK 14 | testObj["+"] = undefined; // OK 15 | testObj["*"] = undefined; // OK 16 | testObj["/"] = undefined; // OK 17 | testObj["\\"] = undefined; // Breaks 18 | testObj["%"] = undefined; // OK 19 | testObj["<"] = undefined; // Breaks 20 | testObj[">"] = undefined; // Breaks 21 | testObj["!"] = undefined; // OK 22 | testObj["?"] = undefined; // Breaks 23 | testObj[","] = undefined; // OK 24 | testObj["@"] = undefined; // Breaks 25 | testObj["#"] = undefined; // OK 26 | testObj["&"] = undefined; // OK 27 | testObj["|"] = undefined; // OK 28 | testObj["~"] = undefined; // OK 29 | testObj["`"] = undefined; // Breaks 30 | testObj["."] = undefined; // OK 31 | -------------------------------------------------------------------------------- /tests/samples/misc002.js: -------------------------------------------------------------------------------- 1 | var dateFormatters = { 2 | s : function(d) { return d.getSeconds() }, 3 | ss : function(d) { return zeroPad(d.getSeconds()) }, 4 | m : function(d) { return d.getMinutes() }, 5 | mm : function(d) { return zeroPad(d.getMinutes()) }, 6 | h : function(d) { return d.getHours() % 12 || 12 }, 7 | hh : function(d) { return zeroPad(d.getHours() % 12 || 12) }, 8 | H : function(d) { return d.getHours() }, 9 | HH : function(d) { return zeroPad(d.getHours()) }, 10 | d : function(d) { return d.getDate() }, 11 | dd : function(d) { return zeroPad(d.getDate()) }, 12 | ddd : function(d,o) { return o.dayNamesShort[d.getDay()] }, 13 | dddd: function(d,o) { return o.dayNames[d.getDay()] }, 14 | M : function(d) { return d.getMonth() + 1 }, 15 | MM : function(d) { return zeroPad(d.getMonth() + 1) }, 16 | MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] }, 17 | MMMM: function(d,o) { return o.monthNames[d.getMonth()] }, 18 | yy : function(d) { return (d.getFullYear()+'').substring(2) }, 19 | yyyy: function(d) { return d.getFullYear() }, 20 | t : function(d) { return d.getHours() < 12 ? 'a' : 'p' }, 21 | tt : function(d) { return d.getHours() < 12 ? 'am' : 'pm' }, 22 | T : function(d) { return d.getHours() < 12 ? 'A' : 'P' }, 23 | TT : function(d) { return d.getHours() < 12 ? 'AM' : 'PM' }, 24 | u : function(d) { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") }, 25 | S : function(d) { 26 | var date = d.getDate(); 27 | if (date > 10 && date < 20) { 28 | return 'th'; 29 | } 30 | return ['st', 'nd', 'rd'][date%10-1] || 'th'; 31 | }, 32 | w : function(d, o) { // local 33 | return o.weekNumberCalculation(d); 34 | }, 35 | W : function(d) { // ISO 36 | return iso8601Week(d); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /tests/samples/misc003.js: -------------------------------------------------------------------------------- 1 | var child_process = require('child_process'); 2 | var fs = require('fs'); 3 | var optimist = require('optimist'); 4 | var phantom = require('phantom'); 5 | 6 | var argv = optimist 7 | .usage('Usage: depict URL OUT_FILE [OPTIONS]') 8 | .options('h', { 9 | alias: 'help', 10 | describe: 'Display help', 11 | default: false 12 | }) 13 | .options('s', { 14 | alias: 'selector', 15 | describe: 'CSS selector', 16 | default: 'body' 17 | }) 18 | .options('c', { 19 | alias: 'css', 20 | describe: 'CSS file to include in rendering', 21 | default: false 22 | }) 23 | .options('H', { 24 | alias: 'hide-selector', 25 | describe: 'Hide attributes of this selector before rendering.', 26 | default: false 27 | }) 28 | .options('w', { 29 | alias: 'browser-width', 30 | describe: 'Specify the desired browser width.', 31 | default: 1440 32 | }) 33 | .options('d', { 34 | alias: 'delay', 35 | describe: 'Specify a delay time, in milliseconds.', 36 | default: 1000 37 | }) 38 | .check(function(argv) { 39 | if (argv._.length !== 2) throw new Error('URL and OUT_FILE must be given.'); 40 | }) 41 | .argv; 42 | 43 | if (argv.h || argv.help) return optimist.showHelp(); 44 | 45 | // Append 'http://' if protocol not specified 46 | var url = argv._[0]; 47 | if (!url.match(/^\w+:\/\//)) { 48 | url = 'http://' + url; 49 | } 50 | 51 | var selector = argv.s || argv.selector; 52 | var out_file = argv._[1]; 53 | 54 | var css_file = argv.c || argv.css; 55 | var css_text = ''; 56 | if (css_file) { 57 | css_text = fs.readFileSync(css_file, 'utf8'); 58 | } 59 | 60 | var hide_selector = argv.H || argv["hide-selector"]; 61 | if (hide_selector) { 62 | css_text += "\n\n " + hide_selector + " { display: none; }\n"; 63 | } 64 | 65 | var viewport_width = argv.w || argv['browser-width']; 66 | var delay_time = argv.d || argv['delay']; 67 | 68 | function depict(url, out_file, selector, css_text) { 69 | // phantomjs heavily relies on callback functions 70 | 71 | var page; 72 | var ph; 73 | 74 | console.log('\nRequesting', url); 75 | 76 | phantom.create(createPage) 77 | 78 | function createPage(_ph) { 79 | ph = _ph; 80 | ph.createPage(openPage); 81 | } 82 | 83 | function openPage(_page) { 84 | page = _page; 85 | page.set('onError', function() { return; }); 86 | page.onConsoleMessage = function (msg) { console.log(msg); }; 87 | page.open(url, prepForRender); 88 | page.set('viewportSize', {width: viewport_width, height: 900}); // The height isn't taken into account here but phantomjs requires an object with both a width and a height. 89 | } 90 | 91 | function prepForRender(status) { 92 | page.evaluate(runInPhantomBrowser, renderImage, selector, css_text); 93 | } 94 | 95 | function runInPhantomBrowser(selector, css_text) { 96 | if (css_text) { 97 | var style = document.createElement('style'); 98 | style.appendChild(document.createTextNode(css_text)); 99 | document.head.appendChild(style); 100 | } 101 | 102 | var element = document.querySelector(selector); 103 | return element.getBoundingClientRect(); 104 | } 105 | 106 | function renderImage(rect) { 107 | setTimeout(function(){ 108 | page.set('clipRect', rect); 109 | page.render(out_file, cleanup); 110 | }, delay_time) 111 | } 112 | 113 | function cleanup() { 114 | console.log('Saved imaged to', out_file); 115 | ph.exit(); 116 | } 117 | } 118 | 119 | depict(url, out_file, selector, css_text); 120 | -------------------------------------------------------------------------------- /tests/samples/misc004.js: -------------------------------------------------------------------------------- 1 | Ee.addDays = l, Ee.cloneDate = d, Ee.parseDate = p, Ee.parseISO8601 = m, Ee.parseTime = y, Ee.formatDate = w, Ee.formatDates = b; 2 | var ke = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"], 3 | He = 864e5, 4 | Fe = 36e5, 5 | Re = 6e4; 6 | -------------------------------------------------------------------------------- /tests/samples/misc005.js: -------------------------------------------------------------------------------- 1 | gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { 2 | if (gapi.iframes && gapi.iframes.getContext) { 3 | gapi.iframes.getContext().openChild({ 4 | url: 'https://www.blogger.com/navbar.g?targetBlogID\0754325487278375417853\46blogName\75spirello\46publishMode\75PUBLISH_MODE_BLOGSPOT\46navbarType\75LIGHT\46layoutType\75LAYOUTS\46searchRoot\75http://spirelloskrimskramserier.blogspot.com/search\46blogLocale\75no\46v\0752\46homepageUrl\75http://spirelloskrimskramserier.blogspot.com/\46vt\0751357383140196484672', 5 | where: document.getElementById("navbar-iframe-container"), 6 | id: "navbar-iframe" 7 | }); 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /tests/samples/misc006.js: -------------------------------------------------------------------------------- 1 | (function(){(function(){function e(a){this.t={};this.tick=function(a,c,b){var d=void 0!=b?b:(new Date).getTime();this.t[a]=[d,c];if(void 0==b)try{window.console.timeStamp("CSI/"+a)}catch(e){}};this.tick("start",null,a)}var a;window.performance&&(a=window.performance.timing);var f=a?new e(a.responseStart):new e;window.jstiming={Timer:e,load:f};if(a){var c=a.navigationStart,d=a.responseStart;0<c&&d>=c&&(window.jstiming.srt=d-c)}if(a){var b=window.jstiming.load;0<c&&d>=c&&(b.tick("_wtsrt",void 0,c),b.tick("wtsrt_", 2 | "_wtsrt",d),b.tick("tbsd_","wtsrt_"))}try{a=null,window.chrome&&window.chrome.csi&&(a=Math.floor(window.chrome.csi().pageT),b&&0<c&&(b.tick("_tbnd",void 0,window.chrome.csi().startE),b.tick("tbnd_","_tbnd",c))),null==a&&window.gtbExternal&&(a=window.gtbExternal.pageT()),null==a&&window.external&&(a=window.external.pageT,b&&0<c&&(b.tick("_tbnd",void 0,window.external.startE),b.tick("tbnd_","_tbnd",c))),a&&(window.jstiming.pt=a)}catch(g){}})();})(); 3 | -------------------------------------------------------------------------------- /tests/samples/quotes001.js: -------------------------------------------------------------------------------- 1 | var test1 = '124234' + "asdasf" + '(\'events\',\'event4\')'; 2 | var test2 = "124234" + 'asdasf' + "(\"events\",\"event4\")"; 3 | -------------------------------------------------------------------------------- /tests/samples/return001.js: -------------------------------------------------------------------------------- 1 | function myfunction(arg1, arg2){ 2 | var r; 3 | r = arg1 * arg2; 4 | return(r); 5 | } 6 | -------------------------------------------------------------------------------- /tests/samples/return002.js: -------------------------------------------------------------------------------- 1 | function doWork() { 2 | return function calculate(y) { return y + 1; }; 3 | } 4 | 5 | var func = doWork(); 6 | var x = func(5); 7 | document.write(x); 8 | -------------------------------------------------------------------------------- /tests/samples/switch001.js: -------------------------------------------------------------------------------- 1 | function MyObjectType(obj) { 2 | switch (obj.constructor) { 3 | case Date: 4 | document.write("Object is a Date."); 5 | break; 6 | case Number: 7 | document.write("Object is a Number."); 8 | break; 9 | case String: 10 | document.write("Object is a String."); 11 | break; 12 | default: 13 | document.write("Object is unknown."); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/samples/switch002.js: -------------------------------------------------------------------------------- 1 | function MyObjectType(obj) { 2 | switch (obj.constructor) { 3 | case Date: 4 | document.write("Object is a Date."); 5 | case Number: 6 | document.write("Object is a Number."); 7 | case String: 8 | document.write("Object is a String."); 9 | default: 10 | document.write("Object is unknown."); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/samples/this001.js: -------------------------------------------------------------------------------- 1 | function Car(color, make, model){ 2 | this.color = color; 3 | this.make = make; 4 | this.model = model; 5 | } 6 | -------------------------------------------------------------------------------- /tests/samples/this002.js: -------------------------------------------------------------------------------- 1 | document.getElementById("clicker").addEventListener("click", eventHandler, false); 2 | 3 | function eventHandler(ev) { 4 | document.write(this.toString()); 5 | } 6 | -------------------------------------------------------------------------------- /tests/samples/throw001.js: -------------------------------------------------------------------------------- 1 | try { 2 | throw new Error(200, "x equals zero"); 3 | } 4 | catch (e) { 5 | document.write(e.message); 6 | } 7 | -------------------------------------------------------------------------------- /tests/samples/trycatch001.js: -------------------------------------------------------------------------------- 1 | try { 2 | addalert("bad call"); 3 | } 4 | catch(e) { 5 | document.write ("Error Message: " + e.message); 6 | document.write ("<br />"); 7 | document.write ("Error Code: "); 8 | document.write (e.number & 0xFFFF); 9 | document.write ("<br />"); 10 | document.write ("Error Name: " + e.name); 11 | } 12 | -------------------------------------------------------------------------------- /tests/samples/trycatch002.js: -------------------------------------------------------------------------------- 1 | try { 2 | document.write("Outer try running...<br/>"); 3 | 4 | try { 5 | document.write("Nested try running...<br/>"); 6 | throw new Error(301, "an error"); 7 | } 8 | catch (e) { 9 | document.write ("Nested catch caught " + e.message + "<br/>"); 10 | throw e; 11 | } 12 | finally { 13 | document.write ("Nested finally is running...<br/>"); 14 | } 15 | } 16 | catch (e) { 17 | document.write ("Outer catch caught " + e.message + "<br/>"); 18 | } 19 | finally { 20 | document.write ("Outer finally running"); 21 | } 22 | -------------------------------------------------------------------------------- /tests/samples/var001.js: -------------------------------------------------------------------------------- 1 | var index; 2 | var name = "Thomas Jefferson"; 3 | var answer = 42, counter, numpages = 10; 4 | var myarray = new Array(); 5 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/complextypes-array001.js: -------------------------------------------------------------------------------- 1 | var myArray = new Array(0, 2, 4); 2 | var myOtherArray = new Array(); 3 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/complextypes-array002.js: -------------------------------------------------------------------------------- 1 | var myArray = [0, 2, 4]; 2 | var myOtherArray = []; 3 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/complextypes-array003.js: -------------------------------------------------------------------------------- 1 | myArray[2] = "Hello"; 2 | var text = myArray[2]; 3 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/complextypes-object001.js: -------------------------------------------------------------------------------- 1 | var myObject = new Object(); 2 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/complextypes-object002.js: -------------------------------------------------------------------------------- 1 | var myObject = {}; 2 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/functions-expr-anonymous.js: -------------------------------------------------------------------------------- 1 | var square = function(number) {return number * number}; 2 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/functions-expr-names.js: -------------------------------------------------------------------------------- 1 | var factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)}; 2 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/functions001.js: -------------------------------------------------------------------------------- 1 | function hello() { 2 | alert("Hello, World!"); 3 | } 4 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/functions002.js: -------------------------------------------------------------------------------- 1 | var hello = function() { 2 | alert("Hello, World!"); 3 | }; 4 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/functions003.js: -------------------------------------------------------------------------------- 1 | var hello = new Function( 2 | 'alert("Hello, World!");' 3 | ); 4 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/functions004.js: -------------------------------------------------------------------------------- 1 | function hello(who) { 2 | alert("Hello, " + who + "!"); 3 | } 4 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/functions005.js: -------------------------------------------------------------------------------- 1 | var hello = function(who) { 2 | alert("Hello, " + who + "!"); 3 | }; 4 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/functions006.js: -------------------------------------------------------------------------------- 1 | var hello = new Function('who', 2 | 'alert("Hello, " + who + "!");' 3 | ); 4 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/primitivetypes-boolean.js: -------------------------------------------------------------------------------- 1 | var mayday = false; 2 | var birthday = true; 3 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/primitivetypes-numeric.js: -------------------------------------------------------------------------------- 1 | var sal = 20; 2 | var pal = 12.1; 3 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/primitivetypes-string.js: -------------------------------------------------------------------------------- 1 | var myName = "Some Name"; 2 | var myChar = 'f'; 3 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/variable001.js: -------------------------------------------------------------------------------- 1 | var c; 2 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/variable002.js: -------------------------------------------------------------------------------- 1 | var c = 0; 2 | -------------------------------------------------------------------------------- /tests/samples/wikibooks/variable003.js: -------------------------------------------------------------------------------- 1 | c = 1; 2 | -------------------------------------------------------------------------------- /tests/samples/with001.js: -------------------------------------------------------------------------------- 1 | with (Math){ 2 | x = cos(3 * PI) + sin (LN10) 3 | y = tan(14 * E) 4 | } 5 | -------------------------------------------------------------------------------- /tests/test_json.py: -------------------------------------------------------------------------------- 1 | import js2xml 2 | import js2xml.jsonlike 3 | 4 | 5 | def test_json(): 6 | jscode_snippets = [ 7 | ( 8 | r""" 9 | var arr1 = ["a","b","c"]; 10 | var arr2 = ["d","e","f"]; 11 | """, 12 | [["a", "b", "c"], ["d", "e", "f"]], 13 | ), 14 | ( 15 | r""" 16 | var arr1 = ["a", null, "c"]; 17 | var arr2 = [null, "e", null]; 18 | """, 19 | [["a", None, "c"], [None, "e", None]], 20 | ), 21 | ( 22 | r""" 23 | var arr1 = ["a", undefined, "c"]; 24 | var arr2 = [undefined, "e", null]; 25 | """, 26 | [["a", "undefined", "c"], ["undefined", "e", None]], 27 | ), 28 | ( 29 | r""" 30 | var i = -3.14; 31 | """, 32 | [], 33 | ), 34 | ( 35 | r""" 36 | money = { 37 | 'quarters': 20 38 | }; 39 | """, 40 | [{"quarters": 20}], 41 | ), 42 | ( 43 | r""" 44 | money = { 45 | quarters: 20 46 | }; 47 | """, 48 | [{"quarters": 20}], 49 | ), 50 | ( 51 | r""" 52 | currency = 'USD', 53 | money = { 54 | "value": 20, 55 | "currency": currency 56 | }; 57 | """, 58 | [{"currency": "currency", "value": 20}], 59 | ), 60 | ( 61 | r""" 62 | t = {a: "3", "b": 3, "3": 3.0}; 63 | """, 64 | [{"3": 3.0, "a": "3", "b": 3}], 65 | ), 66 | ( 67 | r""" 68 | money = { 69 | 'quarters': 10, 70 | 'addQuarters': function(amount) { 71 | this.quarters += amount; 72 | } 73 | }; 74 | money.addQuarters(10); 75 | """, 76 | [], 77 | ), 78 | ( 79 | r""" 80 | var money = { 81 | 'quarters': 10, 82 | 'something': [1,2,3,4], 83 | 'somethingelse': {'nested': [5,6,7,8]}, 84 | 'addQuarters': function(amount) { 85 | this.quarters += amount; 86 | } 87 | }; 88 | money.addQuarters(10); 89 | """, 90 | [[1, 2, 3, 4], {"nested": [5, 6, 7, 8]}], 91 | ), 92 | ( 93 | r""" 94 | var store = { 95 | 'apples': 10, 96 | 'carrots': [1,2,3,4], 97 | 'chicken': {'eggs': [5,6,7,8]} 98 | }; 99 | """, 100 | [{"apples": 10, "carrots": [1, 2, 3, 4], "chicken": {"eggs": [5, 6, 7, 8]}}], 101 | ), 102 | ( 103 | r""" 104 | var store1 = { 105 | 'apples': 10, 106 | 'carrots': [1,2,3,4], 107 | 'chicken': {'eggs': [5,6,7,8]} 108 | }; 109 | var store2 = { 110 | 'tomatoes': 20, 111 | 'potatoes': [9, false, 7, 6], 112 | 'spinach': {'cans': [true, 2]} 113 | }; 114 | """, 115 | [ 116 | {"apples": 10, "carrots": [1, 2, 3, 4], "chicken": {"eggs": [5, 6, 7, 8]}}, 117 | {"potatoes": [9, False, 7, 6], "spinach": {"cans": [True, 2]}, "tomatoes": 20}, 118 | ], 119 | ), 120 | ] 121 | for snippet, expected in jscode_snippets: 122 | jsxml = js2xml.parse(snippet) 123 | assert js2xml.jsonlike.getall(jsxml) == expected 124 | 125 | 126 | def test_findall(): 127 | jscode_snippets = [ 128 | ( 129 | r""" 130 | var arr1 = ["a","b","c"]; 131 | var arr2 = ["d","e","f"]; 132 | """, 133 | "//array", 134 | [["a", "b", "c"], ["d", "e", "f"]], 135 | ), 136 | ( 137 | r""" 138 | var arr1 = {"a": "b", "c": "d"}; 139 | var arr2 = {"e": 1, "f": 2}; 140 | """, 141 | "//object", 142 | [{"a": "b", "c": "d"}, {"e": 1, "f": 2}], 143 | ), 144 | ] 145 | 146 | for snippet, xp, expected in jscode_snippets: 147 | js = js2xml.parse(snippet) 148 | results = [] 149 | for r in js.xpath(xp): 150 | results.extend(js2xml.jsonlike.findall(r)) 151 | assert [js2xml.jsonlike.make_dict(r) for r in results] == expected 152 | 153 | 154 | def test_getall_complex(): 155 | jscode_snippets = [ 156 | ( 157 | r""" 158 | var needleParam = needleParam || {}; 159 | needleParam.chatGroup = "test"; 160 | needleParam.productId = "6341292"; 161 | needleParam.productPrice = "EUR 138.53".replace("$","n_").replace(/,/g,""); 162 | //Begin Needle (fan-sourcing platform) snippet 163 | jQuery(document).ready(function(){ 164 | 165 | var e = document.createElement("script"); e.type = "text/javascript"; 166 | e.async = true; 167 | e.src = document.location.protocol + 168 | 169 | "//overstock.needle.com/needle_service.js?1"; document.body.appendChild(e); 170 | 171 | }); 172 | // End Needle snippet 173 | """, 174 | [{}], 175 | ), 176 | ] 177 | 178 | for snippet, expected in jscode_snippets: 179 | jsxml = js2xml.parse(snippet) 180 | assert js2xml.jsonlike.getall(jsxml) == expected 181 | -------------------------------------------------------------------------------- /tests/test_parse.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import js2xml 4 | 5 | 6 | def test_parse(): 7 | jscode_snippets = [ 8 | r""" 9 | var i = 0; 10 | """, 11 | r""" 12 | document.write("\n"); 13 | """, 14 | r""" 15 | var t1 = "nested \"quote\"."; 16 | var t2 = 'nested \'quote\'.'; 17 | var t3 = 'nested \"quote\".'; 18 | var t2 = "nested \'quote\'."; 19 | """, 20 | ] 21 | 22 | for snippet in jscode_snippets: 23 | assert js2xml.parse(snippet) is not None 24 | 25 | 26 | def test_parse_exception(): 27 | jscode_snippets = [ 28 | r""" 29 | var i = '; 30 | """, 31 | r""" 32 | { 33 | .document.write; 34 | } 35 | """, 36 | r""" 37 | var t = "nested "quote"."d; 38 | """, 39 | r""" 40 | t = -; 41 | """, 42 | ] 43 | 44 | for snippet in jscode_snippets: 45 | with pytest.raises(SyntaxError): 46 | js2xml.parse(snippet) 47 | 48 | 49 | def test_parse_string(): 50 | jscode_snippets = [ 51 | ( 52 | r""" 53 | var h = 'test'; 54 | var i = "test"; 55 | var j = ""; 56 | var k = '""'; 57 | var l = '"'; 58 | var m = ''; 59 | var n = "''"; 60 | var o = "'"; 61 | """, 62 | ["test", "test", "", '""', '"', "", "''", "'"], 63 | ), 64 | ( 65 | r""" 66 | var i = 'test\'s output'; 67 | """, 68 | [r"test's output"], 69 | ), 70 | ( 71 | r""" 72 | var i = 'test\ 73 | multiline'; 74 | """, 75 | [r"test multiline"], 76 | ), 77 | ( 78 | r""" 79 | var i = 'test\ 80 | long \ 81 | multiline'; 82 | """, 83 | [r"test long multiline"], 84 | ), 85 | ( 86 | r""" 87 | var i = ["\"", '\'']; 88 | var j = "test\'s output"; 89 | var k = "test\\'s output"; 90 | var l = "nested \"quotes\"."; 91 | """, 92 | ['"', "'", r"test's output", r"test\'s output", r'nested "quotes".'], 93 | ), 94 | ( 95 | r""" 96 | var i = 'https://www.blogger.com/navbar.g?targetBlogID\0754325487278375417853\46blogName\75spirello\46publishMode\75PUBLISH_MODE_BLOGSPOT\46navbarType\75LIGHT\46layoutType\75LAYOUTS\46searchRoot\75http://spirelloskrimskramserier.blogspot.com/search\46blogLocale\75no\46v\0752\46homepageUrl\75http://spirelloskrimskramserier.blogspot.com/\46vt\0751357383140196484672'; 97 | """, 98 | [ 99 | r"https://www.blogger.com/navbar.g?targetBlogID=4325487278375417853&blogName=spirello&publishMode=PUBLISH_MODE_BLOGSPOT&navbarType=LIGHT&layoutType=LAYOUTS&searchRoot=http://spirelloskrimskramserier.blogspot.com/search&blogLocale=no&v=2&homepageUrl=http://spirelloskrimskramserier.blogspot.com/&vt=1357383140196484672" 100 | ], 101 | ), 102 | ( 103 | r""" 104 | var i = "foo \ 105 | bar"; 106 | var j = "foo \ 107 | bar"; 108 | """, 109 | [r"foo bar", "foo bar"], 110 | ), 111 | ( 112 | # testing Unicode literals 113 | b""" 114 | var x = "\\u00A9 Netscape Communications 1"; 115 | """, 116 | [u"\u00a9 Netscape Communications 1"], 117 | ), 118 | ( 119 | # testing Unicode characters 120 | u""" 121 | var x = "\u00A9 Netscape Communications 2"; 122 | """.encode( 123 | "utf8" 124 | ), 125 | [u"\u00a9 Netscape Communications 2"], 126 | ), 127 | # a real example 128 | ( 129 | r""" 130 | var needleParam = needleParam || {}; 131 | needleParam.chatGroup = "test"; 132 | needleParam.productId = "6341292"; 133 | needleParam.productPrice = "EUR 138.53".replace("$","n_").replace(/,/g,""); 134 | //Begin Needle (fan-sourcing platform) snippet 135 | jQuery(document).ready(function(){ 136 | 137 | var e = document.createElement("script"); e.type = "text/javascript"; 138 | e.async = true; 139 | e.src = document.location.protocol + 140 | 141 | "//overstock.needle.com/needle_service.js?1"; document.body.appendChild(e); 142 | 143 | }); 144 | // End Needle snippet 145 | """, 146 | [ 147 | "test", 148 | "6341292", 149 | "EUR 138.53", 150 | "$", 151 | "n_", 152 | "", 153 | "script", 154 | "text/javascript", 155 | "//overstock.needle.com/needle_service.js?1", 156 | ], 157 | ), 158 | # test replacing some control characters 159 | ( 160 | r""" 161 | var name = "\u13e9\u0352\u0362\u044f\u2778\u00b3\u1d43\u034e\u034e\u0442\u035b\u13b7\u0362\u033b\u1d51A\u0362\u13de\u0001\u0001\u277c00b"; 162 | """, 163 | [ 164 | u"\u13e9\u0352\u0362\u044f\u2778\xb3\u1d43\u034e\u034e\u0442\u035b\u13b7\u0362\u033b\u1d51A\u0362\u13de\ufffd\ufffd\u277c00b" 165 | ], 166 | ), 167 | # surrogate pairs 168 | (r'''var name = "\ud835\udebd"''', [u"\U0001d6bd"]), 169 | ] 170 | 171 | for snippet, expected in jscode_snippets: 172 | jsxml = js2xml.parse(snippet) 173 | result = jsxml.xpath("//string/text()") 174 | assert result == expected 175 | 176 | 177 | def test_parse_url(): 178 | jscode_snippets = [ 179 | ( 180 | r""" 181 | var i = 'http://www.example.com'; 182 | """, 183 | [r"http://www.example.com"], 184 | ), 185 | ( 186 | r""" 187 | var i = 'http:\/\/www.example.com'; 188 | """, 189 | [r"http://www.example.com"], 190 | ), 191 | ] 192 | 193 | for snippet, expected in jscode_snippets: 194 | jsxml = js2xml.parse(snippet) 195 | result = jsxml.xpath("//string/text()") 196 | assert result == expected 197 | 198 | 199 | def test_parse_number(): 200 | jscode_snippets = [ 201 | ( 202 | r""" 203 | var i = 3; 204 | """, 205 | [r"3"], 206 | ), 207 | ( 208 | r""" 209 | var i = -3.14; 210 | """, 211 | [r"-3.14"], 212 | ), 213 | ] 214 | 215 | for snippet, expected in jscode_snippets: 216 | jsxml = js2xml.parse(snippet) 217 | result = jsxml.xpath("//number/@value") 218 | assert result == expected 219 | 220 | 221 | def test_parse_undefined(): 222 | jscode_snippets = [ 223 | ( 224 | r""" 225 | myArray = [0,1,,,4,5]; 226 | """, 227 | 2, 228 | ), 229 | ( 230 | r""" 231 | myArray = [,1,,,4,]; 232 | """, 233 | 3, # and not 4 234 | ), 235 | ( 236 | r""" 237 | myArray = [,1,,,4,,,]; 238 | """, 239 | 5, 240 | ), 241 | ] 242 | 243 | for snippet, expected in jscode_snippets: 244 | jsxml = js2xml.parse(snippet) 245 | result = jsxml.xpath("count(//array/undefined)") 246 | assert result == expected 247 | 248 | 249 | def test_parse_encoding(): 250 | 251 | jscode_snippets = [ 252 | ( 253 | u""" 254 | var test = "Daniel Gra\xf1a"; 255 | """, 256 | None, 257 | [u"Daniel Gra\xf1a"], 258 | ), 259 | ( 260 | u""" 261 | var test = "Daniel Gra\xf1a"; 262 | """.encode( 263 | "latin1" 264 | ), 265 | "latin1", 266 | [u"Daniel Gra\xf1a"], 267 | ), 268 | ] 269 | 270 | for snippet, encoding, expected in jscode_snippets: 271 | jsxml = js2xml.parse(snippet, encoding=encoding) 272 | result = jsxml.xpath("//string/text()") 273 | assert result == expected 274 | 275 | 276 | def test_keywords_as_object_keys(): 277 | # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords 278 | keywords = [ 279 | "abstract", 280 | "arguments", 281 | "await", 282 | "boolean", 283 | "break", 284 | "byte", 285 | "catch", 286 | "char", 287 | "class", 288 | "const", 289 | "continue", 290 | "debugger", 291 | "default", 292 | "delete", 293 | "do", 294 | "double", 295 | "else", 296 | "enum", 297 | "eval", 298 | "export", 299 | "extends", 300 | "false", 301 | "final", 302 | "finally", 303 | "float", 304 | "for", 305 | "function", 306 | "goto", 307 | "if", 308 | "implements", 309 | "import", 310 | "in", 311 | "instanceof", 312 | "int", 313 | "interface", 314 | "let", 315 | "long", 316 | "native", 317 | "new", 318 | "null", 319 | "package", 320 | "private", 321 | "protected", 322 | "public", 323 | "return", 324 | "short", 325 | "static", 326 | "super", 327 | "switch", 328 | "synchronized", 329 | "this", 330 | "throw", 331 | "throws", 332 | "transient", 333 | "true", 334 | "try", 335 | "typeof", 336 | "var", 337 | "void", 338 | "volatile", 339 | "while", 340 | "with", 341 | "yield", 342 | ] 343 | for keyword in keywords: 344 | jsxml = js2xml.parse("x={{{}: 0}}".format(keyword)) 345 | xpath = "//property[@name='{}']".format(keyword) 346 | matches = len(jsxml.xpath(xpath)) 347 | assert matches == 1 348 | -------------------------------------------------------------------------------- /tests/test_syntax.py: -------------------------------------------------------------------------------- 1 | import js2xml 2 | 3 | 4 | def test_syntax(): 5 | jscode_snippets = [ 6 | # strings 7 | r""" 8 | "test"; 9 | """, 10 | r""" 11 | "test\ 12 | multiline"; 13 | """, 14 | # numbers 15 | "3.14;", 16 | "-12;", 17 | "3.45e2;", 18 | "0377;", 19 | "0xFF;" 20 | # arrays 21 | "[]", 22 | "[1,2]", 23 | "[1,,2]", 24 | "[1,,2,,3,]", 25 | "['a', 'b','c']", 26 | "[a, 'b', c]", 27 | # objects 28 | "o = {};", 29 | "o = {a: 1};", 30 | "o = {a: 1, b: 2};", 31 | "o = {'c': 1, 'd': 2};", 32 | 'o = {"c": 1, "d": 2};', 33 | 'o = {"c": 1, d: "e"};', 34 | "e = {foo: 5, bar: 6, baz: ['Baz', 'Content']};", 35 | "e = {1: a, 2: b};", 36 | # other primitive data types 37 | "null;", 38 | "undefined;", 39 | "true;", 40 | "false;", 41 | # variables 42 | r""" 43 | var i; 44 | """, 45 | r""" 46 | var i,j,k; 47 | """, 48 | r""" 49 | var i = 0; 50 | """, 51 | r""" 52 | var i = "test"; 53 | """, 54 | r"""var z = 'foxes', r = 'birds';""", 55 | r""" 56 | var i, j, k = 0; 57 | """, 58 | r""" 59 | var i=1, j, k = 2; 60 | """, 61 | r""" 62 | var i = obj.prop; 63 | """, 64 | r"""var testObj = {};""", 65 | r"""var testObj = [];""", 66 | # assignements 67 | r""" 68 | i = b; 69 | """, 70 | r""" 71 | i.a = "b"; 72 | """, 73 | r""" 74 | i["a"] = "b"; 75 | """, 76 | r""" 77 | i[a] = "b"; 78 | """, 79 | # control structures 80 | r""" 81 | if (condition) { 82 | result = expression; 83 | };""", 84 | r""" 85 | if (condition) { 86 | result = expression; 87 | } else { 88 | result = alternative; 89 | };""", 90 | r""" 91 | if (exprA == exprB) { 92 | result = expression; 93 | } else if (expr2) { 94 | result = alternative1; 95 | } else { 96 | result = alternative2; 97 | };""", 98 | "result = condition ? expression : alternative;", 99 | # switch 100 | r""" 101 | switch (expr) { 102 | case SOMEVALUE: 103 | //statements; 104 | break; 105 | case ANOTHERVALUE: 106 | //statements; 107 | break; 108 | default: 109 | //statements; 110 | break; 111 | } 112 | """ 113 | # for loop 114 | r""" 115 | for (var i = 0; i < 5; i++) { 116 | a = i; 117 | } 118 | """, 119 | r""" 120 | for (var i = 0; i < 5; i++) { 121 | a = i 122 | } 123 | """, 124 | r""" 125 | for (var key in array) { 126 | continue; 127 | } 128 | """, 129 | r""" 130 | for (;;) { 131 | break; 132 | } 133 | """, 134 | r""" 135 | for (; i < len; i++) { 136 | text += cars[i] + "<br>"; 137 | } 138 | """, 139 | r""" 140 | for (var i = 0, len = cars.length, text = ""; i < len; i++) { 141 | text += cars[i] + "<br>"; 142 | } 143 | """, 144 | """ 145 | for (; i < len; ) { 146 | text += cars[i] + "<br>"; 147 | i++; 148 | } 149 | """, 150 | # while loop 151 | """ 152 | while (a<b) { 153 | a+=1; 154 | } 155 | """, 156 | """ 157 | do { 158 | a+=1; 159 | } while (a<b); 160 | """, 161 | # with 162 | """ 163 | with (document) { 164 | var a = getElementById('a'); 165 | var b = getElementById('b'); 166 | var c = getElementById('c'); 167 | }; 168 | """, 169 | # label 170 | r""" 171 | loop1: for (var a = 0; a < 10; a++) { 172 | if (a == 4) { 173 | break loop1; // Stops after the 4th attempt 174 | } 175 | alert('a = ' + a); 176 | loop2: for (var b = 0; b < 10; ++b) { 177 | if (b == 3) { 178 | continue loop2; // Number 3 is skipped 179 | } 180 | if (b == 6) { 181 | continue loop1; // Continues the first loop, 'finished' is not shown 182 | } 183 | alert('b = ' + b); 184 | } 185 | alert('finished') 186 | } 187 | block1: { 188 | alert('hello'); // Displays 'hello' 189 | break block1; 190 | alert('world'); // Will never get here 191 | } 192 | """, 193 | # functions 194 | """ 195 | function foo(p) { 196 | p = "bar"; 197 | } 198 | """, 199 | """ 200 | function hello() { 201 | alert('world'); 202 | } 203 | """, 204 | """ 205 | var x = function(y) { 206 | return y * y; 207 | }; 208 | """, 209 | """ 210 | var math = { 211 | 'factorial': function factorial(n) { 212 | if (n <= 1) 213 | return 1; 214 | return n * factorial(n - 1); 215 | } 216 | }; 217 | """, 218 | """ 219 | var anon = function() { 220 | alert('I am anonymous'); 221 | }; 222 | """, 223 | """ 224 | anon(); 225 | """, 226 | """ 227 | setTimeout(function() { 228 | alert('hello'); 229 | }, 1000) 230 | """, 231 | """ 232 | (function() { 233 | alert('foo'); 234 | }()); 235 | """, 236 | # get/set 237 | """ 238 | var obj = { 239 | get latest () { 240 | return "latest"; 241 | } 242 | } 243 | """, 244 | """ 245 | delete obj.latest; 246 | """, 247 | """ 248 | var o = { 249 | set current (str) { 250 | return this.log[this.log.length] = str; 251 | }, 252 | log: [] 253 | } 254 | """, 255 | # new 256 | """var mycar = new car("Eagle", "Talon TSi", 1993);""", 257 | # try / catch 258 | """ 259 | try { 260 | throw "myException"; // generates an exception 261 | } 262 | catch (e) { 263 | // statements to handle any exceptions 264 | logMyErrors(e); // pass exception object to error handler 265 | } 266 | """, 267 | """ 268 | try { 269 | addalert("bad call"); 270 | } 271 | catch(e) { 272 | document.write ("Error Message: " + e.message); 273 | document.write ("<br />"); 274 | document.write ("Error Code: "); 275 | document.write (e.number & 0xFFFF); 276 | document.write ("<br />"); 277 | document.write ("Error Name: " + e.name); 278 | } 279 | """, 280 | """ 281 | try { 282 | document.write("Outer try running...<br/>"); 283 | 284 | try { 285 | document.write("Nested try running...<br/>"); 286 | throw new Error(301, "an error"); 287 | } 288 | catch (e) { 289 | document.write ("Nested catch caught " + e.message + "<br/>"); 290 | throw e; 291 | } 292 | finally { 293 | document.write ("Nested finally is running...<br/>"); 294 | } 295 | } 296 | catch (e) { 297 | document.write ("Outer catch caught " + e.message + "<br/>"); 298 | } 299 | finally { 300 | document.write ("Outer finally running"); 301 | } 302 | """, 303 | """ 304 | new Map 305 | """, 306 | ] 307 | 308 | for snippet in jscode_snippets: 309 | assert js2xml.parse(snippet) is not None 310 | -------------------------------------------------------------------------------- /tests/test_utils_objects.py: -------------------------------------------------------------------------------- 1 | import js2xml 2 | from js2xml.utils.objects import findall, getall, make 3 | 4 | 5 | def test_json(): 6 | jscode_snippets = [ 7 | ( 8 | r""" 9 | var arr1 = ["a","b","c"]; 10 | var arr2 = ["d","e","f"]; 11 | """, 12 | [["a", "b", "c"], ["d", "e", "f"]], 13 | ), 14 | ( 15 | r""" 16 | var arr1 = ["a", null, "c"]; 17 | var arr2 = [null, "e", null]; 18 | """, 19 | [["a", None, "c"], [None, "e", None]], 20 | ), 21 | ( 22 | r""" 23 | var arr1 = ["a", undefined, "c"]; 24 | var arr2 = [undefined, "e", null]; 25 | """, 26 | [["a", "undefined", "c"], ["undefined", "e", None]], 27 | ), 28 | ( 29 | r""" 30 | var i = -3.14; 31 | """, 32 | [], 33 | ), 34 | ( 35 | r""" 36 | money = { 37 | 'quarters': 20 38 | }; 39 | """, 40 | [{"quarters": 20}], 41 | ), 42 | ( 43 | r""" 44 | money = { 45 | quarters: 20 46 | }; 47 | """, 48 | [{"quarters": 20}], 49 | ), 50 | ( 51 | r""" 52 | currency = 'USD', 53 | money = { 54 | "value": 20, 55 | "currency": currency 56 | }; 57 | """, 58 | [{"currency": "currency", "value": 20}], 59 | ), 60 | ( 61 | r""" 62 | t = {a: "3", "b": 3, "3": 3.0}; 63 | """, 64 | [{"3": 3.0, "a": "3", "b": 3}], 65 | ), 66 | ( 67 | r""" 68 | money = { 69 | 'quarters': 10, 70 | 'addQuarters': function(amount) { 71 | this.quarters += amount; 72 | } 73 | }; 74 | money.addQuarters(10); 75 | """, 76 | [], 77 | ), 78 | ( 79 | r""" 80 | var money = { 81 | 'quarters': 10, 82 | 'something': [1,2,3,4], 83 | 'somethingelse': {'nested': [5,6,7,8]}, 84 | 'addQuarters': function(amount) { 85 | this.quarters += amount; 86 | } 87 | }; 88 | money.addQuarters(10); 89 | """, 90 | [[1, 2, 3, 4], {"nested": [5, 6, 7, 8]}], 91 | ), 92 | ( 93 | r""" 94 | var store = { 95 | 'apples': 10, 96 | 'carrots': [1,2,3,4], 97 | 'chicken': {'eggs': [5,6,7,8]} 98 | }; 99 | """, 100 | [{"apples": 10, "carrots": [1, 2, 3, 4], "chicken": {"eggs": [5, 6, 7, 8]}}], 101 | ), 102 | ( 103 | r""" 104 | var store1 = { 105 | 'apples': 10, 106 | 'carrots': [1,2,3,4], 107 | 'chicken': {'eggs': [5,6,7,8]} 108 | }; 109 | var store2 = { 110 | 'tomatoes': 20, 111 | 'potatoes': [9, false, 7, 6], 112 | 'spinach': {'cans': [true, 2]} 113 | }; 114 | """, 115 | [ 116 | {"apples": 10, "carrots": [1, 2, 3, 4], "chicken": {"eggs": [5, 6, 7, 8]}}, 117 | {"potatoes": [9, False, 7, 6], "spinach": {"cans": [True, 2]}, "tomatoes": 20}, 118 | ], 119 | ), 120 | ] 121 | for snippet, expected in jscode_snippets: 122 | jsxml = js2xml.parse(snippet) 123 | assert getall(jsxml, types=[dict, list]) == expected 124 | 125 | 126 | def test_findall(): 127 | jscode_snippets = [ 128 | ( 129 | r""" 130 | var arr1 = ["a","b","c"]; 131 | var arr2 = ["d","e","f"]; 132 | """, 133 | "//array", 134 | [dict, list], 135 | [["a", "b", "c"], ["d", "e", "f"]], 136 | ), 137 | ( 138 | r""" 139 | var arr1 = {"a": "b", "c": "d"}; 140 | var arr2 = {"e": 1, "f": 2}; 141 | """, 142 | "//object", 143 | [dict, list], 144 | [{"a": "b", "c": "d"}, {"e": 1, "f": 2}], 145 | ), 146 | ] 147 | 148 | for snippet, xp, types, expected in jscode_snippets: 149 | js = js2xml.parse(snippet) 150 | results = [] 151 | for r in js.xpath(xp): 152 | results.extend(findall(r, types=types)) 153 | assert [make(r) for r in results] == expected 154 | 155 | 156 | def test_getall_complex(): 157 | jscode_snippets = [ 158 | ( 159 | r""" 160 | var needleParam = needleParam || {}; 161 | needleParam.chatGroup = "test"; 162 | needleParam.productId = "6341292"; 163 | needleParam.productPrice = "EUR 138.53".replace("$","n_").replace(/,/g,""); 164 | //Begin Needle (fan-sourcing platform) snippet 165 | jQuery(document).ready(function(){ 166 | 167 | var e = document.createElement("script"); e.type = "text/javascript"; 168 | e.async = true; 169 | e.src = document.location.protocol + 170 | 171 | "//overstock.needle.com/needle_service.js?1"; document.body.appendChild(e); 172 | 173 | }); 174 | // End Needle snippet 175 | """, 176 | [dict, list], 177 | [{}], 178 | ), 179 | ( 180 | r""" 181 | var needleParam = needleParam || {}; 182 | needleParam.chatGroup = "test"; 183 | needleParam.productId = "6341292"; 184 | needleParam.productPrice = "EUR 138.53".replace("$","n_").replace(/,/g,""); 185 | //Begin Needle (fan-sourcing platform) snippet 186 | jQuery(document).ready(function(){ 187 | 188 | var e = document.createElement("script"); e.type = "text/javascript"; 189 | e.async = true; 190 | e.src = document.location.protocol + 191 | 192 | "//overstock.needle.com/needle_service.js?1"; document.body.appendChild(e); 193 | 194 | }); 195 | // End Needle snippet 196 | """, 197 | [str], 198 | [ 199 | "test", 200 | "6341292", 201 | "EUR 138.53", 202 | "$", 203 | "n_", 204 | "", 205 | "script", 206 | "text/javascript", 207 | "//overstock.needle.com/needle_service.js?1", 208 | ], 209 | ), 210 | ( 211 | r""" 212 | var needleParam = needleParam || {}; 213 | needleParam.chatGroup = "test"; 214 | needleParam.productId = "6341292"; 215 | needleParam.productPrice = "EUR 138.53".replace("$","n_").replace(/,/g,""); 216 | //Begin Needle (fan-sourcing platform) snippet 217 | jQuery(document).ready(function(){ 218 | 219 | var e = document.createElement("script"); e.type = "text/javascript"; 220 | e.async = true; 221 | e.src = document.location.protocol + 222 | 223 | "//overstock.needle.com/needle_service.js?1"; document.body.appendChild(e); 224 | 225 | }); 226 | // End Needle snippet 227 | """, 228 | [bool], 229 | [True], 230 | ), 231 | ] 232 | 233 | for snippet, types, expected in jscode_snippets: 234 | jsxml = js2xml.parse(snippet) 235 | assert getall(jsxml, types=types) == expected 236 | -------------------------------------------------------------------------------- /tests/test_utils_vars.py: -------------------------------------------------------------------------------- 1 | from js2xml import parse 2 | from js2xml.utils.vars import get_vars 3 | 4 | 5 | def test_vars(): 6 | jscode_snippets = [ 7 | ( 8 | r""" 9 | var arr1 = ["a","b","c"]; 10 | var arr2 = ["d","e","f"]; 11 | """, 12 | {"arr1": ["a", "b", "c"], "arr2": ["d", "e", "f"]}, 13 | ), 14 | ( 15 | r""" 16 | var arr1 = ["a", null, "c"]; 17 | var arr2 = [null, "e", null]; 18 | """, 19 | {"arr1": ["a", None, "c"], "arr2": [None, "e", None]}, 20 | ), 21 | ( 22 | r""" 23 | var arr1 = ["a", undefined, "c"]; 24 | var arr2 = [undefined, "e", null]; 25 | """, 26 | {"arr1": ["a", "undefined", "c"], "arr2": ["undefined", "e", None]}, 27 | ), 28 | ( 29 | r""" 30 | var i = -3.14; 31 | """, 32 | {"i": -3.14}, 33 | ), 34 | ( 35 | r""" 36 | money = { 37 | 'quarters': 20 38 | }; 39 | """, 40 | {"money": {"quarters": 20}}, 41 | ), 42 | ( 43 | r""" 44 | money = { 45 | quarters: 20 46 | }; 47 | """, 48 | {"money": {"quarters": 20}}, 49 | ), 50 | ( 51 | r""" 52 | currency = 'USD'; 53 | money = { 54 | "value": 20, 55 | "currency": currency 56 | }; 57 | """, 58 | {"currency": "USD", "money": {"currency": "currency", "value": 20}}, 59 | ), 60 | ( 61 | r""" 62 | t = {a: "3", "b": 3, "3": 3.0}; 63 | """, 64 | {"t": {"3": 3.0, "a": "3", "b": 3}}, 65 | ), 66 | ( 67 | r""" 68 | money = { 69 | 'quarters': 10, 70 | 'addQuarters': function(amount) { 71 | this.quarters += amount; 72 | } 73 | }; 74 | money.addQuarters(10); 75 | """, 76 | {"money": {"quarters": 10, "addQuarters": None}}, 77 | ), 78 | ( 79 | r""" 80 | var money = { 81 | 'quarters': 10, 82 | 'something': [1,2,3,4], 83 | 'somethingelse': {'nested': [5,6,7,8]}, 84 | 'addQuarters': function(amount) { 85 | this.quarters += amount; 86 | } 87 | }; 88 | money.addQuarters(10); 89 | """, 90 | { 91 | "money": { 92 | "quarters": 10, 93 | "addQuarters": None, 94 | "something": [1, 2, 3, 4], 95 | "somethingelse": {"nested": [5, 6, 7, 8]}, 96 | } 97 | }, 98 | ), 99 | ( 100 | r""" 101 | var store = { 102 | 'apples': 10, 103 | 'carrots': [1,2,3,4], 104 | 'chicken': {'eggs': [5,6,7,8]} 105 | }; 106 | """, 107 | {"store": {"apples": 10, "carrots": [1, 2, 3, 4], "chicken": {"eggs": [5, 6, 7, 8]}}}, 108 | ), 109 | ( 110 | r""" 111 | var store1 = { 112 | 'apples': 10, 113 | 'carrots': [1,2,3,4], 114 | 'chicken': {'eggs': [5,6,7,8]} 115 | }; 116 | var store2 = { 117 | 'tomatoes': 20, 118 | 'potatoes': [9, false, 7, 6], 119 | 'spinach': {'cans': [true, 2]} 120 | }; 121 | """, 122 | { 123 | "store1": { 124 | "apples": 10, 125 | "carrots": [1, 2, 3, 4], 126 | "chicken": {"eggs": [5, 6, 7, 8]}, 127 | }, 128 | "store2": { 129 | "potatoes": [9, False, 7, 6], 130 | "spinach": {"cans": [True, 2]}, 131 | "tomatoes": 20, 132 | }, 133 | }, 134 | ), 135 | ] 136 | for snippet, expected in jscode_snippets: 137 | tree = parse(snippet) 138 | assert get_vars(tree) == expected, (snippet, expected) 139 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = black,flake8,py 3 | 4 | [testenv] 5 | basepython = python3 6 | deps = 7 | pytest-cov>=2.8 8 | pytest>=5.4 9 | commands = 10 | pytest --cov=js2xml \ 11 | --cov-report=term-missing --cov-report=html --cov-report=xml \ 12 | --verbose {posargs: js2xml tests} 13 | 14 | [testenv:black] 15 | basepython = python3 16 | deps = 17 | black==21.6b0 18 | commands = 19 | black --check {posargs: js2xml setup.py tests} 20 | 21 | [testenv:flake8] 22 | basepython = python3 23 | deps = 24 | flake8==3.9.2 25 | commands = 26 | flake8 {posargs: js2xml setup.py tests} 27 | --------------------------------------------------------------------------------