+ + No hay dibujo + + + + |
From 95dfe14528663923ca2a88ec928f1d8d9df2402b Mon Sep 17 00:00:00 2001
From: bai
Date: Fri, 29 Mar 2019 02:14:43 +0000
Subject: Init
---
cgi/.htaccess | 9 +
cgi/BeautifulSoup.py | 2017 +++++++
cgi/GeoIP.dat | Bin 0 -> 878459 bytes
cgi/anarkia.py | 439 ++
cgi/api.py | 392 ++
cgi/database.py | 69 +
cgi/fcgi.py | 1332 +++++
cgi/formatting.py | 425 ++
cgi/framework.py | 467 ++
cgi/geoip.py | 128 +
cgi/img.py | 416 ++
cgi/locale/es/LC_MESSAGES/weabot.mo | Bin 0 -> 17305 bytes
cgi/manage.py | 1823 ++++++
cgi/markdown.py | 2044 +++++++
cgi/oekaki.py | 176 +
cgi/post.py | 1260 ++++
cgi/proxy.txt | 3251 +++++++++++
cgi/quotes.conf | 13 +
cgi/template.py | 117 +
cgi/templates/anarkia.html | 329 ++
cgi/templates/banned.html | 34 +
cgi/templates/base_bottom.html | 3 +
cgi/templates/base_top.html | 55 +
cgi/templates/board.0.html | 230 +
cgi/templates/board.html | 264 +
cgi/templates/board.jp.html | 271 +
cgi/templates/catalog.html | 30 +
cgi/templates/error.html | 7 +
cgi/templates/exception.html | 36 +
cgi/templates/home.rss | 24 +
cgi/templates/htaccess | 24 +
cgi/templates/kako.html | 60 +
cgi/templates/manage/addboard.html | 21 +
cgi/templates/manage/bans.html | 92 +
cgi/templates/manage/boardoptions.html | 195 +
cgi/templates/manage/changepassword.html | 24 +
cgi/templates/manage/delete.html | 23 +
cgi/templates/manage/filters.html | 119 +
cgi/templates/manage/ipdelete.html | 24 +
cgi/templates/manage/ipshow.html | 73 +
cgi/templates/manage/lockboard.html | 20 +
cgi/templates/manage/login.html | 21 +
cgi/templates/manage/logs.html | 17 +
cgi/templates/manage/manage.html | 22 +
cgi/templates/manage/menu.html | 30 +
cgi/templates/manage/message.html | 8 +
cgi/templates/manage/mod.html | 96 +
cgi/templates/manage/move.html | 60 +
cgi/templates/manage/quotes.html | 12 +
cgi/templates/manage/rebuild.html | 20 +
cgi/templates/manage/recent_images.html | 24 +
cgi/templates/manage/recyclebin.html | 72 +
cgi/templates/manage/reports.html | 58 +
cgi/templates/manage/search.html | 27 +
cgi/templates/manage/staff.html | 63 +
cgi/templates/mobile/base_top.html | 14 +
cgi/templates/mobile/board.html | 55 +
cgi/templates/mobile/error.html | 6 +
cgi/templates/mobile/latest.html | 14 +
cgi/templates/mobile/newest.html | 14 +
cgi/templates/mobile/threadlist.html | 43 +
cgi/templates/mobile/txt_newthread.html | 35 +
cgi/templates/mobile/txt_thread.html | 74 +
cgi/templates/mobile/txt_threadlist.html | 26 +
cgi/templates/mod.html | 86 +
cgi/templates/navbar.html | 16 +
cgi/templates/paint.html | 79 +
cgi/templates/redirect.html | 12 +
cgi/templates/report.html | 29 +
cgi/templates/revision.html | 1 +
cgi/templates/stats.html | 163 +
cgi/templates/txt_archive.html | 104 +
cgi/templates/txt_base_top.html | 44 +
cgi/templates/txt_board.en.html | 137 +
cgi/templates/txt_board.html | 137 +
cgi/templates/txt_error.html | 50 +
cgi/templates/txt_thread.en.html | 105 +
cgi/templates/txt_thread.html | 101 +
cgi/templates/txt_threadlist.html | 67 +
cgi/tenjin.py | 2118 +++++++
cgi/tor.txt | 1140 ++++
cgi/weabot.py | 1021 ++++
static/css/buri.css | 23 +
static/css/cyber.css | 39 +
static/css/dickgirl.css | 23 +
static/css/easymodo.css | 25 +
static/css/futaba.css | 22 +
static/css/guro.css | 24 +
static/css/ib.css | 72 +
static/css/img/0back.png | Bin 0 -> 148 bytes
static/css/img/0info.png | Bin 0 -> 284 bytes
static/css/img/0pc.png | Bin 0 -> 276 bytes
static/css/img/ba.gif | Bin 0 -> 856 bytes
static/css/img/barra_dulce.png | Bin 0 -> 1266 bytes
static/css/img/bg_deportes.gif | Bin 0 -> 3584 bytes
static/css/img/bg_madera.png | Bin 0 -> 4216 bytes
static/css/img/bg_oculto.gif | Bin 0 -> 871 bytes
static/css/img/bgtb.gif | Bin 0 -> 854 bytes
static/css/img/checked.png | Bin 0 -> 110 bytes
static/css/img/cyb.png | Bin 0 -> 4844 bytes
static/css/img/cyba.png | Bin 0 -> 201 bytes
static/css/img/fondo2012.gif | Bin 0 -> 72620 bytes
static/css/img/green.gif | Bin 0 -> 37408 bytes
static/css/img/hand.png | Bin 0 -> 213 bytes
static/css/img/luz.gif | Bin 0 -> 3413 bytes
static/css/img/muro.jpg | Bin 0 -> 37725 bytes
static/css/img/nieve.png | Bin 0 -> 2394 bytes
static/css/img/picnicbdy.gif | Bin 0 -> 1049 bytes
static/css/img/picnicbg.gif | Bin 0 -> 10945 bytes
static/css/img/picnicbtm.gif | Bin 0 -> 3923 bytes
static/css/img/picnicbtn.gif | Bin 0 -> 436 bytes
static/css/img/picnicfg.gif | Bin 0 -> 1044 bytes
static/css/img/picnichr.gif | Bin 0 -> 1368 bytes
static/css/img/picnicmid.gif | Bin 0 -> 9502 bytes
static/css/img/picnicthr1.gif | Bin 0 -> 12688 bytes
static/css/img/picnicthr2.gif | Bin 0 -> 2946 bytes
static/css/img/picnicthr3.gif | Bin 0 -> 12154 bytes
static/css/img/picnictop.gif | Bin 0 -> 6939 bytes
static/css/img/scan.png | Bin 0 -> 182 bytes
static/css/img/scroller1.gif | Bin 0 -> 3456 bytes
static/css/img/tanasinn.gif | Bin 0 -> 1176 bytes
static/css/img/vndb1.jpg | Bin 0 -> 36419 bytes
static/css/img/vndb2.jpg | Bin 0 -> 4366 bytes
static/css/img/vndb3.png | Bin 0 -> 2791 bytes
static/css/kraut.css | 24 +
static/css/mobile.css | 129 +
static/css/night.css | 22 +
static/css/photon.css | 22 +
static/css/putaba.css | 46 +
static/css/red.css | 21 +
static/css/rene.css | 22 +
static/css/spc/base.css | 269 +
static/css/spc/halloween.css | 47 +
static/css/spc/layout.css | 58 +
static/css/spc/navidad.css | 161 +
static/css/spc/skeleton.css | 242 +
static/css/spc/valentin.css | 170 +
static/css/spc/valentin2.css | 177 +
static/css/txt/4am.css | 42 +
static/css/txt/amber.css | 44 +
static/css/txt/ayashii.css | 52 +
static/css/txt/baisano.css | 43 +
static/css/txt/bbs.css | 95 +
static/css/txt/bios.css | 51 +
static/css/txt/blue moon.css | 61 +
static/css/txt/ciber.css | 55 +
static/css/txt/futanari.css | 49 +
static/css/txt/headline.css | 41 +
static/css/txt/postal.css | 47 +
static/css/txt/sjis.css | 1 +
static/css/txt/ventanas.css | 48 +
static/css/vndb.css | 30 +
static/ico/1372836.gif | Bin 0 -> 374 bytes
static/ico/1k.gif | Bin 0 -> 101 bytes
static/ico/2-1.gif | Bin 0 -> 1012 bytes
static/ico/2ppa.gif | Bin 0 -> 285 bytes
static/ico/2syobo_2.gif | Bin 0 -> 270 bytes
static/ico/3-2.gif | Bin 0 -> 19802 bytes
static/ico/3.gif | Bin 0 -> 1006 bytes
static/ico/3na.gif | Bin 0 -> 324 bytes
static/ico/4-2.gif | Bin 0 -> 1107 bytes
static/ico/4248688.gif | Bin 0 -> 8947 bytes
static/ico/5007629.gif | Bin 0 -> 14263 bytes
static/ico/5296219.gif | Bin 0 -> 4028 bytes
static/ico/5ta.gif | Bin 0 -> 364 bytes
static/ico/6396408.gif | Bin 0 -> 8590 bytes
static/ico/6za.gif | Bin 0 -> 395 bytes
static/ico/8028885.gif | Bin 0 -> 41133 bytes
static/ico/8toushinnomonar16.gif | Bin 0 -> 118 bytes
static/ico/8toushinnomonar32.gif | Bin 0 -> 247 bytes
static/ico/ace.gif | Bin 0 -> 317 bytes
static/ico/af1.gif | Bin 0 -> 254 bytes
static/ico/af2.gif | Bin 0 -> 228 bytes
static/ico/ahya_xmas_2.gif | Bin 0 -> 227 bytes
static/ico/aka.gif | Bin 0 -> 307 bytes
static/ico/ame.gif | Bin 0 -> 224 bytes
static/ico/anime_buun02.gif | Bin 0 -> 2036 bytes
static/ico/anime_charhan01.gif | Bin 0 -> 2257 bytes
static/ico/anime_charhan02.gif | Bin 0 -> 10274 bytes
static/ico/anime_giko01.gif | Bin 0 -> 2183 bytes
static/ico/anime_giko04.gif | Bin 0 -> 1516 bytes
static/ico/anime_giko10.gif | Bin 0 -> 2250 bytes
static/ico/anime_giko11.gif | Bin 0 -> 2291 bytes
static/ico/anime_giko12.gif | Bin 0 -> 1760 bytes
static/ico/anime_giko13.gif | Bin 0 -> 3135 bytes
static/ico/anime_hossyu01.gif | Bin 0 -> 1300 bytes
static/ico/anime_imanouchi01.gif | Bin 0 -> 1459 bytes
static/ico/anime_iyou02.gif | Bin 0 -> 3351 bytes
static/ico/anime_jien01.gif | Bin 0 -> 3345 bytes
static/ico/anime_jien02.gif | Bin 0 -> 3403 bytes
static/ico/anime_jien03.gif | Bin 0 -> 3231 bytes
static/ico/anime_jyorujyu01.gif | Bin 0 -> 1431 bytes
static/ico/anime_jyorujyu02.gif | Bin 0 -> 1441 bytes
static/ico/anime_jyorujyu03.gif | Bin 0 -> 1450 bytes
static/ico/anime_kukkuru01.gif | Bin 0 -> 1665 bytes
static/ico/anime_kuma01.gif | Bin 0 -> 2099 bytes
static/ico/anime_kumaface01.gif | Bin 0 -> 3924 bytes
static/ico/anime_loop.gif | Bin 0 -> 20749 bytes
static/ico/anime_marara02.gif | Bin 0 -> 1925 bytes
static/ico/anime_matanki01.gif | Bin 0 -> 1392 bytes
static/ico/anime_matanki02.gif | Bin 0 -> 4616 bytes
static/ico/anime_miruna01.gif | Bin 0 -> 1567 bytes
static/ico/anime_monar02.gif | Bin 0 -> 1594 bytes
static/ico/anime_monar03.gif | Bin 0 -> 2564 bytes
static/ico/anime_monar05.gif | Bin 0 -> 5846 bytes
static/ico/anime_morara01.gif | Bin 0 -> 1576 bytes
static/ico/anime_morara02.gif | Bin 0 -> 1734 bytes
static/ico/anime_morara04.gif | Bin 0 -> 1634 bytes
static/ico/anime_nokar01.gif | Bin 0 -> 2334 bytes
static/ico/anime_okashi01.gif | Bin 0 -> 5999 bytes
static/ico/anime_okashi02.gif | Bin 0 -> 4348 bytes
static/ico/anime_onigiri04.gif | Bin 0 -> 8654 bytes
static/ico/anime_saitama01.gif | Bin 0 -> 1440 bytes
static/ico/anime_saitama02.gif | Bin 0 -> 1348 bytes
static/ico/anime_saitama03.gif | Bin 0 -> 1454 bytes
static/ico/anime_sasuga01.gif | Bin 0 -> 1546 bytes
static/ico/anime_sasuga03.gif | Bin 0 -> 9729 bytes
static/ico/anime_sasuga04.gif | Bin 0 -> 16392 bytes
static/ico/anime_shii01.gif | Bin 0 -> 11664 bytes
static/ico/anime_shii02.gif | Bin 0 -> 8324 bytes
static/ico/anime_shii03.gif | Bin 0 -> 1506 bytes
static/ico/anime_syobon01.gif | Bin 0 -> 2156 bytes
static/ico/anime_syobon03.gif | Bin 0 -> 2361 bytes
static/ico/anime_tarn01.gif | Bin 0 -> 3754 bytes
static/ico/anime_uwan01.gif | Bin 0 -> 1966 bytes
static/ico/anime_uwan02.gif | Bin 0 -> 1441 bytes
static/ico/anime_uwan03.gif | Bin 0 -> 2216 bytes
static/ico/anime_youkanman01.gif | Bin 0 -> 1915 bytes
static/ico/anime_youkanman02.gif | Bin 0 -> 1882 bytes
static/ico/anime_youkanman03.gif | Bin 0 -> 1365 bytes
static/ico/anime_zonu01.gif | Bin 0 -> 1426 bytes
static/ico/anime_zonu02.gif | Bin 0 -> 1476 bytes
static/ico/aramaki.gif | Bin 0 -> 236 bytes
static/ico/aroeri-na32.gif | Bin 0 -> 268 bytes
static/ico/asopasomaso.gif | Bin 0 -> 247 bytes
static/ico/bikyakusan32.gif | Bin 0 -> 213 bytes
static/ico/bs.gif | Bin 0 -> 378 bytes
static/ico/button1_03.gif | Bin 0 -> 218 bytes
static/ico/buun.gif | Bin 0 -> 251 bytes
static/ico/chahan.gif | Bin 0 -> 279 bytes
static/ico/dokuo1.gif | Bin 0 -> 414 bytes
static/ico/file2_01.gif | Bin 0 -> 282 bytes
static/ico/fujisan.gif | Bin 0 -> 216 bytes
static/ico/fuun.gif | Bin 0 -> 244 bytes
static/ico/gaku.gif | Bin 0 -> 196 bytes
static/ico/gaku2.gif | Bin 0 -> 252 bytes
static/ico/gaku3.gif | Bin 0 -> 199 bytes
static/ico/gekisya1.gif | Bin 0 -> 333 bytes
static/ico/giko1.gif | Bin 0 -> 407 bytes
static/ico/gikog_gomibako.gif | Bin 0 -> 233 bytes
static/ico/gikog_gyunyupack.gif | Bin 0 -> 277 bytes
static/ico/gikog_pimiento.gif | Bin 0 -> 279 bytes
static/ico/gikoinu.gif | Bin 0 -> 252 bytes
static/ico/gikoneko.gif | Bin 0 -> 997 bytes
static/ico/gikoneko2.gif | Bin 0 -> 1021 bytes
static/ico/gikoneko_1.gif | Bin 0 -> 200 bytes
static/ico/gocchin_face.gif | Bin 0 -> 256 bytes
static/ico/gomiopen.gif | Bin 0 -> 425 bytes
static/ico/goo_1.gif | Bin 0 -> 209 bytes
static/ico/goo_3.gif | Bin 0 -> 213 bytes
static/ico/gya-.gif | Bin 0 -> 129 bytes
static/ico/hagenin-shuriken.gif | Bin 0 -> 8609 bytes
static/ico/hagurumaou.gif | Bin 0 -> 283 bytes
static/ico/hikky.gif | Bin 0 -> 246 bytes
static/ico/hikky_xmas_2.gif | Bin 0 -> 250 bytes
static/ico/hyou.gif | Bin 0 -> 212 bytes
static/ico/iirasan_face.gif | Bin 0 -> 283 bytes
static/ico/imanouchi_1.gif | Bin 0 -> 232 bytes
static/ico/iyahoo.gif | Bin 0 -> 1456 bytes
static/ico/iyou.gif | Bin 0 -> 233 bytes
static/ico/jisakujien_2.gif | Bin 0 -> 181 bytes
static/ico/jisakujien_xmas.gif | Bin 0 -> 219 bytes
static/ico/kantoku1.gif | Bin 0 -> 314 bytes
static/ico/kappappa1.gif | Bin 0 -> 416 bytes
static/ico/kasa-ri.gif | Bin 0 -> 196 bytes
static/ico/kashiwamo-chi32.gif | Bin 0 -> 352 bytes
static/ico/kinokorusensei32.gif | Bin 0 -> 337 bytes
static/ico/kita_.gif | Bin 0 -> 260 bytes
static/ico/kodomona.gif | Bin 0 -> 237 bytes
static/ico/konkon_folder.gif | Bin 0 -> 373 bytes
static/ico/kossorisan.gif | Bin 0 -> 99 bytes
static/ico/kotatu.gif | Bin 0 -> 248 bytes
static/ico/kuma.gif | Bin 0 -> 3875 bytes
static/ico/kuma2.gif | Bin 0 -> 484 bytes
static/ico/maimai.gif | Bin 0 -> 327 bytes
static/ico/makotan2_folder.gif | Bin 0 -> 389 bytes
static/ico/mona.gif | Bin 0 -> 254 bytes
static/ico/mona_shiri.gif | Bin 0 -> 238 bytes
static/ico/mona_tya.gif | Bin 0 -> 298 bytes
static/ico/monaazarashi_1.gif | Bin 0 -> 168 bytes
static/ico/namaetukenai.gif | Bin 0 -> 3331 bytes
static/ico/naoruyo.gif | Bin 0 -> 257 bytes
static/ico/nida.gif | Bin 0 -> 254 bytes
static/ico/nigete.gif | Bin 0 -> 2304 bytes
static/ico/nono_ie.gif | Bin 0 -> 365 bytes
static/ico/nurupo_ga_2.gif | Bin 0 -> 1270 bytes
static/ico/onigiri_seito.gif | Bin 0 -> 288 bytes
static/ico/otiketu48.gif | Bin 0 -> 378 bytes
static/ico/pc3.gif | Bin 0 -> 288 bytes
static/ico/pgya.gif | Bin 0 -> 288 bytes
static/ico/sasuga1.gif | Bin 0 -> 15680 bytes
static/ico/seito_2.gif | Bin 0 -> 216 bytes
static/ico/soon.gif | Bin 0 -> 245 bytes
static/ico/tasukete.gif | Bin 0 -> 312 bytes
static/ico/torimasu1.gif | Bin 0 -> 317 bytes
static/ico/torimasu2.gif | Bin 0 -> 313 bytes
static/ico/u_ame.gif | Bin 0 -> 3608 bytes
static/ico/u_hoshi.gif | Bin 0 -> 3076 bytes
static/ico/u_naoruyo_bath.gif | Bin 0 -> 4552 bytes
static/ico/u_okotowari_a.gif | Bin 0 -> 6831 bytes
static/ico/u_sofa.gif | Bin 0 -> 1280 bytes
static/ico/wakannai1.gif | Bin 0 -> 304 bytes
static/ico/yakimochi.gif | Bin 0 -> 242 bytes
static/ico/youkan.gif | Bin 0 -> 254 bytes
static/ico/zonu_1.gif | Bin 0 -> 179 bytes
static/ico/zuzagiko48.gif | Bin 0 -> 368 bytes
static/img/anarkia.jpg | Bin 0 -> 35677 bytes
static/img/bai.jpg | Bin 0 -> 38030 bytes
static/img/cero.gif | Bin 0 -> 80262 bytes
static/img/default.png | Bin 0 -> 9073 bytes
static/img/juegos1.jpg | Bin 0 -> 43984 bytes
static/img/juegos2.jpg | Bin 0 -> 35649 bytes
static/img/juegos3.png | Bin 0 -> 10301 bytes
static/img/juegos4.gif | Bin 0 -> 24664 bytes
static/img/letras1.png | Bin 0 -> 52461 bytes
static/img/letras2.png | Bin 0 -> 69712 bytes
static/img/letras3.png | Bin 0 -> 97055 bytes
static/img/letras4.jpg | Bin 0 -> 46084 bytes
static/img/letras5.jpg | Bin 0 -> 50947 bytes
static/img/musica1.jpg | Bin 0 -> 24487 bytes
static/img/noticias.png | Bin 0 -> 13249 bytes
static/img/old/2d_1.jpg | Bin 0 -> 29384 bytes
static/img/old/2d_2.jpg | Bin 0 -> 36070 bytes
static/img/old/2d_3.png | Bin 0 -> 5899 bytes
static/img/old/2d_4.jpg | Bin 0 -> 29392 bytes
static/img/old/argentina1.png | Bin 0 -> 27794 bytes
static/img/old/chile1.png | Bin 0 -> 44923 bytes
static/img/old/chile2.jpg | Bin 0 -> 44202 bytes
static/img/old/g0.jpg | Bin 0 -> 39681 bytes
static/img/old/g1.jpg | Bin 0 -> 45641 bytes
static/img/old/g2.jpg | Bin 0 -> 38041 bytes
static/img/old/g3.jpg | Bin 0 -> 52934 bytes
static/img/old/g4.jpg | Bin 0 -> 36197 bytes
static/img/old/g5.jpg | Bin 0 -> 23641 bytes
static/img/old/peli.jpg | Bin 0 -> 36293 bytes
static/img/old/salon2d_3.jpg | Bin 0 -> 35496 bytes
static/img/old/salon2d_4.png | Bin 0 -> 4978 bytes
static/img/old/salon2d_5.jpg | Bin 0 -> 25550 bytes
static/img/old/zine.png | Bin 0 -> 75774 bytes
static/img/salon2d_1.png | Bin 0 -> 15409 bytes
static/img/salon2d_3.jpg | Bin 0 -> 35496 bytes
static/img/salon2d_4.png | Bin 0 -> 4978 bytes
static/img/salon2d_5.jpg | Bin 0 -> 25550 bytes
static/img/tech1.png | Bin 0 -> 7802 bytes
static/img/tech2.jpg | Bin 0 -> 36871 bytes
static/img/tech3.png | Bin 0 -> 42940 bytes
static/img/tech4.jpg | Bin 0 -> 48759 bytes
static/img/tech5.jpg | Bin 0 -> 37524 bytes
static/img/tech6.png | Bin 0 -> 17938 bytes
static/img/tv1.png | Bin 0 -> 3501 bytes
static/img/weird-al.jpg | Bin 0 -> 14523 bytes
static/img/world.gif | Bin 0 -> 100563 bytes
static/img/zonavip1.jpg | Bin 0 -> 45584 bytes
static/img/zonavip2.gif | Bin 0 -> 38269 bytes
static/img/zonavip3.png | Bin 0 -> 85601 bytes
static/img/zonavip4.jpg | Bin 0 -> 41923 bytes
static/img/zonavip5.gif | Bin 0 -> 35591 bytes
static/img/zonavip6.png | Bin 0 -> 13149 bytes
static/img/zonavip7.gif | Bin 0 -> 50028 bytes
static/img/zonavip8.png | Bin 0 -> 53463 bytes
static/img/zonavip9.gif | Bin 0 -> 19603 bytes
static/img/zonavip9.jpg | Bin 0 -> 46773 bytes
static/img/zonavip_halloween.jpg | Bin 0 -> 38379 bytes
static/img/zonavip_nav.jpg | Bin 0 -> 51171 bytes
static/js/aquiencitas.js | 168 +
static/js/autorefresh.js | 275 +
static/js/home.js | 173 +
static/js/jquery.js | 545 ++
static/js/manage.js | 22 +
static/js/mobile.js | 447 ++
static/js/paintbbs/PaintBBS-1.1.11.css | 535 ++
static/js/paintbbs/PaintBBS-1.1.11.js | 5686 ++++++++++++++++++
static/js/paintbbs/PaintBBS-1.3.4.css | 547 ++
static/js/paintbbs/PaintBBS-1.3.4.js | 6171 ++++++++++++++++++++
static/js/palette_selfy.js | 972 +++
static/js/shobon.js | 408 ++
static/js/tegaki/tegaki.css | 187 +
static/js/tegaki/tegaki.js | 1947 ++++++
static/js/weabot.js | 456 ++
static/js/weabotxt.js | 299 +
static/js/wpaint/.gitignore | 3 +
static/js/wpaint/README.md | 421 ++
static/js/wpaint/bai.js | 23 +
static/js/wpaint/demo/demo.css | 266 +
static/js/wpaint/demo/img/facebook-icon.png | Bin 0 -> 274 bytes
static/js/wpaint/demo/img/favicon.ico | Bin 0 -> 1150 bytes
.../js/wpaint/demo/img/forkme_right_darkblue.png | Bin 0 -> 7791 bytes
static/js/wpaint/demo/img/github-icon.png | Bin 0 -> 596 bytes
static/js/wpaint/demo/img/googleplus-icon.png | Bin 0 -> 522 bytes
static/js/wpaint/demo/img/linkedin-icon.png | Bin 0 -> 380 bytes
static/js/wpaint/demo/img/rss-icon.png | Bin 0 -> 521 bytes
static/js/wpaint/demo/img/stumbleupon-icon.png | Bin 0 -> 537 bytes
static/js/wpaint/demo/img/twitter-icon.png | Bin 0 -> 514 bytes
.../demo/img/websanova-logo-small-full-black.png | Bin 0 -> 1028 bytes
static/js/wpaint/demo/img/youtube-icon.png | Bin 0 -> 587 bytes
static/js/wpaint/gruntfile.js | 90 +
static/js/wpaint/index.html | 136 +
static/js/wpaint/lib/jquery.1.10.2.min.js | 6 +
static/js/wpaint/lib/jquery.ui.core.1.10.3.min.js | 4 +
.../wpaint/lib/jquery.ui.draggable.1.10.3.min.js | 4 +
static/js/wpaint/lib/jquery.ui.mouse.1.10.3.min.js | 4 +
.../js/wpaint/lib/jquery.ui.widget.1.10.3.min.js | 4 +
static/js/wpaint/lib/mixins.styl | 7 +
static/js/wpaint/lib/wColorPicker.min.css | 42 +
static/js/wpaint/lib/wColorPicker.min.js | 2 +
static/js/wpaint/package.json | 25 +
.../plugins/file/img/icons-menu-main-file.png | Bin 0 -> 835 bytes
.../plugins/file/src/wPaint.menu.main.file.js | 75 +
.../plugins/file/wPaint.menu.main.file.min.js | 1 +
.../js/wpaint/plugins/main/img/cursor-bucket.png | Bin 0 -> 450 bytes
.../wpaint/plugins/main/img/cursor-crosshair.png | Bin 0 -> 208 bytes
.../js/wpaint/plugins/main/img/cursor-dropper.png | Bin 0 -> 403 bytes
.../js/wpaint/plugins/main/img/cursor-eraser1.png | Bin 0 -> 193 bytes
.../js/wpaint/plugins/main/img/cursor-eraser10.png | Bin 0 -> 247 bytes
.../js/wpaint/plugins/main/img/cursor-eraser2.png | Bin 0 -> 200 bytes
.../js/wpaint/plugins/main/img/cursor-eraser3.png | Bin 0 -> 206 bytes
.../js/wpaint/plugins/main/img/cursor-eraser4.png | Bin 0 -> 209 bytes
.../js/wpaint/plugins/main/img/cursor-eraser5.png | Bin 0 -> 225 bytes
.../js/wpaint/plugins/main/img/cursor-eraser6.png | Bin 0 -> 229 bytes
.../js/wpaint/plugins/main/img/cursor-eraser7.png | Bin 0 -> 236 bytes
.../js/wpaint/plugins/main/img/cursor-eraser8.png | Bin 0 -> 240 bytes
.../js/wpaint/plugins/main/img/cursor-eraser9.png | Bin 0 -> 244 bytes
.../js/wpaint/plugins/main/img/cursor-pencil.png | Bin 0 -> 449 bytes
.../wpaint/plugins/main/img/icon-group-arrow.png | Bin 0 -> 208 bytes
.../js/wpaint/plugins/main/img/icons-menu-main.png | Bin 0 -> 2836 bytes
static/js/wpaint/plugins/main/src/fillArea.min.js | 1 +
.../js/wpaint/plugins/main/src/wPaint.menu.main.js | 338 ++
.../js/wpaint/plugins/main/wPaint.menu.main.min.js | 1 +
.../plugins/shapes/img/icons-menu-main-shapes.png | Bin 0 -> 903 bytes
static/js/wpaint/plugins/shapes/src/shapes.min.js | 1 +
.../plugins/shapes/src/wPaint.menu.main.shapes.js | 207 +
.../plugins/shapes/wPaint.menu.main.shapes.min.js | 1 +
.../js/wpaint/plugins/text/img/icons-menu-text.png | Bin 0 -> 802 bytes
.../js/wpaint/plugins/text/src/wPaint.menu.text.js | 227 +
.../js/wpaint/plugins/text/wPaint.menu.text.min.js | 1 +
static/js/wpaint/src/wPaint.css | 348 ++
static/js/wpaint/src/wPaint.js | 1181 ++++
static/js/wpaint/src/wPaint.utils.js | 70 +
static/js/wpaint/test/dev.html | 123 +
static/js/wpaint/test/fullscreen.html | 79 +
static/js/wpaint/test/upload.php | 11 +
static/js/wpaint/test/uploads/test1.png | Bin 0 -> 432 bytes
static/js/wpaint/test/uploads/test2.png | Bin 0 -> 462 bytes
static/js/wpaint/test/uploads/test3.png | Bin 0 -> 454 bytes
static/js/wpaint/test/uploads/wPaint.png | Bin 0 -> 3096 bytes
static/js/wpaint/wPaint.jquery.json | 38 +
static/js/wpaint/wPaint.min.css | 66 +
static/js/wpaint/wPaint.min.js | 1 +
static/meta/bbs.png | Bin 0 -> 7192 bytes
static/meta/faq_1.png | Bin 0 -> 18358 bytes
static/meta/faq_2.png | Bin 0 -> 12504 bytes
static/meta/faq_3.png | Bin 0 -> 19818 bytes
static/meta/ib.png | Bin 0 -> 10709 bytes
static/meta/portada_6.jpg | Bin 0 -> 368440 bytes
static/meta/portada_7.jpg | Bin 0 -> 51433 bytes
static/meta/portada_8.jpg | Bin 0 -> 266858 bytes
static/meta/portada_asp.gif | Bin 0 -> 441933 bytes
static/meta/portada_cap1.jpg | Bin 0 -> 101119 bytes
static/meta/portada_orig5.jpg | Bin 0 -> 67175 bytes
static/meta/portada_toesca.jpg | Bin 0 -> 35123 bytes
static/meta/portadaphil.jpg | Bin 0 -> 102067 bytes
static/meta/primeraportada.png | Bin 0 -> 385869 bytes
static/meta/sanvalentin2013.jpg | Bin 0 -> 98509 bytes
static/meta/welcome.gif | Bin 0 -> 238888 bytes
static/meta/welcome.jpg | Bin 0 -> 28985 bytes
static/mime/epub.png | Bin 0 -> 1320 bytes
static/mime/epub_small.png | Bin 0 -> 1019 bytes
static/mime/mod.png | Bin 0 -> 1362 bytes
static/mime/mod_small.png | Bin 0 -> 1058 bytes
static/mime/pdf.png | Bin 0 -> 1264 bytes
static/mime/pdf_small.png | Bin 0 -> 984 bytes
static/mime/s3m.png | Bin 0 -> 1397 bytes
static/mime/s3m_small.png | Bin 0 -> 1104 bytes
static/mime/swf.png | Bin 0 -> 1375 bytes
static/mime/swf_small.png | Bin 0 -> 1067 bytes
static/mime/torrent.png | Bin 0 -> 1377 bytes
static/mime/torrent_small.png | Bin 0 -> 1070 bytes
static/mime/xm.png | Bin 0 -> 1318 bytes
static/mime/xm_small.png | Bin 0 -> 1011 bytes
489 files changed, 47499 insertions(+)
create mode 100644 cgi/.htaccess
create mode 100644 cgi/BeautifulSoup.py
create mode 100644 cgi/GeoIP.dat
create mode 100644 cgi/anarkia.py
create mode 100644 cgi/api.py
create mode 100644 cgi/database.py
create mode 100644 cgi/fcgi.py
create mode 100644 cgi/formatting.py
create mode 100644 cgi/framework.py
create mode 100644 cgi/geoip.py
create mode 100644 cgi/img.py
create mode 100644 cgi/locale/es/LC_MESSAGES/weabot.mo
create mode 100644 cgi/manage.py
create mode 100644 cgi/markdown.py
create mode 100644 cgi/oekaki.py
create mode 100644 cgi/post.py
create mode 100644 cgi/proxy.txt
create mode 100644 cgi/quotes.conf
create mode 100644 cgi/template.py
create mode 100644 cgi/templates/anarkia.html
create mode 100644 cgi/templates/banned.html
create mode 100644 cgi/templates/base_bottom.html
create mode 100644 cgi/templates/base_top.html
create mode 100644 cgi/templates/board.0.html
create mode 100644 cgi/templates/board.html
create mode 100644 cgi/templates/board.jp.html
create mode 100644 cgi/templates/catalog.html
create mode 100644 cgi/templates/error.html
create mode 100644 cgi/templates/exception.html
create mode 100644 cgi/templates/home.rss
create mode 100644 cgi/templates/htaccess
create mode 100644 cgi/templates/kako.html
create mode 100644 cgi/templates/manage/addboard.html
create mode 100644 cgi/templates/manage/bans.html
create mode 100644 cgi/templates/manage/boardoptions.html
create mode 100644 cgi/templates/manage/changepassword.html
create mode 100644 cgi/templates/manage/delete.html
create mode 100644 cgi/templates/manage/filters.html
create mode 100644 cgi/templates/manage/ipdelete.html
create mode 100644 cgi/templates/manage/ipshow.html
create mode 100644 cgi/templates/manage/lockboard.html
create mode 100644 cgi/templates/manage/login.html
create mode 100644 cgi/templates/manage/logs.html
create mode 100644 cgi/templates/manage/manage.html
create mode 100644 cgi/templates/manage/menu.html
create mode 100644 cgi/templates/manage/message.html
create mode 100644 cgi/templates/manage/mod.html
create mode 100644 cgi/templates/manage/move.html
create mode 100644 cgi/templates/manage/quotes.html
create mode 100644 cgi/templates/manage/rebuild.html
create mode 100644 cgi/templates/manage/recent_images.html
create mode 100644 cgi/templates/manage/recyclebin.html
create mode 100644 cgi/templates/manage/reports.html
create mode 100644 cgi/templates/manage/search.html
create mode 100644 cgi/templates/manage/staff.html
create mode 100644 cgi/templates/mobile/base_top.html
create mode 100644 cgi/templates/mobile/board.html
create mode 100644 cgi/templates/mobile/error.html
create mode 100644 cgi/templates/mobile/latest.html
create mode 100644 cgi/templates/mobile/newest.html
create mode 100644 cgi/templates/mobile/threadlist.html
create mode 100644 cgi/templates/mobile/txt_newthread.html
create mode 100644 cgi/templates/mobile/txt_thread.html
create mode 100644 cgi/templates/mobile/txt_threadlist.html
create mode 100644 cgi/templates/mod.html
create mode 100644 cgi/templates/navbar.html
create mode 100644 cgi/templates/paint.html
create mode 100644 cgi/templates/redirect.html
create mode 100644 cgi/templates/report.html
create mode 100644 cgi/templates/revision.html
create mode 100644 cgi/templates/stats.html
create mode 100644 cgi/templates/txt_archive.html
create mode 100644 cgi/templates/txt_base_top.html
create mode 100644 cgi/templates/txt_board.en.html
create mode 100644 cgi/templates/txt_board.html
create mode 100644 cgi/templates/txt_error.html
create mode 100644 cgi/templates/txt_thread.en.html
create mode 100644 cgi/templates/txt_thread.html
create mode 100644 cgi/templates/txt_threadlist.html
create mode 100644 cgi/tenjin.py
create mode 100644 cgi/tor.txt
create mode 100755 cgi/weabot.py
create mode 100644 static/css/buri.css
create mode 100644 static/css/cyber.css
create mode 100644 static/css/dickgirl.css
create mode 100644 static/css/easymodo.css
create mode 100644 static/css/futaba.css
create mode 100644 static/css/guro.css
create mode 100644 static/css/ib.css
create mode 100644 static/css/img/0back.png
create mode 100644 static/css/img/0info.png
create mode 100644 static/css/img/0pc.png
create mode 100644 static/css/img/ba.gif
create mode 100644 static/css/img/barra_dulce.png
create mode 100644 static/css/img/bg_deportes.gif
create mode 100644 static/css/img/bg_madera.png
create mode 100644 static/css/img/bg_oculto.gif
create mode 100644 static/css/img/bgtb.gif
create mode 100644 static/css/img/checked.png
create mode 100644 static/css/img/cyb.png
create mode 100644 static/css/img/cyba.png
create mode 100644 static/css/img/fondo2012.gif
create mode 100644 static/css/img/green.gif
create mode 100644 static/css/img/hand.png
create mode 100644 static/css/img/luz.gif
create mode 100644 static/css/img/muro.jpg
create mode 100644 static/css/img/nieve.png
create mode 100644 static/css/img/picnicbdy.gif
create mode 100644 static/css/img/picnicbg.gif
create mode 100644 static/css/img/picnicbtm.gif
create mode 100644 static/css/img/picnicbtn.gif
create mode 100644 static/css/img/picnicfg.gif
create mode 100644 static/css/img/picnichr.gif
create mode 100644 static/css/img/picnicmid.gif
create mode 100644 static/css/img/picnicthr1.gif
create mode 100644 static/css/img/picnicthr2.gif
create mode 100644 static/css/img/picnicthr3.gif
create mode 100644 static/css/img/picnictop.gif
create mode 100644 static/css/img/scan.png
create mode 100644 static/css/img/scroller1.gif
create mode 100644 static/css/img/tanasinn.gif
create mode 100644 static/css/img/vndb1.jpg
create mode 100644 static/css/img/vndb2.jpg
create mode 100644 static/css/img/vndb3.png
create mode 100644 static/css/kraut.css
create mode 100644 static/css/mobile.css
create mode 100644 static/css/night.css
create mode 100644 static/css/photon.css
create mode 100644 static/css/putaba.css
create mode 100644 static/css/red.css
create mode 100644 static/css/rene.css
create mode 100644 static/css/spc/base.css
create mode 100644 static/css/spc/halloween.css
create mode 100644 static/css/spc/layout.css
create mode 100644 static/css/spc/navidad.css
create mode 100644 static/css/spc/skeleton.css
create mode 100644 static/css/spc/valentin.css
create mode 100644 static/css/spc/valentin2.css
create mode 100644 static/css/txt/4am.css
create mode 100644 static/css/txt/amber.css
create mode 100644 static/css/txt/ayashii.css
create mode 100644 static/css/txt/baisano.css
create mode 100644 static/css/txt/bbs.css
create mode 100644 static/css/txt/bios.css
create mode 100644 static/css/txt/blue moon.css
create mode 100644 static/css/txt/ciber.css
create mode 100644 static/css/txt/futanari.css
create mode 100644 static/css/txt/headline.css
create mode 100644 static/css/txt/postal.css
create mode 100644 static/css/txt/sjis.css
create mode 100644 static/css/txt/ventanas.css
create mode 100644 static/css/vndb.css
create mode 100644 static/ico/1372836.gif
create mode 100644 static/ico/1k.gif
create mode 100644 static/ico/2-1.gif
create mode 100644 static/ico/2ppa.gif
create mode 100644 static/ico/2syobo_2.gif
create mode 100644 static/ico/3-2.gif
create mode 100644 static/ico/3.gif
create mode 100644 static/ico/3na.gif
create mode 100644 static/ico/4-2.gif
create mode 100644 static/ico/4248688.gif
create mode 100644 static/ico/5007629.gif
create mode 100644 static/ico/5296219.gif
create mode 100644 static/ico/5ta.gif
create mode 100644 static/ico/6396408.gif
create mode 100644 static/ico/6za.gif
create mode 100644 static/ico/8028885.gif
create mode 100644 static/ico/8toushinnomonar16.gif
create mode 100644 static/ico/8toushinnomonar32.gif
create mode 100644 static/ico/ace.gif
create mode 100644 static/ico/af1.gif
create mode 100644 static/ico/af2.gif
create mode 100644 static/ico/ahya_xmas_2.gif
create mode 100644 static/ico/aka.gif
create mode 100644 static/ico/ame.gif
create mode 100644 static/ico/anime_buun02.gif
create mode 100644 static/ico/anime_charhan01.gif
create mode 100644 static/ico/anime_charhan02.gif
create mode 100644 static/ico/anime_giko01.gif
create mode 100644 static/ico/anime_giko04.gif
create mode 100644 static/ico/anime_giko10.gif
create mode 100644 static/ico/anime_giko11.gif
create mode 100644 static/ico/anime_giko12.gif
create mode 100644 static/ico/anime_giko13.gif
create mode 100644 static/ico/anime_hossyu01.gif
create mode 100644 static/ico/anime_imanouchi01.gif
create mode 100644 static/ico/anime_iyou02.gif
create mode 100644 static/ico/anime_jien01.gif
create mode 100644 static/ico/anime_jien02.gif
create mode 100644 static/ico/anime_jien03.gif
create mode 100644 static/ico/anime_jyorujyu01.gif
create mode 100644 static/ico/anime_jyorujyu02.gif
create mode 100644 static/ico/anime_jyorujyu03.gif
create mode 100644 static/ico/anime_kukkuru01.gif
create mode 100644 static/ico/anime_kuma01.gif
create mode 100644 static/ico/anime_kumaface01.gif
create mode 100644 static/ico/anime_loop.gif
create mode 100644 static/ico/anime_marara02.gif
create mode 100644 static/ico/anime_matanki01.gif
create mode 100644 static/ico/anime_matanki02.gif
create mode 100644 static/ico/anime_miruna01.gif
create mode 100644 static/ico/anime_monar02.gif
create mode 100644 static/ico/anime_monar03.gif
create mode 100644 static/ico/anime_monar05.gif
create mode 100644 static/ico/anime_morara01.gif
create mode 100644 static/ico/anime_morara02.gif
create mode 100644 static/ico/anime_morara04.gif
create mode 100644 static/ico/anime_nokar01.gif
create mode 100644 static/ico/anime_okashi01.gif
create mode 100644 static/ico/anime_okashi02.gif
create mode 100644 static/ico/anime_onigiri04.gif
create mode 100644 static/ico/anime_saitama01.gif
create mode 100644 static/ico/anime_saitama02.gif
create mode 100644 static/ico/anime_saitama03.gif
create mode 100644 static/ico/anime_sasuga01.gif
create mode 100644 static/ico/anime_sasuga03.gif
create mode 100644 static/ico/anime_sasuga04.gif
create mode 100644 static/ico/anime_shii01.gif
create mode 100644 static/ico/anime_shii02.gif
create mode 100644 static/ico/anime_shii03.gif
create mode 100644 static/ico/anime_syobon01.gif
create mode 100644 static/ico/anime_syobon03.gif
create mode 100644 static/ico/anime_tarn01.gif
create mode 100644 static/ico/anime_uwan01.gif
create mode 100644 static/ico/anime_uwan02.gif
create mode 100644 static/ico/anime_uwan03.gif
create mode 100644 static/ico/anime_youkanman01.gif
create mode 100644 static/ico/anime_youkanman02.gif
create mode 100644 static/ico/anime_youkanman03.gif
create mode 100644 static/ico/anime_zonu01.gif
create mode 100644 static/ico/anime_zonu02.gif
create mode 100644 static/ico/aramaki.gif
create mode 100644 static/ico/aroeri-na32.gif
create mode 100644 static/ico/asopasomaso.gif
create mode 100644 static/ico/bikyakusan32.gif
create mode 100644 static/ico/bs.gif
create mode 100644 static/ico/button1_03.gif
create mode 100644 static/ico/buun.gif
create mode 100644 static/ico/chahan.gif
create mode 100644 static/ico/dokuo1.gif
create mode 100644 static/ico/file2_01.gif
create mode 100644 static/ico/fujisan.gif
create mode 100644 static/ico/fuun.gif
create mode 100644 static/ico/gaku.gif
create mode 100644 static/ico/gaku2.gif
create mode 100644 static/ico/gaku3.gif
create mode 100644 static/ico/gekisya1.gif
create mode 100644 static/ico/giko1.gif
create mode 100644 static/ico/gikog_gomibako.gif
create mode 100644 static/ico/gikog_gyunyupack.gif
create mode 100644 static/ico/gikog_pimiento.gif
create mode 100644 static/ico/gikoinu.gif
create mode 100644 static/ico/gikoneko.gif
create mode 100644 static/ico/gikoneko2.gif
create mode 100644 static/ico/gikoneko_1.gif
create mode 100644 static/ico/gocchin_face.gif
create mode 100644 static/ico/gomiopen.gif
create mode 100644 static/ico/goo_1.gif
create mode 100644 static/ico/goo_3.gif
create mode 100644 static/ico/gya-.gif
create mode 100644 static/ico/hagenin-shuriken.gif
create mode 100644 static/ico/hagurumaou.gif
create mode 100644 static/ico/hikky.gif
create mode 100644 static/ico/hikky_xmas_2.gif
create mode 100644 static/ico/hyou.gif
create mode 100644 static/ico/iirasan_face.gif
create mode 100644 static/ico/imanouchi_1.gif
create mode 100644 static/ico/iyahoo.gif
create mode 100644 static/ico/iyou.gif
create mode 100644 static/ico/jisakujien_2.gif
create mode 100644 static/ico/jisakujien_xmas.gif
create mode 100644 static/ico/kantoku1.gif
create mode 100644 static/ico/kappappa1.gif
create mode 100644 static/ico/kasa-ri.gif
create mode 100644 static/ico/kashiwamo-chi32.gif
create mode 100644 static/ico/kinokorusensei32.gif
create mode 100644 static/ico/kita_.gif
create mode 100644 static/ico/kodomona.gif
create mode 100644 static/ico/konkon_folder.gif
create mode 100644 static/ico/kossorisan.gif
create mode 100644 static/ico/kotatu.gif
create mode 100644 static/ico/kuma.gif
create mode 100644 static/ico/kuma2.gif
create mode 100644 static/ico/maimai.gif
create mode 100644 static/ico/makotan2_folder.gif
create mode 100644 static/ico/mona.gif
create mode 100644 static/ico/mona_shiri.gif
create mode 100644 static/ico/mona_tya.gif
create mode 100644 static/ico/monaazarashi_1.gif
create mode 100644 static/ico/namaetukenai.gif
create mode 100644 static/ico/naoruyo.gif
create mode 100644 static/ico/nida.gif
create mode 100644 static/ico/nigete.gif
create mode 100644 static/ico/nono_ie.gif
create mode 100644 static/ico/nurupo_ga_2.gif
create mode 100644 static/ico/onigiri_seito.gif
create mode 100644 static/ico/otiketu48.gif
create mode 100644 static/ico/pc3.gif
create mode 100644 static/ico/pgya.gif
create mode 100644 static/ico/sasuga1.gif
create mode 100644 static/ico/seito_2.gif
create mode 100644 static/ico/soon.gif
create mode 100644 static/ico/tasukete.gif
create mode 100644 static/ico/torimasu1.gif
create mode 100644 static/ico/torimasu2.gif
create mode 100644 static/ico/u_ame.gif
create mode 100644 static/ico/u_hoshi.gif
create mode 100644 static/ico/u_naoruyo_bath.gif
create mode 100644 static/ico/u_okotowari_a.gif
create mode 100644 static/ico/u_sofa.gif
create mode 100644 static/ico/wakannai1.gif
create mode 100644 static/ico/yakimochi.gif
create mode 100644 static/ico/youkan.gif
create mode 100644 static/ico/zonu_1.gif
create mode 100644 static/ico/zuzagiko48.gif
create mode 100644 static/img/anarkia.jpg
create mode 100644 static/img/bai.jpg
create mode 100644 static/img/cero.gif
create mode 100644 static/img/default.png
create mode 100644 static/img/juegos1.jpg
create mode 100644 static/img/juegos2.jpg
create mode 100644 static/img/juegos3.png
create mode 100644 static/img/juegos4.gif
create mode 100644 static/img/letras1.png
create mode 100644 static/img/letras2.png
create mode 100644 static/img/letras3.png
create mode 100644 static/img/letras4.jpg
create mode 100644 static/img/letras5.jpg
create mode 100644 static/img/musica1.jpg
create mode 100644 static/img/noticias.png
create mode 100644 static/img/old/2d_1.jpg
create mode 100644 static/img/old/2d_2.jpg
create mode 100644 static/img/old/2d_3.png
create mode 100644 static/img/old/2d_4.jpg
create mode 100644 static/img/old/argentina1.png
create mode 100644 static/img/old/chile1.png
create mode 100644 static/img/old/chile2.jpg
create mode 100644 static/img/old/g0.jpg
create mode 100644 static/img/old/g1.jpg
create mode 100644 static/img/old/g2.jpg
create mode 100644 static/img/old/g3.jpg
create mode 100644 static/img/old/g4.jpg
create mode 100644 static/img/old/g5.jpg
create mode 100644 static/img/old/peli.jpg
create mode 100644 static/img/old/salon2d_3.jpg
create mode 100644 static/img/old/salon2d_4.png
create mode 100644 static/img/old/salon2d_5.jpg
create mode 100644 static/img/old/zine.png
create mode 100644 static/img/salon2d_1.png
create mode 100644 static/img/salon2d_3.jpg
create mode 100644 static/img/salon2d_4.png
create mode 100644 static/img/salon2d_5.jpg
create mode 100644 static/img/tech1.png
create mode 100644 static/img/tech2.jpg
create mode 100644 static/img/tech3.png
create mode 100644 static/img/tech4.jpg
create mode 100644 static/img/tech5.jpg
create mode 100644 static/img/tech6.png
create mode 100644 static/img/tv1.png
create mode 100644 static/img/weird-al.jpg
create mode 100644 static/img/world.gif
create mode 100644 static/img/zonavip1.jpg
create mode 100644 static/img/zonavip2.gif
create mode 100644 static/img/zonavip3.png
create mode 100644 static/img/zonavip4.jpg
create mode 100644 static/img/zonavip5.gif
create mode 100644 static/img/zonavip6.png
create mode 100644 static/img/zonavip7.gif
create mode 100644 static/img/zonavip8.png
create mode 100644 static/img/zonavip9.gif
create mode 100644 static/img/zonavip9.jpg
create mode 100644 static/img/zonavip_halloween.jpg
create mode 100644 static/img/zonavip_nav.jpg
create mode 100644 static/js/aquiencitas.js
create mode 100644 static/js/autorefresh.js
create mode 100644 static/js/home.js
create mode 100644 static/js/jquery.js
create mode 100644 static/js/manage.js
create mode 100644 static/js/mobile.js
create mode 100644 static/js/paintbbs/PaintBBS-1.1.11.css
create mode 100644 static/js/paintbbs/PaintBBS-1.1.11.js
create mode 100644 static/js/paintbbs/PaintBBS-1.3.4.css
create mode 100644 static/js/paintbbs/PaintBBS-1.3.4.js
create mode 100755 static/js/palette_selfy.js
create mode 100644 static/js/shobon.js
create mode 100644 static/js/tegaki/tegaki.css
create mode 100644 static/js/tegaki/tegaki.js
create mode 100644 static/js/weabot.js
create mode 100644 static/js/weabotxt.js
create mode 100644 static/js/wpaint/.gitignore
create mode 100644 static/js/wpaint/README.md
create mode 100644 static/js/wpaint/bai.js
create mode 100644 static/js/wpaint/demo/demo.css
create mode 100644 static/js/wpaint/demo/img/facebook-icon.png
create mode 100644 static/js/wpaint/demo/img/favicon.ico
create mode 100644 static/js/wpaint/demo/img/forkme_right_darkblue.png
create mode 100644 static/js/wpaint/demo/img/github-icon.png
create mode 100644 static/js/wpaint/demo/img/googleplus-icon.png
create mode 100644 static/js/wpaint/demo/img/linkedin-icon.png
create mode 100644 static/js/wpaint/demo/img/rss-icon.png
create mode 100644 static/js/wpaint/demo/img/stumbleupon-icon.png
create mode 100644 static/js/wpaint/demo/img/twitter-icon.png
create mode 100644 static/js/wpaint/demo/img/websanova-logo-small-full-black.png
create mode 100644 static/js/wpaint/demo/img/youtube-icon.png
create mode 100644 static/js/wpaint/gruntfile.js
create mode 100644 static/js/wpaint/index.html
create mode 100644 static/js/wpaint/lib/jquery.1.10.2.min.js
create mode 100644 static/js/wpaint/lib/jquery.ui.core.1.10.3.min.js
create mode 100644 static/js/wpaint/lib/jquery.ui.draggable.1.10.3.min.js
create mode 100644 static/js/wpaint/lib/jquery.ui.mouse.1.10.3.min.js
create mode 100644 static/js/wpaint/lib/jquery.ui.widget.1.10.3.min.js
create mode 100644 static/js/wpaint/lib/mixins.styl
create mode 100644 static/js/wpaint/lib/wColorPicker.min.css
create mode 100644 static/js/wpaint/lib/wColorPicker.min.js
create mode 100644 static/js/wpaint/package.json
create mode 100644 static/js/wpaint/plugins/file/img/icons-menu-main-file.png
create mode 100644 static/js/wpaint/plugins/file/src/wPaint.menu.main.file.js
create mode 100644 static/js/wpaint/plugins/file/wPaint.menu.main.file.min.js
create mode 100644 static/js/wpaint/plugins/main/img/cursor-bucket.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-crosshair.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-dropper.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-eraser1.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-eraser10.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-eraser2.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-eraser3.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-eraser4.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-eraser5.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-eraser6.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-eraser7.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-eraser8.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-eraser9.png
create mode 100644 static/js/wpaint/plugins/main/img/cursor-pencil.png
create mode 100644 static/js/wpaint/plugins/main/img/icon-group-arrow.png
create mode 100644 static/js/wpaint/plugins/main/img/icons-menu-main.png
create mode 100644 static/js/wpaint/plugins/main/src/fillArea.min.js
create mode 100644 static/js/wpaint/plugins/main/src/wPaint.menu.main.js
create mode 100644 static/js/wpaint/plugins/main/wPaint.menu.main.min.js
create mode 100644 static/js/wpaint/plugins/shapes/img/icons-menu-main-shapes.png
create mode 100644 static/js/wpaint/plugins/shapes/src/shapes.min.js
create mode 100644 static/js/wpaint/plugins/shapes/src/wPaint.menu.main.shapes.js
create mode 100644 static/js/wpaint/plugins/shapes/wPaint.menu.main.shapes.min.js
create mode 100644 static/js/wpaint/plugins/text/img/icons-menu-text.png
create mode 100644 static/js/wpaint/plugins/text/src/wPaint.menu.text.js
create mode 100644 static/js/wpaint/plugins/text/wPaint.menu.text.min.js
create mode 100644 static/js/wpaint/src/wPaint.css
create mode 100644 static/js/wpaint/src/wPaint.js
create mode 100644 static/js/wpaint/src/wPaint.utils.js
create mode 100644 static/js/wpaint/test/dev.html
create mode 100644 static/js/wpaint/test/fullscreen.html
create mode 100644 static/js/wpaint/test/upload.php
create mode 100644 static/js/wpaint/test/uploads/test1.png
create mode 100644 static/js/wpaint/test/uploads/test2.png
create mode 100644 static/js/wpaint/test/uploads/test3.png
create mode 100644 static/js/wpaint/test/uploads/wPaint.png
create mode 100644 static/js/wpaint/wPaint.jquery.json
create mode 100644 static/js/wpaint/wPaint.min.css
create mode 100644 static/js/wpaint/wPaint.min.js
create mode 100644 static/meta/bbs.png
create mode 100644 static/meta/faq_1.png
create mode 100644 static/meta/faq_2.png
create mode 100644 static/meta/faq_3.png
create mode 100644 static/meta/ib.png
create mode 100644 static/meta/portada_6.jpg
create mode 100644 static/meta/portada_7.jpg
create mode 100644 static/meta/portada_8.jpg
create mode 100644 static/meta/portada_asp.gif
create mode 100644 static/meta/portada_cap1.jpg
create mode 100644 static/meta/portada_orig5.jpg
create mode 100644 static/meta/portada_toesca.jpg
create mode 100644 static/meta/portadaphil.jpg
create mode 100644 static/meta/primeraportada.png
create mode 100644 static/meta/sanvalentin2013.jpg
create mode 100644 static/meta/welcome.gif
create mode 100644 static/meta/welcome.jpg
create mode 100644 static/mime/epub.png
create mode 100644 static/mime/epub_small.png
create mode 100644 static/mime/mod.png
create mode 100644 static/mime/mod_small.png
create mode 100644 static/mime/pdf.png
create mode 100644 static/mime/pdf_small.png
create mode 100644 static/mime/s3m.png
create mode 100644 static/mime/s3m_small.png
create mode 100644 static/mime/swf.png
create mode 100644 static/mime/swf_small.png
create mode 100644 static/mime/torrent.png
create mode 100644 static/mime/torrent_small.png
create mode 100644 static/mime/xm.png
create mode 100644 static/mime/xm_small.png
diff --git a/cgi/.htaccess b/cgi/.htaccess
new file mode 100644
index 0000000..97a4f17
--- /dev/null
+++ b/cgi/.htaccess
@@ -0,0 +1,9 @@
+AddHandler cgi-script .py
+Options +ExecCGI
+
+# Uncomment if you want pretty URL (ie cgi/post)
+#RewriteEngine On
+#RewriteBase /cgi/
+#RewriteRule ^weabot\.py/ - [L]
+#RewriteRule ^(.*)$ weabot.py/$1 [L]
+
diff --git a/cgi/BeautifulSoup.py b/cgi/BeautifulSoup.py
new file mode 100644
index 0000000..7278215
--- /dev/null
+++ b/cgi/BeautifulSoup.py
@@ -0,0 +1,2017 @@
+"""Beautiful Soup
+Elixir and Tonic
+"The Screen-Scraper's Friend"
+http://www.crummy.com/software/BeautifulSoup/
+
+Beautiful Soup parses a (possibly invalid) XML or HTML document into a
+tree representation. It provides methods and Pythonic idioms that make
+it easy to navigate, search, and modify the tree.
+
+A well-formed XML/HTML document yields a well-formed data
+structure. An ill-formed XML/HTML document yields a correspondingly
+ill-formed data structure. If your document is only locally
+well-formed, you can use this library to find and process the
+well-formed part of it.
+
+Beautiful Soup works with Python 2.2 and up. It has no external
+dependencies, but you'll have more success at converting data to UTF-8
+if you also install these three packages:
+
+* chardet, for auto-detecting character encodings
+ http://chardet.feedparser.org/
+* cjkcodecs and iconv_codec, which add more encodings to the ones supported
+ by stock Python.
+ http://cjkpython.i18n.org/
+
+Beautiful Soup defines classes for two main parsing strategies:
+
+ * BeautifulStoneSoup, for parsing XML, SGML, or your domain-specific
+ language that kind of looks like XML.
+
+ * BeautifulSoup, for parsing run-of-the-mill HTML code, be it valid
+ or invalid. This class has web browser-like heuristics for
+ obtaining a sensible parse tree in the face of common HTML errors.
+
+Beautiful Soup also defines a class (UnicodeDammit) for autodetecting
+the encoding of an HTML or XML document, and converting it to
+Unicode. Much of this code is taken from Mark Pilgrim's Universal Feed Parser.
+
+For more than you ever wanted to know about Beautiful Soup, see the
+documentation:
+http://www.crummy.com/software/BeautifulSoup/documentation.html
+
+Here, have some legalese:
+
+Copyright (c) 2004-2010, Leonard Richardson
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of the the Beautiful Soup Consortium and All
+ Night Kosher Bakery nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE, DAMMIT.
+
+"""
+from __future__ import generators
+
+__author__ = "Leonard Richardson (leonardr@segfault.org)"
+__version__ = "3.2.1"
+__copyright__ = "Copyright (c) 2004-2012 Leonard Richardson"
+__license__ = "New-style BSD"
+
+from sgmllib import SGMLParser, SGMLParseError
+import codecs
+import markupbase
+import types
+import re
+import sgmllib
+try:
+ from htmlentitydefs import name2codepoint
+except ImportError:
+ name2codepoint = {}
+try:
+ set
+except NameError:
+ from sets import Set as set
+
+#These hacks make Beautiful Soup able to parse XML with namespaces
+sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*')
+markupbase._declname_match = re.compile(r'[a-zA-Z][-_.:a-zA-Z0-9]*\s*').match
+
+DEFAULT_OUTPUT_ENCODING = "utf-8"
+
+def _match_css_class(str):
+ """Build a RE to match the given CSS class."""
+ return re.compile(r"(^|.*\s)%s($|\s)" % str)
+
+# First, the classes that represent markup elements.
+
+class PageElement(object):
+ """Contains the navigational information for some part of the page
+ (either a tag or a piece of text)"""
+
+ def _invert(h):
+ "Cheap function to invert a hash."
+ i = {}
+ for k,v in h.items():
+ i[v] = k
+ return i
+
+ XML_ENTITIES_TO_SPECIAL_CHARS = { "apos" : "'",
+ "quot" : '"',
+ "amp" : "&",
+ "lt" : "<",
+ "gt" : ">" }
+
+ XML_SPECIAL_CHARS_TO_ENTITIES = _invert(XML_ENTITIES_TO_SPECIAL_CHARS)
+
+ def setup(self, parent=None, previous=None):
+ """Sets up the initial relations between this element and
+ other elements."""
+ self.parent = parent
+ self.previous = previous
+ self.next = None
+ self.previousSibling = None
+ self.nextSibling = None
+ if self.parent and self.parent.contents:
+ self.previousSibling = self.parent.contents[-1]
+ self.previousSibling.nextSibling = self
+
+ def replaceWith(self, replaceWith):
+ oldParent = self.parent
+ myIndex = self.parent.index(self)
+ if hasattr(replaceWith, "parent")\
+ and replaceWith.parent is self.parent:
+ # We're replacing this element with one of its siblings.
+ index = replaceWith.parent.index(replaceWith)
+ if index and index < myIndex:
+ # Furthermore, it comes before this element. That
+ # means that when we extract it, the index of this
+ # element will change.
+ myIndex = myIndex - 1
+ self.extract()
+ oldParent.insert(myIndex, replaceWith)
+
+ def replaceWithChildren(self):
+ myParent = self.parent
+ myIndex = self.parent.index(self)
+ self.extract()
+ reversedChildren = list(self.contents)
+ reversedChildren.reverse()
+ for child in reversedChildren:
+ myParent.insert(myIndex, child)
+
+ def extract(self):
+ """Destructively rips this element out of the tree."""
+ if self.parent:
+ try:
+ del self.parent.contents[self.parent.index(self)]
+ except ValueError:
+ pass
+
+ #Find the two elements that would be next to each other if
+ #this element (and any children) hadn't been parsed. Connect
+ #the two.
+ lastChild = self._lastRecursiveChild()
+ nextElement = lastChild.next
+
+ if self.previous:
+ self.previous.next = nextElement
+ if nextElement:
+ nextElement.previous = self.previous
+ self.previous = None
+ lastChild.next = None
+
+ self.parent = None
+ if self.previousSibling:
+ self.previousSibling.nextSibling = self.nextSibling
+ if self.nextSibling:
+ self.nextSibling.previousSibling = self.previousSibling
+ self.previousSibling = self.nextSibling = None
+ return self
+
+ def _lastRecursiveChild(self):
+ "Finds the last element beneath this object to be parsed."
+ lastChild = self
+ while hasattr(lastChild, 'contents') and lastChild.contents:
+ lastChild = lastChild.contents[-1]
+ return lastChild
+
+ def insert(self, position, newChild):
+ if isinstance(newChild, basestring) \
+ and not isinstance(newChild, NavigableString):
+ newChild = NavigableString(newChild)
+
+ position = min(position, len(self.contents))
+ if hasattr(newChild, 'parent') and newChild.parent is not None:
+ # We're 'inserting' an element that's already one
+ # of this object's children.
+ if newChild.parent is self:
+ index = self.index(newChild)
+ if index > position:
+ # Furthermore we're moving it further down the
+ # list of this object's children. That means that
+ # when we extract this element, our target index
+ # will jump down one.
+ position = position - 1
+ newChild.extract()
+
+ newChild.parent = self
+ previousChild = None
+ if position == 0:
+ newChild.previousSibling = None
+ newChild.previous = self
+ else:
+ previousChild = self.contents[position-1]
+ newChild.previousSibling = previousChild
+ newChild.previousSibling.nextSibling = newChild
+ newChild.previous = previousChild._lastRecursiveChild()
+ if newChild.previous:
+ newChild.previous.next = newChild
+
+ newChildsLastElement = newChild._lastRecursiveChild()
+
+ if position >= len(self.contents):
+ newChild.nextSibling = None
+
+ parent = self
+ parentsNextSibling = None
+ while not parentsNextSibling:
+ parentsNextSibling = parent.nextSibling
+ parent = parent.parent
+ if not parent: # This is the last element in the document.
+ break
+ if parentsNextSibling:
+ newChildsLastElement.next = parentsNextSibling
+ else:
+ newChildsLastElement.next = None
+ else:
+ nextChild = self.contents[position]
+ newChild.nextSibling = nextChild
+ if newChild.nextSibling:
+ newChild.nextSibling.previousSibling = newChild
+ newChildsLastElement.next = nextChild
+
+ if newChildsLastElement.next:
+ newChildsLastElement.next.previous = newChildsLastElement
+ self.contents.insert(position, newChild)
+
+ def append(self, tag):
+ """Appends the given tag to the contents of this tag."""
+ self.insert(len(self.contents), tag)
+
+ def findNext(self, name=None, attrs={}, text=None, **kwargs):
+ """Returns the first item that matches the given criteria and
+ appears after this Tag in the document."""
+ return self._findOne(self.findAllNext, name, attrs, text, **kwargs)
+
+ def findAllNext(self, name=None, attrs={}, text=None, limit=None,
+ **kwargs):
+ """Returns all items that match the given criteria and appear
+ after this Tag in the document."""
+ return self._findAll(name, attrs, text, limit, self.nextGenerator,
+ **kwargs)
+
+ def findNextSibling(self, name=None, attrs={}, text=None, **kwargs):
+ """Returns the closest sibling to this Tag that matches the
+ given criteria and appears after this Tag in the document."""
+ return self._findOne(self.findNextSiblings, name, attrs, text,
+ **kwargs)
+
+ def findNextSiblings(self, name=None, attrs={}, text=None, limit=None,
+ **kwargs):
+ """Returns the siblings of this Tag that match the given
+ criteria and appear after this Tag in the document."""
+ return self._findAll(name, attrs, text, limit,
+ self.nextSiblingGenerator, **kwargs)
+ fetchNextSiblings = findNextSiblings # Compatibility with pre-3.x
+
+ def findPrevious(self, name=None, attrs={}, text=None, **kwargs):
+ """Returns the first item that matches the given criteria and
+ appears before this Tag in the document."""
+ return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs)
+
+ def findAllPrevious(self, name=None, attrs={}, text=None, limit=None,
+ **kwargs):
+ """Returns all items that match the given criteria and appear
+ before this Tag in the document."""
+ return self._findAll(name, attrs, text, limit, self.previousGenerator,
+ **kwargs)
+ fetchPrevious = findAllPrevious # Compatibility with pre-3.x
+
+ def findPreviousSibling(self, name=None, attrs={}, text=None, **kwargs):
+ """Returns the closest sibling to this Tag that matches the
+ given criteria and appears before this Tag in the document."""
+ return self._findOne(self.findPreviousSiblings, name, attrs, text,
+ **kwargs)
+
+ def findPreviousSiblings(self, name=None, attrs={}, text=None,
+ limit=None, **kwargs):
+ """Returns the siblings of this Tag that match the given
+ criteria and appear before this Tag in the document."""
+ return self._findAll(name, attrs, text, limit,
+ self.previousSiblingGenerator, **kwargs)
+ fetchPreviousSiblings = findPreviousSiblings # Compatibility with pre-3.x
+
+ def findParent(self, name=None, attrs={}, **kwargs):
+ """Returns the closest parent of this Tag that matches the given
+ criteria."""
+ # NOTE: We can't use _findOne because findParents takes a different
+ # set of arguments.
+ r = None
+ l = self.findParents(name, attrs, 1)
+ if l:
+ r = l[0]
+ return r
+
+ def findParents(self, name=None, attrs={}, limit=None, **kwargs):
+ """Returns the parents of this Tag that match the given
+ criteria."""
+
+ return self._findAll(name, attrs, None, limit, self.parentGenerator,
+ **kwargs)
+ fetchParents = findParents # Compatibility with pre-3.x
+
+ #These methods do the real heavy lifting.
+
+ def _findOne(self, method, name, attrs, text, **kwargs):
+ r = None
+ l = method(name, attrs, text, 1, **kwargs)
+ if l:
+ r = l[0]
+ return r
+
+ def _findAll(self, name, attrs, text, limit, generator, **kwargs):
+ "Iterates over a generator looking for things that match."
+
+ if isinstance(name, SoupStrainer):
+ strainer = name
+ # (Possibly) special case some findAll*(...) searches
+ elif text is None and not limit and not attrs and not kwargs:
+ # findAll*(True)
+ if name is True:
+ return [element for element in generator()
+ if isinstance(element, Tag)]
+ # findAll*('tag-name')
+ elif isinstance(name, basestring):
+ return [element for element in generator()
+ if isinstance(element, Tag) and
+ element.name == name]
+ else:
+ strainer = SoupStrainer(name, attrs, text, **kwargs)
+ # Build a SoupStrainer
+ else:
+ strainer = SoupStrainer(name, attrs, text, **kwargs)
+ results = ResultSet(strainer)
+ g = generator()
+ while True:
+ try:
+ i = g.next()
+ except StopIteration:
+ break
+ if i:
+ found = strainer.search(i)
+ if found:
+ results.append(found)
+ if limit and len(results) >= limit:
+ break
+ return results
+
+ #These Generators can be used to navigate starting from both
+ #NavigableStrings and Tags.
+ def nextGenerator(self):
+ i = self
+ while i is not None:
+ i = i.next
+ yield i
+
+ def nextSiblingGenerator(self):
+ i = self
+ while i is not None:
+ i = i.nextSibling
+ yield i
+
+ def previousGenerator(self):
+ i = self
+ while i is not None:
+ i = i.previous
+ yield i
+
+ def previousSiblingGenerator(self):
+ i = self
+ while i is not None:
+ i = i.previousSibling
+ yield i
+
+ def parentGenerator(self):
+ i = self
+ while i is not None:
+ i = i.parent
+ yield i
+
+ # Utility methods
+ def substituteEncoding(self, str, encoding=None):
+ encoding = encoding or "utf-8"
+ return str.replace("%SOUP-ENCODING%", encoding)
+
+ def toEncoding(self, s, encoding=None):
+ """Encodes an object to a string in some encoding, or to Unicode.
+ ."""
+ if isinstance(s, unicode):
+ if encoding:
+ s = s.encode(encoding)
+ elif isinstance(s, str):
+ if encoding:
+ s = s.encode(encoding)
+ else:
+ s = unicode(s)
+ else:
+ if encoding:
+ s = self.toEncoding(str(s), encoding)
+ else:
+ s = unicode(s)
+ return s
+
+ BARE_AMPERSAND_OR_BRACKET = re.compile("([<>]|"
+ + "&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)"
+ + ")")
+
+ def _sub_entity(self, x):
+ """Used with a regular expression to substitute the
+ appropriate XML entity for an XML special character."""
+ return "&" + self.XML_SPECIAL_CHARS_TO_ENTITIES[x.group(0)[0]] + ";"
+
+
+class NavigableString(unicode, PageElement):
+
+ def __new__(cls, value):
+ """Create a new NavigableString.
+
+ When unpickling a NavigableString, this method is called with
+ the string in DEFAULT_OUTPUT_ENCODING. That encoding needs to be
+ passed in to the superclass's __new__ or the superclass won't know
+ how to handle non-ASCII characters.
+ """
+ if isinstance(value, unicode):
+ return unicode.__new__(cls, value)
+ return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)
+
+ def __getnewargs__(self):
+ return (NavigableString.__str__(self),)
+
+ def __getattr__(self, attr):
+ """text.string gives you text. This is for backwards
+ compatibility for Navigable*String, but for CData* it lets you
+ get the string without the CData wrapper."""
+ if attr == 'string':
+ return self
+ else:
+ raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr)
+
+ def __unicode__(self):
+ return str(self).decode(DEFAULT_OUTPUT_ENCODING)
+
+ def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
+ # Substitute outgoing XML entities.
+ data = self.BARE_AMPERSAND_OR_BRACKET.sub(self._sub_entity, self)
+ if encoding:
+ return data.encode(encoding)
+ else:
+ return data
+
+class CData(NavigableString):
+
+ def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
+ return "" % NavigableString.__str__(self, encoding)
+
+class ProcessingInstruction(NavigableString):
+ def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
+ output = self
+ if "%SOUP-ENCODING%" in output:
+ output = self.substituteEncoding(output, encoding)
+ return "%s?>" % self.toEncoding(output, encoding)
+
+class Comment(NavigableString):
+ def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
+ return "" % NavigableString.__str__(self, encoding)
+
+class Declaration(NavigableString):
+ def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
+ return "" % NavigableString.__str__(self, encoding)
+
+class Tag(PageElement):
+
+ """Represents a found HTML tag with its attributes and contents."""
+
+ def _convertEntities(self, match):
+ """Used in a call to re.sub to replace HTML, XML, and numeric
+ entities with the appropriate Unicode characters. If HTML
+ entities are being converted, any unrecognized entities are
+ escaped."""
+ x = match.group(1)
+ if self.convertHTMLEntities and x in name2codepoint:
+ return unichr(name2codepoint[x])
+ elif x in self.XML_ENTITIES_TO_SPECIAL_CHARS:
+ if self.convertXMLEntities:
+ return self.XML_ENTITIES_TO_SPECIAL_CHARS[x]
+ else:
+ return u'&%s;' % x
+ elif len(x) > 0 and x[0] == '#':
+ # Handle numeric entities
+ if len(x) > 1 and x[1] == 'x':
+ return unichr(int(x[2:], 16))
+ else:
+ return unichr(int(x[1:]))
+
+ elif self.escapeUnrecognizedEntities:
+ return u'&%s;' % x
+ else:
+ return u'&%s;' % x
+
+ def __init__(self, parser, name, attrs=None, parent=None,
+ previous=None):
+ "Basic constructor."
+
+ # We don't actually store the parser object: that lets extracted
+ # chunks be garbage-collected
+ self.parserClass = parser.__class__
+ self.isSelfClosing = parser.isSelfClosingTag(name)
+ self.name = name
+ if attrs is None:
+ attrs = []
+ elif isinstance(attrs, dict):
+ attrs = attrs.items()
+ self.attrs = attrs
+ self.contents = []
+ self.setup(parent, previous)
+ self.hidden = False
+ self.containsSubstitutions = False
+ self.convertHTMLEntities = parser.convertHTMLEntities
+ self.convertXMLEntities = parser.convertXMLEntities
+ self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities
+
+ # Convert any HTML, XML, or numeric entities in the attribute values.
+ convert = lambda(k, val): (k,
+ re.sub("&(#\d+|#x[0-9a-fA-F]+|\w+);",
+ self._convertEntities,
+ val))
+ self.attrs = map(convert, self.attrs)
+
+ def getString(self):
+ if (len(self.contents) == 1
+ and isinstance(self.contents[0], NavigableString)):
+ return self.contents[0]
+
+ def setString(self, string):
+ """Replace the contents of the tag with a string"""
+ self.clear()
+ self.append(string)
+
+ string = property(getString, setString)
+
+ def getText(self, separator=u""):
+ if not len(self.contents):
+ return u""
+ stopNode = self._lastRecursiveChild().next
+ strings = []
+ current = self.contents[0]
+ while current is not stopNode:
+ if isinstance(current, NavigableString):
+ strings.append(current.strip())
+ current = current.next
+ return separator.join(strings)
+
+ text = property(getText)
+
+ def get(self, key, default=None):
+ """Returns the value of the 'key' attribute for the tag, or
+ the value given for 'default' if it doesn't have that
+ attribute."""
+ return self._getAttrMap().get(key, default)
+
+ def clear(self):
+ """Extract all children."""
+ for child in self.contents[:]:
+ child.extract()
+
+ def index(self, element):
+ for i, child in enumerate(self.contents):
+ if child is element:
+ return i
+ raise ValueError("Tag.index: element not in tag")
+
+ def has_key(self, key):
+ return self._getAttrMap().has_key(key)
+
+ def __getitem__(self, key):
+ """tag[key] returns the value of the 'key' attribute for the tag,
+ and throws an exception if it's not there."""
+ return self._getAttrMap()[key]
+
+ def __iter__(self):
+ "Iterating over a tag iterates over its contents."
+ return iter(self.contents)
+
+ def __len__(self):
+ "The length of a tag is the length of its list of contents."
+ return len(self.contents)
+
+ def __contains__(self, x):
+ return x in self.contents
+
+ def __nonzero__(self):
+ "A tag is non-None even if it has no contents."
+ return True
+
+ def __setitem__(self, key, value):
+ """Setting tag[key] sets the value of the 'key' attribute for the
+ tag."""
+ self._getAttrMap()
+ self.attrMap[key] = value
+ found = False
+ for i in range(0, len(self.attrs)):
+ if self.attrs[i][0] == key:
+ self.attrs[i] = (key, value)
+ found = True
+ if not found:
+ self.attrs.append((key, value))
+ self._getAttrMap()[key] = value
+
+ def __delitem__(self, key):
+ "Deleting tag[key] deletes all 'key' attributes for the tag."
+ for item in self.attrs:
+ if item[0] == key:
+ self.attrs.remove(item)
+ #We don't break because bad HTML can define the same
+ #attribute multiple times.
+ self._getAttrMap()
+ if self.attrMap.has_key(key):
+ del self.attrMap[key]
+
+ def __call__(self, *args, **kwargs):
+ """Calling a tag like a function is the same as calling its
+ findAll() method. Eg. tag('a') returns a list of all the A tags
+ found within this tag."""
+ return apply(self.findAll, args, kwargs)
+
+ def __getattr__(self, tag):
+ #print "Getattr %s.%s" % (self.__class__, tag)
+ if len(tag) > 3 and tag.rfind('Tag') == len(tag)-3:
+ return self.find(tag[:-3])
+ elif tag.find('__') != 0:
+ return self.find(tag)
+ raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__, tag)
+
+ def __eq__(self, other):
+ """Returns true iff this tag has the same name, the same attributes,
+ and the same contents (recursively) as the given tag.
+
+ NOTE: right now this will return false if two tags have the
+ same attributes in a different order. Should this be fixed?"""
+ if other is self:
+ return True
+ if not hasattr(other, 'name') or not hasattr(other, 'attrs') or not hasattr(other, 'contents') or self.name != other.name or self.attrs != other.attrs or len(self) != len(other):
+ return False
+ for i in range(0, len(self.contents)):
+ if self.contents[i] != other.contents[i]:
+ return False
+ return True
+
+ def __ne__(self, other):
+ """Returns true iff this tag is not identical to the other tag,
+ as defined in __eq__."""
+ return not self == other
+
+ def __repr__(self, encoding=DEFAULT_OUTPUT_ENCODING):
+ """Renders this tag as a string."""
+ return self.__str__(encoding)
+
+ def __unicode__(self):
+ return self.__str__(None)
+
+ def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING,
+ prettyPrint=False, indentLevel=0):
+ """Returns a string or Unicode representation of this tag and
+ its contents. To get Unicode, pass None for encoding.
+
+ NOTE: since Python's HTML parser consumes whitespace, this
+ method is not certain to reproduce the whitespace present in
+ the original string."""
+
+ encodedName = self.toEncoding(self.name, encoding)
+
+ attrs = []
+ if self.attrs:
+ for key, val in self.attrs:
+ fmt = '%s="%s"'
+ if isinstance(val, basestring):
+ if self.containsSubstitutions and '%SOUP-ENCODING%' in val:
+ val = self.substituteEncoding(val, encoding)
+
+ # The attribute value either:
+ #
+ # * Contains no embedded double quotes or single quotes.
+ # No problem: we enclose it in double quotes.
+ # * Contains embedded single quotes. No problem:
+ # double quotes work here too.
+ # * Contains embedded double quotes. No problem:
+ # we enclose it in single quotes.
+ # * Embeds both single _and_ double quotes. This
+ # can't happen naturally, but it can happen if
+ # you modify an attribute value after parsing
+ # the document. Now we have a bit of a
+ # problem. We solve it by enclosing the
+ # attribute in single quotes, and escaping any
+ # embedded single quotes to XML entities.
+ if '"' in val:
+ fmt = "%s='%s'"
+ if "'" in val:
+ # TODO: replace with apos when
+ # appropriate.
+ val = val.replace("'", "&squot;")
+
+ # Now we're okay w/r/t quotes. But the attribute
+ # value might also contain angle brackets, or
+ # ampersands that aren't part of entities. We need
+ # to escape those to XML entities too.
+ val = self.BARE_AMPERSAND_OR_BRACKET.sub(self._sub_entity, val)
+
+ attrs.append(fmt % (self.toEncoding(key, encoding),
+ self.toEncoding(val, encoding)))
+ close = ''
+ closeTag = ''
+ if self.isSelfClosing:
+ close = ' /'
+ else:
+ closeTag = '%s>' % encodedName
+
+ indentTag, indentContents = 0, 0
+ if prettyPrint:
+ indentTag = indentLevel
+ space = (' ' * (indentTag-1))
+ indentContents = indentTag + 1
+ contents = self.renderContents(encoding, prettyPrint, indentContents)
+ if self.hidden:
+ s = contents
+ else:
+ s = []
+ attributeString = ''
+ if attrs:
+ attributeString = ' ' + ' '.join(attrs)
+ if prettyPrint:
+ s.append(space)
+ s.append('<%s%s%s>' % (encodedName, attributeString, close))
+ if prettyPrint:
+ s.append("\n")
+ s.append(contents)
+ if prettyPrint and contents and contents[-1] != "\n":
+ s.append("\n")
+ if prettyPrint and closeTag:
+ s.append(space)
+ s.append(closeTag)
+ if prettyPrint and closeTag and self.nextSibling:
+ s.append("\n")
+ s = ''.join(s)
+ return s
+
+ def decompose(self):
+ """Recursively destroys the contents of this tree."""
+ self.extract()
+ if len(self.contents) == 0:
+ return
+ current = self.contents[0]
+ while current is not None:
+ next = current.next
+ if isinstance(current, Tag):
+ del current.contents[:]
+ current.parent = None
+ current.previous = None
+ current.previousSibling = None
+ current.next = None
+ current.nextSibling = None
+ current = next
+
+ def prettify(self, encoding=DEFAULT_OUTPUT_ENCODING):
+ return self.__str__(encoding, True)
+
+ def renderContents(self, encoding=DEFAULT_OUTPUT_ENCODING,
+ prettyPrint=False, indentLevel=0):
+ """Renders the contents of this tag as a string in the given
+ encoding. If encoding is None, returns a Unicode string.."""
+ s=[]
+ for c in self:
+ text = None
+ if isinstance(c, NavigableString):
+ text = c.__str__(encoding)
+ elif isinstance(c, Tag):
+ s.append(c.__str__(encoding, prettyPrint, indentLevel))
+ if text and prettyPrint:
+ text = text.strip()
+ if text:
+ if prettyPrint:
+ s.append(" " * (indentLevel-1))
+ s.append(text)
+ if prettyPrint:
+ s.append("\n")
+ return ''.join(s)
+
+ #Soup methods
+
+ def find(self, name=None, attrs={}, recursive=True, text=None,
+ **kwargs):
+ """Return only the first child of this Tag matching the given
+ criteria."""
+ r = None
+ l = self.findAll(name, attrs, recursive, text, 1, **kwargs)
+ if l:
+ r = l[0]
+ return r
+ findChild = find
+
+ def findAll(self, name=None, attrs={}, recursive=True, text=None,
+ limit=None, **kwargs):
+ """Extracts a list of Tag objects that match the given
+ criteria. You can specify the name of the Tag and any
+ attributes you want the Tag to have.
+
+ The value of a key-value pair in the 'attrs' map can be a
+ string, a list of strings, a regular expression object, or a
+ callable that takes a string and returns whether or not the
+ string matches for some custom definition of 'matches'. The
+ same is true of the tag name."""
+ generator = self.recursiveChildGenerator
+ if not recursive:
+ generator = self.childGenerator
+ return self._findAll(name, attrs, text, limit, generator, **kwargs)
+ findChildren = findAll
+
+ # Pre-3.x compatibility methods
+ first = find
+ fetch = findAll
+
+ def fetchText(self, text=None, recursive=True, limit=None):
+ return self.findAll(text=text, recursive=recursive, limit=limit)
+
+ def firstText(self, text=None, recursive=True):
+ return self.find(text=text, recursive=recursive)
+
+ #Private methods
+
+ def _getAttrMap(self):
+ """Initializes a map representation of this tag's attributes,
+ if not already initialized."""
+ if not getattr(self, 'attrMap'):
+ self.attrMap = {}
+ for (key, value) in self.attrs:
+ self.attrMap[key] = value
+ return self.attrMap
+
+ #Generator methods
+ def childGenerator(self):
+ # Just use the iterator from the contents
+ return iter(self.contents)
+
+ def recursiveChildGenerator(self):
+ if not len(self.contents):
+ raise StopIteration
+ stopNode = self._lastRecursiveChild().next
+ current = self.contents[0]
+ while current is not stopNode:
+ yield current
+ current = current.next
+
+
+# Next, a couple classes to represent queries and their results.
+class SoupStrainer:
+ """Encapsulates a number of ways of matching a markup element (tag or
+ text)."""
+
+ def __init__(self, name=None, attrs={}, text=None, **kwargs):
+ self.name = name
+ if isinstance(attrs, basestring):
+ kwargs['class'] = _match_css_class(attrs)
+ attrs = None
+ if kwargs:
+ if attrs:
+ attrs = attrs.copy()
+ attrs.update(kwargs)
+ else:
+ attrs = kwargs
+ self.attrs = attrs
+ self.text = text
+
+ def __str__(self):
+ if self.text:
+ return self.text
+ else:
+ return "%s|%s" % (self.name, self.attrs)
+
+ def searchTag(self, markupName=None, markupAttrs={}):
+ found = None
+ markup = None
+ if isinstance(markupName, Tag):
+ markup = markupName
+ markupAttrs = markup
+ callFunctionWithTagData = callable(self.name) \
+ and not isinstance(markupName, Tag)
+
+ if (not self.name) \
+ or callFunctionWithTagData \
+ or (markup and self._matches(markup, self.name)) \
+ or (not markup and self._matches(markupName, self.name)):
+ if callFunctionWithTagData:
+ match = self.name(markupName, markupAttrs)
+ else:
+ match = True
+ markupAttrMap = None
+ for attr, matchAgainst in self.attrs.items():
+ if not markupAttrMap:
+ if hasattr(markupAttrs, 'get'):
+ markupAttrMap = markupAttrs
+ else:
+ markupAttrMap = {}
+ for k,v in markupAttrs:
+ markupAttrMap[k] = v
+ attrValue = markupAttrMap.get(attr)
+ if not self._matches(attrValue, matchAgainst):
+ match = False
+ break
+ if match:
+ if markup:
+ found = markup
+ else:
+ found = markupName
+ return found
+
+ def search(self, markup):
+ #print 'looking for %s in %s' % (self, markup)
+ found = None
+ # If given a list of items, scan it for a text element that
+ # matches.
+ if hasattr(markup, "__iter__") \
+ and not isinstance(markup, Tag):
+ for element in markup:
+ if isinstance(element, NavigableString) \
+ and self.search(element):
+ found = element
+ break
+ # If it's a Tag, make sure its name or attributes match.
+ # Don't bother with Tags if we're searching for text.
+ elif isinstance(markup, Tag):
+ if not self.text:
+ found = self.searchTag(markup)
+ # If it's text, make sure the text matches.
+ elif isinstance(markup, NavigableString) or \
+ isinstance(markup, basestring):
+ if self._matches(markup, self.text):
+ found = markup
+ else:
+ raise Exception, "I don't know how to match against a %s" \
+ % markup.__class__
+ return found
+
+ def _matches(self, markup, matchAgainst):
+ #print "Matching %s against %s" % (markup, matchAgainst)
+ result = False
+ if matchAgainst is True:
+ result = markup is not None
+ elif callable(matchAgainst):
+ result = matchAgainst(markup)
+ else:
+ #Custom match methods take the tag as an argument, but all
+ #other ways of matching match the tag name as a string.
+ if isinstance(markup, Tag):
+ markup = markup.name
+ if markup and not isinstance(markup, basestring):
+ markup = unicode(markup)
+ #Now we know that chunk is either a string, or None.
+ if hasattr(matchAgainst, 'match'):
+ # It's a regexp object.
+ result = markup and matchAgainst.search(markup)
+ elif hasattr(matchAgainst, '__iter__'): # list-like
+ result = markup in matchAgainst
+ elif hasattr(matchAgainst, 'items'):
+ result = markup.has_key(matchAgainst)
+ elif matchAgainst and isinstance(markup, basestring):
+ if isinstance(markup, unicode):
+ matchAgainst = unicode(matchAgainst)
+ else:
+ matchAgainst = str(matchAgainst)
+
+ if not result:
+ result = matchAgainst == markup
+ return result
+
+class ResultSet(list):
+ """A ResultSet is just a list that keeps track of the SoupStrainer
+ that created it."""
+ def __init__(self, source):
+ list.__init__([])
+ self.source = source
+
+# Now, some helper functions.
+
+def buildTagMap(default, *args):
+ """Turns a list of maps, lists, or scalars into a single map.
+ Used to build the SELF_CLOSING_TAGS, NESTABLE_TAGS, and
+ NESTING_RESET_TAGS maps out of lists and partial maps."""
+ built = {}
+ for portion in args:
+ if hasattr(portion, 'items'):
+ #It's a map. Merge it.
+ for k,v in portion.items():
+ built[k] = v
+ elif hasattr(portion, '__iter__'): # is a list
+ #It's a list. Map each item to the default.
+ for k in portion:
+ built[k] = default
+ else:
+ #It's a scalar. Map it to the default.
+ built[portion] = default
+ return built
+
+# Now, the parser classes.
+
+class BeautifulStoneSoup(Tag, SGMLParser):
+
+ """This class contains the basic parser and search code. It defines
+ a parser that knows nothing about tag behavior except for the
+ following:
+
+ You can't close a tag without closing all the tags it encloses.
+ That is, "
(No space between name of closing tag and tag close)
+ (Extraneous whitespace in declaration)
+
+ You can pass in a custom list of (RE object, replace method)
+ tuples to get Beautiful Soup to scrub your input the way you
+ want."""
+
+ self.parseOnlyThese = parseOnlyThese
+ self.fromEncoding = fromEncoding
+ self.smartQuotesTo = smartQuotesTo
+ self.convertEntities = convertEntities
+ # Set the rules for how we'll deal with the entities we
+ # encounter
+ if self.convertEntities:
+ # It doesn't make sense to convert encoded characters to
+ # entities even while you're converting entities to Unicode.
+ # Just convert it all to Unicode.
+ self.smartQuotesTo = None
+ if convertEntities == self.HTML_ENTITIES:
+ self.convertXMLEntities = False
+ self.convertHTMLEntities = True
+ self.escapeUnrecognizedEntities = True
+ elif convertEntities == self.XHTML_ENTITIES:
+ self.convertXMLEntities = True
+ self.convertHTMLEntities = True
+ self.escapeUnrecognizedEntities = False
+ elif convertEntities == self.XML_ENTITIES:
+ self.convertXMLEntities = True
+ self.convertHTMLEntities = False
+ self.escapeUnrecognizedEntities = False
+ else:
+ self.convertXMLEntities = False
+ self.convertHTMLEntities = False
+ self.escapeUnrecognizedEntities = False
+
+ self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags)
+ SGMLParser.__init__(self)
+
+ if hasattr(markup, 'read'): # It's a file-type object.
+ markup = markup.read()
+ self.markup = markup
+ self.markupMassage = markupMassage
+ try:
+ self._feed(isHTML=isHTML)
+ except StopParsing:
+ pass
+ self.markup = None # The markup can now be GCed
+
+ def convert_charref(self, name):
+ """This method fixes a bug in Python's SGMLParser."""
+ try:
+ n = int(name)
+ except ValueError:
+ return
+ if not 0 <= n <= 127 : # ASCII ends at 127, not 255
+ return
+ return self.convert_codepoint(n)
+
+ def _feed(self, inDocumentEncoding=None, isHTML=False):
+ # Convert the document to Unicode.
+ markup = self.markup
+ if isinstance(markup, unicode):
+ if not hasattr(self, 'originalEncoding'):
+ self.originalEncoding = None
+ else:
+ dammit = UnicodeDammit\
+ (markup, [self.fromEncoding, inDocumentEncoding],
+ smartQuotesTo=self.smartQuotesTo, isHTML=isHTML)
+ markup = dammit.unicode
+ self.originalEncoding = dammit.originalEncoding
+ self.declaredHTMLEncoding = dammit.declaredHTMLEncoding
+ if markup:
+ if self.markupMassage:
+ if not hasattr(self.markupMassage, "__iter__"):
+ self.markupMassage = self.MARKUP_MASSAGE
+ for fix, m in self.markupMassage:
+ markup = fix.sub(m, markup)
+ # TODO: We get rid of markupMassage so that the
+ # soup object can be deepcopied later on. Some
+ # Python installations can't copy regexes. If anyone
+ # was relying on the existence of markupMassage, this
+ # might cause problems.
+ del(self.markupMassage)
+ self.reset()
+
+ SGMLParser.feed(self, markup)
+ # Close out any unfinished strings and close all the open tags.
+ self.endData()
+ while self.currentTag.name != self.ROOT_TAG_NAME:
+ self.popTag()
+
+ def __getattr__(self, methodName):
+ """This method routes method call requests to either the SGMLParser
+ superclass or the Tag superclass, depending on the method name."""
+ #print "__getattr__ called on %s.%s" % (self.__class__, methodName)
+
+ if methodName.startswith('start_') or methodName.startswith('end_') \
+ or methodName.startswith('do_'):
+ return SGMLParser.__getattr__(self, methodName)
+ elif not methodName.startswith('__'):
+ return Tag.__getattr__(self, methodName)
+ else:
+ raise AttributeError
+
+ def isSelfClosingTag(self, name):
+ """Returns true iff the given string is the name of a
+ self-closing tag according to this parser."""
+ return self.SELF_CLOSING_TAGS.has_key(name) \
+ or self.instanceSelfClosingTags.has_key(name)
+
+ def reset(self):
+ Tag.__init__(self, self, self.ROOT_TAG_NAME)
+ self.hidden = 1
+ SGMLParser.reset(self)
+ self.currentData = []
+ self.currentTag = None
+ self.tagStack = []
+ self.quoteStack = []
+ self.pushTag(self)
+
+ def popTag(self):
+ tag = self.tagStack.pop()
+
+ #print "Pop", tag.name
+ if self.tagStack:
+ self.currentTag = self.tagStack[-1]
+ return self.currentTag
+
+ def pushTag(self, tag):
+ #print "Push", tag.name
+ if self.currentTag:
+ self.currentTag.contents.append(tag)
+ self.tagStack.append(tag)
+ self.currentTag = self.tagStack[-1]
+
+ def endData(self, containerClass=NavigableString):
+ if self.currentData:
+ currentData = u''.join(self.currentData)
+ if (currentData.translate(self.STRIP_ASCII_SPACES) == '' and
+ not set([tag.name for tag in self.tagStack]).intersection(
+ self.PRESERVE_WHITESPACE_TAGS)):
+ if '\n' in currentData:
+ currentData = '\n'
+ else:
+ currentData = ' '
+ self.currentData = []
+ if self.parseOnlyThese and len(self.tagStack) <= 1 and \
+ (not self.parseOnlyThese.text or \
+ not self.parseOnlyThese.search(currentData)):
+ return
+ o = containerClass(currentData)
+ o.setup(self.currentTag, self.previous)
+ if self.previous:
+ self.previous.next = o
+ self.previous = o
+ self.currentTag.contents.append(o)
+
+
+ def _popToTag(self, name, inclusivePop=True):
+ """Pops the tag stack up to and including the most recent
+ instance of the given tag. If inclusivePop is false, pops the tag
+ stack up to but *not* including the most recent instqance of
+ the given tag."""
+ #print "Popping to %s" % name
+ if name == self.ROOT_TAG_NAME:
+ return
+
+ numPops = 0
+ mostRecentTag = None
+ for i in range(len(self.tagStack)-1, 0, -1):
+ if name == self.tagStack[i].name:
+ numPops = len(self.tagStack)-i
+ break
+ if not inclusivePop:
+ numPops = numPops - 1
+
+ for i in range(0, numPops):
+ mostRecentTag = self.popTag()
+ return mostRecentTag
+
+ def _smartPop(self, name):
+
+ """We need to pop up to the previous tag of this type, unless
+ one of this tag's nesting reset triggers comes between this
+ tag and the previous tag of this type, OR unless this tag is a
+ generic nesting trigger and another generic nesting trigger
+ comes between this tag and the previous tag of this type.
+
+ Examples:
+
FooBar *
* should pop to 'p', not 'b'. +
Foo
* | * should pop to 'tr', not the first 'td'
+ """
+
+ nestingResetTriggers = self.NESTABLE_TAGS.get(name)
+ isNestable = nestingResetTriggers != None
+ isResetNesting = self.RESET_NESTING_TAGS.has_key(name)
+ popTo = None
+ inclusive = True
+ for i in range(len(self.tagStack)-1, 0, -1):
+ p = self.tagStack[i]
+ if (not p or p.name == name) and not isNestable:
+ #Non-nestable tags get popped to the top or to their
+ #last occurance.
+ popTo = name
+ break
+ if (nestingResetTriggers is not None
+ and p.name in nestingResetTriggers) \
+ or (nestingResetTriggers is None and isResetNesting
+ and self.RESET_NESTING_TAGS.has_key(p.name)):
+
+ #If we encounter one of the nesting reset triggers
+ #peculiar to this tag, or we encounter another tag
+ #that causes nesting to reset, pop up to but not
+ #including that tag.
+ popTo = p.name
+ inclusive = False
+ break
+ p = p.parent
+ if popTo:
+ self._popToTag(popTo, inclusive)
+
+ def unknown_starttag(self, name, attrs, selfClosing=0):
+ #print "Start tag %s: %s" % (name, attrs)
+ if self.quoteStack:
+ #This is not a real tag.
+ #print "<%s> is not real!" % name
+ attrs = ''.join([' %s="%s"' % (x, y) for x, y in attrs])
+ self.handle_data('<%s%s>' % (name, attrs))
+ return
+ self.endData()
+
+ if not self.isSelfClosingTag(name) and not selfClosing:
+ self._smartPop(name)
+
+ if self.parseOnlyThese and len(self.tagStack) <= 1 \
+ and (self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs)):
+ return
+
+ tag = Tag(self, name, attrs, self.currentTag, self.previous)
+ if self.previous:
+ self.previous.next = tag
+ self.previous = tag
+ self.pushTag(tag)
+ if selfClosing or self.isSelfClosingTag(name):
+ self.popTag()
+ if name in self.QUOTE_TAGS:
+ #print "Beginning quote (%s)" % name
+ self.quoteStack.append(name)
+ self.literal = 1
+ return tag
+
+ def unknown_endtag(self, name):
+ #print "End tag %s" % name
+ if self.quoteStack and self.quoteStack[-1] != name:
+ #This is not a real end tag.
+ #print "%s> is not real!" % name
+ self.handle_data('%s>' % name)
+ return
+ self.endData()
+ self._popToTag(name)
+ if self.quoteStack and self.quoteStack[-1] == name:
+ self.quoteStack.pop()
+ self.literal = (len(self.quoteStack) > 0)
+
+ def handle_data(self, data):
+ self.currentData.append(data)
+
+ def _toStringSubclass(self, text, subclass):
+ """Adds a certain piece of text to the tree as a NavigableString
+ subclass."""
+ self.endData()
+ self.handle_data(text)
+ self.endData(subclass)
+
+ def handle_pi(self, text):
+ """Handle a processing instruction as a ProcessingInstruction
+ object, possibly one with a %SOUP-ENCODING% slot into which an
+ encoding will be plugged later."""
+ if text[:3] == "xml":
+ text = u"xml version='1.0' encoding='%SOUP-ENCODING%'"
+ self._toStringSubclass(text, ProcessingInstruction)
+
+ def handle_comment(self, text):
+ "Handle comments as Comment objects."
+ self._toStringSubclass(text, Comment)
+
+ def handle_charref(self, ref):
+ "Handle character references as data."
+ if self.convertEntities:
+ data = unichr(int(ref))
+ else:
+ data = '%s;' % ref
+ self.handle_data(data)
+
+ def handle_entityref(self, ref):
+ """Handle entity references as data, possibly converting known
+ HTML and/or XML entity references to the corresponding Unicode
+ characters."""
+ data = None
+ if self.convertHTMLEntities:
+ try:
+ data = unichr(name2codepoint[ref])
+ except KeyError:
+ pass
+
+ if not data and self.convertXMLEntities:
+ data = self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref)
+
+ if not data and self.convertHTMLEntities and \
+ not self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref):
+ # TODO: We've got a problem here. We're told this is
+ # an entity reference, but it's not an XML entity
+ # reference or an HTML entity reference. Nonetheless,
+ # the logical thing to do is to pass it through as an
+ # unrecognized entity reference.
+ #
+ # Except: when the input is "&carol;" this function
+ # will be called with input "carol". When the input is
+ # "AT&T", this function will be called with input
+ # "T". We have no way of knowing whether a semicolon
+ # was present originally, so we don't know whether
+ # this is an unknown entity or just a misplaced
+ # ampersand.
+ #
+ # The more common case is a misplaced ampersand, so I
+ # escape the ampersand and omit the trailing semicolon.
+ data = "&%s" % ref
+ if not data:
+ # This case is different from the one above, because we
+ # haven't already gone through a supposedly comprehensive
+ # mapping of entities to Unicode characters. We might not
+ # have gone through any mapping at all. So the chances are
+ # very high that this is a real entity, and not a
+ # misplaced ampersand.
+ data = "&%s;" % ref
+ self.handle_data(data)
+
+ def handle_decl(self, data):
+ "Handle DOCTYPEs and the like as Declaration objects."
+ self._toStringSubclass(data, Declaration)
+
+ def parse_declaration(self, i):
+ """Treat a bogus SGML declaration as raw data. Treat a CDATA
+ declaration as a CData object."""
+ j = None
+ if self.rawdata[i:i+9] == '', i)
+ if k == -1:
+ k = len(self.rawdata)
+ data = self.rawdata[i+9:k]
+ j = k+3
+ self._toStringSubclass(data, CData)
+ else:
+ try:
+ j = SGMLParser.parse_declaration(self, i)
+ except SGMLParseError:
+ toHandle = self.rawdata[i:]
+ self.handle_data(toHandle)
+ j = i + len(toHandle)
+ return j
+
+class BeautifulSoup(BeautifulStoneSoup):
+
+ """This parser knows the following facts about HTML:
+
+ * Some tags have no closing tag and should be interpreted as being
+ closed as soon as they are encountered.
+
+ * The text inside some tags (ie. 'script') may contain tags which
+ are not really part of the document and which should be parsed
+ as text, not tags. If you want to parse the text as tags, you can
+ always fetch it and parse it explicitly.
+
+ * Tag nesting rules:
+
+ Most tags can't be nested at all. For instance, the occurance of
+ a tag should implicitly close the previous tag. + + Para1 Para2 + should be transformed into: + Para1 Para2 + + Some tags can be nested arbitrarily. For instance, the occurance + of a tag should _not_ implicitly close the previous +tag. + + Alice said:Bob said:Blah + should NOT be transformed into: + Alice said:Bob said:Blah + + Some tags can be nested, but the nesting is reset by the + interposition of other tags. For instance, a |