From 64f41d53728a966f10aef6d7ffbc00853d754300 Mon Sep 17 00:00:00 2001 From: yo mama Date: Fri, 13 Feb 2015 02:41:42 -0800 Subject: fixed some basics --- README.md | 0 impattern/..gitignore.un~ | Bin 3984 -> 0 bytes impattern/.config.py.un~ | Bin 15453 -> 0 bytes impattern/.gitignore | 0 impattern/.imdither.html.un~ | Bin 11414 -> 0 bytes impattern/.pbutils.py.un~ | Bin 6728 -> 0 bytes impattern/.run.py.un~ | Bin 21953 -> 0 bytes impattern/.urlDownload.py.un~ | Bin 49596 -> 0 bytes impattern/1.png | Bin 90 -> 0 bytes impattern/460mask_file-0.png | Bin 287 -> 0 bytes impattern/460mask_file-1.png | Bin 97831 -> 0 bytes impattern/Math.uuid.js | 92 - impattern/bottle.py | 3605 --------------------- impattern/bottle.pyc | Bin 159903 -> 0 bytes impattern/config.py | 3 - impattern/config.pyc | Bin 250 -> 0 bytes impattern/dithers/1.png | Bin 90 -> 0 bytes impattern/dithers/10.png | Bin 2484 -> 0 bytes impattern/dithers/11.png | Bin 2484 -> 0 bytes impattern/dithers/12.png | Bin 2483 -> 0 bytes impattern/dithers/13.png | Bin 2467 -> 0 bytes impattern/dithers/14.png | Bin 2478 -> 0 bytes impattern/dithers/15.png | Bin 2487 -> 0 bytes impattern/dithers/16.png | Bin 2480 -> 0 bytes impattern/dithers/17.png | Bin 2461 -> 0 bytes impattern/dithers/18.png | Bin 2475 -> 0 bytes impattern/dithers/19.png | Bin 2471 -> 0 bytes impattern/dithers/2.png | Bin 2470 -> 0 bytes impattern/dithers/20.png | Bin 2472 -> 0 bytes impattern/dithers/21.png | Bin 2472 -> 0 bytes impattern/dithers/22.png | Bin 2477 -> 0 bytes impattern/dithers/23.png | Bin 2472 -> 0 bytes impattern/dithers/24.png | Bin 2467 -> 0 bytes impattern/dithers/25.png | Bin 2473 -> 0 bytes impattern/dithers/26.png | Bin 2480 -> 0 bytes impattern/dithers/27.png | Bin 2482 -> 0 bytes impattern/dithers/28.png | Bin 2482 -> 0 bytes impattern/dithers/29.png | Bin 2487 -> 0 bytes impattern/dithers/3.png | Bin 2469 -> 0 bytes impattern/dithers/30.png | Bin 2487 -> 0 bytes impattern/dithers/31.png | Bin 2478 -> 0 bytes impattern/dithers/32.png | Bin 2465 -> 0 bytes impattern/dithers/33.png | Bin 2484 -> 0 bytes impattern/dithers/34.png | Bin 2484 -> 0 bytes impattern/dithers/35.png | Bin 2476 -> 0 bytes impattern/dithers/36.png | Bin 2490 -> 0 bytes impattern/dithers/37.png | Bin 2495 -> 0 bytes impattern/dithers/38.png | Bin 2492 -> 0 bytes impattern/dithers/39.png | Bin 2482 -> 0 bytes impattern/dithers/4.png | Bin 2465 -> 0 bytes impattern/dithers/40.png | Bin 2485 -> 0 bytes impattern/dithers/41.png | Bin 2496 -> 0 bytes impattern/dithers/42.png | Bin 2494 -> 0 bytes impattern/dithers/43.png | Bin 2490 -> 0 bytes impattern/dithers/44.png | Bin 2491 -> 0 bytes impattern/dithers/45.png | Bin 2484 -> 0 bytes impattern/dithers/46.png | Bin 2490 -> 0 bytes impattern/dithers/47.png | Bin 2486 -> 0 bytes impattern/dithers/48.png | Bin 2490 -> 0 bytes impattern/dithers/49.png | Bin 2483 -> 0 bytes impattern/dithers/5.png | Bin 2476 -> 0 bytes impattern/dithers/50.png | Bin 2486 -> 0 bytes impattern/dithers/51.png | Bin 2485 -> 0 bytes impattern/dithers/52.png | Bin 2490 -> 0 bytes impattern/dithers/53.png | Bin 2494 -> 0 bytes impattern/dithers/54.png | Bin 2491 -> 0 bytes impattern/dithers/55.png | Bin 2474 -> 0 bytes impattern/dithers/56.png | Bin 2492 -> 0 bytes impattern/dithers/57.png | Bin 2492 -> 0 bytes impattern/dithers/58.png | Bin 2487 -> 0 bytes impattern/dithers/59.png | Bin 2493 -> 0 bytes impattern/dithers/6.png | Bin 2480 -> 0 bytes impattern/dithers/60.png | Bin 2488 -> 0 bytes impattern/dithers/61.png | Bin 2492 -> 0 bytes impattern/dithers/62.png | Bin 2487 -> 0 bytes impattern/dithers/63.png | Bin 2494 -> 0 bytes impattern/dithers/64.png | Bin 2487 -> 0 bytes impattern/dithers/65.png | Bin 2467 -> 0 bytes impattern/dithers/66.png | Bin 2471 -> 0 bytes impattern/dithers/67.png | Bin 2470 -> 0 bytes impattern/dithers/68.png | Bin 2471 -> 0 bytes impattern/dithers/69.png | Bin 2470 -> 0 bytes impattern/dithers/7.png | Bin 2474 -> 0 bytes impattern/dithers/70.png | Bin 2473 -> 0 bytes impattern/dithers/71.png | Bin 2477 -> 0 bytes impattern/dithers/72.png | Bin 2476 -> 0 bytes impattern/dithers/73.png | Bin 2484 -> 0 bytes impattern/dithers/74.png | Bin 2486 -> 0 bytes impattern/dithers/75.png | Bin 2490 -> 0 bytes impattern/dithers/76.png | Bin 2484 -> 0 bytes impattern/dithers/77.png | Bin 2492 -> 0 bytes impattern/dithers/78.png | Bin 2491 -> 0 bytes impattern/dithers/79.png | Bin 2496 -> 0 bytes impattern/dithers/8.png | Bin 2476 -> 0 bytes impattern/dithers/80.png | Bin 2493 -> 0 bytes impattern/dithers/81.png | Bin 2462 -> 0 bytes impattern/dithers/82.png | Bin 2472 -> 0 bytes impattern/dithers/83.png | Bin 2462 -> 0 bytes impattern/dithers/84.png | Bin 2467 -> 0 bytes impattern/dithers/85.png | Bin 2462 -> 0 bytes impattern/dithers/86.png | Bin 2468 -> 0 bytes impattern/dithers/87.png | Bin 2472 -> 0 bytes impattern/dithers/88.png | Bin 2475 -> 0 bytes impattern/dithers/89.png | Bin 2467 -> 0 bytes impattern/dithers/9.png | Bin 2477 -> 0 bytes impattern/dithers/90.png | Bin 2471 -> 0 bytes impattern/dithers/91.png | Bin 2467 -> 0 bytes impattern/dithers/92.png | Bin 2470 -> 0 bytes impattern/dithers/93.png | Bin 2483 -> 0 bytes impattern/dithers/94.png | Bin 2485 -> 0 bytes impattern/dithers/95.png | Bin 2485 -> 0 bytes impattern/dithers/96.png | Bin 2477 -> 0 bytes impattern/dithers/a0.png | Bin 138 -> 0 bytes impattern/dithers/a1.png | Bin 122 -> 0 bytes impattern/dithers/a10.png | Bin 129 -> 0 bytes impattern/dithers/a11.png | Bin 138 -> 0 bytes impattern/dithers/a12.png | Bin 125 -> 0 bytes impattern/dithers/a13.png | Bin 132 -> 0 bytes impattern/dithers/a14.png | Bin 91 -> 0 bytes impattern/dithers/a15.png | Bin 143 -> 0 bytes impattern/dithers/a16.png | Bin 87 -> 0 bytes impattern/dithers/a17.png | Bin 93 -> 0 bytes impattern/dithers/a18.png | Bin 163 -> 0 bytes impattern/dithers/a19.png | Bin 119 -> 0 bytes impattern/dithers/a2.png | Bin 123 -> 0 bytes impattern/dithers/a20.png | Bin 83 -> 0 bytes impattern/dithers/a21.png | Bin 111 -> 0 bytes impattern/dithers/a22.png | Bin 137 -> 0 bytes impattern/dithers/a23.png | Bin 116 -> 0 bytes impattern/dithers/a24.png | Bin 96 -> 0 bytes impattern/dithers/a25.png | Bin 108 -> 0 bytes impattern/dithers/a26.png | Bin 138 -> 0 bytes impattern/dithers/a27.png | Bin 108 -> 0 bytes impattern/dithers/a28.png | Bin 115 -> 0 bytes impattern/dithers/a29.png | Bin 93 -> 0 bytes impattern/dithers/a3.png | Bin 124 -> 0 bytes impattern/dithers/a30.png | Bin 88 -> 0 bytes impattern/dithers/a31.png | Bin 114 -> 0 bytes impattern/dithers/a32.png | Bin 139 -> 0 bytes impattern/dithers/a33.png | Bin 127 -> 0 bytes impattern/dithers/a34.png | Bin 92 -> 0 bytes impattern/dithers/a35.png | Bin 91 -> 0 bytes impattern/dithers/a36.png | Bin 87 -> 0 bytes impattern/dithers/a37.png | Bin 89 -> 0 bytes impattern/dithers/a38.png | Bin 83 -> 0 bytes impattern/dithers/a39.png | Bin 132 -> 0 bytes impattern/dithers/a4.png | Bin 92 -> 0 bytes impattern/dithers/a40.png | Bin 138 -> 0 bytes impattern/dithers/a41.png | Bin 113 -> 0 bytes impattern/dithers/a5.png | Bin 120 -> 0 bytes impattern/dithers/a6.png | Bin 105 -> 0 bytes impattern/dithers/a7.png | Bin 106 -> 0 bytes impattern/dithers/a8.png | Bin 110 -> 0 bytes impattern/dithers/a9.png | Bin 110 -> 0 bytes impattern/face.png | Bin 107 -> 0 bytes impattern/im/cgi-bin/.gallery.cgi.swo | Bin 20480 -> 0 bytes impattern/im/cgi-bin/.gallery.cgi.swp | Bin 36864 -> 0 bytes impattern/im/cgi-bin/.gallery.cgi.un~ | Bin 59038 -> 0 bytes impattern/im/cgi-bin/.imditherconf.cgi.swo | Bin 12288 -> 0 bytes impattern/im/cgi-bin/.imditherconf.cgi.swp | Bin 12288 -> 0 bytes impattern/im/cgi-bin/.imditherconf.cgi.un~ | Bin 51229 -> 0 bytes impattern/im/cgi-bin/.queryTest.cgi.un~ | Bin 7485 -> 0 bytes impattern/im/cgi-bin/.test.cgi.un~ | Bin 519 -> 0 bytes impattern/im/cgi-bin/1.png | Bin 2482 -> 0 bytes impattern/im/cgi-bin/db.py | 29 - impattern/im/cgi-bin/db.pyc | Bin 1515 -> 0 bytes impattern/im/cgi-bin/downloader.py | 114 - impattern/im/cgi-bin/downloader.pyc | Bin 2923 -> 0 bytes impattern/im/cgi-bin/gallery.cgi | 474 --- impattern/im/cgi-bin/imdither.cgi | 145 - impattern/im/cgi-bin/imditherconf.cgi | 23 - impattern/im/cgi-bin/imweb/databaser.py | 24 - impattern/im/cgi-bin/imweb/downImg | 55 - impattern/im/cgi-bin/imweb/downloader.py | 114 - impattern/im/cgi-bin/imweb/fieldstorager.py | 21 - impattern/im/cgi-bin/imweb/filenamer.py | 115 - impattern/im/cgi-bin/imweb/hexdirer.py | 38 - impattern/im/cgi-bin/imweb/saveImg | 48 - impattern/im/cgi-bin/pythonhelloworld.cgi | 5 - impattern/im/cgi-bin/queryTest.cgi | 9 - impattern/im/cgi-bin/test.cgi | 49 - impattern/im/cgi-bin/youtube-dl | 1 - impattern/im/imdither/.fieldstorager.py.un~ | Bin 1130 -> 0 bytes impattern/im/imdither/.filenamer.py.un~ | Bin 11672 -> 0 bytes impattern/im/imdither/.gallery_style.css.swp | Bin 12288 -> 0 bytes impattern/im/imdither/.hexdirer.py.un~ | Bin 10637 -> 0 bytes impattern/im/imdither/.imdither.py.swp | Bin 16384 -> 0 bytes impattern/im/imdither/.imdither.py.un~ | Bin 281434 -> 0 bytes impattern/im/imdither/.imdither_tests.py.un~ | Bin 8586 -> 0 bytes impattern/im/imdither/.index.html.un~ | Bin 164842 -> 0 bytes impattern/im/imdither/.inventnamer.py.un~ | Bin 5823 -> 0 bytes impattern/im/imdither/.isotope_config.js.swp | Bin 16384 -> 0 bytes impattern/im/imdither/.isotope_config.js.un~ | Bin 16101 -> 0 bytes impattern/im/imdither/.sorted.un~ | Bin 6260 -> 0 bytes impattern/im/imdither/.style.css.un~ | Bin 5290 -> 0 bytes impattern/im/imdither/.test.html.un~ | Bin 32199 -> 0 bytes impattern/im/imdither/bottle-loader.py | 47 - impattern/im/imdither/bottle.py | 3081 ------------------ impattern/im/imdither/dithers/1.png | Bin 2455 -> 0 bytes impattern/im/imdither/dithers/10.png | Bin 2484 -> 0 bytes impattern/im/imdither/dithers/11.png | Bin 2484 -> 0 bytes impattern/im/imdither/dithers/12.png | Bin 2483 -> 0 bytes impattern/im/imdither/dithers/13.png | Bin 2467 -> 0 bytes impattern/im/imdither/dithers/14.png | Bin 2478 -> 0 bytes impattern/im/imdither/dithers/15.png | Bin 2487 -> 0 bytes impattern/im/imdither/dithers/16.png | Bin 2480 -> 0 bytes impattern/im/imdither/dithers/17.png | Bin 2461 -> 0 bytes impattern/im/imdither/dithers/18.png | Bin 2475 -> 0 bytes impattern/im/imdither/dithers/19.png | Bin 2471 -> 0 bytes impattern/im/imdither/dithers/2.png | Bin 2470 -> 0 bytes impattern/im/imdither/dithers/20.png | Bin 2472 -> 0 bytes impattern/im/imdither/dithers/21.png | Bin 2472 -> 0 bytes impattern/im/imdither/dithers/22.png | Bin 2477 -> 0 bytes impattern/im/imdither/dithers/23.png | Bin 2472 -> 0 bytes impattern/im/imdither/dithers/24.png | Bin 2467 -> 0 bytes impattern/im/imdither/dithers/25.png | Bin 2473 -> 0 bytes impattern/im/imdither/dithers/26.png | Bin 2480 -> 0 bytes impattern/im/imdither/dithers/27.png | Bin 2482 -> 0 bytes impattern/im/imdither/dithers/28.png | Bin 2482 -> 0 bytes impattern/im/imdither/dithers/29.png | Bin 2487 -> 0 bytes impattern/im/imdither/dithers/3.png | Bin 2469 -> 0 bytes impattern/im/imdither/dithers/30.png | Bin 2487 -> 0 bytes impattern/im/imdither/dithers/31.png | Bin 2478 -> 0 bytes impattern/im/imdither/dithers/32.png | Bin 2465 -> 0 bytes impattern/im/imdither/dithers/33.png | Bin 2484 -> 0 bytes impattern/im/imdither/dithers/34.png | Bin 2484 -> 0 bytes impattern/im/imdither/dithers/35.png | Bin 2476 -> 0 bytes impattern/im/imdither/dithers/36.png | Bin 2490 -> 0 bytes impattern/im/imdither/dithers/37.png | Bin 2495 -> 0 bytes impattern/im/imdither/dithers/38.png | Bin 2492 -> 0 bytes impattern/im/imdither/dithers/39.png | Bin 2482 -> 0 bytes impattern/im/imdither/dithers/4.png | Bin 2465 -> 0 bytes impattern/im/imdither/dithers/40.png | Bin 2485 -> 0 bytes impattern/im/imdither/dithers/41.png | Bin 2496 -> 0 bytes impattern/im/imdither/dithers/42.png | Bin 2494 -> 0 bytes impattern/im/imdither/dithers/43.png | Bin 2490 -> 0 bytes impattern/im/imdither/dithers/44.png | Bin 2491 -> 0 bytes impattern/im/imdither/dithers/45.png | Bin 2484 -> 0 bytes impattern/im/imdither/dithers/46.png | Bin 2490 -> 0 bytes impattern/im/imdither/dithers/47.png | Bin 2486 -> 0 bytes impattern/im/imdither/dithers/48.png | Bin 2490 -> 0 bytes impattern/im/imdither/dithers/49.png | Bin 2483 -> 0 bytes impattern/im/imdither/dithers/5.png | Bin 2476 -> 0 bytes impattern/im/imdither/dithers/50.png | Bin 2486 -> 0 bytes impattern/im/imdither/dithers/51.png | Bin 2485 -> 0 bytes impattern/im/imdither/dithers/52.png | Bin 2490 -> 0 bytes impattern/im/imdither/dithers/53.png | Bin 2494 -> 0 bytes impattern/im/imdither/dithers/54.png | Bin 2491 -> 0 bytes impattern/im/imdither/dithers/55.png | Bin 2474 -> 0 bytes impattern/im/imdither/dithers/56.png | Bin 2492 -> 0 bytes impattern/im/imdither/dithers/57.png | Bin 2492 -> 0 bytes impattern/im/imdither/dithers/58.png | Bin 2487 -> 0 bytes impattern/im/imdither/dithers/59.png | Bin 2493 -> 0 bytes impattern/im/imdither/dithers/6.png | Bin 2480 -> 0 bytes impattern/im/imdither/dithers/60.png | Bin 2488 -> 0 bytes impattern/im/imdither/dithers/61.png | Bin 2492 -> 0 bytes impattern/im/imdither/dithers/62.png | Bin 2487 -> 0 bytes impattern/im/imdither/dithers/63.png | Bin 2494 -> 0 bytes impattern/im/imdither/dithers/64.png | Bin 2487 -> 0 bytes impattern/im/imdither/dithers/65.png | Bin 2467 -> 0 bytes impattern/im/imdither/dithers/66.png | Bin 2471 -> 0 bytes impattern/im/imdither/dithers/67.png | Bin 2470 -> 0 bytes impattern/im/imdither/dithers/68.png | Bin 2471 -> 0 bytes impattern/im/imdither/dithers/69.png | Bin 2470 -> 0 bytes impattern/im/imdither/dithers/7.png | Bin 2474 -> 0 bytes impattern/im/imdither/dithers/70.png | Bin 2473 -> 0 bytes impattern/im/imdither/dithers/71.png | Bin 2477 -> 0 bytes impattern/im/imdither/dithers/72.png | Bin 2476 -> 0 bytes impattern/im/imdither/dithers/73.png | Bin 2484 -> 0 bytes impattern/im/imdither/dithers/74.png | Bin 2486 -> 0 bytes impattern/im/imdither/dithers/75.png | Bin 2490 -> 0 bytes impattern/im/imdither/dithers/76.png | Bin 2484 -> 0 bytes impattern/im/imdither/dithers/77.png | Bin 2492 -> 0 bytes impattern/im/imdither/dithers/78.png | Bin 2491 -> 0 bytes impattern/im/imdither/dithers/79.png | Bin 2496 -> 0 bytes impattern/im/imdither/dithers/8.png | Bin 2476 -> 0 bytes impattern/im/imdither/dithers/80.png | Bin 2493 -> 0 bytes impattern/im/imdither/dithers/81.png | Bin 2462 -> 0 bytes impattern/im/imdither/dithers/82.png | Bin 2472 -> 0 bytes impattern/im/imdither/dithers/83.png | Bin 2462 -> 0 bytes impattern/im/imdither/dithers/84.png | Bin 2467 -> 0 bytes impattern/im/imdither/dithers/85.png | Bin 2462 -> 0 bytes impattern/im/imdither/dithers/86.png | Bin 2468 -> 0 bytes impattern/im/imdither/dithers/87.png | Bin 2472 -> 0 bytes impattern/im/imdither/dithers/88.png | Bin 2475 -> 0 bytes impattern/im/imdither/dithers/89.png | Bin 2467 -> 0 bytes impattern/im/imdither/dithers/9.png | Bin 2477 -> 0 bytes impattern/im/imdither/dithers/90.png | Bin 2471 -> 0 bytes impattern/im/imdither/dithers/91.png | Bin 2467 -> 0 bytes impattern/im/imdither/dithers/92.png | Bin 2470 -> 0 bytes impattern/im/imdither/dithers/93.png | Bin 2483 -> 0 bytes impattern/im/imdither/dithers/94.png | Bin 2485 -> 0 bytes impattern/im/imdither/dithers/95.png | Bin 2485 -> 0 bytes impattern/im/imdither/dithers/96.png | Bin 2477 -> 0 bytes impattern/im/imdither/dithers/a1.png | Bin 122 -> 0 bytes impattern/im/imdither/dithers/a10.png | Bin 129 -> 0 bytes impattern/im/imdither/dithers/a11.png | Bin 138 -> 0 bytes impattern/im/imdither/dithers/a12.png | Bin 125 -> 0 bytes impattern/im/imdither/dithers/a13.png | Bin 132 -> 0 bytes impattern/im/imdither/dithers/a14.png | Bin 91 -> 0 bytes impattern/im/imdither/dithers/a15.png | Bin 143 -> 0 bytes impattern/im/imdither/dithers/a16.png | Bin 87 -> 0 bytes impattern/im/imdither/dithers/a17.png | Bin 93 -> 0 bytes impattern/im/imdither/dithers/a18.png | Bin 163 -> 0 bytes impattern/im/imdither/dithers/a19.png | Bin 119 -> 0 bytes impattern/im/imdither/dithers/a2.png | Bin 123 -> 0 bytes impattern/im/imdither/dithers/a20.png | Bin 83 -> 0 bytes impattern/im/imdither/dithers/a21.png | Bin 111 -> 0 bytes impattern/im/imdither/dithers/a22.png | Bin 137 -> 0 bytes impattern/im/imdither/dithers/a23.png | Bin 116 -> 0 bytes impattern/im/imdither/dithers/a24.png | Bin 96 -> 0 bytes impattern/im/imdither/dithers/a25.png | Bin 108 -> 0 bytes impattern/im/imdither/dithers/a26.png | Bin 138 -> 0 bytes impattern/im/imdither/dithers/a27.png | Bin 108 -> 0 bytes impattern/im/imdither/dithers/a28.png | Bin 115 -> 0 bytes impattern/im/imdither/dithers/a29.png | Bin 93 -> 0 bytes impattern/im/imdither/dithers/a3.png | Bin 124 -> 0 bytes impattern/im/imdither/dithers/a30.png | Bin 88 -> 0 bytes impattern/im/imdither/dithers/a31.png | Bin 114 -> 0 bytes impattern/im/imdither/dithers/a32.png | Bin 139 -> 0 bytes impattern/im/imdither/dithers/a33.png | Bin 127 -> 0 bytes impattern/im/imdither/dithers/a34.png | Bin 92 -> 0 bytes impattern/im/imdither/dithers/a35.png | Bin 91 -> 0 bytes impattern/im/imdither/dithers/a36.png | Bin 87 -> 0 bytes impattern/im/imdither/dithers/a37.png | Bin 89 -> 0 bytes impattern/im/imdither/dithers/a38.png | Bin 83 -> 0 bytes impattern/im/imdither/dithers/a39.png | Bin 132 -> 0 bytes impattern/im/imdither/dithers/a4.png | Bin 92 -> 0 bytes impattern/im/imdither/dithers/a40.png | Bin 138 -> 0 bytes impattern/im/imdither/dithers/a41.png | Bin 113 -> 0 bytes impattern/im/imdither/dithers/a5.png | Bin 120 -> 0 bytes impattern/im/imdither/dithers/a6.png | Bin 105 -> 0 bytes impattern/im/imdither/dithers/a7.png | Bin 106 -> 0 bytes impattern/im/imdither/dithers/a8.png | Bin 110 -> 0 bytes impattern/im/imdither/dithers/a9.png | Bin 110 -> 0 bytes impattern/im/imdither/downloader.pyc | Bin 2779 -> 0 bytes impattern/im/imdither/gallery_isotope_config.js?v | 186 -- impattern/im/imdither/gallery_style.css | 51 - impattern/im/imdither/imdither_tests.py | 17 - impattern/im/imdither/index.html | 61 - impattern/im/imdither/isotope_config.js | 131 - impattern/im/imdither/jquery-1.10.1.min.js | 6 - impattern/im/imdither/jquery.isotope.min.js | 13 - impattern/im/imdither/jquery.masonry.min.js | 10 - impattern/im/imdither/jquery.min.js | 4 - impattern/im/imdither/main.js | 114 - impattern/im/imdither/sorted | 143 - impattern/im/imdither/style.css | 30 - impattern/im/imdither/test.html | 42 - impattern/im/js/ZeroClipboard.js | 311 -- impattern/im/js/jquery.js | 18 - impattern/imdither.html | 305 -- impattern/img/logo.png | Bin 8810 -> 0 bytes impattern/jquery.min.js | 18 - impattern/makeDither.py | 72 - impattern/makeDither.pyc | Bin 2817 -> 0 bytes impattern/pbutils.py | 235 -- impattern/pbutils.pyc | Bin 10042 -> 0 bytes impattern/run.py | 48 - impattern/s3.py | 618 ---- impattern/s3.pyc | Bin 22261 -> 0 bytes impattern/test.gif | Bin 700673 -> 0 bytes impattern/urlDownload.py | 33 - impattern/urlDownload.pyc | Bin 1675 -> 0 bytes 364 files changed, 10558 deletions(-) delete mode 100644 README.md delete mode 100644 impattern/..gitignore.un~ delete mode 100644 impattern/.config.py.un~ delete mode 100644 impattern/.gitignore delete mode 100644 impattern/.imdither.html.un~ delete mode 100644 impattern/.pbutils.py.un~ delete mode 100644 impattern/.run.py.un~ delete mode 100644 impattern/.urlDownload.py.un~ delete mode 100644 impattern/1.png delete mode 100644 impattern/460mask_file-0.png delete mode 100644 impattern/460mask_file-1.png delete mode 100644 impattern/Math.uuid.js delete mode 100644 impattern/bottle.py delete mode 100644 impattern/bottle.pyc delete mode 100644 impattern/config.py delete mode 100644 impattern/config.pyc delete mode 100644 impattern/dithers/1.png delete mode 100644 impattern/dithers/10.png delete mode 100644 impattern/dithers/11.png delete mode 100644 impattern/dithers/12.png delete mode 100644 impattern/dithers/13.png delete mode 100644 impattern/dithers/14.png delete mode 100644 impattern/dithers/15.png delete mode 100644 impattern/dithers/16.png delete mode 100644 impattern/dithers/17.png delete mode 100644 impattern/dithers/18.png delete mode 100644 impattern/dithers/19.png delete mode 100644 impattern/dithers/2.png delete mode 100644 impattern/dithers/20.png delete mode 100644 impattern/dithers/21.png delete mode 100644 impattern/dithers/22.png delete mode 100644 impattern/dithers/23.png delete mode 100644 impattern/dithers/24.png delete mode 100644 impattern/dithers/25.png delete mode 100644 impattern/dithers/26.png delete mode 100644 impattern/dithers/27.png delete mode 100644 impattern/dithers/28.png delete mode 100644 impattern/dithers/29.png delete mode 100644 impattern/dithers/3.png delete mode 100644 impattern/dithers/30.png delete mode 100644 impattern/dithers/31.png delete mode 100644 impattern/dithers/32.png delete mode 100644 impattern/dithers/33.png delete mode 100644 impattern/dithers/34.png delete mode 100644 impattern/dithers/35.png delete mode 100644 impattern/dithers/36.png delete mode 100644 impattern/dithers/37.png delete mode 100644 impattern/dithers/38.png delete mode 100644 impattern/dithers/39.png delete mode 100644 impattern/dithers/4.png delete mode 100644 impattern/dithers/40.png delete mode 100644 impattern/dithers/41.png delete mode 100644 impattern/dithers/42.png delete mode 100644 impattern/dithers/43.png delete mode 100644 impattern/dithers/44.png delete mode 100644 impattern/dithers/45.png delete mode 100644 impattern/dithers/46.png delete mode 100644 impattern/dithers/47.png delete mode 100644 impattern/dithers/48.png delete mode 100644 impattern/dithers/49.png delete mode 100644 impattern/dithers/5.png delete mode 100644 impattern/dithers/50.png delete mode 100644 impattern/dithers/51.png delete mode 100644 impattern/dithers/52.png delete mode 100644 impattern/dithers/53.png delete mode 100644 impattern/dithers/54.png delete mode 100644 impattern/dithers/55.png delete mode 100644 impattern/dithers/56.png delete mode 100644 impattern/dithers/57.png delete mode 100644 impattern/dithers/58.png delete mode 100644 impattern/dithers/59.png delete mode 100644 impattern/dithers/6.png delete mode 100644 impattern/dithers/60.png delete mode 100644 impattern/dithers/61.png delete mode 100644 impattern/dithers/62.png delete mode 100644 impattern/dithers/63.png delete mode 100644 impattern/dithers/64.png delete mode 100644 impattern/dithers/65.png delete mode 100644 impattern/dithers/66.png delete mode 100644 impattern/dithers/67.png delete mode 100644 impattern/dithers/68.png delete mode 100644 impattern/dithers/69.png delete mode 100644 impattern/dithers/7.png delete mode 100644 impattern/dithers/70.png delete mode 100644 impattern/dithers/71.png delete mode 100644 impattern/dithers/72.png delete mode 100644 impattern/dithers/73.png delete mode 100644 impattern/dithers/74.png delete mode 100644 impattern/dithers/75.png delete mode 100644 impattern/dithers/76.png delete mode 100644 impattern/dithers/77.png delete mode 100644 impattern/dithers/78.png delete mode 100644 impattern/dithers/79.png delete mode 100644 impattern/dithers/8.png delete mode 100644 impattern/dithers/80.png delete mode 100644 impattern/dithers/81.png delete mode 100644 impattern/dithers/82.png delete mode 100644 impattern/dithers/83.png delete mode 100644 impattern/dithers/84.png delete mode 100644 impattern/dithers/85.png delete mode 100644 impattern/dithers/86.png delete mode 100644 impattern/dithers/87.png delete mode 100644 impattern/dithers/88.png delete mode 100644 impattern/dithers/89.png delete mode 100644 impattern/dithers/9.png delete mode 100644 impattern/dithers/90.png delete mode 100644 impattern/dithers/91.png delete mode 100644 impattern/dithers/92.png delete mode 100644 impattern/dithers/93.png delete mode 100644 impattern/dithers/94.png delete mode 100644 impattern/dithers/95.png delete mode 100644 impattern/dithers/96.png delete mode 100644 impattern/dithers/a0.png delete mode 100644 impattern/dithers/a1.png delete mode 100644 impattern/dithers/a10.png delete mode 100644 impattern/dithers/a11.png delete mode 100644 impattern/dithers/a12.png delete mode 100644 impattern/dithers/a13.png delete mode 100644 impattern/dithers/a14.png delete mode 100644 impattern/dithers/a15.png delete mode 100644 impattern/dithers/a16.png delete mode 100644 impattern/dithers/a17.png delete mode 100644 impattern/dithers/a18.png delete mode 100644 impattern/dithers/a19.png delete mode 100644 impattern/dithers/a2.png delete mode 100644 impattern/dithers/a20.png delete mode 100644 impattern/dithers/a21.png delete mode 100644 impattern/dithers/a22.png delete mode 100644 impattern/dithers/a23.png delete mode 100644 impattern/dithers/a24.png delete mode 100644 impattern/dithers/a25.png delete mode 100644 impattern/dithers/a26.png delete mode 100644 impattern/dithers/a27.png delete mode 100644 impattern/dithers/a28.png delete mode 100644 impattern/dithers/a29.png delete mode 100644 impattern/dithers/a3.png delete mode 100644 impattern/dithers/a30.png delete mode 100644 impattern/dithers/a31.png delete mode 100644 impattern/dithers/a32.png delete mode 100644 impattern/dithers/a33.png delete mode 100644 impattern/dithers/a34.png delete mode 100644 impattern/dithers/a35.png delete mode 100644 impattern/dithers/a36.png delete mode 100644 impattern/dithers/a37.png delete mode 100644 impattern/dithers/a38.png delete mode 100644 impattern/dithers/a39.png delete mode 100644 impattern/dithers/a4.png delete mode 100644 impattern/dithers/a40.png delete mode 100644 impattern/dithers/a41.png delete mode 100644 impattern/dithers/a5.png delete mode 100644 impattern/dithers/a6.png delete mode 100644 impattern/dithers/a7.png delete mode 100644 impattern/dithers/a8.png delete mode 100644 impattern/dithers/a9.png delete mode 100644 impattern/face.png delete mode 100644 impattern/im/cgi-bin/.gallery.cgi.swo delete mode 100644 impattern/im/cgi-bin/.gallery.cgi.swp delete mode 100755 impattern/im/cgi-bin/.gallery.cgi.un~ delete mode 100644 impattern/im/cgi-bin/.imditherconf.cgi.swo delete mode 100644 impattern/im/cgi-bin/.imditherconf.cgi.swp delete mode 100755 impattern/im/cgi-bin/.imditherconf.cgi.un~ delete mode 100755 impattern/im/cgi-bin/.queryTest.cgi.un~ delete mode 100755 impattern/im/cgi-bin/.test.cgi.un~ delete mode 100755 impattern/im/cgi-bin/1.png delete mode 100755 impattern/im/cgi-bin/db.py delete mode 100755 impattern/im/cgi-bin/db.pyc delete mode 100755 impattern/im/cgi-bin/downloader.py delete mode 100755 impattern/im/cgi-bin/downloader.pyc delete mode 100755 impattern/im/cgi-bin/gallery.cgi delete mode 100755 impattern/im/cgi-bin/imdither.cgi delete mode 100755 impattern/im/cgi-bin/imditherconf.cgi delete mode 100755 impattern/im/cgi-bin/imweb/databaser.py delete mode 100755 impattern/im/cgi-bin/imweb/downImg delete mode 100755 impattern/im/cgi-bin/imweb/downloader.py delete mode 100755 impattern/im/cgi-bin/imweb/fieldstorager.py delete mode 100755 impattern/im/cgi-bin/imweb/filenamer.py delete mode 100755 impattern/im/cgi-bin/imweb/hexdirer.py delete mode 100755 impattern/im/cgi-bin/imweb/saveImg delete mode 100755 impattern/im/cgi-bin/pythonhelloworld.cgi delete mode 100755 impattern/im/cgi-bin/queryTest.cgi delete mode 100755 impattern/im/cgi-bin/test.cgi delete mode 160000 impattern/im/cgi-bin/youtube-dl delete mode 100755 impattern/im/imdither/.fieldstorager.py.un~ delete mode 100755 impattern/im/imdither/.filenamer.py.un~ delete mode 100644 impattern/im/imdither/.gallery_style.css.swp delete mode 100755 impattern/im/imdither/.hexdirer.py.un~ delete mode 100755 impattern/im/imdither/.imdither.py.swp delete mode 100755 impattern/im/imdither/.imdither.py.un~ delete mode 100755 impattern/im/imdither/.imdither_tests.py.un~ delete mode 100755 impattern/im/imdither/.index.html.un~ delete mode 100755 impattern/im/imdither/.inventnamer.py.un~ delete mode 100644 impattern/im/imdither/.isotope_config.js.swp delete mode 100644 impattern/im/imdither/.isotope_config.js.un~ delete mode 100755 impattern/im/imdither/.sorted.un~ delete mode 100755 impattern/im/imdither/.style.css.un~ delete mode 100755 impattern/im/imdither/.test.html.un~ delete mode 100755 impattern/im/imdither/bottle-loader.py delete mode 100755 impattern/im/imdither/bottle.py delete mode 100755 impattern/im/imdither/dithers/1.png delete mode 100755 impattern/im/imdither/dithers/10.png delete mode 100755 impattern/im/imdither/dithers/11.png delete mode 100755 impattern/im/imdither/dithers/12.png delete mode 100755 impattern/im/imdither/dithers/13.png delete mode 100755 impattern/im/imdither/dithers/14.png delete mode 100755 impattern/im/imdither/dithers/15.png delete mode 100755 impattern/im/imdither/dithers/16.png delete mode 100755 impattern/im/imdither/dithers/17.png delete mode 100755 impattern/im/imdither/dithers/18.png delete mode 100755 impattern/im/imdither/dithers/19.png delete mode 100755 impattern/im/imdither/dithers/2.png delete mode 100755 impattern/im/imdither/dithers/20.png delete mode 100755 impattern/im/imdither/dithers/21.png delete mode 100755 impattern/im/imdither/dithers/22.png delete mode 100755 impattern/im/imdither/dithers/23.png delete mode 100755 impattern/im/imdither/dithers/24.png delete mode 100755 impattern/im/imdither/dithers/25.png delete mode 100755 impattern/im/imdither/dithers/26.png delete mode 100755 impattern/im/imdither/dithers/27.png delete mode 100755 impattern/im/imdither/dithers/28.png delete mode 100755 impattern/im/imdither/dithers/29.png delete mode 100755 impattern/im/imdither/dithers/3.png delete mode 100755 impattern/im/imdither/dithers/30.png delete mode 100755 impattern/im/imdither/dithers/31.png delete mode 100755 impattern/im/imdither/dithers/32.png delete mode 100755 impattern/im/imdither/dithers/33.png delete mode 100755 impattern/im/imdither/dithers/34.png delete mode 100755 impattern/im/imdither/dithers/35.png delete mode 100755 impattern/im/imdither/dithers/36.png delete mode 100755 impattern/im/imdither/dithers/37.png delete mode 100755 impattern/im/imdither/dithers/38.png delete mode 100755 impattern/im/imdither/dithers/39.png delete mode 100755 impattern/im/imdither/dithers/4.png delete mode 100755 impattern/im/imdither/dithers/40.png delete mode 100755 impattern/im/imdither/dithers/41.png delete mode 100755 impattern/im/imdither/dithers/42.png delete mode 100755 impattern/im/imdither/dithers/43.png delete mode 100755 impattern/im/imdither/dithers/44.png delete mode 100755 impattern/im/imdither/dithers/45.png delete mode 100755 impattern/im/imdither/dithers/46.png delete mode 100755 impattern/im/imdither/dithers/47.png delete mode 100755 impattern/im/imdither/dithers/48.png delete mode 100755 impattern/im/imdither/dithers/49.png delete mode 100755 impattern/im/imdither/dithers/5.png delete mode 100755 impattern/im/imdither/dithers/50.png delete mode 100755 impattern/im/imdither/dithers/51.png delete mode 100755 impattern/im/imdither/dithers/52.png delete mode 100755 impattern/im/imdither/dithers/53.png delete mode 100755 impattern/im/imdither/dithers/54.png delete mode 100755 impattern/im/imdither/dithers/55.png delete mode 100755 impattern/im/imdither/dithers/56.png delete mode 100755 impattern/im/imdither/dithers/57.png delete mode 100755 impattern/im/imdither/dithers/58.png delete mode 100755 impattern/im/imdither/dithers/59.png delete mode 100755 impattern/im/imdither/dithers/6.png delete mode 100755 impattern/im/imdither/dithers/60.png delete mode 100755 impattern/im/imdither/dithers/61.png delete mode 100755 impattern/im/imdither/dithers/62.png delete mode 100755 impattern/im/imdither/dithers/63.png delete mode 100755 impattern/im/imdither/dithers/64.png delete mode 100755 impattern/im/imdither/dithers/65.png delete mode 100755 impattern/im/imdither/dithers/66.png delete mode 100755 impattern/im/imdither/dithers/67.png delete mode 100755 impattern/im/imdither/dithers/68.png delete mode 100755 impattern/im/imdither/dithers/69.png delete mode 100755 impattern/im/imdither/dithers/7.png delete mode 100755 impattern/im/imdither/dithers/70.png delete mode 100755 impattern/im/imdither/dithers/71.png delete mode 100755 impattern/im/imdither/dithers/72.png delete mode 100755 impattern/im/imdither/dithers/73.png delete mode 100755 impattern/im/imdither/dithers/74.png delete mode 100755 impattern/im/imdither/dithers/75.png delete mode 100755 impattern/im/imdither/dithers/76.png delete mode 100755 impattern/im/imdither/dithers/77.png delete mode 100755 impattern/im/imdither/dithers/78.png delete mode 100755 impattern/im/imdither/dithers/79.png delete mode 100755 impattern/im/imdither/dithers/8.png delete mode 100755 impattern/im/imdither/dithers/80.png delete mode 100755 impattern/im/imdither/dithers/81.png delete mode 100755 impattern/im/imdither/dithers/82.png delete mode 100755 impattern/im/imdither/dithers/83.png delete mode 100755 impattern/im/imdither/dithers/84.png delete mode 100755 impattern/im/imdither/dithers/85.png delete mode 100755 impattern/im/imdither/dithers/86.png delete mode 100755 impattern/im/imdither/dithers/87.png delete mode 100755 impattern/im/imdither/dithers/88.png delete mode 100755 impattern/im/imdither/dithers/89.png delete mode 100755 impattern/im/imdither/dithers/9.png delete mode 100755 impattern/im/imdither/dithers/90.png delete mode 100755 impattern/im/imdither/dithers/91.png delete mode 100755 impattern/im/imdither/dithers/92.png delete mode 100755 impattern/im/imdither/dithers/93.png delete mode 100755 impattern/im/imdither/dithers/94.png delete mode 100755 impattern/im/imdither/dithers/95.png delete mode 100755 impattern/im/imdither/dithers/96.png delete mode 100755 impattern/im/imdither/dithers/a1.png delete mode 100755 impattern/im/imdither/dithers/a10.png delete mode 100755 impattern/im/imdither/dithers/a11.png delete mode 100755 impattern/im/imdither/dithers/a12.png delete mode 100755 impattern/im/imdither/dithers/a13.png delete mode 100755 impattern/im/imdither/dithers/a14.png delete mode 100755 impattern/im/imdither/dithers/a15.png delete mode 100755 impattern/im/imdither/dithers/a16.png delete mode 100755 impattern/im/imdither/dithers/a17.png delete mode 100755 impattern/im/imdither/dithers/a18.png delete mode 100755 impattern/im/imdither/dithers/a19.png delete mode 100755 impattern/im/imdither/dithers/a2.png delete mode 100755 impattern/im/imdither/dithers/a20.png delete mode 100755 impattern/im/imdither/dithers/a21.png delete mode 100755 impattern/im/imdither/dithers/a22.png delete mode 100755 impattern/im/imdither/dithers/a23.png delete mode 100755 impattern/im/imdither/dithers/a24.png delete mode 100755 impattern/im/imdither/dithers/a25.png delete mode 100755 impattern/im/imdither/dithers/a26.png delete mode 100755 impattern/im/imdither/dithers/a27.png delete mode 100755 impattern/im/imdither/dithers/a28.png delete mode 100755 impattern/im/imdither/dithers/a29.png delete mode 100755 impattern/im/imdither/dithers/a3.png delete mode 100755 impattern/im/imdither/dithers/a30.png delete mode 100755 impattern/im/imdither/dithers/a31.png delete mode 100755 impattern/im/imdither/dithers/a32.png delete mode 100755 impattern/im/imdither/dithers/a33.png delete mode 100755 impattern/im/imdither/dithers/a34.png delete mode 100755 impattern/im/imdither/dithers/a35.png delete mode 100755 impattern/im/imdither/dithers/a36.png delete mode 100755 impattern/im/imdither/dithers/a37.png delete mode 100755 impattern/im/imdither/dithers/a38.png delete mode 100755 impattern/im/imdither/dithers/a39.png delete mode 100755 impattern/im/imdither/dithers/a4.png delete mode 100755 impattern/im/imdither/dithers/a40.png delete mode 100755 impattern/im/imdither/dithers/a41.png delete mode 100755 impattern/im/imdither/dithers/a5.png delete mode 100755 impattern/im/imdither/dithers/a6.png delete mode 100755 impattern/im/imdither/dithers/a7.png delete mode 100755 impattern/im/imdither/dithers/a8.png delete mode 100755 impattern/im/imdither/dithers/a9.png delete mode 100755 impattern/im/imdither/downloader.pyc delete mode 100644 impattern/im/imdither/gallery_isotope_config.js?v delete mode 100644 impattern/im/imdither/gallery_style.css delete mode 100755 impattern/im/imdither/imdither_tests.py delete mode 100755 impattern/im/imdither/index.html delete mode 100644 impattern/im/imdither/isotope_config.js delete mode 100755 impattern/im/imdither/jquery-1.10.1.min.js delete mode 100644 impattern/im/imdither/jquery.isotope.min.js delete mode 100755 impattern/im/imdither/jquery.masonry.min.js delete mode 100755 impattern/im/imdither/jquery.min.js delete mode 100755 impattern/im/imdither/main.js delete mode 100755 impattern/im/imdither/sorted delete mode 100755 impattern/im/imdither/style.css delete mode 100755 impattern/im/imdither/test.html delete mode 100644 impattern/im/js/ZeroClipboard.js delete mode 100644 impattern/im/js/jquery.js delete mode 100644 impattern/imdither.html delete mode 100644 impattern/img/logo.png delete mode 100644 impattern/jquery.min.js delete mode 100644 impattern/makeDither.py delete mode 100644 impattern/makeDither.pyc delete mode 100644 impattern/pbutils.py delete mode 100644 impattern/pbutils.pyc delete mode 100644 impattern/run.py delete mode 100644 impattern/s3.py delete mode 100644 impattern/s3.pyc delete mode 100644 impattern/test.gif delete mode 100644 impattern/urlDownload.py delete mode 100644 impattern/urlDownload.pyc diff --git a/README.md b/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/impattern/..gitignore.un~ b/impattern/..gitignore.un~ deleted file mode 100644 index b80a256..0000000 Binary files a/impattern/..gitignore.un~ and /dev/null differ diff --git a/impattern/.config.py.un~ b/impattern/.config.py.un~ deleted file mode 100644 index 91af382..0000000 Binary files a/impattern/.config.py.un~ and /dev/null differ diff --git a/impattern/.gitignore b/impattern/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/impattern/.imdither.html.un~ b/impattern/.imdither.html.un~ deleted file mode 100644 index 114ee9d..0000000 Binary files a/impattern/.imdither.html.un~ and /dev/null differ diff --git a/impattern/.pbutils.py.un~ b/impattern/.pbutils.py.un~ deleted file mode 100644 index 53a13c5..0000000 Binary files a/impattern/.pbutils.py.un~ and /dev/null differ diff --git a/impattern/.run.py.un~ b/impattern/.run.py.un~ deleted file mode 100644 index eeb8667..0000000 Binary files a/impattern/.run.py.un~ and /dev/null differ diff --git a/impattern/.urlDownload.py.un~ b/impattern/.urlDownload.py.un~ deleted file mode 100644 index 6676092..0000000 Binary files a/impattern/.urlDownload.py.un~ and /dev/null differ diff --git a/impattern/1.png b/impattern/1.png deleted file mode 100644 index a0aeccb..0000000 Binary files a/impattern/1.png and /dev/null differ diff --git a/impattern/460mask_file-0.png b/impattern/460mask_file-0.png deleted file mode 100644 index 7a624bd..0000000 Binary files a/impattern/460mask_file-0.png and /dev/null differ diff --git a/impattern/460mask_file-1.png b/impattern/460mask_file-1.png deleted file mode 100644 index f4abaf8..0000000 Binary files a/impattern/460mask_file-1.png and /dev/null differ diff --git a/impattern/Math.uuid.js b/impattern/Math.uuid.js deleted file mode 100644 index 7538e67..0000000 --- a/impattern/Math.uuid.js +++ /dev/null @@ -1,92 +0,0 @@ -/*! -Math.uuid.js (v1.4) -http://www.broofa.com -mailto:robert@broofa.com - -Copyright (c) 2010 Robert Kieffer -Dual licensed under the MIT and GPL licenses. -*/ - -/* - * Generate a random uuid. - * - * USAGE: Math.uuid(length, radix) - * length - the desired number of characters - * radix - the number of allowable values for each character. - * - * EXAMPLES: - * // No arguments - returns RFC4122, version 4 ID - * >>> Math.uuid() - * "92329D39-6F5C-4520-ABFC-AAB64544E172" - * - * // One argument - returns ID of the specified length - * >>> Math.uuid(15) // 15 character ID (default base=62) - * "VcydxgltxrVZSTV" - * - * // Two arguments - returns ID of the specified length, and radix. (Radix must be <= 62) - * >>> Math.uuid(8, 2) // 8 character ID (base=2) - * "01001010" - * >>> Math.uuid(8, 10) // 8 character ID (base=10) - * "47473046" - * >>> Math.uuid(8, 16) // 8 character ID (base=16) - * "098F4D35" - */ -(function() { - // Private array of chars to use - var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); - - Math.uuid = function (len, radix) { - var chars = CHARS, uuid = [], i; - radix = radix || chars.length; - - if (len) { - // Compact form - for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix]; - } else { - // rfc4122, version 4 form - var r; - - // rfc4122 requires these characters - uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; - uuid[14] = '4'; - - // Fill in random data. At i==19 set the high bits of clock sequence as - // per rfc4122, sec. 4.1.5 - for (i = 0; i < 36; i++) { - if (!uuid[i]) { - r = 0 | Math.random()*16; - uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; - } - } - } - - return uuid.join(''); - }; - - // A more performant, but slightly bulkier, RFC4122v4 solution. We boost performance - // by minimizing calls to random() - Math.uuidFast = function() { - var chars = CHARS, uuid = new Array(36), rnd=0, r; - for (var i = 0; i < 36; i++) { - if (i==8 || i==13 || i==18 || i==23) { - uuid[i] = '-'; - } else if (i==14) { - uuid[i] = '4'; - } else { - if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0; - r = rnd & 0xf; - rnd = rnd >> 4; - uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; - } - } - return uuid.join(''); - }; - - // A more compact, but less performant, RFC4122v4 solution: - Math.uuidCompact = function() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); - return v.toString(16); - }); - }; -})(); diff --git a/impattern/bottle.py b/impattern/bottle.py deleted file mode 100644 index 40a858d..0000000 --- a/impattern/bottle.py +++ /dev/null @@ -1,3605 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Bottle is a fast and simple micro-framework for small web applications. It -offers request dispatching (Routes) with url parameter support, templates, -a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and -template engines - all in a single file and with no dependencies other than the -Python Standard Library. - -Homepage and documentation: http://bottlepy.org/ - -Copyright (c) 2012, Marcel Hellkamp. -License: MIT (see LICENSE for details) -""" - -from __future__ import with_statement - -__author__ = 'Marcel Hellkamp' -__version__ = '0.12-dev' -__license__ = 'MIT' - -# The gevent server adapter needs to patch some modules before they are imported -# This is why we parse the commandline parameters here but handle them later -if __name__ == '__main__': - from optparse import OptionParser - _cmd_parser = OptionParser(usage="usage: %prog [options] package.module:app") - _opt = _cmd_parser.add_option - _opt("--version", action="store_true", help="show version number.") - _opt("-b", "--bind", metavar="ADDRESS", help="bind socket to ADDRESS.") - _opt("-s", "--server", default='wsgiref', help="use SERVER as backend.") - _opt("-p", "--plugin", action="append", help="install additional plugin/s.") - _opt("--debug", action="store_true", help="start server in debug mode.") - _opt("--reload", action="store_true", help="auto-reload on file changes.") - _cmd_options, _cmd_args = _cmd_parser.parse_args() - if _cmd_options.server and _cmd_options.server.startswith('gevent'): - import gevent.monkey; gevent.monkey.patch_all() - -import base64, cgi, email.utils, functools, hmac, imp, itertools, mimetypes,\ - os, re, subprocess, sys, tempfile, threading, time, warnings - -from datetime import date as datedate, datetime, timedelta -from tempfile import TemporaryFile -from traceback import format_exc, print_exc -from inspect import getargspec - -try: from simplejson import dumps as json_dumps, loads as json_lds -except ImportError: # pragma: no cover - try: from json import dumps as json_dumps, loads as json_lds - except ImportError: - try: from django.utils.simplejson import dumps as json_dumps, loads as json_lds - except ImportError: - def json_dumps(data): - raise ImportError("JSON support requires Python 2.6 or simplejson.") - json_lds = json_dumps - - - -# We now try to fix 2.5/2.6/3.1/3.2 incompatibilities. -# It ain't pretty but it works... Sorry for the mess. - -py = sys.version_info -py3k = py >= (3, 0, 0) -py25 = py < (2, 6, 0) -py31 = (3, 1, 0) <= py < (3, 2, 0) - -# Workaround for the missing "as" keyword in py3k. -def _e(): return sys.exc_info()[1] - -# Workaround for the "print is a keyword/function" Python 2/3 dilemma -# and a fallback for mod_wsgi (resticts stdout/err attribute access) -try: - _stdout, _stderr = sys.stdout.write, sys.stderr.write -except IOError: - _stdout = lambda x: sys.stdout.write(x) - _stderr = lambda x: sys.stderr.write(x) - -# Lots of stdlib and builtin differences. -if py3k: - import http.client as httplib - import _thread as thread - from urllib.parse import urljoin, SplitResult as UrlSplitResult - from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote - urlunquote = functools.partial(urlunquote, encoding='latin1') - from http.cookies import SimpleCookie - from collections import MutableMapping as DictMixin - import pickle - from io import BytesIO - from configparser import ConfigParser - basestring = str - unicode = str - json_loads = lambda s: json_lds(touni(s)) - callable = lambda x: hasattr(x, '__call__') - imap = map - def _raise(*a): raise a[0](a[1]).with_traceback(a[2]) -else: # 2.x - import httplib - import thread - from urlparse import urljoin, SplitResult as UrlSplitResult - from urllib import urlencode, quote as urlquote, unquote as urlunquote - from Cookie import SimpleCookie - from itertools import imap - import cPickle as pickle - from StringIO import StringIO as BytesIO - from ConfigParser import SafeConfigParser as ConfigParser - if py25: - msg = "Python 2.5 support may be dropped in future versions of Bottle." - warnings.warn(msg, DeprecationWarning) - from UserDict import DictMixin - def next(it): return it.next() - bytes = str - else: # 2.6, 2.7 - from collections import MutableMapping as DictMixin - json_loads = json_lds - eval(compile('def _raise(*a): raise a[0], a[1], a[2]', '', 'exec')) - -# Some helpers for string/byte handling -def tob(s, enc='utf8'): - return s.encode(enc) if isinstance(s, unicode) else bytes(s) -def touni(s, enc='utf8', err='strict'): - return s.decode(enc, err) if isinstance(s, bytes) else unicode(s) -tonat = touni if py3k else tob - -# 3.2 fixes cgi.FieldStorage to accept bytes (which makes a lot of sense). -# 3.1 needs a workaround. -if py31: - from io import TextIOWrapper - class NCTextIOWrapper(TextIOWrapper): - def close(self): pass # Keep wrapped buffer open. - - -# A bug in functools causes it to break if the wrapper is an instance method -def update_wrapper(wrapper, wrapped, *a, **ka): - try: functools.update_wrapper(wrapper, wrapped, *a, **ka) - except AttributeError: pass - - - -# These helpers are used at module level and need to be defined first. -# And yes, I know PEP-8, but sometimes a lower-case classname makes more sense. - -def depr(message, hard=False): - warnings.warn(message, DeprecationWarning, stacklevel=3) - -def makelist(data): # This is just to handy - if isinstance(data, (tuple, list, set, dict)): return list(data) - elif data: return [data] - else: return [] - - -class DictProperty(object): - ''' Property that maps to a key in a local dict-like attribute. ''' - def __init__(self, attr, key=None, read_only=False): - self.attr, self.key, self.read_only = attr, key, read_only - - def __call__(self, func): - functools.update_wrapper(self, func, updated=[]) - self.getter, self.key = func, self.key or func.__name__ - return self - - def __get__(self, obj, cls): - if obj is None: return self - key, storage = self.key, getattr(obj, self.attr) - if key not in storage: storage[key] = self.getter(obj) - return storage[key] - - def __set__(self, obj, value): - if self.read_only: raise AttributeError("Read-Only property.") - getattr(obj, self.attr)[self.key] = value - - def __delete__(self, obj): - if self.read_only: raise AttributeError("Read-Only property.") - del getattr(obj, self.attr)[self.key] - - -class cached_property(object): - ''' A property that is only computed once per instance and then replaces - itself with an ordinary attribute. Deleting the attribute resets the - property. ''' - - def __init__(self, func): - self.__doc__ = getattr(func, '__doc__') - self.func = func - - def __get__(self, obj, cls): - if obj is None: return self - value = obj.__dict__[self.func.__name__] = self.func(obj) - return value - - -class lazy_attribute(object): - ''' A property that caches itself to the class object. ''' - def __init__(self, func): - functools.update_wrapper(self, func, updated=[]) - self.getter = func - - def __get__(self, obj, cls): - value = self.getter(cls) - setattr(cls, self.__name__, value) - return value - - - - - - -############################################################################### -# Exceptions and Events ######################################################## -############################################################################### - - -class BottleException(Exception): - """ A base class for exceptions used by bottle. """ - pass - - - - - - -############################################################################### -# Routing ###################################################################### -############################################################################### - - -class RouteError(BottleException): - """ This is a base class for all routing related exceptions """ - - -class RouteReset(BottleException): - """ If raised by a plugin or request handler, the route is reset and all - plugins are re-applied. """ - -class RouterUnknownModeError(RouteError): pass - - -class RouteSyntaxError(RouteError): - """ The route parser found something not supported by this router. """ - - -class RouteBuildError(RouteError): - """ The route could not be built. """ - - -def _re_flatten(p): - ''' Turn all capturing groups in a regular expression pattern into - non-capturing groups. ''' - if '(' not in p: return p - return re.sub(r'(\\*)(\(\?P<[^>]+>|\((?!\?))', - lambda m: m.group(0) if len(m.group(1)) % 2 else m.group(1) + '(?:', p) - - -class Router(object): - ''' A Router is an ordered collection of route->target pairs. It is used to - efficiently match WSGI requests against a number of routes and return - the first target that satisfies the request. The target may be anything, - usually a string, ID or callable object. A route consists of a path-rule - and a HTTP method. - - The path-rule is either a static path (e.g. `/contact`) or a dynamic - path that contains wildcards (e.g. `/wiki/`). The wildcard syntax - and details on the matching order are described in docs:`routing`. - ''' - - default_pattern = '[^/]+' - default_filter = 're' - - #: The current CPython regexp implementation does not allow more - #: than 99 matching groups per regular expression. - _MAX_GROUPS_PER_PATTERN = 99 - - def __init__(self, strict=False): - self.rules = [] # All rules in order - self._groups = {} # index of regexes to find them in dyna_routes - self.builder = {} # Data structure for the url builder - self.static = {} # Search structure for static routes - self.dyna_routes = {} - self.dyna_regexes = {} # Search structure for dynamic routes - #: If true, static routes are no longer checked first. - self.strict_order = strict - self.filters = { - 're': lambda conf: - (_re_flatten(conf or self.default_pattern), None, None), - 'int': lambda conf: (r'-?\d+', int, lambda x: str(int(x))), - 'float': lambda conf: (r'-?[\d.]+', float, lambda x: str(float(x))), - 'path': lambda conf: (r'.+?', None, None)} - - def add_filter(self, name, func): - ''' Add a filter. The provided function is called with the configuration - string as parameter and must return a (regexp, to_python, to_url) tuple. - The first element is a string, the last two are callables or None. ''' - self.filters[name] = func - - rule_syntax = re.compile('(\\\\*)'\ - '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)'\ - '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)'\ - '(?::((?:\\\\.|[^\\\\>]+)+)?)?)?>))') - - def _itertokens(self, rule): - offset, prefix = 0, '' - for match in self.rule_syntax.finditer(rule): - prefix += rule[offset:match.start()] - g = match.groups() - if len(g[0])%2: # Escaped wildcard - prefix += match.group(0)[len(g[0]):] - offset = match.end() - continue - if prefix: - yield prefix, None, None - name, filtr, conf = g[4:7] if g[2] is None else g[1:4] - yield name, filtr or 'default', conf or None - offset, prefix = match.end(), '' - if offset <= len(rule) or prefix: - yield prefix+rule[offset:], None, None - - def add(self, rule, method, target, name=None): - ''' Add a new rule or replace the target for an existing rule. ''' - anons = 0 # Number of anonymous wildcards found - keys = [] # Names of keys - pattern = '' # Regular expression pattern with named groups - filters = [] # Lists of wildcard input filters - builder = [] # Data structure for the URL builder - is_static = True - - for key, mode, conf in self._itertokens(rule): - if mode: - is_static = False - if mode == 'default': mode = self.default_filter - mask, in_filter, out_filter = self.filters[mode](conf) - if not key: - pattern += '(?:%s)' % mask - key = 'anon%d' % anons - anons += 1 - else: - pattern += '(?P<%s>%s)' % (key, mask) - keys.append(key) - if in_filter: filters.append((key, in_filter)) - builder.append((key, out_filter or str)) - elif key: - pattern += re.escape(key) - builder.append((None, key)) - - self.builder[rule] = builder - if name: self.builder[name] = builder - - if is_static and not self.strict_order: - self.static.setdefault(method, {}) - self.static[method][self.build(rule)] = (target, None) - return - - try: - re_pattern = re.compile('^(%s)$' % pattern) - re_match = re_pattern.match - except re.error: - raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, _e())) - - if filters: - def getargs(path): - url_args = re_match(path).groupdict() - for name, wildcard_filter in filters: - try: - url_args[name] = wildcard_filter(url_args[name]) - except ValueError: - raise HTTPError(400, 'Path has wrong format.') - return url_args - elif re_pattern.groupindex: - def getargs(path): - return re_match(path).groupdict() - else: - getargs = None - - flatpat = _re_flatten(pattern) - whole_rule = (rule, flatpat, target, getargs) - - if (flatpat, method) in self._groups: - if DEBUG: - msg = 'Route <%s %s> overwrites a previously defined route' - warnings.warn(msg % (method, rule), RuntimeWarning) - self.dyna_routes[method][self._groups[flatpat, method]] = whole_rule - else: - self.dyna_routes.setdefault(method, []).append(whole_rule) - self._groups[flatpat, method] = len(self.dyna_routes[method]) - 1 - - self._compile(method) - - def _compile(self, method): - all_rules = self.dyna_routes[method] - comborules = self.dyna_regexes[method] = [] - maxgroups = self._MAX_GROUPS_PER_PATTERN - for x in range(0, len(all_rules), maxgroups): - some = all_rules[x:x+maxgroups] - combined = (flatpat for (_, flatpat, _, _) in some) - combined = '|'.join('(^%s$)' % flatpat for flatpat in combined) - combined = re.compile(combined).match - rules = [(target, getargs) for (_, _, target, getargs) in some] - comborules.append((combined, rules)) - - def build(self, _name, *anons, **query): - ''' Build an URL by filling the wildcards in a rule. ''' - builder = self.builder.get(_name) - if not builder: raise RouteBuildError("No route with that name.", _name) - try: - for i, value in enumerate(anons): query['anon%d'%i] = value - url = ''.join([f(query.pop(n)) if n else f for (n,f) in builder]) - return url if not query else url+'?'+urlencode(query) - except KeyError: - raise RouteBuildError('Missing URL argument: %r' % _e().args[0]) - - def match(self, environ): - ''' Return a (target, url_agrs) tuple or raise HTTPError(400/404/405). ''' - verb = environ['REQUEST_METHOD'].upper() - path = environ['PATH_INFO'] or '/' - target = None - methods = [verb, 'GET', 'ANY'] if verb == 'HEAD' else [verb, 'ANY'] - - for method in methods: - if method in self.static and path in self.static[method]: - target, getargs = self.static[method][path] - return target, getargs(path) if getargs else {} - elif method in self.dyna_regexes: - for combined, rules in self.dyna_regexes[method]: - match = combined(path) - if match: - target, getargs = rules[match.lastindex - 1] - return target, getargs(path) if getargs else {} - - # No matching route found. Collect alternative methods for 405 response - allowed = set([]) - nocheck = set(methods) - for method in set(self.static) - nocheck: - if path in self.static[method]: - allowed.add(verb) - for method in set(self.dyna_regexes) - allowed - nocheck: - for combined, rules in self.dyna_regexes[method]: - match = combined(path) - if match: - allowed.add(method) - if allowed: - allow_header = ",".join(sorted(allowed)) - raise HTTPError(405, "Method not allowed.", Allow=allow_header) - - # No matching route and no alternative method found. We give up - raise HTTPError(404, "Not found: " + repr(path)) - - - - - - -class Route(object): - ''' This class wraps a route callback along with route specific metadata and - configuration and applies Plugins on demand. It is also responsible for - turing an URL path rule into a regular expression usable by the Router. - ''' - - def __init__(self, app, rule, method, callback, name=None, - plugins=None, skiplist=None, **config): - #: The application this route is installed to. - self.app = app - #: The path-rule string (e.g. ``/wiki/:page``). - self.rule = rule - #: The HTTP method as a string (e.g. ``GET``). - self.method = method - #: The original callback with no plugins applied. Useful for introspection. - self.callback = callback - #: The name of the route (if specified) or ``None``. - self.name = name or None - #: A list of route-specific plugins (see :meth:`Bottle.route`). - self.plugins = plugins or [] - #: A list of plugins to not apply to this route (see :meth:`Bottle.route`). - self.skiplist = skiplist or [] - #: Additional keyword arguments passed to the :meth:`Bottle.route` - #: decorator are stored in this dictionary. Used for route-specific - #: plugin configuration and meta-data. - self.config = ConfigDict().load_dict(config) - - def __call__(self, *a, **ka): - depr("Some APIs changed to return Route() instances instead of"\ - " callables. Make sure to use the Route.call method and not to"\ - " call Route instances directly.") - return self.call(*a, **ka) - - @cached_property - def call(self): - ''' The route callback with all plugins applied. This property is - created on demand and then cached to speed up subsequent requests.''' - return self._make_callback() - - def reset(self): - ''' Forget any cached values. The next time :attr:`call` is accessed, - all plugins are re-applied. ''' - self.__dict__.pop('call', None) - - def prepare(self): - ''' Do all on-demand work immediately (useful for debugging).''' - self.call - - @property - def _context(self): - depr('Switch to Plugin API v2 and access the Route object directly.') - return dict(rule=self.rule, method=self.method, callback=self.callback, - name=self.name, app=self.app, config=self.config, - apply=self.plugins, skip=self.skiplist) - - def all_plugins(self): - ''' Yield all Plugins affecting this route. ''' - unique = set() - for p in reversed(self.app.plugins + self.plugins): - if True in self.skiplist: break - name = getattr(p, 'name', False) - if name and (name in self.skiplist or name in unique): continue - if p in self.skiplist or type(p) in self.skiplist: continue - if name: unique.add(name) - yield p - - def _make_callback(self): - callback = self.callback - for plugin in self.all_plugins(): - try: - if hasattr(plugin, 'apply'): - api = getattr(plugin, 'api', 1) - context = self if api > 1 else self._context - callback = plugin.apply(callback, context) - else: - callback = plugin(callback) - except RouteReset: # Try again with changed configuration. - return self._make_callback() - if not callback is self.callback: - update_wrapper(callback, self.callback) - return callback - - def get_undecorated_callback(self): - ''' Return the callback. If the callback is a decorated function, try to - recover the original function. ''' - func = self.callback - func = getattr(func, '__func__' if py3k else 'im_func', func) - closure_attr = '__closure__' if py3k else 'func_closure' - while hasattr(func, closure_attr) and getattr(func, closure_attr): - func = getattr(func, closure_attr)[0].cell_contents - return func - - def get_callback_args(self): - ''' Return a list of argument names the callback (most likely) accepts - as keyword arguments. If the callback is a decorated function, try - to recover the original function before inspection. ''' - return getargspec(self.get_undecorated_callback())[0] - - def get_config(key, default=None): - ''' Lookup a config field and return its value, first checking the - route.config, then route.app.config.''' - for conf in (self.config, self.app.conifg): - if key in conf: return conf[key] - return default - - def __repr__(self): - cb = self.get_undecorated_callback() - return '<%s %r %r>' % (self.method, self.rule, cb) - - - - - - -############################################################################### -# Application Object ########################################################### -############################################################################### - - -class Bottle(object): - """ Each Bottle object represents a single, distinct web application and - consists of routes, callbacks, plugins, resources and configuration. - Instances are callable WSGI applications. - - :param catchall: If true (default), handle all exceptions. Turn off to - let debugging middleware handle exceptions. - """ - - def __init__(self, catchall=True, autojson=True): - - #: A :class:`ConfigDict` for app specific configuration. - self.config = ConfigDict() - self.config._on_change = functools.partial(self.trigger_hook, 'config') - self.config.meta_set('autojson', 'validate', bool) - self.config.meta_set('catchall', 'validate', bool) - self.config['catchall'] = catchall - self.config['autojson'] = autojson - - #: A :class:`ResourceManager` for application files - self.resources = ResourceManager() - - self.routes = [] # List of installed :class:`Route` instances. - self.router = Router() # Maps requests to :class:`Route` instances. - self.error_handler = {} - - # Core plugins - self.plugins = [] # List of installed plugins. - if self.config['autojson']: - self.install(JSONPlugin()) - self.install(TemplatePlugin()) - - #: If true, most exceptions are caught and returned as :exc:`HTTPError` - catchall = DictProperty('config', 'catchall') - - __hook_names = 'before_request', 'after_request', 'app_reset', 'config' - __hook_reversed = 'after_request' - - @cached_property - def _hooks(self): - return dict((name, []) for name in self.__hook_names) - - def add_hook(self, name, func): - ''' Attach a callback to a hook. Three hooks are currently implemented: - - before_request - Executed once before each request. The request context is - available, but no routing has happened yet. - after_request - Executed once after each request regardless of its outcome. - app_reset - Called whenever :meth:`Bottle.reset` is called. - ''' - if name in self.__hook_reversed: - self._hooks[name].insert(0, func) - else: - self._hooks[name].append(func) - - def remove_hook(self, name, func): - ''' Remove a callback from a hook. ''' - if name in self._hooks and func in self._hooks[name]: - self._hooks[name].remove(func) - return True - - def trigger_hook(self, __name, *args, **kwargs): - ''' Trigger a hook and return a list of results. ''' - return [hook(*args, **kwargs) for hook in self._hooks[__name][:]] - - def hook(self, name): - """ Return a decorator that attaches a callback to a hook. See - :meth:`add_hook` for details.""" - def decorator(func): - self.add_hook(name, func) - return func - return decorator - - def mount(self, prefix, app, **options): - ''' Mount an application (:class:`Bottle` or plain WSGI) to a specific - URL prefix. Example:: - - root_app.mount('/admin/', admin_app) - - :param prefix: path prefix or `mount-point`. If it ends in a slash, - that slash is mandatory. - :param app: an instance of :class:`Bottle` or a WSGI application. - - All other parameters are passed to the underlying :meth:`route` call. - ''' - if isinstance(app, basestring): - depr('Parameter order of Bottle.mount() changed.', True) # 0.10 - - segments = [p for p in prefix.split('/') if p] - if not segments: raise ValueError('Empty path prefix.') - path_depth = len(segments) - - def mountpoint_wrapper(): - try: - request.path_shift(path_depth) - rs = HTTPResponse([]) - def start_response(status, headerlist, exc_info=None): - if exc_info: - try: - _raise(*exc_info) - finally: - exc_info = None - rs.status = status - for name, value in headerlist: rs.add_header(name, value) - return rs.body.append - body = app(request.environ, start_response) - if body and rs.body: body = itertools.chain(rs.body, body) - rs.body = body or rs.body - return rs - finally: - request.path_shift(-path_depth) - - options.setdefault('skip', True) - options.setdefault('method', 'ANY') - options.setdefault('mountpoint', {'prefix': prefix, 'target': app}) - options['callback'] = mountpoint_wrapper - - self.route('/%s/<:re:.*>' % '/'.join(segments), **options) - if not prefix.endswith('/'): - self.route('/' + '/'.join(segments), **options) - - def merge(self, routes): - ''' Merge the routes of another :class:`Bottle` application or a list of - :class:`Route` objects into this application. The routes keep their - 'owner', meaning that the :data:`Route.app` attribute is not - changed. ''' - if isinstance(routes, Bottle): - routes = routes.routes - for route in routes: - self.add_route(route) - - def install(self, plugin): - ''' Add a plugin to the list of plugins and prepare it for being - applied to all routes of this application. A plugin may be a simple - decorator or an object that implements the :class:`Plugin` API. - ''' - if hasattr(plugin, 'setup'): plugin.setup(self) - if not callable(plugin) and not hasattr(plugin, 'apply'): - raise TypeError("Plugins must be callable or implement .apply()") - self.plugins.append(plugin) - self.reset() - return plugin - - def uninstall(self, plugin): - ''' Uninstall plugins. Pass an instance to remove a specific plugin, a type - object to remove all plugins that match that type, a string to remove - all plugins with a matching ``name`` attribute or ``True`` to remove all - plugins. Return the list of removed plugins. ''' - removed, remove = [], plugin - for i, plugin in list(enumerate(self.plugins))[::-1]: - if remove is True or remove is plugin or remove is type(plugin) \ - or getattr(plugin, 'name', True) == remove: - removed.append(plugin) - del self.plugins[i] - if hasattr(plugin, 'close'): plugin.close() - if removed: self.reset() - return removed - - def reset(self, route=None): - ''' Reset all routes (force plugins to be re-applied) and clear all - caches. If an ID or route object is given, only that specific route - is affected. ''' - if route is None: routes = self.routes - elif isinstance(route, Route): routes = [route] - else: routes = [self.routes[route]] - for route in routes: route.reset() - if DEBUG: - for route in routes: route.prepare() - self.trigger_hook('app_reset') - - def close(self): - ''' Close the application and all installed plugins. ''' - for plugin in self.plugins: - if hasattr(plugin, 'close'): plugin.close() - self.stopped = True - - def run(self, **kwargs): - ''' Calls :func:`run` with the same parameters. ''' - run(self, **kwargs) - - def match(self, environ): - """ Search for a matching route and return a (:class:`Route` , urlargs) - tuple. The second value is a dictionary with parameters extracted - from the URL. Raise :exc:`HTTPError` (404/405) on a non-match.""" - return self.router.match(environ) - - def get_url(self, routename, **kargs): - """ Return a string that matches a named route """ - scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/' - location = self.router.build(routename, **kargs).lstrip('/') - return urljoin(urljoin('/', scriptname), location) - - def add_route(self, route): - ''' Add a route object, but do not change the :data:`Route.app` - attribute.''' - self.routes.append(route) - self.router.add(route.rule, route.method, route, name=route.name) - if DEBUG: route.prepare() - - def route(self, path=None, method='GET', callback=None, name=None, - apply=None, skip=None, **config): - """ A decorator to bind a function to a request URL. Example:: - - @app.route('/hello/:name') - def hello(name): - return 'Hello %s' % name - - The ``:name`` part is a wildcard. See :class:`Router` for syntax - details. - - :param path: Request path or a list of paths to listen to. If no - path is specified, it is automatically generated from the - signature of the function. - :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of - methods to listen to. (default: `GET`) - :param callback: An optional shortcut to avoid the decorator - syntax. ``route(..., callback=func)`` equals ``route(...)(func)`` - :param name: The name for this route. (default: None) - :param apply: A decorator or plugin or a list of plugins. These are - applied to the route callback in addition to installed plugins. - :param skip: A list of plugins, plugin classes or names. Matching - plugins are not installed to this route. ``True`` skips all. - - Any additional keyword arguments are stored as route-specific - configuration and passed to plugins (see :meth:`Plugin.apply`). - """ - if callable(path): path, callback = None, path - plugins = makelist(apply) - skiplist = makelist(skip) - def decorator(callback): - # TODO: Documentation and tests - if isinstance(callback, basestring): callback = load(callback) - for rule in makelist(path) or yieldroutes(callback): - for verb in makelist(method): - verb = verb.upper() - route = Route(self, rule, verb, callback, name=name, - plugins=plugins, skiplist=skiplist, **config) - self.add_route(route) - return callback - return decorator(callback) if callback else decorator - - def get(self, path=None, method='GET', **options): - """ Equals :meth:`route`. """ - return self.route(path, method, **options) - - def post(self, path=None, method='POST', **options): - """ Equals :meth:`route` with a ``POST`` method parameter. """ - return self.route(path, method, **options) - - def put(self, path=None, method='PUT', **options): - """ Equals :meth:`route` with a ``PUT`` method parameter. """ - return self.route(path, method, **options) - - def delete(self, path=None, method='DELETE', **options): - """ Equals :meth:`route` with a ``DELETE`` method parameter. """ - return self.route(path, method, **options) - - def error(self, code=500): - """ Decorator: Register an output handler for a HTTP error code""" - def wrapper(handler): - self.error_handler[int(code)] = handler - return handler - return wrapper - - def handle(self, path, method='GET'): - """ (deprecated) Execute the first matching route callback and return - the result. :exc:`HTTPResponse` exceptions are caught and returned. - If :attr:`Bottle.catchall` is true, other exceptions are caught as - well and returned as :exc:`HTTPError` instances (500). - """ - depr("This method will change semantics in 0.10. Try to avoid it.") - if isinstance(path, dict): - return self._handle(path) - return self._handle({'PATH_INFO': path, 'REQUEST_METHOD': method.upper()}) - - def default_error_handler(self, res): - return tob(template(ERROR_PAGE_TEMPLATE, e=res)) - - def _handle(self, environ): - try: - environ['bottle.app'] = self - request.bind(environ) - response.bind() - - try: - self.trigger_hook('before_request') - route, args = self.router.match(environ) - environ['route.handle'] = route - environ['bottle.route'] = route - environ['route.url_args'] = args - return route.call(**args) - finally: - self.trigger_hook('after_request') - - except HTTPResponse: - return _e() - except RouteReset: - route.reset() - return self._handle(environ) - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - stacktrace = format_exc() - environ['wsgi.errors'].write(stacktrace) - return HTTPError(500, "Internal Server Error", _e(), stacktrace) - - def _cast(self, out, peek=None): - """ Try to convert the parameter into something WSGI compatible and set - correct HTTP headers when possible. - Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like, - iterable of strings and iterable of unicodes - """ - - # Empty output is done here - if not out: - if 'Content-Length' not in response: - response['Content-Length'] = 0 - return [] - # Join lists of byte or unicode strings. Mixed lists are NOT supported - if isinstance(out, (tuple, list))\ - and isinstance(out[0], (bytes, unicode)): - out = out[0][0:0].join(out) # b'abc'[0:0] -> b'' - # Encode unicode strings - if isinstance(out, unicode): - out = out.encode(response.charset) - # Byte Strings are just returned - if isinstance(out, bytes): - if 'Content-Length' not in response: - response['Content-Length'] = len(out) - return [out] - # HTTPError or HTTPException (recursive, because they may wrap anything) - # TODO: Handle these explicitly in handle() or make them iterable. - if isinstance(out, HTTPError): - out.apply(response) - out = self.error_handler.get(out.status_code, self.default_error_handler)(out) - return self._cast(out) - if isinstance(out, HTTPResponse): - out.apply(response) - return self._cast(out.body) - - # File-like objects. - if hasattr(out, 'read'): - if 'wsgi.file_wrapper' in request.environ: - return request.environ['wsgi.file_wrapper'](out) - elif hasattr(out, 'close') or not hasattr(out, '__iter__'): - return WSGIFileWrapper(out) - - # Handle Iterables. We peek into them to detect their inner type. - try: - iout = iter(out) - first = next(iout) - while not first: - first = next(iout) - except StopIteration: - return self._cast('') - except HTTPResponse: - first = _e() - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - first = HTTPError(500, 'Unhandled exception', _e(), format_exc()) - - # These are the inner types allowed in iterator or generator objects. - if isinstance(first, HTTPResponse): - return self._cast(first) - elif isinstance(first, bytes): - new_iter = itertools.chain([first], iout) - elif isinstance(first, unicode): - encoder = lambda x: x.encode(response.charset) - new_iter = imap(encoder, itertools.chain([first], iout)) - else: - msg = 'Unsupported response type: %s' % type(first) - return self._cast(HTTPError(500, msg)) - if hasattr(out, 'close'): - new_iter = _closeiter(new_iter, out.close) - return new_iter - - def wsgi(self, environ, start_response): - """ The bottle WSGI-interface. """ - try: - out = self._cast(self._handle(environ)) - # rfc2616 section 4.3 - if response._status_code in (100, 101, 204, 304)\ - or environ['REQUEST_METHOD'] == 'HEAD': - if hasattr(out, 'close'): out.close() - out = [] - start_response(response._status_line, response.headerlist) - return out - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - err = '

Critical error while processing request: %s

' \ - % html_escape(environ.get('PATH_INFO', '/')) - if DEBUG: - err += '

Error:

\n
\n%s\n
\n' \ - '

Traceback:

\n
\n%s\n
\n' \ - % (html_escape(repr(_e())), html_escape(format_exc())) - environ['wsgi.errors'].write(err) - headers = [('Content-Type', 'text/html; charset=UTF-8')] - start_response('500 INTERNAL SERVER ERROR', headers, sys.exc_info()) - return [tob(err)] - - def __call__(self, environ, start_response): - ''' Each instance of :class:'Bottle' is a WSGI application. ''' - return self.wsgi(environ, start_response) - - - - - - -############################################################################### -# HTTP and WSGI Tools ########################################################## -############################################################################### - -class BaseRequest(object): - """ A wrapper for WSGI environment dictionaries that adds a lot of - convenient access methods and properties. Most of them are read-only. - - Adding new attributes to a request actually adds them to the environ - dictionary (as 'bottle.request.ext.'). This is the recommended - way to store and access request-specific data. - """ - - __slots__ = ('environ') - - #: Maximum size of memory buffer for :attr:`body` in bytes. - MEMFILE_MAX = 102400 - #: Maximum number pr GET or POST parameters per request - MAX_PARAMS = 100 - - def __init__(self, environ=None): - """ Wrap a WSGI environ dictionary. """ - #: The wrapped WSGI environ dictionary. This is the only real attribute. - #: All other attributes actually are read-only properties. - self.environ = {} if environ is None else environ - self.environ['bottle.request'] = self - - @DictProperty('environ', 'bottle.app', read_only=True) - def app(self): - ''' Bottle application handling this request. ''' - raise RuntimeError('This request is not connected to an application.') - - @DictProperty('environ', 'bottle.route', read_only=True) - def route(self): - """ The bottle :class:`Route` object that matches this request. """ - raise RuntimeError('This request is not connected to a route.') - - @DictProperty('environ', 'route.url_args', read_only=True) - def url_args(self): - """ The arguments extracted from the URL. """ - raise RuntimeError('This request is not connected to a route.') - - @property - def path(self): - ''' The value of ``PATH_INFO`` with exactly one prefixed slash (to fix - broken clients and avoid the "empty path" edge case). ''' - return '/' + self.environ.get('PATH_INFO','').lstrip('/') - - @property - def method(self): - ''' The ``REQUEST_METHOD`` value as an uppercase string. ''' - return self.environ.get('REQUEST_METHOD', 'GET').upper() - - @DictProperty('environ', 'bottle.request.headers', read_only=True) - def headers(self): - ''' A :class:`WSGIHeaderDict` that provides case-insensitive access to - HTTP request headers. ''' - return WSGIHeaderDict(self.environ) - - def get_header(self, name, default=None): - ''' Return the value of a request header, or a given default value. ''' - return self.headers.get(name, default) - - @DictProperty('environ', 'bottle.request.cookies', read_only=True) - def cookies(self): - """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT - decoded. Use :meth:`get_cookie` if you expect signed cookies. """ - cookies = SimpleCookie(self.environ.get('HTTP_COOKIE','')).values() - if len(cookies) > self.MAX_PARAMS: - raise HTTPError(413, 'Too many cookies') - return FormsDict((c.key, c.value) for c in cookies) - - def get_cookie(self, key, default=None, secret=None): - """ Return the content of a cookie. To read a `Signed Cookie`, the - `secret` must match the one used to create the cookie (see - :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing - cookie or wrong signature), return a default value. """ - value = self.cookies.get(key) - if secret and value: - dec = cookie_decode(value, secret) # (key, value) tuple or None - return dec[1] if dec and dec[0] == key else default - return value or default - - @DictProperty('environ', 'bottle.request.query', read_only=True) - def query(self): - ''' The :attr:`query_string` parsed into a :class:`FormsDict`. These - values are sometimes called "URL arguments" or "GET parameters", but - not to be confused with "URL wildcards" as they are provided by the - :class:`Router`. ''' - get = self.environ['bottle.get'] = FormsDict() - pairs = _parse_qsl(self.environ.get('QUERY_STRING', '')) - if len(pairs) > self.MAX_PARAMS: - raise HTTPError(413, 'Too many parameters') - for key, value in pairs: - get[key] = value - return get - - @DictProperty('environ', 'bottle.request.forms', read_only=True) - def forms(self): - """ Form values parsed from an `url-encoded` or `multipart/form-data` - encoded POST or PUT request body. The result is returned as a - :class:`FormsDict`. All keys and values are strings. File uploads - are stored separately in :attr:`files`. """ - forms = FormsDict() - for name, item in self.POST.allitems(): - if not isinstance(item, FileUpload): - forms[name] = item - return forms - - @DictProperty('environ', 'bottle.request.params', read_only=True) - def params(self): - """ A :class:`FormsDict` with the combined values of :attr:`query` and - :attr:`forms`. File uploads are stored in :attr:`files`. """ - params = FormsDict() - for key, value in self.query.allitems(): - params[key] = value - for key, value in self.forms.allitems(): - params[key] = value - return params - - @DictProperty('environ', 'bottle.request.files', read_only=True) - def files(self): - """ File uploads parsed from `multipart/form-data` encoded POST or PUT - request body. The values are instances of :class:`FileUpload`. - - """ - files = FormsDict() - for name, item in self.POST.allitems(): - if isinstance(item, FileUpload): - files[name] = item - return files - - @DictProperty('environ', 'bottle.request.json', read_only=True) - def json(self): - ''' If the ``Content-Type`` header is ``application/json``, this - property holds the parsed content of the request body. Only requests - smaller than :attr:`MEMFILE_MAX` are processed to avoid memory - exhaustion. ''' - if 'application/json' in self.environ.get('CONTENT_TYPE', ''): - return json_loads(self._get_body_string()) - return None - - @DictProperty('environ', 'bottle.request.body', read_only=True) - def _body(self): - maxread = max(0, self.content_length) - stream = self.environ['wsgi.input'] - body = BytesIO() if maxread < self.MEMFILE_MAX else TemporaryFile(mode='w+b') - while maxread > 0: - part = stream.read(min(maxread, self.MEMFILE_MAX)) - if not part: break - body.write(part) - maxread -= len(part) - self.environ['wsgi.input'] = body - body.seek(0) - return body - - def _get_body_string(self): - ''' read body until content-length or MEMFILE_MAX into a string. Raise - HTTPError(413) on requests that are to large. ''' - clen = self.content_length - if clen > self.MEMFILE_MAX: - raise HTTPError(413, 'Request to large') - if clen < 0: clen = self.MEMFILE_MAX + 1 - data = self.body.read(clen) - if len(data) > self.MEMFILE_MAX: # Fail fast - raise HTTPError(413, 'Request to large') - return data - - @property - def body(self): - """ The HTTP request body as a seek-able file-like object. Depending on - :attr:`MEMFILE_MAX`, this is either a temporary file or a - :class:`io.BytesIO` instance. Accessing this property for the first - time reads and replaces the ``wsgi.input`` environ variable. - Subsequent accesses just do a `seek(0)` on the file object. """ - self._body.seek(0) - return self._body - - #: An alias for :attr:`query`. - GET = query - - @DictProperty('environ', 'bottle.request.post', read_only=True) - def POST(self): - """ The values of :attr:`forms` and :attr:`files` combined into a single - :class:`FormsDict`. Values are either strings (form values) or - instances of :class:`cgi.FieldStorage` (file uploads). - """ - post = FormsDict() - # We default to application/x-www-form-urlencoded for everything that - # is not multipart and take the fast path (also: 3.1 workaround) - if not self.content_type.startswith('multipart/'): - pairs = _parse_qsl(tonat(self._get_body_string(), 'latin1')) - if len(pairs) > self.MAX_PARAMS: - raise HTTPError(413, 'Too many parameters') - for key, value in pairs: - post[key] = value - return post - - safe_env = {'QUERY_STRING':''} # Build a safe environment for cgi - for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'): - if key in self.environ: safe_env[key] = self.environ[key] - args = dict(fp=self.body, environ=safe_env, keep_blank_values=True) - if py31: - args['fp'] = NCTextIOWrapper(args['fp'], encoding='latin1', - newline='\n') - elif py3k: - args['encoding'] = 'latin1' - data = cgi.FieldStorage(**args) - data = data.list or [] - if len(data) > self.MAX_PARAMS: - raise HTTPError(413, 'Too many parameters') - for item in data: - if item.filename: - post[item.name] = FileUpload(item.file, item.name, - item.filename, item.headers) - else: - post[item.name] = item.value - return post - - @property - def COOKIES(self): - ''' Alias for :attr:`cookies` (deprecated). ''' - depr('BaseRequest.COOKIES was renamed to BaseRequest.cookies (lowercase).') - return self.cookies - - @property - def url(self): - """ The full request URI including hostname and scheme. If your app - lives behind a reverse proxy or load balancer and you get confusing - results, make sure that the ``X-Forwarded-Host`` header is set - correctly. """ - return self.urlparts.geturl() - - @DictProperty('environ', 'bottle.request.urlparts', read_only=True) - def urlparts(self): - ''' The :attr:`url` string as an :class:`urlparse.SplitResult` tuple. - The tuple contains (scheme, host, path, query_string and fragment), - but the fragment is always empty because it is not visible to the - server. ''' - env = self.environ - http = env.get('HTTP_X_FORWARDED_PROTO') or env.get('wsgi.url_scheme', 'http') - host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST') - if not host: - # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients. - host = env.get('SERVER_NAME', '127.0.0.1') - port = env.get('SERVER_PORT') - if port and port != ('80' if http == 'http' else '443'): - host += ':' + port - path = urlquote(self.fullpath) - return UrlSplitResult(http, host, path, env.get('QUERY_STRING'), '') - - @property - def fullpath(self): - """ Request path including :attr:`script_name` (if present). """ - return urljoin(self.script_name, self.path.lstrip('/')) - - @property - def query_string(self): - """ The raw :attr:`query` part of the URL (everything in between ``?`` - and ``#``) as a string. """ - return self.environ.get('QUERY_STRING', '') - - @property - def script_name(self): - ''' The initial portion of the URL's `path` that was removed by a higher - level (server or routing middleware) before the application was - called. This script path is returned with leading and tailing - slashes. ''' - script_name = self.environ.get('SCRIPT_NAME', '').strip('/') - return '/' + script_name + '/' if script_name else '/' - - def path_shift(self, shift=1): - ''' Shift path segments from :attr:`path` to :attr:`script_name` and - vice versa. - - :param shift: The number of path segments to shift. May be negative - to change the shift direction. (default: 1) - ''' - script = self.environ.get('SCRIPT_NAME','/') - self['SCRIPT_NAME'], self['PATH_INFO'] = path_shift(script, self.path, shift) - - @property - def content_length(self): - ''' The request body length as an integer. The client is responsible to - set this header. Otherwise, the real length of the body is unknown - and -1 is returned. In this case, :attr:`body` will be empty. ''' - return int(self.environ.get('CONTENT_LENGTH') or -1) - - @property - def content_type(self): - ''' The Content-Type header as a lowercase-string (default: empty). ''' - return self.environ.get('CONTENT_TYPE', '').lower() - - @property - def is_xhr(self): - ''' True if the request was triggered by a XMLHttpRequest. This only - works with JavaScript libraries that support the `X-Requested-With` - header (most of the popular libraries do). ''' - requested_with = self.environ.get('HTTP_X_REQUESTED_WITH','') - return requested_with.lower() == 'xmlhttprequest' - - @property - def is_ajax(self): - ''' Alias for :attr:`is_xhr`. "Ajax" is not the right term. ''' - return self.is_xhr - - @property - def auth(self): - """ HTTP authentication data as a (user, password) tuple. This - implementation currently supports basic (not digest) authentication - only. If the authentication happened at a higher level (e.g. in the - front web-server or a middleware), the password field is None, but - the user field is looked up from the ``REMOTE_USER`` environ - variable. On any errors, None is returned. """ - basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION','')) - if basic: return basic - ruser = self.environ.get('REMOTE_USER') - if ruser: return (ruser, None) - return None - - @property - def remote_route(self): - """ A list of all IPs that were involved in this request, starting with - the client IP and followed by zero or more proxies. This does only - work if all proxies support the ```X-Forwarded-For`` header. Note - that this information can be forged by malicious clients. """ - proxy = self.environ.get('HTTP_X_FORWARDED_FOR') - if proxy: return [ip.strip() for ip in proxy.split(',')] - remote = self.environ.get('REMOTE_ADDR') - return [remote] if remote else [] - - @property - def remote_addr(self): - """ The client IP as a string. Note that this information can be forged - by malicious clients. """ - route = self.remote_route - return route[0] if route else None - - def copy(self): - """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """ - return Request(self.environ.copy()) - - def get(self, value, default=None): return self.environ.get(value, default) - def __getitem__(self, key): return self.environ[key] - def __delitem__(self, key): self[key] = ""; del(self.environ[key]) - def __iter__(self): return iter(self.environ) - def __len__(self): return len(self.environ) - def keys(self): return self.environ.keys() - def __setitem__(self, key, value): - """ Change an environ value and clear all caches that depend on it. """ - - if self.environ.get('bottle.request.readonly'): - raise KeyError('The environ dictionary is read-only.') - - self.environ[key] = value - todelete = () - - if key == 'wsgi.input': - todelete = ('body', 'forms', 'files', 'params', 'post', 'json') - elif key == 'QUERY_STRING': - todelete = ('query', 'params') - elif key.startswith('HTTP_'): - todelete = ('headers', 'cookies') - - for key in todelete: - self.environ.pop('bottle.request.'+key, None) - - def __repr__(self): - return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url) - - def __getattr__(self, name): - ''' Search in self.environ for additional user defined attributes. ''' - try: - var = self.environ['bottle.request.ext.%s'%name] - return var.__get__(self) if hasattr(var, '__get__') else var - except KeyError: - raise AttributeError('Attribute %r not defined.' % name) - - def __setattr__(self, name, value): - if name == 'environ': return object.__setattr__(self, name, value) - self.environ['bottle.request.ext.%s'%name] = value - - - - -def _hkey(s): - return s.title().replace('_','-') - - -class HeaderProperty(object): - def __init__(self, name, reader=None, writer=str, default=''): - self.name, self.default = name, default - self.reader, self.writer = reader, writer - self.__doc__ = 'Current value of the %r header.' % name.title() - - def __get__(self, obj, cls): - if obj is None: return self - value = obj.headers.get(self.name, self.default) - return self.reader(value) if self.reader else value - - def __set__(self, obj, value): - obj.headers[self.name] = self.writer(value) - - def __delete__(self, obj): - del obj.headers[self.name] - - -class BaseResponse(object): - """ Storage class for a response body as well as headers and cookies. - - This class does support dict-like case-insensitive item-access to - headers, but is NOT a dict. Most notably, iterating over a response - yields parts of the body and not the headers. - - :param body: The response body as one of the supported types. - :param status: Either an HTTP status code (e.g. 200) or a status line - including the reason phrase (e.g. '200 OK'). - :param headers: A dictionary or a list of name-value pairs. - - Additional keyword arguments are added to the list of headers. - Underscores in the header name are replaced with dashes. - """ - - default_status = 200 - default_content_type = 'text/html; charset=UTF-8' - - # Header blacklist for specific response codes - # (rfc2616 section 10.2.3 and 10.3.5) - bad_headers = { - 204: set(('Content-Type',)), - 304: set(('Allow', 'Content-Encoding', 'Content-Language', - 'Content-Length', 'Content-Range', 'Content-Type', - 'Content-Md5', 'Last-Modified'))} - - def __init__(self, body='', status=None, headers=None, **more_headers): - self._cookies = None - self._headers = {} - self.body = body - self.status = status or self.default_status - if headers: - if isinstance(headers, dict): - headers = headers.items() - for name, value in headers: - self.add_header(name, value) - if more_headers: - for name, value in more_headers.items(): - self.add_header(name, value) - - def copy(self, cls=None): - ''' Returns a copy of self. ''' - cls = cls or BaseResponse - assert issubclass(cls, BaseResponse) - copy = cls() - copy.status = self.status - copy._headers = dict((k, v[:]) for (k, v) in self._headers.items()) - copy.COOKIES.load(self.COOKIES.output()) - return copy - - def __iter__(self): - return iter(self.body) - - def close(self): - if hasattr(self.body, 'close'): - self.body.close() - - @property - def status_line(self): - ''' The HTTP status line as a string (e.g. ``404 Not Found``).''' - return self._status_line - - @property - def status_code(self): - ''' The HTTP status code as an integer (e.g. 404).''' - return self._status_code - - def _set_status(self, status): - if isinstance(status, int): - code, status = status, _HTTP_STATUS_LINES.get(status) - elif ' ' in status: - status = status.strip() - code = int(status.split()[0]) - else: - raise ValueError('String status line without a reason phrase.') - if not 100 <= code <= 999: raise ValueError('Status code out of range.') - self._status_code = code - self._status_line = str(status or ('%d Unknown' % code)) - - def _get_status(self): - return self._status_line - - status = property(_get_status, _set_status, None, - ''' A writeable property to change the HTTP response status. It accepts - either a numeric code (100-999) or a string with a custom reason - phrase (e.g. "404 Brain not found"). Both :data:`status_line` and - :data:`status_code` are updated accordingly. The return value is - always a status string. ''') - del _get_status, _set_status - - @property - def headers(self): - ''' An instance of :class:`HeaderDict`, a case-insensitive dict-like - view on the response headers. ''' - hdict = HeaderDict() - hdict.dict = self._headers - return hdict - - def __contains__(self, name): return _hkey(name) in self._headers - def __delitem__(self, name): del self._headers[_hkey(name)] - def __getitem__(self, name): return self._headers[_hkey(name)][-1] - def __setitem__(self, name, value): self._headers[_hkey(name)] = [str(value)] - - def get_header(self, name, default=None): - ''' Return the value of a previously defined header. If there is no - header with that name, return a default value. ''' - return self._headers.get(_hkey(name), [default])[-1] - - def set_header(self, name, value): - ''' Create a new response header, replacing any previously defined - headers with the same name. ''' - self._headers[_hkey(name)] = [str(value)] - - def add_header(self, name, value): - ''' Add an additional response header, not removing duplicates. ''' - self._headers.setdefault(_hkey(name), []).append(str(value)) - - def iter_headers(self): - ''' Yield (header, value) tuples, skipping headers that are not - allowed with the current response status code. ''' - return self.headerlist - - def wsgiheader(self): - depr('The wsgiheader method is deprecated. See headerlist.') #0.10 - return self.headerlist - - @property - def headerlist(self): - ''' WSGI conform list of (header, value) tuples. ''' - out = [] - headers = list(self._headers.items()) - if 'Content-Type' not in self._headers: - headers.append(('Content-Type', [self.default_content_type])) - if self._status_code in self.bad_headers: - bad_headers = self.bad_headers[self._status_code] - headers = [h for h in headers if h[0] not in bad_headers] - out += [(name, val) for name, vals in headers for val in vals] - if self._cookies: - for c in self._cookies.values(): - out.append(('Set-Cookie', c.OutputString())) - return out - - content_type = HeaderProperty('Content-Type') - content_length = HeaderProperty('Content-Length', reader=int) - expires = HeaderProperty('Expires', - reader=lambda x: datetime.utcfromtimestamp(parse_date(x)), - writer=lambda x: http_date(x)) - - @property - def charset(self, default='UTF-8'): - """ Return the charset specified in the content-type header (default: utf8). """ - if 'charset=' in self.content_type: - return self.content_type.split('charset=')[-1].split(';')[0].strip() - return default - - @property - def COOKIES(self): - """ A dict-like SimpleCookie instance. This should not be used directly. - See :meth:`set_cookie`. """ - depr('The COOKIES dict is deprecated. Use `set_cookie()` instead.') # 0.10 - if not self._cookies: - self._cookies = SimpleCookie() - return self._cookies - - def set_cookie(self, name, value, secret=None, **options): - ''' Create a new cookie or replace an old one. If the `secret` parameter is - set, create a `Signed Cookie` (described below). - - :param name: the name of the cookie. - :param value: the value of the cookie. - :param secret: a signature key required for signed cookies. - - Additionally, this method accepts all RFC 2109 attributes that are - supported by :class:`cookie.Morsel`, including: - - :param max_age: maximum age in seconds. (default: None) - :param expires: a datetime object or UNIX timestamp. (default: None) - :param domain: the domain that is allowed to read the cookie. - (default: current domain) - :param path: limits the cookie to a given path (default: current path) - :param secure: limit the cookie to HTTPS connections (default: off). - :param httponly: prevents client-side javascript to read this cookie - (default: off, requires Python 2.6 or newer). - - If neither `expires` nor `max_age` is set (default), the cookie will - expire at the end of the browser session (as soon as the browser - window is closed). - - Signed cookies may store any pickle-able object and are - cryptographically signed to prevent manipulation. Keep in mind that - cookies are limited to 4kb in most browsers. - - Warning: Signed cookies are not encrypted (the client can still see - the content) and not copy-protected (the client can restore an old - cookie). The main intention is to make pickling and unpickling - save, not to store secret information at client side. - ''' - if not self._cookies: - self._cookies = SimpleCookie() - - if secret: - value = touni(cookie_encode((name, value), secret)) - elif not isinstance(value, basestring): - raise TypeError('Secret key missing for non-string Cookie.') - - if len(value) > 4096: raise ValueError('Cookie value to long.') - self._cookies[name] = value - - for key, value in options.items(): - if key == 'max_age': - if isinstance(value, timedelta): - value = value.seconds + value.days * 24 * 3600 - if key == 'expires': - if isinstance(value, (datedate, datetime)): - value = value.timetuple() - elif isinstance(value, (int, float)): - value = time.gmtime(value) - value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value) - self._cookies[name][key.replace('_', '-')] = value - - def delete_cookie(self, key, **kwargs): - ''' Delete a cookie. Be sure to use the same `domain` and `path` - settings as used to create the cookie. ''' - kwargs['max_age'] = -1 - kwargs['expires'] = 0 - self.set_cookie(key, '', **kwargs) - - def __repr__(self): - out = '' - for name, value in self.headerlist: - out += '%s: %s\n' % (name.title(), value.strip()) - return out - -#: Thread-local storage for :class:`LocalRequest` and :class:`LocalResponse` -#: attributes. -_lctx = threading.local() - -def local_property(name): - def fget(self): - try: - return getattr(_lctx, name) - except AttributeError: - raise RuntimeError("Request context not initialized.") - def fset(self, value): setattr(_lctx, name, value) - def fdel(self): delattr(_lctx, name) - return property(fget, fset, fdel, - 'Thread-local property stored in :data:`_lctx.%s`' % name) - - -class LocalRequest(BaseRequest): - ''' A thread-local subclass of :class:`BaseRequest` with a different - set of attribues for each thread. There is usually only one global - instance of this class (:data:`request`). If accessed during a - request/response cycle, this instance always refers to the *current* - request (even on a multithreaded server). ''' - bind = BaseRequest.__init__ - environ = local_property('request_environ') - - -class LocalResponse(BaseResponse): - ''' A thread-local subclass of :class:`BaseResponse` with a different - set of attribues for each thread. There is usually only one global - instance of this class (:data:`response`). Its attributes are used - to build the HTTP response at the end of the request/response cycle. - ''' - bind = BaseResponse.__init__ - _status_line = local_property('response_status_line') - _status_code = local_property('response_status_code') - _cookies = local_property('response_cookies') - _headers = local_property('response_headers') - body = local_property('response_body') - - -Request = BaseRequest -Response = BaseResponse - - -class HTTPResponse(Response, BottleException): - def __init__(self, body='', status=None, headers=None, - header=None, **more_headers): - if header or 'output' in more_headers: - depr('Call signature changed (for the better). See BaseResponse') - if header: more_headers.update(header) - if 'output' in more_headers: body = more_headers.pop('output') - super(HTTPResponse, self).__init__(body, status, headers, **more_headers) - - def apply(self, response): - response._status_code = self._status_code - response._status_line = self._status_line - response._headers = self._headers - response._cookies = self._cookies - response.body = self.body - - def _output(self, value=None): - depr('Use HTTPResponse.body instead of HTTPResponse.output') - if value is None: return self.body - self.body = value - - output = property(_output, _output, doc='Alias for .body') - - -class HTTPError(HTTPResponse): - default_status = 500 - def __init__(self, status=None, body=None, exception=None, traceback=None, - **options): - self.exception = exception - self.traceback = traceback - super(HTTPError, self).__init__(body, status, **options) - - - - - -############################################################################### -# Plugins ###################################################################### -############################################################################### - -class PluginError(BottleException): pass - - -class JSONPlugin(object): - name = 'json' - api = 2 - - def __init__(self, json_dumps=json_dumps): - self.json_dumps = json_dumps - - def apply(self, callback, route): - dumps = self.json_dumps - if not dumps: return callback - def wrapper(*a, **ka): - try: - rv = callback(*a, **ka) - except HTTPError: - rv = _e() - - if isinstance(rv, dict): - #Attempt to serialize, raises exception on failure - json_response = dumps(rv) - #Set content type only if serialization succesful - response.content_type = 'application/json' - return json_response - elif isinstance(rv, HTTPResponse) and isinstance(rv.body, dict): - rv.body = dumps(rv.body) - rv.content_type = 'application/json' - return rv - - return wrapper - - -class TemplatePlugin(object): - ''' This plugin applies the :func:`view` decorator to all routes with a - `template` config parameter. If the parameter is a tuple, the second - element must be a dict with additional options (e.g. `template_engine`) - or default variables for the template. ''' - name = 'template' - api = 2 - - def apply(self, callback, route): - conf = route.config.get('template') - if isinstance(conf, (tuple, list)) and len(conf) == 2: - return view(conf[0], **conf[1])(callback) - elif isinstance(conf, str): - return view(conf)(callback) - else: - return callback - - -#: Not a plugin, but part of the plugin API. TODO: Find a better place. -class _ImportRedirect(object): - def __init__(self, name, impmask): - ''' Create a virtual package that redirects imports (see PEP 302). ''' - self.name = name - self.impmask = impmask - self.module = sys.modules.setdefault(name, imp.new_module(name)) - self.module.__dict__.update({'__file__': __file__, '__path__': [], - '__all__': [], '__loader__': self}) - sys.meta_path.append(self) - - def find_module(self, fullname, path=None): - if '.' not in fullname: return - packname, modname = fullname.rsplit('.', 1) - if packname != self.name: return - return self - - def load_module(self, fullname): - if fullname in sys.modules: return sys.modules[fullname] - packname, modname = fullname.rsplit('.', 1) - realname = self.impmask % modname - __import__(realname) - module = sys.modules[fullname] = sys.modules[realname] - setattr(self.module, modname, module) - module.__loader__ = self - return module - - - - - - -############################################################################### -# Common Utilities ############################################################# -############################################################################### - - -class MultiDict(DictMixin): - """ This dict stores multiple values per key, but behaves exactly like a - normal dict in that it returns only the newest value for any given key. - There are special methods available to access the full list of values. - """ - - def __init__(self, *a, **k): - self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items()) - - def __len__(self): return len(self.dict) - def __iter__(self): return iter(self.dict) - def __contains__(self, key): return key in self.dict - def __delitem__(self, key): del self.dict[key] - def __getitem__(self, key): return self.dict[key][-1] - def __setitem__(self, key, value): self.append(key, value) - def keys(self): return self.dict.keys() - - if py3k: - def values(self): return (v[-1] for v in self.dict.values()) - def items(self): return ((k, v[-1]) for k, v in self.dict.items()) - def allitems(self): - return ((k, v) for k, vl in self.dict.items() for v in vl) - iterkeys = keys - itervalues = values - iteritems = items - iterallitems = allitems - - else: - def values(self): return [v[-1] for v in self.dict.values()] - def items(self): return [(k, v[-1]) for k, v in self.dict.items()] - def iterkeys(self): return self.dict.iterkeys() - def itervalues(self): return (v[-1] for v in self.dict.itervalues()) - def iteritems(self): - return ((k, v[-1]) for k, v in self.dict.iteritems()) - def iterallitems(self): - return ((k, v) for k, vl in self.dict.iteritems() for v in vl) - def allitems(self): - return [(k, v) for k, vl in self.dict.iteritems() for v in vl] - - def get(self, key, default=None, index=-1, type=None): - ''' Return the most recent value for a key. - - :param default: The default value to be returned if the key is not - present or the type conversion fails. - :param index: An index for the list of available values. - :param type: If defined, this callable is used to cast the value - into a specific type. Exception are suppressed and result in - the default value to be returned. - ''' - try: - val = self.dict[key][index] - return type(val) if type else val - except Exception: - pass - return default - - def append(self, key, value): - ''' Add a new value to the list of values for this key. ''' - self.dict.setdefault(key, []).append(value) - - def replace(self, key, value): - ''' Replace the list of values with a single value. ''' - self.dict[key] = [value] - - def getall(self, key): - ''' Return a (possibly empty) list of values for a key. ''' - return self.dict.get(key) or [] - - #: Aliases for WTForms to mimic other multi-dict APIs (Django) - getone = get - getlist = getall - - -class FormsDict(MultiDict): - ''' This :class:`MultiDict` subclass is used to store request form data. - Additionally to the normal dict-like item access methods (which return - unmodified data as native strings), this container also supports - attribute-like access to its values. Attributes are automatically de- - or recoded to match :attr:`input_encoding` (default: 'utf8'). Missing - attributes default to an empty string. ''' - - #: Encoding used for attribute values. - input_encoding = 'utf8' - #: If true (default), unicode strings are first encoded with `latin1` - #: and then decoded to match :attr:`input_encoding`. - recode_unicode = True - - def _fix(self, s, encoding=None): - if isinstance(s, unicode) and self.recode_unicode: # Python 3 WSGI - s = s.encode('latin1') - if isinstance(s, bytes): # Python 2 WSGI - return s.decode(encoding or self.input_encoding) - return s - - def decode(self, encoding=None): - ''' Returns a copy with all keys and values de- or recoded to match - :attr:`input_encoding`. Some libraries (e.g. WTForms) want a - unicode dictionary. ''' - copy = FormsDict() - enc = copy.input_encoding = encoding or self.input_encoding - copy.recode_unicode = False - for key, value in self.allitems(): - copy.append(self._fix(key, enc), self._fix(value, enc)) - return copy - - def getunicode(self, name, default=None, encoding=None): - ''' Return the value as a unicode string, or the default. ''' - try: - return self._fix(self[name], encoding) - except (UnicodeError, KeyError): - return default - - def __getattr__(self, name, default=unicode()): - # Without this guard, pickle generates a cryptic TypeError: - if name.startswith('__') and name.endswith('__'): - return super(FormsDict, self).__getattr__(name) - return self.getunicode(name, default=default) - - -class HeaderDict(MultiDict): - """ A case-insensitive version of :class:`MultiDict` that defaults to - replace the old value instead of appending it. """ - - def __init__(self, *a, **ka): - self.dict = {} - if a or ka: self.update(*a, **ka) - - def __contains__(self, key): return _hkey(key) in self.dict - def __delitem__(self, key): del self.dict[_hkey(key)] - def __getitem__(self, key): return self.dict[_hkey(key)][-1] - def __setitem__(self, key, value): self.dict[_hkey(key)] = [str(value)] - def append(self, key, value): - self.dict.setdefault(_hkey(key), []).append(str(value)) - def replace(self, key, value): self.dict[_hkey(key)] = [str(value)] - def getall(self, key): return self.dict.get(_hkey(key)) or [] - def get(self, key, default=None, index=-1): - return MultiDict.get(self, _hkey(key), default, index) - def filter(self, names): - for name in [_hkey(n) for n in names]: - if name in self.dict: - del self.dict[name] - - -class WSGIHeaderDict(DictMixin): - ''' This dict-like class wraps a WSGI environ dict and provides convenient - access to HTTP_* fields. Keys and values are native strings - (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI - environment contains non-native string values, these are de- or encoded - using a lossless 'latin1' character set. - - The API will remain stable even on changes to the relevant PEPs. - Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one - that uses non-native strings.) - ''' - #: List of keys that do not have a ``HTTP_`` prefix. - cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH') - - def __init__(self, environ): - self.environ = environ - - def _ekey(self, key): - ''' Translate header field name to CGI/WSGI environ key. ''' - key = key.replace('-','_').upper() - if key in self.cgikeys: - return key - return 'HTTP_' + key - - def raw(self, key, default=None): - ''' Return the header value as is (may be bytes or unicode). ''' - return self.environ.get(self._ekey(key), default) - - def __getitem__(self, key): - return tonat(self.environ[self._ekey(key)], 'latin1') - - def __setitem__(self, key, value): - raise TypeError("%s is read-only." % self.__class__) - - def __delitem__(self, key): - raise TypeError("%s is read-only." % self.__class__) - - def __iter__(self): - for key in self.environ: - if key[:5] == 'HTTP_': - yield key[5:].replace('_', '-').title() - elif key in self.cgikeys: - yield key.replace('_', '-').title() - - def keys(self): return [x for x in self] - def __len__(self): return len(self.keys()) - def __contains__(self, key): return self._ekey(key) in self.environ - - - -class ConfigDict(dict): - ''' A dict-like configuration storage with additional support for - namespaces, validators, meta-data, on_change listeners and more. - ''' - - __slots__ = ('_meta', '_on_change') - - def __init__(self, *a, **ka): - self._meta = {} - self._on_change = lambda name, value: None - if a or ka: - depr('Constructor does no longer accept parameters.') - self.update(*a, **ka) - - def load_config(self, filename): - ''' Load values from an *.ini style config file. - - If the config file contains sections, their names are used as - namespaces for the values within. The two special sections - ``DEFAULT`` and ``bottle`` refer to the root namespace (no prefix). - ''' - conf = ConfigParser() - conf.read(filename) - for section in conf.sections(): - for key, value in conf.items(section): - if section not in ('DEFAULT', 'bottle'): - key = section + '.' + key - self[key] = value - return self - - def load_dict(self, source, namespace=''): - ''' Load values from a dictionary structure. Nesting can be used to - represent namespaces. - - >>> c.load_dict({'some': {'namespace': {'key': 'value'} } }) - {'some.namespace.key': 'value'} - ''' - for key, value in source.items(): - if isinstance(key, str): - nskey = (namespace + '.' + key).strip('.') - if isinstance(value, dict): - self.load_dict(value, namespace=nskey) - else: - self[nskey] = value - else: - raise TypeError('Key has type %r (not a string)' % type(key)) - return self - - def update(self, *a, **ka): - ''' If the first parameter is a string, all keys are prefixed with this - namespace. Apart from that it works just as the usual dict.update(). - Example: ``update('some.namespace', key='value')`` ''' - prefix = '' - if a and isinstance(a[0], str): - prefix = a[0].strip('.') + '.' - a = a[1:] - for key, value in dict(*a, **ka).items(): - self[prefix+key] = value - - def setdefault(self, key, value): - if key not in self: - self[key] = value - - def __setitem__(self, key, value): - if not isinstance(key, str): - raise TypeError('Key has type %r (not a string)' % type(key)) - value = self.meta_get(key, 'filter', lambda x: x)(value) - if key in self and self[key] is value: - return - self._on_change(key, value) - dict.__setitem__(self, key, value) - - def __delitem__(self, key): - self._on_change(key, None) - dict.__delitem__(self, key) - - def meta_get(self, key, metafield, default=None): - ''' Return the value of a meta field for a key. ''' - return self._meta.get(key, {}).get(metafield, default) - - def meta_set(self, key, metafield, value): - ''' Set the meta field for a key to a new value. This triggers the - on-change handler for existing keys. ''' - self._meta.setdefault(key, {})[metafield] = value - if key in self: - self[key] = self[key] - - def meta_list(self, key): - ''' Return an iterable of meta field names defined for a key. ''' - return self._meta.get(key, {}).keys() - - # Deprecated ConfigDict features - def __getattr__(self, key): - depr('Attribute access is deprecated.') #0.12 - if key not in self and key[0].isupper(): - self[key] = ConfigDict() - return self.get(key) - - def __setattr__(self, key, value): - if key in self.__slots__: - return dict.__setattr__(self, key, value) - depr('Attribute assignment is deprecated.') #0.12 - if hasattr(dict, key): - raise AttributeError('Read-only attribute.') - if key in self and self[key] and isinstance(self[key], ConfigDict): - raise AttributeError('Non-empty namespace attribute.') - self[key] = value - - def __delattr__(self, key): - if key in self: del self[key] - - def __call__(self, *a, **ka): - depr('Calling ConfDict is deprecated. Use the update() method.') #0.12 - self.update(*a, **ka) - return self - - - -class AppStack(list): - """ A stack-like list. Calling it returns the head of the stack. """ - - def __call__(self): - """ Return the current default application. """ - return self[-1] - - def push(self, value=None): - """ Add a new :class:`Bottle` instance to the stack """ - if not isinstance(value, Bottle): - value = Bottle() - self.append(value) - return value - - -class WSGIFileWrapper(object): - - def __init__(self, fp, buffer_size=1024*64): - self.fp, self.buffer_size = fp, buffer_size - for attr in ('fileno', 'close', 'read', 'readlines', 'tell', 'seek'): - if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr)) - - def __iter__(self): - buff, read = self.buffer_size, self.read - while True: - part = read(buff) - if not part: return - yield part - - -class _closeiter(object): - ''' This only exists to be able to attach a .close method to iterators that - do not support attribute assignment (most of itertools). ''' - - def __init__(self, iterator, close=None): - self.iterator = iterator - self.close_callbacks = makelist(close) - - def __iter__(self): - return iter(self.iterator) - - def close(self): - for func in self.close_callbacks: - func() - - -class ResourceManager(object): - ''' This class manages a list of search paths and helps to find and open - application-bound resources (files). - - :param base: default value for :meth:`add_path` calls. - :param opener: callable used to open resources. - :param cachemode: controls which lookups are cached. One of 'all', - 'found' or 'none'. - ''' - - def __init__(self, base='./', opener=open, cachemode='all'): - self.opener = open - self.base = base - self.cachemode = cachemode - - #: A list of search paths. See :meth:`add_path` for details. - self.path = [] - #: A cache for resolved paths. ``res.cache.clear()`` clears the cache. - self.cache = {} - - def add_path(self, path, base=None, index=None, create=False): - ''' Add a new path to the list of search paths. Return False if the - path does not exist. - - :param path: The new search path. Relative paths are turned into - an absolute and normalized form. If the path looks like a file - (not ending in `/`), the filename is stripped off. - :param base: Path used to absolutize relative search paths. - Defaults to :attr:`base` which defaults to ``os.getcwd()``. - :param index: Position within the list of search paths. Defaults - to last index (appends to the list). - - The `base` parameter makes it easy to reference files installed - along with a python module or package:: - - res.add_path('./resources/', __file__) - ''' - base = os.path.abspath(os.path.dirname(base or self.base)) - path = os.path.abspath(os.path.join(base, os.path.dirname(path))) - path += os.sep - if path in self.path: - self.path.remove(path) - if create and not os.path.isdir(path): - os.makedirs(path) - if index is None: - self.path.append(path) - else: - self.path.insert(index, path) - self.cache.clear() - return os.path.exists(path) - - def __iter__(self): - ''' Iterate over all existing files in all registered paths. ''' - search = self.path[:] - while search: - path = search.pop() - if not os.path.isdir(path): continue - for name in os.listdir(path): - full = os.path.join(path, name) - if os.path.isdir(full): search.append(full) - else: yield full - - def lookup(self, name): - ''' Search for a resource and return an absolute file path, or `None`. - - The :attr:`path` list is searched in order. The first match is - returend. Symlinks are followed. The result is cached to speed up - future lookups. ''' - if name not in self.cache or DEBUG: - for path in self.path: - fpath = os.path.join(path, name) - if os.path.isfile(fpath): - if self.cachemode in ('all', 'found'): - self.cache[name] = fpath - return fpath - if self.cachemode == 'all': - self.cache[name] = None - return self.cache[name] - - def open(self, name, mode='r', *args, **kwargs): - ''' Find a resource and return a file object, or raise IOError. ''' - fname = self.lookup(name) - if not fname: raise IOError("Resource %r not found." % name) - return self.opener(fname, mode=mode, *args, **kwargs) - - -class FileUpload(object): - - def __init__(self, fileobj, name, filename, headers=None): - ''' Wrapper for file uploads. ''' - #: Open file(-like) object (BytesIO buffer or temporary file) - self.file = fileobj - #: Name of the upload form field - self.name = name - #: Raw filename as sent by the client (may contain unsafe characters) - self.raw_filename = filename - #: A :class:`HeaderDict` with additional headers (e.g. content-type) - self.headers = HeaderDict(headers) if headers else HeaderDict() - - content_type = HeaderProperty('Content-Type') - content_length = HeaderProperty('Content-Length', reader=int, default=-1) - - @cached_property - def filename(self): - ''' Name of the file on the client file system, but normalized to ensure - file system compatibility (lowercase, no whitespace, no path - separators, no unsafe characters, ASCII only). An empty filename - is returned as 'empty'. - ''' - from unicodedata import normalize #TODO: Module level import? - fname = self.raw_filename - if isinstance(fname, unicode): - fname = normalize('NFKD', fname).encode('ASCII', 'ignore') - fname = fname.decode('ASCII', 'ignore') - fname = os.path.basename(fname.replace('\\', os.path.sep)) - fname = re.sub(r'[^a-zA-Z0-9-_.\s]', '', fname).strip().lower() - fname = re.sub(r'[-\s]+', '-', fname.strip('.').strip()) - return fname or 'empty' - - def _copy_file(self, fp, chunk_size=2**16): - read, write, offset = self.file.read, fp.write, self.file.tell() - while 1: - buf = read(chunk_size) - if not buf: break - write(buf) - self.file.seek(offset) - - def save(self, destination, overwrite=False, chunk_size=2**16): - ''' Save file to disk or copy its content to an open file(-like) object. - If *destination* is a directory, :attr:`filename` is added to the - path. Existing files are not overwritten by default (IOError). - - :param destination: File path, directory or file(-like) object. - :param overwrite: If True, replace existing files. (default: False) - :param chunk_size: Bytes to read at a time. (default: 64kb) - ''' - if isinstance(destination, basestring): # Except file-likes here - if os.path.isdir(destination): - destination = os.path.join(destination, self.filename) - if not overwrite and os.path.exists(destination): - raise IOError('File exists.') - with open(destination, 'wb') as fp: - self._copy_file(fp, chunk_size) - else: - self._copy_file(destination, chunk_size) - - - - - - -############################################################################### -# Application Helper ########################################################### -############################################################################### - - -def abort(code=500, text='Unknown Error: Application stopped.'): - """ Aborts execution and causes a HTTP error. """ - raise HTTPError(code, text) - - -def redirect(url, code=None): - """ Aborts execution and causes a 303 or 302 redirect, depending on - the HTTP protocol version. """ - if not code: - code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302 - res = response.copy(cls=HTTPResponse) - res.status = code - res.body = "" - res.set_header('Location', urljoin(request.url, url)) - raise res - - -def _file_iter_range(fp, offset, bytes, maxread=1024*1024): - ''' Yield chunks from a range in a file. No chunk is bigger than maxread.''' - fp.seek(offset) - while bytes > 0: - part = fp.read(min(bytes, maxread)) - if not part: break - bytes -= len(part) - yield part - - -def static_file(filename, root, mimetype='auto', download=False, charset='UTF-8'): - """ Open a file in a safe way and return :exc:`HTTPResponse` with status - code 200, 305, 401 or 404. The ``Content-Type``, ``Content-Encoding``, - ``Content-Length`` and ``Last-Modified`` headers are set if possible. - Special support for ``If-Modified-Since``, ``Range`` and ``HEAD`` - requests. - - :param filename: Name or path of the file to send. - :param root: Root path for file lookups. Should be an absolute directory - path. - :param mimetype: Defines the content-type header (default: guess from - file extension) - :param download: If True, ask the browser to open a `Save as...` dialog - instead of opening the file with the associated program. You can - specify a custom filename as a string. If not specified, the - original filename is used (default: False). - :param charset: The charset to use for files with a ``text/*`` - mime-type. (default: UTF-8) - """ - - root = os.path.abspath(root) + os.sep - filename = os.path.abspath(os.path.join(root, filename.strip('/\\'))) - headers = dict() - - if not filename.startswith(root): - return HTTPError(403, "Access denied.") - if not os.path.exists(filename) or not os.path.isfile(filename): - return HTTPError(404, "File does not exist.") - if not os.access(filename, os.R_OK): - return HTTPError(403, "You do not have permission to access this file.") - - if mimetype == 'auto': - mimetype, encoding = mimetypes.guess_type(filename) - if encoding: headers['Content-Encoding'] = encoding - - if mimetype: - if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype: - mimetype += '; charset=%s' % charset - headers['Content-Type'] = mimetype - - if download: - download = os.path.basename(filename if download == True else download) - headers['Content-Disposition'] = 'attachment; filename="%s"' % download - - stats = os.stat(filename) - headers['Content-Length'] = clen = stats.st_size - lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)) - headers['Last-Modified'] = lm - - ims = request.environ.get('HTTP_IF_MODIFIED_SINCE') - if ims: - ims = parse_date(ims.split(";")[0].strip()) - if ims is not None and ims >= int(stats.st_mtime): - headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) - return HTTPResponse(status=304, **headers) - - body = '' if request.method == 'HEAD' else open(filename, 'rb') - - headers["Accept-Ranges"] = "bytes" - ranges = request.environ.get('HTTP_RANGE') - if 'HTTP_RANGE' in request.environ: - ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen)) - if not ranges: - return HTTPError(416, "Requested Range Not Satisfiable") - offset, end = ranges[0] - headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end-1, clen) - headers["Content-Length"] = str(end-offset) - if body: body = _file_iter_range(body, offset, end-offset) - return HTTPResponse(body, status=206, **headers) - return HTTPResponse(body, **headers) - - - - - - -############################################################################### -# HTTP Utilities and MISC (TODO) ############################################### -############################################################################### - - -def debug(mode=True): - """ Change the debug level. - There is only one debug level supported at the moment.""" - global DEBUG - if mode: warnings.simplefilter('default') - DEBUG = bool(mode) - -def http_date(value): - if isinstance(value, (datedate, datetime)): - value = value.utctimetuple() - elif isinstance(value, (int, float)): - value = time.gmtime(value) - if not isinstance(value, basestring): - value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value) - return value - -def parse_date(ims): - """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """ - try: - ts = email.utils.parsedate_tz(ims) - return time.mktime(ts[:8] + (0,)) - (ts[9] or 0) - time.timezone - except (TypeError, ValueError, IndexError, OverflowError): - return None - -def parse_auth(header): - """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None""" - try: - method, data = header.split(None, 1) - if method.lower() == 'basic': - user, pwd = touni(base64.b64decode(tob(data))).split(':',1) - return user, pwd - except (KeyError, ValueError): - return None - -def parse_range_header(header, maxlen=0): - ''' Yield (start, end) ranges parsed from a HTTP Range header. Skip - unsatisfiable ranges. The end index is non-inclusive.''' - if not header or header[:6] != 'bytes=': return - ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r] - for start, end in ranges: - try: - if not start: # bytes=-100 -> last 100 bytes - start, end = max(0, maxlen-int(end)), maxlen - elif not end: # bytes=100- -> all but the first 99 bytes - start, end = int(start), maxlen - else: # bytes=100-200 -> bytes 100-200 (inclusive) - start, end = int(start), min(int(end)+1, maxlen) - if 0 <= start < end <= maxlen: - yield start, end - except ValueError: - pass - -def _parse_qsl(qs): - r = [] - for pair in qs.replace(';','&').split('&'): - if not pair: continue - nv = pair.split('=', 1) - if len(nv) != 2: nv.append('') - key = urlunquote(nv[0].replace('+', ' ')) - value = urlunquote(nv[1].replace('+', ' ')) - r.append((key, value)) - return r - -def _lscmp(a, b): - ''' Compares two strings in a cryptographically safe way: - Runtime is not affected by length of common prefix. ''' - return not sum(0 if x==y else 1 for x, y in zip(a, b)) and len(a) == len(b) - - -def cookie_encode(data, key): - ''' Encode and sign a pickle-able object. Return a (byte) string ''' - msg = base64.b64encode(pickle.dumps(data, -1)) - sig = base64.b64encode(hmac.new(tob(key), msg).digest()) - return tob('!') + sig + tob('?') + msg - - -def cookie_decode(data, key): - ''' Verify and decode an encoded string. Return an object or None.''' - data = tob(data) - if cookie_is_encoded(data): - sig, msg = data.split(tob('?'), 1) - if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())): - return pickle.loads(base64.b64decode(msg)) - return None - - -def cookie_is_encoded(data): - ''' Return True if the argument looks like a encoded cookie.''' - return bool(data.startswith(tob('!')) and tob('?') in data) - - -def html_escape(string): - ''' Escape HTML special characters ``&<>`` and quotes ``'"``. ''' - return string.replace('&','&').replace('<','<').replace('>','>')\ - .replace('"','"').replace("'",''') - - -def html_quote(string): - ''' Escape and quote a string to be used as an HTTP attribute.''' - return '"%s"' % html_escape(string).replace('\n','%#10;')\ - .replace('\r',' ').replace('\t',' ') - - -def yieldroutes(func): - """ Return a generator for routes that match the signature (name, args) - of the func parameter. This may yield more than one route if the function - takes optional keyword arguments. The output is best described by example:: - - a() -> '/a' - b(x, y) -> '/b//' - c(x, y=5) -> '/c/' and '/c//' - d(x=5, y=6) -> '/d' and '/d/' and '/d//' - """ - path = '/' + func.__name__.replace('__','/').lstrip('/') - spec = getargspec(func) - argc = len(spec[0]) - len(spec[3] or []) - path += ('/<%s>' * argc) % tuple(spec[0][:argc]) - yield path - for arg in spec[0][argc:]: - path += '/<%s>' % arg - yield path - - -def path_shift(script_name, path_info, shift=1): - ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa. - - :return: The modified paths. - :param script_name: The SCRIPT_NAME path. - :param script_name: The PATH_INFO path. - :param shift: The number of path fragments to shift. May be negative to - change the shift direction. (default: 1) - ''' - if shift == 0: return script_name, path_info - pathlist = path_info.strip('/').split('/') - scriptlist = script_name.strip('/').split('/') - if pathlist and pathlist[0] == '': pathlist = [] - if scriptlist and scriptlist[0] == '': scriptlist = [] - if shift > 0 and shift <= len(pathlist): - moved = pathlist[:shift] - scriptlist = scriptlist + moved - pathlist = pathlist[shift:] - elif shift < 0 and shift >= -len(scriptlist): - moved = scriptlist[shift:] - pathlist = moved + pathlist - scriptlist = scriptlist[:shift] - else: - empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO' - raise AssertionError("Cannot shift. Nothing left from %s" % empty) - new_script_name = '/' + '/'.join(scriptlist) - new_path_info = '/' + '/'.join(pathlist) - if path_info.endswith('/') and pathlist: new_path_info += '/' - return new_script_name, new_path_info - - -def auth_basic(check, realm="private", text="Access denied"): - ''' Callback decorator to require HTTP auth (basic). - TODO: Add route(check_auth=...) parameter. ''' - def decorator(func): - def wrapper(*a, **ka): - user, password = request.auth or (None, None) - if user is None or not check(user, password): - err = HTTPError(401, text) - err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm) - return err - return func(*a, **ka) - return wrapper - return decorator - - -# Shortcuts for common Bottle methods. -# They all refer to the current default application. - -def make_default_app_wrapper(name): - ''' Return a callable that relays calls to the current default app. ''' - @functools.wraps(getattr(Bottle, name)) - def wrapper(*a, **ka): - return getattr(app(), name)(*a, **ka) - return wrapper - -route = make_default_app_wrapper('route') -get = make_default_app_wrapper('get') -post = make_default_app_wrapper('post') -put = make_default_app_wrapper('put') -delete = make_default_app_wrapper('delete') -error = make_default_app_wrapper('error') -mount = make_default_app_wrapper('mount') -hook = make_default_app_wrapper('hook') -install = make_default_app_wrapper('install') -uninstall = make_default_app_wrapper('uninstall') -url = make_default_app_wrapper('get_url') - - - - - - - -############################################################################### -# Server Adapter ############################################################### -############################################################################### - - -class ServerAdapter(object): - quiet = False - def __init__(self, host='127.0.0.1', port=8080, **options): - self.options = options - self.host = host - self.port = int(port) - - def run(self, handler): # pragma: no cover - pass - - def __repr__(self): - args = ', '.join(['%s=%s'%(k,repr(v)) for k, v in self.options.items()]) - return "%s(%s)" % (self.__class__.__name__, args) - - -class CGIServer(ServerAdapter): - quiet = True - def run(self, handler): # pragma: no cover - from wsgiref.handlers import CGIHandler - def fixed_environ(environ, start_response): - environ.setdefault('PATH_INFO', '') - return handler(environ, start_response) - CGIHandler().run(fixed_environ) - - -class FlupFCGIServer(ServerAdapter): - def run(self, handler): # pragma: no cover - import flup.server.fcgi - self.options.setdefault('bindAddress', (self.host, self.port)) - flup.server.fcgi.WSGIServer(handler, **self.options).run() - - -class WSGIRefServer(ServerAdapter): - def run(self, app): # pragma: no cover - from wsgiref.simple_server import WSGIRequestHandler, WSGIServer - from wsgiref.simple_server import make_server - import socket - - class FixedHandler(WSGIRequestHandler): - def address_string(self): # Prevent reverse DNS lookups please. - return self.client_address[0] - def log_request(*args, **kw): - if not self.quiet: - return WSGIRequestHandler.log_request(*args, **kw) - - handler_cls = self.options.get('handler_class', FixedHandler) - server_cls = self.options.get('server_class', WSGIServer) - - if ':' in self.host: # Fix wsgiref for IPv6 addresses. - if getattr(server_cls, 'address_family') == socket.AF_INET: - class server_cls(server_cls): - address_family = socket.AF_INET6 - - srv = make_server(self.host, self.port, app, server_cls, handler_cls) - srv.serve_forever() - - -class CherryPyServer(ServerAdapter): - def run(self, handler): # pragma: no cover - from cherrypy import wsgiserver - self.options['bind_addr'] = (self.host, self.port) - self.options['wsgi_app'] = handler - server = wsgiserver.CherryPyWSGIServer(**self.options) - try: - server.start() - finally: - server.stop() - - -class WaitressServer(ServerAdapter): - def run(self, handler): - from waitress import serve - serve(handler, host=self.host, port=self.port) - - -class PasteServer(ServerAdapter): - def run(self, handler): # pragma: no cover - from paste import httpserver - from paste.translogger import TransLogger - handler = TransLogger(handler, setup_console_handler=(not self.quiet)) - httpserver.serve(handler, host=self.host, port=str(self.port), - **self.options) - - -class MeinheldServer(ServerAdapter): - def run(self, handler): - from meinheld import server - server.listen((self.host, self.port)) - server.run(handler) - - -class FapwsServer(ServerAdapter): - """ Extremely fast webserver using libev. See http://www.fapws.org/ """ - def run(self, handler): # pragma: no cover - import fapws._evwsgi as evwsgi - from fapws import base, config - port = self.port - if float(config.SERVER_IDENT[-2:]) > 0.4: - # fapws3 silently changed its API in 0.5 - port = str(port) - evwsgi.start(self.host, port) - # fapws3 never releases the GIL. Complain upstream. I tried. No luck. - if 'BOTTLE_CHILD' in os.environ and not self.quiet: - _stderr("WARNING: Auto-reloading does not work with Fapws3.\n") - _stderr(" (Fapws3 breaks python thread support)\n") - evwsgi.set_base_module(base) - def app(environ, start_response): - environ['wsgi.multiprocess'] = False - return handler(environ, start_response) - evwsgi.wsgi_cb(('', app)) - evwsgi.run() - - -class TornadoServer(ServerAdapter): - """ The super hyped asynchronous server by facebook. Untested. """ - def run(self, handler): # pragma: no cover - import tornado.wsgi, tornado.httpserver, tornado.ioloop - container = tornado.wsgi.WSGIContainer(handler) - server = tornado.httpserver.HTTPServer(container) - server.listen(port=self.port,address=self.host) - tornado.ioloop.IOLoop.instance().start() - - -class AppEngineServer(ServerAdapter): - """ Adapter for Google App Engine. """ - quiet = True - def run(self, handler): - from google.appengine.ext.webapp import util - # A main() function in the handler script enables 'App Caching'. - # Lets makes sure it is there. This _really_ improves performance. - module = sys.modules.get('__main__') - if module and not hasattr(module, 'main'): - module.main = lambda: util.run_wsgi_app(handler) - util.run_wsgi_app(handler) - - -class TwistedServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from twisted.web import server, wsgi - from twisted.python.threadpool import ThreadPool - from twisted.internet import reactor - thread_pool = ThreadPool() - thread_pool.start() - reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop) - factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler)) - reactor.listenTCP(self.port, factory, interface=self.host) - reactor.run() - - -class DieselServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from diesel.protocols.wsgi import WSGIApplication - app = WSGIApplication(handler, port=self.port) - app.run() - - -class GeventServer(ServerAdapter): - """ Untested. Options: - - * `fast` (default: False) uses libevent's http server, but has some - issues: No streaming, no pipelining, no SSL. - * See gevent.wsgi.WSGIServer() documentation for more options. - """ - def run(self, handler): - from gevent import wsgi, pywsgi, local - if not isinstance(_lctx, local.local): - msg = "Bottle requires gevent.monkey.patch_all() (before import)" - raise RuntimeError(msg) - if not self.options.pop('fast', None): wsgi = pywsgi - self.options['log'] = None if self.quiet else 'default' - address = (self.host, self.port) - wsgi.WSGIServer(address, handler, **self.options).serve_forever() - - -class GunicornServer(ServerAdapter): - """ Untested. See http://gunicorn.org/configure.html for options. """ - def run(self, handler): - from gunicorn.app.base import Application - - config = {'bind': "%s:%d" % (self.host, int(self.port))} - config.update(self.options) - - class GunicornApplication(Application): - def init(self, parser, opts, args): - return config - - def load(self): - return handler - - GunicornApplication().run() - - -class EventletServer(ServerAdapter): - """ Untested """ - def run(self, handler): - from eventlet import wsgi, listen - try: - wsgi.server(listen((self.host, self.port)), handler, - log_output=(not self.quiet)) - except TypeError: - # Fallback, if we have old version of eventlet - wsgi.server(listen((self.host, self.port)), handler) - - -class RocketServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from rocket import Rocket - server = Rocket((self.host, self.port), 'wsgi', { 'wsgi_app' : handler }) - server.start() - - -class BjoernServer(ServerAdapter): - """ Fast server written in C: https://github.com/jonashaag/bjoern """ - def run(self, handler): - from bjoern import run - run(handler, self.host, self.port) - - -class AutoServer(ServerAdapter): - """ Untested. """ - adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer, WSGIRefServer] - def run(self, handler): - for sa in self.adapters: - try: - return sa(self.host, self.port, **self.options).run(handler) - except ImportError: - pass - -server_names = { - 'cgi': CGIServer, - 'flup': FlupFCGIServer, - 'wsgiref': WSGIRefServer, - 'waitress': WaitressServer, - 'cherrypy': CherryPyServer, - 'paste': PasteServer, - 'fapws3': FapwsServer, - 'tornado': TornadoServer, - 'gae': AppEngineServer, - 'twisted': TwistedServer, - 'diesel': DieselServer, - 'meinheld': MeinheldServer, - 'gunicorn': GunicornServer, - 'eventlet': EventletServer, - 'gevent': GeventServer, - 'rocket': RocketServer, - 'bjoern' : BjoernServer, - 'auto': AutoServer, -} - - - - - - -############################################################################### -# Application Control ########################################################## -############################################################################### - - -def load(target, **namespace): - """ Import a module or fetch an object from a module. - - * ``package.module`` returns `module` as a module object. - * ``pack.mod:name`` returns the module variable `name` from `pack.mod`. - * ``pack.mod:func()`` calls `pack.mod.func()` and returns the result. - - The last form accepts not only function calls, but any type of - expression. Keyword arguments passed to this function are available as - local variables. Example: ``import_string('re:compile(x)', x='[a-z]')`` - """ - module, target = target.split(":", 1) if ':' in target else (target, None) - if module not in sys.modules: __import__(module) - if not target: return sys.modules[module] - if target.isalnum(): return getattr(sys.modules[module], target) - package_name = module.split('.')[0] - namespace[package_name] = sys.modules[package_name] - return eval('%s.%s' % (module, target), namespace) - - -def load_app(target): - """ Load a bottle application from a module and make sure that the import - does not affect the current default application, but returns a separate - application object. See :func:`load` for the target parameter. """ - global NORUN; NORUN, nr_old = True, NORUN - try: - tmp = default_app.push() # Create a new "default application" - rv = load(target) # Import the target module - return rv if callable(rv) else tmp - finally: - default_app.remove(tmp) # Remove the temporary added default application - NORUN = nr_old - -_debug = debug -def run(app=None, server='wsgiref', host='127.0.0.1', port=8080, - interval=1, reloader=False, quiet=False, plugins=None, - debug=None, **kargs): - """ Start a server instance. This method blocks until the server terminates. - - :param app: WSGI application or target string supported by - :func:`load_app`. (default: :func:`default_app`) - :param server: Server adapter to use. See :data:`server_names` keys - for valid names or pass a :class:`ServerAdapter` subclass. - (default: `wsgiref`) - :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on - all interfaces including the external one. (default: 127.0.0.1) - :param port: Server port to bind to. Values below 1024 require root - privileges. (default: 8080) - :param reloader: Start auto-reloading server? (default: False) - :param interval: Auto-reloader interval in seconds (default: 1) - :param quiet: Suppress output to stdout and stderr? (default: False) - :param options: Options passed to the server adapter. - """ - if NORUN: return - if reloader and not os.environ.get('BOTTLE_CHILD'): - try: - lockfile = None - fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock') - os.close(fd) # We only need this file to exist. We never write to it - while os.path.exists(lockfile): - args = [sys.executable] + sys.argv - environ = os.environ.copy() - environ['BOTTLE_CHILD'] = 'true' - environ['BOTTLE_LOCKFILE'] = lockfile - p = subprocess.Popen(args, env=environ) - while p.poll() is None: # Busy wait... - os.utime(lockfile, None) # I am alive! - time.sleep(interval) - if p.poll() != 3: - if os.path.exists(lockfile): os.unlink(lockfile) - sys.exit(p.poll()) - except KeyboardInterrupt: - pass - finally: - if os.path.exists(lockfile): - os.unlink(lockfile) - return - - try: - if debug is not None: _debug(debug) - app = app or default_app() - if isinstance(app, basestring): - app = load_app(app) - if not callable(app): - raise ValueError("Application is not callable: %r" % app) - - for plugin in plugins or []: - app.install(plugin) - - if server in server_names: - server = server_names.get(server) - if isinstance(server, basestring): - server = load(server) - if isinstance(server, type): - server = server(host=host, port=port, **kargs) - if not isinstance(server, ServerAdapter): - raise ValueError("Unknown or unsupported server: %r" % server) - - server.quiet = server.quiet or quiet - if not server.quiet: - _stderr("Bottle v%s server starting up (using %s)...\n" % (__version__, repr(server))) - _stderr("Listening on http://%s:%d/\n" % (server.host, server.port)) - _stderr("Hit Ctrl-C to quit.\n\n") - - if reloader: - lockfile = os.environ.get('BOTTLE_LOCKFILE') - bgcheck = FileCheckerThread(lockfile, interval) - with bgcheck: - server.run(app) - if bgcheck.status == 'reload': - sys.exit(3) - else: - server.run(app) - except KeyboardInterrupt: - pass - except (SystemExit, MemoryError): - raise - except: - if not reloader: raise - if not getattr(server, 'quiet', quiet): - print_exc() - time.sleep(interval) - sys.exit(3) - - - -class FileCheckerThread(threading.Thread): - ''' Interrupt main-thread as soon as a changed module file is detected, - the lockfile gets deleted or gets to old. ''' - - def __init__(self, lockfile, interval): - threading.Thread.__init__(self) - self.lockfile, self.interval = lockfile, interval - #: Is one of 'reload', 'error' or 'exit' - self.status = None - - def run(self): - exists = os.path.exists - mtime = lambda path: os.stat(path).st_mtime - files = dict() - - for module in list(sys.modules.values()): - path = getattr(module, '__file__', '') - if path[-4:] in ('.pyo', '.pyc'): path = path[:-1] - if path and exists(path): files[path] = mtime(path) - - while not self.status: - if not exists(self.lockfile)\ - or mtime(self.lockfile) < time.time() - self.interval - 5: - self.status = 'error' - thread.interrupt_main() - for path, lmtime in list(files.items()): - if not exists(path) or mtime(path) > lmtime: - self.status = 'reload' - thread.interrupt_main() - break - time.sleep(self.interval) - - def __enter__(self): - self.start() - - def __exit__(self, exc_type, exc_val, exc_tb): - if not self.status: self.status = 'exit' # silent exit - self.join() - return exc_type is not None and issubclass(exc_type, KeyboardInterrupt) - - - - - -############################################################################### -# Template Adapters ############################################################ -############################################################################### - - -class TemplateError(HTTPError): - def __init__(self, message): - HTTPError.__init__(self, 500, message) - - -class BaseTemplate(object): - """ Base class and minimal API for template adapters """ - extensions = ['tpl','html','thtml','stpl'] - settings = {} #used in prepare() - defaults = {} #used in render() - - def __init__(self, source=None, name=None, lookup=[], encoding='utf8', **settings): - """ Create a new template. - If the source parameter (str or buffer) is missing, the name argument - is used to guess a template filename. Subclasses can assume that - self.source and/or self.filename are set. Both are strings. - The lookup, encoding and settings parameters are stored as instance - variables. - The lookup parameter stores a list containing directory paths. - The encoding parameter should be used to decode byte strings or files. - The settings parameter contains a dict for engine-specific settings. - """ - self.name = name - self.source = source.read() if hasattr(source, 'read') else source - self.filename = source.filename if hasattr(source, 'filename') else None - self.lookup = [os.path.abspath(x) for x in lookup] - self.encoding = encoding - self.settings = self.settings.copy() # Copy from class variable - self.settings.update(settings) # Apply - if not self.source and self.name: - self.filename = self.search(self.name, self.lookup) - if not self.filename: - raise TemplateError('Template %s not found.' % repr(name)) - if not self.source and not self.filename: - raise TemplateError('No template specified.') - self.prepare(**self.settings) - - @classmethod - def search(cls, name, lookup=[]): - """ Search name in all directories specified in lookup. - First without, then with common extensions. Return first hit. """ - if not lookup: - depr('The template lookup path list should not be empty.') - lookup = ['.'] - - if os.path.isabs(name) and os.path.isfile(name): - depr('Absolute template path names are deprecated.') - return os.path.abspath(name) - - for spath in lookup: - spath = os.path.abspath(spath) + os.sep - fname = os.path.abspath(os.path.join(spath, name)) - if not fname.startswith(spath): continue - if os.path.isfile(fname): return fname - for ext in cls.extensions: - if os.path.isfile('%s.%s' % (fname, ext)): - return '%s.%s' % (fname, ext) - - @classmethod - def global_config(cls, key, *args): - ''' This reads or sets the global settings stored in class.settings. ''' - if args: - cls.settings = cls.settings.copy() # Make settings local to class - cls.settings[key] = args[0] - else: - return cls.settings[key] - - def prepare(self, **options): - """ Run preparations (parsing, caching, ...). - It should be possible to call this again to refresh a template or to - update settings. - """ - raise NotImplementedError - - def render(self, *args, **kwargs): - """ Render the template with the specified local variables and return - a single byte or unicode string. If it is a byte string, the encoding - must match self.encoding. This method must be thread-safe! - Local variables may be provided in dictionaries (args) - or directly, as keywords (kwargs). - """ - raise NotImplementedError - - -class MakoTemplate(BaseTemplate): - def prepare(self, **options): - from mako.template import Template - from mako.lookup import TemplateLookup - options.update({'input_encoding':self.encoding}) - options.setdefault('format_exceptions', bool(DEBUG)) - lookup = TemplateLookup(directories=self.lookup, **options) - if self.source: - self.tpl = Template(self.source, lookup=lookup, **options) - else: - self.tpl = Template(uri=self.name, filename=self.filename, lookup=lookup, **options) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - _defaults = self.defaults.copy() - _defaults.update(kwargs) - return self.tpl.render(**_defaults) - - -class CheetahTemplate(BaseTemplate): - def prepare(self, **options): - from Cheetah.Template import Template - self.context = threading.local() - self.context.vars = {} - options['searchList'] = [self.context.vars] - if self.source: - self.tpl = Template(source=self.source, **options) - else: - self.tpl = Template(file=self.filename, **options) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - self.context.vars.update(self.defaults) - self.context.vars.update(kwargs) - out = str(self.tpl) - self.context.vars.clear() - return out - - -class Jinja2Template(BaseTemplate): - def prepare(self, filters=None, tests=None, **kwargs): - from jinja2 import Environment, FunctionLoader - if 'prefix' in kwargs: # TODO: to be removed after a while - raise RuntimeError('The keyword argument `prefix` has been removed. ' - 'Use the full jinja2 environment name line_statement_prefix instead.') - self.env = Environment(loader=FunctionLoader(self.loader), **kwargs) - if filters: self.env.filters.update(filters) - if tests: self.env.tests.update(tests) - if self.source: - self.tpl = self.env.from_string(self.source) - else: - self.tpl = self.env.get_template(self.filename) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - _defaults = self.defaults.copy() - _defaults.update(kwargs) - return self.tpl.render(**_defaults) - - def loader(self, name): - fname = self.search(name, self.lookup) - if not fname: return - with open(fname, "rb") as f: - return f.read().decode(self.encoding) - - -class SimpleTemplate(BaseTemplate): - - def prepare(self, escape_func=html_escape, noescape=False, syntax=None, **ka): - self.cache = {} - enc = self.encoding - self._str = lambda x: touni(x, enc) - self._escape = lambda x: escape_func(touni(x, enc)) - self.syntax = syntax - if noescape: - self._str, self._escape = self._escape, self._str - - @cached_property - def co(self): - return compile(self.code, self.filename or '', 'exec') - - @cached_property - def code(self): - source = self.source or open(self.filename, 'rb').read() - try: - source, encoding = touni(source), 'utf8' - except UnicodeError: - depr('Template encodings other than utf8 are no longer supported.') - source, encoding = touni(source, 'latin1'), 'latin1' - parser = StplParser(source, encoding=encoding, syntax=self.syntax) - code = parser.translate() - self.encoding = parser.encoding - return code - - def _rebase(self, _env, _name=None, **kwargs): - if _name is None: - depr('Rebase function called without arguments.' - ' You were probably looking for {{base}}?', True) - _env['_rebase'] = (_name, kwargs) - - def _include(self, _env, _name=None, **kwargs): - if _name is None: - depr('Rebase function called without arguments.' - ' You were probably looking for {{base}}?', True) - env = _env.copy() - env.update(kwargs) - if _name not in self.cache: - self.cache[_name] = self.__class__(name=_name, lookup=self.lookup) - return self.cache[_name].execute(env['_stdout'], env) - - def execute(self, _stdout, kwargs): - env = self.defaults.copy() - env.update(kwargs) - env.update({'_stdout': _stdout, '_printlist': _stdout.extend, - 'include': functools.partial(self._include, env), - 'rebase': functools.partial(self._rebase, env), '_rebase': None, - '_str': self._str, '_escape': self._escape, 'get': env.get, - 'setdefault': env.setdefault, 'defined': env.__contains__ }) - eval(self.co, env) - if env.get('_rebase'): - subtpl, rargs = env.pop('_rebase') - rargs['base'] = ''.join(_stdout) #copy stdout - del _stdout[:] # clear stdout - return self._include(env, subtpl, **rargs) - return env - - def render(self, *args, **kwargs): - """ Render the template using keyword arguments as local variables. """ - env = {}; stdout = [] - for dictarg in args: env.update(dictarg) - env.update(kwargs) - self.execute(stdout, env) - return ''.join(stdout) - - -class StplSyntaxError(TemplateError): pass - - -class StplParser(object): - ''' Parser for stpl templates. ''' - _re_cache = {} #: Cache for compiled re patterns - # This huge pile of voodoo magic splits python code into 8 different tokens. - # 1: All kinds of python strings (trust me, it works) - _re_tok = '((?m)[urbURB]?(?:\'\'(?!\')|""(?!")|\'{6}|"{6}' \ - '|\'(?:[^\\\\\']|\\\\.)+?\'|"(?:[^\\\\"]|\\\\.)+?"' \ - '|\'{3}(?:[^\\\\]|\\\\.|\\n)+?\'{3}' \ - '|"{3}(?:[^\\\\]|\\\\.|\\n)+?"{3}))' - _re_inl = _re_tok.replace('|\\n','') # We re-use this string pattern later - # 2: Comments (until end of line, but not the newline itself) - _re_tok += '|(#.*)' - # 3,4: Keywords that start or continue a python block (only start of line) - _re_tok += '|^([ \\t]*(?:if|for|while|with|try|def|class)\\b)' \ - '|^([ \\t]*(?:elif|else|except|finally)\\b)' - # 5: Our special 'end' keyword (but only if it stands alone) - _re_tok += '|((?:^|;)[ \\t]*end[ \\t]*(?=(?:%(block_close)s[ \\t]*)?\\r?$|;|#))' - # 6: A customizable end-of-code-block template token (only end of line) - _re_tok += '|(%(block_close)s[ \\t]*(?=$))' - # 7: And finally, a single newline. The 8th token is 'everything else' - _re_tok += '|(\\r?\\n)' - # Match the start tokens of code areas in a template - _re_split = '(?m)^[ \t]*(\\\\?)((%(line_start)s)|(%(block_start)s))(%%?)' - # Match inline statements (may contain python strings) - _re_inl = '%%(inline_start)s((?:%s|[^\'"\n]*?)+)%%(inline_end)s' % _re_inl - - default_syntax = '<% %> % {{ }}' - - def __init__(self, source, syntax=None, encoding='utf8'): - self.source, self.encoding = touni(source, encoding), encoding - self.set_syntax(syntax or self.default_syntax) - self.code_buffer, self.text_buffer = [], [] - self.lineno, self.offset = 1, 0 - self.indent, self.indent_mod = 0, 0 - - def get_syntax(self): - ''' Tokens as a space separated string (default: <% %> % {{ }}) ''' - return self._syntax - - def set_syntax(self, syntax): - self._syntax = syntax - self._tokens = syntax.split() - if not syntax in self._re_cache: - names = 'block_start block_close line_start inline_start inline_end' - etokens = map(re.escape, self._tokens) - pattern_vars = dict(zip(names.split(), etokens)) - patterns = (self._re_split, self._re_tok, self._re_inl) - patterns = [re.compile(p%pattern_vars) for p in patterns] - self._re_cache[syntax] = patterns - self.re_split, self.re_tok, self.re_inl = self._re_cache[syntax] - - syntax = property(get_syntax, set_syntax) - - def translate(self): - if self.offset: raise RuntimeError('Parser is a one time instance.') - while True: - m = self.re_split.search(self.source[self.offset:]) - if m: - text = self.source[self.offset:self.offset+m.start()] - self.text_buffer.append(text) - self.offset += m.end() - if m.group(1): # New escape syntax - line, sep, _ = self.source[self.offset:].partition('\n') - self.text_buffer.append(m.group(2)+m.group(5)+line+sep) - self.offset += len(line+sep)+1 - continue - elif m.group(5): # Old escape syntax - depr('Escape code lines with a backslash.') - line, sep, _ = self.source[self.offset:].partition('\n') - self.text_buffer.append(m.group(2)+line+sep) - self.offset += len(line+sep)+1 - continue - self.flush_text() - self.read_code(multiline=bool(m.group(4))) - else: break - self.text_buffer.append(self.source[self.offset:]) - self.flush_text() - return ''.join(self.code_buffer) - - def read_code(self, multiline): - code_line, comment, start_line = '', '', self.lineno - while True: - m = self.re_tok.search(self.source[self.offset:]) - if not m: - code_line += self.source[self.offset:] - self.offset = len(self.source) - self.write_code(code_line.strip(), comment) - return - code_line += self.source[self.offset:self.offset+m.start()] - self.offset += m.end() - _str, _com, _blk1, _blk2, _end, _cend, _nl = m.groups() - if _str: # Python string - code_line += _str - elif _com: # Python comment (up to EOL) - comment = _com - if multiline and _com.strip().endswith(self._tokens[1]): - multiline = False # Allow end-of-block in comments - elif _blk1: # Start-block keyword (if/for/while/def/try/...) - code_line, self.indent_mod = _blk1, -1 - self.indent += 1 - elif _blk2: # Continue-block keyword (else/elif/except/...) - code_line, self.indent_mod = _blk2, -1 - elif _end: # The non-standard 'end'-keyword (ends a block) - self.indent -= 1 - elif _cend: # The end-code-block template token (usually '%>') - if multiline: multiline = False - else: code_line += _cend - else: # \n - self.write_code(code_line.strip(), comment) - self.lineno += 1 - code_line, comment, self.indent_mod = '', '', 0 - if not multiline: - break - - def flush_text(self): - text = ''.join(self.text_buffer) - del self.text_buffer[:] - if not text: return - parts, pos, nl = [], 0, '\\\n'+' '*self.indent - for m in self.re_inl.finditer(text): - prefix, pos = text[pos:m.start()], m.end() - if prefix: - parts.append(nl.join(map(repr, prefix.splitlines(True)))) - if prefix.endswith('\n'): parts[-1] += nl - parts.append(self.process_inline(m.group(1).strip())) - if pos < len(text): - prefix = text[pos:] - lines = prefix.splitlines(True) - if lines[-1].endswith('\\\\\n'): lines[-1] = lines[-1][:-3] - parts.append(nl.join(map(repr, lines))) - code = '_printlist((%s,))' % ', '.join(parts) - self.lineno += code.count('\n')+1 - self.write_code(code) - - def process_inline(self, chunk): - if chunk[0] == '!': return '_str(%s)' % chunk[1:] - return '_escape(%s)' % chunk - - def write_code(self, line, comment=''): - line, comment = self.fix_backward_compatibility(line, comment) - code = ' ' * (self.indent+self.indent_mod) - code += line.lstrip() + comment + '\n' - self.code_buffer.append(code) - - def fix_backward_compatibility(self, line, comment): - parts = line.strip().split(None, 2) - if parts and parts[0] in ('include', 'rebase'): - depr('The include and rebase keywords are functions now.') - if len(parts) == 1: return "_printlist([base])", comment - elif len(parts) == 2: return "_=%s(%r)" % tuple(parts), comment - else: return "_=%s(%r, %s)" % tuple(parts), comment - if self.lineno <= 2 and not line.strip() and 'coding' in comment: - m = re.match(r"#.*coding[:=]\s*([-\w.]+)", comment) - if m: - depr('PEP263 encoding strings in templates are deprecated.') - enc = m.group(1) - self.source = self.source.encode(self.encoding).decode(enc) - self.encoding = enc - return line, comment.replace('coding','coding*') - return line, comment - - -def template(*args, **kwargs): - ''' - Get a rendered template as a string iterator. - You can use a name, a filename or a template string as first parameter. - Template rendering arguments can be passed as dictionaries - or directly (as keyword arguments). - ''' - tpl = args[0] if args else None - adapter = kwargs.pop('template_adapter', SimpleTemplate) - lookup = kwargs.pop('template_lookup', TEMPLATE_PATH) - tplid = (id(lookup), tpl) - if tplid not in TEMPLATES or DEBUG: - settings = kwargs.pop('template_settings', {}) - if isinstance(tpl, adapter): - TEMPLATES[tplid] = tpl - if settings: TEMPLATES[tplid].prepare(**settings) - elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl: - TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings) - else: - TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings) - if not TEMPLATES[tplid]: - abort(500, 'Template (%s) not found' % tpl) - for dictarg in args[1:]: kwargs.update(dictarg) - return TEMPLATES[tplid].render(kwargs) - -mako_template = functools.partial(template, template_adapter=MakoTemplate) -cheetah_template = functools.partial(template, template_adapter=CheetahTemplate) -jinja2_template = functools.partial(template, template_adapter=Jinja2Template) - - -def view(tpl_name, **defaults): - ''' Decorator: renders a template for a handler. - The handler can control its behavior like that: - - - return a dict of template vars to fill out the template - - return something other than a dict and the view decorator will not - process the template, but return the handler result as is. - This includes returning a HTTPResponse(dict) to get, - for instance, JSON with autojson or other castfilters. - ''' - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - result = func(*args, **kwargs) - if isinstance(result, (dict, DictMixin)): - tplvars = defaults.copy() - tplvars.update(result) - return template(tpl_name, **tplvars) - elif result is None: - return template(tpl_name, defaults) - return result - return wrapper - return decorator - -mako_view = functools.partial(view, template_adapter=MakoTemplate) -cheetah_view = functools.partial(view, template_adapter=CheetahTemplate) -jinja2_view = functools.partial(view, template_adapter=Jinja2Template) - - - - - - -############################################################################### -# Constants and Globals ######################################################## -############################################################################### - - -TEMPLATE_PATH = ['./', './views/'] -TEMPLATES = {} -DEBUG = False -NORUN = False # If set, run() does nothing. Used by load_app() - -#: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found') -HTTP_CODES = httplib.responses -HTTP_CODES[418] = "I'm a teapot" # RFC 2324 -HTTP_CODES[428] = "Precondition Required" -HTTP_CODES[429] = "Too Many Requests" -HTTP_CODES[431] = "Request Header Fields Too Large" -HTTP_CODES[511] = "Network Authentication Required" -_HTTP_STATUS_LINES = dict((k, '%d %s'%(k,v)) for (k,v) in HTTP_CODES.items()) - -#: The default template used for error pages. Override with @error() -ERROR_PAGE_TEMPLATE = """ -%%try: - %%from %s import DEBUG, HTTP_CODES, request, touni - - - - Error: {{e.status}} - - - -

Error: {{e.status}}

-

Sorry, the requested URL {{repr(request.url)}} - caused an error:

-
{{e.body}}
- %%if DEBUG and e.exception: -

Exception:

-
{{repr(e.exception)}}
- %%end - %%if DEBUG and e.traceback: -

Traceback:

-
{{e.traceback}}
- %%end - - -%%except ImportError: - ImportError: Could not generate the error page. Please add bottle to - the import path. -%%end -""" % __name__ - -#: A thread-safe instance of :class:`LocalRequest`. If accessed from within a -#: request callback, this instance always refers to the *current* request -#: (even on a multithreaded server). -request = LocalRequest() - -#: A thread-safe instance of :class:`LocalResponse`. It is used to change the -#: HTTP response for the *current* request. -response = LocalResponse() - -#: A thread-safe namespace. Not used by Bottle. -local = threading.local() - -# Initialize app stack (create first empty Bottle app) -# BC: 0.6.4 and needed for run() -app = default_app = AppStack() -app.push() - -#: A virtual package that redirects import statements. -#: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`. -ext = _ImportRedirect('bottle.ext' if __name__ == '__main__' else __name__+".ext", 'bottle_%s').module - -if __name__ == '__main__': - opt, args, parser = _cmd_options, _cmd_args, _cmd_parser - if opt.version: - _stdout('Bottle %s\n'%__version__) - sys.exit(0) - if not args: - parser.print_help() - _stderr('\nError: No application specified.\n') - sys.exit(1) - - sys.path.insert(0, '.') - sys.modules.setdefault('bottle', sys.modules['__main__']) - - host, port = (opt.bind or 'localhost'), 8080 - if ':' in host and host.rfind(']') < host.rfind(':'): - host, port = host.rsplit(':', 1) - host = host.strip('[]') - - run(args[0], host=host, port=int(port), server=opt.server, - reloader=opt.reload, plugins=opt.plugin, debug=opt.debug) - - - - -# THE END diff --git a/impattern/bottle.pyc b/impattern/bottle.pyc deleted file mode 100644 index da7d1e1..0000000 Binary files a/impattern/bottle.pyc and /dev/null differ diff --git a/impattern/config.py b/impattern/config.py deleted file mode 100644 index 655e142..0000000 --- a/impattern/config.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/python -AWS_ACCESS_KEY_ID = '' -AWS_SECRET_ACCESS_KEY = '' diff --git a/impattern/config.pyc b/impattern/config.pyc deleted file mode 100644 index b3c24b1..0000000 Binary files a/impattern/config.pyc and /dev/null differ diff --git a/impattern/dithers/1.png b/impattern/dithers/1.png deleted file mode 100644 index a0aeccb..0000000 Binary files a/impattern/dithers/1.png and /dev/null differ diff --git a/impattern/dithers/10.png b/impattern/dithers/10.png deleted file mode 100644 index 3865589..0000000 Binary files a/impattern/dithers/10.png and /dev/null differ diff --git a/impattern/dithers/11.png b/impattern/dithers/11.png deleted file mode 100644 index 2a8ca34..0000000 Binary files a/impattern/dithers/11.png and /dev/null differ diff --git a/impattern/dithers/12.png b/impattern/dithers/12.png deleted file mode 100644 index 4adb35f..0000000 Binary files a/impattern/dithers/12.png and /dev/null differ diff --git a/impattern/dithers/13.png b/impattern/dithers/13.png deleted file mode 100644 index 48e9362..0000000 Binary files a/impattern/dithers/13.png and /dev/null differ diff --git a/impattern/dithers/14.png b/impattern/dithers/14.png deleted file mode 100644 index 867b0a9..0000000 Binary files a/impattern/dithers/14.png and /dev/null differ diff --git a/impattern/dithers/15.png b/impattern/dithers/15.png deleted file mode 100644 index f37be5a..0000000 Binary files a/impattern/dithers/15.png and /dev/null differ diff --git a/impattern/dithers/16.png b/impattern/dithers/16.png deleted file mode 100644 index 7994ced..0000000 Binary files a/impattern/dithers/16.png and /dev/null differ diff --git a/impattern/dithers/17.png b/impattern/dithers/17.png deleted file mode 100644 index 9ffa444..0000000 Binary files a/impattern/dithers/17.png and /dev/null differ diff --git a/impattern/dithers/18.png b/impattern/dithers/18.png deleted file mode 100644 index 2522e8d..0000000 Binary files a/impattern/dithers/18.png and /dev/null differ diff --git a/impattern/dithers/19.png b/impattern/dithers/19.png deleted file mode 100644 index 5b4ac7a..0000000 Binary files a/impattern/dithers/19.png and /dev/null differ diff --git a/impattern/dithers/2.png b/impattern/dithers/2.png deleted file mode 100644 index a6aa256..0000000 Binary files a/impattern/dithers/2.png and /dev/null differ diff --git a/impattern/dithers/20.png b/impattern/dithers/20.png deleted file mode 100644 index 57a82c1..0000000 Binary files a/impattern/dithers/20.png and /dev/null differ diff --git a/impattern/dithers/21.png b/impattern/dithers/21.png deleted file mode 100644 index 2b671c7..0000000 Binary files a/impattern/dithers/21.png and /dev/null differ diff --git a/impattern/dithers/22.png b/impattern/dithers/22.png deleted file mode 100644 index d28f071..0000000 Binary files a/impattern/dithers/22.png and /dev/null differ diff --git a/impattern/dithers/23.png b/impattern/dithers/23.png deleted file mode 100644 index 052cf6f..0000000 Binary files a/impattern/dithers/23.png and /dev/null differ diff --git a/impattern/dithers/24.png b/impattern/dithers/24.png deleted file mode 100644 index 4bee000..0000000 Binary files a/impattern/dithers/24.png and /dev/null differ diff --git a/impattern/dithers/25.png b/impattern/dithers/25.png deleted file mode 100644 index cbd1097..0000000 Binary files a/impattern/dithers/25.png and /dev/null differ diff --git a/impattern/dithers/26.png b/impattern/dithers/26.png deleted file mode 100644 index 5fc32f4..0000000 Binary files a/impattern/dithers/26.png and /dev/null differ diff --git a/impattern/dithers/27.png b/impattern/dithers/27.png deleted file mode 100644 index 18ce493..0000000 Binary files a/impattern/dithers/27.png and /dev/null differ diff --git a/impattern/dithers/28.png b/impattern/dithers/28.png deleted file mode 100644 index 5924c44..0000000 Binary files a/impattern/dithers/28.png and /dev/null differ diff --git a/impattern/dithers/29.png b/impattern/dithers/29.png deleted file mode 100644 index 16fc2a5..0000000 Binary files a/impattern/dithers/29.png and /dev/null differ diff --git a/impattern/dithers/3.png b/impattern/dithers/3.png deleted file mode 100644 index 8c3d334..0000000 Binary files a/impattern/dithers/3.png and /dev/null differ diff --git a/impattern/dithers/30.png b/impattern/dithers/30.png deleted file mode 100644 index 50c8289..0000000 Binary files a/impattern/dithers/30.png and /dev/null differ diff --git a/impattern/dithers/31.png b/impattern/dithers/31.png deleted file mode 100644 index 3dd944e..0000000 Binary files a/impattern/dithers/31.png and /dev/null differ diff --git a/impattern/dithers/32.png b/impattern/dithers/32.png deleted file mode 100644 index 79aeb84..0000000 Binary files a/impattern/dithers/32.png and /dev/null differ diff --git a/impattern/dithers/33.png b/impattern/dithers/33.png deleted file mode 100644 index 55def42..0000000 Binary files a/impattern/dithers/33.png and /dev/null differ diff --git a/impattern/dithers/34.png b/impattern/dithers/34.png deleted file mode 100644 index f7616ea..0000000 Binary files a/impattern/dithers/34.png and /dev/null differ diff --git a/impattern/dithers/35.png b/impattern/dithers/35.png deleted file mode 100644 index 3ecbaaf..0000000 Binary files a/impattern/dithers/35.png and /dev/null differ diff --git a/impattern/dithers/36.png b/impattern/dithers/36.png deleted file mode 100644 index 0243cf7..0000000 Binary files a/impattern/dithers/36.png and /dev/null differ diff --git a/impattern/dithers/37.png b/impattern/dithers/37.png deleted file mode 100644 index 012ed79..0000000 Binary files a/impattern/dithers/37.png and /dev/null differ diff --git a/impattern/dithers/38.png b/impattern/dithers/38.png deleted file mode 100644 index 9aa54b7..0000000 Binary files a/impattern/dithers/38.png and /dev/null differ diff --git a/impattern/dithers/39.png b/impattern/dithers/39.png deleted file mode 100644 index e98ec53..0000000 Binary files a/impattern/dithers/39.png and /dev/null differ diff --git a/impattern/dithers/4.png b/impattern/dithers/4.png deleted file mode 100644 index 473e6e7..0000000 Binary files a/impattern/dithers/4.png and /dev/null differ diff --git a/impattern/dithers/40.png b/impattern/dithers/40.png deleted file mode 100644 index d7d7cba..0000000 Binary files a/impattern/dithers/40.png and /dev/null differ diff --git a/impattern/dithers/41.png b/impattern/dithers/41.png deleted file mode 100644 index 5d15dd7..0000000 Binary files a/impattern/dithers/41.png and /dev/null differ diff --git a/impattern/dithers/42.png b/impattern/dithers/42.png deleted file mode 100644 index dedbfe2..0000000 Binary files a/impattern/dithers/42.png and /dev/null differ diff --git a/impattern/dithers/43.png b/impattern/dithers/43.png deleted file mode 100644 index dd690d6..0000000 Binary files a/impattern/dithers/43.png and /dev/null differ diff --git a/impattern/dithers/44.png b/impattern/dithers/44.png deleted file mode 100644 index 12afc85..0000000 Binary files a/impattern/dithers/44.png and /dev/null differ diff --git a/impattern/dithers/45.png b/impattern/dithers/45.png deleted file mode 100644 index 972933b..0000000 Binary files a/impattern/dithers/45.png and /dev/null differ diff --git a/impattern/dithers/46.png b/impattern/dithers/46.png deleted file mode 100644 index 857ef06..0000000 Binary files a/impattern/dithers/46.png and /dev/null differ diff --git a/impattern/dithers/47.png b/impattern/dithers/47.png deleted file mode 100644 index 4002e54..0000000 Binary files a/impattern/dithers/47.png and /dev/null differ diff --git a/impattern/dithers/48.png b/impattern/dithers/48.png deleted file mode 100644 index ff785ed..0000000 Binary files a/impattern/dithers/48.png and /dev/null differ diff --git a/impattern/dithers/49.png b/impattern/dithers/49.png deleted file mode 100644 index 1a8525f..0000000 Binary files a/impattern/dithers/49.png and /dev/null differ diff --git a/impattern/dithers/5.png b/impattern/dithers/5.png deleted file mode 100644 index 7bd6619..0000000 Binary files a/impattern/dithers/5.png and /dev/null differ diff --git a/impattern/dithers/50.png b/impattern/dithers/50.png deleted file mode 100644 index f32d908..0000000 Binary files a/impattern/dithers/50.png and /dev/null differ diff --git a/impattern/dithers/51.png b/impattern/dithers/51.png deleted file mode 100644 index 20dc9ca..0000000 Binary files a/impattern/dithers/51.png and /dev/null differ diff --git a/impattern/dithers/52.png b/impattern/dithers/52.png deleted file mode 100644 index 0e097fa..0000000 Binary files a/impattern/dithers/52.png and /dev/null differ diff --git a/impattern/dithers/53.png b/impattern/dithers/53.png deleted file mode 100644 index eec7951..0000000 Binary files a/impattern/dithers/53.png and /dev/null differ diff --git a/impattern/dithers/54.png b/impattern/dithers/54.png deleted file mode 100644 index e23a862..0000000 Binary files a/impattern/dithers/54.png and /dev/null differ diff --git a/impattern/dithers/55.png b/impattern/dithers/55.png deleted file mode 100644 index d908a85..0000000 Binary files a/impattern/dithers/55.png and /dev/null differ diff --git a/impattern/dithers/56.png b/impattern/dithers/56.png deleted file mode 100644 index 330b589..0000000 Binary files a/impattern/dithers/56.png and /dev/null differ diff --git a/impattern/dithers/57.png b/impattern/dithers/57.png deleted file mode 100644 index d9587f3..0000000 Binary files a/impattern/dithers/57.png and /dev/null differ diff --git a/impattern/dithers/58.png b/impattern/dithers/58.png deleted file mode 100644 index 0ccfba0..0000000 Binary files a/impattern/dithers/58.png and /dev/null differ diff --git a/impattern/dithers/59.png b/impattern/dithers/59.png deleted file mode 100644 index 673a3b7..0000000 Binary files a/impattern/dithers/59.png and /dev/null differ diff --git a/impattern/dithers/6.png b/impattern/dithers/6.png deleted file mode 100644 index a3b8272..0000000 Binary files a/impattern/dithers/6.png and /dev/null differ diff --git a/impattern/dithers/60.png b/impattern/dithers/60.png deleted file mode 100644 index e8ea7a6..0000000 Binary files a/impattern/dithers/60.png and /dev/null differ diff --git a/impattern/dithers/61.png b/impattern/dithers/61.png deleted file mode 100644 index 4a8f42c..0000000 Binary files a/impattern/dithers/61.png and /dev/null differ diff --git a/impattern/dithers/62.png b/impattern/dithers/62.png deleted file mode 100644 index 491e211..0000000 Binary files a/impattern/dithers/62.png and /dev/null differ diff --git a/impattern/dithers/63.png b/impattern/dithers/63.png deleted file mode 100644 index 456203e..0000000 Binary files a/impattern/dithers/63.png and /dev/null differ diff --git a/impattern/dithers/64.png b/impattern/dithers/64.png deleted file mode 100644 index 363c9e1..0000000 Binary files a/impattern/dithers/64.png and /dev/null differ diff --git a/impattern/dithers/65.png b/impattern/dithers/65.png deleted file mode 100644 index 40b3ab0..0000000 Binary files a/impattern/dithers/65.png and /dev/null differ diff --git a/impattern/dithers/66.png b/impattern/dithers/66.png deleted file mode 100644 index 4c46c0d..0000000 Binary files a/impattern/dithers/66.png and /dev/null differ diff --git a/impattern/dithers/67.png b/impattern/dithers/67.png deleted file mode 100644 index 62f44fa..0000000 Binary files a/impattern/dithers/67.png and /dev/null differ diff --git a/impattern/dithers/68.png b/impattern/dithers/68.png deleted file mode 100644 index 32bd5bc..0000000 Binary files a/impattern/dithers/68.png and /dev/null differ diff --git a/impattern/dithers/69.png b/impattern/dithers/69.png deleted file mode 100644 index 35b5d56..0000000 Binary files a/impattern/dithers/69.png and /dev/null differ diff --git a/impattern/dithers/7.png b/impattern/dithers/7.png deleted file mode 100644 index 3e01636..0000000 Binary files a/impattern/dithers/7.png and /dev/null differ diff --git a/impattern/dithers/70.png b/impattern/dithers/70.png deleted file mode 100644 index a6771e5..0000000 Binary files a/impattern/dithers/70.png and /dev/null differ diff --git a/impattern/dithers/71.png b/impattern/dithers/71.png deleted file mode 100644 index 649c43f..0000000 Binary files a/impattern/dithers/71.png and /dev/null differ diff --git a/impattern/dithers/72.png b/impattern/dithers/72.png deleted file mode 100644 index d51cde8..0000000 Binary files a/impattern/dithers/72.png and /dev/null differ diff --git a/impattern/dithers/73.png b/impattern/dithers/73.png deleted file mode 100644 index 79d7585..0000000 Binary files a/impattern/dithers/73.png and /dev/null differ diff --git a/impattern/dithers/74.png b/impattern/dithers/74.png deleted file mode 100644 index 9e91e73..0000000 Binary files a/impattern/dithers/74.png and /dev/null differ diff --git a/impattern/dithers/75.png b/impattern/dithers/75.png deleted file mode 100644 index 8702a1e..0000000 Binary files a/impattern/dithers/75.png and /dev/null differ diff --git a/impattern/dithers/76.png b/impattern/dithers/76.png deleted file mode 100644 index 618f42a..0000000 Binary files a/impattern/dithers/76.png and /dev/null differ diff --git a/impattern/dithers/77.png b/impattern/dithers/77.png deleted file mode 100644 index a2b5966..0000000 Binary files a/impattern/dithers/77.png and /dev/null differ diff --git a/impattern/dithers/78.png b/impattern/dithers/78.png deleted file mode 100644 index 82867f8..0000000 Binary files a/impattern/dithers/78.png and /dev/null differ diff --git a/impattern/dithers/79.png b/impattern/dithers/79.png deleted file mode 100644 index 3bbd78d..0000000 Binary files a/impattern/dithers/79.png and /dev/null differ diff --git a/impattern/dithers/8.png b/impattern/dithers/8.png deleted file mode 100644 index 73f4d43..0000000 Binary files a/impattern/dithers/8.png and /dev/null differ diff --git a/impattern/dithers/80.png b/impattern/dithers/80.png deleted file mode 100644 index 810fba6..0000000 Binary files a/impattern/dithers/80.png and /dev/null differ diff --git a/impattern/dithers/81.png b/impattern/dithers/81.png deleted file mode 100644 index 92fa744..0000000 Binary files a/impattern/dithers/81.png and /dev/null differ diff --git a/impattern/dithers/82.png b/impattern/dithers/82.png deleted file mode 100644 index bc30bcf..0000000 Binary files a/impattern/dithers/82.png and /dev/null differ diff --git a/impattern/dithers/83.png b/impattern/dithers/83.png deleted file mode 100644 index caddb63..0000000 Binary files a/impattern/dithers/83.png and /dev/null differ diff --git a/impattern/dithers/84.png b/impattern/dithers/84.png deleted file mode 100644 index 4176f9a..0000000 Binary files a/impattern/dithers/84.png and /dev/null differ diff --git a/impattern/dithers/85.png b/impattern/dithers/85.png deleted file mode 100644 index 8215f71..0000000 Binary files a/impattern/dithers/85.png and /dev/null differ diff --git a/impattern/dithers/86.png b/impattern/dithers/86.png deleted file mode 100644 index c85e4f7..0000000 Binary files a/impattern/dithers/86.png and /dev/null differ diff --git a/impattern/dithers/87.png b/impattern/dithers/87.png deleted file mode 100644 index 2933559..0000000 Binary files a/impattern/dithers/87.png and /dev/null differ diff --git a/impattern/dithers/88.png b/impattern/dithers/88.png deleted file mode 100644 index 7793e19..0000000 Binary files a/impattern/dithers/88.png and /dev/null differ diff --git a/impattern/dithers/89.png b/impattern/dithers/89.png deleted file mode 100644 index 1523885..0000000 Binary files a/impattern/dithers/89.png and /dev/null differ diff --git a/impattern/dithers/9.png b/impattern/dithers/9.png deleted file mode 100644 index 6741ffd..0000000 Binary files a/impattern/dithers/9.png and /dev/null differ diff --git a/impattern/dithers/90.png b/impattern/dithers/90.png deleted file mode 100644 index e001830..0000000 Binary files a/impattern/dithers/90.png and /dev/null differ diff --git a/impattern/dithers/91.png b/impattern/dithers/91.png deleted file mode 100644 index e6e87f0..0000000 Binary files a/impattern/dithers/91.png and /dev/null differ diff --git a/impattern/dithers/92.png b/impattern/dithers/92.png deleted file mode 100644 index 30f150d..0000000 Binary files a/impattern/dithers/92.png and /dev/null differ diff --git a/impattern/dithers/93.png b/impattern/dithers/93.png deleted file mode 100644 index fb1c648..0000000 Binary files a/impattern/dithers/93.png and /dev/null differ diff --git a/impattern/dithers/94.png b/impattern/dithers/94.png deleted file mode 100644 index 171a461..0000000 Binary files a/impattern/dithers/94.png and /dev/null differ diff --git a/impattern/dithers/95.png b/impattern/dithers/95.png deleted file mode 100644 index 27f0f06..0000000 Binary files a/impattern/dithers/95.png and /dev/null differ diff --git a/impattern/dithers/96.png b/impattern/dithers/96.png deleted file mode 100644 index 11114ec..0000000 Binary files a/impattern/dithers/96.png and /dev/null differ diff --git a/impattern/dithers/a0.png b/impattern/dithers/a0.png deleted file mode 100644 index 5fb0b18..0000000 Binary files a/impattern/dithers/a0.png and /dev/null differ diff --git a/impattern/dithers/a1.png b/impattern/dithers/a1.png deleted file mode 100644 index cab1780..0000000 Binary files a/impattern/dithers/a1.png and /dev/null differ diff --git a/impattern/dithers/a10.png b/impattern/dithers/a10.png deleted file mode 100644 index 193ff24..0000000 Binary files a/impattern/dithers/a10.png and /dev/null differ diff --git a/impattern/dithers/a11.png b/impattern/dithers/a11.png deleted file mode 100644 index a211a04..0000000 Binary files a/impattern/dithers/a11.png and /dev/null differ diff --git a/impattern/dithers/a12.png b/impattern/dithers/a12.png deleted file mode 100644 index 0c493b9..0000000 Binary files a/impattern/dithers/a12.png and /dev/null differ diff --git a/impattern/dithers/a13.png b/impattern/dithers/a13.png deleted file mode 100644 index bfa653c..0000000 Binary files a/impattern/dithers/a13.png and /dev/null differ diff --git a/impattern/dithers/a14.png b/impattern/dithers/a14.png deleted file mode 100644 index 10d7b13..0000000 Binary files a/impattern/dithers/a14.png and /dev/null differ diff --git a/impattern/dithers/a15.png b/impattern/dithers/a15.png deleted file mode 100644 index 2381078..0000000 Binary files a/impattern/dithers/a15.png and /dev/null differ diff --git a/impattern/dithers/a16.png b/impattern/dithers/a16.png deleted file mode 100644 index 9636ee5..0000000 Binary files a/impattern/dithers/a16.png and /dev/null differ diff --git a/impattern/dithers/a17.png b/impattern/dithers/a17.png deleted file mode 100644 index 9bc91db..0000000 Binary files a/impattern/dithers/a17.png and /dev/null differ diff --git a/impattern/dithers/a18.png b/impattern/dithers/a18.png deleted file mode 100644 index a2e2928..0000000 Binary files a/impattern/dithers/a18.png and /dev/null differ diff --git a/impattern/dithers/a19.png b/impattern/dithers/a19.png deleted file mode 100644 index 7d45270..0000000 Binary files a/impattern/dithers/a19.png and /dev/null differ diff --git a/impattern/dithers/a2.png b/impattern/dithers/a2.png deleted file mode 100644 index 7d9c7d9..0000000 Binary files a/impattern/dithers/a2.png and /dev/null differ diff --git a/impattern/dithers/a20.png b/impattern/dithers/a20.png deleted file mode 100644 index f670cf5..0000000 Binary files a/impattern/dithers/a20.png and /dev/null differ diff --git a/impattern/dithers/a21.png b/impattern/dithers/a21.png deleted file mode 100644 index 14aac9b..0000000 Binary files a/impattern/dithers/a21.png and /dev/null differ diff --git a/impattern/dithers/a22.png b/impattern/dithers/a22.png deleted file mode 100644 index e9f8a43..0000000 Binary files a/impattern/dithers/a22.png and /dev/null differ diff --git a/impattern/dithers/a23.png b/impattern/dithers/a23.png deleted file mode 100644 index bbdaa29..0000000 Binary files a/impattern/dithers/a23.png and /dev/null differ diff --git a/impattern/dithers/a24.png b/impattern/dithers/a24.png deleted file mode 100644 index 0746ced..0000000 Binary files a/impattern/dithers/a24.png and /dev/null differ diff --git a/impattern/dithers/a25.png b/impattern/dithers/a25.png deleted file mode 100644 index 2f23bdd..0000000 Binary files a/impattern/dithers/a25.png and /dev/null differ diff --git a/impattern/dithers/a26.png b/impattern/dithers/a26.png deleted file mode 100644 index 0e21976..0000000 Binary files a/impattern/dithers/a26.png and /dev/null differ diff --git a/impattern/dithers/a27.png b/impattern/dithers/a27.png deleted file mode 100644 index 0c11122..0000000 Binary files a/impattern/dithers/a27.png and /dev/null differ diff --git a/impattern/dithers/a28.png b/impattern/dithers/a28.png deleted file mode 100644 index 4469d65..0000000 Binary files a/impattern/dithers/a28.png and /dev/null differ diff --git a/impattern/dithers/a29.png b/impattern/dithers/a29.png deleted file mode 100644 index e956157..0000000 Binary files a/impattern/dithers/a29.png and /dev/null differ diff --git a/impattern/dithers/a3.png b/impattern/dithers/a3.png deleted file mode 100644 index 41cd44f..0000000 Binary files a/impattern/dithers/a3.png and /dev/null differ diff --git a/impattern/dithers/a30.png b/impattern/dithers/a30.png deleted file mode 100644 index 9058a6d..0000000 Binary files a/impattern/dithers/a30.png and /dev/null differ diff --git a/impattern/dithers/a31.png b/impattern/dithers/a31.png deleted file mode 100644 index 6692715..0000000 Binary files a/impattern/dithers/a31.png and /dev/null differ diff --git a/impattern/dithers/a32.png b/impattern/dithers/a32.png deleted file mode 100644 index daf16c1..0000000 Binary files a/impattern/dithers/a32.png and /dev/null differ diff --git a/impattern/dithers/a33.png b/impattern/dithers/a33.png deleted file mode 100644 index b3cee15..0000000 Binary files a/impattern/dithers/a33.png and /dev/null differ diff --git a/impattern/dithers/a34.png b/impattern/dithers/a34.png deleted file mode 100644 index 0557dae..0000000 Binary files a/impattern/dithers/a34.png and /dev/null differ diff --git a/impattern/dithers/a35.png b/impattern/dithers/a35.png deleted file mode 100644 index 66d88c9..0000000 Binary files a/impattern/dithers/a35.png and /dev/null differ diff --git a/impattern/dithers/a36.png b/impattern/dithers/a36.png deleted file mode 100644 index 7abb51b..0000000 Binary files a/impattern/dithers/a36.png and /dev/null differ diff --git a/impattern/dithers/a37.png b/impattern/dithers/a37.png deleted file mode 100644 index f30a1ce..0000000 Binary files a/impattern/dithers/a37.png and /dev/null differ diff --git a/impattern/dithers/a38.png b/impattern/dithers/a38.png deleted file mode 100644 index 3e03424..0000000 Binary files a/impattern/dithers/a38.png and /dev/null differ diff --git a/impattern/dithers/a39.png b/impattern/dithers/a39.png deleted file mode 100644 index a802ae0..0000000 Binary files a/impattern/dithers/a39.png and /dev/null differ diff --git a/impattern/dithers/a4.png b/impattern/dithers/a4.png deleted file mode 100644 index 943ecac..0000000 Binary files a/impattern/dithers/a4.png and /dev/null differ diff --git a/impattern/dithers/a40.png b/impattern/dithers/a40.png deleted file mode 100644 index 7e3f149..0000000 Binary files a/impattern/dithers/a40.png and /dev/null differ diff --git a/impattern/dithers/a41.png b/impattern/dithers/a41.png deleted file mode 100644 index 95a7558..0000000 Binary files a/impattern/dithers/a41.png and /dev/null differ diff --git a/impattern/dithers/a5.png b/impattern/dithers/a5.png deleted file mode 100644 index c0d0801..0000000 Binary files a/impattern/dithers/a5.png and /dev/null differ diff --git a/impattern/dithers/a6.png b/impattern/dithers/a6.png deleted file mode 100644 index 414ebb4..0000000 Binary files a/impattern/dithers/a6.png and /dev/null differ diff --git a/impattern/dithers/a7.png b/impattern/dithers/a7.png deleted file mode 100644 index e93e21f..0000000 Binary files a/impattern/dithers/a7.png and /dev/null differ diff --git a/impattern/dithers/a8.png b/impattern/dithers/a8.png deleted file mode 100644 index 8fa69bb..0000000 Binary files a/impattern/dithers/a8.png and /dev/null differ diff --git a/impattern/dithers/a9.png b/impattern/dithers/a9.png deleted file mode 100644 index 98c81ad..0000000 Binary files a/impattern/dithers/a9.png and /dev/null differ diff --git a/impattern/face.png b/impattern/face.png deleted file mode 100644 index 9452555..0000000 Binary files a/impattern/face.png and /dev/null differ diff --git a/impattern/im/cgi-bin/.gallery.cgi.swo b/impattern/im/cgi-bin/.gallery.cgi.swo deleted file mode 100644 index 8e49f67..0000000 Binary files a/impattern/im/cgi-bin/.gallery.cgi.swo and /dev/null differ diff --git a/impattern/im/cgi-bin/.gallery.cgi.swp b/impattern/im/cgi-bin/.gallery.cgi.swp deleted file mode 100644 index f1e8117..0000000 Binary files a/impattern/im/cgi-bin/.gallery.cgi.swp and /dev/null differ diff --git a/impattern/im/cgi-bin/.gallery.cgi.un~ b/impattern/im/cgi-bin/.gallery.cgi.un~ deleted file mode 100755 index 0720bde..0000000 Binary files a/impattern/im/cgi-bin/.gallery.cgi.un~ and /dev/null differ diff --git a/impattern/im/cgi-bin/.imditherconf.cgi.swo b/impattern/im/cgi-bin/.imditherconf.cgi.swo deleted file mode 100644 index 1bbf2e2..0000000 Binary files a/impattern/im/cgi-bin/.imditherconf.cgi.swo and /dev/null differ diff --git a/impattern/im/cgi-bin/.imditherconf.cgi.swp b/impattern/im/cgi-bin/.imditherconf.cgi.swp deleted file mode 100644 index 7de91b5..0000000 Binary files a/impattern/im/cgi-bin/.imditherconf.cgi.swp and /dev/null differ diff --git a/impattern/im/cgi-bin/.imditherconf.cgi.un~ b/impattern/im/cgi-bin/.imditherconf.cgi.un~ deleted file mode 100755 index bef4dd6..0000000 Binary files a/impattern/im/cgi-bin/.imditherconf.cgi.un~ and /dev/null differ diff --git a/impattern/im/cgi-bin/.queryTest.cgi.un~ b/impattern/im/cgi-bin/.queryTest.cgi.un~ deleted file mode 100755 index 31f6494..0000000 Binary files a/impattern/im/cgi-bin/.queryTest.cgi.un~ and /dev/null differ diff --git a/impattern/im/cgi-bin/.test.cgi.un~ b/impattern/im/cgi-bin/.test.cgi.un~ deleted file mode 100755 index 769e9af..0000000 Binary files a/impattern/im/cgi-bin/.test.cgi.un~ and /dev/null differ diff --git a/impattern/im/cgi-bin/1.png b/impattern/im/cgi-bin/1.png deleted file mode 100755 index e98ec53..0000000 Binary files a/impattern/im/cgi-bin/1.png and /dev/null differ diff --git a/impattern/im/cgi-bin/db.py b/impattern/im/cgi-bin/db.py deleted file mode 100755 index 4da7b83..0000000 --- a/impattern/im/cgi-bin/db.py +++ /dev/null @@ -1,29 +0,0 @@ -import MySQLdb -import time - -def now (): - return int(time.mktime(time.localtime())) -class db: - def __init__ (self): - self.conn = None - self.cursor = None - self.connect() - - def connect (self): - self.conn = MySQLdb.connect (host = "127.0.0.1", - user = "asdfus", - passwd = "gTYgT&M6q", - db = "asdfus") - self.cursor = self.conn.cursor () - - def execute (self,sql,args=()): - try: - self.cursor.execute(sql,args) - except MySQLdb.Error, e: - print "Error %d: %s" % (e.args[0], e.args[1]) - # sys.exit (1) - self.connect() - self.cursor.execute(sql,args) - - def lastinsertid (self): - return DB.conn.insert_id() diff --git a/impattern/im/cgi-bin/db.pyc b/impattern/im/cgi-bin/db.pyc deleted file mode 100755 index 6347826..0000000 Binary files a/impattern/im/cgi-bin/db.pyc and /dev/null differ diff --git a/impattern/im/cgi-bin/downloader.py b/impattern/im/cgi-bin/downloader.py deleted file mode 100755 index 7083b11..0000000 --- a/impattern/im/cgi-bin/downloader.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python -import random -import urllib, urllib2 -urlopen = urllib2.urlopen -Request = urllib2.Request -import sys - -MAX_SIZE = 1024 * 1024 * 1.2 - -###___this somewhat poorly written program basically performs a get request -#you can specify three arguments, the url, the destination path and the filename#that you'll want your file to be called -#these can be specified as argument values from the commandline or as -#values in the mainfunction which is downloader.download(url,filename,destination) -#the destination and filename arguments are optional - - - -url = "" -destination = "" -filename = "" - -def argval(): - global destination - global url - global filename - stuff = sys.argv - lenstuff = len(stuff) - if lenstuff >= 4: - getvalues() - if lenstuff ==3: - print str(lenstuff) - url = sys.argv[-2] - filename = sys.argv[-1] - if lenstuff ==2: - url = sys.argv[-1] - destination = "" - partz = url.split('/') - filename = partz[-1] - if lenstuff ==1: - print 'please provide url' - return None - return url - -def getvalues(): - global url - global destination - global filename - url = sys.argv[-3] - destination = sys.argv[-1] - filename = sys.argv[-2] - if len(filename) == 0: - partz = url.split('/') - filename = partz[-1] - if filename == "" or filename == None: - partz = url.split('/') - filename = partz[-1] - return url - -def browser_request (url,data=None): - headers = { - 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)', - 'Accept': '*/*', - } - try: - req = Request(url, data, headers) - response = urlopen(req) - return response - except: - print "ERROR" - sys.stdout.write('there is a problem with the url or an I/O error') - return None - sys.exit() - - -def download(url, filename="",destination=""): - response = browser_request(url) - rawimg = response.read() - if len(rawimg) == 0: - print error("got zero-length file") - sys.stdout.write("file did not exist or was zero-length") - sys.exit() - if len(rawimg) > MAX_SIZE and "asdf.us" not in url: - error = "file too big: max size " + str(MAX_SIZE/1024) + " KB / " + filename + " is " + str(len(rawimg)/1024) + " KB" - print error - sys.stdout.write(error) - sys.exit() - if filename == "": - partz = url.split('/') - filename = partz[-1] - path = "" - if destination == "" or destination == '': - path = filename - else: - path = destination+filename - f = open(path, "w") - f.write(rawimg) - print 'success' - f.close() - finalitems = {} - finalitems['url'] = url - finalitems['filename'] = filename - finalitems['destination'] = destination - return finalitems - -def downloader(): - global url - global destination - global filename - argval() - finalitems = download(url, filename, destination) - return finalitems - -if __name__ == '__main__': - downloader() diff --git a/impattern/im/cgi-bin/downloader.pyc b/impattern/im/cgi-bin/downloader.pyc deleted file mode 100755 index 91ba5b0..0000000 Binary files a/impattern/im/cgi-bin/downloader.pyc and /dev/null differ diff --git a/impattern/im/cgi-bin/gallery.cgi b/impattern/im/cgi-bin/gallery.cgi deleted file mode 100755 index 0f762eb..0000000 --- a/impattern/im/cgi-bin/gallery.cgi +++ /dev/null @@ -1,474 +0,0 @@ -#!/usr/bin/python -import os -import cgi -import re - -import random - -import db -db = db.db() - -BASE_HREF = "http://i.asdf.us/im/" -PARAMLIST = "addr start limit name interface random" -LIMIT = 20 - -def get_params (paramlist): - paramkeys = paramlist.split() - form = {} - try: - qs = os.environ['QUERY_STRING'] - except: - qs = "" - if len(qs): - pairs = qs.replace("&", "&").split("&") - for pair in pairs: - k,v = pair.split("=", 1) - form[k] = v - params = {} - for key in paramkeys: - if key in form: - params[key] = sanitize(form[key]) - else: - params[key] = None - return params -def sanitize (str): - return re.sub(r'\W+', '', str) -def get_files (params): - sql = "SELECT * FROM im_cmd " - args = [] - where = [] - - if params['start'] is not None: - where.append("id < %s") - args.append(params['start']) - if params['name'] is not None: - where.append("name=%s") - args.append(params['name']) - if len(where): - sql += "WHERE " - sql += " AND ".join(where) - sql += " " - - if params['random'] is not None: - sql += "ORDER BY RAND() " - else: - sql += "ORDER BY id DESC " - sql += "LIMIT %s" - - if params['limit'] is not None: - args.append( int(params['limit']) ) - else: - args.append( LIMIT ) - db.execute(sql, args) - rows = db.cursor.fetchall () - return rows - -def is_image (img): - if "jpeg" in img: - return True - if "jpg" in img: - return True - if "gif" in img: - return True - if "png" in img: - return True - return False - - -params = get_params(PARAMLIST) - -titlephrase = random.choice([ - 'Keep on pickin\' on..', - 'Pickolaus Pickleby by Charles Pickens!', - 'You pick potato and I pick potahto...', - 'Take your piq!', - 'Show em what you got', - 'I sure know how to pick \'em', - 'Jus pick somethin already!', - 'You can\'t pick your friends...', - 'Select your image my liege', - 'There\'s a time to choose...', - 'gimme a choice! gimme lil\' choice-a-that...', - 'You choose you lose', - 'novels by James CHOICE...', - 'Choose away, chooser-man...', - ]) - -print "Content-type: text/html" -print "Pragma: no-cache" -print -print """ - - - - -""" -print ""+titlephrase+"" -print""" - - - - -
- key controls -
- ESC toggle
- C clear
- R reverse
- BACKSPACE delete
- LEFT ARROW newer
- RIGHT ARROW older
-
-
-
-
- -
-
copy
-
- -
-
-""" - - -files = get_files(params) -count = 0 -for f in files: - # url = BASE_HREF + f.replace(" ","%20") - url = BASE_HREF + f[5] + "/" + f[7] - print "
" % (url) -print "
" - -lowest_id = files[-1][0] -previous_id = lowest_id + (LIMIT * 2) -back = ["start=%d" % lowest_id, "limit=%d" % LIMIT] -newer = ["start=%d" % previous_id, "limit=%d" % LIMIT] -random_time = [] -if params['name'] is not None: - back.append("name=%s" % params['name']) - newer.append("name=%s" % params['name']) - random_time.append("name=%s" % params['name']) - -newer_QS = "&".join(newer) -back_QS = "&".join(back) -random_time_QS = "&".join(back) -print "
" -print "← newer" % newer_QS; -print "|" -print "editor" -print "|" -print "random" % random_time_QS; -print "|" -print "older →" % back_QS; -print "
" -print """ - - - - - - - -""" - diff --git a/impattern/im/cgi-bin/imdither.cgi b/impattern/im/cgi-bin/imdither.cgi deleted file mode 100755 index 29837ed..0000000 --- a/impattern/im/cgi-bin/imdither.cgi +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/python - -import downloader -import time -import re -import cgi -from subprocess import Popen, PIPE, call -from os import stat, path, makedirs -import sys -import sha -BASE_DIR = "" -OTHER_BASE = "" -#BASE_DIR = "/var/www/asdf.us/httpdocs/im/" -#OTHER_BASE = "http://asdf.us/im/" - -print "Content-Type: text/html" -print "" -print "" - -NAMETAG = "imDither" - -MAIN_DIRECTORY = "" -#BIN_CONVERT = "/usr/bin/convert" -#BIN_IDENTIFY = "/usr/bin/identify" -#BIN_COMPOSITE = "/usr/bin/composite" -BIN_CONVERT = "convert" -BIN_IDENTIFY = "identify" -BIN_COMPOSITE = "composite" - -NOW = str(int(time.time())) -MAX_LENGTH_BEFORE_ABRIDGED = 30 -ABBREVIATION = "xx_abridged__" - -################################### -### DEFAULT VALUES HERE -params = { - "url" : "http://asdf.us/im/10/friedeggstresstoy_1321140711_1322282769_pepper.gif", - "ditherType" : "1.png", - "username" : "pepper", - "formatType" : "png", -} -################################### - -form = cgi.FieldStorage() -if form: - for key, value in form.iteritems(): - formValues[key] = params[key] #CHECKME - -class Utils: - def file_size (self, imgFile): - return stat(imgFile)[6] - - def imageDimensions(self, thefile): - ident = Popen([BIN_IDENTIFY, thefile], stdout=PIPE).communicate()[0] - parts = ident.split(" ") - return parts[2].split("x") - - def hash_dir(self, s): - """get a random number-letter pair""" - return sha.new(s).hexdigest()[:2] - - def hexdir(self): - """create a random path name""" - directoryName = utils.hash_dir(NOW) - - dirs = [ - BASE_DIR+directoryName+'/', - OTHER_BASE+directoryName+'/', - ] - ################################### - if not path.exists(directoryName): - makedirs(directoryName) - - - return dirs - - def makeOriginalName(self, params, canvas_type="", nametag_part=""): - if not nametag_part: - nametag_part = NAMETAG - if not canvas_type: - canvastype = params["formatType"] - filename = '%s_%s_%s.%s' % (nametag_part, NOW, params["username"], canvas_type) - return filename - - def sanitize (self,string): - """strip out all non-word characters""" - return re.sub(r'\W+', '', string) - - def makeNameFromFilename(self, params): - parts = params["url"].split("?")[0].split('/') - filename = parts[-1] - name, extension = filename.split('.') - if len(extension) >= 5: - sys.stdout.write('the file is not an image or any standard format') - sys.exit() - if len(name)> MAX_LENGTH_BEFORE_ABRIDGED: - name = ABBREVIATION - sanitized = self.sanitize(name) - thefilename = "%s_%s_%s_%s.%s" % (NAMETAG, NOW, sanitized, params["username"], extension) - return thefilename - -utils = Utils(); - - -class Dither: - def makeCanvas(self, thefile): - call([BIN_CONVERT,"-size", self.dimensions[0]+"x"+self.dimensions[1],"canvas:transparent", thefile]) - def makeMask(self, themask, thedither, thebackground): - """ composite -tile DITHERNAME BACKGROUNDNAME MASKNAME """ - call([BIN_COMPOSITE,"-tile",thedither,thebackground,themask]) - #convert thebg.gif -compose Dst_In null: thefile.gif -matte -layers composite new.gif - call([BIN_CONVERT,themask,"-compose","Dst_In","null:",self.privatepath+self.mainfile,"-matte","-layers","composite",themask]) - # os.system("rm "+thebackground) - def fuseMask(self, themask, theimage): - call([BIN_COMPOSITE,theimage,"-compose","Pin_Light",themask,theimage]) - # os.system("rm "+themask) - def __init__(self, url, thedither, username): - self.mainfile = utils.makeNameFromFilename(params) - filepaths = utils.hexdir() - self.privatepath = filepaths[0] - self.publicpath = filepaths[1] - downloader.download(url, self.mainfile, self.privatepath) - - parts = self.mainfile.split('.') - print parts - if "gif" in parts[-1]: - canvastype = "gif" - else: - canvastype = "png" - - self.canvasfile = "/tmp/"+utils.makeOriginalName(params, canvastype, "canvasfile") - self.dimensions = utils.imageDimensions(self.privatepath+self.mainfile) - self.makeCanvas(self.canvasfile) - self.theMask = "/tmp/"+utils.makeOriginalName(params, canvastype, "maskfile") - #good up to here - self.makeMask(self.theMask, thedither, self.canvasfile) - self.fuseMask(self.theMask, self.privatepath+self.mainfile) - #self.mainfile = self.mainfile.replace(, do something with s3) - print self.privatepath+self.mainfile - print utils.file_size(self.privatepath+self.mainfile) - print 'width: '+self.dimensions[0]+'px' - print 'height: '+self.dimensions[1]+'px' - -if __name__ == "__main__": - newImage = Dither(params["url"], params["ditherType"], params["username"]) diff --git a/impattern/im/cgi-bin/imditherconf.cgi b/impattern/im/cgi-bin/imditherconf.cgi deleted file mode 100755 index 8e7b7f2..0000000 --- a/impattern/im/cgi-bin/imditherconf.cgi +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -from subprocess import * -import re -print "Content-type: text/html\n\n" -DITHERDIR = "/home/pepper/im/imdither/dithers"; - - -def get_ditherfiles(): - filesString = Popen(["ls", DITHERDIR], stdout=PIPE).communicate()[0] - files = filesString.split("\n"); - #javascript = "var ditherfiles = [" - javascript = "[" - for i in files: - if not i: - continue - javascript += "\"%s\", " % (i); - - javascript = javascript[:-2] - javascript += "]"; - return javascript -print get_ditherfiles(); - diff --git a/impattern/im/cgi-bin/imweb/databaser.py b/impattern/im/cgi-bin/imweb/databaser.py deleted file mode 100755 index 07b8723..0000000 --- a/impattern/im/cgi-bin/imweb/databaser.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/python - -from db import * -import os -DB = db () - -PATH_TO_S3CMD = "/var/www/asdf.us/cgi-bin/im/s3/s3cmd-1.1.0-beta3/s3cmd" -PATH_TO_IMAGES = "/var/www/asdf.us/docs/im" - -global idnumber - -function dbGet(idnumber): - try: - DB.cursor.execute('SELECT dir, newfile, id FROM im_cmd WHERE id='+id) - #sql = "INSERT INTO im_cmd (date,remote_addr,name,dir,newfile,cmd) VALUES(%s,%s,%s,%s,%s,%s)" - #args = (now(), remoteaddress, name, dir, newfile, cmd) - data = cursor.fetchall - print str(data) - except (): - return - -if __name__ == '__main__': - sys.argv[1] = idnumber - dbGet(idnumber) \ No newline at end of file diff --git a/impattern/im/cgi-bin/imweb/downImg b/impattern/im/cgi-bin/imweb/downImg deleted file mode 100755 index d029477..0000000 --- a/impattern/im/cgi-bin/imweb/downImg +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/python - -import cgi -import downloader -import fieldstorager -import sys -import os -#import gifchooser - -#BASE_DIR = "/var/www/asdf.us/httpdocs/imweb/" -BASE_DIR = "" -PARAM_LIST = "texture heightmap" -#BIN_CONVERT = "/usr/bin/convert" -BIN_CONVERT = "convert" - -print "Content-type: text/plain" -print "" - -form = cgi.FieldStorage() -newvalues = fieldstorager.fieldstorage(PARAM_LIST, form) -thekeys = PARAM_LIST.split() -for key in thekeys: - globals()[key] = newvalues[key] -######################################################## -#test with values here -texture = "http://i.asdf.us/im/f3/imBreak_1327641897_clifford_1327640637_pepper.png" -heightmap = "http://dump.fm/images/20120127/1327645413657-dumpfm-pepper-topcomments.png" -####################################################### -def uploadit(pictureUrl): - if len(pictureUrl) > 3 and "." in pictureUrl: - imageparts = pictureUrl.split('.') - filetype = imageparts[-1] - if "?" in filetype: - theparts = filetype.split('?') - filetype = theparts[0] - if pictureUrl == texture: - imagename = "Texture" - if pictureUrl == heightmap: - imagename = "Heightmap" - if "png" in pictureUrl.lower() or "jpg" in pictureUrl.lower() or "jpeg" in pictureUrl.lower() or "gif" in pictureUrl.lower(): - downloader.download(pictureUrl, (imagename+"."+filetype), BASE_DIR) - firstfile = BASE_DIR+imagename+"."+filetype - endfile = BASE_DIR+imagename+"."+"jpg" - os.system(BIN_CONVERT+" -resize 256x256\! "+firstfile+" "+endfile) - if pictureUrl == "Heightmap": - os.system(BIN_CONVERT+" colorspace -gray "+" "+endfile+" "+endfile) - os.system("rm heightmap.png heightmap.gif texture.png texture.gif") - print "upload"+imagename+" successful" - return endfile - else: - print "This is not an acceptable image format for "+imagename - return -uploadit(texture) -uploadit(heightmap) -sys.exit() diff --git a/impattern/im/cgi-bin/imweb/downloader.py b/impattern/im/cgi-bin/imweb/downloader.py deleted file mode 100755 index 7083b11..0000000 --- a/impattern/im/cgi-bin/imweb/downloader.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python -import random -import urllib, urllib2 -urlopen = urllib2.urlopen -Request = urllib2.Request -import sys - -MAX_SIZE = 1024 * 1024 * 1.2 - -###___this somewhat poorly written program basically performs a get request -#you can specify three arguments, the url, the destination path and the filename#that you'll want your file to be called -#these can be specified as argument values from the commandline or as -#values in the mainfunction which is downloader.download(url,filename,destination) -#the destination and filename arguments are optional - - - -url = "" -destination = "" -filename = "" - -def argval(): - global destination - global url - global filename - stuff = sys.argv - lenstuff = len(stuff) - if lenstuff >= 4: - getvalues() - if lenstuff ==3: - print str(lenstuff) - url = sys.argv[-2] - filename = sys.argv[-1] - if lenstuff ==2: - url = sys.argv[-1] - destination = "" - partz = url.split('/') - filename = partz[-1] - if lenstuff ==1: - print 'please provide url' - return None - return url - -def getvalues(): - global url - global destination - global filename - url = sys.argv[-3] - destination = sys.argv[-1] - filename = sys.argv[-2] - if len(filename) == 0: - partz = url.split('/') - filename = partz[-1] - if filename == "" or filename == None: - partz = url.split('/') - filename = partz[-1] - return url - -def browser_request (url,data=None): - headers = { - 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)', - 'Accept': '*/*', - } - try: - req = Request(url, data, headers) - response = urlopen(req) - return response - except: - print "ERROR" - sys.stdout.write('there is a problem with the url or an I/O error') - return None - sys.exit() - - -def download(url, filename="",destination=""): - response = browser_request(url) - rawimg = response.read() - if len(rawimg) == 0: - print error("got zero-length file") - sys.stdout.write("file did not exist or was zero-length") - sys.exit() - if len(rawimg) > MAX_SIZE and "asdf.us" not in url: - error = "file too big: max size " + str(MAX_SIZE/1024) + " KB / " + filename + " is " + str(len(rawimg)/1024) + " KB" - print error - sys.stdout.write(error) - sys.exit() - if filename == "": - partz = url.split('/') - filename = partz[-1] - path = "" - if destination == "" or destination == '': - path = filename - else: - path = destination+filename - f = open(path, "w") - f.write(rawimg) - print 'success' - f.close() - finalitems = {} - finalitems['url'] = url - finalitems['filename'] = filename - finalitems['destination'] = destination - return finalitems - -def downloader(): - global url - global destination - global filename - argval() - finalitems = download(url, filename, destination) - return finalitems - -if __name__ == '__main__': - downloader() diff --git a/impattern/im/cgi-bin/imweb/fieldstorager.py b/impattern/im/cgi-bin/imweb/fieldstorager.py deleted file mode 100755 index 55b00ad..0000000 --- a/impattern/im/cgi-bin/imweb/fieldstorager.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/python - -def fieldstorage(paramlist, form): - theparams = paramlist.split() - newvalues = {} - for item in theparams: - if item in form: - newvalues[item] = form[item].value - else: - newvalues[item] = None - for item in theparams: - if newvalues[item] is not None: - if 'true' in newvalues[item] or 'True' in newvalues[item]: - if len(newvalues[item]) <= 6: - newvalues[item] = True - elif 'false' in newvalues[item] or 'False' in newvalues[item]: - if len(newvalues[item]) <= 7: - newvalues[item] = False - return newvalues - - diff --git a/impattern/im/cgi-bin/imweb/filenamer.py b/impattern/im/cgi-bin/imweb/filenamer.py deleted file mode 100755 index 9b6ef32..0000000 --- a/impattern/im/cgi-bin/imweb/filenamer.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/python - -import sys -import os -import re -import time -import string -import urllib -import sha - -#this program takes a url and makes a new filename for it. it also -#splits out the extension. this is important for manipulating images -#it returns a hash, part[0] is the filename and part[1] is the extension -#called like this filenamer.filename(url, nametag, username) - - -thefilename = "" -extension = "" - -nametag = "" -username = "" -url = "" - -def getargs(): - global username - global nametag - global url - args = sys.argv - if len(args) < 2: - return None - if len(args) >= 4: - username = args[-1] - nametag = args[-2] - url = args[-3] - if len(args) == 3: - nametag = args[-1] - url = args[-2] - if len(args) == 2: - url = args[-1] - thereturn = {} - thereturn['url'] = url - thereturn['nametag'] = nametag - thereturn['username'] = username - return thereturn - -def sanitize (str):#this removes pretty much everything but letters(and underscores) from a url - return re.sub(r'\W+', '', str) -#def sanitize (str): -# tmp_str = urllib.unquote(str) -# return re.sub(r'\W+', '', tmp_str) - - - - -def now (): - return int(time.mktime(time.localtime())) - -def spliturl(url): - if len(url) < 3 or '/' not in url: - sys.stdout.write('you have not provided a url') - print 'you have not provided a url' - sys.exit() - parts = url.split('/') - namepart = parts[-1] - importantparts = namepart.split('.') - extension = importantparts[-1] - if "?" in extension: - querysplit = extension.split('?') - extension = querysplit[0] - if len(extension) >= 5: - sys.stdout.write('the file is not an image or any standard format') - print 'the file is not an image or any standard format' - sys.exit - namepart = importantparts[-2] - if len(namepart)>50: - namepart = "xx_abridged__" - sanitized = sanitize(namepart) - salvaged = {} - salvaged['extension'] = extension - salvaged['namepart'] = sanitized - return salvaged - -def prepare(url): - global nametag - global username - time = now() - datestamp = str(time) - parts = spliturl(url) - extension = parts['extension'] - original = parts['namepart'] - thefilename = nametag+'_'+datestamp+"_"+original+"_"+username+'.'+extension - return thefilename - -def filename(url, tag="", usrname=""): - global nametag - global username - if tag == None: - tag = "" - if usrname == None: - usrname = "" - nametag = tag - username = usrname - thefilename = prepare(url) - print thefilename - return thefilename - sys.exit() - - -if __name__ == '__main__': - vars = getargs() - url=vars['url'] - nametag=vars['nametag'] - username = vars['username'] - filename(url,nametag,username) - diff --git a/impattern/im/cgi-bin/imweb/hexdirer.py b/impattern/im/cgi-bin/imweb/hexdirer.py deleted file mode 100755 index af6f60e..0000000 --- a/impattern/im/cgi-bin/imweb/hexdirer.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/python - -import sys -import os -import time -import string -import sha -BASE_DIR = "/var/www/asdf.us/httpdocs/im/" -OTHER_BASE = "http://asdf.us/im/" - -#THIS MODULE RETURNS A COMPLETE PATH! ALL YOU HAVE TO DO IS -#SPECIFY THE BASE DIRECTORY -#the hash works like this: -#0 is private path -#1 is the public path - - - -def hash_dir (s): - return sha.new(s).hexdigest()[:2] - -def now (): - return int(time.mktime(time.localtime())) - -def hexdir(): - time = now() - time = str(time) - ending = hash_dir(time) - dirs = [] - dirs.append(BASE_DIR+ending+'/') - dirs.append(OTHER_BASE+ending+'/') - print str(dirs) - return dirs - sys.exit() - -if __name__ == "__main__": - hexdir() - diff --git a/impattern/im/cgi-bin/imweb/saveImg b/impattern/im/cgi-bin/imweb/saveImg deleted file mode 100755 index e76c36a..0000000 --- a/impattern/im/cgi-bin/imweb/saveImg +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/python - -import hexdirer -import filenamer -import cgi -import fieldstorager -import base64 -import random -import sys -import databaser - -print "Content-type: text/plain" -print "" - -nametag = "imWeb" -#name is the image name - -PARAM_LIST = "name username picture" -form = cgi.FieldStorage() -newvalues = fieldstorager.fieldstorage(PARAM_LIST, form) -thekeys = PARAM_LIST.split() -for key in thekeys: - globals()[key] = newvalues[key] -if username == None: - username = "" -if name == None: - name = str(random.randint(0,100)) -############################################################ -#test here -########################################################### -try: - #the picture is first encoded so we have to decode it - picture = base64.b64decode(picture) - namepart = filenamer.filename(("/"+name+".png"),username,nametag) - filepaths = hexdirer.hexdir() - filepaths = ["",""] - - thefile = filepaths[0]+namepart - f = open(thefile,'w') - for line in picture: - f.write(line) - f.close() - -# databaser.database(filepaths[0],namepart,"imweb capture",username) - print "capture successful" - print filepaths[1]+namepart -except: - print "sorry, there is a problem with image capture" diff --git a/impattern/im/cgi-bin/pythonhelloworld.cgi b/impattern/im/cgi-bin/pythonhelloworld.cgi deleted file mode 100755 index cede50f..0000000 --- a/impattern/im/cgi-bin/pythonhelloworld.cgi +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python - -print "Content-type: text/html\n\n" -print "Hello world!" - diff --git a/impattern/im/cgi-bin/queryTest.cgi b/impattern/im/cgi-bin/queryTest.cgi deleted file mode 100755 index 5e6d573..0000000 --- a/impattern/im/cgi-bin/queryTest.cgi +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/perl - -use CGI; -my $cgi = new CGI; -print $cgi->header('text/html'); -print "what's up doc"; -#print( $cgi->param( "test" )); - - diff --git a/impattern/im/cgi-bin/test.cgi b/impattern/im/cgi-bin/test.cgi deleted file mode 100755 index 1159090..0000000 --- a/impattern/im/cgi-bin/test.cgi +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/perl - -print "Content-Type: text/html\r\n\r\nHello, world!\n"; -open MYFILE, ">/home8/basedxco/public_html/TESTFILE" or die $!; - - - -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -print MYFILE "this is a test!!!!"; -close(MYFILE); diff --git a/impattern/im/cgi-bin/youtube-dl b/impattern/im/cgi-bin/youtube-dl deleted file mode 160000 index fc79158..0000000 --- a/impattern/im/cgi-bin/youtube-dl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fc79158de2779a9f2d3fb16ddfb2878b82693b79 diff --git a/impattern/im/imdither/.fieldstorager.py.un~ b/impattern/im/imdither/.fieldstorager.py.un~ deleted file mode 100755 index 20a9590..0000000 Binary files a/impattern/im/imdither/.fieldstorager.py.un~ and /dev/null differ diff --git a/impattern/im/imdither/.filenamer.py.un~ b/impattern/im/imdither/.filenamer.py.un~ deleted file mode 100755 index 5b57a03..0000000 Binary files a/impattern/im/imdither/.filenamer.py.un~ and /dev/null differ diff --git a/impattern/im/imdither/.gallery_style.css.swp b/impattern/im/imdither/.gallery_style.css.swp deleted file mode 100644 index 12ae29c..0000000 Binary files a/impattern/im/imdither/.gallery_style.css.swp and /dev/null differ diff --git a/impattern/im/imdither/.hexdirer.py.un~ b/impattern/im/imdither/.hexdirer.py.un~ deleted file mode 100755 index 5b8b4c6..0000000 Binary files a/impattern/im/imdither/.hexdirer.py.un~ and /dev/null differ diff --git a/impattern/im/imdither/.imdither.py.swp b/impattern/im/imdither/.imdither.py.swp deleted file mode 100755 index e8370e1..0000000 Binary files a/impattern/im/imdither/.imdither.py.swp and /dev/null differ diff --git a/impattern/im/imdither/.imdither.py.un~ b/impattern/im/imdither/.imdither.py.un~ deleted file mode 100755 index f6e0105..0000000 Binary files a/impattern/im/imdither/.imdither.py.un~ and /dev/null differ diff --git a/impattern/im/imdither/.imdither_tests.py.un~ b/impattern/im/imdither/.imdither_tests.py.un~ deleted file mode 100755 index d80d7a3..0000000 Binary files a/impattern/im/imdither/.imdither_tests.py.un~ and /dev/null differ diff --git a/impattern/im/imdither/.index.html.un~ b/impattern/im/imdither/.index.html.un~ deleted file mode 100755 index 429eaf9..0000000 Binary files a/impattern/im/imdither/.index.html.un~ and /dev/null differ diff --git a/impattern/im/imdither/.inventnamer.py.un~ b/impattern/im/imdither/.inventnamer.py.un~ deleted file mode 100755 index afebb28..0000000 Binary files a/impattern/im/imdither/.inventnamer.py.un~ and /dev/null differ diff --git a/impattern/im/imdither/.isotope_config.js.swp b/impattern/im/imdither/.isotope_config.js.swp deleted file mode 100644 index a6f05b4..0000000 Binary files a/impattern/im/imdither/.isotope_config.js.swp and /dev/null differ diff --git a/impattern/im/imdither/.isotope_config.js.un~ b/impattern/im/imdither/.isotope_config.js.un~ deleted file mode 100644 index 5316182..0000000 Binary files a/impattern/im/imdither/.isotope_config.js.un~ and /dev/null differ diff --git a/impattern/im/imdither/.sorted.un~ b/impattern/im/imdither/.sorted.un~ deleted file mode 100755 index db64a0f..0000000 Binary files a/impattern/im/imdither/.sorted.un~ and /dev/null differ diff --git a/impattern/im/imdither/.style.css.un~ b/impattern/im/imdither/.style.css.un~ deleted file mode 100755 index 98f8e28..0000000 Binary files a/impattern/im/imdither/.style.css.un~ and /dev/null differ diff --git a/impattern/im/imdither/.test.html.un~ b/impattern/im/imdither/.test.html.un~ deleted file mode 100755 index 826e199..0000000 Binary files a/impattern/im/imdither/.test.html.un~ and /dev/null differ diff --git a/impattern/im/imdither/bottle-loader.py b/impattern/im/imdither/bottle-loader.py deleted file mode 100755 index 05ceb93..0000000 --- a/impattern/im/imdither/bottle-loader.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/opt/python2.6/bin/python - -from bottle import get, post, run -from bottle import request -from bottle import default_app - -from bottle import static_file, route -import imdither - -@post('/imdither') -def hit_script(): - param_list = "pattern style url username fileformat".split() - defaults = { - pattern:"2.png", style: "punched", url:"http://www.google.com/images/srpr/logo3w.png", username:"test", fileformat:'png' - } - for element in param_list: - if element in request.forms: - vars()[element] = request.forms(element) - else: - vars()[element] = defaults[element] - ditheraction = imdither.Dither() - g = dither.makedither(pattern, url, username, fileformat) - #print request.forms.items() - return g - -@route('/') -def server_static(filename): - return static_file(filename, root='') - -@route('/dithers/') -def server_static(filename): - return static_file(filename, root='dithers/') - -@route('/notes/') -def server_static(filename): - return static_file(filename, root='notes/') - - -@get('/') -def form(): - f = open('index.html','r') - lines = f.read() - f.close() - return lines - - -run(host='asdf.us', port=8000, debug=True) diff --git a/impattern/im/imdither/bottle.py b/impattern/im/imdither/bottle.py deleted file mode 100755 index 5a9a78d..0000000 --- a/impattern/im/imdither/bottle.py +++ /dev/null @@ -1,3081 +0,0 @@ -#!/opt/python2.6/bin/python -# -*- coding: utf-8 -*- -""" -Bottle is a fast and simple micro-framework for small web applications. It -offers request dispatching (Routes) with url parameter support, templates, -a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and -template engines - all in a single file and with no dependencies other than the -Python Standard Library. - -Homepage and documentation: http://bottlepy.org/ - -Copyright (c) 2011, Marcel Hellkamp. -License: MIT (see LICENSE for details) -""" - -from __future__ import with_statement - -__author__ = 'Marcel Hellkamp' -__version__ = '0.11.dev' -__license__ = 'MIT' - -# The gevent server adapter needs to patch some modules before they are imported -# This is why we parse the commandline parameters here but handle them later -if __name__ == '__main__': - from optparse import OptionParser - _cmd_parser = OptionParser(usage="usage: %prog [options] package.module:app") - _opt = _cmd_parser.add_option - _opt("--version", action="store_true", help="show version number.") - _opt("-b", "--bind", metavar="ADDRESS", help="bind socket to ADDRESS.") - _opt("-s", "--server", default='wsgiref', help="use SERVER as backend.") - _opt("-p", "--plugin", action="append", help="install additional plugin/s.") - _opt("--debug", action="store_true", help="start server in debug mode.") - _opt("--reload", action="store_true", help="auto-reload on file changes.") - _cmd_options, _cmd_args = _cmd_parser.parse_args() - if _cmd_options.server and _cmd_options.server.startswith('gevent'): - import gevent.monkey; gevent.monkey.patch_all() - -import base64, cgi, email.utils, functools, hmac, imp, itertools, mimetypes,\ - os, re, subprocess, sys, tempfile, threading, time, urllib, warnings - -from datetime import date as datedate, datetime, timedelta -from tempfile import TemporaryFile -from traceback import format_exc, print_exc - -try: from json import dumps as json_dumps, loads as json_lds -except ImportError: # pragma: no cover - try: from simplejson import dumps as json_dumps, loads as json_lds - except ImportError: - try: from django.utils.simplejson import dumps as json_dumps, loads as json_lds - except ImportError: - def json_dumps(data): - raise ImportError("JSON support requires Python 2.6 or simplejson.") - json_lds = json_dumps - - - -# We now try to fix 2.5/2.6/3.1/3.2 incompatibilities. -# It ain't pretty but it works... Sorry for the mess. - -py = sys.version_info -py3k = py >= (3,0,0) -py25 = py < (2,6,0) - -# Workaround for the missing "as" keyword in py3k. -def _e(): return sys.exc_info()[1] - -# Workaround for the "print is a keyword/function" dilemma. -_stdout, _stderr = sys.stdout.write, sys.stderr.write - -# Lots of stdlib and builtin differences. -if py3k: - import http.client as httplib - import _thread as thread - from urllib.parse import urljoin, parse_qsl, SplitResult as UrlSplitResult - from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote - from http.cookies import SimpleCookie - from collections import MutableMapping as DictMixin - import pickle - from io import BytesIO - basestring = str - unicode = str - json_loads = lambda s: json_lds(touni(s)) - callable = lambda x: hasattr(x, '__call__') - imap = map -else: # 2.x - import httplib - import thread - from urlparse import urljoin, SplitResult as UrlSplitResult - from urllib import urlencode, quote as urlquote, unquote as urlunquote - from Cookie import SimpleCookie - from itertools import imap - import cPickle as pickle - from StringIO import StringIO as BytesIO - if py25: - msg = "Python 2.5 support may be dropped in future versions of Bottle." - warnings.warn(msg, DeprecationWarning) - from cgi import parse_qsl - from UserDict import DictMixin - def next(it): return it.next() - bytes = str - else: # 2.6, 2.7 - from urlparse import parse_qsl - from collections import MutableMapping as DictMixin - json_loads = json_lds - -# Some helpers for string/byte handling -def tob(s, enc='utf8'): - return s.encode(enc) if isinstance(s, unicode) else bytes(s) -def touni(s, enc='utf8', err='strict'): - return s.decode(enc, err) if isinstance(s, bytes) else unicode(s) -tonat = touni if py3k else tob - -# 3.2 fixes cgi.FieldStorage to accept bytes (which makes a lot of sense). -# 3.1 needs a workaround. -NCTextIOWrapper = None -if (3,0,0) < py < (3,2,0): - from io import TextIOWrapper - class NCTextIOWrapper(TextIOWrapper): - def close(self): pass # Keep wrapped buffer open. - -# A bug in functools causes it to break if the wrapper is an instance method -def update_wrapper(wrapper, wrapped, *a, **ka): - try: functools.update_wrapper(wrapper, wrapped, *a, **ka) - except AttributeError: pass - - - -# These helpers are used at module level and need to be defined first. -# And yes, I know PEP-8, but sometimes a lower-case classname makes more sense. - -def depr(message): - warnings.warn(message, DeprecationWarning, stacklevel=3) - -def makelist(data): # This is just to handy - if isinstance(data, (tuple, list, set, dict)): return list(data) - elif data: return [data] - else: return [] - - -class DictProperty(object): - ''' Property that maps to a key in a local dict-like attribute. ''' - def __init__(self, attr, key=None, read_only=False): - self.attr, self.key, self.read_only = attr, key, read_only - - def __call__(self, func): - functools.update_wrapper(self, func, updated=[]) - self.getter, self.key = func, self.key or func.__name__ - return self - - def __get__(self, obj, cls): - if obj is None: return self - key, storage = self.key, getattr(obj, self.attr) - if key not in storage: storage[key] = self.getter(obj) - return storage[key] - - def __set__(self, obj, value): - if self.read_only: raise AttributeError("Read-Only property.") - getattr(obj, self.attr)[self.key] = value - - def __delete__(self, obj): - if self.read_only: raise AttributeError("Read-Only property.") - del getattr(obj, self.attr)[self.key] - - -class cached_property(object): - ''' A property that is only computed once per instance and then replaces - itself with an ordinary attribute. Deleting the attribute resets the - property. ''' - - def __init__(self, func): - self.func = func - - def __get__(self, obj, cls): - if obj is None: return self - value = obj.__dict__[self.func.__name__] = self.func(obj) - return value - - -class lazy_attribute(object): - ''' A property that caches itself to the class object. ''' - def __init__(self, func): - functools.update_wrapper(self, func, updated=[]) - self.getter = func - - def __get__(self, obj, cls): - value = self.getter(cls) - setattr(cls, self.__name__, value) - return value - - - - - - -############################################################################### -# Exceptions and Events ######################################################## -############################################################################### - - -class BottleException(Exception): - """ A base class for exceptions used by bottle. """ - pass - - -#TODO: This should subclass BaseRequest -class HTTPResponse(BottleException): - """ Used to break execution and immediately finish the response """ - def __init__(self, output='', status=200, header=None): - super(BottleException, self).__init__("HTTP Response %d" % status) - self.status = int(status) - self.output = output - self.headers = HeaderDict(header) if header else None - - def apply(self, response): - if self.headers: - for key, value in self.headers.allitems(): - response.headers[key] = value - response.status = self.status - - -class HTTPError(HTTPResponse): - """ Used to generate an error page """ - def __init__(self, code=500, output='Unknown Error', exception=None, - traceback=None, header=None): - super(HTTPError, self).__init__(output, code, header) - self.exception = exception - self.traceback = traceback - - def __repr__(self): - return tonat(template(ERROR_PAGE_TEMPLATE, e=self)) - - - - - - -############################################################################### -# Routing ###################################################################### -############################################################################### - - -class RouteError(BottleException): - """ This is a base class for all routing related exceptions """ - - -class RouteReset(BottleException): - """ If raised by a plugin or request handler, the route is reset and all - plugins are re-applied. """ - -class RouterUnknownModeError(RouteError): pass - - -class RouteSyntaxError(RouteError): - """ The route parser found something not supported by this router """ - - -class RouteBuildError(RouteError): - """ The route could not been built """ - - -class Router(object): - ''' A Router is an ordered collection of route->target pairs. It is used to - efficiently match WSGI requests against a number of routes and return - the first target that satisfies the request. The target may be anything, - usually a string, ID or callable object. A route consists of a path-rule - and a HTTP method. - - The path-rule is either a static path (e.g. `/contact`) or a dynamic - path that contains wildcards (e.g. `/wiki/`). The wildcard syntax - and details on the matching order are described in docs:`routing`. - ''' - - default_pattern = '[^/]+' - default_filter = 're' - #: Sorry for the mess. It works. Trust me. - rule_syntax = re.compile('(\\\\*)'\ - '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)'\ - '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)'\ - '(?::((?:\\\\.|[^\\\\>]+)+)?)?)?>))') - - def __init__(self, strict=False): - self.rules = {} # A {rule: Rule} mapping - self.builder = {} # A rule/name->build_info mapping - self.static = {} # Cache for static routes: {path: {method: target}} - self.dynamic = [] # Cache for dynamic routes. See _compile() - #: If true, static routes are no longer checked first. - self.strict_order = strict - self.filters = {'re': self.re_filter, 'int': self.int_filter, - 'float': self.float_filter, 'path': self.path_filter} - - def re_filter(self, conf): - return conf or self.default_pattern, None, None - - def int_filter(self, conf): - return r'-?\d+', int, lambda x: str(int(x)) - - def float_filter(self, conf): - return r'-?[\d.]+', float, lambda x: str(float(x)) - - def path_filter(self, conf): - return r'.+?', None, None - - def add_filter(self, name, func): - ''' Add a filter. The provided function is called with the configuration - string as parameter and must return a (regexp, to_python, to_url) tuple. - The first element is a string, the last two are callables or None. ''' - self.filters[name] = func - - def parse_rule(self, rule): - ''' Parses a rule into a (name, filter, conf) token stream. If mode is - None, name contains a static rule part. ''' - offset, prefix = 0, '' - for match in self.rule_syntax.finditer(rule): - prefix += rule[offset:match.start()] - g = match.groups() - if len(g[0])%2: # Escaped wildcard - prefix += match.group(0)[len(g[0]):] - offset = match.end() - continue - if prefix: yield prefix, None, None - name, filtr, conf = g[1:4] if not g[2] is None else g[4:7] - if not filtr: filtr = self.default_filter - yield name, filtr, conf or None - offset, prefix = match.end(), '' - if offset <= len(rule) or prefix: - yield prefix+rule[offset:], None, None - - def add(self, rule, method, target, name=None): - ''' Add a new route or replace the target for an existing route. ''' - if rule in self.rules: - self.rules[rule][method] = target - if name: self.builder[name] = self.builder[rule] - return - - target = self.rules[rule] = {method: target} - - # Build pattern and other structures for dynamic routes - anons = 0 # Number of anonymous wildcards - pattern = '' # Regular expression pattern - filters = [] # Lists of wildcard input filters - builder = [] # Data structure for the URL builder - is_static = True - for key, mode, conf in self.parse_rule(rule): - if mode: - is_static = False - mask, in_filter, out_filter = self.filters[mode](conf) - if key: - pattern += '(?P<%s>%s)' % (key, mask) - else: - pattern += '(?:%s)' % mask - key = 'anon%d' % anons; anons += 1 - if in_filter: filters.append((key, in_filter)) - builder.append((key, out_filter or str)) - elif key: - pattern += re.escape(key) - builder.append((None, key)) - self.builder[rule] = builder - if name: self.builder[name] = builder - - if is_static and not self.strict_order: - self.static[self.build(rule)] = target - return - - def fpat_sub(m): - return m.group(0) if len(m.group(1)) % 2 else m.group(1) + '(?:' - flat_pattern = re.sub(r'(\\*)(\(\?P<[^>]*>|\((?!\?))', fpat_sub, pattern) - - try: - re_match = re.compile('^(%s)$' % pattern).match - except re.error: - raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, _e())) - - def match(path): - """ Return an url-argument dictionary. """ - url_args = re_match(path).groupdict() - for name, wildcard_filter in filters: - try: - url_args[name] = wildcard_filter(url_args[name]) - except ValueError: - raise HTTPError(400, 'Path has wrong format.') - return url_args - - try: - combined = '%s|(^%s$)' % (self.dynamic[-1][0].pattern, flat_pattern) - self.dynamic[-1] = (re.compile(combined), self.dynamic[-1][1]) - self.dynamic[-1][1].append((match, target)) - except (AssertionError, IndexError): # AssertionError: Too many groups - self.dynamic.append((re.compile('(^%s$)' % flat_pattern), - [(match, target)])) - return match - - def build(self, _name, *anons, **query): - ''' Build an URL by filling the wildcards in a rule. ''' - builder = self.builder.get(_name) - if not builder: raise RouteBuildError("No route with that name.", _name) - try: - for i, value in enumerate(anons): query['anon%d'%i] = value - url = ''.join([f(query.pop(n)) if n else f for (n,f) in builder]) - return url if not query else url+'?'+urlencode(query) - except KeyError: - raise RouteBuildError('Missing URL argument: %r' % _e().args[0]) - - def match(self, environ): - ''' Return a (target, url_agrs) tuple or raise HTTPError(400/404/405). ''' - path, targets, urlargs = environ['PATH_INFO'] or '/', None, {} - if path in self.static: - targets = self.static[path] - else: - for combined, rules in self.dynamic: - match = combined.match(path) - if not match: continue - getargs, targets = rules[match.lastindex - 1] - urlargs = getargs(path) if getargs else {} - break - - if not targets: - raise HTTPError(404, "Not found: " + repr(environ['PATH_INFO'])) - method = environ['REQUEST_METHOD'].upper() - if method in targets: - return targets[method], urlargs - if method == 'HEAD' and 'GET' in targets: - return targets['GET'], urlargs - if 'ANY' in targets: - return targets['ANY'], urlargs - allowed = [verb for verb in targets if verb != 'ANY'] - if 'GET' in allowed and 'HEAD' not in allowed: - allowed.append('HEAD') - raise HTTPError(405, "Method not allowed.", - header=[('Allow',",".join(allowed))]) - - -class Route(object): - ''' This class wraps a route callback along with route specific metadata and - configuration and applies Plugins on demand. It is also responsible for - turing an URL path rule into a regular expression usable by the Router. - ''' - - def __init__(self, app, rule, method, callback, name=None, - plugins=None, skiplist=None, **config): - #: The application this route is installed to. - self.app = app - #: The path-rule string (e.g. ``/wiki/:page``). - self.rule = rule - #: The HTTP method as a string (e.g. ``GET``). - self.method = method - #: The original callback with no plugins applied. Useful for introspection. - self.callback = callback - #: The name of the route (if specified) or ``None``. - self.name = name or None - #: A list of route-specific plugins (see :meth:`Bottle.route`). - self.plugins = plugins or [] - #: A list of plugins to not apply to this route (see :meth:`Bottle.route`). - self.skiplist = skiplist or [] - #: Additional keyword arguments passed to the :meth:`Bottle.route` - #: decorator are stored in this dictionary. Used for route-specific - #: plugin configuration and meta-data. - self.config = ConfigDict(config) - - def __call__(self, *a, **ka): - depr("Some APIs changed to return Route() instances instead of"\ - " callables. Make sure to use the Route.call method and not to"\ - " call Route instances directly.") - return self.call(*a, **ka) - - @cached_property - def call(self): - ''' The route callback with all plugins applied. This property is - created on demand and then cached to speed up subsequent requests.''' - return self._make_callback() - - def reset(self): - ''' Forget any cached values. The next time :attr:`call` is accessed, - all plugins are re-applied. ''' - self.__dict__.pop('call', None) - - def prepare(self): - ''' Do all on-demand work immediately (useful for debugging).''' - self.call - - @property - def _context(self): - depr('Switch to Plugin API v2 and access the Route object directly.') - return dict(rule=self.rule, method=self.method, callback=self.callback, - name=self.name, app=self.app, config=self.config, - apply=self.plugins, skip=self.skiplist) - - def all_plugins(self): - ''' Yield all Plugins affecting this route. ''' - unique = set() - for p in reversed(self.app.plugins + self.plugins): - if True in self.skiplist: break - name = getattr(p, 'name', False) - if name and (name in self.skiplist or name in unique): continue - if p in self.skiplist or type(p) in self.skiplist: continue - if name: unique.add(name) - yield p - - def _make_callback(self): - callback = self.callback - for plugin in self.all_plugins(): - try: - if hasattr(plugin, 'apply'): - api = getattr(plugin, 'api', 1) - context = self if api > 1 else self._context - callback = plugin.apply(callback, context) - else: - callback = plugin(callback) - except RouteReset: # Try again with changed configuration. - return self._make_callback() - if not callback is self.callback: - update_wrapper(callback, self.callback) - return callback - - def __repr__(self): - return '<%s %r %r>' % (self.method, self.rule, self.callback) - - - - - - -############################################################################### -# Application Object ########################################################### -############################################################################### - - -class Bottle(object): - """ Each Bottle object represents a single, distinct web application and - consists of routes, callbacks, plugins and configuration. Instances are - callable WSGI applications. """ - - def __init__(self, catchall=True, autojson=True, config=None): - self.routes = [] # List of installed :class:`Route` instances. - self.router = Router() # Maps requests to :class:`Route` instances. - self.plugins = [] # List of installed plugins. - - self.error_handler = {} - self.config = ConfigDict(config or {}) - #: If true, most exceptions are catched and returned as :exc:`HTTPError` - self.catchall = catchall - #: An instance of :class:`HooksPlugin`. Empty by default. - self.hooks = HooksPlugin() - self.install(self.hooks) - if autojson: - self.install(JSONPlugin()) - self.install(TemplatePlugin()) - - def mount(self, prefix, app, **options): - ''' Mount an application (:class:`Bottle` or plain WSGI) to a specific - URL prefix. Example:: - - root_app.mount('/admin/', admin_app) - - :param prefix: path prefix or `mount-point`. If it ends in a slash, - that slash is mandatory. - :param app: an instance of :class:`Bottle` or a WSGI application. - - All other parameters are passed to the underlying :meth:`route` call. - ''' - if isinstance(app, basestring): - prefix, app = app, prefix - depr('Parameter order of Bottle.mount() changed.') # 0.10 - - parts = [p for p in prefix.split('/') if p] - if not parts: raise ValueError('Empty path prefix.') - path_depth = len(parts) - options.setdefault('skip', True) - options.setdefault('method', 'ANY') - - @self.route('/%s/:#.*#' % '/'.join(parts), **options) - def mountpoint(): - try: - request.path_shift(path_depth) - rs = BaseResponse([], 200) - def start_response(status, header): - rs.status = status - for name, value in header: rs.add_header(name, value) - return rs.body.append - rs.body = itertools.chain(rs.body, app(request.environ, start_response)) - return HTTPResponse(rs.body, rs.status_code, rs.headers) - finally: - request.path_shift(-path_depth) - - if not prefix.endswith('/'): - self.route('/' + '/'.join(parts), callback=mountpoint, **options) - - def merge(self, routes): - ''' Merge the routes of another :cls:`Bottle` application or a list of - :class:`Route` objects into this application. The routes keep their - 'owner', meaning that the :data:`Route.app` attribute is not - changed. ''' - if isinstance(routes, Bottle): - routes = routes.routes - for route in routes: - self.add_route(route) - - def install(self, plugin): - ''' Add a plugin to the list of plugins and prepare it for being - applied to all routes of this application. A plugin may be a simple - decorator or an object that implements the :class:`Plugin` API. - ''' - if hasattr(plugin, 'setup'): plugin.setup(self) - if not callable(plugin) and not hasattr(plugin, 'apply'): - raise TypeError("Plugins must be callable or implement .apply()") - self.plugins.append(plugin) - self.reset() - return plugin - - def uninstall(self, plugin): - ''' Uninstall plugins. Pass an instance to remove a specific plugin, a type - object to remove all plugins that match that type, a string to remove - all plugins with a matching ``name`` attribute or ``True`` to remove all - plugins. Return the list of removed plugins. ''' - removed, remove = [], plugin - for i, plugin in list(enumerate(self.plugins))[::-1]: - if remove is True or remove is plugin or remove is type(plugin) \ - or getattr(plugin, 'name', True) == remove: - removed.append(plugin) - del self.plugins[i] - if hasattr(plugin, 'close'): plugin.close() - if removed: self.reset() - return removed - - def run(self, **kwargs): - ''' Calls :func:`run` with the same parameters. ''' - run(self, **kwargs) - - def reset(self, route=None): - ''' Reset all routes (force plugins to be re-applied) and clear all - caches. If an ID or route object is given, only that specific route - is affected. ''' - if route is None: routes = self.routes - elif isinstance(route, Route): routes = [route] - else: routes = [self.routes[route]] - for route in routes: route.reset() - if DEBUG: - for route in routes: route.prepare() - self.hooks.trigger('app_reset') - - def close(self): - ''' Close the application and all installed plugins. ''' - for plugin in self.plugins: - if hasattr(plugin, 'close'): plugin.close() - self.stopped = True - - def match(self, environ): - """ Search for a matching route and return a (:class:`Route` , urlargs) - tuple. The second value is a dictionary with parameters extracted - from the URL. Raise :exc:`HTTPError` (404/405) on a non-match.""" - return self.router.match(environ) - - def get_url(self, routename, **kargs): - """ Return a string that matches a named route """ - scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/' - location = self.router.build(routename, **kargs).lstrip('/') - return urljoin(urljoin('/', scriptname), location) - - def add_route(self, route): - ''' Add a route object, but do not change the :data:`Route.app` - attribute.''' - self.routes.append(route) - self.router.add(route.rule, route.method, route, name=route.name) - if DEBUG: route.prepare() - - def route(self, path=None, method='GET', callback=None, name=None, - apply=None, skip=None, **config): - """ A decorator to bind a function to a request URL. Example:: - - @app.route('/hello/:name') - def hello(name): - return 'Hello %s' % name - - The ``:name`` part is a wildcard. See :class:`Router` for syntax - details. - - :param path: Request path or a list of paths to listen to. If no - path is specified, it is automatically generated from the - signature of the function. - :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of - methods to listen to. (default: `GET`) - :param callback: An optional shortcut to avoid the decorator - syntax. ``route(..., callback=func)`` equals ``route(...)(func)`` - :param name: The name for this route. (default: None) - :param apply: A decorator or plugin or a list of plugins. These are - applied to the route callback in addition to installed plugins. - :param skip: A list of plugins, plugin classes or names. Matching - plugins are not installed to this route. ``True`` skips all. - - Any additional keyword arguments are stored as route-specific - configuration and passed to plugins (see :meth:`Plugin.apply`). - """ - if callable(path): path, callback = None, path - plugins = makelist(apply) - skiplist = makelist(skip) - def decorator(callback): - # TODO: Documentation and tests - if isinstance(callback, basestring): callback = load(callback) - for rule in makelist(path) or yieldroutes(callback): - for verb in makelist(method): - verb = verb.upper() - route = Route(self, rule, verb, callback, name=name, - plugins=plugins, skiplist=skiplist, **config) - self.add_route(route) - return callback - return decorator(callback) if callback else decorator - - def get(self, path=None, method='GET', **options): - """ Equals :meth:`route`. """ - return self.route(path, method, **options) - - def post(self, path=None, method='POST', **options): - """ Equals :meth:`route` with a ``POST`` method parameter. """ - return self.route(path, method, **options) - - def put(self, path=None, method='PUT', **options): - """ Equals :meth:`route` with a ``PUT`` method parameter. """ - return self.route(path, method, **options) - - def delete(self, path=None, method='DELETE', **options): - """ Equals :meth:`route` with a ``DELETE`` method parameter. """ - return self.route(path, method, **options) - - def error(self, code=500): - """ Decorator: Register an output handler for a HTTP error code""" - def wrapper(handler): - self.error_handler[int(code)] = handler - return handler - return wrapper - - def hook(self, name): - """ Return a decorator that attaches a callback to a hook. Three hooks - are currently implemented: - - - before_request: Executed once before each request - - after_request: Executed once after each request - - app_reset: Called whenever :meth:`reset` is called. - """ - def wrapper(func): - self.hooks.add(name, func) - return func - return wrapper - - def handle(self, path, method='GET'): - """ (deprecated) Execute the first matching route callback and return - the result. :exc:`HTTPResponse` exceptions are catched and returned. - If :attr:`Bottle.catchall` is true, other exceptions are catched as - well and returned as :exc:`HTTPError` instances (500). - """ - depr("This method will change semantics in 0.10. Try to avoid it.") - if isinstance(path, dict): - return self._handle(path) - return self._handle({'PATH_INFO': path, 'REQUEST_METHOD': method.upper()}) - - def _handle(self, environ): - try: - environ['bottle.app'] = self - request.bind(environ) - response.bind() - route, args = self.router.match(environ) - environ['route.handle'] = environ['bottle.route'] = route - environ['route.url_args'] = args - return route.call(**args) - except HTTPResponse: - return _e() - except RouteReset: - route.reset() - return self._handle(environ) - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - stacktrace = format_exc(10) - environ['wsgi.errors'].write(stacktrace) - return HTTPError(500, "Internal Server Error", _e(), stacktrace) - - def _cast(self, out, peek=None): - """ Try to convert the parameter into something WSGI compatible and set - correct HTTP headers when possible. - Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like, - iterable of strings and iterable of unicodes - """ - - # Empty output is done here - if not out: - response['Content-Length'] = 0 - return [] - # Join lists of byte or unicode strings. Mixed lists are NOT supported - if isinstance(out, (tuple, list))\ - and isinstance(out[0], (bytes, unicode)): - out = out[0][0:0].join(out) # b'abc'[0:0] -> b'' - # Encode unicode strings - if isinstance(out, unicode): - out = out.encode(response.charset) - # Byte Strings are just returned - if isinstance(out, bytes): - response['Content-Length'] = len(out) - return [out] - # HTTPError or HTTPException (recursive, because they may wrap anything) - # TODO: Handle these explicitly in handle() or make them iterable. - if isinstance(out, HTTPError): - out.apply(response) - out = self.error_handler.get(out.status, repr)(out) - if isinstance(out, HTTPResponse): - depr('Error handlers must not return :exc:`HTTPResponse`.') #0.9 - return self._cast(out) - if isinstance(out, HTTPResponse): - out.apply(response) - return self._cast(out.output) - - # File-like objects. - if hasattr(out, 'read'): - if 'wsgi.file_wrapper' in request.environ: - return request.environ['wsgi.file_wrapper'](out) - elif hasattr(out, 'close') or not hasattr(out, '__iter__'): - return WSGIFileWrapper(out) - - # Handle Iterables. We peek into them to detect their inner type. - try: - out = iter(out) - first = next(out) - while not first: - first = next(out) - except StopIteration: - return self._cast('') - except HTTPResponse: - first = _e() - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - first = HTTPError(500, 'Unhandled exception', _e(), format_exc(10)) - - # These are the inner types allowed in iterator or generator objects. - if isinstance(first, HTTPResponse): - return self._cast(first) - if isinstance(first, bytes): - return itertools.chain([first], out) - if isinstance(first, unicode): - return imap(lambda x: x.encode(response.charset), - itertools.chain([first], out)) - return self._cast(HTTPError(500, 'Unsupported response type: %s'\ - % type(first))) - - def wsgi(self, environ, start_response): - """ The bottle WSGI-interface. """ - try: - out = self._cast(self._handle(environ)) - # rfc2616 section 4.3 - if response._status_code in (100, 101, 204, 304)\ - or request.method == 'HEAD': - if hasattr(out, 'close'): out.close() - out = [] - if isinstance(response._status_line, unicode): - response._status_line = str(response._status_line) - start_response(response._status_line, list(response.iter_headers())) - return out - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - err = '

Critical error while processing request: %s

' \ - % html_escape(environ.get('PATH_INFO', '/')) - if DEBUG: - err += '

Error:

\n
\n%s\n
\n' \ - '

Traceback:

\n
\n%s\n
\n' \ - % (html_escape(repr(_e())), html_escape(format_exc(10))) - environ['wsgi.errors'].write(err) - headers = [('Content-Type', 'text/html; charset=UTF-8')] - start_response('500 INTERNAL SERVER ERROR', headers) - return [tob(err)] - - def __call__(self, environ, start_response): - ''' Each instance of :class:'Bottle' is a WSGI application. ''' - return self.wsgi(environ, start_response) - - - - - - -############################################################################### -# HTTP and WSGI Tools ########################################################## -############################################################################### - - -class BaseRequest(object): - """ A wrapper for WSGI environment dictionaries that adds a lot of - convenient access methods and properties. Most of them are read-only.""" - - #: Maximum size of memory buffer for :attr:`body` in bytes. - MEMFILE_MAX = 102400 - #: Maximum number pr GET or POST parameters per request - MAX_PARAMS = 100 - - def __init__(self, environ): - """ Wrap a WSGI environ dictionary. """ - #: The wrapped WSGI environ dictionary. This is the only real attribute. - #: All other attributes actually are read-only properties. - self.environ = environ - environ['bottle.request'] = self - - @DictProperty('environ', 'bottle.app', read_only=True) - def app(self): - ''' Bottle application handling this request. ''' - raise AttributeError('This request is not connected to an application.') - - @property - def path(self): - ''' The value of ``PATH_INFO`` with exactly one prefixed slash (to fix - broken clients and avoid the "empty path" edge case). ''' - return '/' + self.environ.get('PATH_INFO','').lstrip('/') - - @property - def method(self): - ''' The ``REQUEST_METHOD`` value as an uppercase string. ''' - return self.environ.get('REQUEST_METHOD', 'GET').upper() - - @DictProperty('environ', 'bottle.request.headers', read_only=True) - def headers(self): - ''' A :class:`WSGIHeaderDict` that provides case-insensitive access to - HTTP request headers. ''' - return WSGIHeaderDict(self.environ) - - def get_header(self, name, default=None): - ''' Return the value of a request header, or a given default value. ''' - return self.headers.get(name, default) - - @DictProperty('environ', 'bottle.request.cookies', read_only=True) - def cookies(self): - """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT - decoded. Use :meth:`get_cookie` if you expect signed cookies. """ - cookies = SimpleCookie(self.environ.get('HTTP_COOKIE','')) - cookies = list(cookies.values())[:self.MAX_PARAMS] - return FormsDict((c.key, c.value) for c in cookies) - - def get_cookie(self, key, default=None, secret=None): - """ Return the content of a cookie. To read a `Signed Cookie`, the - `secret` must match the one used to create the cookie (see - :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing - cookie or wrong signature), return a default value. """ - value = self.cookies.get(key) - if secret and value: - dec = cookie_decode(value, secret) # (key, value) tuple or None - return dec[1] if dec and dec[0] == key else default - return value or default - - @DictProperty('environ', 'bottle.request.query', read_only=True) - def query(self): - ''' The :attr:`query_string` parsed into a :class:`FormsDict`. These - values are sometimes called "URL arguments" or "GET parameters", but - not to be confused with "URL wildcards" as they are provided by the - :class:`Router`. ''' - pairs = parse_qsl(self.query_string, keep_blank_values=True) - get = self.environ['bottle.get'] = FormsDict() - for key, value in pairs[:self.MAX_PARAMS]: - get[key] = value - return get - - @DictProperty('environ', 'bottle.request.forms', read_only=True) - def forms(self): - """ Form values parsed from an `url-encoded` or `multipart/form-data` - encoded POST or PUT request body. The result is retuned as a - :class:`FormsDict`. All keys and values are strings. File uploads - are stored separately in :attr:`files`. """ - forms = FormsDict() - for name, item in self.POST.allitems(): - if not hasattr(item, 'filename'): - forms[name] = item - return forms - - @DictProperty('environ', 'bottle.request.params', read_only=True) - def params(self): - """ A :class:`FormsDict` with the combined values of :attr:`query` and - :attr:`forms`. File uploads are stored in :attr:`files`. """ - params = FormsDict() - for key, value in self.query.allitems(): - params[key] = value - for key, value in self.forms.allitems(): - params[key] = value - return params - - @DictProperty('environ', 'bottle.request.files', read_only=True) - def files(self): - """ File uploads parsed from an `url-encoded` or `multipart/form-data` - encoded POST or PUT request body. The values are instances of - :class:`cgi.FieldStorage`. The most important attributes are: - - filename - The filename, if specified; otherwise None; this is the client - side filename, *not* the file name on which it is stored (that's - a temporary file you don't deal with) - file - The file(-like) object from which you can read the data. - value - The value as a *string*; for file uploads, this transparently - reads the file every time you request the value. Do not do this - on big files. - """ - files = FormsDict() - for name, item in self.POST.allitems(): - if hasattr(item, 'filename'): - files[name] = item - return files - - @DictProperty('environ', 'bottle.request.json', read_only=True) - def json(self): - ''' If the ``Content-Type`` header is ``application/json``, this - property holds the parsed content of the request body. Only requests - smaller than :attr:`MEMFILE_MAX` are processed to avoid memory - exhaustion. ''' - if 'application/json' in self.environ.get('CONTENT_TYPE', '') \ - and 0 < self.content_length < self.MEMFILE_MAX: - return json_loads(self.body.read(self.MEMFILE_MAX)) - return None - - @DictProperty('environ', 'bottle.request.body', read_only=True) - def _body(self): - maxread = max(0, self.content_length) - stream = self.environ['wsgi.input'] - body = BytesIO() if maxread < self.MEMFILE_MAX else TemporaryFile(mode='w+b') - while maxread > 0: - part = stream.read(min(maxread, self.MEMFILE_MAX)) - if not part: break - body.write(part) - maxread -= len(part) - self.environ['wsgi.input'] = body - body.seek(0) - return body - - @property - def body(self): - """ The HTTP request body as a seek-able file-like object. Depending on - :attr:`MEMFILE_MAX`, this is either a temporary file or a - :class:`io.BytesIO` instance. Accessing this property for the first - time reads and replaces the ``wsgi.input`` environ variable. - Subsequent accesses just do a `seek(0)` on the file object. """ - self._body.seek(0) - return self._body - - #: An alias for :attr:`query`. - GET = query - - @DictProperty('environ', 'bottle.request.post', read_only=True) - def POST(self): - """ The values of :attr:`forms` and :attr:`files` combined into a single - :class:`FormsDict`. Values are either strings (form values) or - instances of :class:`cgi.FieldStorage` (file uploads). - """ - post = FormsDict() - safe_env = {'QUERY_STRING':''} # Build a safe environment for cgi - for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'): - if key in self.environ: safe_env[key] = self.environ[key] - if NCTextIOWrapper: - fb = NCTextIOWrapper(self.body, encoding='ISO-8859-1', newline='\n') - else: - fb = self.body - data = cgi.FieldStorage(fp=fb, environ=safe_env, keep_blank_values=True) - for item in (data.list or [])[:self.MAX_PARAMS]: - post[item.name] = item if item.filename else item.value - return post - - @property - def COOKIES(self): - ''' Alias for :attr:`cookies` (deprecated). ''' - depr('BaseRequest.COOKIES was renamed to BaseRequest.cookies (lowercase).') - return self.cookies - - @property - def url(self): - """ The full request URI including hostname and scheme. If your app - lives behind a reverse proxy or load balancer and you get confusing - results, make sure that the ``X-Forwarded-Host`` header is set - correctly. """ - return self.urlparts.geturl() - - @DictProperty('environ', 'bottle.request.urlparts', read_only=True) - def urlparts(self): - ''' The :attr:`url` string as an :class:`urlparse.SplitResult` tuple. - The tuple contains (scheme, host, path, query_string and fragment), - but the fragment is always empty because it is not visible to the - server. ''' - env = self.environ - http = env.get('wsgi.url_scheme', 'http') - host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST') - if not host: - # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients. - host = env.get('SERVER_NAME', '127.0.0.1') - port = env.get('SERVER_PORT') - if port and port != ('80' if http == 'http' else '443'): - host += ':' + port - path = urlquote(self.fullpath) - return UrlSplitResult(http, host, path, env.get('QUERY_STRING'), '') - - @property - def fullpath(self): - """ Request path including :attr:`script_name` (if present). """ - return urljoin(self.script_name, self.path.lstrip('/')) - - @property - def query_string(self): - """ The raw :attr:`query` part of the URL (everything in between ``?`` - and ``#``) as a string. """ - return self.environ.get('QUERY_STRING', '') - - @property - def script_name(self): - ''' The initial portion of the URL's `path` that was removed by a higher - level (server or routing middleware) before the application was - called. This script path is returned with leading and tailing - slashes. ''' - script_name = self.environ.get('SCRIPT_NAME', '').strip('/') - return '/' + script_name + '/' if script_name else '/' - - def path_shift(self, shift=1): - ''' Shift path segments from :attr:`path` to :attr:`script_name` and - vice versa. - - :param shift: The number of path segments to shift. May be negative - to change the shift direction. (default: 1) - ''' - script = self.environ.get('SCRIPT_NAME','/') - self['SCRIPT_NAME'], self['PATH_INFO'] = path_shift(script, self.path, shift) - - @property - def content_length(self): - ''' The request body length as an integer. The client is responsible to - set this header. Otherwise, the real length of the body is unknown - and -1 is returned. In this case, :attr:`body` will be empty. ''' - return int(self.environ.get('CONTENT_LENGTH') or -1) - - @property - def is_xhr(self): - ''' True if the request was triggered by a XMLHttpRequest. This only - works with JavaScript libraries that support the `X-Requested-With` - header (most of the popular libraries do). ''' - requested_with = self.environ.get('HTTP_X_REQUESTED_WITH','') - return requested_with.lower() == 'xmlhttprequest' - - @property - def is_ajax(self): - ''' Alias for :attr:`is_xhr`. "Ajax" is not the right term. ''' - return self.is_xhr - - @property - def auth(self): - """ HTTP authentication data as a (user, password) tuple. This - implementation currently supports basic (not digest) authentication - only. If the authentication happened at a higher level (e.g. in the - front web-server or a middleware), the password field is None, but - the user field is looked up from the ``REMOTE_USER`` environ - variable. On any errors, None is returned. """ - basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION','')) - if basic: return basic - ruser = self.environ.get('REMOTE_USER') - if ruser: return (ruser, None) - return None - - @property - def remote_route(self): - """ A list of all IPs that were involved in this request, starting with - the client IP and followed by zero or more proxies. This does only - work if all proxies support the ```X-Forwarded-For`` header. Note - that this information can be forged by malicious clients. """ - proxy = self.environ.get('HTTP_X_FORWARDED_FOR') - if proxy: return [ip.strip() for ip in proxy.split(',')] - remote = self.environ.get('REMOTE_ADDR') - return [remote] if remote else [] - - @property - def remote_addr(self): - """ The client IP as a string. Note that this information can be forged - by malicious clients. """ - route = self.remote_route - return route[0] if route else None - - def copy(self): - """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """ - return Request(self.environ.copy()) - - def get(self, value, default=None): return self.environ.get(value, default) - def __getitem__(self, key): return self.environ[key] - def __delitem__(self, key): self[key] = ""; del(self.environ[key]) - def __iter__(self): return iter(self.environ) - def __len__(self): return len(self.environ) - def keys(self): return self.environ.keys() - def __setitem__(self, key, value): - """ Change an environ value and clear all caches that depend on it. """ - - if self.environ.get('bottle.request.readonly'): - raise KeyError('The environ dictionary is read-only.') - - self.environ[key] = value - todelete = () - - if key == 'wsgi.input': - todelete = ('body', 'forms', 'files', 'params', 'post', 'json') - elif key == 'QUERY_STRING': - todelete = ('query', 'params') - elif key.startswith('HTTP_'): - todelete = ('headers', 'cookies') - - for key in todelete: - self.environ.pop('bottle.request.'+key, None) - - def __repr__(self): - return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url) - -def _hkey(s): - return s.title().replace('_','-') - - -class HeaderProperty(object): - def __init__(self, name, reader=None, writer=str, default=''): - self.name, self.reader, self.writer, self.default = name, reader, writer, default - self.__doc__ = 'Current value of the %r header.' % name.title() - - def __get__(self, obj, cls): - if obj is None: return self - value = obj.headers.get(self.name) - return self.reader(value) if (value and self.reader) else (value or self.default) - - def __set__(self, obj, value): - if self.writer: value = self.writer(value) - obj.headers[self.name] = value - - def __delete__(self, obj): - if self.name in obj.headers: - del obj.headers[self.name] - - -class BaseResponse(object): - """ Storage class for a response body as well as headers and cookies. - - This class does support dict-like case-insensitive item-access to - headers, but is NOT a dict. Most notably, iterating over a response - yields parts of the body and not the headers. - """ - - default_status = 200 - default_content_type = 'text/html; charset=UTF-8' - - # Header blacklist for specific response codes - # (rfc2616 section 10.2.3 and 10.3.5) - bad_headers = { - 204: set(('Content-Type',)), - 304: set(('Allow', 'Content-Encoding', 'Content-Language', - 'Content-Length', 'Content-Range', 'Content-Type', - 'Content-Md5', 'Last-Modified'))} - - def __init__(self, body='', status=None, **headers): - self._status_line = None - self._status_code = None - self._cookies = None - self._headers = {'Content-Type': [self.default_content_type]} - self.body = body - self.status = status or self.default_status - if headers: - for name, value in headers.items(): - self[name] = value - - def copy(self): - ''' Returns a copy of self. ''' - copy = Response() - copy.status = self.status - copy._headers = dict((k, v[:]) for (k, v) in self._headers.items()) - return copy - - def __iter__(self): - return iter(self.body) - - def close(self): - if hasattr(self.body, 'close'): - self.body.close() - - @property - def status_line(self): - ''' The HTTP status line as a string (e.g. ``404 Not Found``).''' - return self._status_line - - @property - def status_code(self): - ''' The HTTP status code as an integer (e.g. 404).''' - return self._status_code - - def _set_status(self, status): - if isinstance(status, int): - code, status = status, _HTTP_STATUS_LINES.get(status) - elif ' ' in status: - status = status.strip() - code = int(status.split()[0]) - else: - raise ValueError('String status line without a reason phrase.') - if not 100 <= code <= 999: raise ValueError('Status code out of range.') - self._status_code = code - self._status_line = status or ('%d Unknown' % code) - - def _get_status(self): - return self._status_line - - status = property(_get_status, _set_status, None, - ''' A writeable property to change the HTTP response status. It accepts - either a numeric code (100-999) or a string with a custom reason - phrase (e.g. "404 Brain not found"). Both :data:`status_line` and - :data:`status_code` are updated accordingly. The return value is - always a status string. ''') - del _get_status, _set_status - - @property - def headers(self): - ''' An instance of :class:`HeaderDict`, a case-insensitive dict-like - view on the response headers. ''' - self.__dict__['headers'] = hdict = HeaderDict() - hdict.dict = self._headers - return hdict - - def __contains__(self, name): return _hkey(name) in self._headers - def __delitem__(self, name): del self._headers[_hkey(name)] - def __getitem__(self, name): return self._headers[_hkey(name)][-1] - def __setitem__(self, name, value): self._headers[_hkey(name)] = [str(value)] - - def get_header(self, name, default=None): - ''' Return the value of a previously defined header. If there is no - header with that name, return a default value. ''' - return self._headers.get(_hkey(name), [default])[-1] - - def set_header(self, name, value, append=False): - ''' Create a new response header, replacing any previously defined - headers with the same name. ''' - if append: - self.add_header(name, value) - else: - self._headers[_hkey(name)] = [str(value)] - - def add_header(self, name, value): - ''' Add an additional response header, not removing duplicates. ''' - self._headers.setdefault(_hkey(name), []).append(str(value)) - - def iter_headers(self): - ''' Yield (header, value) tuples, skipping headers that are not - allowed with the current response status code. ''' - headers = self._headers.items() - bad_headers = self.bad_headers.get(self._status_code) - if bad_headers: - headers = [h for h in headers if h[0] not in bad_headers] - for name, values in headers: - for value in values: - yield name, value - if self._cookies: - for c in self._cookies.values(): - yield 'Set-Cookie', c.OutputString() - - def wsgiheader(self): - depr('The wsgiheader method is deprecated. See headerlist.') #0.10 - return self.headerlist - - @property - def headerlist(self): - ''' WSGI conform list of (header, value) tuples. ''' - return list(self.iter_headers()) - - content_type = HeaderProperty('Content-Type') - content_length = HeaderProperty('Content-Length', reader=int) - - @property - def charset(self): - """ Return the charset specified in the content-type header (default: utf8). """ - if 'charset=' in self.content_type: - return self.content_type.split('charset=')[-1].split(';')[0].strip() - return 'UTF-8' - - @property - def COOKIES(self): - """ A dict-like SimpleCookie instance. This should not be used directly. - See :meth:`set_cookie`. """ - depr('The COOKIES dict is deprecated. Use `set_cookie()` instead.') # 0.10 - if not self._cookies: - self._cookies = SimpleCookie() - return self._cookies - - def set_cookie(self, name, value, secret=None, **options): - ''' Create a new cookie or replace an old one. If the `secret` parameter is - set, create a `Signed Cookie` (described below). - - :param name: the name of the cookie. - :param value: the value of the cookie. - :param secret: a signature key required for signed cookies. - - Additionally, this method accepts all RFC 2109 attributes that are - supported by :class:`cookie.Morsel`, including: - - :param max_age: maximum age in seconds. (default: None) - :param expires: a datetime object or UNIX timestamp. (default: None) - :param domain: the domain that is allowed to read the cookie. - (default: current domain) - :param path: limits the cookie to a given path (default: current path) - :param secure: limit the cookie to HTTPS connections (default: off). - :param httponly: prevents client-side javascript to read this cookie - (default: off, requires Python 2.6 or newer). - - If neither `expires` nor `max_age` is set (default), the cookie will - expire at the end of the browser session (as soon as the browser - window is closed). - - Signed cookies may store any pickle-able object and are - cryptographically signed to prevent manipulation. Keep in mind that - cookies are limited to 4kb in most browsers. - - Warning: Signed cookies are not encrypted (the client can still see - the content) and not copy-protected (the client can restore an old - cookie). The main intention is to make pickling and unpickling - save, not to store secret information at client side. - ''' - if not self._cookies: - self._cookies = SimpleCookie() - - if secret: - value = touni(cookie_encode((name, value), secret)) - elif not isinstance(value, basestring): - raise TypeError('Secret key missing for non-string Cookie.') - - if len(value) > 4096: raise ValueError('Cookie value to long.') - self._cookies[name] = value - - for key, value in options.items(): - if key == 'max_age': - if isinstance(value, timedelta): - value = value.seconds + value.days * 24 * 3600 - if key == 'expires': - if isinstance(value, (datedate, datetime)): - value = value.timetuple() - elif isinstance(value, (int, float)): - value = time.gmtime(value) - value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value) - self._cookies[name][key.replace('_', '-')] = value - - def delete_cookie(self, key, **kwargs): - ''' Delete a cookie. Be sure to use the same `domain` and `path` - settings as used to create the cookie. ''' - kwargs['max_age'] = -1 - kwargs['expires'] = 0 - self.set_cookie(key, '', **kwargs) - - def __repr__(self): - out = '' - for name, value in self.headerlist: - out += '%s: %s\n' % (name.title(), value.strip()) - return out - -#: Thread-local storage for :class:`LocalRequest` and :class:`LocalResponse` -#: attributes. -_lctx = threading.local() - -def local_property(name, doc=None): - - return property( - lambda self: getattr(_lctx, name), - lambda self, value: setattr(_lctx, name, value), - lambda self: delattr(_lctx, name), - doc or ('Thread-local property stored in :data:`_lctx.%s` ' % name) - ) - -class LocalRequest(BaseRequest): - ''' A thread-local subclass of :class:`BaseRequest` with a different - set of attribues for each thread. There is usually only one global - instance of this class (:data:`request`). If accessed during a - request/response cycle, this instance always refers to the *current* - request (even on a multithreaded server). ''' - def __init__(self): pass - bind = BaseRequest.__init__ - environ = local_property('request_environ') - - -class LocalResponse(BaseResponse): - ''' A thread-local subclass of :class:`BaseResponse` with a different - set of attribues for each thread. There is usually only one global - instance of this class (:data:`response`). Its attributes are used - to build the HTTP response at the end of the request/response cycle. - ''' - def __init__(self): pass - bind = BaseResponse.__init__ - _status_line = local_property('response_status_line') - _status_code = local_property('response_status_code') - _cookies = local_property('response_cookies') - _headers = local_property('response_headers') - body = local_property('response_body') - -Response = LocalResponse # BC 0.9 -Request = LocalRequest # BC 0.9 - - - - - - -############################################################################### -# Plugins ###################################################################### -############################################################################### - -class PluginError(BottleException): pass - -class JSONPlugin(object): - name = 'json' - api = 2 - - def __init__(self, json_dumps=json_dumps): - self.json_dumps = json_dumps - - def apply(self, callback, context): - dumps = self.json_dumps - if not dumps: return callback - def wrapper(*a, **ka): - rv = callback(*a, **ka) - if isinstance(rv, dict): - #Attempt to serialize, raises exception on failure - json_response = dumps(rv) - #Set content type only if serialization succesful - response.content_type = 'application/json' - return json_response - return rv - return wrapper - - -class HooksPlugin(object): - name = 'hooks' - api = 2 - - _names = 'before_request', 'after_request', 'app_reset' - - def __init__(self): - self.hooks = dict((name, []) for name in self._names) - self.app = None - - def _empty(self): - return not (self.hooks['before_request'] or self.hooks['after_request']) - - def setup(self, app): - self.app = app - - def add(self, name, func): - ''' Attach a callback to a hook. ''' - was_empty = self._empty() - self.hooks.setdefault(name, []).append(func) - if self.app and was_empty and not self._empty(): self.app.reset() - - def remove(self, name, func): - ''' Remove a callback from a hook. ''' - was_empty = self._empty() - if name in self.hooks and func in self.hooks[name]: - self.hooks[name].remove(func) - if self.app and not was_empty and self._empty(): self.app.reset() - - def trigger(self, name, *a, **ka): - ''' Trigger a hook and return a list of results. ''' - hooks = self.hooks[name] - if ka.pop('reversed', False): hooks = hooks[::-1] - return [hook(*a, **ka) for hook in hooks] - - def apply(self, callback, context): - if self._empty(): return callback - def wrapper(*a, **ka): - self.trigger('before_request') - rv = callback(*a, **ka) - self.trigger('after_request', reversed=True) - return rv - return wrapper - - -class TemplatePlugin(object): - ''' This plugin applies the :func:`view` decorator to all routes with a - `template` config parameter. If the parameter is a tuple, the second - element must be a dict with additional options (e.g. `template_engine`) - or default variables for the template. ''' - name = 'template' - api = 2 - - def apply(self, callback, route): - conf = route.config.get('template') - if isinstance(conf, (tuple, list)) and len(conf) == 2: - return view(conf[0], **conf[1])(callback) - elif isinstance(conf, str) and 'template_opts' in route.config: - depr('The `template_opts` parameter is deprecated.') #0.9 - return view(conf, **route.config['template_opts'])(callback) - elif isinstance(conf, str): - return view(conf)(callback) - else: - return callback - - -#: Not a plugin, but part of the plugin API. TODO: Find a better place. -class _ImportRedirect(object): - def __init__(self, name, impmask): - ''' Create a virtual package that redirects imports (see PEP 302). ''' - self.name = name - self.impmask = impmask - self.module = sys.modules.setdefault(name, imp.new_module(name)) - self.module.__dict__.update({'__file__': __file__, '__path__': [], - '__all__': [], '__loader__': self}) - sys.meta_path.append(self) - - def find_module(self, fullname, path=None): - if '.' not in fullname: return - packname, modname = fullname.rsplit('.', 1) - if packname != self.name: return - return self - - def load_module(self, fullname): - if fullname in sys.modules: return sys.modules[fullname] - packname, modname = fullname.rsplit('.', 1) - realname = self.impmask % modname - __import__(realname) - module = sys.modules[fullname] = sys.modules[realname] - setattr(self.module, modname, module) - module.__loader__ = self - return module - - - - - - -############################################################################### -# Common Utilities ############################################################# -############################################################################### - - -class MultiDict(DictMixin): - """ This dict stores multiple values per key, but behaves exactly like a - normal dict in that it returns only the newest value for any given key. - There are special methods available to access the full list of values. - """ - - def __init__(self, *a, **k): - self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items()) - - def __len__(self): return len(self.dict) - def __iter__(self): return iter(self.dict) - def __contains__(self, key): return key in self.dict - def __delitem__(self, key): del self.dict[key] - def __getitem__(self, key): return self.dict[key][-1] - def __setitem__(self, key, value): self.append(key, value) - def keys(self): return self.dict.keys() - - if py3k: - def values(self): return (v[-1] for v in self.dict.values()) - def items(self): return ((k, v[-1]) for k, v in self.dict.items()) - def allitems(self): - return ((k, v) for k, vl in self.dict.items() for v in vl) - iterkeys = keys - itervalues = values - iteritems = items - iterallitems = allitems - - else: - def values(self): return [v[-1] for v in self.dict.values()] - def items(self): return [(k, v[-1]) for k, v in self.dict.items()] - def iterkeys(self): return self.dict.iterkeys() - def itervalues(self): return (v[-1] for v in self.dict.itervalues()) - def iteritems(self): - return ((k, v[-1]) for k, v in self.dict.iteritems()) - def iterallitems(self): - return ((k, v) for k, vl in self.dict.iteritems() for v in vl) - def allitems(self): - return [(k, v) for k, vl in self.dict.iteritems() for v in vl] - - def get(self, key, default=None, index=-1, type=None): - ''' Return the most recent value for a key. - - :param default: The default value to be returned if the key is not - present or the type conversion fails. - :param index: An index for the list of available values. - :param type: If defined, this callable is used to cast the value - into a specific type. Exception are suppressed and result in - the default value to be returned. - ''' - try: - val = self.dict[key][index] - return type(val) if type else val - except Exception: - pass - return default - - def append(self, key, value): - ''' Add a new value to the list of values for this key. ''' - self.dict.setdefault(key, []).append(value) - - def replace(self, key, value): - ''' Replace the list of values with a single value. ''' - self.dict[key] = [value] - - def getall(self, key): - ''' Return a (possibly empty) list of values for a key. ''' - return self.dict.get(key) or [] - - #: Aliases for WTForms to mimic other multi-dict APIs (Django) - getone = get - getlist = getall - - - -class FormsDict(MultiDict): - ''' This :class:`MultiDict` subclass is used to store request form data. - Additionally to the normal dict-like item access methods (which return - unmodified data as native strings), this container also supports - attribute-like access to its values. Attributes are automatically de- - or recoded to match :attr:`input_encoding` (default: 'utf8'). Missing - attributes default to an empty string. ''' - - #: Encoding used for attribute values. - input_encoding = 'utf8' - #: If true (default), unicode strings are first encoded with `latin1` - #: and then decoded to match :attr:`input_encoding`. - recode_unicode = True - - def _fix(self, s, encoding=None): - if isinstance(s, unicode) and self.recode_unicode: # Python 3 WSGI - s = s.encode('latin1') - if isinstance(s, bytes): # Python 2 WSGI - return s.decode(encoding or self.input_encoding) - return s - - def decode(self, encoding=None): - ''' Returns a copy with all keys and values de- or recoded to match - :attr:`input_encoding`. Some libraries (e.g. WTForms) want a - unicode dictionary. ''' - copy = FormsDict() - enc = copy.input_encoding = encoding or self.input_encoding - copy.recode_unicode = False - for key, value in self.allitems(): - copy.append(self._fix(key, enc), self._fix(value, enc)) - return copy - - def getunicode(self, name, default=None, encoding=None): - try: - return self._fix(self[name], encoding) - except (UnicodeError, KeyError): - return default - - def __getattr__(self, name, default=unicode()): - return self.getunicode(name, default=default) - - -class HeaderDict(MultiDict): - """ A case-insensitive version of :class:`MultiDict` that defaults to - replace the old value instead of appending it. """ - - def __init__(self, *a, **ka): - self.dict = {} - if a or ka: self.update(*a, **ka) - - def __contains__(self, key): return _hkey(key) in self.dict - def __delitem__(self, key): del self.dict[_hkey(key)] - def __getitem__(self, key): return self.dict[_hkey(key)][-1] - def __setitem__(self, key, value): self.dict[_hkey(key)] = [str(value)] - def append(self, key, value): - self.dict.setdefault(_hkey(key), []).append(str(value)) - def replace(self, key, value): self.dict[_hkey(key)] = [str(value)] - def getall(self, key): return self.dict.get(_hkey(key)) or [] - def get(self, key, default=None, index=-1): - return MultiDict.get(self, _hkey(key), default, index) - def filter(self, names): - for name in [_hkey(n) for n in names]: - if name in self.dict: - del self.dict[name] - - -class WSGIHeaderDict(DictMixin): - ''' This dict-like class wraps a WSGI environ dict and provides convenient - access to HTTP_* fields. Keys and values are native strings - (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI - environment contains non-native string values, these are de- or encoded - using a lossless 'latin1' character set. - - The API will remain stable even on changes to the relevant PEPs. - Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one - that uses non-native strings.) - ''' - #: List of keys that do not have a 'HTTP_' prefix. - cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH') - - def __init__(self, environ): - self.environ = environ - - def _ekey(self, key): - ''' Translate header field name to CGI/WSGI environ key. ''' - key = key.replace('-','_').upper() - if key in self.cgikeys: - return key - return 'HTTP_' + key - - def raw(self, key, default=None): - ''' Return the header value as is (may be bytes or unicode). ''' - return self.environ.get(self._ekey(key), default) - - def __getitem__(self, key): - return tonat(self.environ[self._ekey(key)], 'latin1') - - def __setitem__(self, key, value): - raise TypeError("%s is read-only." % self.__class__) - - def __delitem__(self, key): - raise TypeError("%s is read-only." % self.__class__) - - def __iter__(self): - for key in self.environ: - if key[:5] == 'HTTP_': - yield key[5:].replace('_', '-').title() - elif key in self.cgikeys: - yield key.replace('_', '-').title() - - def keys(self): return [x for x in self] - def __len__(self): return len(self.keys()) - def __contains__(self, key): return self._ekey(key) in self.environ - - -class ConfigDict(dict): - ''' A dict-subclass with some extras: You can access keys like attributes. - Uppercase attributes create new ConfigDicts and act as name-spaces. - Other missing attributes return None. Calling a ConfigDict updates its - values and returns itself. - - >>> cfg = ConfigDict() - >>> cfg.Namespace.value = 5 - >>> cfg.OtherNamespace(a=1, b=2) - >>> cfg - {'Namespace': {'value': 5}, 'OtherNamespace': {'a': 1, 'b': 2}} - ''' - - def __getattr__(self, key): - if key not in self and key[0].isupper(): - self[key] = ConfigDict() - return self.get(key) - - def __setattr__(self, key, value): - if hasattr(dict, key): - raise AttributeError('Read-only attribute.') - if key in self and self[key] and isinstance(self[key], ConfigDict): - raise AttributeError('Non-empty namespace attribute.') - self[key] = value - - def __delattr__(self, key): - if key in self: del self[key] - - def __call__(self, *a, **ka): - for key, value in dict(*a, **ka).items(): setattr(self, key, value) - return self - - -class AppStack(list): - """ A stack-like list. Calling it returns the head of the stack. """ - - def __call__(self): - """ Return the current default application. """ - return self[-1] - - def push(self, value=None): - """ Add a new :class:`Bottle` instance to the stack """ - if not isinstance(value, Bottle): - value = Bottle() - self.append(value) - return value - - -class WSGIFileWrapper(object): - - def __init__(self, fp, buffer_size=1024*64): - self.fp, self.buffer_size = fp, buffer_size - for attr in ('fileno', 'close', 'read', 'readlines', 'tell', 'seek'): - if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr)) - - def __iter__(self): - buff, read = self.buffer_size, self.read - while True: - part = read(buff) - if not part: return - yield part - - - - - - -############################################################################### -# Application Helper ########################################################### -############################################################################### - - -def abort(code=500, text='Unknown Error: Application stopped.'): - """ Aborts execution and causes a HTTP error. """ - raise HTTPError(code, text) - - -def redirect(url, code=None): - """ Aborts execution and causes a 303 or 302 redirect, depending on - the HTTP protocol version. """ - if code is None: - code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302 - location = urljoin(request.url, url) - raise HTTPResponse("", status=code, header=dict(Location=location)) - - -def _file_iter_range(fp, offset, bytes, maxread=1024*1024): - ''' Yield chunks from a range in a file. No chunk is bigger than maxread.''' - fp.seek(offset) - while bytes > 0: - part = fp.read(min(bytes, maxread)) - if not part: break - bytes -= len(part) - yield part - - -def static_file(filename, root, mimetype='auto', download=False): - """ Open a file in a safe way and return :exc:`HTTPResponse` with status - code 200, 305, 401 or 404. Set Content-Type, Content-Encoding, - Content-Length and Last-Modified header. Obey If-Modified-Since header - and HEAD requests. - """ - root = os.path.abspath(root) + os.sep - filename = os.path.abspath(os.path.join(root, filename.strip('/\\'))) - header = dict() - - if not filename.startswith(root): - return HTTPError(403, "Access denied.") - if not os.path.exists(filename) or not os.path.isfile(filename): - return HTTPError(404, "File does not exist.") - if not os.access(filename, os.R_OK): - return HTTPError(403, "You do not have permission to access this file.") - - if mimetype == 'auto': - mimetype, encoding = mimetypes.guess_type(filename) - if mimetype: header['Content-Type'] = mimetype - if encoding: header['Content-Encoding'] = encoding - elif mimetype: - header['Content-Type'] = mimetype - - if download: - download = os.path.basename(filename if download == True else download) - header['Content-Disposition'] = 'attachment; filename="%s"' % download - - stats = os.stat(filename) - header['Content-Length'] = clen = stats.st_size - lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)) - header['Last-Modified'] = lm - - ims = request.environ.get('HTTP_IF_MODIFIED_SINCE') - if ims: - ims = parse_date(ims.split(";")[0].strip()) - if ims is not None and ims >= int(stats.st_mtime): - header['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) - return HTTPResponse(status=304, header=header) - - body = '' if request.method == 'HEAD' else open(filename, 'rb') - - header["Accept-Ranges"] = "bytes" - ranges = request.environ.get('HTTP_RANGE') - if 'HTTP_RANGE' in request.environ: - ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen)) - if not ranges: - return HTTPError(416, "Requested Range Not Satisfiable") - offset, end = ranges[0] - header["Content-Range"] = "bytes %d-%d/%d" % (offset, end-1, clen) - header["Content-Length"] = str(end-offset) - if body: body = _file_iter_range(body, offset, end-offset) - return HTTPResponse(body, header=header, status=206) - return HTTPResponse(body, header=header) - - - - - - -############################################################################### -# HTTP Utilities and MISC (TODO) ############################################### -############################################################################### - - -def debug(mode=True): - """ Change the debug level. - There is only one debug level supported at the moment.""" - global DEBUG - DEBUG = bool(mode) - - -def parse_date(ims): - """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """ - try: - ts = email.utils.parsedate_tz(ims) - return time.mktime(ts[:8] + (0,)) - (ts[9] or 0) - time.timezone - except (TypeError, ValueError, IndexError, OverflowError): - return None - - -def parse_auth(header): - """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None""" - try: - method, data = header.split(None, 1) - if method.lower() == 'basic': - user, pwd = touni(base64.b64decode(tob(data))).split(':',1) - return user, pwd - except (KeyError, ValueError): - return None - -def parse_range_header(header, maxlen=0): - ''' Yield (start, end) ranges parsed from a HTTP Range header. Skip - unsatisfiable ranges. The end index is non-inclusive.''' - if not header or header[:6] != 'bytes=': return - ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r] - for start, end in ranges: - try: - if not start: # bytes=-100 -> last 100 bytes - start, end = max(0, maxlen-int(end)), maxlen - elif not end: # bytes=100- -> all but the first 99 bytes - start, end = int(start), maxlen - else: # bytes=100-200 -> bytes 100-200 (inclusive) - start, end = int(start), min(int(end)+1, maxlen) - if 0 <= start < end <= maxlen: - yield start, end - except ValueError: - pass - -def _lscmp(a, b): - ''' Compares two strings in a cryptographically save way: - Runtime is not affected by length of common prefix. ''' - return not sum(0 if x==y else 1 for x, y in zip(a, b)) and len(a) == len(b) - - -def cookie_encode(data, key): - ''' Encode and sign a pickle-able object. Return a (byte) string ''' - msg = base64.b64encode(pickle.dumps(data, -1)) - sig = base64.b64encode(hmac.new(tob(key), msg).digest()) - return tob('!') + sig + tob('?') + msg - - -def cookie_decode(data, key): - ''' Verify and decode an encoded string. Return an object or None.''' - data = tob(data) - if cookie_is_encoded(data): - sig, msg = data.split(tob('?'), 1) - if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())): - return pickle.loads(base64.b64decode(msg)) - return None - - -def cookie_is_encoded(data): - ''' Return True if the argument looks like a encoded cookie.''' - return bool(data.startswith(tob('!')) and tob('?') in data) - - -def html_escape(string): - ''' Escape HTML special characters ``&<>`` and quotes ``'"``. ''' - return string.replace('&','&').replace('<','<').replace('>','>')\ - .replace('"','"').replace("'",''') - - -def html_quote(string): - ''' Escape and quote a string to be used as an HTTP attribute.''' - return '"%s"' % html_escape(string).replace('\n','%#10;')\ - .replace('\r',' ').replace('\t',' ') - - -def yieldroutes(func): - """ Return a generator for routes that match the signature (name, args) - of the func parameter. This may yield more than one route if the function - takes optional keyword arguments. The output is best described by example:: - - a() -> '/a' - b(x, y) -> '/b/:x/:y' - c(x, y=5) -> '/c/:x' and '/c/:x/:y' - d(x=5, y=6) -> '/d' and '/d/:x' and '/d/:x/:y' - """ - import inspect # Expensive module. Only import if necessary. - path = '/' + func.__name__.replace('__','/').lstrip('/') - spec = inspect.getargspec(func) - argc = len(spec[0]) - len(spec[3] or []) - path += ('/:%s' * argc) % tuple(spec[0][:argc]) - yield path - for arg in spec[0][argc:]: - path += '/:%s' % arg - yield path - - -def path_shift(script_name, path_info, shift=1): - ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa. - - :return: The modified paths. - :param script_name: The SCRIPT_NAME path. - :param script_name: The PATH_INFO path. - :param shift: The number of path fragments to shift. May be negative to - change the shift direction. (default: 1) - ''' - if shift == 0: return script_name, path_info - pathlist = path_info.strip('/').split('/') - scriptlist = script_name.strip('/').split('/') - if pathlist and pathlist[0] == '': pathlist = [] - if scriptlist and scriptlist[0] == '': scriptlist = [] - if shift > 0 and shift <= len(pathlist): - moved = pathlist[:shift] - scriptlist = scriptlist + moved - pathlist = pathlist[shift:] - elif shift < 0 and shift >= -len(scriptlist): - moved = scriptlist[shift:] - pathlist = moved + pathlist - scriptlist = scriptlist[:shift] - else: - empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO' - raise AssertionError("Cannot shift. Nothing left from %s" % empty) - new_script_name = '/' + '/'.join(scriptlist) - new_path_info = '/' + '/'.join(pathlist) - if path_info.endswith('/') and pathlist: new_path_info += '/' - return new_script_name, new_path_info - - -def validate(**vkargs): - """ - Validates and manipulates keyword arguments by user defined callables. - Handles ValueError and missing arguments by raising HTTPError(403). - """ - depr('Use route wildcard filters instead.') - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kargs): - for key, value in vkargs.items(): - if key not in kargs: - abort(403, 'Missing parameter: %s' % key) - try: - kargs[key] = value(kargs[key]) - except ValueError: - abort(403, 'Wrong parameter format for: %s' % key) - return func(*args, **kargs) - return wrapper - return decorator - - -def auth_basic(check, realm="private", text="Access denied"): - ''' Callback decorator to require HTTP auth (basic). - TODO: Add route(check_auth=...) parameter. ''' - def decorator(func): - def wrapper(*a, **ka): - user, password = request.auth or (None, None) - if user is None or not check(user, password): - response.headers['WWW-Authenticate'] = 'Basic realm="%s"' % realm - return HTTPError(401, text) - return func(*a, **ka) - return wrapper - return decorator - - -# Shortcuts for common Bottle methods. -# They all refer to the current default application. - -def make_default_app_wrapper(name): - ''' Return a callable that relays calls to the current default app. ''' - @functools.wraps(getattr(Bottle, name)) - def wrapper(*a, **ka): - return getattr(app(), name)(*a, **ka) - return wrapper - -route = make_default_app_wrapper('route') -get = make_default_app_wrapper('get') -post = make_default_app_wrapper('post') -put = make_default_app_wrapper('put') -delete = make_default_app_wrapper('delete') -error = make_default_app_wrapper('error') -mount = make_default_app_wrapper('mount') -hook = make_default_app_wrapper('hook') -install = make_default_app_wrapper('install') -uninstall = make_default_app_wrapper('uninstall') -url = make_default_app_wrapper('get_url') - - - - - - - -############################################################################### -# Server Adapter ############################################################### -############################################################################### - - -class ServerAdapter(object): - quiet = False - def __init__(self, host='127.0.0.1', port=8080, **config): - self.options = config - self.host = host - self.port = int(port) - - def run(self, handler): # pragma: no cover - pass - - def __repr__(self): - args = ', '.join(['%s=%s'%(k,repr(v)) for k, v in self.options.items()]) - return "%s(%s)" % (self.__class__.__name__, args) - - -class CGIServer(ServerAdapter): - quiet = True - def run(self, handler): # pragma: no cover - from wsgiref.handlers import CGIHandler - def fixed_environ(environ, start_response): - environ.setdefault('PATH_INFO', '') - return handler(environ, start_response) - CGIHandler().run(fixed_environ) - - -class FlupFCGIServer(ServerAdapter): - def run(self, handler): # pragma: no cover - import flup.server.fcgi - self.options.setdefault('bindAddress', (self.host, self.port)) - flup.server.fcgi.WSGIServer(handler, **self.options).run() - - -class WSGIRefServer(ServerAdapter): - def run(self, handler): # pragma: no cover - from wsgiref.simple_server import make_server, WSGIRequestHandler - if self.quiet: - class QuietHandler(WSGIRequestHandler): - def log_request(*args, **kw): pass - self.options['handler_class'] = QuietHandler - srv = make_server(self.host, self.port, handler, **self.options) - srv.serve_forever() - - -class CherryPyServer(ServerAdapter): - def run(self, handler): # pragma: no cover - from cherrypy import wsgiserver - server = wsgiserver.CherryPyWSGIServer((self.host, self.port), handler) - try: - server.start() - finally: - server.stop() - - -class WaitressServer(ServerAdapter): - def run(self, handler): - from waitress import serve - serve(handler, host=self.host, port=self.port) - - -class PasteServer(ServerAdapter): - def run(self, handler): # pragma: no cover - from paste import httpserver - if not self.quiet: - from paste.translogger import TransLogger - handler = TransLogger(handler) - httpserver.serve(handler, host=self.host, port=str(self.port), - **self.options) - - -class MeinheldServer(ServerAdapter): - def run(self, handler): - from meinheld import server - server.listen((self.host, self.port)) - server.run(handler) - - -class FapwsServer(ServerAdapter): - """ Extremely fast webserver using libev. See http://www.fapws.org/ """ - def run(self, handler): # pragma: no cover - import fapws._evwsgi as evwsgi - from fapws import base, config - port = self.port - if float(config.SERVER_IDENT[-2:]) > 0.4: - # fapws3 silently changed its API in 0.5 - port = str(port) - evwsgi.start(self.host, port) - # fapws3 never releases the GIL. Complain upstream. I tried. No luck. - if 'BOTTLE_CHILD' in os.environ and not self.quiet: - _stderr("WARNING: Auto-reloading does not work with Fapws3.\n") - _stderr(" (Fapws3 breaks python thread support)\n") - evwsgi.set_base_module(base) - def app(environ, start_response): - environ['wsgi.multiprocess'] = False - return handler(environ, start_response) - evwsgi.wsgi_cb(('', app)) - evwsgi.run() - - -class TornadoServer(ServerAdapter): - """ The super hyped asynchronous server by facebook. Untested. """ - def run(self, handler): # pragma: no cover - import tornado.wsgi, tornado.httpserver, tornado.ioloop - container = tornado.wsgi.WSGIContainer(handler) - server = tornado.httpserver.HTTPServer(container) - server.listen(port=self.port) - tornado.ioloop.IOLoop.instance().start() - - -class AppEngineServer(ServerAdapter): - """ Adapter for Google App Engine. """ - quiet = True - def run(self, handler): - from google.appengine.ext.webapp import util - # A main() function in the handler script enables 'App Caching'. - # Lets makes sure it is there. This _really_ improves performance. - module = sys.modules.get('__main__') - if module and not hasattr(module, 'main'): - module.main = lambda: util.run_wsgi_app(handler) - util.run_wsgi_app(handler) - - -class TwistedServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from twisted.web import server, wsgi - from twisted.python.threadpool import ThreadPool - from twisted.internet import reactor - thread_pool = ThreadPool() - thread_pool.start() - reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop) - factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler)) - reactor.listenTCP(self.port, factory, interface=self.host) - reactor.run() - - -class DieselServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from diesel.protocols.wsgi import WSGIApplication - app = WSGIApplication(handler, port=self.port) - app.run() - - -class GeventServer(ServerAdapter): - """ Untested. Options: - - * `monkey` (default: True) fixes the stdlib to use greenthreads. - * `fast` (default: False) uses libevent's http server, but has some - issues: No streaming, no pipelining, no SSL. - """ - def run(self, handler): - from gevent import wsgi as wsgi_fast, pywsgi, monkey, local - if self.options.get('monkey', True): - if not threading.local is local.local: monkey.patch_all() - wsgi = wsgi_fast if self.options.get('fast') else pywsgi - wsgi.WSGIServer((self.host, self.port), handler).serve_forever() - - -class GunicornServer(ServerAdapter): - """ Untested. See http://gunicorn.org/configure.html for options. """ - def run(self, handler): - from gunicorn.app.base import Application - - config = {'bind': "%s:%d" % (self.host, int(self.port))} - config.update(self.options) - - class GunicornApplication(Application): - def init(self, parser, opts, args): - return config - - def load(self): - return handler - - GunicornApplication().run() - - -class EventletServer(ServerAdapter): - """ Untested """ - def run(self, handler): - from eventlet import wsgi, listen - try: - wsgi.server(listen((self.host, self.port)), handler, - log_output=(not self.quiet)) - except TypeError: - # Fallback, if we have old version of eventlet - wsgi.server(listen((self.host, self.port)), handler) - - -class RocketServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from rocket import Rocket - server = Rocket((self.host, self.port), 'wsgi', { 'wsgi_app' : handler }) - server.start() - - -class BjoernServer(ServerAdapter): - """ Fast server written in C: https://github.com/jonashaag/bjoern """ - def run(self, handler): - from bjoern import run - run(handler, self.host, self.port) - - -class AutoServer(ServerAdapter): - """ Untested. """ - adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer, WSGIRefServer] - def run(self, handler): - for sa in self.adapters: - try: - return sa(self.host, self.port, **self.options).run(handler) - except ImportError: - pass - -server_names = { - 'cgi': CGIServer, - 'flup': FlupFCGIServer, - 'wsgiref': WSGIRefServer, - 'waitress': WaitressServer, - 'cherrypy': CherryPyServer, - 'paste': PasteServer, - 'fapws3': FapwsServer, - 'tornado': TornadoServer, - 'gae': AppEngineServer, - 'twisted': TwistedServer, - 'diesel': DieselServer, - 'meinheld': MeinheldServer, - 'gunicorn': GunicornServer, - 'eventlet': EventletServer, - 'gevent': GeventServer, - 'rocket': RocketServer, - 'bjoern' : BjoernServer, - 'auto': AutoServer, -} - - - - - - -############################################################################### -# Application Control ########################################################## -############################################################################### - - -def load(target, **namespace): - """ Import a module or fetch an object from a module. - - * ``package.module`` returns `module` as a module object. - * ``pack.mod:name`` returns the module variable `name` from `pack.mod`. - * ``pack.mod:func()`` calls `pack.mod.func()` and returns the result. - - The last form accepts not only function calls, but any type of - expression. Keyword arguments passed to this function are available as - local variables. Example: ``import_string('re:compile(x)', x='[a-z]')`` - """ - module, target = target.split(":", 1) if ':' in target else (target, None) - if module not in sys.modules: __import__(module) - if not target: return sys.modules[module] - if target.isalnum(): return getattr(sys.modules[module], target) - package_name = module.split('.')[0] - namespace[package_name] = sys.modules[package_name] - return eval('%s.%s' % (module, target), namespace) - - -def load_app(target): - """ Load a bottle application from a module and make sure that the import - does not affect the current default application, but returns a separate - application object. See :func:`load` for the target parameter. """ - global NORUN; NORUN, nr_old = True, NORUN - try: - tmp = default_app.push() # Create a new "default application" - rv = load(target) # Import the target module - return rv if callable(rv) else tmp - finally: - default_app.remove(tmp) # Remove the temporary added default application - NORUN = nr_old - -_debug = debug -def run(app=None, server='wsgiref', host='127.0.0.1', port=8080, - interval=1, reloader=False, quiet=False, plugins=None, - debug=False, **kargs): - """ Start a server instance. This method blocks until the server terminates. - - :param app: WSGI application or target string supported by - :func:`load_app`. (default: :func:`default_app`) - :param server: Server adapter to use. See :data:`server_names` keys - for valid names or pass a :class:`ServerAdapter` subclass. - (default: `wsgiref`) - :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on - all interfaces including the external one. (default: 127.0.0.1) - :param port: Server port to bind to. Values below 1024 require root - privileges. (default: 8080) - :param reloader: Start auto-reloading server? (default: False) - :param interval: Auto-reloader interval in seconds (default: 1) - :param quiet: Suppress output to stdout and stderr? (default: False) - :param options: Options passed to the server adapter. - """ - if NORUN: return - if reloader and not os.environ.get('BOTTLE_CHILD'): - try: - lockfile = None - fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock') - os.close(fd) # We only need this file to exist. We never write to it - while os.path.exists(lockfile): - args = [sys.executable] + sys.argv - environ = os.environ.copy() - environ['BOTTLE_CHILD'] = 'true' - environ['BOTTLE_LOCKFILE'] = lockfile - p = subprocess.Popen(args, env=environ) - while p.poll() is None: # Busy wait... - os.utime(lockfile, None) # I am alive! - time.sleep(interval) - if p.poll() != 3: - if os.path.exists(lockfile): os.unlink(lockfile) - sys.exit(p.poll()) - except KeyboardInterrupt: - pass - finally: - if os.path.exists(lockfile): - os.unlink(lockfile) - return - - try: - _debug(debug) - app = app or default_app() - if isinstance(app, basestring): - app = load_app(app) - if not callable(app): - raise ValueError("Application is not callable: %r" % app) - - for plugin in plugins or []: - app.install(plugin) - - if server in server_names: - server = server_names.get(server) - if isinstance(server, basestring): - server = load(server) - if isinstance(server, type): - server = server(host=host, port=port, **kargs) - if not isinstance(server, ServerAdapter): - raise ValueError("Unknown or unsupported server: %r" % server) - - server.quiet = server.quiet or quiet - if not server.quiet: - _stderr("Bottle v%s server starting up (using %s)...\n" % (__version__, repr(server))) - _stderr("Listening on http://%s:%d/\n" % (server.host, server.port)) - _stderr("Hit Ctrl-C to quit.\n\n") - - if reloader: - lockfile = os.environ.get('BOTTLE_LOCKFILE') - bgcheck = FileCheckerThread(lockfile, interval) - with bgcheck: - server.run(app) - if bgcheck.status == 'reload': - sys.exit(3) - else: - server.run(app) - except KeyboardInterrupt: - pass - except (SystemExit, MemoryError): - raise - except: - if not reloader: raise - if not getattr(server, 'quiet', quiet): - print_exc() - time.sleep(interval) - sys.exit(3) - - - -class FileCheckerThread(threading.Thread): - ''' Interrupt main-thread as soon as a changed module file is detected, - the lockfile gets deleted or gets to old. ''' - - def __init__(self, lockfile, interval): - threading.Thread.__init__(self) - self.lockfile, self.interval = lockfile, interval - #: Is one of 'reload', 'error' or 'exit' - self.status = None - - def run(self): - exists = os.path.exists - mtime = lambda path: os.stat(path).st_mtime - files = dict() - - for module in list(sys.modules.values()): - path = getattr(module, '__file__', '') - if path[-4:] in ('.pyo', '.pyc'): path = path[:-1] - if path and exists(path): files[path] = mtime(path) - - while not self.status: - if not exists(self.lockfile)\ - or mtime(self.lockfile) < time.time() - self.interval - 5: - self.status = 'error' - thread.interrupt_main() - for path, lmtime in list(files.items()): - if not exists(path) or mtime(path) > lmtime: - self.status = 'reload' - thread.interrupt_main() - break - time.sleep(self.interval) - - def __enter__(self): - self.start() - - def __exit__(self, exc_type, exc_val, exc_tb): - if not self.status: self.status = 'exit' # silent exit - self.join() - return exc_type is not None and issubclass(exc_type, KeyboardInterrupt) - - - - - -############################################################################### -# Template Adapters ############################################################ -############################################################################### - - -class TemplateError(HTTPError): - def __init__(self, message): - HTTPError.__init__(self, 500, message) - - -class BaseTemplate(object): - """ Base class and minimal API for template adapters """ - extensions = ['tpl','html','thtml','stpl'] - settings = {} #used in prepare() - defaults = {} #used in render() - - def __init__(self, source=None, name=None, lookup=[], encoding='utf8', **settings): - """ Create a new template. - If the source parameter (str or buffer) is missing, the name argument - is used to guess a template filename. Subclasses can assume that - self.source and/or self.filename are set. Both are strings. - The lookup, encoding and settings parameters are stored as instance - variables. - The lookup parameter stores a list containing directory paths. - The encoding parameter should be used to decode byte strings or files. - The settings parameter contains a dict for engine-specific settings. - """ - self.name = name - self.source = source.read() if hasattr(source, 'read') else source - self.filename = source.filename if hasattr(source, 'filename') else None - self.lookup = [os.path.abspath(x) for x in lookup] - self.encoding = encoding - self.settings = self.settings.copy() # Copy from class variable - self.settings.update(settings) # Apply - if not self.source and self.name: - self.filename = self.search(self.name, self.lookup) - if not self.filename: - raise TemplateError('Template %s not found.' % repr(name)) - if not self.source and not self.filename: - raise TemplateError('No template specified.') - self.prepare(**self.settings) - - @classmethod - def search(cls, name, lookup=[]): - """ Search name in all directories specified in lookup. - First without, then with common extensions. Return first hit. """ - if os.path.isfile(name): return name - for spath in lookup: - fname = os.path.join(spath, name) - if os.path.isfile(fname): - return fname - for ext in cls.extensions: - if os.path.isfile('%s.%s' % (fname, ext)): - return '%s.%s' % (fname, ext) - - @classmethod - def global_config(cls, key, *args): - ''' This reads or sets the global settings stored in class.settings. ''' - if args: - cls.settings = cls.settings.copy() # Make settings local to class - cls.settings[key] = args[0] - else: - return cls.settings[key] - - def prepare(self, **options): - """ Run preparations (parsing, caching, ...). - It should be possible to call this again to refresh a template or to - update settings. - """ - raise NotImplementedError - - def render(self, *args, **kwargs): - """ Render the template with the specified local variables and return - a single byte or unicode string. If it is a byte string, the encoding - must match self.encoding. This method must be thread-safe! - Local variables may be provided in dictionaries (*args) - or directly, as keywords (**kwargs). - """ - raise NotImplementedError - - -class MakoTemplate(BaseTemplate): - def prepare(self, **options): - from mako.template import Template - from mako.lookup import TemplateLookup - options.update({'input_encoding':self.encoding}) - options.setdefault('format_exceptions', bool(DEBUG)) - lookup = TemplateLookup(directories=self.lookup, **options) - if self.source: - self.tpl = Template(self.source, lookup=lookup, **options) - else: - self.tpl = Template(uri=self.name, filename=self.filename, lookup=lookup, **options) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - _defaults = self.defaults.copy() - _defaults.update(kwargs) - return self.tpl.render(**_defaults) - - -class CheetahTemplate(BaseTemplate): - def prepare(self, **options): - from Cheetah.Template import Template - self.context = threading.local() - self.context.vars = {} - options['searchList'] = [self.context.vars] - if self.source: - self.tpl = Template(source=self.source, **options) - else: - self.tpl = Template(file=self.filename, **options) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - self.context.vars.update(self.defaults) - self.context.vars.update(kwargs) - out = str(self.tpl) - self.context.vars.clear() - return out - - -class Jinja2Template(BaseTemplate): - def prepare(self, filters=None, tests=None, **kwargs): - from jinja2 import Environment, FunctionLoader - if 'prefix' in kwargs: # TODO: to be removed after a while - raise RuntimeError('The keyword argument `prefix` has been removed. ' - 'Use the full jinja2 environment name line_statement_prefix instead.') - self.env = Environment(loader=FunctionLoader(self.loader), **kwargs) - if filters: self.env.filters.update(filters) - if tests: self.env.tests.update(tests) - if self.source: - self.tpl = self.env.from_string(self.source) - else: - self.tpl = self.env.get_template(self.filename) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - _defaults = self.defaults.copy() - _defaults.update(kwargs) - return self.tpl.render(**_defaults) - - def loader(self, name): - fname = self.search(name, self.lookup) - if not fname: return - with open(fname, "rb") as f: - return f.read().decode(self.encoding) - - -class SimpleTALTemplate(BaseTemplate): - ''' Deprecated, do not use. ''' - def prepare(self, **options): - depr('The SimpleTAL template handler is deprecated'\ - ' and will be removed in 0.12') - from simpletal import simpleTAL - if self.source: - self.tpl = simpleTAL.compileHTMLTemplate(self.source) - else: - with open(self.filename, 'rb') as fp: - self.tpl = simpleTAL.compileHTMLTemplate(tonat(fp.read())) - - def render(self, *args, **kwargs): - from simpletal import simpleTALES - for dictarg in args: kwargs.update(dictarg) - context = simpleTALES.Context() - for k,v in self.defaults.items(): - context.addGlobal(k, v) - for k,v in kwargs.items(): - context.addGlobal(k, v) - output = StringIO() - self.tpl.expand(context, output) - return output.getvalue() - - -class SimpleTemplate(BaseTemplate): - blocks = ('if', 'elif', 'else', 'try', 'except', 'finally', 'for', 'while', - 'with', 'def', 'class') - dedent_blocks = ('elif', 'else', 'except', 'finally') - - @lazy_attribute - def re_pytokens(cls): - ''' This matches comments and all kinds of quoted strings but does - NOT match comments (#...) within quoted strings. (trust me) ''' - return re.compile(r''' - (''(?!')|""(?!")|'{6}|"{6} # Empty strings (all 4 types) - |'(?:[^\\']|\\.)+?' # Single quotes (') - |"(?:[^\\"]|\\.)+?" # Double quotes (") - |'{3}(?:[^\\]|\\.|\n)+?'{3} # Triple-quoted strings (') - |"{3}(?:[^\\]|\\.|\n)+?"{3} # Triple-quoted strings (") - |\#.* # Comments - )''', re.VERBOSE) - - def prepare(self, escape_func=html_escape, noescape=False, **kwargs): - self.cache = {} - enc = self.encoding - self._str = lambda x: touni(x, enc) - self._escape = lambda x: escape_func(touni(x, enc)) - if noescape: - self._str, self._escape = self._escape, self._str - - @classmethod - def split_comment(cls, code): - """ Removes comments (#...) from python code. """ - if '#' not in code: return code - #: Remove comments only (leave quoted strings as they are) - subf = lambda m: '' if m.group(0)[0]=='#' else m.group(0) - return re.sub(cls.re_pytokens, subf, code) - - @cached_property - def co(self): - return compile(self.code, self.filename or '', 'exec') - - @cached_property - def code(self): - stack = [] # Current Code indentation - lineno = 0 # Current line of code - ptrbuffer = [] # Buffer for printable strings and token tuple instances - codebuffer = [] # Buffer for generated python code - multiline = dedent = oneline = False - template = self.source or open(self.filename, 'rb').read() - - def yield_tokens(line): - for i, part in enumerate(re.split(r'\{\{(.*?)\}\}', line)): - if i % 2: - if part.startswith('!'): yield 'RAW', part[1:] - else: yield 'CMD', part - else: yield 'TXT', part - - def flush(): # Flush the ptrbuffer - if not ptrbuffer: return - cline = '' - for line in ptrbuffer: - for token, value in line: - if token == 'TXT': cline += repr(value) - elif token == 'RAW': cline += '_str(%s)' % value - elif token == 'CMD': cline += '_escape(%s)' % value - cline += ', ' - cline = cline[:-2] + '\\\n' - cline = cline[:-2] - if cline[:-1].endswith('\\\\\\\\\\n'): - cline = cline[:-7] + cline[-1] # 'nobr\\\\\n' --> 'nobr' - cline = '_printlist([' + cline + '])' - del ptrbuffer[:] # Do this before calling code() again - code(cline) - - def code(stmt): - for line in stmt.splitlines(): - codebuffer.append(' ' * len(stack) + line.strip()) - - for line in template.splitlines(True): - lineno += 1 - line = touni(line, self.encoding) - sline = line.lstrip() - if lineno <= 2: - m = re.match(r"%\s*#.*coding[:=]\s*([-\w.]+)", sline) - if m: self.encoding = m.group(1) - if m: line = line.replace('coding','coding (removed)') - if sline and sline[0] == '%' and sline[:2] != '%%': - line = line.split('%',1)[1].lstrip() # Full line following the % - cline = self.split_comment(line).strip() - cmd = re.split(r'[^a-zA-Z0-9_]', cline)[0] - flush() # You are actually reading this? Good luck, it's a mess :) - if cmd in self.blocks or multiline: - cmd = multiline or cmd - dedent = cmd in self.dedent_blocks # "else:" - if dedent and not oneline and not multiline: - cmd = stack.pop() - code(line) - oneline = not cline.endswith(':') # "if 1: pass" - multiline = cmd if cline.endswith('\\') else False - if not oneline and not multiline: - stack.append(cmd) - elif cmd == 'end' and stack: - code('#end(%s) %s' % (stack.pop(), line.strip()[3:])) - elif cmd == 'include': - p = cline.split(None, 2)[1:] - if len(p) == 2: - code("_=_include(%s, _stdout, %s)" % (repr(p[0]), p[1])) - elif p: - code("_=_include(%s, _stdout)" % repr(p[0])) - else: # Empty %include -> reverse of %rebase - code("_printlist(_base)") - elif cmd == 'rebase': - p = cline.split(None, 2)[1:] - if len(p) == 2: - code("globals()['_rebase']=(%s, dict(%s))" % (repr(p[0]), p[1])) - elif p: - code("globals()['_rebase']=(%s, {})" % repr(p[0])) - else: - code(line) - else: # Line starting with text (not '%') or '%%' (escaped) - if line.strip().startswith('%%'): - line = line.replace('%%', '%', 1) - ptrbuffer.append(yield_tokens(line)) - flush() - return '\n'.join(codebuffer) + '\n' - - def subtemplate(self, _name, _stdout, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - if _name not in self.cache: - self.cache[_name] = self.__class__(name=_name, lookup=self.lookup) - return self.cache[_name].execute(_stdout, kwargs) - - def execute(self, _stdout, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - env = self.defaults.copy() - env.update({'_stdout': _stdout, '_printlist': _stdout.extend, - '_include': self.subtemplate, '_str': self._str, - '_escape': self._escape, 'get': env.get, - 'setdefault': env.setdefault, 'defined': env.__contains__}) - env.update(kwargs) - eval(self.co, env) - if '_rebase' in env: - subtpl, rargs = env['_rebase'] - rargs['_base'] = _stdout[:] #copy stdout - del _stdout[:] # clear stdout - return self.subtemplate(subtpl,_stdout,rargs) - return env - - def render(self, *args, **kwargs): - """ Render the template using keyword arguments as local variables. """ - for dictarg in args: kwargs.update(dictarg) - stdout = [] - self.execute(stdout, kwargs) - return ''.join(stdout) - - -def template(*args, **kwargs): - ''' - Get a rendered template as a string iterator. - You can use a name, a filename or a template string as first parameter. - Template rendering arguments can be passed as dictionaries - or directly (as keyword arguments). - ''' - tpl = args[0] if args else None - template_adapter = kwargs.pop('template_adapter', SimpleTemplate) - if tpl not in TEMPLATES or DEBUG: - settings = kwargs.pop('template_settings', {}) - lookup = kwargs.pop('template_lookup', TEMPLATE_PATH) - if isinstance(tpl, template_adapter): - TEMPLATES[tpl] = tpl - if settings: TEMPLATES[tpl].prepare(**settings) - elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl: - TEMPLATES[tpl] = template_adapter(source=tpl, lookup=lookup, **settings) - else: - TEMPLATES[tpl] = template_adapter(name=tpl, lookup=lookup, **settings) - if not TEMPLATES[tpl]: - abort(500, 'Template (%s) not found' % tpl) - for dictarg in args[1:]: kwargs.update(dictarg) - return TEMPLATES[tpl].render(kwargs) - -mako_template = functools.partial(template, template_adapter=MakoTemplate) -cheetah_template = functools.partial(template, template_adapter=CheetahTemplate) -jinja2_template = functools.partial(template, template_adapter=Jinja2Template) -simpletal_template = functools.partial(template, template_adapter=SimpleTALTemplate) - - -def view(tpl_name, **defaults): - ''' Decorator: renders a template for a handler. - The handler can control its behavior like that: - - - return a dict of template vars to fill out the template - - return something other than a dict and the view decorator will not - process the template, but return the handler result as is. - This includes returning a HTTPResponse(dict) to get, - for instance, JSON with autojson or other castfilters. - ''' - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - result = func(*args, **kwargs) - if isinstance(result, (dict, DictMixin)): - tplvars = defaults.copy() - tplvars.update(result) - return template(tpl_name, **tplvars) - return result - return wrapper - return decorator - -mako_view = functools.partial(view, template_adapter=MakoTemplate) -cheetah_view = functools.partial(view, template_adapter=CheetahTemplate) -jinja2_view = functools.partial(view, template_adapter=Jinja2Template) -simpletal_view = functools.partial(view, template_adapter=SimpleTALTemplate) - - - - - - -############################################################################### -# Constants and Globals ######################################################## -############################################################################### - - -TEMPLATE_PATH = ['./', './views/'] -TEMPLATES = {} -DEBUG = False -NORUN = False # If set, run() does nothing. Used by load_app() - -#: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found') -HTTP_CODES = httplib.responses -HTTP_CODES[418] = "I'm a teapot" # RFC 2324 -HTTP_CODES[428] = "Precondition Required" -HTTP_CODES[429] = "Too Many Requests" -HTTP_CODES[431] = "Request Header Fields Too Large" -HTTP_CODES[511] = "Network Authentication Required" -_HTTP_STATUS_LINES = dict((k, '%d %s'%(k,v)) for (k,v) in HTTP_CODES.items()) - -#: The default template used for error pages. Override with @error() -ERROR_PAGE_TEMPLATE = """ -%try: - %from bottle import DEBUG, HTTP_CODES, request, touni - %status_name = HTTP_CODES.get(e.status, 'Unknown').title() - - - - Error {{e.status}}: {{status_name}} - - - -

Error {{e.status}}: {{status_name}}

-

Sorry, the requested URL {{repr(request.url)}} - caused an error:

-
{{e.output}}
- %if DEBUG and e.exception: -

Exception:

-
{{repr(e.exception)}}
- %end - %if DEBUG and e.traceback: -

Traceback:

-
{{e.traceback}}
- %end - - -%except ImportError: - ImportError: Could not generate the error page. Please add bottle to - the import path. -%end -""" - -#: A thread-safe instance of :class:`LocalRequest`. If accessed from within a -#: request callback, this instance always refers to the *current* request -#: (even on a multithreaded server). -request = LocalRequest() - -#: A thread-safe instance of :class:`LocalResponse`. It is used to change the -#: HTTP response for the *current* request. -response = LocalResponse() - -#: A thread-safe namespace. Not used by Bottle. -local = threading.local() - -# Initialize app stack (create first empty Bottle app) -# BC: 0.6.4 and needed for run() -app = default_app = AppStack() -app.push() - -#: A virtual package that redirects import statements. -#: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`. -ext = _ImportRedirect(__name__+'.ext', 'bottle_%s').module - -if __name__ == '__main__': - opt, args, parser = _cmd_options, _cmd_args, _cmd_parser - if opt.version: - _stdout('Bottle %s\n'%__version__) - sys.exit(0) - if not args: - parser.print_help() - _stderr('\nError: No application specified.\n') - sys.exit(1) - - sys.path.insert(0, '.') - sys.modules.setdefault('bottle', sys.modules['__main__']) - - host, port = (opt.bind or 'localhost'), 8080 - if ':' in host: - host, port = host.rsplit(':', 1) - - run(args[0], host=host, port=port, server=opt.server, - reloader=opt.reload, plugins=opt.plugin, debug=opt.debug) - - - - -# THE END diff --git a/impattern/im/imdither/dithers/1.png b/impattern/im/imdither/dithers/1.png deleted file mode 100755 index 6e02104..0000000 Binary files a/impattern/im/imdither/dithers/1.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/10.png b/impattern/im/imdither/dithers/10.png deleted file mode 100755 index 3865589..0000000 Binary files a/impattern/im/imdither/dithers/10.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/11.png b/impattern/im/imdither/dithers/11.png deleted file mode 100755 index 2a8ca34..0000000 Binary files a/impattern/im/imdither/dithers/11.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/12.png b/impattern/im/imdither/dithers/12.png deleted file mode 100755 index 4adb35f..0000000 Binary files a/impattern/im/imdither/dithers/12.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/13.png b/impattern/im/imdither/dithers/13.png deleted file mode 100755 index 48e9362..0000000 Binary files a/impattern/im/imdither/dithers/13.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/14.png b/impattern/im/imdither/dithers/14.png deleted file mode 100755 index 867b0a9..0000000 Binary files a/impattern/im/imdither/dithers/14.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/15.png b/impattern/im/imdither/dithers/15.png deleted file mode 100755 index f37be5a..0000000 Binary files a/impattern/im/imdither/dithers/15.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/16.png b/impattern/im/imdither/dithers/16.png deleted file mode 100755 index 7994ced..0000000 Binary files a/impattern/im/imdither/dithers/16.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/17.png b/impattern/im/imdither/dithers/17.png deleted file mode 100755 index 9ffa444..0000000 Binary files a/impattern/im/imdither/dithers/17.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/18.png b/impattern/im/imdither/dithers/18.png deleted file mode 100755 index 2522e8d..0000000 Binary files a/impattern/im/imdither/dithers/18.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/19.png b/impattern/im/imdither/dithers/19.png deleted file mode 100755 index 5b4ac7a..0000000 Binary files a/impattern/im/imdither/dithers/19.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/2.png b/impattern/im/imdither/dithers/2.png deleted file mode 100755 index a6aa256..0000000 Binary files a/impattern/im/imdither/dithers/2.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/20.png b/impattern/im/imdither/dithers/20.png deleted file mode 100755 index 57a82c1..0000000 Binary files a/impattern/im/imdither/dithers/20.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/21.png b/impattern/im/imdither/dithers/21.png deleted file mode 100755 index 2b671c7..0000000 Binary files a/impattern/im/imdither/dithers/21.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/22.png b/impattern/im/imdither/dithers/22.png deleted file mode 100755 index d28f071..0000000 Binary files a/impattern/im/imdither/dithers/22.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/23.png b/impattern/im/imdither/dithers/23.png deleted file mode 100755 index 052cf6f..0000000 Binary files a/impattern/im/imdither/dithers/23.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/24.png b/impattern/im/imdither/dithers/24.png deleted file mode 100755 index 4bee000..0000000 Binary files a/impattern/im/imdither/dithers/24.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/25.png b/impattern/im/imdither/dithers/25.png deleted file mode 100755 index cbd1097..0000000 Binary files a/impattern/im/imdither/dithers/25.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/26.png b/impattern/im/imdither/dithers/26.png deleted file mode 100755 index 5fc32f4..0000000 Binary files a/impattern/im/imdither/dithers/26.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/27.png b/impattern/im/imdither/dithers/27.png deleted file mode 100755 index 18ce493..0000000 Binary files a/impattern/im/imdither/dithers/27.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/28.png b/impattern/im/imdither/dithers/28.png deleted file mode 100755 index 5924c44..0000000 Binary files a/impattern/im/imdither/dithers/28.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/29.png b/impattern/im/imdither/dithers/29.png deleted file mode 100755 index 16fc2a5..0000000 Binary files a/impattern/im/imdither/dithers/29.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/3.png b/impattern/im/imdither/dithers/3.png deleted file mode 100755 index 8c3d334..0000000 Binary files a/impattern/im/imdither/dithers/3.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/30.png b/impattern/im/imdither/dithers/30.png deleted file mode 100755 index 50c8289..0000000 Binary files a/impattern/im/imdither/dithers/30.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/31.png b/impattern/im/imdither/dithers/31.png deleted file mode 100755 index 3dd944e..0000000 Binary files a/impattern/im/imdither/dithers/31.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/32.png b/impattern/im/imdither/dithers/32.png deleted file mode 100755 index 79aeb84..0000000 Binary files a/impattern/im/imdither/dithers/32.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/33.png b/impattern/im/imdither/dithers/33.png deleted file mode 100755 index 55def42..0000000 Binary files a/impattern/im/imdither/dithers/33.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/34.png b/impattern/im/imdither/dithers/34.png deleted file mode 100755 index f7616ea..0000000 Binary files a/impattern/im/imdither/dithers/34.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/35.png b/impattern/im/imdither/dithers/35.png deleted file mode 100755 index 3ecbaaf..0000000 Binary files a/impattern/im/imdither/dithers/35.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/36.png b/impattern/im/imdither/dithers/36.png deleted file mode 100755 index 0243cf7..0000000 Binary files a/impattern/im/imdither/dithers/36.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/37.png b/impattern/im/imdither/dithers/37.png deleted file mode 100755 index 012ed79..0000000 Binary files a/impattern/im/imdither/dithers/37.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/38.png b/impattern/im/imdither/dithers/38.png deleted file mode 100755 index 9aa54b7..0000000 Binary files a/impattern/im/imdither/dithers/38.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/39.png b/impattern/im/imdither/dithers/39.png deleted file mode 100755 index e98ec53..0000000 Binary files a/impattern/im/imdither/dithers/39.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/4.png b/impattern/im/imdither/dithers/4.png deleted file mode 100755 index 473e6e7..0000000 Binary files a/impattern/im/imdither/dithers/4.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/40.png b/impattern/im/imdither/dithers/40.png deleted file mode 100755 index d7d7cba..0000000 Binary files a/impattern/im/imdither/dithers/40.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/41.png b/impattern/im/imdither/dithers/41.png deleted file mode 100755 index 5d15dd7..0000000 Binary files a/impattern/im/imdither/dithers/41.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/42.png b/impattern/im/imdither/dithers/42.png deleted file mode 100755 index dedbfe2..0000000 Binary files a/impattern/im/imdither/dithers/42.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/43.png b/impattern/im/imdither/dithers/43.png deleted file mode 100755 index dd690d6..0000000 Binary files a/impattern/im/imdither/dithers/43.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/44.png b/impattern/im/imdither/dithers/44.png deleted file mode 100755 index 12afc85..0000000 Binary files a/impattern/im/imdither/dithers/44.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/45.png b/impattern/im/imdither/dithers/45.png deleted file mode 100755 index 972933b..0000000 Binary files a/impattern/im/imdither/dithers/45.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/46.png b/impattern/im/imdither/dithers/46.png deleted file mode 100755 index 857ef06..0000000 Binary files a/impattern/im/imdither/dithers/46.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/47.png b/impattern/im/imdither/dithers/47.png deleted file mode 100755 index 4002e54..0000000 Binary files a/impattern/im/imdither/dithers/47.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/48.png b/impattern/im/imdither/dithers/48.png deleted file mode 100755 index ff785ed..0000000 Binary files a/impattern/im/imdither/dithers/48.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/49.png b/impattern/im/imdither/dithers/49.png deleted file mode 100755 index 1a8525f..0000000 Binary files a/impattern/im/imdither/dithers/49.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/5.png b/impattern/im/imdither/dithers/5.png deleted file mode 100755 index 7bd6619..0000000 Binary files a/impattern/im/imdither/dithers/5.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/50.png b/impattern/im/imdither/dithers/50.png deleted file mode 100755 index f32d908..0000000 Binary files a/impattern/im/imdither/dithers/50.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/51.png b/impattern/im/imdither/dithers/51.png deleted file mode 100755 index 20dc9ca..0000000 Binary files a/impattern/im/imdither/dithers/51.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/52.png b/impattern/im/imdither/dithers/52.png deleted file mode 100755 index 0e097fa..0000000 Binary files a/impattern/im/imdither/dithers/52.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/53.png b/impattern/im/imdither/dithers/53.png deleted file mode 100755 index eec7951..0000000 Binary files a/impattern/im/imdither/dithers/53.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/54.png b/impattern/im/imdither/dithers/54.png deleted file mode 100755 index e23a862..0000000 Binary files a/impattern/im/imdither/dithers/54.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/55.png b/impattern/im/imdither/dithers/55.png deleted file mode 100755 index d908a85..0000000 Binary files a/impattern/im/imdither/dithers/55.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/56.png b/impattern/im/imdither/dithers/56.png deleted file mode 100755 index 330b589..0000000 Binary files a/impattern/im/imdither/dithers/56.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/57.png b/impattern/im/imdither/dithers/57.png deleted file mode 100755 index d9587f3..0000000 Binary files a/impattern/im/imdither/dithers/57.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/58.png b/impattern/im/imdither/dithers/58.png deleted file mode 100755 index 0ccfba0..0000000 Binary files a/impattern/im/imdither/dithers/58.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/59.png b/impattern/im/imdither/dithers/59.png deleted file mode 100755 index 673a3b7..0000000 Binary files a/impattern/im/imdither/dithers/59.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/6.png b/impattern/im/imdither/dithers/6.png deleted file mode 100755 index a3b8272..0000000 Binary files a/impattern/im/imdither/dithers/6.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/60.png b/impattern/im/imdither/dithers/60.png deleted file mode 100755 index e8ea7a6..0000000 Binary files a/impattern/im/imdither/dithers/60.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/61.png b/impattern/im/imdither/dithers/61.png deleted file mode 100755 index 4a8f42c..0000000 Binary files a/impattern/im/imdither/dithers/61.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/62.png b/impattern/im/imdither/dithers/62.png deleted file mode 100755 index 491e211..0000000 Binary files a/impattern/im/imdither/dithers/62.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/63.png b/impattern/im/imdither/dithers/63.png deleted file mode 100755 index 456203e..0000000 Binary files a/impattern/im/imdither/dithers/63.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/64.png b/impattern/im/imdither/dithers/64.png deleted file mode 100755 index 363c9e1..0000000 Binary files a/impattern/im/imdither/dithers/64.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/65.png b/impattern/im/imdither/dithers/65.png deleted file mode 100755 index 40b3ab0..0000000 Binary files a/impattern/im/imdither/dithers/65.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/66.png b/impattern/im/imdither/dithers/66.png deleted file mode 100755 index 4c46c0d..0000000 Binary files a/impattern/im/imdither/dithers/66.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/67.png b/impattern/im/imdither/dithers/67.png deleted file mode 100755 index 62f44fa..0000000 Binary files a/impattern/im/imdither/dithers/67.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/68.png b/impattern/im/imdither/dithers/68.png deleted file mode 100755 index 32bd5bc..0000000 Binary files a/impattern/im/imdither/dithers/68.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/69.png b/impattern/im/imdither/dithers/69.png deleted file mode 100755 index 35b5d56..0000000 Binary files a/impattern/im/imdither/dithers/69.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/7.png b/impattern/im/imdither/dithers/7.png deleted file mode 100755 index 3e01636..0000000 Binary files a/impattern/im/imdither/dithers/7.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/70.png b/impattern/im/imdither/dithers/70.png deleted file mode 100755 index a6771e5..0000000 Binary files a/impattern/im/imdither/dithers/70.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/71.png b/impattern/im/imdither/dithers/71.png deleted file mode 100755 index 649c43f..0000000 Binary files a/impattern/im/imdither/dithers/71.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/72.png b/impattern/im/imdither/dithers/72.png deleted file mode 100755 index d51cde8..0000000 Binary files a/impattern/im/imdither/dithers/72.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/73.png b/impattern/im/imdither/dithers/73.png deleted file mode 100755 index 79d7585..0000000 Binary files a/impattern/im/imdither/dithers/73.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/74.png b/impattern/im/imdither/dithers/74.png deleted file mode 100755 index 9e91e73..0000000 Binary files a/impattern/im/imdither/dithers/74.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/75.png b/impattern/im/imdither/dithers/75.png deleted file mode 100755 index 8702a1e..0000000 Binary files a/impattern/im/imdither/dithers/75.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/76.png b/impattern/im/imdither/dithers/76.png deleted file mode 100755 index 618f42a..0000000 Binary files a/impattern/im/imdither/dithers/76.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/77.png b/impattern/im/imdither/dithers/77.png deleted file mode 100755 index a2b5966..0000000 Binary files a/impattern/im/imdither/dithers/77.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/78.png b/impattern/im/imdither/dithers/78.png deleted file mode 100755 index 82867f8..0000000 Binary files a/impattern/im/imdither/dithers/78.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/79.png b/impattern/im/imdither/dithers/79.png deleted file mode 100755 index 3bbd78d..0000000 Binary files a/impattern/im/imdither/dithers/79.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/8.png b/impattern/im/imdither/dithers/8.png deleted file mode 100755 index 73f4d43..0000000 Binary files a/impattern/im/imdither/dithers/8.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/80.png b/impattern/im/imdither/dithers/80.png deleted file mode 100755 index 810fba6..0000000 Binary files a/impattern/im/imdither/dithers/80.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/81.png b/impattern/im/imdither/dithers/81.png deleted file mode 100755 index 92fa744..0000000 Binary files a/impattern/im/imdither/dithers/81.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/82.png b/impattern/im/imdither/dithers/82.png deleted file mode 100755 index bc30bcf..0000000 Binary files a/impattern/im/imdither/dithers/82.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/83.png b/impattern/im/imdither/dithers/83.png deleted file mode 100755 index caddb63..0000000 Binary files a/impattern/im/imdither/dithers/83.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/84.png b/impattern/im/imdither/dithers/84.png deleted file mode 100755 index 4176f9a..0000000 Binary files a/impattern/im/imdither/dithers/84.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/85.png b/impattern/im/imdither/dithers/85.png deleted file mode 100755 index 8215f71..0000000 Binary files a/impattern/im/imdither/dithers/85.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/86.png b/impattern/im/imdither/dithers/86.png deleted file mode 100755 index c85e4f7..0000000 Binary files a/impattern/im/imdither/dithers/86.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/87.png b/impattern/im/imdither/dithers/87.png deleted file mode 100755 index 2933559..0000000 Binary files a/impattern/im/imdither/dithers/87.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/88.png b/impattern/im/imdither/dithers/88.png deleted file mode 100755 index 7793e19..0000000 Binary files a/impattern/im/imdither/dithers/88.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/89.png b/impattern/im/imdither/dithers/89.png deleted file mode 100755 index 1523885..0000000 Binary files a/impattern/im/imdither/dithers/89.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/9.png b/impattern/im/imdither/dithers/9.png deleted file mode 100755 index 6741ffd..0000000 Binary files a/impattern/im/imdither/dithers/9.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/90.png b/impattern/im/imdither/dithers/90.png deleted file mode 100755 index e001830..0000000 Binary files a/impattern/im/imdither/dithers/90.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/91.png b/impattern/im/imdither/dithers/91.png deleted file mode 100755 index e6e87f0..0000000 Binary files a/impattern/im/imdither/dithers/91.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/92.png b/impattern/im/imdither/dithers/92.png deleted file mode 100755 index 30f150d..0000000 Binary files a/impattern/im/imdither/dithers/92.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/93.png b/impattern/im/imdither/dithers/93.png deleted file mode 100755 index fb1c648..0000000 Binary files a/impattern/im/imdither/dithers/93.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/94.png b/impattern/im/imdither/dithers/94.png deleted file mode 100755 index 171a461..0000000 Binary files a/impattern/im/imdither/dithers/94.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/95.png b/impattern/im/imdither/dithers/95.png deleted file mode 100755 index 27f0f06..0000000 Binary files a/impattern/im/imdither/dithers/95.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/96.png b/impattern/im/imdither/dithers/96.png deleted file mode 100755 index 11114ec..0000000 Binary files a/impattern/im/imdither/dithers/96.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a1.png b/impattern/im/imdither/dithers/a1.png deleted file mode 100755 index cab1780..0000000 Binary files a/impattern/im/imdither/dithers/a1.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a10.png b/impattern/im/imdither/dithers/a10.png deleted file mode 100755 index 193ff24..0000000 Binary files a/impattern/im/imdither/dithers/a10.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a11.png b/impattern/im/imdither/dithers/a11.png deleted file mode 100755 index a211a04..0000000 Binary files a/impattern/im/imdither/dithers/a11.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a12.png b/impattern/im/imdither/dithers/a12.png deleted file mode 100755 index 0c493b9..0000000 Binary files a/impattern/im/imdither/dithers/a12.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a13.png b/impattern/im/imdither/dithers/a13.png deleted file mode 100755 index bfa653c..0000000 Binary files a/impattern/im/imdither/dithers/a13.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a14.png b/impattern/im/imdither/dithers/a14.png deleted file mode 100755 index 10d7b13..0000000 Binary files a/impattern/im/imdither/dithers/a14.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a15.png b/impattern/im/imdither/dithers/a15.png deleted file mode 100755 index 2381078..0000000 Binary files a/impattern/im/imdither/dithers/a15.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a16.png b/impattern/im/imdither/dithers/a16.png deleted file mode 100755 index 9636ee5..0000000 Binary files a/impattern/im/imdither/dithers/a16.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a17.png b/impattern/im/imdither/dithers/a17.png deleted file mode 100755 index 9bc91db..0000000 Binary files a/impattern/im/imdither/dithers/a17.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a18.png b/impattern/im/imdither/dithers/a18.png deleted file mode 100755 index a2e2928..0000000 Binary files a/impattern/im/imdither/dithers/a18.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a19.png b/impattern/im/imdither/dithers/a19.png deleted file mode 100755 index 7d45270..0000000 Binary files a/impattern/im/imdither/dithers/a19.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a2.png b/impattern/im/imdither/dithers/a2.png deleted file mode 100755 index 7d9c7d9..0000000 Binary files a/impattern/im/imdither/dithers/a2.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a20.png b/impattern/im/imdither/dithers/a20.png deleted file mode 100755 index f670cf5..0000000 Binary files a/impattern/im/imdither/dithers/a20.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a21.png b/impattern/im/imdither/dithers/a21.png deleted file mode 100755 index 14aac9b..0000000 Binary files a/impattern/im/imdither/dithers/a21.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a22.png b/impattern/im/imdither/dithers/a22.png deleted file mode 100755 index e9f8a43..0000000 Binary files a/impattern/im/imdither/dithers/a22.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a23.png b/impattern/im/imdither/dithers/a23.png deleted file mode 100755 index bbdaa29..0000000 Binary files a/impattern/im/imdither/dithers/a23.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a24.png b/impattern/im/imdither/dithers/a24.png deleted file mode 100755 index 0746ced..0000000 Binary files a/impattern/im/imdither/dithers/a24.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a25.png b/impattern/im/imdither/dithers/a25.png deleted file mode 100755 index 2f23bdd..0000000 Binary files a/impattern/im/imdither/dithers/a25.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a26.png b/impattern/im/imdither/dithers/a26.png deleted file mode 100755 index 0e21976..0000000 Binary files a/impattern/im/imdither/dithers/a26.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a27.png b/impattern/im/imdither/dithers/a27.png deleted file mode 100755 index 0c11122..0000000 Binary files a/impattern/im/imdither/dithers/a27.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a28.png b/impattern/im/imdither/dithers/a28.png deleted file mode 100755 index 4469d65..0000000 Binary files a/impattern/im/imdither/dithers/a28.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a29.png b/impattern/im/imdither/dithers/a29.png deleted file mode 100755 index e956157..0000000 Binary files a/impattern/im/imdither/dithers/a29.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a3.png b/impattern/im/imdither/dithers/a3.png deleted file mode 100755 index 41cd44f..0000000 Binary files a/impattern/im/imdither/dithers/a3.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a30.png b/impattern/im/imdither/dithers/a30.png deleted file mode 100755 index 9058a6d..0000000 Binary files a/impattern/im/imdither/dithers/a30.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a31.png b/impattern/im/imdither/dithers/a31.png deleted file mode 100755 index 6692715..0000000 Binary files a/impattern/im/imdither/dithers/a31.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a32.png b/impattern/im/imdither/dithers/a32.png deleted file mode 100755 index daf16c1..0000000 Binary files a/impattern/im/imdither/dithers/a32.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a33.png b/impattern/im/imdither/dithers/a33.png deleted file mode 100755 index b3cee15..0000000 Binary files a/impattern/im/imdither/dithers/a33.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a34.png b/impattern/im/imdither/dithers/a34.png deleted file mode 100755 index 0557dae..0000000 Binary files a/impattern/im/imdither/dithers/a34.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a35.png b/impattern/im/imdither/dithers/a35.png deleted file mode 100755 index 66d88c9..0000000 Binary files a/impattern/im/imdither/dithers/a35.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a36.png b/impattern/im/imdither/dithers/a36.png deleted file mode 100755 index 7abb51b..0000000 Binary files a/impattern/im/imdither/dithers/a36.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a37.png b/impattern/im/imdither/dithers/a37.png deleted file mode 100755 index f30a1ce..0000000 Binary files a/impattern/im/imdither/dithers/a37.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a38.png b/impattern/im/imdither/dithers/a38.png deleted file mode 100755 index 3e03424..0000000 Binary files a/impattern/im/imdither/dithers/a38.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a39.png b/impattern/im/imdither/dithers/a39.png deleted file mode 100755 index a802ae0..0000000 Binary files a/impattern/im/imdither/dithers/a39.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a4.png b/impattern/im/imdither/dithers/a4.png deleted file mode 100755 index 943ecac..0000000 Binary files a/impattern/im/imdither/dithers/a4.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a40.png b/impattern/im/imdither/dithers/a40.png deleted file mode 100755 index 7e3f149..0000000 Binary files a/impattern/im/imdither/dithers/a40.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a41.png b/impattern/im/imdither/dithers/a41.png deleted file mode 100755 index 95a7558..0000000 Binary files a/impattern/im/imdither/dithers/a41.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a5.png b/impattern/im/imdither/dithers/a5.png deleted file mode 100755 index c0d0801..0000000 Binary files a/impattern/im/imdither/dithers/a5.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a6.png b/impattern/im/imdither/dithers/a6.png deleted file mode 100755 index 414ebb4..0000000 Binary files a/impattern/im/imdither/dithers/a6.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a7.png b/impattern/im/imdither/dithers/a7.png deleted file mode 100755 index e93e21f..0000000 Binary files a/impattern/im/imdither/dithers/a7.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a8.png b/impattern/im/imdither/dithers/a8.png deleted file mode 100755 index 8fa69bb..0000000 Binary files a/impattern/im/imdither/dithers/a8.png and /dev/null differ diff --git a/impattern/im/imdither/dithers/a9.png b/impattern/im/imdither/dithers/a9.png deleted file mode 100755 index 98c81ad..0000000 Binary files a/impattern/im/imdither/dithers/a9.png and /dev/null differ diff --git a/impattern/im/imdither/downloader.pyc b/impattern/im/imdither/downloader.pyc deleted file mode 100755 index 4dd0f3f..0000000 Binary files a/impattern/im/imdither/downloader.pyc and /dev/null differ diff --git a/impattern/im/imdither/gallery_isotope_config.js?v b/impattern/im/imdither/gallery_isotope_config.js?v deleted file mode 100644 index ab622a9..0000000 --- a/impattern/im/imdither/gallery_isotope_config.js?v +++ /dev/null @@ -1,186 +0,0 @@ - -$.Isotope.prototype._getCenteredMasonryColumns = function() { - this.width = this.element.width(); - - var parentWidth = this.element.parent().width(); - - // i.e. options.masonry && options.masonry.columnWidth - var colW = this.options.masonry && this.options.masonry.columnWidth || - // or use the size of the first item - this.$filteredAtoms.outerWidth(true) || - // if there's no items, use size of container - parentWidth; - - var cols = Math.floor( parentWidth / colW ); - cols = Math.max( cols, 1 ); - - // i.e. this.masonry.cols = .... - this.masonry.cols = cols; - // i.e. this.masonry.columnWidth = ... - this.masonry.columnWidth = colW; - }; - - $.Isotope.prototype._masonryReset = function() { - // layout-specific props - this.masonry = {}; - // FIXME shouldn't have to call this again - this._getCenteredMasonryColumns(); - var i = this.masonry.cols; - this.masonry.colYs = []; - while (i--) { - this.masonry.colYs.push( 0 ); - } - }; - - $.Isotope.prototype._masonryResizeChanged = function() { - var prevColCount = this.masonry.cols; - // get updated colCount - this._getCenteredMasonryColumns(); - return ( this.masonry.cols !== prevColCount ); - }; - - $.Isotope.prototype._masonryGetContainerSize = function() { - var unusedCols = 0, - i = this.masonry.cols; - // count unused columns - while ( --i ) { - if ( this.masonry.colYs[i] !== 0 ) { - break; - } - unusedCols++; - } - - return { - height : Math.max.apply( Math, this.masonry.colYs ), - // fit container to columns that have been used; - width : (this.masonry.cols - unusedCols) * this.masonry.columnWidth - }; - }; - - -//and once the jquery has loaded... -$(function(){ - - var $container = $('#images'); - - - // add randomish size classes - $container.find('.pb').each(function(){ - var $this = $(this); - - number = parseInt( $this.height(), 10 ); - if ( number % 7 % 2 === 1 ) { - $this.addClass('width2'); - } - if ( number % 3 === 0 ) { - $this.addClass('height2'); - } - }); - - $container.isotope({ - itemSelector : '.pb', - sortAscending : false, - masonry : { - columnWidth : 120 - }, - getSortData : { - date : function( $elem ) { - src = $elem.attr('src'); - var re = /_[0-9]{10}/g; - var matches = src.match(re) - for (var i=0;inew(); -my $response = $ua->post( $URL, { } ); -my $content = $response->decoded_content(); - -my $cgi = CGI->new(); -print $cgi->header(), $content; - diff --git a/impattern/im/imdither/index.html b/impattern/im/imdither/index.html deleted file mode 100755 index fe4c2e1..0000000 --- a/impattern/im/imdither/index.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - -
- - - -
- - - - - - - - - diff --git a/impattern/im/imdither/isotope_config.js b/impattern/im/imdither/isotope_config.js deleted file mode 100644 index b90587d..0000000 --- a/impattern/im/imdither/isotope_config.js +++ /dev/null @@ -1,131 +0,0 @@ -$.Isotope.prototype._getCenteredMasonryColumns = function() { - this.width = this.element.width(); - - var parentWidth = this.element.parent().width(); - - // i.e. options.masonry && options.masonry.columnWidth - var colW = this.options.masonry && this.options.masonry.columnWidth || - // or use the size of the first item - this.$filteredAtoms.outerWidth(true) || - // if there's no items, use size of container - parentWidth; - - var cols = Math.floor( parentWidth / colW ); - cols = Math.max( cols, 1 ); - - // i.e. this.masonry.cols = .... - this.masonry.cols = cols; - // i.e. this.masonry.columnWidth = ... - this.masonry.columnWidth = colW; - }; - - $.Isotope.prototype._masonryReset = function() { - // layout-specific props - this.masonry = {}; - // FIXME shouldn't have to call this again - this._getCenteredMasonryColumns(); - var i = this.masonry.cols; - this.masonry.colYs = []; - while (i--) { - this.masonry.colYs.push( 0 ); - } - }; - - $.Isotope.prototype._masonryResizeChanged = function() { - var prevColCount = this.masonry.cols; - // get updated colCount - this._getCenteredMasonryColumns(); - return ( this.masonry.cols !== prevColCount ); - }; - - $.Isotope.prototype._masonryGetContainerSize = function() { - var unusedCols = 0, - i = this.masonry.cols; - // count unused columns - while ( --i ) { - if ( this.masonry.colYs[i] !== 0 ) { - break; - } - unusedCols++; - } - - return { - height : Math.max.apply( Math, this.masonry.colYs ), - // fit container to columns that have been used; - width : (this.masonry.cols - unusedCols) * this.masonry.columnWidth - }; - }; - - -//and once the jquery has loaded... -$(function(){ - - var $container = $('#dithers'); - - - // add randomish size classes - - $container.isotope({ - itemSelector : '.dither', - sortAscending : false, - masonry : { - columnWidth : 120 - }, - getSortData : { -// date : function( $elem ) { -// username : function( $elem ) { -// return $elem.attr('username'); -// }, -// height : function( $elem ) { -// return $elem.height(); -// }, -// width : function( $elem ) { -// return $elem.width(); -// }, -// gif : function ( $elem ) { -// src = $elem.attr('src'); -// var re = /\.gif$/; -// if (re.test(src)){ -// return 1; -// }else{ -// return 0; -// } - -// } - } - }); - var sorter = { -// date: function(){$container.isotope({sortBy: "date"})}, -// username: function(){$container.isotope({sortBy: "username"})}, -// height: function(){$container.isotope({sortBy: "height"})}, -// width: function(){$container.isotope({sortBy: "width"})}, -// gif:function(){$container.isotope({sortBy: "gif"})}, -// shuffle: function(){$container.isotope("shuffle")}, -// lombada: function(){ -// var min = 4; var max = 14; -// var limit = Math.floor(Math.random() * (max - min + 1)) + min; -// -// var count = 0; -// var t = setInterval(function(){ -// $container.isotope("shuffle") -// if (count == limit){ -// clearInterval(t); -// } -// count += 1; -// }, 100) - -// } - } - - $container.delegate( '.dither', 'click', function(){ - //FIXME add css here - $(this).toggleClass('large'); - $container.isotope('reLayout'); - }); - - // toggle variable sizes of all elements - $(document).ready(function(){ - $container.isotope('reLayout'); - }); - }); - diff --git a/impattern/im/imdither/jquery-1.10.1.min.js b/impattern/im/imdither/jquery-1.10.1.min.js deleted file mode 100755 index e407e76..0000000 --- a/impattern/im/imdither/jquery-1.10.1.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! jQuery v1.10.1 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license -//@ sourceMappingURL=jquery-1.10.1.min.map -*/ -(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.1",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=lt(),k=lt(),E=lt(),S=!1,A=function(){return 0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=bt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+xt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return At(e.replace(z,"$1"),t,n,i)}function st(e){return K.test(e+"")}function lt(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function ut(e){return e[b]=!0,e}function ct(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function pt(e,t,n){e=e.split("|");var r,i=e.length,a=n?null:t;while(i--)(r=o.attrHandle[e[i]])&&r!==t||(o.attrHandle[e[i]]=a)}function ft(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:e[t]===!0?t.toLowerCase():null}function dt(e,t){return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function ht(e){return"input"===e.nodeName.toLowerCase()?e.defaultValue:t}function gt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function mt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function yt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function vt(e){return ut(function(t){return t=+t,ut(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.parentWindow;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.frameElement&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ct(function(e){return e.innerHTML="",pt("type|href|height|width",dt,"#"===e.firstChild.getAttribute("href")),pt(B,ft,null==e.getAttribute("disabled")),e.className="i",!e.getAttribute("className")}),r.input=ct(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}),pt("value",ht,r.attributes&&r.input),r.getElementsByTagName=ct(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ct(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ct(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=st(n.querySelectorAll))&&(ct(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ct(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=st(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ct(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=st(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},r.sortDetached=ct(function(e){return 1&e.compareDocumentPosition(n.createElement("div"))}),A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return gt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?gt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:ut,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=bt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?ut(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ut(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?ut(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ut(function(e){return function(t){return at(e,t).length>0}}),contains:ut(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:ut(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:vt(function(){return[0]}),last:vt(function(e,t){return[t-1]}),eq:vt(function(e,t,n){return[0>n?n+t:n]}),even:vt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:vt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:vt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:vt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=mt(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=yt(n);function bt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function xt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function wt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function Tt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Ct(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function Nt(e,t,n,r,i,o){return r&&!r[b]&&(r=Nt(r)),i&&!i[b]&&(i=Nt(i,o)),ut(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||St(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:Ct(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=Ct(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=Ct(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function kt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=wt(function(e){return e===t},s,!0),p=wt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[wt(Tt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return Nt(l>1&&Tt(f),l>1&&xt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&kt(e.slice(l,r)),i>r&&kt(e=e.slice(r)),i>r&&xt(e))}f.push(n)}return Tt(f)}function Et(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=Ct(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?ut(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=bt(e)),n=t.length;while(n--)o=kt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Et(i,r))}return o};function St(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function At(e,t,n,i){var a,s,u,c,p,f=bt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&xt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}o.pseudos.nth=o.pseudos.eq;function jt(){}jt.prototype=o.filters=o.pseudos,o.setFilters=new jt,r.sortStable=b.split("").sort(A).join("")===b,p(),[0,0].sort(A),r.detectDuplicates=S,x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!l||i&&!u||(n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null) -}),n=s=l=u=r=o=null,t}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=x(this),l=t,u=e.match(T)||[];while(o=u[a++])l=r?l:!s.hasClass(o),s[l?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); -u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("