summaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
Diffstat (limited to 'module')
-rw-r--r--module/Core.py632
-rw-r--r--module/HookManager.py315
-rw-r--r--module/InitHomeDir.py83
-rw-r--r--module/PluginThread.py677
-rw-r--r--module/__init__.py102
-rw-r--r--module/api/__init__.py (renamed from module/Api.py)288
-rw-r--r--module/api/types.py381
-rw-r--r--module/cli/AddPackage.py26
-rw-r--r--module/cli/Cli.py570
-rw-r--r--module/cli/Handler.py24
-rw-r--r--module/cli/ManageFiles.py50
-rw-r--r--module/cli/__init__.py6
-rw-r--r--module/cli/printer.py26
-rw-r--r--module/common/APIExerciser.py157
-rw-r--r--module/common/ImportDebugger.py19
-rw-r--r--module/common/JsEngine.py162
-rw-r--r--module/common/__init__.py2
-rw-r--r--module/common/json_layer.py13
-rw-r--r--module/common/packagetools.py155
-rw-r--r--module/config/Parser.py (renamed from module/ConfigParser.py)149
-rw-r--r--module/config/Setup.py553
-rw-r--r--module/config/__init__.py1
-rw-r--r--module/config/default.conf76
-rw-r--r--module/config/gui_default.xml13
-rw-r--r--module/database/DatabaseBackend.py164
-rw-r--r--module/database/File.py (renamed from module/database/FileDatabase.py)209
-rw-r--r--module/database/Storage.py (renamed from module/database/StorageDatabase.py)29
-rw-r--r--module/database/User.py (renamed from module/database/UserDatabase.py)31
-rw-r--r--module/database/__init__.py11
-rw-r--r--module/datatype/File.py (renamed from module/PyFile.py)101
-rw-r--r--module/datatype/Package.py (renamed from module/PyPackage.py)30
-rw-r--r--module/datatype/__init__.py1
-rw-r--r--module/debug.py95
-rw-r--r--module/forwarder.py73
-rw-r--r--module/gui/AccountEdit.py104
-rw-r--r--module/gui/Accounts.py213
-rw-r--r--module/gui/CNLServer.py226
-rw-r--r--module/gui/CaptchaDock.py94
-rw-r--r--module/gui/Collector.py407
-rw-r--r--module/gui/ConnectionManager.py302
-rw-r--r--module/gui/CoreConfigParser.py165
-rw-r--r--module/gui/LinkDock.py56
-rw-r--r--module/gui/MainWindow.py697
-rw-r--r--module/gui/Overview.py197
-rw-r--r--module/gui/PackageDock.py90
-rw-r--r--module/gui/Queue.py390
-rw-r--r--module/gui/SettingsWidget.py202
-rw-r--r--module/gui/XMLParser.py71
-rw-r--r--module/gui/__init__.py1
-rw-r--r--module/gui/connector.py165
-rw-r--r--module/lib/BeautifulSoup.py2012
-rw-r--r--module/lib/Getch.py76
-rw-r--r--module/lib/MultipartPostHandler.py139
-rw-r--r--module/lib/SafeEval.py47
-rw-r--r--module/lib/Unzip.py50
-rw-r--r--module/lib/__init__.py0
-rw-r--r--module/lib/beaker/__init__.py1
-rw-r--r--module/lib/beaker/cache.py459
-rw-r--r--module/lib/beaker/container.py633
-rw-r--r--module/lib/beaker/converters.py26
-rw-r--r--module/lib/beaker/crypto/__init__.py40
-rw-r--r--module/lib/beaker/crypto/jcecrypto.py30
-rw-r--r--module/lib/beaker/crypto/pbkdf2.py342
-rw-r--r--module/lib/beaker/crypto/pycrypto.py31
-rw-r--r--module/lib/beaker/crypto/util.py30
-rw-r--r--module/lib/beaker/exceptions.py24
-rw-r--r--module/lib/beaker/ext/__init__.py0
-rw-r--r--module/lib/beaker/ext/database.py165
-rw-r--r--module/lib/beaker/ext/google.py120
-rw-r--r--module/lib/beaker/ext/memcached.py82
-rw-r--r--module/lib/beaker/ext/sqla.py133
-rw-r--r--module/lib/beaker/middleware.py165
-rw-r--r--module/lib/beaker/session.py618
-rw-r--r--module/lib/beaker/synchronization.py381
-rw-r--r--module/lib/beaker/util.py302
-rw-r--r--module/lib/bottle.py2922
-rw-r--r--module/lib/feedparser.py3885
-rw-r--r--module/lib/jinja2/__init__.py73
-rw-r--r--module/lib/jinja2/_markupsafe/__init__.py225
-rw-r--r--module/lib/jinja2/_markupsafe/_bundle.py49
-rw-r--r--module/lib/jinja2/_markupsafe/_constants.py267
-rw-r--r--module/lib/jinja2/_markupsafe/_native.py45
-rw-r--r--module/lib/jinja2/_markupsafe/tests.py80
-rw-r--r--module/lib/jinja2/_stringdefs.py130
-rw-r--r--module/lib/jinja2/bccache.py280
-rw-r--r--module/lib/jinja2/compiler.py1640
-rw-r--r--module/lib/jinja2/constants.py32
-rw-r--r--module/lib/jinja2/debug.py308
-rw-r--r--module/lib/jinja2/defaults.py40
-rw-r--r--module/lib/jinja2/environment.py1118
-rw-r--r--module/lib/jinja2/exceptions.py143
-rw-r--r--module/lib/jinja2/ext.py610
-rw-r--r--module/lib/jinja2/filters.py719
-rw-r--r--module/lib/jinja2/lexer.py681
-rw-r--r--module/lib/jinja2/loaders.py449
-rw-r--r--module/lib/jinja2/meta.py102
-rw-r--r--module/lib/jinja2/nodes.py901
-rw-r--r--module/lib/jinja2/optimizer.py68
-rw-r--r--module/lib/jinja2/parser.py896
-rw-r--r--module/lib/jinja2/runtime.py544
-rw-r--r--module/lib/jinja2/sandbox.py271
-rw-r--r--module/lib/jinja2/tests.py146
-rw-r--r--module/lib/jinja2/utils.py601
-rw-r--r--module/lib/jinja2/visitor.py87
-rw-r--r--module/lib/rename_process.py14
-rw-r--r--module/lib/simplejson/__init__.py466
-rw-r--r--module/lib/simplejson/decoder.py421
-rw-r--r--module/lib/simplejson/encoder.py534
-rw-r--r--module/lib/simplejson/ordered_dict.py119
-rw-r--r--module/lib/simplejson/scanner.py77
-rw-r--r--module/lib/simplejson/tool.py39
-rw-r--r--module/lib/thrift/TSCons.py33
-rw-r--r--module/lib/thrift/TSerialization.py34
-rw-r--r--module/lib/thrift/Thrift.py154
-rw-r--r--module/lib/thrift/__init__.py20
-rw-r--r--module/lib/thrift/protocol/TBase.py72
-rw-r--r--module/lib/thrift/protocol/TBinaryProtocol.py259
-rw-r--r--module/lib/thrift/protocol/TCompactProtocol.py395
-rw-r--r--module/lib/thrift/protocol/TProtocol.py404
-rw-r--r--module/lib/thrift/protocol/__init__.py20
-rw-r--r--module/lib/thrift/server/THttpServer.py82
-rw-r--r--module/lib/thrift/server/TNonblockingServer.py310
-rw-r--r--module/lib/thrift/server/TProcessPoolServer.py125
-rw-r--r--module/lib/thrift/server/TServer.py274
-rw-r--r--module/lib/thrift/server/__init__.py20
-rw-r--r--module/lib/thrift/transport/THttpClient.py126
-rw-r--r--module/lib/thrift/transport/TSocket.py163
-rw-r--r--module/lib/thrift/transport/TTransport.py331
-rw-r--r--module/lib/thrift/transport/TTwisted.py219
-rw-r--r--module/lib/thrift/transport/TZlibTransport.py261
-rw-r--r--module/lib/thrift/transport/__init__.py20
-rw-r--r--module/lib/wsgiserver/LICENSE.txt25
-rw-r--r--module/lib/wsgiserver/__init__.py1794
-rw-r--r--module/manager/Account.py (renamed from module/plugins/AccountManager.py)164
-rw-r--r--module/manager/Addon.py304
-rw-r--r--module/manager/Captcha.py (renamed from module/CaptchaManager.py)78
-rw-r--r--module/manager/Event.py (renamed from module/PullEvents.py)60
-rw-r--r--module/manager/Plugin.py404
-rw-r--r--module/manager/Remote.py (renamed from module/remote/RemoteManager.py)21
-rw-r--r--module/manager/Thread.py (renamed from module/ThreadManager.py)92
-rw-r--r--module/manager/__init__.py1
-rw-r--r--module/manager/event/Scheduler.py (renamed from module/Scheduler.py)35
-rw-r--r--module/manager/event/__init__.py1
-rw-r--r--module/manager/thread/Addon.py69
-rw-r--r--module/manager/thread/Decrypter.py101
-rw-r--r--module/manager/thread/Download.py213
-rw-r--r--module/manager/thread/Info.py225
-rw-r--r--module/manager/thread/Plugin.py130
-rw-r--r--module/manager/thread/Server.py (renamed from module/web/ServerThread.py)64
-rw-r--r--module/manager/thread/__init__.py1
-rw-r--r--module/network/Browser.py18
-rw-r--r--module/network/Bucket.py45
-rw-r--r--module/network/CookieJar.py66
-rw-r--r--module/network/HTTPChunk.py53
-rw-r--r--module/network/HTTPDownload.py66
-rw-r--r--module/network/HTTPRequest.py104
-rw-r--r--module/network/JsEngine.py256
-rw-r--r--module/network/RequestFactory.py45
-rw-r--r--module/network/XDCCRequest.py84
-rw-r--r--module/network/__init__.py2
-rw-r--r--module/plugins/Account.py167
-rw-r--r--module/plugins/Addon.py185
-rw-r--r--module/plugins/Captcha.py51
-rw-r--r--module/plugins/Container.py79
-rw-r--r--module/plugins/Crypter.py133
-rw-r--r--module/plugins/Hook.py161
-rw-r--r--module/plugins/Hoster.py34
-rw-r--r--module/plugins/OCR.py (renamed from module/plugins/captcha/captcha.py)23
-rw-r--r--module/plugins/Plugin.py422
-rw-r--r--module/plugins/PluginManager.py380
-rw-r--r--module/plugins/__init__.py1
-rw-r--r--module/plugins/accounts/AlldebridCom.py2
-rw-r--r--module/plugins/accounts/BillionuploadsCom.py2
-rw-r--r--module/plugins/accounts/BitshareCom.py4
-rw-r--r--module/plugins/accounts/CatShareNet.py4
-rw-r--r--module/plugins/accounts/CramitIn.py2
-rw-r--r--module/plugins/accounts/CzshareCom.py2
-rw-r--r--module/plugins/accounts/DebridItaliaCom.py6
-rw-r--r--module/plugins/accounts/DepositfilesCom.py2
-rw-r--r--module/plugins/accounts/DropboxCom.py (renamed from module/plugins/hoster/DropboxCom.py)2
-rw-r--r--module/plugins/accounts/EasybytezCom.py2
-rw-r--r--module/plugins/accounts/EuroshareEu.py2
-rw-r--r--module/plugins/accounts/FastixRu.py4
-rw-r--r--module/plugins/accounts/FastshareCz.py3
-rw-r--r--module/plugins/accounts/File4SafeCom.py2
-rw-r--r--module/plugins/accounts/FileParadoxIn.py2
-rw-r--r--module/plugins/accounts/FilecloudIo.py4
-rw-r--r--module/plugins/accounts/FilefactoryCom.py2
-rw-r--r--module/plugins/accounts/FilejungleCom.py2
-rw-r--r--module/plugins/accounts/FileomCom.py2
-rw-r--r--module/plugins/accounts/FilerNet.py2
-rw-r--r--module/plugins/accounts/FilerioCom.py2
-rw-r--r--module/plugins/accounts/FilesMailRu.py2
-rw-r--r--module/plugins/accounts/FileserveCom.py4
-rw-r--r--module/plugins/accounts/FourSharedCom.py3
-rw-r--r--module/plugins/accounts/FreakshareCom.py2
-rw-r--r--module/plugins/accounts/FreeWayMe.py4
-rw-r--r--module/plugins/accounts/FshareVn.py2
-rw-r--r--module/plugins/accounts/Ftp.py6
-rw-r--r--module/plugins/accounts/HellshareCz.py2
-rw-r--r--module/plugins/accounts/Http.py6
-rw-r--r--module/plugins/accounts/HugefilesNet.py2
-rw-r--r--module/plugins/accounts/HundredEightyUploadCom.py2
-rw-r--r--module/plugins/accounts/JunocloudMe.py2
-rw-r--r--module/plugins/accounts/Keep2ShareCc.py57
-rw-r--r--module/plugins/accounts/LetitbitNet.py4
-rw-r--r--module/plugins/accounts/LinestorageCom.py2
-rw-r--r--module/plugins/accounts/LinksnappyCom.py4
-rw-r--r--module/plugins/accounts/MegaDebridEu.py4
-rw-r--r--module/plugins/accounts/MegaRapidCz.py2
-rw-r--r--module/plugins/accounts/MegasharesCom.py2
-rw-r--r--module/plugins/accounts/MovReelCom.py2
-rw-r--r--module/plugins/accounts/MultishareCz.py2
-rw-r--r--module/plugins/accounts/MyfastfileCom.py4
-rw-r--r--module/plugins/accounts/NetloadIn.py2
-rw-r--r--module/plugins/accounts/NosuploadCom.py2
-rw-r--r--module/plugins/accounts/NovafileCom.py2
-rw-r--r--module/plugins/accounts/NowVideoSx.py2
-rw-r--r--module/plugins/accounts/OboomCom.py4
-rw-r--r--module/plugins/accounts/OneFichierCom.py2
-rw-r--r--module/plugins/accounts/OverLoadMe.py4
-rw-r--r--module/plugins/accounts/PremiumTo.py3
-rw-r--r--module/plugins/accounts/PremiumizeMe.py4
-rw-r--r--module/plugins/accounts/QuickshareCz.py2
-rw-r--r--module/plugins/accounts/RPNetBiz.py4
-rw-r--r--module/plugins/accounts/RapidfileshareNet.py2
-rw-r--r--module/plugins/accounts/RapidgatorNet.py4
-rw-r--r--module/plugins/accounts/RarefileNet.py2
-rw-r--r--module/plugins/accounts/RealdebridCom.py2
-rw-r--r--module/plugins/accounts/RehostTo.py2
-rw-r--r--module/plugins/accounts/RyushareCom.py2
-rw-r--r--module/plugins/accounts/SafesharingEu.py2
-rw-r--r--module/plugins/accounts/SecureUploadEu.py2
-rw-r--r--module/plugins/accounts/SendmywayCom.py2
-rw-r--r--module/plugins/accounts/ShareonlineBiz.py2
-rw-r--r--module/plugins/accounts/SimplyPremiumCom.py4
-rw-r--r--module/plugins/accounts/SimplydebridCom.py2
-rw-r--r--module/plugins/accounts/StahnuTo.py2
-rw-r--r--module/plugins/accounts/StreamcloudEu.py2
-rw-r--r--module/plugins/accounts/TurbobitNet.py2
-rw-r--r--module/plugins/accounts/TusfilesNet.py4
-rw-r--r--module/plugins/accounts/UlozTo.py4
-rw-r--r--module/plugins/accounts/UnrestrictLi.py4
-rw-r--r--module/plugins/accounts/UploadcCom.py2
-rw-r--r--module/plugins/accounts/UploadedTo.py2
-rw-r--r--module/plugins/accounts/UploadheroCom.py2
-rw-r--r--module/plugins/accounts/UploadingCom.py4
-rw-r--r--module/plugins/accounts/UptoboxCom.py2
-rw-r--r--module/plugins/accounts/VidPlayNet.py2
-rw-r--r--module/plugins/accounts/XFileSharingPro.py2
-rw-r--r--module/plugins/accounts/YibaishiwuCom.py2
-rw-r--r--module/plugins/accounts/ZeveraCom.py2
-rw-r--r--module/plugins/accounts/__init__.py1
-rw-r--r--module/plugins/addon/Checksum.py (renamed from module/plugins/hooks/Checksum.py)34
-rw-r--r--module/plugins/addon/ClickAndLoad.py74
-rw-r--r--module/plugins/addon/DeleteFinished.py (renamed from module/plugins/hooks/DeleteFinished.py)18
-rw-r--r--module/plugins/addon/DownloadScheduler.py (renamed from module/plugins/hooks/DownloadScheduler.py)20
-rw-r--r--module/plugins/addon/ExternalScripts.py (renamed from module/plugins/hooks/ExternalScripts.py)33
-rw-r--r--module/plugins/addon/ExtractArchive.py (renamed from module/plugins/hooks/ExtractArchive.py)0
-rw-r--r--module/plugins/addon/HotFolder.py (renamed from module/plugins/hooks/HotFolder.py)22
-rw-r--r--module/plugins/addon/IRCInterface.py (renamed from module/plugins/hooks/IRCInterface.py)45
-rw-r--r--module/plugins/addon/MergeFiles.py (renamed from module/plugins/hooks/MergeFiles.py)20
-rw-r--r--module/plugins/addon/MultiHome.py (renamed from module/plugins/hooks/MultiHome.py)14
-rw-r--r--module/plugins/addon/RestartFailed.py (renamed from module/plugins/hooks/RestartFailed.py)13
-rw-r--r--module/plugins/addon/RestartSlow.py (renamed from module/plugins/hooks/RestartSlow.py)22
-rw-r--r--module/plugins/addon/SkipRev.py (renamed from module/plugins/hooks/SkipRev.py)0
-rw-r--r--module/plugins/addon/UnSkipOnFail.py (renamed from module/plugins/hooks/UnSkipOnFail.py)0
-rw-r--r--module/plugins/addon/UpdateManager.py (renamed from module/plugins/hooks/UpdateManager.py)60
-rw-r--r--module/plugins/addon/XMPPInterface.py (renamed from module/plugins/hooks/XMPPInterface.py)20
-rw-r--r--module/plugins/addon/__init__.py1
-rw-r--r--module/plugins/captcha/AdYouLike.py107
-rw-r--r--module/plugins/captcha/AdsCaptcha.py77
-rw-r--r--module/plugins/captcha/ReCaptcha.py73
-rw-r--r--module/plugins/captcha/SolveMedia.py50
-rw-r--r--module/plugins/captcha/__init__.py1
-rw-r--r--module/plugins/container/__init__.py1
-rw-r--r--module/plugins/crypter/BitshareCom.py (renamed from module/plugins/crypter/BitshareComFolder.py)6
-rw-r--r--module/plugins/crypter/C1NeonCom.py2
-rw-r--r--module/plugins/crypter/ChipDe.py2
-rw-r--r--module/plugins/crypter/CrockoCom.py (renamed from module/plugins/crypter/CrockoComFolder.py)6
-rw-r--r--module/plugins/crypter/CryptItCom.py2
-rw-r--r--module/plugins/crypter/CzshareCom.py (renamed from module/plugins/crypter/CzshareComFolder.py)6
-rw-r--r--module/plugins/crypter/DDLMusicOrg.py2
-rw-r--r--module/plugins/crypter/DailymotionBatch.py8
-rw-r--r--module/plugins/crypter/DataHu.py (renamed from module/plugins/crypter/DataHuFolder.py)10
-rw-r--r--module/plugins/crypter/DdlstorageCom.py (renamed from module/plugins/crypter/DdlstorageComFolder.py)8
-rw-r--r--module/plugins/crypter/DepositfilesCom.py (renamed from module/plugins/crypter/DepositfilesComFolder.py)6
-rw-r--r--module/plugins/crypter/Dereferer.py2
-rw-r--r--module/plugins/crypter/DevhostStFolder.py2
-rw-r--r--module/plugins/crypter/DlProtectCom.py2
-rw-r--r--module/plugins/crypter/DontKnowMe.py2
-rw-r--r--module/plugins/crypter/DuckCryptInfo.py2
-rw-r--r--module/plugins/crypter/DuploadOrg.py (renamed from module/plugins/crypter/DuploadOrgFolder.py)8
-rw-r--r--module/plugins/crypter/EasybytezCom.py (renamed from module/plugins/crypter/EasybytezComFolder.py)6
-rw-r--r--module/plugins/crypter/EmbeduploadCom.py10
-rw-r--r--module/plugins/crypter/FilebeerInfo.py (renamed from module/plugins/crypter/FilebeerInfoFolder.py)8
-rw-r--r--module/plugins/crypter/FilecloudIo.py (renamed from module/plugins/crypter/FilecloudIoFolder.py)6
-rw-r--r--module/plugins/crypter/FilecryptCc.py8
-rw-r--r--module/plugins/crypter/FilefactoryCom.py (renamed from module/plugins/crypter/FilefactoryComFolder.py)6
-rw-r--r--module/plugins/crypter/FilerNet.py (renamed from module/plugins/crypter/FilerNetFolder.py)6
-rw-r--r--module/plugins/crypter/FileserveCom.py (renamed from module/plugins/crypter/FileserveComFolder.py)6
-rw-r--r--module/plugins/crypter/FilesonicCom.py (renamed from module/plugins/crypter/FilesonicComFolder.py)8
-rw-r--r--module/plugins/crypter/FilestubeCom.py2
-rw-r--r--module/plugins/crypter/FiletramCom.py2
-rw-r--r--module/plugins/crypter/FiredriveCom.py (renamed from module/plugins/crypter/FiredriveComFolder.py)8
-rw-r--r--module/plugins/crypter/FourChanOrg.py2
-rw-r--r--module/plugins/crypter/FreakhareCom.py (renamed from module/plugins/crypter/FreakhareComFolder.py)6
-rw-r--r--module/plugins/crypter/FreetexthostCom.py2
-rw-r--r--module/plugins/crypter/FshareVn.py (renamed from module/plugins/crypter/FshareVnFolder.py)6
-rw-r--r--module/plugins/crypter/Go4UpCom.py2
-rw-r--r--module/plugins/crypter/GooGl.py4
-rw-r--r--module/plugins/crypter/HoerbuchIn.py2
-rw-r--r--module/plugins/crypter/HotfileCom.py (renamed from module/plugins/crypter/HotfileComFolder.py)8
-rw-r--r--module/plugins/crypter/ILoadTo.py4
-rw-r--r--module/plugins/crypter/LetitbitNet.py (renamed from module/plugins/crypter/LetitbitNetFolder.py)6
-rw-r--r--module/plugins/crypter/LinkCryptWs.py8
-rw-r--r--module/plugins/crypter/LinkdecrypterCom.py4
-rw-r--r--module/plugins/crypter/LixIn.py2
-rw-r--r--module/plugins/crypter/LofCc.py2
-rw-r--r--module/plugins/crypter/MBLinkInfo.py2
-rw-r--r--module/plugins/crypter/MediafireCom.py (renamed from module/plugins/crypter/MediafireComFolder.py)10
-rw-r--r--module/plugins/crypter/MegaRapidCz.py (renamed from module/plugins/crypter/MegaRapidCzFolder.py)4
-rw-r--r--module/plugins/crypter/MegauploadCom.py (renamed from module/plugins/crypter/MegauploadComFolder.py)8
-rw-r--r--module/plugins/crypter/Movie2KTo.py2
-rw-r--r--module/plugins/crypter/MultiUpOrg.py2
-rw-r--r--module/plugins/crypter/MultiloadCz.py8
-rw-r--r--module/plugins/crypter/MultiuploadCom.py5
-rw-r--r--module/plugins/crypter/NCryptIn.py4
-rw-r--r--module/plugins/crypter/OneKhDe.py7
-rw-r--r--module/plugins/crypter/OronCom.py (renamed from module/plugins/crypter/OronComFolder.py)8
-rw-r--r--module/plugins/crypter/QuickshareCz.py (renamed from module/plugins/crypter/QuickshareCzFolder.py)6
-rw-r--r--module/plugins/crypter/RSLayerCom.py4
-rw-r--r--module/plugins/crypter/RelinkUs.py4
-rw-r--r--module/plugins/crypter/SafelinkingNet.py8
-rw-r--r--module/plugins/crypter/SecuredIn.py2
-rw-r--r--module/plugins/crypter/SexuriaCom.py2
-rw-r--r--module/plugins/crypter/ShareLinksBiz.py2
-rw-r--r--module/plugins/crypter/SharingmatrixCom.py (renamed from module/plugins/crypter/SharingmatrixComFolder.py)8
-rw-r--r--module/plugins/crypter/SpeedLoadOrg.py (renamed from module/plugins/crypter/SpeedLoadOrgFolder.py)8
-rw-r--r--module/plugins/crypter/StealthTo.py2
-rw-r--r--module/plugins/crypter/TrailerzoneInfo.py2
-rw-r--r--module/plugins/crypter/TurbobitNet.py (renamed from module/plugins/crypter/TurbobitNetFolder.py)6
-rw-r--r--module/plugins/crypter/TusfilesNet.py (renamed from module/plugins/crypter/TusfilesNetFolder.py)4
-rw-r--r--module/plugins/crypter/UlozTo.py (renamed from module/plugins/crypter/UlozToFolder.py)6
-rw-r--r--module/plugins/crypter/UploadableCh.py (renamed from module/plugins/crypter/UploadableChFolder.py)6
-rw-r--r--module/plugins/crypter/UploadedTo.py (renamed from module/plugins/crypter/UploadedToFolder.py)4
-rw-r--r--module/plugins/crypter/WiiReloadedOrg.py4
-rw-r--r--module/plugins/crypter/WuploadCom.py (renamed from module/plugins/crypter/WuploadComFolder.py)8
-rw-r--r--module/plugins/crypter/XFileSharingPro.py (renamed from module/plugins/crypter/XFileSharingProFolder.py)6
-rw-r--r--module/plugins/crypter/XupPl.py2
-rw-r--r--module/plugins/crypter/YoutubeBatch.py16
-rw-r--r--module/plugins/crypter/__init__.py1
-rw-r--r--module/plugins/hooks/BypassCaptcha.py17
-rw-r--r--module/plugins/hooks/Captcha9Kw.py36
-rw-r--r--module/plugins/hooks/CaptchaBrotherhood.py19
-rw-r--r--module/plugins/hooks/DeathByCaptcha.py23
-rw-r--r--module/plugins/hooks/ExpertDecoders.py15
-rw-r--r--module/plugins/hooks/FastixRu.py2
-rw-r--r--module/plugins/hooks/ImageTyperz.py17
-rw-r--r--module/plugins/hooks/LinksnappyCom.py2
-rw-r--r--module/plugins/hooks/MegaDebridEu.py2
-rw-r--r--module/plugins/hooks/MyfastfileCom.py2
-rw-r--r--module/plugins/hooks/PremiumizeMe.py2
-rw-r--r--module/plugins/hooks/RPNetBiz.py2
-rw-r--r--module/plugins/hooks/SimplyPremiumCom.py2
-rw-r--r--module/plugins/hooks/UnrestrictLi.py2
-rw-r--r--module/plugins/hooks/XFileSharingPro.py44
-rw-r--r--module/plugins/hooks/__init__.py1
-rw-r--r--module/plugins/hoster/BezvadataCz.py2
-rw-r--r--module/plugins/hoster/BillionuploadsCom.py2
-rw-r--r--module/plugins/hoster/BitshareCom.py6
-rw-r--r--module/plugins/hoster/BoltsharingCom.py2
-rw-r--r--module/plugins/hoster/CatShareNet.py6
-rw-r--r--module/plugins/hoster/CloudzerNet.py2
-rw-r--r--module/plugins/hoster/CramitIn.py2
-rw-r--r--module/plugins/hoster/CrockoCom.py4
-rw-r--r--module/plugins/hoster/CyberlockerCh.py2
-rw-r--r--module/plugins/hoster/CzshareCom.py4
-rw-r--r--module/plugins/hoster/DailymotionCom.py8
-rw-r--r--module/plugins/hoster/DataHu.py4
-rw-r--r--module/plugins/hoster/DataportCz.py2
-rw-r--r--module/plugins/hoster/DateiTo.py4
-rw-r--r--module/plugins/hoster/DdlstorageCom.py2
-rw-r--r--module/plugins/hoster/DepositfilesCom.py4
-rw-r--r--module/plugins/hoster/DevhostSt.py2
-rw-r--r--module/plugins/hoster/DlFreeFr.py9
-rw-r--r--module/plugins/hoster/DuploadOrg.py2
-rw-r--r--module/plugins/hoster/EasybytezCom.py2
-rw-r--r--module/plugins/hoster/EdiskCz.py2
-rw-r--r--module/plugins/hoster/EgoFilesCom.py2
-rw-r--r--module/plugins/hoster/EnteruploadCom.py2
-rw-r--r--module/plugins/hoster/EpicShareNet.py2
-rw-r--r--module/plugins/hoster/EuroshareEu.py2
-rw-r--r--module/plugins/hoster/ExtabitCom.py2
-rw-r--r--module/plugins/hoster/FastixRu.py2
-rw-r--r--module/plugins/hoster/FileApeCom.py4
-rw-r--r--module/plugins/hoster/FileSharkPl.py4
-rw-r--r--module/plugins/hoster/FileStoreTo.py2
-rw-r--r--module/plugins/hoster/FilebeerInfo.py2
-rw-r--r--module/plugins/hoster/FilecloudIo.py6
-rw-r--r--module/plugins/hoster/FilefactoryCom.py4
-rw-r--r--module/plugins/hoster/FilejungleCom.py4
-rw-r--r--module/plugins/hoster/FileomCom.py2
-rw-r--r--module/plugins/hoster/FilepostCom.py6
-rw-r--r--module/plugins/hoster/FilepupNet.py2
-rw-r--r--module/plugins/hoster/FilerNet.py4
-rw-r--r--module/plugins/hoster/FilerioCom.py2
-rw-r--r--module/plugins/hoster/FilesMailRu.py6
-rw-r--r--module/plugins/hoster/FileserveCom.py2
-rw-r--r--module/plugins/hoster/FileshareInUa.py2
-rw-r--r--module/plugins/hoster/FilesonicCom.py4
-rw-r--r--module/plugins/hoster/FilezyNet.py2
-rw-r--r--module/plugins/hoster/FiredriveCom.py2
-rw-r--r--module/plugins/hoster/FlyFilesNet.py4
-rw-r--r--module/plugins/hoster/FourSharedCom.py2
-rw-r--r--module/plugins/hoster/FreevideoCz.py2
-rw-r--r--module/plugins/hoster/FshareVn.py4
-rw-r--r--module/plugins/hoster/Ftp.py2
-rw-r--r--module/plugins/hoster/GamefrontCom.py6
-rw-r--r--module/plugins/hoster/GigapetaCom.py17
-rw-r--r--module/plugins/hoster/GooIm.py2
-rw-r--r--module/plugins/hoster/HellshareCz.py2
-rw-r--r--module/plugins/hoster/HellspyCz.py2
-rw-r--r--module/plugins/hoster/HotfileCom.py2
-rw-r--r--module/plugins/hoster/HugefilesNet.py2
-rw-r--r--module/plugins/hoster/HundredEightyUploadCom.py2
-rw-r--r--module/plugins/hoster/IFileWs.py2
-rw-r--r--module/plugins/hoster/IcyFilesCom.py2
-rw-r--r--module/plugins/hoster/IfolderRu.py2
-rw-r--r--module/plugins/hoster/JumbofilesCom.py2
-rw-r--r--module/plugins/hoster/JunocloudMe.py2
-rw-r--r--module/plugins/hoster/Keep2ShareCc.py2
-rw-r--r--module/plugins/hoster/KickloadCom.py2
-rw-r--r--module/plugins/hoster/KingfilesNet.py4
-rw-r--r--module/plugins/hoster/LemUploadsCom.py2
-rw-r--r--module/plugins/hoster/LoadTo.py6
-rw-r--r--module/plugins/hoster/LuckyShareNet.py4
-rw-r--r--module/plugins/hoster/MediafireCom.py6
-rw-r--r--module/plugins/hoster/MegaFilesSe.py2
-rw-r--r--module/plugins/hoster/MegacrypterCom.py4
-rw-r--r--module/plugins/hoster/MegareleaseOrg.py2
-rw-r--r--module/plugins/hoster/MegasharesCom.py2
-rw-r--r--module/plugins/hoster/MegauploadCom.py2
-rw-r--r--module/plugins/hoster/MegavideoCom.py2
-rw-r--r--module/plugins/hoster/MovReelCom.py2
-rw-r--r--module/plugins/hoster/MultishareCz.py2
-rw-r--r--module/plugins/hoster/MyfastfileCom.py1
-rw-r--r--module/plugins/hoster/MyvideoDe.py8
-rw-r--r--module/plugins/hoster/NahrajCz.py2
-rw-r--r--module/plugins/hoster/NarodRu.py2
-rw-r--r--module/plugins/hoster/NetloadIn.py10
-rw-r--r--module/plugins/hoster/NosuploadCom.py2
-rw-r--r--module/plugins/hoster/NovafileCom.py2
-rw-r--r--module/plugins/hoster/NowDownloadSx.py4
-rw-r--r--module/plugins/hoster/NowVideoSx.py2
-rw-r--r--module/plugins/hoster/OboomCom.py6
-rw-r--r--module/plugins/hoster/OneFichierCom.py4
-rw-r--r--module/plugins/hoster/OronCom.py2
-rw-r--r--module/plugins/hoster/PandaplaNet.py2
-rw-r--r--module/plugins/hoster/PornhostCom.py2
-rw-r--r--module/plugins/hoster/PornhubCom.py2
-rw-r--r--module/plugins/hoster/PotloadCom.py2
-rw-r--r--module/plugins/hoster/PromptfileCom.py2
-rw-r--r--module/plugins/hoster/PrzeklejPl.py2
-rw-r--r--module/plugins/hoster/QuickshareCz.py10
-rw-r--r--module/plugins/hoster/RapidfileshareNet.py2
-rw-r--r--module/plugins/hoster/RapidgatorNet.py2
-rw-r--r--module/plugins/hoster/RapiduNet.py6
-rw-r--r--module/plugins/hoster/RarefileNet.py2
-rw-r--r--module/plugins/hoster/RedtubeCom.py6
-rw-r--r--module/plugins/hoster/RemixshareCom.py4
-rw-r--r--module/plugins/hoster/RgHostNet.py2
-rw-r--r--module/plugins/hoster/SafesharingEu.py2
-rw-r--r--module/plugins/hoster/SecureUploadEu.py2
-rw-r--r--module/plugins/hoster/SendspaceCom.py2
-rw-r--r--module/plugins/hoster/Share4WebCom.py4
-rw-r--r--module/plugins/hoster/Share76Com.py2
-rw-r--r--module/plugins/hoster/ShareFilesCo.py2
-rw-r--r--module/plugins/hoster/SharebeesCom.py2
-rw-r--r--module/plugins/hoster/ShareonlineBiz.py6
-rw-r--r--module/plugins/hoster/ShareplaceCom.py4
-rw-r--r--module/plugins/hoster/SharingmatrixCom.py4
-rw-r--r--module/plugins/hoster/ShragleCom.py2
-rw-r--r--module/plugins/hoster/SockshareCom.py2
-rw-r--r--module/plugins/hoster/SoundcloudCom.py2
-rw-r--r--module/plugins/hoster/SpeedLoadOrg.py2
-rw-r--r--module/plugins/hoster/SpeedfileCz.py2
-rw-r--r--module/plugins/hoster/SpeedyshareCom.py2
-rw-r--r--module/plugins/hoster/StorageTo.py2
-rw-r--r--module/plugins/hoster/StreamCz.py4
-rw-r--r--module/plugins/hoster/StreamcloudEu.py2
-rw-r--r--module/plugins/hoster/TurbobitNet.py10
-rw-r--r--module/plugins/hoster/TurbouploadCom.py2
-rw-r--r--module/plugins/hoster/TwoSharedCom.py2
-rw-r--r--module/plugins/hoster/UlozTo.py4
-rw-r--r--module/plugins/hoster/UloziskoSk.py2
-rw-r--r--module/plugins/hoster/UnibytesCom.py9
-rw-r--r--module/plugins/hoster/UpleaCom.py4
-rw-r--r--module/plugins/hoster/UploadStationCom.py2
-rw-r--r--module/plugins/hoster/UploadableCh.py4
-rw-r--r--module/plugins/hoster/UploadboxCom.py2
-rw-r--r--module/plugins/hoster/UploadhereCom.py2
-rw-r--r--module/plugins/hoster/UploadheroCom.py2
-rw-r--r--module/plugins/hoster/UploadingCom.py4
-rw-r--r--module/plugins/hoster/UploadkingCom.py2
-rw-r--r--module/plugins/hoster/UpstoreNet.py4
-rw-r--r--module/plugins/hoster/UptoboxCom.py2
-rw-r--r--module/plugins/hoster/VeehdCom.py4
-rw-r--r--module/plugins/hoster/VeohCom.py2
-rw-r--r--module/plugins/hoster/VidPlayNet.py2
-rw-r--r--module/plugins/hoster/VimeoCom.py4
-rw-r--r--module/plugins/hoster/Vipleech4UCom.py2
-rw-r--r--module/plugins/hoster/WarserverCz.py2
-rw-r--r--module/plugins/hoster/WrzucTo.py2
-rw-r--r--module/plugins/hoster/WuploadCom.py4
-rw-r--r--module/plugins/hoster/X7To.py2
-rw-r--r--module/plugins/hoster/XFileSharingPro.py2
-rw-r--r--module/plugins/hoster/XHamsterCom.py4
-rw-r--r--module/plugins/hoster/XVideosCom.py2
-rw-r--r--module/plugins/hoster/Xdcc.py10
-rw-r--r--module/plugins/hoster/YibaishiwuCom.py4
-rw-r--r--module/plugins/hoster/YoupornCom.py2
-rw-r--r--module/plugins/hoster/YourfilesTo.py2
-rw-r--r--module/plugins/hoster/YoutubeCom.py6
-rw-r--r--module/plugins/hoster/ZDF.py2
-rw-r--r--module/plugins/hoster/ZShareNet.py6
-rw-r--r--module/plugins/hoster/ZippyshareCom.py2
-rw-r--r--module/plugins/hoster/__init__.py1
-rw-r--r--module/plugins/internal/BasePlugin.py (renamed from module/plugins/hoster/BasePlugin.py)2
-rw-r--r--module/plugins/internal/DeadCrypter.py6
-rw-r--r--module/plugins/internal/DeadHoster.py6
-rw-r--r--module/plugins/internal/SimpleCrypter.py19
-rw-r--r--module/plugins/internal/UpdateManager.py300
-rw-r--r--module/plugins/internal/XFSAccount.py9
-rw-r--r--module/plugins/internal/XFSHoster.py12
-rw-r--r--module/plugins/internal/__init__.py1
-rw-r--r--module/plugins/ocr/GigasizeCom.py (renamed from module/plugins/captcha/GigasizeCom.py)2
-rw-r--r--module/plugins/ocr/LinksaveIn.py (renamed from module/plugins/captcha/LinksaveIn.py)2
-rw-r--r--module/plugins/ocr/NetloadIn.py (renamed from module/plugins/captcha/NetloadIn.py)2
-rw-r--r--module/plugins/ocr/ShareonlineBiz.py (renamed from module/plugins/captcha/ShareonlineBiz.py)2
-rw-r--r--module/plugins/ocr/__init__.py1
-rw-r--r--module/remote/ClickAndLoadBackend.py26
-rw-r--r--module/remote/SocketBackend.py2
-rw-r--r--module/remote/ThriftBackend.py36
-rw-r--r--module/remote/__init__.py1
-rw-r--r--module/remote/socketbackend/__init__.py3
-rw-r--r--module/remote/socketbackend/create_ttypes.py27
-rw-r--r--module/remote/socketbackend/ttypes.py383
-rw-r--r--module/remote/thriftbackend/Processor.py2
-rw-r--r--module/remote/thriftbackend/Protocol.py4
-rw-r--r--module/remote/thriftbackend/Socket.py8
-rw-r--r--module/remote/thriftbackend/ThriftClient.py36
-rw-r--r--module/remote/thriftbackend/ThriftTest.py18
-rw-r--r--module/remote/thriftbackend/Transport.py6
-rw-r--r--module/remote/thriftbackend/__init__.py1
-rw-r--r--module/remote/thriftbackend/pyload.thrift10
-rw-r--r--module/remote/thriftbackend/thriftgen/__init__.py1
-rw-r--r--[-rwxr-xr-x]module/remote/thriftbackend/thriftgen/pyload/Pyload-remote49
-rw-r--r--module/remote/thriftbackend/thriftgen/pyload/Pyload.py821
-rw-r--r--module/remote/thriftbackend/thriftgen/pyload/__init__.py2
-rw-r--r--module/remote/thriftbackend/thriftgen/pyload/constants.py3
-rw-r--r--module/remote/thriftbackend/thriftgen/pyload/ttypes.py241
-rw-r--r--module/setup.py514
-rw-r--r--module/unescape.py3
-rw-r--r--module/utils.py201
-rw-r--r--module/utils/__init__.py263
-rw-r--r--module/utils/packagetools.py155
-rw-r--r--module/utils/printer.py16
-rw-r--r--module/utils/pylgettext.py (renamed from module/common/pylgettext.py)1
-rw-r--r--module/web/__init__.py0
-rw-r--r--module/web/media/default/css/MooDialog.css92
-rw-r--r--module/web/media/default/css/default.css908
-rw-r--r--module/web/media/default/css/log.css72
-rw-r--r--module/web/media/default/css/pathchooser.css68
-rw-r--r--module/web/media/default/css/window.css73
-rw-r--r--module/web/media/img/favicon.icobin7206 -> 0 bytes
-rw-r--r--module/web/media/js/MooDialog_static.js401
-rw-r--r--module/web/media/js/admin.coffee58
-rw-r--r--module/web/media/js/base.coffee173
-rw-r--r--module/web/media/js/settings.coffee107
-rw-r--r--module/web/servers/lighttpd_default.conf153
-rw-r--r--module/web/templates/500.html10
-rw-r--r--module/web/templates/default/captcha.html42
-rw-r--r--module/web/templates/default/folder.html15
-rw-r--r--module/web/templates/default/setup.html13
-rw-r--r--module/webui/__init__.py (renamed from module/web/webinterface.py)59
-rw-r--r--module/webui/app/__init__.py3
-rw-r--r--module/webui/app/api.py (renamed from module/web/api_app.py)23
-rw-r--r--module/webui/app/cnl.py (renamed from module/web/cnl_app.py)59
-rw-r--r--module/webui/app/json.py (renamed from module/web/json_app.py)73
-rw-r--r--module/webui/app/pyload.py (renamed from module/web/pyload_app.py)213
-rw-r--r--module/webui/app/utils.py (renamed from module/web/utils.py)34
-rw-r--r--module/webui/filters.py (renamed from module/web/filters.py)9
-rw-r--r--module/webui/middlewares.py (renamed from module/web/middlewares.py)3
-rw-r--r--module/webui/servers/lighttpd_default.conf154
-rw-r--r--module/webui/servers/nginx_default.conf (renamed from module/web/servers/nginx_default.conf)2
-rw-r--r--module/webui/themes/Dark/css/MooDialog.css94
-rw-r--r--module/webui/themes/Dark/css/base.css962
-rw-r--r--module/webui/themes/Dark/css/log.css75
-rw-r--r--module/webui/themes/Dark/css/pathchooser.css68
-rw-r--r--module/webui/themes/Dark/css/window.css92
-rw-r--r--module/webui/themes/Dark/img/MooDialog/dialog-close.png (renamed from module/web/media/img/dialog-close.png)bin689 -> 689 bytes
-rw-r--r--module/webui/themes/Dark/img/MooDialog/dialog-error.pngbin0 -> 1472 bytes
-rw-r--r--module/webui/themes/Dark/img/MooDialog/dialog-question.png (renamed from module/web/media/img/dialog-question.png)bin2073 -> 2073 bytes
-rw-r--r--module/webui/themes/Dark/img/MooDialog/dialog-warning.pngbin0 -> 1651 bytes
-rw-r--r--module/webui/themes/Dark/img/button.pngbin0 -> 569 bytes
-rw-r--r--module/webui/themes/Dark/img/dark-bg.jpgbin0 -> 40930 bytes
-rw-r--r--module/webui/themes/Dark/img/default/add_folder.png (renamed from module/web/media/default/img/add_folder.png)bin571 -> 571 bytes
-rw-r--r--module/webui/themes/Dark/img/default/ajax-loader.gif (renamed from module/web/media/default/img/ajax-loader.gif)bin404 -> 404 bytes
-rw-r--r--module/webui/themes/Dark/img/default/arrow_refresh.png (renamed from module/web/media/default/img/arrow_refresh.png)bin685 -> 685 bytes
-rw-r--r--module/webui/themes/Dark/img/default/arrow_right.png (renamed from module/web/media/default/img/arrow_right.png)bin349 -> 349 bytes
-rw-r--r--module/webui/themes/Dark/img/default/big_button.gif (renamed from module/web/media/default/img/big_button.gif)bin1905 -> 1905 bytes
-rw-r--r--module/webui/themes/Dark/img/default/big_button_over.gif (renamed from module/web/media/default/img/big_button_over.gif)bin728 -> 728 bytes
-rw-r--r--module/webui/themes/Dark/img/default/body.png (renamed from module/web/media/default/img/body.png)bin402 -> 402 bytes
-rw-r--r--module/webui/themes/Dark/img/default/closebtn.gif (renamed from module/web/media/default/img/closebtn.gif)bin254 -> 254 bytes
-rw-r--r--module/webui/themes/Dark/img/default/cog.png (renamed from module/web/media/default/img/cog.png)bin512 -> 512 bytes
-rw-r--r--module/webui/themes/Dark/img/default/control_add.png (renamed from module/web/media/default/img/control_add.png)bin446 -> 446 bytes
-rw-r--r--module/webui/themes/Dark/img/default/control_add_blue.png (renamed from module/web/media/default/img/control_add_blue.png)bin845 -> 845 bytes
-rw-r--r--module/webui/themes/Dark/img/default/control_cancel.png (renamed from module/web/media/default/img/control_cancel.png)bin3349 -> 3349 bytes
-rw-r--r--module/webui/themes/Dark/img/default/control_cancel_blue.png (renamed from module/web/media/default/img/control_cancel_blue.png)bin787 -> 787 bytes
-rw-r--r--module/webui/themes/Dark/img/default/control_pause.png (renamed from module/web/media/default/img/control_pause.png)bin598 -> 598 bytes
-rw-r--r--module/webui/themes/Dark/img/default/control_pause_blue.png (renamed from module/web/media/default/img/control_pause_blue.png)bin721 -> 721 bytes
-rw-r--r--module/webui/themes/Dark/img/default/control_play.png (renamed from module/web/media/default/img/control_play.png)bin592 -> 592 bytes
-rw-r--r--module/webui/themes/Dark/img/default/control_play_blue.png (renamed from module/web/media/default/img/control_play_blue.png)bin717 -> 717 bytes
-rw-r--r--module/webui/themes/Dark/img/default/control_stop.png (renamed from module/web/media/default/img/control_stop.png)bin403 -> 403 bytes
-rw-r--r--module/webui/themes/Dark/img/default/control_stop_blue.png (renamed from module/web/media/default/img/control_stop_blue.png)bin695 -> 695 bytes
-rw-r--r--module/webui/themes/Dark/img/default/delete.png (renamed from module/web/media/default/img/delete.png)bin715 -> 715 bytes
-rw-r--r--module/webui/themes/Dark/img/default/drag_corner.gif (renamed from module/web/media/default/img/drag_corner.gif)bin76 -> 76 bytes
-rw-r--r--module/webui/themes/Dark/img/default/error.png (renamed from module/web/media/default/img/error.png)bin701 -> 701 bytes
-rw-r--r--module/webui/themes/Dark/img/default/folder.png (renamed from module/web/media/default/img/folder.png)bin537 -> 537 bytes
-rw-r--r--module/webui/themes/Dark/img/default/full.png (renamed from module/web/media/default/img/full.png)bin3543 -> 3543 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-login.png (renamed from module/web/media/default/img/head-login.png)bin1288 -> 1288 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-menu-collector.png (renamed from module/web/media/default/img/head-menu-collector.png)bin1953 -> 1953 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-menu-config.png (renamed from module/web/media/default/img/head-menu-config.png)bin1802 -> 1802 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-menu-development.png (renamed from module/web/media/default/img/head-menu-development.png)bin876 -> 876 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-menu-download.png (renamed from module/web/media/default/img/head-menu-download.png)bin721 -> 721 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-menu-home.png (renamed from module/web/media/default/img/head-menu-home.png)bin920 -> 920 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-menu-index.png (renamed from module/web/media/default/img/head-menu-index.png)bin482 -> 482 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-menu-news.png (renamed from module/web/media/default/img/head-menu-news.png)bin628 -> 628 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-menu-queue.png (renamed from module/web/media/default/img/head-menu-queue.png)bin2629 -> 2629 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-menu-recent.png (renamed from module/web/media/default/img/head-menu-recent.png)bin932 -> 932 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-menu-wiki.png (renamed from module/web/media/default/img/head-menu-wiki.png)bin1204 -> 1204 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head-search-noshadow.png (renamed from module/web/media/default/img/head-search-noshadow.png)bin1187 -> 1187 bytes
-rw-r--r--module/webui/themes/Dark/img/default/head_bg1.png (renamed from module/web/media/default/img/head_bg1.png)bin125 -> 125 bytes
-rw-r--r--module/webui/themes/Dark/img/default/images.png (renamed from module/web/media/default/img/images.png)bin661 -> 661 bytes
-rw-r--r--module/webui/themes/Dark/img/default/notice.png (renamed from module/web/media/default/img/notice.png)bin778 -> 778 bytes
-rw-r--r--module/webui/themes/Dark/img/default/package_go.png (renamed from module/web/media/default/img/package_go.png)bin898 -> 898 bytes
-rw-r--r--module/webui/themes/Dark/img/default/page-tools-backlinks.png (renamed from module/web/media/default/img/page-tools-backlinks.png)bin540 -> 540 bytes
-rw-r--r--module/webui/themes/Dark/img/default/page-tools-edit.png (renamed from module/web/media/default/img/page-tools-edit.png)bin574 -> 574 bytes
-rw-r--r--module/webui/themes/Dark/img/default/page-tools-revisions.png (renamed from module/web/media/default/img/page-tools-revisions.png)bin603 -> 603 bytes
-rw-r--r--module/webui/themes/Dark/img/default/parseUri.png (renamed from module/web/media/default/img/parseUri.png)bin666 -> 666 bytes
-rw-r--r--module/webui/themes/Dark/img/default/pencil.png (renamed from module/web/media/default/img/pencil.png)bin450 -> 450 bytes
-rw-r--r--module/webui/themes/Dark/img/default/reconnect.png (renamed from module/web/media/default/img/reconnect.png)bin755 -> 755 bytes
-rw-r--r--module/webui/themes/Dark/img/default/status_None.png (renamed from module/web/media/default/img/status_None.png)bin7613 -> 7613 bytes
-rw-r--r--module/webui/themes/Dark/img/default/status_downloading.png (renamed from module/web/media/default/img/status_downloading.png)bin943 -> 943 bytes
-rw-r--r--module/webui/themes/Dark/img/default/status_failed.png (renamed from module/web/media/default/img/status_failed.png)bin701 -> 701 bytes
-rw-r--r--module/webui/themes/Dark/img/default/status_finished.png (renamed from module/web/media/default/img/status_finished.png)bin781 -> 781 bytes
-rw-r--r--module/webui/themes/Dark/img/default/status_offline.png (renamed from module/web/media/default/img/status_offline.png)bin700 -> 700 bytes
-rw-r--r--module/webui/themes/Dark/img/default/status_proc.png (renamed from module/web/media/default/img/status_proc.png)bin512 -> 512 bytes
-rw-r--r--module/webui/themes/Dark/img/default/status_queue.png (renamed from module/web/media/default/img/status_queue.png)bin7613 -> 7613 bytes
-rw-r--r--module/webui/themes/Dark/img/default/status_waiting.png (renamed from module/web/media/default/img/status_waiting.png)bin889 -> 889 bytes
-rw-r--r--module/webui/themes/Dark/img/default/success.png (renamed from module/web/media/default/img/success.png)bin781 -> 781 bytes
-rw-r--r--module/webui/themes/Dark/img/default/tabs-border-bottom.png (renamed from module/web/media/default/img/tabs-border-bottom.png)bin163 -> 163 bytes
-rw-r--r--module/webui/themes/Dark/img/default/user-actions-logout.png (renamed from module/web/media/default/img/user-actions-logout.png)bin799 -> 799 bytes
-rw-r--r--module/webui/themes/Dark/img/default/user-actions-profile.png (renamed from module/web/media/default/img/user-actions-profile.png)bin628 -> 628 bytes
-rw-r--r--module/webui/themes/Dark/img/default/user-info.png (renamed from module/web/media/default/img/user-info.png)bin3963 -> 3963 bytes
-rw-r--r--module/webui/themes/Dark/img/pyload-logo.pngbin0 -> 6947 bytes
-rw-r--r--module/webui/themes/Dark/img/tab-background.pngbin0 -> 3044 bytes
-rw-r--r--module/webui/themes/Dark/js/render/admin.coffee58
-rw-r--r--module/webui/themes/Dark/js/render/admin.min.js (renamed from module/web/media/js/admin.js)2
-rw-r--r--module/webui/themes/Dark/js/render/base.coffee177
-rw-r--r--module/webui/themes/Dark/js/render/base.min.js (renamed from module/web/media/js/base.js)2
-rw-r--r--module/webui/themes/Dark/js/render/package.js (renamed from module/web/media/js/package_ui.js)31
-rw-r--r--module/webui/themes/Dark/js/render/settings.coffee107
-rw-r--r--module/webui/themes/Dark/js/render/settings.min.js (renamed from module/web/media/js/settings.js)2
-rw-r--r--module/webui/themes/Dark/js/static/MooDialog.js140
-rw-r--r--module/webui/themes/Dark/js/static/MooDialog.min.js1
-rw-r--r--module/webui/themes/Dark/js/static/MooDropMenu.js (renamed from module/web/media/js/MooDropMenu_static.js)33
-rw-r--r--module/webui/themes/Dark/js/static/MooDropMenu.min.js1
-rw-r--r--module/webui/themes/Dark/js/static/mootools-core.js5977
-rw-r--r--module/webui/themes/Dark/js/static/mootools-core.min.js (renamed from module/web/media/js/mootools-core-1.4.1.js)475
-rw-r--r--module/webui/themes/Dark/js/static/mootools-more.js2856
-rw-r--r--module/webui/themes/Dark/js/static/mootools-more.min.js (renamed from module/web/media/js/mootools-more-1.4.0.1.js)94
-rw-r--r--module/webui/themes/Dark/js/static/purr.js (renamed from module/web/media/js/purr_static.js)19
-rw-r--r--module/webui/themes/Dark/js/static/purr.min.js1
-rw-r--r--module/webui/themes/Dark/js/static/tinytab.js (renamed from module/web/media/js/tinytab_static.js)9
-rw-r--r--module/webui/themes/Dark/js/static/tinytab.min.js1
-rw-r--r--module/webui/themes/Dark/tml/admin.html (renamed from module/web/templates/default/admin.html)8
-rw-r--r--module/webui/themes/Dark/tml/base.html177
-rw-r--r--module/webui/themes/Dark/tml/captcha.html42
-rw-r--r--module/webui/themes/Dark/tml/downloads.html (renamed from module/web/templates/default/downloads.html)4
-rw-r--r--module/webui/themes/Dark/tml/folder.html15
-rw-r--r--module/webui/themes/Dark/tml/home.html (renamed from module/web/templates/default/home.html)65
-rw-r--r--module/webui/themes/Dark/tml/info.html76
-rw-r--r--module/webui/themes/Dark/tml/login.html37
-rw-r--r--module/webui/themes/Dark/tml/logout.html (renamed from module/web/templates/default/logout.html)2
-rw-r--r--module/webui/themes/Dark/tml/logs.html (renamed from module/web/templates/default/logs.html)4
-rw-r--r--module/webui/themes/Dark/tml/pathchooser.html (renamed from module/web/templates/default/pathchooser.html)26
-rw-r--r--module/webui/themes/Dark/tml/queue.html104
-rw-r--r--module/webui/themes/Dark/tml/settings.html204
-rw-r--r--module/webui/themes/Dark/tml/settings_item.html48
-rw-r--r--module/webui/themes/Dark/tml/window.html52
-rw-r--r--module/webui/themes/Default/css/MooDialog.css91
-rw-r--r--module/webui/themes/Default/css/base.css902
-rw-r--r--module/webui/themes/Default/css/log.css71
-rw-r--r--module/webui/themes/Default/css/pathchooser.css68
-rw-r--r--module/webui/themes/Default/css/window.css73
-rw-r--r--module/webui/themes/Default/img/MooDialog/dialog-close.pngbin0 -> 689 bytes
-rw-r--r--module/webui/themes/Default/img/MooDialog/dialog-error.pngbin0 -> 1472 bytes
-rw-r--r--module/webui/themes/Default/img/MooDialog/dialog-question.pngbin0 -> 2073 bytes
-rw-r--r--module/webui/themes/Default/img/MooDialog/dialog-warning.pngbin0 -> 1651 bytes
-rw-r--r--module/webui/themes/Default/img/add_folder.pngbin0 -> 571 bytes
-rw-r--r--module/webui/themes/Default/img/ajax-loader.gifbin0 -> 404 bytes
-rw-r--r--module/webui/themes/Default/img/arrow_refresh.pngbin0 -> 685 bytes
-rw-r--r--module/webui/themes/Default/img/arrow_right.pngbin0 -> 349 bytes
-rw-r--r--module/webui/themes/Default/img/big_button.gifbin0 -> 1905 bytes
-rw-r--r--module/webui/themes/Default/img/big_button_over.gifbin0 -> 728 bytes
-rw-r--r--module/webui/themes/Default/img/body.pngbin0 -> 402 bytes
-rw-r--r--module/webui/themes/Default/img/button.png (renamed from module/web/media/default/img/button.png)bin452 -> 452 bytes
-rw-r--r--module/webui/themes/Default/img/closebtn.gifbin0 -> 254 bytes
-rw-r--r--module/webui/themes/Default/img/cog.pngbin0 -> 512 bytes
-rw-r--r--module/webui/themes/Default/img/control_add.pngbin0 -> 446 bytes
-rw-r--r--module/webui/themes/Default/img/control_add_blue.pngbin0 -> 845 bytes
-rw-r--r--module/webui/themes/Default/img/control_cancel.pngbin0 -> 3349 bytes
-rw-r--r--module/webui/themes/Default/img/control_cancel_blue.pngbin0 -> 787 bytes
-rw-r--r--module/webui/themes/Default/img/control_pause.pngbin0 -> 598 bytes
-rw-r--r--module/webui/themes/Default/img/control_pause_blue.pngbin0 -> 721 bytes
-rw-r--r--module/webui/themes/Default/img/control_play.pngbin0 -> 592 bytes
-rw-r--r--module/webui/themes/Default/img/control_play_blue.pngbin0 -> 717 bytes
-rw-r--r--module/webui/themes/Default/img/control_stop.pngbin0 -> 403 bytes
-rw-r--r--module/webui/themes/Default/img/control_stop_blue.pngbin0 -> 695 bytes
-rw-r--r--module/webui/themes/Default/img/delete.pngbin0 -> 715 bytes
-rw-r--r--module/webui/themes/Default/img/drag_corner.gifbin0 -> 76 bytes
-rw-r--r--module/webui/themes/Default/img/error.pngbin0 -> 701 bytes
-rw-r--r--module/webui/themes/Default/img/folder.pngbin0 -> 537 bytes
-rw-r--r--module/webui/themes/Default/img/full.pngbin0 -> 3543 bytes
-rw-r--r--module/webui/themes/Default/img/head-login.pngbin0 -> 1288 bytes
-rw-r--r--module/webui/themes/Default/img/head-menu-collector.pngbin0 -> 1953 bytes
-rw-r--r--module/webui/themes/Default/img/head-menu-config.pngbin0 -> 1802 bytes
-rw-r--r--module/webui/themes/Default/img/head-menu-development.pngbin0 -> 876 bytes
-rw-r--r--module/webui/themes/Default/img/head-menu-download.pngbin0 -> 721 bytes
-rw-r--r--module/webui/themes/Default/img/head-menu-home.pngbin0 -> 920 bytes
-rw-r--r--module/webui/themes/Default/img/head-menu-index.pngbin0 -> 482 bytes
-rw-r--r--module/webui/themes/Default/img/head-menu-news.pngbin0 -> 628 bytes
-rw-r--r--module/webui/themes/Default/img/head-menu-queue.pngbin0 -> 2629 bytes
-rw-r--r--module/webui/themes/Default/img/head-menu-recent.pngbin0 -> 932 bytes
-rw-r--r--module/webui/themes/Default/img/head-menu-wiki.pngbin0 -> 1204 bytes
-rw-r--r--module/webui/themes/Default/img/head-search-noshadow.pngbin0 -> 1187 bytes
-rw-r--r--module/webui/themes/Default/img/head_bg1.pngbin0 -> 125 bytes
-rw-r--r--module/webui/themes/Default/img/images.pngbin0 -> 661 bytes
-rw-r--r--module/webui/themes/Default/img/notice.pngbin0 -> 778 bytes
-rw-r--r--module/webui/themes/Default/img/package_go.pngbin0 -> 898 bytes
-rw-r--r--module/webui/themes/Default/img/page-tools-backlinks.pngbin0 -> 540 bytes
-rw-r--r--module/webui/themes/Default/img/page-tools-edit.pngbin0 -> 574 bytes
-rw-r--r--module/webui/themes/Default/img/page-tools-revisions.pngbin0 -> 603 bytes
-rw-r--r--module/webui/themes/Default/img/parseUri.pngbin0 -> 666 bytes
-rw-r--r--module/webui/themes/Default/img/pencil.pngbin0 -> 450 bytes
-rw-r--r--module/webui/themes/Default/img/pyload-logo.png (renamed from module/web/media/default/img/pyload-logo-edited3.5-new-font-small.png)bin8457 -> 8457 bytes
-rw-r--r--module/webui/themes/Default/img/reconnect.pngbin0 -> 755 bytes
-rw-r--r--module/webui/themes/Default/img/status_None.pngbin0 -> 7613 bytes
-rw-r--r--module/webui/themes/Default/img/status_downloading.pngbin0 -> 943 bytes
-rw-r--r--module/webui/themes/Default/img/status_failed.pngbin0 -> 701 bytes
-rw-r--r--module/webui/themes/Default/img/status_finished.pngbin0 -> 781 bytes
-rw-r--r--module/webui/themes/Default/img/status_offline.pngbin0 -> 700 bytes
-rw-r--r--module/webui/themes/Default/img/status_proc.pngbin0 -> 512 bytes
-rw-r--r--module/webui/themes/Default/img/status_queue.pngbin0 -> 7613 bytes
-rw-r--r--module/webui/themes/Default/img/status_waiting.pngbin0 -> 889 bytes
-rw-r--r--module/webui/themes/Default/img/success.pngbin0 -> 781 bytes
-rw-r--r--module/webui/themes/Default/img/tab-background.png (renamed from module/web/media/default/img/tab-background.png)bin179 -> 179 bytes
-rw-r--r--module/webui/themes/Default/img/tabs-border-bottom.pngbin0 -> 163 bytes
-rw-r--r--module/webui/themes/Default/img/user-actions-logout.pngbin0 -> 799 bytes
-rw-r--r--module/webui/themes/Default/img/user-actions-profile.pngbin0 -> 628 bytes
-rw-r--r--module/webui/themes/Default/img/user-info.pngbin0 -> 3963 bytes
-rw-r--r--module/webui/themes/Default/js/render/admin.coffee58
-rw-r--r--module/webui/themes/Default/js/render/admin.min.js3
-rw-r--r--module/webui/themes/Default/js/render/base.coffee177
-rw-r--r--module/webui/themes/Default/js/render/base.min.js3
-rw-r--r--module/webui/themes/Default/js/render/package.js376
-rw-r--r--module/webui/themes/Default/js/render/settings.coffee107
-rw-r--r--module/webui/themes/Default/js/render/settings.min.js3
-rw-r--r--module/webui/themes/Default/js/static/MooDialog.js140
-rw-r--r--module/webui/themes/Default/js/static/MooDialog.min.js1
-rw-r--r--module/webui/themes/Default/js/static/MooDropMenu.js86
-rw-r--r--module/webui/themes/Default/js/static/MooDropMenu.min.js1
-rw-r--r--module/webui/themes/Default/js/static/mootools-core.js5977
-rw-r--r--module/webui/themes/Default/js/static/mootools-core.min.js491
-rw-r--r--module/webui/themes/Default/js/static/mootools-more.js2856
-rw-r--r--module/webui/themes/Default/js/static/mootools-more.min.js226
-rw-r--r--module/webui/themes/Default/js/static/purr.js309
-rw-r--r--module/webui/themes/Default/js/static/purr.min.js1
-rw-r--r--module/webui/themes/Default/js/static/tinytab.js43
-rw-r--r--module/webui/themes/Default/js/static/tinytab.min.js1
-rw-r--r--module/webui/themes/Default/tml/admin.html98
-rw-r--r--module/webui/themes/Default/tml/base.html (renamed from module/web/templates/default/base.html)109
-rw-r--r--module/webui/themes/Default/tml/captcha.html42
-rw-r--r--module/webui/themes/Default/tml/downloads.html29
-rw-r--r--module/webui/themes/Default/tml/filemanager.html76
-rw-r--r--module/webui/themes/Default/tml/folder.html15
-rw-r--r--module/webui/themes/Default/tml/home.html263
-rw-r--r--module/webui/themes/Default/tml/info.html (renamed from module/web/templates/default/info.html)6
-rw-r--r--module/webui/themes/Default/tml/login.html (renamed from module/web/templates/default/login.html)6
-rw-r--r--module/webui/themes/Default/tml/logout.html9
-rw-r--r--module/webui/themes/Default/tml/logs.html41
-rw-r--r--module/webui/themes/Default/tml/pathchooser.html76
-rw-r--r--module/webui/themes/Default/tml/queue.html (renamed from module/web/templates/default/queue.html)18
-rw-r--r--module/webui/themes/Default/tml/settings.html (renamed from module/web/templates/default/settings.html)18
-rw-r--r--module/webui/themes/Default/tml/settings_item.html48
-rw-r--r--module/webui/themes/Default/tml/window.html (renamed from module/web/templates/default/window.html)4
-rw-r--r--module/webui/themes/Flat/css/MooDialog.css85
-rw-r--r--module/webui/themes/Flat/css/base.css866
-rw-r--r--module/webui/themes/Flat/css/log.css72
-rw-r--r--module/webui/themes/Flat/css/pathchooser.css68
-rw-r--r--module/webui/themes/Flat/css/window.css70
-rw-r--r--module/webui/themes/Flat/img/MooDialog/dialog-close.pngbin0 -> 689 bytes
-rw-r--r--module/webui/themes/Flat/img/MooDialog/dialog-error.pngbin0 -> 1472 bytes
-rw-r--r--module/webui/themes/Flat/img/MooDialog/dialog-question.pngbin0 -> 2073 bytes
-rw-r--r--module/webui/themes/Flat/img/MooDialog/dialog-warning.pngbin0 -> 1651 bytes
-rw-r--r--module/webui/themes/Flat/img/arrow_refresh.pngbin0 -> 119032 bytes
-rw-r--r--module/webui/themes/Flat/img/arrow_right.pngbin0 -> 136967 bytes
-rw-r--r--module/webui/themes/Flat/img/button.pngbin0 -> 569 bytes
-rw-r--r--module/webui/themes/Flat/img/cog.pngbin0 -> 137406 bytes
-rw-r--r--module/webui/themes/Flat/img/control_add.pngbin0 -> 116941 bytes
-rw-r--r--module/webui/themes/Flat/img/control_add_blue.pngbin0 -> 116941 bytes
-rw-r--r--module/webui/themes/Flat/img/control_cancel.pngbin0 -> 116939 bytes
-rw-r--r--module/webui/themes/Flat/img/control_cancel_blue.pngbin0 -> 116939 bytes
-rw-r--r--module/webui/themes/Flat/img/control_pause.pngbin0 -> 134855 bytes
-rw-r--r--module/webui/themes/Flat/img/control_pause_blue.pngbin0 -> 134855 bytes
-rw-r--r--module/webui/themes/Flat/img/control_play.pngbin0 -> 134904 bytes
-rw-r--r--module/webui/themes/Flat/img/control_play_blue.pngbin0 -> 134904 bytes
-rw-r--r--module/webui/themes/Flat/img/control_stop.pngbin0 -> 134835 bytes
-rw-r--r--module/webui/themes/Flat/img/control_stop_blue.pngbin0 -> 134835 bytes
-rw-r--r--module/webui/themes/Flat/img/default/add_folder.pngbin0 -> 571 bytes
-rw-r--r--module/webui/themes/Flat/img/default/ajax-loader.gifbin0 -> 404 bytes
-rw-r--r--module/webui/themes/Flat/img/default/big_button.gifbin0 -> 1905 bytes
-rw-r--r--module/webui/themes/Flat/img/default/big_button_over.gifbin0 -> 728 bytes
-rw-r--r--module/webui/themes/Flat/img/default/body.pngbin0 -> 402 bytes
-rw-r--r--module/webui/themes/Flat/img/default/closebtn.gifbin0 -> 254 bytes
-rw-r--r--module/webui/themes/Flat/img/default/drag_corner.gifbin0 -> 76 bytes
-rw-r--r--module/webui/themes/Flat/img/default/full.pngbin0 -> 3543 bytes
-rw-r--r--module/webui/themes/Flat/img/default/head-menu-recent.pngbin0 -> 932 bytes
-rw-r--r--module/webui/themes/Flat/img/default/head_bg1.pngbin0 -> 125 bytes
-rw-r--r--module/webui/themes/Flat/img/default/images.pngbin0 -> 661 bytes
-rw-r--r--module/webui/themes/Flat/img/default/parseUri.pngbin0 -> 666 bytes
-rw-r--r--module/webui/themes/Flat/img/default/pyload-logo.pngbin0 -> 8457 bytes
-rw-r--r--module/webui/themes/Flat/img/default/tab-background.pngbin0 -> 179 bytes
-rw-r--r--module/webui/themes/Flat/img/default/tabs-border-bottom.pngbin0 -> 163 bytes
-rw-r--r--module/webui/themes/Flat/img/delete.pngbin0 -> 117658 bytes
-rw-r--r--module/webui/themes/Flat/img/error.pngbin0 -> 137673 bytes
-rw-r--r--module/webui/themes/Flat/img/folder.pngbin0 -> 134669 bytes
-rw-r--r--module/webui/themes/Flat/img/head-login.pngbin0 -> 137406 bytes
-rw-r--r--module/webui/themes/Flat/img/head-menu-collector.pngbin0 -> 134985 bytes
-rw-r--r--module/webui/themes/Flat/img/head-menu-config.pngbin0 -> 137664 bytes
-rw-r--r--module/webui/themes/Flat/img/head-menu-development.pngbin0 -> 135818 bytes
-rw-r--r--module/webui/themes/Flat/img/head-menu-download.pngbin0 -> 137664 bytes
-rw-r--r--module/webui/themes/Flat/img/head-menu-home.pngbin0 -> 139387 bytes
-rw-r--r--module/webui/themes/Flat/img/head-menu-index.pngbin0 -> 136511 bytes
-rw-r--r--module/webui/themes/Flat/img/head-menu-news.pngbin0 -> 136511 bytes
-rw-r--r--module/webui/themes/Flat/img/head-menu-queue.pngbin0 -> 136269 bytes
-rw-r--r--module/webui/themes/Flat/img/head-menu-wiki.pngbin0 -> 137217 bytes
-rw-r--r--module/webui/themes/Flat/img/head-search-noshadow.pngbin0 -> 137217 bytes
-rw-r--r--module/webui/themes/Flat/img/notice.pngbin0 -> 3061 bytes
-rw-r--r--module/webui/themes/Flat/img/package_go.pngbin0 -> 136299 bytes
-rw-r--r--module/webui/themes/Flat/img/page-tools-backlinks.pngbin0 -> 138112 bytes
-rw-r--r--module/webui/themes/Flat/img/page-tools-edit.pngbin0 -> 138112 bytes
-rw-r--r--module/webui/themes/Flat/img/page-tools-revisions.pngbin0 -> 138112 bytes
-rw-r--r--module/webui/themes/Flat/img/pencil.pngbin0 -> 138112 bytes
-rw-r--r--module/webui/themes/Flat/img/reconnect.pngbin0 -> 3063 bytes
-rw-r--r--module/webui/themes/Flat/img/status_None.pngbin0 -> 138112 bytes
-rw-r--r--module/webui/themes/Flat/img/status_downloading.pngbin0 -> 3061 bytes
-rw-r--r--module/webui/themes/Flat/img/status_failed.pngbin0 -> 137673 bytes
-rw-r--r--module/webui/themes/Flat/img/status_finished.pngbin0 -> 117658 bytes
-rw-r--r--module/webui/themes/Flat/img/status_offline.pngbin0 -> 137673 bytes
-rw-r--r--module/webui/themes/Flat/img/status_proc.pngbin0 -> 137406 bytes
-rw-r--r--module/webui/themes/Flat/img/status_queue.pngbin0 -> 138112 bytes
-rw-r--r--module/webui/themes/Flat/img/status_waiting.pngbin0 -> 138112 bytes
-rw-r--r--module/webui/themes/Flat/img/success.pngbin0 -> 117658 bytes
-rw-r--r--module/webui/themes/Flat/img/user-actions-logout.pngbin0 -> 138112 bytes
-rw-r--r--module/webui/themes/Flat/img/user-actions-profile.pngbin0 -> 138112 bytes
-rw-r--r--module/webui/themes/Flat/img/user-info.pngbin0 -> 3080 bytes
-rw-r--r--module/webui/themes/Flat/js/render/admin.coffee58
-rw-r--r--module/webui/themes/Flat/js/render/admin.min.js3
-rw-r--r--module/webui/themes/Flat/js/render/base.coffee177
-rw-r--r--module/webui/themes/Flat/js/render/base.min.js3
-rw-r--r--module/webui/themes/Flat/js/render/filemanager.js (renamed from module/web/templates/default/filemanager_ui.js)254
-rw-r--r--module/webui/themes/Flat/js/render/package.js376
-rw-r--r--module/webui/themes/Flat/js/render/settings.coffee107
-rw-r--r--module/webui/themes/Flat/js/render/settings.min.js3
-rw-r--r--module/webui/themes/Flat/js/static/MooDialog.js140
-rw-r--r--module/webui/themes/Flat/js/static/MooDialog.min.js1
-rw-r--r--module/webui/themes/Flat/js/static/MooDropMenu.js86
-rw-r--r--module/webui/themes/Flat/js/static/MooDropMenu.min.js1
-rw-r--r--module/webui/themes/Flat/js/static/mootools-core.js5977
-rw-r--r--module/webui/themes/Flat/js/static/mootools-core.min.js491
-rw-r--r--module/webui/themes/Flat/js/static/mootools-more.js2856
-rw-r--r--module/webui/themes/Flat/js/static/mootools-more.min.js226
-rw-r--r--module/webui/themes/Flat/js/static/purr.js309
-rw-r--r--module/webui/themes/Flat/js/static/purr.min.js1
-rw-r--r--module/webui/themes/Flat/js/static/tinytab.js43
-rw-r--r--module/webui/themes/Flat/js/static/tinytab.min.js1
-rw-r--r--module/webui/themes/Flat/tml/admin.html98
-rw-r--r--module/webui/themes/Flat/tml/base.html180
-rw-r--r--module/webui/themes/Flat/tml/captcha.html42
-rw-r--r--module/webui/themes/Flat/tml/downloads.html29
-rw-r--r--module/webui/themes/Flat/tml/filemanager.html (renamed from module/web/templates/default/filemanager.html)54
-rw-r--r--module/webui/themes/Flat/tml/folder.html15
-rw-r--r--module/webui/themes/Flat/tml/home.html266
-rw-r--r--module/webui/themes/Flat/tml/info.html81
-rw-r--r--module/webui/themes/Flat/tml/login.html36
-rw-r--r--module/webui/themes/Flat/tml/logout.html9
-rw-r--r--module/webui/themes/Flat/tml/logs.html41
-rw-r--r--module/webui/themes/Flat/tml/pathchooser.html76
-rw-r--r--module/webui/themes/Flat/tml/queue.html104
-rw-r--r--module/webui/themes/Flat/tml/settings.html204
-rw-r--r--module/webui/themes/Flat/tml/settings_item.html (renamed from module/web/templates/default/settings_item.html)0
-rw-r--r--module/webui/themes/Flat/tml/window.html46
915 files changed, 46452 insertions, 43211 deletions
diff --git a/module/Core.py b/module/Core.py
new file mode 100644
index 000000000..fe4ae566e
--- /dev/null
+++ b/module/Core.py
@@ -0,0 +1,632 @@
+# -*- coding: utf-8 -*-
+# @author: RaNaN, mkaay, sebnapi, spoob
+# @version: v0.4.10
+
+CURRENT_VERSION = '0.4.10'
+
+import __builtin__
+
+from getopt import getopt, GetoptError
+import pyload.utils.pylgettext as gettext
+from imp import find_module
+import logging
+import logging.handlers
+import os
+from os import _exit, execl, getcwd, makedirs, remove, sep, walk, chdir, close
+from os.path import exists, join
+import signal
+import subprocess
+import sys
+from sys import argv, executable, exit
+from time import time, sleep
+from traceback import print_exc
+
+from pyload.manager.Account import AccountManager
+from pyload.manager.Captcha import CaptchaManager
+from pyload.config.Parser import ConfigParser
+from pyload.manager.Plugin import PluginManager
+from pyload.manager.Event import PullManager
+from pyload.network.RequestFactory import RequestFactory
+from pyload.manager.thread.Server import WebServer
+from pyload.manager.event.Scheduler import Scheduler
+from pyload.network.JsEngine import JsEngine
+from pyload import remote
+from pyload.manager.Remote import RemoteManager
+from pyload.database import DatabaseBackend, FileHandler
+
+from pyload.utils import freeSpace, formatSize, get_console_encoding
+
+from codecs import getwriter
+
+enc = get_console_encoding(sys.stdout.encoding)
+sys.stdout = getwriter(enc)(sys.stdout, errors="replace")
+
+# TODO List
+# - configurable auth system ldap/mysql
+# - cron job like sheduler
+
+class Core(object):
+ """pyLoad Core, one tool to rule them all... (the filehosters) :D"""
+
+ def __init__(self):
+ self.doDebug = False
+ self.running = False
+ self.daemon = False
+ self.remote = True
+ self.arg_links = []
+ self.pidfile = "pyload.pid"
+ self.deleteLinks = False # will delete links on startup
+
+ if len(argv) > 1:
+ try:
+ options, args = getopt(argv[1:], 'vchdusqp:',
+ ["version", "clear", "clean", "help", "debug", "user",
+ "setup", "configdir=", "changedir", "daemon",
+ "quit", "status", "no-remote","pidfile="])
+
+ for option, argument in options:
+ if option in ("-v", "--version"):
+ print "pyLoad", CURRENT_VERSION
+ exit()
+ elif option in ("-p", "--pidfile"):
+ self.pidfile = argument
+ elif option == "--daemon":
+ self.daemon = True
+ elif option in ("-c", "--clear"):
+ self.deleteLinks = True
+ elif option in ("-h", "--help"):
+ self.print_help()
+ exit()
+ elif option in ("-d", "--debug"):
+ self.doDebug = True
+ elif option in ("-u", "--user"):
+ from pyload.config.Setup import SetupAssistant as Setup
+
+ self.config = ConfigParser()
+ s = Setup(self.config)
+ s.set_user()
+ exit()
+ elif option in ("-s", "--setup"):
+ from pyload.config.Setup import SetupAssistant as Setup
+
+ self.config = ConfigParser()
+ s = Setup(self.config)
+ s.start()
+ exit()
+ elif option == "--changedir":
+ from pyload.config.Setup import SetupAssistant as Setup
+
+ self.config = ConfigParser()
+ s = Setup(self.config)
+ s.conf_path(True)
+ exit()
+ elif option in ("-q", "--quit"):
+ self.quitInstance()
+ exit()
+ elif option == "--status":
+ pid = self.isAlreadyRunning()
+ if self.isAlreadyRunning():
+ print pid
+ exit(0)
+ else:
+ print "false"
+ exit(1)
+ elif option == "--clean":
+ self.cleanTree()
+ exit()
+ elif option == "--no-remote":
+ self.remote = False
+
+ except GetoptError:
+ print 'Unknown Argument(s) "%s"' % " ".join(argv[1:])
+ self.print_help()
+ exit()
+
+ def print_help(self):
+ print
+ print "pyLoad v%s 2008-2015 the pyLoad Team" % CURRENT_VERSION
+ print
+ if sys.argv[0].endswith(".py"):
+ print "Usage: python pyload.py [options]"
+ else:
+ print "Usage: pyload [options]"
+ print
+ print "<Options>"
+ print " -v, --version", " " * 10, "Print version to terminal"
+ print " -c, --clear", " " * 12, "Delete all saved packages/links"
+ #print " -a, --add=<link/list>", " " * 2, "Add the specified links"
+ print " -u, --user", " " * 13, "Manages users"
+ print " -d, --debug", " " * 12, "Enable debug mode"
+ print " -s, --setup", " " * 12, "Run Setup Assistant"
+ print " --configdir=<dir>", " " * 6, "Run with <dir> as config directory"
+ print " -p, --pidfile=<file>", " " * 3, "Set pidfile to <file>"
+ print " --changedir", " " * 12, "Change config dir permanently"
+ print " --daemon", " " * 15, "Daemonmize after start"
+ print " --no-remote", " " * 12, "Disable remote access (saves RAM)"
+ print " --status", " " * 15, "Display pid if running or False"
+ print " --clean", " " * 16, "Remove .pyc/.pyo files"
+ print " -q, --quit", " " * 13, "Quit running pyLoad instance"
+ print " -h, --help", " " * 13, "Display this help screen"
+ print
+
+ def toggle_pause(self):
+ if self.threadManager.pause:
+ self.threadManager.pause = False
+ return False
+ elif not self.threadManager.pause:
+ self.threadManager.pause = True
+ return True
+
+ def quit(self, a, b):
+ self.shutdown()
+ self.log.info(_("Received Quit signal"))
+ _exit(1)
+
+ def writePidFile(self):
+ self.deletePidFile()
+ pid = os.getpid()
+ f = open(self.pidfile, "wb")
+ f.write(str(pid))
+ f.close()
+
+ def deletePidFile(self):
+ if self.checkPidFile():
+ self.log.debug("Deleting old pidfile %s" % self.pidfile)
+ os.remove(self.pidfile)
+
+ def checkPidFile(self):
+ """ return pid as int or 0"""
+ if os.path.isfile(self.pidfile):
+ f = open(self.pidfile, "rb")
+ pid = f.read().strip()
+ f.close()
+ if pid:
+ pid = int(pid)
+ return pid
+
+ return 0
+
+ def isAlreadyRunning(self):
+ pid = self.checkPidFile()
+ if not pid or os.name == "nt": return False
+ try:
+ os.kill(pid, 0) # 0 - default signal (does nothing)
+ except Exception:
+ return 0
+
+ return pid
+
+ def quitInstance(self):
+ if os.name == "nt":
+ print "Not supported on windows."
+ return
+
+ pid = self.isAlreadyRunning()
+ if not pid:
+ print "No pyLoad running."
+ return
+
+ try:
+ os.kill(pid, 3) #SIGUIT
+
+ t = time()
+ print "waiting for pyLoad to quit"
+
+ while exists(self.pidfile) and t + 10 > time():
+ sleep(0.25)
+
+ if not exists(self.pidfile):
+ print "pyLoad successfully stopped"
+ else:
+ os.kill(pid, 9) #SIGKILL
+ print "pyLoad did not respond"
+ print "Kill signal was send to process with id %s" % pid
+
+ except Exception:
+ print "Error quitting pyLoad"
+
+
+ def cleanTree(self):
+ for path, dirs, files in walk(self.path("")):
+ for f in files:
+ if not f.endswith(".pyo") and not f.endswith(".pyc"):
+ continue
+
+ if "_25" in f or "_26" in f or "_27" in f:
+ continue
+
+ print join(path, f)
+ remove(join(path, f))
+
+ def start(self, rpc=True, web=True):
+ """ starts the fun :D """
+
+ self.version = CURRENT_VERSION
+
+ if not exists("pyload.conf"):
+ from pyload.config.Setup import SetupAssistant as Setup
+
+ print "This is your first start, running configuration assistent now."
+ self.config = ConfigParser()
+ s = Setup(self.config)
+ res = False
+ try:
+ res = s.start()
+ except SystemExit:
+ pass
+ except KeyboardInterrupt:
+ print "\nSetup interrupted"
+ except Exception:
+ res = False
+ print_exc()
+ print "Setup failed"
+ if not res:
+ remove("pyload.conf")
+
+ exit()
+
+ try: signal.signal(signal.SIGQUIT, self.quit)
+ except Exception: pass
+
+ self.config = ConfigParser()
+
+ gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None])
+ translation = gettext.translation("pyLoad", self.path("locale"),
+ languages=[self.config['general']['language'], "en"], fallback=True)
+ translation.install(True)
+
+ self.debug = self.doDebug or self.config['general']['debug_mode']
+ self.remote &= self.config['remote']['activated']
+
+ pid = self.isAlreadyRunning()
+ if pid:
+ print _("pyLoad already running with pid %s") % pid
+ exit()
+
+ if os.name != "nt" and self.config["general"]["renice"]:
+ os.system("renice %d %d" % (self.config["general"]["renice"], os.getpid()))
+
+ if self.config["permission"]["change_group"]:
+ if os.name != "nt":
+ try:
+ from grp import getgrnam
+
+ group = getgrnam(self.config["permission"]["group"])
+ os.setgid(group[2])
+ except Exception, e:
+ print _("Failed changing group: %s") % e
+
+ if self.config["permission"]["change_user"]:
+ if os.name != "nt":
+ try:
+ from pwd import getpwnam
+
+ user = getpwnam(self.config["permission"]["user"])
+ os.setuid(user[2])
+ except Exception, e:
+ print _("Failed changing user: %s") % e
+
+ self.check_file(self.config['log']['log_folder'], _("folder for logs"), True)
+
+ if self.debug:
+ self.init_logger(logging.DEBUG) # logging level
+ else:
+ self.init_logger(logging.INFO) # logging level
+
+ self.do_kill = False
+ self.do_restart = False
+ self.shuttedDown = False
+
+ self.log.info(_("Starting") + " pyLoad %s" % CURRENT_VERSION)
+ self.log.info(_("Using home directory: %s") % getcwd())
+
+ self.writePidFile()
+
+ #@TODO refractor
+
+ remote.activated = self.remote
+ self.log.debug("Remote activated: %s" % self.remote)
+
+ self.check_install("Crypto", _("pycrypto to decode container files"))
+ #img = self.check_install("Image", _("Python Image Library (PIL) for captcha reading"))
+ #self.check_install("pycurl", _("pycurl to download any files"), True, True)
+ self.check_file("tmp", _("folder for temporary files"), True)
+ #tesser = self.check_install("tesseract", _("tesseract for captcha reading"), False) if os.name != "nt" else True
+
+ self.captcha = True # checks seems to fail, although tesseract is available
+
+ self.check_file(self.config['general']['download_folder'], _("folder for downloads"), True)
+
+ if self.config['ssl']['activated']:
+ self.check_install("OpenSSL", _("OpenSSL for secure connection"))
+
+ self.setupDB()
+ if self.config.oldRemoteData:
+ self.log.info(_("Moving old user config to DB"))
+ self.db.addUser(self.config.oldRemoteData["username"], self.config.oldRemoteData["password"])
+
+ self.log.info(_("Please check your logindata with ./pyload.py -u"))
+
+ if self.deleteLinks:
+ self.log.info(_("All links removed"))
+ self.db.purgeLinks()
+
+ self.requestFactory = RequestFactory(self)
+ __builtin__.pyreq = self.requestFactory
+
+ self.lastClientConnected = 0
+
+ # later imported because they would trigger api import, and remote value not set correctly
+ from pyload import api
+ from pyload.manager.Addon import AddonManager
+ from pyload.manager.Thread import ThreadManager
+
+ if api.activated != self.remote:
+ self.log.warning("Import error: API remote status not correct.")
+
+ self.api = api.Api(self)
+
+ self.scheduler = Scheduler(self)
+
+ #hell yeah, so many important managers :D
+ self.pluginManager = PluginManager(self)
+ self.pullManager = PullManager(self)
+ self.accountManager = AccountManager(self)
+ self.threadManager = ThreadManager(self)
+ self.captchaManager = CaptchaManager(self)
+ self.addonManager = AddonManager(self)
+ self.remoteManager = RemoteManager(self)
+
+ self.js = JsEngine(self)
+
+ self.log.info(_("Downloadtime: %s") % self.api.isTimeDownload())
+
+ if rpc:
+ self.remoteManager.startBackends()
+
+ if web:
+ self.init_webserver()
+
+ spaceLeft = freeSpace(self.config["general"]["download_folder"])
+
+ self.log.info(_("Free space: %s") % formatSize(spaceLeft))
+
+ self.config.save() #save so config files gets filled
+
+ link_file = join(pypath, "links.txt")
+
+ if exists(link_file):
+ f = open(link_file, "rb")
+ if f.read().strip():
+ self.api.addPackage("links.txt", [link_file], 1)
+ f.close()
+
+ link_file = "links.txt"
+ if exists(link_file):
+ f = open(link_file, "rb")
+ if f.read().strip():
+ self.api.addPackage("links.txt", [link_file], 1)
+ f.close()
+
+ #self.scheduler.addJob(0, self.accountManager.getAccountInfos)
+ self.log.info(_("Activating Accounts..."))
+ self.accountManager.getAccountInfos()
+
+ self.threadManager.pause = False
+ self.running = True
+
+ self.log.info(_("Activating Plugins..."))
+ self.addonManager.coreReady()
+
+ self.log.info(_("pyLoad is up and running"))
+
+ locals().clear()
+
+ while True:
+ sleep(2)
+ if self.do_restart:
+ self.log.info(_("restarting pyLoad"))
+ self.restart()
+ if self.do_kill:
+ self.shutdown()
+ self.log.info(_("pyLoad quits"))
+ self.removeLogger()
+ _exit(0) #@TODO thrift blocks shutdown
+
+ self.threadManager.work()
+ self.scheduler.work()
+
+ def setupDB(self):
+ self.db = DatabaseBackend(self) # the backend
+ self.db.setup()
+
+ self.files = FileHandler(self)
+ self.db.manager = self.files #ugly?
+
+ def init_webserver(self):
+ if self.config['webinterface']['activated']:
+ self.webserver = WebServer(self)
+ self.webserver.start()
+
+ def init_logger(self, level):
+ console = logging.StreamHandler(sys.stdout)
+ frm = logging.Formatter("%(asctime)s %(levelname)-8s %(message)s", "%d.%m.%Y %H:%M:%S")
+ console.setFormatter(frm)
+ self.log = logging.getLogger("log") # settable in config
+
+ if self.config['log']['file_log']:
+ if self.config['log']['log_rotate']:
+ file_handler = logging.handlers.RotatingFileHandler(join(self.config['log']['log_folder'], 'log.txt'),
+ maxBytes=self.config['log']['log_size'] * 1024,
+ backupCount=int(self.config['log']['log_count']),
+ encoding="utf8")
+ else:
+ file_handler = logging.FileHandler(join(self.config['log']['log_folder'], 'log.txt'), encoding="utf8")
+
+ file_handler.setFormatter(frm)
+ self.log.addHandler(file_handler)
+
+ self.log.addHandler(console) #if console logging
+ self.log.setLevel(level)
+
+ def removeLogger(self):
+ for h in list(self.log.handlers):
+ self.log.removeHandler(h)
+ h.close()
+
+ def check_install(self, check_name, legend, python=True, essential=False):
+ """check wether needed tools are installed"""
+ try:
+ if python:
+ find_module(check_name)
+ else:
+ pipe = subprocess.PIPE
+ subprocess.Popen(check_name, stdout=pipe, stderr=pipe)
+
+ return True
+ except Exception:
+ if essential:
+ self.log.info(_("Install %s") % legend)
+ exit()
+
+ return False
+
+ def check_file(self, check_names, description="", folder=False, empty=True, essential=False, quiet=False):
+ """check wether needed files exists"""
+ tmp_names = []
+ if not type(check_names) == list:
+ tmp_names.append(check_names)
+ else:
+ tmp_names.extend(check_names)
+ file_created = True
+ file_exists = True
+ for tmp_name in tmp_names:
+ if not exists(tmp_name):
+ file_exists = False
+ if empty:
+ try:
+ if folder:
+ tmp_name = tmp_name.replace("/", sep)
+ makedirs(tmp_name)
+ else:
+ open(tmp_name, "w")
+ except Exception:
+ file_created = False
+ else:
+ file_created = False
+
+ if not file_exists and not quiet:
+ if file_created:
+ #self.log.info( _("%s created") % description )
+ pass
+ else:
+ if not empty:
+ self.log.warning(
+ _("could not find %(desc)s: %(name)s") % {"desc": description, "name": tmp_name})
+ else:
+ print _("could not create %(desc)s: %(name)s") % {"desc": description, "name": tmp_name}
+ if essential:
+ exit()
+
+ def isClientConnected(self):
+ return (self.lastClientConnected + 30) > time()
+
+ def restart(self):
+ self.shutdown()
+ chdir(owd)
+ # close some open fds
+ for i in range(3, 50):
+ try:
+ close(i)
+ except :
+ pass
+
+ execl(executable, executable, *sys.argv)
+ _exit(0)
+
+ def shutdown(self):
+ self.log.info(_("shutting down..."))
+ try:
+ if self.config['webinterface']['activated'] and hasattr(self, "webserver"):
+ self.webserver.quit()
+
+ for thread in self.threadManager.threads:
+ thread.put("quit")
+ pyfiles = self.files.cache.values()
+
+ for pyfile in pyfiles:
+ pyfile.abortDownload()
+
+ self.addonManager.coreExiting()
+
+ except Exception:
+ if self.debug:
+ print_exc()
+ self.log.info(_("error while shutting down"))
+
+ finally:
+ self.files.syncSave()
+ self.shuttedDown = True
+
+ self.deletePidFile()
+
+
+ def path(self, *args):
+ return join(pypath, *args)
+
+
+def deamon():
+ try:
+ pid = os.fork()
+ if pid > 0:
+ sys.exit(0)
+ except OSError, e:
+ print >> sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror)
+ sys.exit(1)
+
+ # decouple from parent environment
+ os.setsid()
+ os.umask(0)
+
+ # do second fork
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # exit from second parent, print eventual PID before
+ print "Daemon PID %d" % pid
+ sys.exit(0)
+ except OSError, e:
+ print >> sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror)
+ sys.exit(1)
+
+ # Iterate through and close some file descriptors.
+ for fd in range(0, 3):
+ try:
+ os.close(fd)
+ except OSError: # ERROR, fd wasn't open to begin with (ignored)
+ pass
+
+ os.open(os.devnull, os.O_RDWR) # standard input (0)
+ os.dup2(0, 1) # standard output (1)
+ os.dup2(0, 2)
+
+ pyload_core = Core()
+ pyload_core.start()
+
+
+def main():
+ if "--daemon" in sys.argv:
+ deamon()
+ else:
+ pyload_core = Core()
+ try:
+ pyload_core.start()
+ except KeyboardInterrupt:
+ pyload_core.shutdown()
+ pyload_core.log.info(_("killed pyLoad from Terminal"))
+ pyload_core.removeLogger()
+ _exit(1)
+
+# And so it begins...
+if __name__ == "__main__":
+ main()
diff --git a/module/HookManager.py b/module/HookManager.py
deleted file mode 100644
index 16f692d76..000000000
--- a/module/HookManager.py
+++ /dev/null
@@ -1,315 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN, mkaay
- @interface-version: 0.1
-"""
-import __builtin__
-
-import traceback
-from thread import start_new_thread
-from threading import RLock
-
-from types import MethodType
-
-from module.PluginThread import HookThread
-from module.plugins.PluginManager import literal_eval
-from utils import lock
-
-class HookManager:
- """Manages hooks, delegates and handles Events.
-
- Every plugin can define events, \
- but some very usefull events are called by the Core.
- Contrary to overwriting hook methods you can use event listener,
- which provides additional entry point in the control flow.
- Only do very short tasks or use threads.
-
- **Known Events:**
- Most hook methods exists as events. These are the additional known events.
-
- ===================== ============== ==================================
- Name Arguments Description
- ===================== ============== ==================================
- downloadPreparing fid A download was just queued and will be prepared now.
- downloadStarts fid A plugin will immediately starts the download afterwards.
- linksAdded links, pid Someone just added links, you are able to modify the links.
- allDownloadsProcessed Every link was handled, pyload would idle afterwards.
- allDownloadsFinished Every download in queue is finished.
- unrarFinished folder, fname An Unrar job finished
- configChanged The config was changed via the api.
- pluginConfigChanged The plugin config changed, due to api or internal process.
- ===================== ============== ==================================
-
- | Notes:
- | allDownloadsProcessed is *always* called before allDownloadsFinished.
- | configChanged is *always* called before pluginConfigChanged.
-
-
- """
-
- def __init__(self, core):
- self.core = core
- self.config = self.core.config
-
- __builtin__.hookManager = self #needed to let hooks register themself
-
- self.log = self.core.log
- self.plugins = []
- self.pluginMap = {}
- self.methods = {} #dict of names and list of methods usable by rpc
-
- self.events = {} # contains events
-
- #registering callback for config event
- self.config.pluginCB = MethodType(self.dispatchEvent, "pluginConfigChanged", basestring)
-
- self.addEvent("pluginConfigChanged", self.manageHooks)
-
- self.lock = RLock()
- self.createIndex()
-
- def try_catch(func):
- def new(*args):
- try:
- return func(*args)
- except Exception, e:
- args[0].log.error(_("Error executing hooks: %s") % str(e))
- if args[0].core.debug:
- traceback.print_exc()
-
- return new
-
-
- def addRPC(self, plugin, func, doc):
- plugin = plugin.rpartition(".")[2]
- doc = doc.strip() if doc else ""
-
- if plugin in self.methods:
- self.methods[plugin][func] = doc
- else:
- self.methods[plugin] = {func: doc}
-
- def callRPC(self, plugin, func, args, parse):
- if not args: args = tuple()
- if parse:
- args = tuple([literal_eval(x) for x in args])
-
- plugin = self.pluginMap[plugin]
- f = getattr(plugin, func)
- return f(*args)
-
-
- def createIndex(self):
- plugins = []
-
- active = []
- deactive = []
-
- for pluginname in self.core.pluginManager.hookPlugins:
- try:
- #hookClass = getattr(plugin, plugin.__name__)
-
- if self.core.config.getPlugin(pluginname, "activated"):
- pluginClass = self.core.pluginManager.loadClass("hooks", pluginname)
- if not pluginClass: continue
-
- plugin = pluginClass(self.core, self)
- plugins.append(plugin)
- self.pluginMap[pluginClass.__name__] = plugin
- if plugin.isActivated():
- active.append(pluginClass.__name__)
- else:
- deactive.append(pluginname)
-
-
- except:
- self.log.warning(_("Failed activating %(name)s") % {"name": pluginname})
- if self.core.debug:
- traceback.print_exc()
-
- self.log.info(_("Activated plugins: %s") % ", ".join(sorted(active)))
- self.log.info(_("Deactivate plugins: %s") % ", ".join(sorted(deactive)))
-
- self.plugins = plugins
-
- def manageHooks(self, plugin, name, value):
- if name == "activated" and value:
- self.activateHook(plugin)
- elif name == "activated" and not value:
- self.deactivateHook(plugin)
-
- def activateHook(self, plugin):
-
- #check if already loaded
- for inst in self.plugins:
- if inst.__name__ == plugin:
- return
-
- pluginClass = self.core.pluginManager.loadClass("hooks", plugin)
-
- if not pluginClass: return
-
- self.log.debug("Plugin loaded: %s" % plugin)
-
- plugin = pluginClass(self.core, self)
- self.plugins.append(plugin)
- self.pluginMap[pluginClass.__name__] = plugin
-
- # call core Ready
- start_new_thread(plugin.coreReady, tuple())
-
- def deactivateHook(self, plugin):
-
- hook = None
- for inst in self.plugins:
- if inst.__name__ == plugin:
- hook = inst
-
- if not hook: return
-
- self.log.debug("Plugin unloaded: %s" % plugin)
-
- hook.unload()
-
- #remove periodic call
- self.log.debug("Removed callback %s" % self.core.scheduler.removeJob(hook.cb))
- self.plugins.remove(hook)
- del self.pluginMap[hook.__name__]
-
-
- @try_catch
- def coreReady(self):
- for plugin in self.plugins:
- if plugin.isActivated():
- plugin.coreReady()
-
- self.dispatchEvent("coreReady")
-
- @try_catch
- def coreExiting(self):
- for plugin in self.plugins:
- if plugin.isActivated():
- plugin.coreExiting()
-
- self.dispatchEvent("coreExiting")
-
- @lock
- def downloadPreparing(self, pyfile):
- for plugin in self.plugins:
- if plugin.isActivated():
- plugin.downloadPreparing(pyfile)
-
- self.dispatchEvent("downloadPreparing", pyfile)
-
- @lock
- def downloadFinished(self, pyfile):
- for plugin in self.plugins:
- if plugin.isActivated():
- if "downloadFinished" in plugin.__threaded__:
- self.startThread(plugin.downloadFinished, pyfile)
- else:
- plugin.downloadFinished(pyfile)
-
- self.dispatchEvent("downloadFinished", pyfile)
-
- @lock
- @try_catch
- def downloadFailed(self, pyfile):
- for plugin in self.plugins:
- if plugin.isActivated():
- if "downloadFailed" in plugin.__threaded__:
- self.startThread(plugin.downloadFinished, pyfile)
- else:
- plugin.downloadFailed(pyfile)
-
- self.dispatchEvent("downloadFailed", pyfile)
-
- @lock
- def packageFinished(self, package):
- for plugin in self.plugins:
- if plugin.isActivated():
- if "packageFinished" in plugin.__threaded__:
- self.startThread(plugin.packageFinished, package)
- else:
- plugin.packageFinished(package)
-
- self.dispatchEvent("packageFinished", package)
-
- @lock
- def beforeReconnecting(self, ip):
- for plugin in self.plugins:
- plugin.beforeReconnecting(ip)
-
- self.dispatchEvent("beforeReconnecting", ip)
-
- @lock
- def afterReconnecting(self, ip):
- for plugin in self.plugins:
- if plugin.isActivated():
- plugin.afterReconnecting(ip)
-
- self.dispatchEvent("afterReconnecting", ip)
-
- def startThread(self, function, *args, **kwargs):
- t = HookThread(self.core.threadManager, function, args, kwargs)
-
- def activePlugins(self):
- """ returns all active plugins """
- return [x for x in self.plugins if x.isActivated()]
-
- def getAllInfo(self):
- """returns info stored by hook plugins"""
- info = {}
- for name, plugin in self.pluginMap.iteritems():
- if plugin.info:
- #copy and convert so str
- info[name] = dict([(x, str(y) if not isinstance(y, basestring) else y) for x, y in plugin.info.iteritems()])
- return info
-
-
- def getInfo(self, plugin):
- info = {}
- if plugin in self.pluginMap and self.pluginMap[plugin].info:
- info = dict([(x, str(y) if not isinstance(y, basestring) else y)
- for x, y in self.pluginMap[plugin].info.iteritems()])
-
- return info
-
- def addEvent(self, event, func):
- """Adds an event listener for event name"""
- if event in self.events:
- self.events[event].append(func)
- else:
- self.events[event] = [func]
-
- def removeEvent(self, event, func):
- """removes previously added event listener"""
- if event in self.events:
- self.events[event].remove(func)
-
- def dispatchEvent(self, event, *args):
- """dispatches event with args"""
- if event in self.events:
- for f in self.events[event]:
- try:
- f(*args)
- except Exception, e:
- self.log.warning("Error calling event handler %s: %s, %s, %s"
- % (event, f, args, str(e)))
- if self.core.debug:
- traceback.print_exc()
-
diff --git a/module/InitHomeDir.py b/module/InitHomeDir.py
deleted file mode 100644
index 156c9f932..000000000
--- a/module/InitHomeDir.py
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-
- This modules inits working directories and global variables, pydir and homedir
-"""
-
-from os import makedirs, path, chdir
-from os.path import join
-import sys
-from sys import argv, platform
-
-import __builtin__
-__builtin__.owd = path.abspath("") #original working directory
-__builtin__.pypath = path.abspath(path.join(__file__, "..", ".."))
-
-sys.path.append(join(pypath, "module", "lib"))
-
-homedir = ""
-
-if platform == 'nt':
- homedir = path.expanduser("~")
- if homedir == "~":
- import ctypes
-
- CSIDL_APPDATA = 26
- _SHGetFolderPath = ctypes.windll.shell32.SHGetFolderPathW
- _SHGetFolderPath.argtypes = [ctypes.wintypes.HWND,
- ctypes.c_int,
- ctypes.wintypes.HANDLE,
- ctypes.wintypes.DWORD, ctypes.wintypes.LPCWSTR]
-
- path_buf = ctypes.wintypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
- result = _SHGetFolderPath(0, CSIDL_APPDATA, 0, 0, path_buf)
- homedir = path_buf.value
-else:
- homedir = path.expanduser("~")
-
-__builtin__.homedir = homedir
-
-args = " ".join(argv[1:])
-
-# dirty method to set configdir from commandline arguments
-if "--configdir=" in args:
- pos = args.find("--configdir=")
- end = args.find("-", pos + 12)
-
- if end == -1:
- configdir = args[pos + 12:].strip()
- else:
- configdir = args[pos + 12:end].strip()
-elif path.exists(path.join(pypath, "module", "config", "configdir")):
- f = open(path.join(pypath, "module", "config", "configdir"), "rb")
- c = f.read().strip()
- f.close()
- configdir = path.join(pypath, c)
-else:
- if platform in ("posix", "linux2"):
- configdir = path.join(homedir, ".pyload")
- else:
- configdir = path.join(homedir, "pyload")
-
-if not path.exists(configdir):
- makedirs(configdir, 0700)
-
-__builtin__.configdir = configdir
-chdir(configdir)
-
-#print "Using %s as working directory." % configdir
diff --git a/module/PluginThread.py b/module/PluginThread.py
deleted file mode 100644
index 56c36c778..000000000
--- a/module/PluginThread.py
+++ /dev/null
@@ -1,677 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
-
-from Queue import Queue
-from threading import Thread
-from os import listdir, stat
-from os.path import join
-from time import sleep, time, strftime, gmtime
-from traceback import print_exc, format_exc
-from pprint import pformat
-from sys import exc_info, exc_clear
-from copy import copy
-from types import MethodType
-
-from pycurl import error
-
-from PyFile import PyFile
-from plugins.Plugin import Abort, Fail, Reconnect, Retry, SkipDownload
-from common.packagetools import parseNames
-from utils import save_join
-from Api import OnlineStatus
-
-class PluginThread(Thread):
- """abstract base class for thread types"""
-
- #----------------------------------------------------------------------
- def __init__(self, manager):
- """Constructor"""
- Thread.__init__(self)
- self.setDaemon(True)
- self.m = manager #thread manager
-
-
- def writeDebugReport(self, pyfile):
- """ writes a
- :return:
- """
-
- dump_name = "debug_%s_%s.zip" % (pyfile.pluginname, strftime("%d-%m-%Y_%H-%M-%S"))
- dump = self.getDebugDump(pyfile)
-
- try:
- import zipfile
-
- zip = zipfile.ZipFile(dump_name, "w")
-
- for f in listdir(join("tmp", pyfile.pluginname)):
- try:
- # avoid encoding errors
- zip.write(join("tmp", pyfile.pluginname, f), save_join(pyfile.pluginname, f))
- except:
- pass
-
- info = zipfile.ZipInfo(save_join(pyfile.pluginname, "debug_Report.txt"), gmtime())
- info.external_attr = 0644 << 16L # change permissions
-
- zip.writestr(info, dump)
- zip.close()
-
- if not stat(dump_name).st_size:
- raise Exception("Empty Zipfile")
-
- except Exception, e:
- self.m.log.debug("Error creating zip file: %s" % e)
-
- dump_name = dump_name.replace(".zip", ".txt")
- f = open(dump_name, "wb")
- f.write(dump)
- f.close()
-
- self.m.core.log.info("Debug Report written to %s" % dump_name)
-
- def getDebugDump(self, pyfile):
- dump = "pyLoad %s Debug Report of %s %s \n\nTRACEBACK:\n %s \n\nFRAMESTACK:\n" % (
- self.m.core.api.getServerVersion(), pyfile.pluginname, pyfile.plugin.__version__, format_exc())
-
- tb = exc_info()[2]
- stack = []
- while tb:
- stack.append(tb.tb_frame)
- tb = tb.tb_next
-
- for frame in stack[1:]:
- dump += "\nFrame %s in %s at line %s\n" % (frame.f_code.co_name,
- frame.f_code.co_filename,
- frame.f_lineno)
-
- for key, value in frame.f_locals.items():
- dump += "\t%20s = " % key
- try:
- dump += pformat(value) + "\n"
- except Exception, e:
- dump += "<ERROR WHILE PRINTING VALUE> " + str(e) + "\n"
-
- del frame
-
- del stack #delete it just to be sure...
-
- dump += "\n\nPLUGIN OBJECT DUMP: \n\n"
-
- for name in dir(pyfile.plugin):
- attr = getattr(pyfile.plugin, name)
- if not name.endswith("__") and type(attr) != MethodType:
- dump += "\t%20s = " % name
- try:
- dump += pformat(attr) + "\n"
- except Exception, e:
- dump += "<ERROR WHILE PRINTING VALUE> " + str(e) + "\n"
-
- dump += "\nPYFILE OBJECT DUMP: \n\n"
-
- for name in dir(pyfile):
- attr = getattr(pyfile, name)
- if not name.endswith("__") and type(attr) != MethodType:
- dump += "\t%20s = " % name
- try:
- dump += pformat(attr) + "\n"
- except Exception, e:
- dump += "<ERROR WHILE PRINTING VALUE> " + str(e) + "\n"
-
- if pyfile.pluginname in self.m.core.config.plugin:
- dump += "\n\nCONFIG: \n\n"
- dump += pformat(self.m.core.config.plugin[pyfile.pluginname]) + "\n"
-
- return dump
-
- def clean(self, pyfile):
- """ set thread unactive and release pyfile """
- self.active = False
- pyfile.release()
-
-
-class DownloadThread(PluginThread):
- """thread for downloading files from 'real' hoster plugins"""
-
- #----------------------------------------------------------------------
- def __init__(self, manager):
- """Constructor"""
- PluginThread.__init__(self, manager)
-
- self.queue = Queue() # job queue
- self.active = False
-
- self.start()
-
- #----------------------------------------------------------------------
- def run(self):
- """run method"""
- pyfile = None
-
- while True:
- del pyfile
- self.active = self.queue.get()
- pyfile = self.active
-
- if self.active == "quit":
- self.active = False
- self.m.threads.remove(self)
- return True
-
- try:
- if not pyfile.hasPlugin(): continue
- #this pyfile was deleted while queueing
-
- pyfile.plugin.checkForSameFiles(starting=True)
- self.m.log.info(_("Download starts: %s" % pyfile.name))
-
- # start download
- self.m.core.hookManager.downloadPreparing(pyfile)
- pyfile.plugin.preprocessing(self)
-
- self.m.log.info(_("Download finished: %s") % pyfile.name)
- self.m.core.hookManager.downloadFinished(pyfile)
- self.m.core.files.checkPackageFinished(pyfile)
-
- except NotImplementedError:
- self.m.log.error(_("Plugin %s is missing a function.") % pyfile.pluginname)
- pyfile.setStatus("failed")
- pyfile.error = "Plugin does not work"
- self.clean(pyfile)
- continue
-
- except Abort:
- try:
- self.m.log.info(_("Download aborted: %s") % pyfile.name)
- except:
- pass
-
- pyfile.setStatus("aborted")
-
- self.clean(pyfile)
- continue
-
- except Reconnect:
- self.queue.put(pyfile)
- #pyfile.req.clearCookies()
-
- while self.m.reconnecting.isSet():
- sleep(0.5)
-
- continue
-
- except Retry, e:
- reason = e.args[0]
- self.m.log.info(_("Download restarted: %(name)s | %(msg)s") % {"name": pyfile.name, "msg": reason})
- self.queue.put(pyfile)
- continue
-
- except Fail, e:
- msg = e.args[0]
-
- if msg == "offline":
- pyfile.setStatus("offline")
- self.m.log.warning(_("Download is offline: %s") % pyfile.name)
- elif msg == "temp. offline":
- pyfile.setStatus("temp. offline")
- self.m.log.warning(_("Download is temporary offline: %s") % pyfile.name)
- else:
- pyfile.setStatus("failed")
- self.m.log.warning(_("Download failed: %(name)s | %(msg)s") % {"name": pyfile.name, "msg": msg})
- pyfile.error = msg
-
- self.m.core.hookManager.downloadFailed(pyfile)
- self.clean(pyfile)
- continue
-
- except error, e:
- if len(e.args) == 2:
- code, msg = e.args
- else:
- code = 0
- msg = e.args
-
- self.m.log.debug("pycurl exception %s: %s" % (code, msg))
-
- if code in (7, 18, 28, 52, 56):
- self.m.log.warning(_("Couldn't connect to host or connection reset, waiting 1 minute and retry."))
- wait = time() + 60
-
- pyfile.waitUntil = wait
- pyfile.setStatus("waiting")
- while time() < wait:
- sleep(1)
- if pyfile.abort:
- break
-
- if pyfile.abort:
- self.m.log.info(_("Download aborted: %s") % pyfile.name)
- pyfile.setStatus("aborted")
-
- self.clean(pyfile)
- else:
- self.queue.put(pyfile)
-
- continue
-
- else:
- pyfile.setStatus("failed")
- self.m.log.error("pycurl error %s: %s" % (code, msg))
- if self.m.core.debug:
- print_exc()
- self.writeDebugReport(pyfile)
-
- self.m.core.hookManager.downloadFailed(pyfile)
-
- self.clean(pyfile)
- continue
-
- except SkipDownload, e:
- pyfile.setStatus("skipped")
-
- self.m.log.info(
- _("Download skipped: %(name)s due to %(plugin)s") % {"name": pyfile.name, "plugin": e.message})
-
- self.clean(pyfile)
-
- self.m.core.files.checkPackageFinished(pyfile)
-
- self.active = False
- self.m.core.files.save()
-
- continue
-
-
- except Exception, e:
- pyfile.setStatus("failed")
- self.m.log.warning(_("Download failed: %(name)s | %(msg)s") % {"name": pyfile.name, "msg": str(e)})
- pyfile.error = str(e)
-
- if self.m.core.debug:
- print_exc()
- self.writeDebugReport(pyfile)
-
- self.m.core.hookManager.downloadFailed(pyfile)
- self.clean(pyfile)
- continue
-
- finally:
- self.m.core.files.save()
- pyfile.checkIfProcessed()
- exc_clear()
-
-
- #pyfile.plugin.req.clean()
-
- self.active = False
- pyfile.finishIfDone()
- self.m.core.files.save()
-
-
- def put(self, job):
- """assing job to thread"""
- self.queue.put(job)
-
-
- def stop(self):
- """stops the thread"""
- self.put("quit")
-
-
-class DecrypterThread(PluginThread):
- """thread for decrypting"""
-
- def __init__(self, manager, pyfile):
- """constructor"""
- PluginThread.__init__(self, manager)
-
- self.active = pyfile
- manager.localThreads.append(self)
-
- pyfile.setStatus("decrypting")
-
- self.start()
-
- def getActiveFiles(self):
- return [self.active]
-
- def run(self):
- """run method"""
-
- pyfile = self.active
- retry = False
-
- try:
- self.m.log.info(_("Decrypting starts: %s") % self.active.name)
- self.active.plugin.preprocessing(self)
-
- except NotImplementedError:
- self.m.log.error(_("Plugin %s is missing a function.") % self.active.pluginname)
- return
-
- except Fail, e:
- msg = e.args[0]
-
- if msg == "offline":
- self.active.setStatus("offline")
- self.m.log.warning(_("Download is offline: %s") % self.active.name)
- else:
- self.active.setStatus("failed")
- self.m.log.error(_("Decrypting failed: %(name)s | %(msg)s") % {"name": self.active.name, "msg": msg})
- self.active.error = msg
-
- return
-
- except Abort:
- self.m.log.info(_("Download aborted: %s") % pyfile.name)
- pyfile.setStatus("aborted")
-
- return
-
- except Retry:
- self.m.log.info(_("Retrying %s") % self.active.name)
- retry = True
- return self.run()
-
- except Exception, e:
- self.active.setStatus("failed")
- self.m.log.error(_("Decrypting failed: %(name)s | %(msg)s") % {"name": self.active.name, "msg": str(e)})
- self.active.error = str(e)
-
- if self.m.core.debug:
- print_exc()
- self.writeDebugReport(pyfile)
-
- return
-
-
- finally:
- if not retry:
- self.active.release()
- self.active = False
- self.m.core.files.save()
- self.m.localThreads.remove(self)
- exc_clear()
-
-
- #self.m.core.hookManager.downloadFinished(pyfile)
-
-
- #self.m.localThreads.remove(self)
- #self.active.finishIfDone()
- if not retry:
- pyfile.delete()
-
-
-class HookThread(PluginThread):
- """thread for hooks"""
-
- #----------------------------------------------------------------------
- def __init__(self, m, function, args, kwargs):
- """Constructor"""
- PluginThread.__init__(self, m)
-
- self.f = function
- self.args = args
- self.kwargs = kwargs
-
- self.active = []
-
- m.localThreads.append(self)
-
- self.start()
-
- def getActiveFiles(self):
- return self.active
-
- def addActive(self, pyfile):
- """ Adds a pyfile to active list and thus will be displayed on overview"""
- if pyfile not in self.active:
- self.active.append(pyfile)
-
- def finishFile(self, pyfile):
- if pyfile in self.active:
- self.active.remove(pyfile)
-
- pyfile.finishIfDone()
-
- def run(self):
- try:
- try:
- self.kwargs["thread"] = self
- self.f(*self.args, **self.kwargs)
- except TypeError, e:
- #dirty method to filter out exceptions
- if "unexpected keyword argument 'thread'" not in e.args[0]:
- raise
-
- del self.kwargs["thread"]
- self.f(*self.args, **self.kwargs)
- finally:
- local = copy(self.active)
- for x in local:
- self.finishFile(x)
-
- self.m.localThreads.remove(self)
-
-
-class InfoThread(PluginThread):
- def __init__(self, manager, data, pid=-1, rid=-1, add=False):
- """Constructor"""
- PluginThread.__init__(self, manager)
-
- self.data = data
- self.pid = pid # package id
- # [ .. (name, plugin) .. ]
-
- self.rid = rid #result id
- self.add = add #add packages instead of return result
-
- self.cache = [] #accumulated data
-
- self.start()
-
- def run(self):
- """run method"""
-
- plugins = {}
- container = []
-
- for url, plugin in self.data:
- if plugin in plugins:
- plugins[plugin].append(url)
- else:
- plugins[plugin] = [url]
-
-
- # filter out container plugins
- for name in self.m.core.pluginManager.containerPlugins:
- if name in plugins:
- container.extend([(name, url) for url in plugins[name]])
-
- del plugins[name]
-
- #directly write to database
- if self.pid > -1:
- for pluginname, urls in plugins.iteritems():
- plugin = self.m.core.pluginManager.getPlugin(pluginname, True)
- if hasattr(plugin, "getInfo"):
- self.fetchForPlugin(pluginname, plugin, urls, self.updateDB)
- self.m.core.files.save()
-
- elif self.add:
- for pluginname, urls in plugins.iteritems():
- plugin = self.m.core.pluginManager.getPlugin(pluginname, True)
- if hasattr(plugin, "getInfo"):
- self.fetchForPlugin(pluginname, plugin, urls, self.updateCache, True)
-
- else:
- #generate default result
- result = [(url, 0, 3, url) for url in urls]
-
- self.updateCache(pluginname, result)
-
- packs = parseNames([(name, url) for name, x, y, url in self.cache])
-
- self.m.log.debug("Fetched and generated %d packages" % len(packs))
-
- for k, v in packs:
- self.m.core.api.addPackage(k, v)
-
- #empty cache
- del self.cache[:]
-
- else: #post the results
-
-
- for name, url in container:
- #attach container content
- try:
- data = self.decryptContainer(name, url)
- except:
- print_exc()
- self.m.log.error("Could not decrypt container.")
- data = []
-
- for url, plugin in data:
- if plugin in plugins:
- plugins[plugin].append(url)
- else:
- plugins[plugin] = [url]
-
- self.m.infoResults[self.rid] = {}
-
- for pluginname, urls in plugins.iteritems():
- plugin = self.m.core.pluginManager.getPlugin(pluginname, True)
- if hasattr(plugin, "getInfo"):
- self.fetchForPlugin(pluginname, plugin, urls, self.updateResult, True)
-
- #force to process cache
- if self.cache:
- self.updateResult(pluginname, [], True)
-
- else:
- #generate default result
- result = [(url, 0, 3, url) for url in urls]
-
- self.updateResult(pluginname, result, True)
-
- self.m.infoResults[self.rid]["ALL_INFO_FETCHED"] = {}
-
- self.m.timestamp = time() + 5 * 60
-
-
- def updateDB(self, plugin, result):
- self.m.core.files.updateFileInfo(result, self.pid)
-
- def updateResult(self, plugin, result, force=False):
- #parse package name and generate result
- #accumulate results
-
- self.cache.extend(result)
-
- if len(self.cache) >= 20 or force:
- #used for package generating
- tmp = [(name, (url, OnlineStatus(name, plugin, "unknown", status, int(size))))
- for name, size, status, url in self.cache]
-
- data = parseNames(tmp)
- result = {}
- for k, v in data.iteritems():
- for url, status in v:
- status.packagename = k
- result[url] = status
-
- self.m.setInfoResults(self.rid, result)
-
- self.cache = []
-
- def updateCache(self, plugin, result):
- self.cache.extend(result)
-
- def fetchForPlugin(self, pluginname, plugin, urls, cb, err=None):
- try:
- result = [] #result loaded from cache
- process = [] #urls to process
- for url in urls:
- if url in self.m.infoCache:
- result.append(self.m.infoCache[url])
- else:
- process.append(url)
-
- if result:
- self.m.log.debug("Fetched %d values from cache for %s" % (len(result), pluginname))
- cb(pluginname, result)
-
- if process:
- self.m.log.debug("Run Info Fetching for %s" % pluginname)
- for result in plugin.getInfo(process):
- #result = [ .. (name, size, status, url) .. ]
- if not type(result) == list: result = [result]
-
- for res in result:
- self.m.infoCache[res[3]] = res
-
- cb(pluginname, result)
-
- self.m.log.debug("Finished Info Fetching for %s" % pluginname)
- except Exception, e:
- self.m.log.warning(_("Info Fetching for %(name)s failed | %(err)s") %
- {"name": pluginname, "err": str(e)})
- if self.m.core.debug:
- print_exc()
-
- # generate default results
- if err:
- result = [(url, 0, 3, url) for url in urls]
- cb(pluginname, result)
-
-
- def decryptContainer(self, plugin, url):
- data = []
- # only works on container plugins
-
- self.m.log.debug("Pre decrypting %s with %s" % (url, plugin))
-
- # dummy pyfile
- pyfile = PyFile(self.m.core.files, -1, url, url, 0, 0, "", plugin, -1, -1)
-
- pyfile.initPlugin()
-
- # little plugin lifecycle
- try:
- pyfile.plugin.setup()
- pyfile.plugin.loadToDisk()
- pyfile.plugin.decrypt(pyfile)
- pyfile.plugin.deleteTmp()
-
- for pack in pyfile.plugin.packages:
- pyfile.plugin.urls.extend(pack[1])
-
- data = self.m.core.pluginManager.parseUrls(pyfile.plugin.urls)
-
- self.m.log.debug("Got %d links." % len(data))
-
- except Exception, e:
- self.m.log.debug("Pre decrypting error: %s" % str(e))
- finally:
- pyfile.release()
-
- return data
diff --git a/module/__init__.py b/module/__init__.py
index e69de29bb..e29c81ad7 100644
--- a/module/__init__.py
+++ b/module/__init__.py
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import with_statement
+
+import __builtin__
+
+import os
+import platform
+import sys
+
+from codecs import getwriter
+
+from pyload.utils import get_console_encoding
+
+
+__all__ = ["__status_code__", "__status__", "__version_info__", "__version__", "__author_name__", "__author_mail__", "__license__"]
+
+__version_info__ = (0, 4, 10)
+__version__ = '.'.join(map(str, __version_info__))
+
+__status_code__ = 4
+__status__ = {1: "Planning",
+ 2: "Pre-Alpha",
+ 3: "Alpha",
+ 4: "Beta",
+ 5: "Production/Stable",
+ 6: "Mature",
+ 7: "Inactive"}[__status_code__] #: PyPI Development Status Classifiers
+
+__description__ = "Fast, lightweight and full featured download manager"
+
+__license__ = "GNU General Public License v3"
+
+__website__ = "http://pyload.org"
+
+__authors__ = [("Marius" , "mkaay@mkaay.de" ),
+ ("RaNaN" , "Mast3rRaNaN@hotmail.de"),
+ ("Stefano" , "l.stickell@yahoo.it" ),
+ ("Walter Purcaro", "vuolter@gmail.com" ),
+ ("himbrr" , "himbrr@himbrr.ws" ),
+ ("sebnapi" , "" ),
+ ("spoob" , "spoob@gmx.de" ),
+ ("zoidberg10" , "zoidberg@mujmail.cz" )]
+
+
+################################# InitHomeDir #################################
+
+__builtin__.owd = os.path.abspath("") #: original working directory
+__builtin__.homedir = os.path.expanduser("~")
+__builtin__.rootdir = os.path.abspath(os.path.join(__file__, ".."))
+__builtin__.configdir = ""
+__builtin__.pypath = os.path.abspath(os.path.join(rootdir, ".."))
+
+
+if "64" in platform.machine():
+ sys.path.append(os.path.join(pypath, "lib64"))
+sys.path.append(os.path.join(pypath, "lib"))
+
+sys.stdout = getwriter(get_console_encoding(sys.stdout.encoding))(sys.stdout, errors="replace")
+
+if homedir == "~" and os.name == "nt":
+ import ctypes
+
+ CSIDL_APPDATA = 26
+
+ _SHGetFolderPath = ctypes.windll.shell32.SHGetFolderPathW
+ _SHGetFolderPath.argtypes = [ctypes.wintypes.HWND,
+ ctypes.c_int,
+ ctypes.wintypes.HANDLE,
+ ctypes.wintypes.DWORD,
+ ctypes.wintypes.LPCWSTR]
+
+ path_buf = ctypes.wintypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
+
+ _SHGetFolderPath(0, CSIDL_APPDATA, 0, 0, path_buf)
+
+ __builtin__.homedir = path_buf.value
+
+try:
+ p = os.path.join(rootdir, "config", "configdir")
+
+ with open(p, "rb") as f:
+ configdir = f.read().strip()
+
+except IOError:
+ if os.name == "posix":
+ configdir = os.path.join(homedir, ".pyload")
+ else:
+ configdir = os.path.join(homedir, "pyload")
+
+try:
+ if not os.path.exists(configdir):
+ os.makedirs(configdir, 0700)
+
+ os.chdir(configdir)
+
+except IOError, e:
+ print >> sys.stderr, "configdir init failed: %d (%s)" % (e.errno, e.strerror)
+ sys.exit(1)
+
+else:
+ __builtin__.configdir = configdir
diff --git a/module/Api.py b/module/api/__init__.py
index f0bf5e264..387481da2 100644
--- a/module/Api.py
+++ b/module/api/__init__.py
@@ -1,76 +1,70 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
from base64 import standard_b64encode
from os.path import join
from time import time
import re
-from PyFile import PyFile
-from utils import freeSpace, compare_time
-from common.packagetools import parseNames
-from network.RequestFactory import getURL
-from remote import activated
+from urlparse import urlparse
+
+from pyload.datatype.File import PyFile
+from pyload.utils.packagetools import parseNames
+from pyload.network.RequestFactory import getURL
+from pyload.remote import activated
+from pyload.utils import compare_time, freeSpace, safe_filename
if activated:
try:
- from remote.thriftbackend.thriftgen.pyload.ttypes import *
- from remote.thriftbackend.thriftgen.pyload.Pyload import Iface
+ from thrift.protocol import TBase
+ from pyload.remote.thriftbackend.thriftgen.pyload.ttypes import *
+ from pyload.remote.thriftbackend.thriftgen.pyload.Pyload import Iface
+
BaseObject = TBase
+
except ImportError:
+ from pyload.api.types import *
+
print "Thrift not imported"
- from remote.socketbackend.ttypes import *
+
else:
- from remote.socketbackend.ttypes import *
+ from pyload.api.types import *
# contains function names mapped to their permissions
# unlisted functions are for admins only
permMap = {}
+
# decorator only called on init, never initialized, so has no effect on runtime
def permission(bits):
class _Dec(object):
def __new__(cls, func, *args, **kwargs):
permMap[func.__name__] = bits
return func
-
return _Dec
-urlmatcher = re.compile(r"((https?|ftps?|xdcc|sftp):((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-=\\\.&]*)", re.IGNORECASE)
+urlmatcher = re.compile(r"((https?|ftps?|xdcc|sftp):((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-=\\\.&\[\]\|]*)", re.IGNORECASE)
-class PERMS:
+
+class PERMS(object):
ALL = 0 # requires no permission, but login
ADD = 1 # can add packages
- DELETE = 2 # can delete packages
- STATUS = 4 # see and change server status
- LIST = 16 # see queue and collector
- MODIFY = 32 # moddify some attribute of downloads
+ DELETE = 2 # can delete packages
+ STATUS = 4 # see and change server status
+ LIST = 16 # see queue and collector
+ MODIFY = 32 # moddify some attribute of downloads
DOWNLOAD = 64 # can download from webinterface
- SETTINGS = 128 # can access settings
- ACCOUNTS = 256 # can access accounts
- LOGS = 512 # can see server logs
+ SETTINGS = 128 # can access settings
+ ACCOUNTS = 256 # can access accounts
+ LOGS = 512 # can see server logs
+
-class ROLE:
- ADMIN = 0 #admin has all permissions implicit
+class ROLE(object):
+ ADMIN = 0 # admin has all permissions implicit
USER = 1
+
def has_permission(userperms, perms):
# bytewise or perms before if needed
return perms == (userperms & perms)
@@ -89,17 +83,16 @@ class Api(Iface):
These can be configured via webinterface.
Admin user have all permissions, and are the only ones who can access the methods with no specific permission.
"""
-
EXTERNAL = Iface # let the json api know which methods are external
def __init__(self, core):
self.core = core
def _convertPyFile(self, p):
- f = FileData(p["id"], p["url"], p["name"], p["plugin"], p["size"],
- p["format_size"], p["status"], p["statusmsg"],
- p["package"], p["error"], p["order"])
- return f
+ fdata = FileData(p["id"], p["url"], p["name"], p["plugin"], p["size"],
+ p["format_size"], p["status"], p["statusmsg"],
+ p["package"], p["error"], p["order"])
+ return fdata
def _convertConfigFormat(self, c):
sections = {}
@@ -134,8 +127,7 @@ class Api(Iface):
value = self.core.config[category][option]
else:
value = self.core.config.getPlugin(category, option)
-
- return str(value) if not isinstance(value, basestring) else value
+ return str(value)
@permission(PERMS.SETTINGS)
def setConfigValue(self, category, option, value, section="core"):
@@ -146,21 +138,18 @@ class Api(Iface):
:param value: new config value
:param section: 'plugin' or 'core
"""
- self.core.hookManager.dispatchEvent("configChanged", category, option, value, section)
-
+ self.core.addonManager.dispatchEvent("config-changed", category, option, value, section)
if section == "core":
self.core.config[category][option] = value
-
- if option in ("limit_speed", "max_speed"): #not so nice to update the limit
+ if option in ("limit_speed", "max_speed"): # not so nice to update the limit
self.core.requestFactory.updateBucket()
-
elif section == "plugin":
self.core.config.setPlugin(category, option, value)
@permission(PERMS.SETTINGS)
def getConfig(self):
"""Retrieves complete config of core.
-
+
:return: list of `ConfigSection`
"""
return self._convertConfigFormat(self.core.config.config)
@@ -187,7 +176,6 @@ class Api(Iface):
"""
return self.core.config.plugin
-
@permission(PERMS.STATUS)
def pauseServer(self):
"""Pause server: Tt wont start any new downloads, but nothing gets aborted."""
@@ -219,17 +207,15 @@ class Api(Iface):
@permission(PERMS.LIST)
def statusServer(self):
"""Some general information about the current status of pyLoad.
-
+
:return: `ServerStatus`
"""
serverStatus = ServerStatus(self.core.threadManager.pause, len(self.core.threadManager.processingIds()),
self.core.files.getQueueCount(), self.core.files.getFileCount(), 0,
not self.core.threadManager.pause and self.isTimeDownload(),
self.core.config['reconnect']['activated'] and self.isTimeReconnect())
-
for pyfile in [x.active for x in self.core.threadManager.threads if x.active and isinstance(x.active, PyFile)]:
- serverStatus.speed += pyfile.getSpeed() #bytes/s
-
+ serverStatus.speed += pyfile.getSpeed() # bytes/s
return serverStatus
@permission(PERMS.STATUS)
@@ -265,7 +251,7 @@ class Api(Iface):
if offset >= len(lines):
return []
return lines[offset:]
- except:
+ except Exception:
return ['No log available']
@permission(PERMS.STATUS)
@@ -298,13 +284,11 @@ class Api(Iface):
for pyfile in self.core.threadManager.getActiveFiles():
if not isinstance(pyfile, PyFile):
continue
-
data.append(DownloadInfo(
pyfile.id, pyfile.name, pyfile.getSpeed(), pyfile.getETA(), pyfile.formatETA(),
pyfile.getBytesLeft(), pyfile.getSize(), pyfile.formatSize(), pyfile.getPercent(),
pyfile.status, pyfile.getStatusName(), pyfile.formatWait(),
pyfile.waitUntil, pyfile.packageid, pyfile.package().name, pyfile.pluginname))
-
return data
@permission(PERMS.ADD)
@@ -317,11 +301,11 @@ class Api(Iface):
:return: package id of the new package
"""
if self.core.config['general']['folder_per_package']:
- folder = name
+ folder = urlparse(name).path.split("/")[-1]
else:
folder = ""
- folder = folder.replace("http://", "").replace(":", "").replace("/", "_").replace("\\", "_")
+ folder = safe_filename(folder)
pid = self.core.files.addPackage(name, folder, dest)
@@ -341,18 +325,14 @@ class Api(Iface):
:return:
"""
urls = []
-
if html:
urls += [x[0] for x in urlmatcher.findall(html)]
-
if url:
page = getURL(url)
urls += [x[0] for x in urlmatcher.findall(page)]
-
# remove duplicates
return self.checkURLs(set(urls))
-
@permission(PERMS.ADD)
def checkURLs(self, urls):
""" Gets urls and returns pluginname mapped to list of matches urls.
@@ -363,11 +343,11 @@ class Api(Iface):
data = self.core.pluginManager.parseUrls(urls)
plugins = {}
- for url, plugin in data:
- if plugin in plugins:
- plugins[plugin].append(url)
- else:
- plugins[plugin] = [url]
+ for url, plugintype, pluginname in data:
+ try:
+ plugins[plugintype][pluginname].append(url)
+ except Exception:
+ plugins[plugintype][pluginname] = [url]
return plugins
@@ -382,10 +362,9 @@ class Api(Iface):
rid = self.core.threadManager.createResultThread(data, False)
- tmp = [(url, (url, OnlineStatus(url, pluginname, "unknown", 3, 0))) for url, pluginname in data]
+ tmp = [(url, (url, OnlineStatus(url, (plugintype, pluginname), "unknown", 3, 0))) for url, plugintype, pluginname in data]
data = parseNames(tmp)
result = {}
-
for k, v in data.iteritems():
for url, status in v:
status.packagename = k
@@ -405,7 +384,6 @@ class Api(Iface):
th = open(join(self.core.config["general"]["download_folder"], "tmp_" + container), "wb")
th.write(str(data))
th.close()
-
return self.checkOnlineStatus(urls + [th.name])
@permission(PERMS.ADD)
@@ -416,14 +394,12 @@ class Api(Iface):
:return: `OnlineCheck`, if rid is -1 then no more data available
"""
result = self.core.threadManager.getInfoResult(rid)
-
if "ALL_INFO_FETCHED" in result:
del result["ALL_INFO_FETCHED"]
return OnlineCheck(-1, result)
else:
return OnlineCheck(rid, result)
-
@permission(PERMS.ADD)
def generatePackages(self, links):
""" Parses links, generates packages names from urls
@@ -431,8 +407,7 @@ class Api(Iface):
:param links: list of urls
:return: package names mapped to urls
"""
- result = parseNames((x, x) for x in links)
- return result
+ return parseNames((x, x) for x in links)
@permission(PERMS.ADD)
def generateAndAddPackages(self, links, dest=Destination.Queue):
@@ -457,7 +432,6 @@ class Api(Iface):
data = self.core.pluginManager.parseUrls(links)
self.core.threadManager.createResultThread(data, True)
-
@permission(PERMS.LIST)
def getPackageData(self, pid):
"""Returns complete information about package, and included files.
@@ -466,15 +440,11 @@ class Api(Iface):
:return: `PackageData` with .links attribute
"""
data = self.core.files.getPackageData(int(pid))
-
if not data:
raise PackageDoesNotExists(pid)
-
- pdata = PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"],
- data["queue"], data["order"],
- links=[self._convertPyFile(x) for x in data["links"].itervalues()])
-
- return pdata
+ return PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"],
+ data["queue"], data["order"],
+ links=[self._convertPyFile(x) for x in data["links"].itervalues()])
@permission(PERMS.LIST)
def getPackageInfo(self, pid):
@@ -484,15 +454,12 @@ class Api(Iface):
:return: `PackageData` with .fid attribute
"""
data = self.core.files.getPackageData(int(pid))
-
+
if not data:
raise PackageDoesNotExists(pid)
-
- pdata = PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"],
- data["queue"], data["order"],
- fids=[int(x) for x in data["links"]])
-
- return pdata
+ return PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"],
+ data["queue"], data["order"],
+ fids=[int(x) for x in data["links"]])
@permission(PERMS.LIST)
def getFileData(self, fid):
@@ -504,19 +471,16 @@ class Api(Iface):
info = self.core.files.getFileData(int(fid))
if not info:
raise FileDoesNotExists(fid)
-
- fdata = self._convertPyFile(info.values()[0])
- return fdata
+ return self._convertPyFile(info.values()[0])
@permission(PERMS.DELETE)
def deleteFiles(self, fids):
"""Deletes several file entries from pyload.
-
+
:param fids: list of file ids
"""
- for id in fids:
- self.core.files.deleteLink(int(id))
-
+ for fid in fids:
+ self.core.files.deleteLink(int(fid))
self.core.files.save()
@permission(PERMS.DELETE)
@@ -525,9 +489,8 @@ class Api(Iface):
:param pids: list of package ids
"""
- for id in pids:
- self.core.files.deletePackage(int(id))
-
+ for pid in pids:
+ self.core.files.deletePackage(int(pid))
self.core.files.save()
@permission(PERMS.LIST)
@@ -580,16 +543,14 @@ class Api(Iface):
links=[self._convertPyFile(x) for x in pack["links"].itervalues()])
for pack in self.core.files.getCompleteData(Destination.Collector).itervalues()]
-
@permission(PERMS.ADD)
def addFiles(self, pid, links):
"""Adds files to specific package.
-
+
:param pid: package id
:param links: list of urls
"""
self.core.files.addLinks(links, int(pid))
-
self.core.log.info(_("Added %(count)d links to package #%(package)d ") % {"count": len(links), "package": pid})
self.core.files.save()
@@ -650,7 +611,6 @@ class Api(Iface):
:return:
"""
pyfiles = self.core.files.cache.values()
-
for pyfile in pyfiles:
if pyfile.id in fids:
pyfile.abortDownload()
@@ -673,8 +633,8 @@ class Api(Iface):
:param destination: `Destination`
:param pid: package id
"""
- if destination not in (0, 1): return
- self.core.files.setPackageLocation(pid, destination)
+ if destination in (0, 1):
+ self.core.files.setPackageLocation(pid, destination)
@permission(PERMS.MODIFY)
def moveFiles(self, fids, pid):
@@ -684,7 +644,7 @@ class Api(Iface):
:param pid: destination package
:return:
"""
- #TODO: implement
+ # TODO: implement
pass
@@ -698,7 +658,6 @@ class Api(Iface):
th = open(join(self.core.config["general"]["download_folder"], "tmp_" + filename), "wb")
th.write(str(data))
th.close()
-
self.addPackage(th.name, [th.name], Destination.Queue)
@permission(PERMS.MODIFY)
@@ -706,7 +665,7 @@ class Api(Iface):
"""Gives a package a new position.
:param pid: package id
- :param position:
+ :param position:
"""
self.core.files.reorderPackage(pid, position)
@@ -726,14 +685,14 @@ class Api(Iface):
:param pid: package id
:param data: dict that maps attribute to desired value
"""
- p = self.core.files.getPackage(pid)
- if not p: raise PackageDoesNotExists(pid)
-
+ package = self.core.files.getPackage(pid)
+ if not package:
+ raise PackageDoesNotExists(pid)
for key, value in data.iteritems():
- if key == "id": continue
- setattr(p, key, value)
-
- p.sync()
+ if key == "id":
+ continue
+ setattr(package, key, value)
+ package.sync()
self.core.files.save()
@permission(PERMS.DELETE)
@@ -756,13 +715,11 @@ class Api(Iface):
:param destination: `Destination`
:return: dict mapping order to package id
"""
-
packs = self.core.files.getInfoData(destination)
order = {}
-
for pid in packs:
pack = self.core.files.getPackageData(int(pid))
- while pack["order"] in order.keys(): #just in case
+ while pack["order"] in order.keys(): # just in case
pack["order"] += 1
order[pack["order"]] = pack["id"]
return order
@@ -774,10 +731,10 @@ class Api(Iface):
:param pid:
:return: dict mapping order to file id
"""
- rawData = self.core.files.getPackageData(int(pid))
+ rawdata = self.core.files.getPackageData(int(pid))
order = {}
- for id, pyfile in rawData["links"].iteritems():
- while pyfile["order"] in order.keys(): #just in case
+ for id, pyfile in rawdata["links"].iteritems():
+ while pyfile["order"] in order.keys(): # just in case
pyfile["order"] += 1
order[pyfile["order"]] = pyfile["id"]
return order
@@ -805,10 +762,9 @@ class Api(Iface):
if task:
task.setWatingForUser(exclusive=exclusive)
data, type, result = task.getCaptcha()
- t = CaptchaTask(int(task.id), standard_b64encode(data), type, result)
- return t
- else:
- return CaptchaTask(-1)
+ ctask = CaptchaTask(int(task.id), standard_b64encode(data), type, result)
+ return ctask
+ return CaptchaTask(-1)
@permission(PERMS.STATUS)
def getCaptchaTaskStatus(self, tid):
@@ -818,8 +774,8 @@ class Api(Iface):
:return: string
"""
self.core.lastClientConnected = time()
- t = self.core.captchaManager.getTaskByID(tid)
- return t.getStatus() if t else ""
+ task = self.core.captchaManager.getTaskByID(tid)
+ return task.getStatus() if task else ""
@permission(PERMS.STATUS)
def setCaptchaResult(self, tid, result):
@@ -834,7 +790,6 @@ class Api(Iface):
task.setResult(result)
self.core.captchaManager.removeTask(task)
-
@permission(PERMS.STATUS)
def getEvents(self, uuid):
"""Lists occured events, may be affected to changes in future.
@@ -843,7 +798,7 @@ class Api(Iface):
:return: list of `Events`
"""
events = self.core.pullManager.getEvents(uuid)
- newEvents = []
+ new_events = []
def convDest(d):
return Destination.Queue if d == "queue" else Destination.Collector
@@ -862,8 +817,8 @@ class Api(Iface):
event.destination = convDest(e[3])
elif e[0] == "reload":
event.destination = convDest(e[1])
- newEvents.append(event)
- return newEvents
+ new_events.append(event)
+ return new_events
@permission(PERMS.ACCOUNTS)
def getAccounts(self, refresh):
@@ -873,12 +828,11 @@ class Api(Iface):
:return: list of `AccountInfo`
"""
accs = self.core.accountManager.getAccountInfos(False, refresh)
- accounts = []
for group in accs.values():
- accounts.extend([AccountInfo(acc["validuntil"], acc["login"], acc["options"], acc["valid"],
- acc["trafficleft"], acc["maxtraffic"], acc["premium"], acc["type"])
- for acc in group])
- return accounts
+ accounts = [AccountInfo(acc["validuntil"], acc["login"], acc["options"], acc["valid"],
+ acc["trafficleft"], acc["maxtraffic"], acc["premium"], acc["type"])
+ for acc in group]
+ return accounts or []
@permission(PERMS.ALL)
def getAccountTypes(self):
@@ -889,9 +843,9 @@ class Api(Iface):
return self.core.accountManager.accounts.keys()
@permission(PERMS.ACCOUNTS)
- def updateAccount(self, plugin, account, password=None, options={}):
+ def updateAccount(self, plugin, account, password=None, options=None):
"""Changes pw/options for specific account."""
- self.core.accountManager.updateAccount(plugin, account, password, options)
+ self.core.accountManager.updateAccount(plugin, account, password, options or {})
@permission(PERMS.ACCOUNTS)
def removeAccount(self, plugin, account):
@@ -911,22 +865,20 @@ class Api(Iface):
:param remoteip: Omit this argument, its only used internal
:return: bool indicating login was successful
"""
- return True if self.checkAuth(username, password, remoteip) else False
+ return bool(self.checkAuth(username, password, remoteip))
def checkAuth(self, username, password, remoteip=None):
"""Check authentication and returns details
:param username:
:param password:
- :param remoteip:
+ :param remoteip:
:return: dict with info, empty when login is incorrect
"""
if self.core.config["remote"]["nolocalauth"] and remoteip == "127.0.0.1":
return "local"
- if self.core.startedInGui and remoteip == "127.0.0.1":
- return "local"
-
- return self.core.db.checkAuth(username, password)
+ else:
+ return self.core.db.checkAuth(username, password)
def isAuthorized(self, func, userdata):
"""checks if the user is authorized for specific method
@@ -942,36 +894,27 @@ class Api(Iface):
else:
return False
-
@permission(PERMS.ALL)
def getUserData(self, username, password):
"""similar to `checkAuth` but returns UserData thrift type """
- user = self.checkAuth(username, password)
+ user = self.checkAuth(username, password)
if user:
return UserData(user["name"], user["email"], user["role"], user["permission"], user["template"])
else:
return UserData()
-
def getAllUserData(self):
"""returns all known user and info"""
- res = {}
- for user, data in self.core.db.getAllUserData().iteritems():
- res[user] = UserData(user, data["email"], data["role"], data["permission"], data["template"])
-
- return res
+ return dict((user, UserData(user, data["email"], data["role"], data["permission"], data["template"])) for user, data
+ in self.core.db.getAllUserData().iteritems())
@permission(PERMS.STATUS)
def getServices(self):
- """ A dict of available services, these can be defined by hook plugins.
+ """ A dict of available services, these can be defined by addon plugins.
:return: dict with this style: {"plugin": {"method": "description"}}
"""
- data = {}
- for plugin, funcs in self.core.hookManager.methods.iteritems():
- data[plugin] = funcs
-
- return data
+ return dict((plugin, funcs) for plugin, funcs in self.core.addonManager.methods.iteritems())
@permission(PERMS.STATUS)
def hasService(self, plugin, func):
@@ -981,12 +924,12 @@ class Api(Iface):
:param func:
:return: bool
"""
- cont = self.core.hookManager.methods
+ cont = self.core.addonManager.methods
return plugin in cont and func in cont[plugin]
@permission(PERMS.STATUS)
def call(self, info):
- """Calls a service (a method in hook plugin).
+ """Calls a service (a method in addon plugin).
:param info: `ServiceCall`
:return: result
@@ -997,23 +940,20 @@ class Api(Iface):
func = info.func
args = info.arguments
parse = info.parseArguments
-
if not self.hasService(plugin, func):
raise ServiceDoesNotExists(plugin, func)
-
try:
- ret = self.core.hookManager.callRPC(plugin, func, args, parse)
- return str(ret)
+ ret = self.core.addonManager.callRPC(plugin, func, args, parse)
except Exception, e:
raise ServiceException(e.message)
@permission(PERMS.STATUS)
def getAllInfo(self):
- """Returns all information stored by hook plugins. Values are always strings
+ """Returns all information stored by addon plugins. Values are always strings
- :return: {"plugin": {"name": value } }
+ :return: {"plugin": {"name": value}}
"""
- return self.core.hookManager.getAllInfo()
+ return self.core.addonManager.getAllInfo()
@permission(PERMS.STATUS)
def getInfoByPlugin(self, plugin):
@@ -1022,12 +962,12 @@ class Api(Iface):
:param plugin: pluginname
:return: dict of attr names mapped to value {"name": value}
"""
- return self.core.hookManager.getInfo(plugin)
+ return self.core.addonManager.getInfo(plugin)
def changePassword(self, user, oldpw, newpw):
""" changes password for specific user """
return self.core.db.changePassword(user, oldpw, newpw)
- def setUserPermission(self, user, permission, role):
- self.core.db.setPermission(user, permission)
- self.core.db.setRole(user, role) \ No newline at end of file
+ def setUserPermission(self, user, perm, role):
+ self.core.db.setPermission(user, perm)
+ self.core.db.setRole(user, role)
diff --git a/module/api/types.py b/module/api/types.py
new file mode 100644
index 000000000..81385bf9f
--- /dev/null
+++ b/module/api/types.py
@@ -0,0 +1,381 @@
+# -*- coding: utf-8 -*-
+# Autogenerated by pyload
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+
+class BaseObject(object):
+ __slots__ = []
+
+class Destination(object):
+ Collector = 0
+ Queue = 1
+
+class DownloadStatus(object):
+ Aborted = 9
+ Custom = 11
+ Decrypting = 10
+ Downloading = 12
+ Failed = 8
+ Finished = 0
+ Offline = 1
+ Online = 2
+ Processing = 13
+ Queued = 3
+ Skipped = 4
+ Starting = 7
+ TempOffline = 6
+ Unknown = 14
+ Waiting = 5
+
+class ElementType(object):
+ File = 1
+ Package = 0
+
+class Input(object):
+ BOOL = 4
+ CHOICE = 6
+ CLICK = 5
+ LIST = 8
+ MULTIPLE = 7
+ NONE = 0
+ PASSWORD = 3
+ TABLE = 9
+ TEXT = 1
+ TEXTBOX = 2
+
+class Output(object):
+ CAPTCHA = 1
+ NOTIFICATION = 4
+ QUESTION = 2
+
+class AccountInfo(BaseObject):
+ __slots__ = ['validuntil', 'login', 'options', 'valid', 'trafficleft', 'maxtraffic', 'premium', 'type']
+
+ def __init__(self, validuntil=None, login=None, options=None, valid=None, trafficleft=None, maxtraffic=None, premium=None, type=None):
+ self.validuntil = validuntil
+ self.login = login
+ self.options = options
+ self.valid = valid
+ self.trafficleft = trafficleft
+ self.maxtraffic = maxtraffic
+ self.premium = premium
+ self.type = type
+
+class CaptchaTask(BaseObject):
+ __slots__ = ['tid', 'data', 'type', 'resultType']
+
+ def __init__(self, tid=None, data=None, type=None, resultType=None):
+ self.tid = tid
+ self.data = data
+ self.type = type
+ self.resultType = resultType
+
+class ConfigItem(BaseObject):
+ __slots__ = ['name', 'description', 'value', 'type']
+
+ def __init__(self, name=None, description=None, value=None, type=None):
+ self.name = name
+ self.description = description
+ self.value = value
+ self.type = type
+
+class ConfigSection(BaseObject):
+ __slots__ = ['name', 'description', 'items', 'outline']
+
+ def __init__(self, name=None, description=None, items=None, outline=None):
+ self.name = name
+ self.description = description
+ self.items = items
+ self.outline = outline
+
+class DownloadInfo(BaseObject):
+ __slots__ = ['fid', 'name', 'speed', 'eta', 'format_eta', 'bleft', 'size', 'format_size', 'percent', 'status', 'statusmsg', 'format_wait', 'wait_until', 'packageID', 'packageName', 'plugin']
+
+ def __init__(self, fid=None, name=None, speed=None, eta=None, format_eta=None, bleft=None, size=None, format_size=None, percent=None, status=None, statusmsg=None, format_wait=None, wait_until=None, packageID=None, packageName=None, plugin=None):
+ self.fid = fid
+ self.name = name
+ self.speed = speed
+ self.eta = eta
+ self.format_eta = format_eta
+ self.bleft = bleft
+ self.size = size
+ self.format_size = format_size
+ self.percent = percent
+ self.status = status
+ self.statusmsg = statusmsg
+ self.format_wait = format_wait
+ self.wait_until = wait_until
+ self.packageID = packageID
+ self.packageName = packageName
+ self.plugin = plugin
+
+class EventInfo(BaseObject):
+ __slots__ = ['eventname', 'id', 'type', 'destination']
+
+ def __init__(self, eventname=None, id=None, type=None, destination=None):
+ self.eventname = eventname
+ self.id = id
+ self.type = type
+ self.destination = destination
+
+class FileData(BaseObject):
+ __slots__ = ['fid', 'url', 'name', 'plugin', 'size', 'format_size', 'status', 'statusmsg', 'packageID', 'error', 'order']
+
+ def __init__(self, fid=None, url=None, name=None, plugin=None, size=None, format_size=None, status=None, statusmsg=None, packageID=None, error=None, order=None):
+ self.fid = fid
+ self.url = url
+ self.name = name
+ self.plugin = plugin
+ self.size = size
+ self.format_size = format_size
+ self.status = status
+ self.statusmsg = statusmsg
+ self.packageID = packageID
+ self.error = error
+ self.order = order
+
+class FileDoesNotExists(Exception):
+ __slots__ = ['fid']
+
+ def __init__(self, fid=None):
+ self.fid = fid
+
+class InteractionTask(BaseObject):
+ __slots__ = ['iid', 'input', 'structure', 'preset', 'output', 'data', 'title', 'description', 'plugin']
+
+ def __init__(self, iid=None, input=None, structure=None, preset=None, output=None, data=None, title=None, description=None, plugin=None):
+ self.iid = iid
+ self.input = input
+ self.structure = structure
+ self.preset = preset
+ self.output = output
+ self.data = data
+ self.title = title
+ self.description = description
+ self.plugin = plugin
+
+class OnlineCheck(BaseObject):
+ __slots__ = ['rid', 'data']
+
+ def __init__(self, rid=None, data=None):
+ self.rid = rid
+ self.data = data
+
+class OnlineStatus(BaseObject):
+ __slots__ = ['name', 'plugin', 'packagename', 'status', 'size']
+
+ def __init__(self, name=None, plugin=(None, None), packagename=None, status=None, size=None):
+ self.name = name
+ self.plugin = plugin
+ self.packagename = packagename
+ self.status = status
+ self.size = size
+
+class PackageData(BaseObject):
+ __slots__ = ['pid', 'name', 'folder', 'site', 'password', 'dest', 'order', 'linksdone', 'sizedone', 'sizetotal', 'linkstotal', 'links', 'fids']
+
+ def __init__(self, pid=None, name=None, folder=None, site=None, password=None, dest=None, order=None, linksdone=None, sizedone=None, sizetotal=None, linkstotal=None, links=None, fids=None):
+ self.pid = pid
+ self.name = name
+ self.folder = folder
+ self.site = site
+ self.password = password
+ self.dest = dest
+ self.order = order
+ self.linksdone = linksdone
+ self.sizedone = sizedone
+ self.sizetotal = sizetotal
+ self.linkstotal = linkstotal
+ self.links = links
+ self.fids = fids
+
+class PackageDoesNotExists(Exception):
+ __slots__ = ['pid']
+
+ def __init__(self, pid=None):
+ self.pid = pid
+
+class ServerStatus(BaseObject):
+ __slots__ = ['pause', 'active', 'queue', 'total', 'speed', 'download', 'reconnect']
+
+ def __init__(self, pause=None, active=None, queue=None, total=None, speed=None, download=None, reconnect=None):
+ self.pause = pause
+ self.active = active
+ self.queue = queue
+ self.total = total
+ self.speed = speed
+ self.download = download
+ self.reconnect = reconnect
+
+class ServiceCall(BaseObject):
+ __slots__ = ['plugin', 'func', 'arguments', 'parseArguments']
+
+ def __init__(self, plugin=None, func=None, arguments=None, parseArguments=None):
+ self.plugin = plugin
+ self.func = func
+ self.arguments = arguments
+ self.parseArguments = parseArguments
+
+class ServiceDoesNotExists(Exception):
+ __slots__ = ['plugin', 'func']
+
+ def __init__(self, plugin=None, func=None):
+ self.plugin = plugin
+ self.func = func
+
+class ServiceException(Exception):
+ __slots__ = ['msg']
+
+ def __init__(self, msg=None):
+ self.msg = msg
+
+class UserData(BaseObject):
+ __slots__ = ['name', 'email', 'role', 'permission', 'templateName']
+
+ def __init__(self, name=None, email=None, role=None, permission=None, templateName=None):
+ self.name = name
+ self.email = email
+ self.role = role
+ self.permission = permission
+ self.templateName = templateName
+
+class Iface(object):
+ def addFiles(self, pid, links):
+ pass
+ def addPackage(self, name, links, dest):
+ pass
+ def call(self, info):
+ pass
+ def checkOnlineStatus(self, urls):
+ pass
+ def checkOnlineStatusContainer(self, urls, filename, data):
+ pass
+ def checkURLs(self, urls):
+ pass
+ def deleteFiles(self, fids):
+ pass
+ def deleteFinished(self):
+ pass
+ def deletePackages(self, pids):
+ pass
+ def freeSpace(self):
+ pass
+ def generateAndAddPackages(self, links, dest):
+ pass
+ def generatePackages(self, links):
+ pass
+ def getAccountTypes(self):
+ pass
+ def getAccounts(self, refresh):
+ pass
+ def getAllInfo(self):
+ pass
+ def getAllUserData(self):
+ pass
+ def getCaptchaTask(self, exclusive):
+ pass
+ def getCaptchaTaskStatus(self, tid):
+ pass
+ def getCollector(self):
+ pass
+ def getCollectorData(self):
+ pass
+ def getConfig(self):
+ pass
+ def getConfigValue(self, category, option, section):
+ pass
+ def getEvents(self, uuid):
+ pass
+ def getFileData(self, fid):
+ pass
+ def getFileOrder(self, pid):
+ pass
+ def getInfoByPlugin(self, plugin):
+ pass
+ def getLog(self, offset):
+ pass
+ def getPackageData(self, pid):
+ pass
+ def getPackageInfo(self, pid):
+ pass
+ def getPackageOrder(self, destination):
+ pass
+ def getPluginConfig(self):
+ pass
+ def getQueue(self):
+ pass
+ def getQueueData(self):
+ pass
+ def getServerVersion(self):
+ pass
+ def getServices(self):
+ pass
+ def getUserData(self, username, password):
+ pass
+ def hasService(self, plugin, func):
+ pass
+ def isCaptchaWaiting(self):
+ pass
+ def isTimeDownload(self):
+ pass
+ def isTimeReconnect(self):
+ pass
+ def kill(self):
+ pass
+ def login(self, username, password):
+ pass
+ def moveFiles(self, fids, pid):
+ pass
+ def movePackage(self, destination, pid):
+ pass
+ def orderFile(self, fid, position):
+ pass
+ def orderPackage(self, pid, position):
+ pass
+ def parseURLs(self, html, url):
+ pass
+ def pauseServer(self):
+ pass
+ def pollResults(self, rid):
+ pass
+ def pullFromQueue(self, pid):
+ pass
+ def pushToQueue(self, pid):
+ pass
+ def recheckPackage(self, pid):
+ pass
+ def removeAccount(self, plugin, account):
+ pass
+ def restart(self):
+ pass
+ def restartFailed(self):
+ pass
+ def restartFile(self, fid):
+ pass
+ def restartPackage(self, pid):
+ pass
+ def setCaptchaResult(self, tid, result):
+ pass
+ def setConfigValue(self, category, option, value, section):
+ pass
+ def setPackageData(self, pid, data):
+ pass
+ def setPackageName(self, pid, name):
+ pass
+ def statusDownloads(self):
+ pass
+ def statusServer(self):
+ pass
+ def stopAllDownloads(self):
+ pass
+ def stopDownloads(self, fids):
+ pass
+ def togglePause(self):
+ pass
+ def toggleReconnect(self):
+ pass
+ def unpauseServer(self):
+ pass
+ def updateAccount(self, plugin, account, password, options):
+ pass
+ def uploadContainer(self, filename, data):
+ pass
diff --git a/module/cli/AddPackage.py b/module/cli/AddPackage.py
index a73401586..f7e30b64e 100644
--- a/module/cli/AddPackage.py
+++ b/module/cli/AddPackage.py
@@ -1,25 +1,9 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-#
-#Copyright (C) 2011 RaNaN
-#
-#This program is free software; you can redistribute it and/or modify
-#it under the terms of the GNU General Public License as published by
-#the Free Software Foundation; either version 3 of the License,
-#or (at your option) any later version.
-#
-#This program is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-#See the GNU General Public License for more details.
-#
-#You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
-#
-###
+# @author: RaNaN
+
+from pyload.cli.Handler import Handler
+from pyload.utils.printer import *
-from Handler import Handler
-from printer import *
class AddPackage(Handler):
""" let the user add packages """
@@ -63,4 +47,4 @@ class AddPackage(Handler):
println(line, "")
println(line + 1, mag("0.") + _(" back to main menu"))
- return line + 2 \ No newline at end of file
+ return line + 2
diff --git a/module/cli/Cli.py b/module/cli/Cli.py
new file mode 100644
index 000000000..d8c8602fc
--- /dev/null
+++ b/module/cli/Cli.py
@@ -0,0 +1,570 @@
+# -*- coding: utf-8 -*-
+# @author: RaNaN
+
+from __future__ import with_statement
+
+from getopt import GetoptError, getopt
+
+import pyload.utils.pylgettext as gettext
+import os
+from os import _exit
+from os.path import join, exists, abspath, basename
+import sys
+from sys import exit
+from threading import Thread, Lock
+from time import sleep
+from traceback import print_exc
+
+from pyload.config.Parser import ConfigParser
+
+from codecs import getwriter
+
+if os.name == "nt":
+ enc = "cp850"
+else:
+ enc = "utf8"
+
+sys.stdout = getwriter(enc)(sys.stdout, errors="replace")
+
+from pyload.cli.printer import *
+from pyload.cli import AddPackage, ManageFiles
+
+from pyload.api import Destination
+from pyload.utils import formatSize, decode
+from pyload.remote.thriftbackend.ThriftClient import ThriftClient, NoConnection, NoSSL, WrongLogin, ConnectionClosed
+from Getch import Getch
+from rename_process import renameProcess
+
+class Cli(object):
+ def __init__(self, client, command):
+ self.client = client
+ self.command = command
+
+ if not self.command:
+ renameProcess('pyload-cli')
+ self.getch = Getch()
+ self.input = ""
+ self.inputline = 0
+ self.lastLowestLine = 0
+ self.menuline = 0
+
+ self.lock = Lock()
+
+ #processor funcions, these will be changed dynamically depending on control flow
+ self.headerHandler = self #the download status
+ self.bodyHandler = self #the menu section
+ self.inputHandler = self
+
+ os.system("clear")
+ println(1, blue("py") + yellow("Load") + white(_(" Command Line Interface")))
+ println(2, "")
+
+ self.thread = RefreshThread(self)
+ self.thread.start()
+
+ self.start()
+ else:
+ self.processCommand()
+
+ def reset(self):
+ """ reset to initial main menu """
+ self.input = ""
+ self.headerHandler = self.bodyHandler = self.inputHandler = self
+
+ def start(self):
+ """ main loop. handle input """
+ while True:
+ #inp = raw_input()
+ inp = self.getch.impl()
+ if ord(inp) == 3:
+ os.system("clear")
+ sys.exit() # ctrl + c
+ elif ord(inp) == 13: #enter
+ try:
+ self.lock.acquire()
+ self.inputHandler.onEnter(self.input)
+
+ except Exception, e:
+ println(2, red(e))
+ finally:
+ self.lock.release()
+
+ elif ord(inp) == 127:
+ self.input = self.input[:-1] #backspace
+ try:
+ self.lock.acquire()
+ self.inputHandler.onBackSpace()
+ finally:
+ self.lock.release()
+
+ elif ord(inp) == 27: #ugly symbol
+ pass
+ else:
+ self.input += inp
+ try:
+ self.lock.acquire()
+ self.inputHandler.onChar(inp)
+ finally:
+ self.lock.release()
+
+ self.inputline = self.bodyHandler.renderBody(self.menuline)
+ self.renderFooter(self.inputline)
+
+
+ def refresh(self):
+ """refresh screen"""
+
+ println(1, blue("py") + yellow("Load") + white(_(" Command Line Interface")))
+ println(2, "")
+
+ self.lock.acquire()
+
+ self.menuline = self.headerHandler.renderHeader(3) + 1
+ println(self.menuline - 1, "")
+ self.inputline = self.bodyHandler.renderBody(self.menuline)
+ self.renderFooter(self.inputline)
+
+ self.lock.release()
+
+
+ def setInput(self, string=""):
+ self.input = string
+
+ def setHandler(self, klass):
+ #create new handler with reference to cli
+ self.bodyHandler = self.inputHandler = klass(self)
+ self.input = ""
+
+ def renderHeader(self, line):
+ """ prints download status """
+ #print updated information
+ # print "\033[J" #clear screen
+ # self.println(1, blue("py") + yellow("Load") + white(_(" Command Line Interface")))
+ # self.println(2, "")
+ # self.println(3, white(_("%s Downloads:") % (len(data))))
+
+ data = self.client.statusDownloads()
+ speed = 0
+
+ println(line, white(_("%s Downloads:") % (len(data))))
+ line += 1
+
+ for download in data:
+ if download.status == 12: # downloading
+ percent = download.percent
+ z = percent / 4
+ speed += download.speed
+ println(line, cyan(download.name))
+ line += 1
+ println(line,
+ blue("[") + yellow(z * "#" + (25 - z) * " ") + blue("] ") + green(str(percent) + "%") + _(
+ " Speed: ") + green(formatSize(download.speed) + "/s") + _(" Size: ") + green(
+ download.format_size) + _(" Finished in: ") + green(download.format_eta) + _(
+ " ID: ") + green(download.fid))
+ line += 1
+ if download.status == 5:
+ println(line, cyan(download.name))
+ line += 1
+ println(line, _("waiting: ") + green(download.format_wait))
+ line += 1
+
+ println(line, "")
+ line += 1
+ status = self.client.statusServer()
+ if status.pause:
+ paused = _("Status:") + " " + red(_("paused"))
+ else:
+ paused = _("Status:") + " " + red(_("running"))
+
+ println(line,"%s %s: %s %s: %s %s: %s" % (
+ paused, _("total Speed"), red(formatSize(speed) + "/s"), _("Files in queue"), red(
+ status.queue), _("Total"), red(status.total)))
+
+ return line + 1
+
+ def renderBody(self, line):
+ """ prints initial menu """
+ println(line, white(_("Menu:")))
+ println(line + 1, "")
+ println(line + 2, mag("1.") + _(" Add Links"))
+ println(line + 3, mag("2.") + _(" Manage Queue"))
+ println(line + 4, mag("3.") + _(" Manage Collector"))
+ println(line + 5, mag("4.") + _(" (Un)Pause Server"))
+ println(line + 6, mag("5.") + _(" Kill Server"))
+ println(line + 7, mag("6.") + _(" Quit"))
+
+ return line + 8
+
+ def renderFooter(self, line):
+ """ prints out the input line with input """
+ println(line, "")
+ line += 1
+
+ println(line, white(" Input: ") + decode(self.input))
+
+ #clear old output
+ if line < self.lastLowestLine:
+ for i in range(line + 1, self.lastLowestLine + 1):
+ println(i, "")
+
+ self.lastLowestLine = line
+
+ #set cursor to position
+ print "\033[" + str(self.inputline) + ";0H"
+
+ def onChar(self, char):
+ """ default no special handling for single chars """
+ if char == "1":
+ self.setHandler(AddPackage)
+ elif char == "2":
+ self.setHandler(ManageFiles)
+ elif char == "3":
+ self.setHandler(ManageFiles)
+ self.bodyHandler.target = Destination.Collector
+ elif char == "4":
+ self.client.togglePause()
+ self.setInput()
+ elif char == "5":
+ self.client.kill()
+ self.client.close()
+ sys.exit()
+ elif char == "6":
+ os.system('clear')
+ sys.exit()
+
+ def onEnter(self, inp):
+ pass
+
+ def onBackSpace(self):
+ pass
+
+ def processCommand(self):
+ command = self.command[0]
+ args = []
+ if len(self.command) > 1:
+ args = self.command[1:]
+
+ if command == "status":
+ files = self.client.statusDownloads()
+
+ if not files:
+ print "No downloads running."
+
+ for download in files:
+ if download.status == 12: # downloading
+ print print_status(download)
+ print "\tDownloading: %s @ %s/s\t %s (%s%%)" % (
+ download.format_eta, formatSize(download.speed), formatSize(download.size - download.bleft),
+ download.percent)
+ elif download.status == 5:
+ print print_status(download)
+ print "\tWaiting: %s" % download.format_wait
+ else:
+ print print_status(download)
+
+ elif command == "queue":
+ print_packages(self.client.getQueueData())
+
+ elif command == "collector":
+ print_packages(self.client.getCollectorData())
+
+ elif command == "add":
+ if len(args) < 2:
+ print _("Please use this syntax: add <Package name> <link> <link2> ...")
+ return
+
+ self.client.addPackage(args[0], args[1:], Destination.Queue)
+
+ elif command == "add_coll":
+ if len(args) < 2:
+ print _("Please use this syntax: add <Package name> <link> <link2> ...")
+ return
+
+ self.client.addPackage(args[0], args[1:], Destination.Collector)
+
+ elif command == "del_file":
+ self.client.deleteFiles([int(x) for x in args])
+ print "Files deleted."
+
+ elif command == "del_package":
+ self.client.deletePackages([int(x) for x in args])
+ print "Packages deleted."
+
+ elif command == "move":
+ for pid in args:
+ pack = self.client.getPackageInfo(int(pid))
+ self.client.movePackage((pack.dest + 1) % 2, pack.pid)
+
+ elif command == "check":
+ print _("Checking %d links:") % len(args)
+ print
+ rid = self.client.checkOnlineStatus(args).rid
+ self.printOnlineCheck(self.client, rid)
+
+
+ elif command == "check_container":
+ path = args[0]
+ if not exists(join(owd, path)):
+ print _("File does not exists.")
+ return
+
+ f = open(join(owd, path), "rb")
+ content = f.read()
+ f.close()
+
+ rid = self.client.checkOnlineStatusContainer([], basename(f.name), content).rid
+ self.printOnlineCheck(self.client, rid)
+
+
+ elif command == "pause":
+ self.client.pause()
+
+ elif command == "unpause":
+ self.client.unpause()
+
+ elif command == "toggle":
+ self.client.togglePause()
+
+ elif command == "kill":
+ self.client.kill()
+ elif command == "restart_file":
+ for x in args:
+ self.client.restartFile(int(x))
+ print "Files restarted."
+ elif command == "restart_package":
+ for pid in args:
+ self.client.restartPackage(int(pid))
+ print "Packages restarted."
+
+ else:
+ print_commands()
+
+ def printOnlineCheck(self, client, rid):
+ while True:
+ sleep(1)
+ result = client.pollResults(rid)
+ for url, status in result.data.iteritems():
+ if status.status == 2: check = "Online"
+ elif status.status == 1: check = "Offline"
+ else: check = "Unknown"
+
+ print "%-45s %-12s\t %-15s\t %s" % (status.name, formatSize(status.size), status.plugin, check)
+
+ if result.rid == -1: break
+
+
+class RefreshThread(Thread):
+ def __init__(self, cli):
+ Thread.__init__(self)
+ self.setDaemon(True)
+ self.cli = cli
+
+ def run(self):
+ while True:
+ sleep(1)
+ try:
+ self.cli.refresh()
+ except ConnectionClosed:
+ os.system("clear")
+ print _("pyLoad was terminated")
+ _exit(0)
+ except Exception, e:
+ println(2, red(str(e)))
+ self.cli.reset()
+ print_exc()
+
+
+def print_help(config):
+ print
+ print "pyLoad CLI Copyright (c) 2008-2015 the pyLoad Team"
+ print
+ print "Usage: [python] pyload-cli.py [options] [command]"
+ print
+ print "<Commands>"
+ print "See pyload-cli.py -c for a complete listing."
+ print
+ print "<Options>"
+ print " -i, --interactive", " Start in interactive mode"
+ print
+ print " -u, --username=", " " * 2, "Specify Username"
+ print " --pw=<password>", " " * 2, "Password"
+ print " -a, --address=", " " * 3, "Specify address (current=%s)" % config["addr"]
+ print " -p, --port", " " * 7, "Specify port (current=%s)" % config["port"]
+ print
+ print " -l, --language", " " * 3, "Set user interface language (current=%s)" % config["language"]
+ print " -h, --help", " " * 7, "Display this help screen"
+ print " -c, --commands", " " * 3, "List all available commands"
+ print
+
+
+def print_packages(data):
+ for pack in data:
+ print "Package %s (#%s):" % (pack.name, pack.pid)
+ for download in pack.links:
+ print "\t" + print_file(download)
+ print
+
+
+def print_file(download):
+ return "#%(id)-6d %(name)-30s %(statusmsg)-10s %(plugin)-8s" % {
+ "id": download.fid,
+ "name": download.name,
+ "statusmsg": download.statusmsg,
+ "plugin": download.plugin
+ }
+
+
+def print_status(download):
+ return "#%(id)-6s %(name)-40s Status: %(statusmsg)-10s Size: %(size)s" % {
+ "id": download.fid,
+ "name": download.name,
+ "statusmsg": download.statusmsg,
+ "size": download.format_size
+ }
+
+
+def print_commands():
+ commands = [("status", _("Prints server status")),
+ ("queue", _("Prints downloads in queue")),
+ ("collector", _("Prints downloads in collector")),
+ ("add <name> <link1> <link2>...", _("Adds package to queue")),
+ ("add_coll <name> <link1> <link2>...", _("Adds package to collector")),
+ ("del_file <fid> <fid2>...", _("Delete Files from Queue/Collector")),
+ ("del_package <pid> <pid2>...", _("Delete Packages from Queue/Collector")),
+ ("move <pid> <pid2>...", _("Move Packages from Queue to Collector or vice versa")),
+ ("restart_file <fid> <fid2>...", _("Restart files")),
+ ("restart_package <pid> <pid2>...", _("Restart packages")),
+ ("check <container|url> ...", _("Check online status, works with local container")),
+ ("check_container path", _("Checks online status of a container file")),
+ ("pause", _("Pause the server")),
+ ("unpause", _("continue downloads")),
+ ("toggle", _("Toggle pause/unpause")),
+ ("kill", _("kill server")), ]
+
+ print _("List of commands:")
+ print
+ for c in commands:
+ print "%-35s %s" % c
+
+
+def writeConfig(opts):
+ try:
+ with open(join(homedir, ".pyload-cli"), "w") as cfgfile:
+ cfgfile.write("[cli]")
+ for opt in opts:
+ cfgfile.write("%s=%s\n" % (opt, opts[opt]))
+ except Exception:
+ print _("Couldn't write user config file")
+
+
+def main():
+ config = {"addr": "127.0.0.1", "port": "7227", "language": "en"}
+ try:
+ config["language"] = os.environ["LANG"][0:2]
+ except Exception:
+ pass
+
+ if (not exists(join(pypath, "locale", config["language"]))) or config["language"] == "":
+ config["language"] = "en"
+
+ configFile = ConfigParser.ConfigParser()
+ configFile.read(join(homedir, ".pyload-cli"))
+
+ if configFile.has_section("cli"):
+ for opt in configFile.items("cli"):
+ config[opt[0]] = opt[1]
+
+ gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None])
+ translation = gettext.translation("Cli", join(pypath, "locale"),
+ languages=[config["language"], "en"], fallback=True)
+ translation.install(unicode=True)
+
+ interactive = False
+ command = None
+ username = ""
+ password = ""
+
+ shortOptions = 'iu:p:a:hcl:'
+ longOptions = ['interactive', "username=", "pw=", "address=", "port=", "help", "commands", "language="]
+
+ try:
+ opts, extraparams = getopt(sys.argv[1:], shortOptions, longOptions)
+ for option, params in opts:
+ if option in ("-i", "--interactive"):
+ interactive = True
+ elif option in ("-u", "--username"):
+ username = params
+ elif option in ("-a", "--address"):
+ config["addr"] = params
+ elif option in ("-p", "--port"):
+ config["port"] = params
+ elif option in ("-l", "--language"):
+ config["language"] = params
+ gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None])
+ translation = gettext.translation("Cli", join(pypath, "locale"),
+ languages=[config["language"], "en"], fallback=True)
+ translation.install(unicode=True)
+ elif option in ("-h", "--help"):
+ print_help(config)
+ exit()
+ elif option in ("--pw"):
+ password = params
+ elif option in ("-c", "--comands"):
+ print_commands()
+ exit()
+
+ except GetoptError:
+ print 'Unknown Argument(s) "%s"' % " ".join(sys.argv[1:])
+ print_help(config)
+ exit()
+
+ if len(extraparams) >= 1:
+ command = extraparams
+
+ client = False
+
+ if interactive:
+ try:
+ client = ThriftClient(config["addr"], int(config["port"]), username, password)
+ except WrongLogin:
+ pass
+ except NoSSL:
+ print _("You need py-openssl to connect to this pyLoad Core.")
+ exit()
+ except NoConnection:
+ config["addr"] = False
+ config["port"] = False
+
+ if not client:
+ if not config["addr"]: config["addr"] = raw_input(_("Address: "))
+ if not config["port"]: config["port"] = raw_input(_("Port: "))
+ if not username: username = raw_input(_("Username: "))
+ if not password:
+ from getpass import getpass
+
+ password = getpass(_("Password: "))
+
+ try:
+ client = ThriftClient(config["addr"], int(config["port"]), username, password)
+ except WrongLogin:
+ print _("Login data is wrong.")
+ except NoConnection:
+ print _("Could not establish connection to %(addr)s:%(port)s." % {"addr": config["addr"],
+ "port": config["port"]})
+
+ else:
+ try:
+ client = ThriftClient(config["addr"], int(config["port"]), username, password)
+ except WrongLogin:
+ print _("Login data is wrong.")
+ except NoConnection:
+ print _("Could not establish connection to %(addr)s:%(port)s." % {"addr": config["addr"],
+ "port": config["port"]})
+ except NoSSL:
+ print _("You need py-openssl to connect to this pyLoad core.")
+
+ if interactive and command: print _("Interactive mode ignored since you passed some commands.")
+
+ if client:
+ writeConfig(config)
+ cli = Cli(client, command)
diff --git a/module/cli/Handler.py b/module/cli/Handler.py
index 476d09386..4aab3f0e2 100644
--- a/module/cli/Handler.py
+++ b/module/cli/Handler.py
@@ -1,23 +1,7 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-#
-#Copyright (C) 2011 RaNaN
-#
-#This program is free software; you can redistribute it and/or modify
-#it under the terms of the GNU General Public License as published by
-#the Free Software Foundation; either version 3 of the License,
-#or (at your option) any later version.
-#
-#This program is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-#See the GNU General Public License for more details.
-#
-#You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
-#
-###
-class Handler:
+# @author: RaNaN
+
+class Handler(object):
def __init__(self, cli):
self.cli = cli
self.init()
@@ -45,4 +29,4 @@ class Handler:
def renderBody(self, line):
""" gets the line where to render output and should return the line number below its content """
- return line + 1 \ No newline at end of file
+ return line + 1
diff --git a/module/cli/ManageFiles.py b/module/cli/ManageFiles.py
index 4d0377d9d..ca1070113 100644
--- a/module/cli/ManageFiles.py
+++ b/module/cli/ManageFiles.py
@@ -1,40 +1,23 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-#
-#Copyright (C) 2011 RaNaN
-#
-#This program is free software; you can redistribute it and/or modify
-#it under the terms of the GNU General Public License as published by
-#the Free Software Foundation; either version 3 of the License,
-#or (at your option) any later version.
-#
-#This program is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-#See the GNU General Public License for more details.
-#
-#You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
-#
-###
+# @author: RaNaN
from itertools import islice
from time import time
-from Handler import Handler
-from printer import *
+from pyload.cli.Handler import Handler
+from pyload.utils.printer import *
+
+from pyload.api import Destination, PackageData
-from module.Api import Destination, PackageData
class ManageFiles(Handler):
""" possibility to manage queue/collector """
def init(self):
self.target = Destination.Queue
- self.pos = 0 #position in queue
+ self.pos = 0 #position in queue
self.package = -1 #choosen package
- self.mode = "" # move/delete/restart
-
+ self.mode = "" # move/delete/restart
self.cache = None
self.links = None
self.time = 0
@@ -82,7 +65,7 @@ class ManageFiles(Handler):
#look into package
try:
self.package = int(input)
- except:
+ except Exception:
pass
self.cache = None
@@ -91,7 +74,6 @@ class ManageFiles(Handler):
self.mode = ""
self.setInput()
-
def renderBody(self, line):
if self.package < 0:
println(line, white(_("Manage Packages:")))
@@ -107,12 +89,12 @@ class ManageFiles(Handler):
elif self.mode == "r":
println(line, _("What do you want to restart?"))
- println(line + 1, "Enter single number, comma seperated numbers or ranges. eg. 1,2,3 or 1-3.")
+ println(line + 1, "Enter single number, comma seperated numbers or ranges. eg. 1, 2, 3 or 1-3.")
line += 2
else:
println(line, _("Choose what yout want to do or enter package number."))
println(line + 1, ("%s - %%s, %s - %%s, %s - %%s" % (mag("d"), mag("m"), mag("r"))) % (
- _("delete"), _("move"), _("restart")))
+ _("delete"), _("move"), _("restart")))
line += 2
if self.package < 0:
@@ -136,7 +118,7 @@ class ManageFiles(Handler):
for value in islice(pack.links, self.pos, self.pos + 5):
try:
println(line, mag(value.fid) + ": %s | %s | %s" % (
- value.name, value.statusmsg, value.plugin))
+ value.name, value.statusmsg, value.plugin))
line += 1
i += 1
except Exception, e:
@@ -147,10 +129,8 @@ class ManageFiles(Handler):
println(line, mag("p") + _(" - previous") + " | " + mag("n") + _(" - next"))
println(line + 1, mag("0.") + _(" back to main menu"))
-
return line + 2
-
def getPackages(self):
if self.cache and self.time + 2 < time():
return self.cache
@@ -160,7 +140,6 @@ class ManageFiles(Handler):
else:
data = self.client.getCollector()
-
self.cache = data
self.time = time()
@@ -169,15 +148,12 @@ class ManageFiles(Handler):
def getLinks(self):
if self.links and self.time + 1 < time():
return self.links
-
try:
data = self.client.getPackageData(self.package)
- except:
+ except Exception:
data = PackageData(links=[])
-
self.links = data
self.time = time()
-
return data
def parseInput(self, inp, package=True):
@@ -197,8 +173,6 @@ class ManageFiles(Handler):
for l in self.links.links:
if l.lid in r:
ret.append(l.lid)
-
return ret
-
else:
return [int(x) for x in inp.split(",")]
diff --git a/module/cli/__init__.py b/module/cli/__init__.py
index fa8a09291..a64fc0c0c 100644
--- a/module/cli/__init__.py
+++ b/module/cli/__init__.py
@@ -1,2 +1,4 @@
-from AddPackage import AddPackage
-from ManageFiles import ManageFiles \ No newline at end of file
+# -*- coding: utf-8 -*-
+
+from pyload.cli.AddPackage import AddPackage
+from pyload.cli.ManageFiles import ManageFiles
diff --git a/module/cli/printer.py b/module/cli/printer.py
deleted file mode 100644
index c62c1800e..000000000
--- a/module/cli/printer.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-def blue(string):
- return "\033[1;34m" + unicode(string) + "\033[0m"
-
-def green(string):
- return "\033[1;32m" + unicode(string) + "\033[0m"
-
-def yellow(string):
- return "\033[1;33m" + unicode(string) + "\033[0m"
-
-def red(string):
- return "\033[1;31m" + unicode(string) + "\033[0m"
-
-def cyan(string):
- return "\033[1;36m" + unicode(string) + "\033[0m"
-
-def mag(string):
- return "\033[1;35m" + unicode(string) + "\033[0m"
-
-def white(string):
- return "\033[1;37m" + unicode(string) + "\033[0m"
-
-def println(line, content):
- print "\033[" + str(line) + ";0H\033[2K" + content \ No newline at end of file
diff --git a/module/common/APIExerciser.py b/module/common/APIExerciser.py
deleted file mode 100644
index 96f5ce9cf..000000000
--- a/module/common/APIExerciser.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import string
-from threading import Thread
-from random import choice, random, sample, randint
-from time import time, sleep
-from math import floor
-import gc
-
-from traceback import print_exc, format_exc
-
-from module.remote.thriftbackend.ThriftClient import ThriftClient, Destination
-
-def createURLs():
- """ create some urls, some may fail """
- urls = []
- for x in range(0, randint(20, 100)):
- name = "DEBUG_API"
- if randint(0, 5) == 5:
- name = "" #this link will fail
-
- urls.append(name + "".join(sample(string.ascii_letters, randint(10, 20))))
-
- return urls
-
-AVOID = (0,3,8)
-
-idPool = 0
-sumCalled = 0
-
-
-def startApiExerciser(core, n):
- for i in range(n):
- APIExerciser(core).start()
-
-class APIExerciser(Thread):
-
-
- def __init__(self, core, thrift=False, user=None, pw=None):
- global idPool
-
- Thread.__init__(self)
- self.setDaemon(True)
- self.core = core
- self.count = 0 #number of methods
- self.time = time()
-
- if thrift:
- self.api = ThriftClient(user=user, password=pw)
- else:
- self.api = core.api
-
-
- self.id = idPool
-
- idPool += 1
-
- #self.start()
-
- def run(self):
-
- self.core.log.info("API Excerciser started %d" % self.id)
-
- out = open("error.log", "ab")
- #core errors are not logged of course
- out.write("\n" + "Starting\n")
- out.flush()
-
- while True:
- try:
- self.testAPI()
- except Exception:
- self.core.log.error("Excerciser %d throw an execption" % self.id)
- print_exc()
- out.write(format_exc() + 2 * "\n")
- out.flush()
-
- if not self.count % 100:
- self.core.log.info("Exerciser %d tested %d api calls" % (self.id, self.count))
- if not self.count % 1000:
- out.flush()
-
- if not sumCalled % 1000: #not thread safe
- self.core.log.info("Exercisers tested %d api calls" % sumCalled)
- persec = sumCalled / (time() - self.time)
- self.core.log.info("Approx. %.2f calls per second." % persec)
- self.core.log.info("Approx. %.2f ms per call." % (1000 / persec))
- self.core.log.info("Collected garbage: %d" % gc.collect())
-
-
- #sleep(random() / 500)
-
- def testAPI(self):
- global sumCalled
-
- m = ["statusDownloads", "statusServer", "addPackage", "getPackageData", "getFileData", "deleteFiles",
- "deletePackages", "getQueue", "getCollector", "getQueueData", "getCollectorData", "isCaptchaWaiting",
- "getCaptchaTask", "stopAllDownloads", "getAllInfo", "getServices" , "getAccounts", "getAllUserData"]
-
- method = choice(m)
- #print "Testing:", method
-
- if hasattr(self, method):
- res = getattr(self, method)()
- else:
- res = getattr(self.api, method)()
-
- self.count += 1
- sumCalled += 1
-
- #print res
-
- def addPackage(self):
- name = "".join(sample(string.ascii_letters, 10))
- urls = createURLs()
-
- self.api.addPackage(name, urls, choice([Destination.Queue, Destination.Collector]))
-
-
- def deleteFiles(self):
- info = self.api.getQueueData()
- if not info: return
-
- pack = choice(info)
- fids = pack.links
-
- if len(fids):
- fids = [f.fid for f in sample(fids, randint(1, max(len(fids) / 2, 1)))]
- self.api.deleteFiles(fids)
-
-
- def deletePackages(self):
- info = choice([self.api.getQueue(), self.api.getCollector()])
- if not info: return
-
- pids = [p.pid for p in info]
- if len(pids):
- pids = sample(pids, randint(1, max(floor(len(pids) / 2.5), 1)))
- self.api.deletePackages(pids)
-
- def getFileData(self):
- info = self.api.getQueueData()
- if info:
- p = choice(info)
- if p.links:
- self.api.getFileData(choice(p.links).fid)
-
- def getPackageData(self):
- info = self.api.getQueue()
- if info:
- self.api.getPackageData(choice(info).pid)
-
- def getAccounts(self):
- self.api.getAccounts(False)
-
- def getCaptchaTask(self):
- self.api.getCaptchaTask(False) \ No newline at end of file
diff --git a/module/common/ImportDebugger.py b/module/common/ImportDebugger.py
deleted file mode 100644
index a997f7b0c..000000000
--- a/module/common/ImportDebugger.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import sys
-
-class ImportDebugger(object):
-
- def __init__(self):
- self.imported = {}
-
- def find_module(self, name, path=None):
-
- if name not in self.imported:
- self.imported[name] = 0
-
- self.imported[name] += 1
-
- print name, path
-
-sys.meta_path.append(ImportDebugger()) \ No newline at end of file
diff --git a/module/common/JsEngine.py b/module/common/JsEngine.py
deleted file mode 100644
index 576be2a1b..000000000
--- a/module/common/JsEngine.py
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
-
-from imp import find_module
-from os.path import join, exists
-from urllib import quote
-
-
-ENGINE = ""
-
-DEBUG = False
-JS = False
-PYV8 = False
-RHINO = False
-
-
-if not ENGINE:
- try:
- import subprocess
-
- subprocess.Popen(["js", "-v"], bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
- p = subprocess.Popen(["js", "-e", "print(23+19)"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- out, err = p.communicate()
- #integrity check
- if out.strip() == "42":
- ENGINE = "js"
- JS = True
- except:
- pass
-
-if not ENGINE or DEBUG:
- try:
- find_module("PyV8")
- ENGINE = "pyv8"
- PYV8 = True
- except:
- pass
-
-if not ENGINE or DEBUG:
- try:
- path = "" #path where to find rhino
-
- if exists("/usr/share/java/js.jar"):
- path = "/usr/share/java/js.jar"
- elif exists("js.jar"):
- path = "js.jar"
- elif exists(join(pypath, "js.jar")): #may raises an exception, but js.jar wasnt found anyway
- path = join(pypath, "js.jar")
-
- if not path:
- raise Exception
-
- import subprocess
-
- p = subprocess.Popen(["java", "-cp", path, "org.mozilla.javascript.tools.shell.Main", "-e", "print(23+19)"],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- out, err = p.communicate()
- #integrity check
- if out.strip() == "42":
- ENGINE = "rhino"
- RHINO = True
- except:
- pass
-
-class JsEngine():
- def __init__(self):
- self.engine = ENGINE
- self.init = False
-
- def __nonzero__(self):
- return False if not ENGINE else True
-
- def eval(self, script):
- if not self.init:
- if ENGINE == "pyv8" or (DEBUG and PYV8):
- import PyV8
- global PyV8
-
- self.init = True
-
- if type(script) == unicode:
- script = script.encode("utf8")
-
- if not ENGINE:
- raise Exception("No JS Engine")
-
- if not DEBUG:
- if ENGINE == "pyv8":
- return self.eval_pyv8(script)
- elif ENGINE == "js":
- return self.eval_js(script)
- elif ENGINE == "rhino":
- return self.eval_rhino(script)
- else:
- results = []
- if PYV8:
- res = self.eval_pyv8(script)
- print "PyV8:", res
- results.append(res)
- if JS:
- res = self.eval_js(script)
- print "JS:", res
- results.append(res)
- if RHINO:
- res = self.eval_rhino(script)
- print "Rhino:", res
- results.append(res)
-
- warning = False
- for x in results:
- for y in results:
- if x != y:
- warning = True
-
- if warning: print "### WARNING ###: Different results"
-
- return results[0]
-
- def eval_pyv8(self, script):
- rt = PyV8.JSContext()
- rt.enter()
- return rt.eval(script)
-
- def eval_js(self, script):
- script = "print(eval(unescape('%s')))" % quote(script)
- p = subprocess.Popen(["js", "-e", script], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=-1)
- out, err = p.communicate()
- res = out.strip()
- return res
-
- def eval_rhino(self, script):
- script = "print(eval(unescape('%s')))" % quote(script)
- p = subprocess.Popen(["java", "-cp", path, "org.mozilla.javascript.tools.shell.Main", "-e", script],
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=-1)
- out, err = p.communicate()
- res = out.strip()
- return res.decode("utf8").encode("ISO-8859-1")
-
- def error(self):
- return _("No js engine detected, please install either Spidermonkey, ossp-js, pyv8 or rhino")
-
-if __name__ == "__main__":
- js = JsEngine()
-
- test = u'"Ì"+"À"'
- js.eval(test) \ No newline at end of file
diff --git a/module/common/__init__.py b/module/common/__init__.py
deleted file mode 100644
index de6d13128..000000000
--- a/module/common/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-__author__ = 'christian'
- \ No newline at end of file
diff --git a/module/common/json_layer.py b/module/common/json_layer.py
deleted file mode 100644
index 4d57a9f38..000000000
--- a/module/common/json_layer.py
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# abstraction layer for json operations
-
-try: # since python 2.6
- import json
- from json import loads as json_loads
- from json import dumps as json_dumps
-except ImportError: #use system simplejson if available
- import simplejson as json
- from simplejson import loads as json_loads
- from simplejson import dumps as json_dumps
diff --git a/module/common/packagetools.py b/module/common/packagetools.py
deleted file mode 100644
index 5bfbcba95..000000000
--- a/module/common/packagetools.py
+++ /dev/null
@@ -1,155 +0,0 @@
-#!/usr/bin/env python
-
-# JDownloader/src/jd/controlling/LinkGrabberPackager.java
-
-import re
-from urlparse import urlparse
-
-def matchFirst(string, *args):
- """ matches against list of regexp and returns first match"""
- for patternlist in args:
- for pattern in patternlist:
- r = pattern.search(string)
- if r is not None:
- name = r.group(1)
- return name
-
- return string
-
-
-def parseNames(files):
- """ Generates packages names from name, data lists
-
- :param files: list of (name, data)
- :return: packagenames mapt to data lists (eg. urls)
- """
- packs = {}
-
- endings = "\\.(3gp|7zip|7z|abr|ac3|aiff|aifc|aif|ai|au|avi|bin|bz2|cbr|cbz|ccf|cue|cvd|chm|dta|deb|divx|djvu|dlc|dmg|doc|docx|dot|eps|exe|ff|flv|f4v|gsd|gif|gz|iwd|iso|ipsw|java|jar|jpg|jpeg|jdeatme|load|mws|mw|m4v|m4a|mkv|mp2|mp3|mp4|mov|movie|mpeg|mpe|mpg|msi|msu|msp|nfo|npk|oga|ogg|ogv|otrkey|pkg|png|pdf|pptx|ppt|pps|ppz|pot|psd|qt|rmvb|rm|rar|ram|ra|rev|rnd|r\\d+|rpm|run|rsdf|rtf|sh(!?tml)|srt|snd|sfv|swf|tar|tif|tiff|ts|txt|viv|vivo|vob|wav|wmv|xla|xls|xpi|zeno|zip|z\\d+|_[_a-z]{2}|\\d+$)"
-
- rarPats = [re.compile("(.*)(\\.|_|-)pa?r?t?\\.?[0-9]+.(rar|exe)$", re.I),
- re.compile("(.*)(\\.|_|-)part\\.?[0]*[1].(rar|exe)$", re.I),
- re.compile("(.*)\\.rar$", re.I),
- re.compile("(.*)\\.r\\d+$", re.I),
- re.compile("(.*)(\\.|_|-)\\d+$", re.I)]
-
- zipPats = [re.compile("(.*)\\.zip$", re.I),
- re.compile("(.*)\\.z\\d+$", re.I),
- re.compile("(?is).*\\.7z\\.[\\d]+$", re.I),
- re.compile("(.*)\\.a.$", re.I)]
-
- ffsjPats = [re.compile("(.*)\\._((_[a-z])|([a-z]{2}))(\\.|$)"),
- re.compile("(.*)(\\.|_|-)[\\d]+(" + endings + "$)", re.I)]
-
- iszPats = [re.compile("(.*)\\.isz$", re.I),
- re.compile("(.*)\\.i\\d{2}$", re.I)]
-
- pat1 = re.compile("(\\.?CD\\d+)", re.I)
- pat2 = re.compile("(\\.?part\\d+)", re.I)
-
- pat3 = re.compile("(.+)[\\.\\-_]+$")
- pat4 = re.compile("(.+)\\.\\d+\\.xtm$")
-
- for file, url in files:
- patternMatch = False
-
- if file is None:
- continue
-
- # remove trailing /
- name = file.rstrip('/')
-
- # extract last path part .. if there is a path
- split = name.rsplit("/", 1)
- if len(split) > 1:
- name = split.pop(1)
-
- #check if a already existing package may be ok for this file
- # found = False
- # for pack in packs:
- # if pack in file:
- # packs[pack].append(url)
- # found = True
- # break
- #
- # if found: continue
-
- # unrar pattern, 7zip/zip and hjmerge pattern, isz pattern, FFSJ pattern
- before = name
- name = matchFirst(name, rarPats, zipPats, iszPats, ffsjPats)
- if before != name:
- patternMatch = True
-
- # xtremsplit pattern
- r = pat4.search(name)
- if r is not None:
- name = r.group(1)
-
- # remove part and cd pattern
- r = pat1.search(name)
- if r is not None:
- name = name.replace(r.group(0), "")
- patternMatch = True
-
- r = pat2.search(name)
- if r is not None:
- name = name.replace(r.group(0), "")
- patternMatch = True
-
- # additional checks if extension pattern matched
- if patternMatch:
- # remove extension
- index = name.rfind(".")
- if index <= 0:
- index = name.rfind("_")
- if index > 0:
- length = len(name) - index
- if length <= 4:
- name = name[:-length]
-
- # remove endings like . _ -
- r = pat3.search(name)
- if r is not None:
- name = r.group(1)
-
- # replace . and _ with space
- name = name.replace(".", " ")
- name = name.replace("_", " ")
-
- name = name.strip()
- else:
- name = ""
-
- # fallback: package by hoster
- if not name:
- name = urlparse(file).hostname
- if name: name = name.replace("www.", "")
-
- # fallback : default name
- if not name:
- name = "unknown"
-
- # build mapping
- if name in packs:
- packs[name].append(url)
- else:
- packs[name] = [url]
-
- return packs
-
-
-if __name__ == "__main__":
- from os.path import join
- from pprint import pprint
-
- f = open(join("..", "..", "testlinks2.txt"), "rb")
- urls = [(x.strip(), x.strip()) for x in f.readlines() if x.strip()]
- f.close()
-
- print "Having %d urls." % len(urls)
-
- packs = parseNames(urls)
-
- pprint(packs)
-
- print "Got %d urls." % sum([len(x) for x in packs.itervalues()])
diff --git a/module/ConfigParser.py b/module/config/Parser.py
index 78b612f13..e21eaba9f 100644
--- a/module/ConfigParser.py
+++ b/module/config/Parser.py
@@ -1,63 +1,55 @@
# -*- coding: utf-8 -*-
from __future__ import with_statement
+
from time import sleep
from os.path import exists, join
from shutil import copy
from traceback import print_exc
-from utils import chmod
+from pyload.utils import chmod, encode, decode
-# ignore these plugin configs, mainly because plugins were wiped out
-IGNORE = (
- "FreakshareNet", "SpeedManager", "ArchiveTo", "ShareCx", ('hooks', 'UnRar'),
- 'EasyShareCom', 'FlyshareCz'
- )
CONF_VERSION = 1
-class ConfigParser:
+
+class ConfigParser(object):
"""
holds and manage the configuration
-
+
current dict layout:
-
+
{
-
- section : {
- option : {
+
+ section: {
+ option: {
value:
type:
desc:
}
- desc:
-
+ desc:
+
}
-
-
"""
-
def __init__(self):
"""Constructor"""
- self.config = {} # the config values
- self.plugin = {} # the config for plugins
+ self.config = {} #: the config values
+ self.plugin = {} #: the config for plugins
self.oldRemoteData = {}
- self.pluginCB = None # callback when plugin config value is changed
+ self.pluginCB = None #: callback when plugin config value is changed
self.checkVersion()
self.readConfig()
- self.deleteOldPlugins()
-
def checkVersion(self, n=0):
"""determines if config need to be copied"""
try:
if not exists("pyload.conf"):
- copy(join(pypath, "module", "config", "default.conf"), "pyload.conf")
+ copy(join(pypath, "pyload", "config", "default.conf"), "pyload.conf")
if not exists("plugin.conf"):
f = open("plugin.conf", "wb")
@@ -70,7 +62,7 @@ class ConfigParser:
v = v[v.find(":") + 1:].strip()
if not v or int(v) < CONF_VERSION:
- copy(join(pypath, "module", "config", "default.conf"), "pyload.conf")
+ copy(join(pypath, "pyload", "config", "default.conf"), "pyload.conf")
print "Old version of config was replaced"
f = open("plugin.conf", "rb")
@@ -83,17 +75,16 @@ class ConfigParser:
f.write("version: " + str(CONF_VERSION))
f.close()
print "Old version of plugin-config replaced"
- except:
- if n < 3:
- sleep(0.3)
- self.checkVersion(n + 1)
- else:
+ except Exception:
+ if n >= 3:
raise
+ sleep(0.3)
+ self.checkVersion(n + 1)
+
def readConfig(self):
"""reads the config file"""
-
- self.config = self.parseConfig(join(pypath, "module", "config", "default.conf"))
+ self.config = self.parseConfig(join(pypath, "pyload", "config", "default.conf"))
self.plugin = self.parseConfig("plugin.conf")
try:
@@ -105,8 +96,7 @@ class ConfigParser:
del homeconf["remote"]["password"]
del homeconf["remote"]["username"]
self.updateValues(homeconf, self.config)
-
- except Exception, e:
+ except Exception:
print "Config Warning"
print_exc()
@@ -129,7 +119,7 @@ class ConfigParser:
for line in config:
comment = line.rfind("#")
if line.find(":", comment) < 0 > line.find("=", comment) and comment > 0 and line[comment - 1].isspace():
- line = line.rpartition("#") # removes comments
+ line = line.rpartition("#") #: removes comments
if line[1]:
line = line[0]
else:
@@ -200,17 +190,19 @@ class ConfigParser:
for section in config.iterkeys():
if section in dest:
for option in config[section].iterkeys():
- if option in ("desc", "outline"): continue
+ if option in ("desc", "outline"):
+ continue
if option in dest[section]:
dest[section][option]["value"] = config[section][option]["value"]
- #else:
- # dest[section][option] = config[section][option]
+ # else:
+ # dest[section][option] = config[section][option]
+
+ # else:
+ # dest[section] = config[section]
- #else:
- # dest[section] = config[section]
def saveConfig(self, config, filename):
"""saves config to filename"""
@@ -229,40 +221,39 @@ class ConfigParser:
value += "\t\t" + str(x) + ",\n"
value += "\t\t]\n"
else:
- if type(data["value"]) in (str, unicode):
+ if isinstance(data["value"], basestring):
value = data["value"] + "\n"
else:
value = str(data["value"]) + "\n"
try:
f.write('\t%s %s : "%s" = %s' % (data["type"], option, data["desc"], value))
except UnicodeEncodeError:
- f.write('\t%s %s : "%s" = %s' % (data["type"], option, data["desc"], value.encode("utf8")))
+ f.write('\t%s %s : "%s" = %s' % (data["type"], option, data["desc"], encode(value)))
+
def cast(self, typ, value):
"""cast value to given format"""
- if type(value) not in (str, unicode):
+ if not isinstance(value, basestring):
return value
elif typ == "int":
return int(value)
elif typ == "bool":
- return True if value.lower() in ("1", "true", "on", "an", "yes") else False
+ return value.lower() in ("1", "true", "on", "an", "yes")
elif typ == "time":
- if not value: value = "0:00"
- if not ":" in value: value += ":00"
+ if not value:
+ value = "0:00"
+ if not ":" in value:
+ value += ":00"
return value
elif typ in ("str", "file", "folder"):
- try:
- return value.encode("utf8")
- except:
- return value
+ return encode(value)
else:
return value
def save(self):
"""saves the configs to disk"""
-
self.saveConfig(self.config, "pyload.conf")
self.saveConfig(self.plugin, "plugin.conf")
@@ -274,14 +265,9 @@ class ConfigParser:
def get(self, section, option):
"""get value"""
- val = self.config[section][option]["value"]
- try:
- if type(val) in (str, unicode):
- return val.decode("utf8")
- else:
- return val
- except:
- return val
+ value = self.config[section][option]["value"]
+ return decode(value)
+
def set(self, section, option, value):
"""set value"""
@@ -291,16 +277,12 @@ class ConfigParser:
self.config[section][option]["value"] = value
self.save()
+
def getPlugin(self, plugin, option):
"""gets a value for a plugin"""
- val = self.plugin[plugin][option]["value"]
- try:
- if type(val) in (str, unicode):
- return val.decode("utf8")
- else:
- return val
- except:
- return val
+ value = self.plugin[plugin][option]["value"]
+ return encode(value)
+
def setPlugin(self, plugin, option, value):
"""sets a value for a plugin"""
@@ -312,10 +294,12 @@ class ConfigParser:
self.plugin[plugin][option]["value"] = value
self.save()
+
def getMetaData(self, section, option):
""" get all config data for an option """
return self.config[section][option]
+
def addPluginConfig(self, name, config, outline=""):
"""adds config options with tuples (name, type, desc, default)"""
if name not in self.plugin:
@@ -338,26 +322,19 @@ class ConfigParser:
}
values = [x[0] for x in config] + ["desc", "outline"]
- #delete old values
+ # delete old values
for item in conf.keys():
if item not in values:
del conf[item]
+
def deleteConfig(self, name):
"""Removes a plugin config"""
if name in self.plugin:
del self.plugin[name]
- def deleteOldPlugins(self):
- """ remove old plugins from config """
-
- for name in IGNORE:
- if name in self.plugin:
- del self.plugin[name]
-
-
-class Section:
+class Section(object):
"""provides dictionary like access for configparser"""
def __init__(self, parser, section):
@@ -365,28 +342,12 @@ class Section:
self.parser = parser
self.section = section
+
def __getitem__(self, item):
"""getitem"""
return self.parser.get(self.section, item)
+
def __setitem__(self, item, value):
"""setitem"""
self.parser.set(self.section, item, value)
-
-
-if __name__ == "__main__":
- pypath = ""
-
- from time import time
-
- a = time()
-
- c = ConfigParser()
-
- b = time()
-
- print "sec", b - a
-
- print c.config
-
- c.saveConfig(c.config, "user.conf")
diff --git a/module/config/Setup.py b/module/config/Setup.py
new file mode 100644
index 000000000..8b88daa48
--- /dev/null
+++ b/module/config/Setup.py
@@ -0,0 +1,553 @@
+# -*- coding: utf-8 -*-
+# @author: vuolter
+
+from __future__ import with_statement
+
+import __builtin__
+
+import os
+import sys
+
+from getpass import getpass
+from os import chdir, makedirs, path
+from subprocess import PIPE, call
+
+from pyload.network.JsEngine import JsEngine
+from pyload.utils import get_console_encoding, load_translation, safe_join, versiontuple
+
+
+class SetupAssistant(object):
+ """ pyLoads initial setup configuration assistant """
+
+ def __init__(self, config):
+ self.config = config
+ self.lang = "en"
+ self.stdin_encoding = get_console_encoding(sys.stdin.encoding)
+
+
+ def start(self):
+ print
+ langs = sorted(self.config.getMetaData("general", "language")['type'].split(";"))
+ self.lang = self.ask(u"Choose setup language", "en", langs)
+
+ load_translation("setup", self.lang)
+
+ #Input shorthand for yes
+ self.yes = _("y")
+ #Input shorthand for no
+ self.no = _("n")
+
+ # print
+ # print _("Would you like to configure pyLoad via Webinterface?")
+ # print _("You need a Browser and a connection to this PC for it.")
+ # viaweb = self.ask(_("Start initial webinterface for configuration?"), "y", bool=True)
+ # ...
+
+ print
+ print
+ print _("## Welcome to the pyLoad Configuration Assistant ##")
+ print
+ print _("It will check your system and make a basic setup in order to run pyLoad.")
+ print
+ print _("The value in brackets [] always is the default value,")
+ print _("in case you don't want to change it or you are unsure what to choose, just hit enter.")
+ print _(
+ "Don't forget: You can always rerun this assistant with --setup or -s parameter, when you start pyload.py .")
+ print _("If you have any problems with this assistant hit STRG-C,")
+ print _("to abort and don't let him start with pyload.py automatically anymore.")
+ print
+ print
+ raw_input(_("When you are ready for system check, hit enter."))
+ print
+ print
+
+ basic, ssl, captcha, web, js = self.system_check()
+ print
+ print
+
+ if not basic:
+ print _("You need pycurl, sqlite and python 2.5, 2.6 or 2.7 to run pyLoad.")
+ print _("Please correct this and re-run pyLoad.")
+ print
+ print _("Setup will now close.")
+ print
+ print
+ raw_input(_("Press Enter to exit."))
+ return False
+
+ raw_input(_("System check finished, hit enter to see your status report."))
+ print
+ print
+ print _("## Status ##")
+ print
+
+ avail = []
+ if self.check_module("Crypto"):
+ avail.append(_("- container decrypting"))
+ if ssl:
+ avail.append(_("- ssl connection"))
+ if captcha:
+ avail.append(_("- automatic captcha decryption"))
+ if web:
+ avail.append(_("- webinterface"))
+ if js:
+ avail.append(_("- extended Click'N'Load"))
+
+ if avail:
+ print _("AVAILABLE FEATURES:")
+ for feature in avail:
+ print feature
+ print
+
+ if len(avail) < 5:
+ print _("MISSING FEATURES:")
+
+ if not self.check_module("Crypto"):
+ print _("- no py-crypto available")
+ print _("You need this if you want to decrypt container files.")
+ print
+
+ if not ssl:
+ print _("- no SSL available")
+ print _("This is needed if you want to establish a secure connection to core or webinterface.")
+ print _("If you only want to access locally to pyLoad ssl is not usefull.")
+ print
+
+ if not captcha:
+ print _("- no Captcha Recognition available")
+ print _("Only needed for some hosters and as freeuser.")
+ print
+
+ if not js:
+ print _("- no JavaScript engine found")
+ print _("You will need this for some Click'N'Load links. Install Spidermonkey, ossp-js, pyv8 or rhino")
+ print
+
+ print
+ print _("You can abort the setup now and fix some dependicies if you want.")
+ else:
+ print _("NO MISSING FEATURES!")
+
+ print
+ print
+ con = self.ask(_("Continue with setup?"), self.yes, bool=True)
+
+ if not con:
+ return False
+
+ print
+ print
+ print _("CURRENT CONFIG PATH: %s") % configdir
+ print
+ print _("NOTE: If you use pyLoad on a server or the home partition lives on an iternal flash it may be a good idea to change it.")
+ confpath = self.ask(_("Do you want to change the config path?"), self.no, bool=True)
+ if confpath:
+ print
+ self.conf_path()
+ print
+
+ print
+ print _("Do you want to configure login data and basic settings?")
+ print _("This is recommend for first run.")
+ con = self.ask(_("Make basic setup?"), self.yes, bool=True)
+
+ if con:
+ print
+ print
+ self.conf_basic()
+
+ if ssl:
+ print
+ print _("Do you want to configure ssl?")
+ ssl = self.ask(_("Configure ssl?"), self.no, bool=True)
+ if ssl:
+ print
+ print
+ self.conf_ssl()
+
+ if web:
+ print
+ print _("Do you want to configure webinterface?")
+ web = self.ask(_("Configure webinterface?"), self.yes, bool=True)
+ if web:
+ print
+ print
+ self.conf_web()
+
+ print
+ print
+ print _("Setup finished successfully!")
+ print
+ print
+ raw_input(_("Hit enter to exit and restart pyLoad."))
+ return True
+
+
+ def system_check(self):
+ """ make a systemcheck and return the results """
+ import platform
+
+ print _("## System Information ##")
+ print
+ print _("Platform: ") + platform.platform(aliased=True)
+ print _("OS: ") + platform.system() or "Unknown"
+ print _("Python: ") + sys.version.replace("\n", "")
+ print
+ print
+
+ print _("## System Check ##")
+ print
+
+ if (2, 5) > sys.version_info > (2, 7):
+ python = False
+ else:
+ python = True
+
+ self.print_dep("python", python, false="NOT OK")
+
+ curl = self.check_module("pycurl")
+ self.print_dep("pycurl", curl)
+
+ sqlite = self.check_module("sqlite3")
+ self.print_dep("sqlite3", sqlite)
+
+ basic = python and curl and sqlite
+
+ print
+
+ crypto = self.check_module("Crypto")
+ self.print_dep("pycrypto", crypto)
+
+ ssl = self.check_module("OpenSSL")
+ self.print_dep("py-OpenSSL", ssl)
+
+ print
+
+ pil = self.check_module("Image")
+ self.print_dep("py-imaging", pil)
+
+ if os.name == "nt":
+ tesser = self.check_prog([path.join(pypath, "tesseract", "tesseract.exe"), "-v"])
+ else:
+ tesser = self.check_prog(["tesseract", "-v"])
+
+ self.print_dep("tesseract", tesser)
+
+ captcha = pil and tesser
+
+ print
+
+ try:
+ import jinja2
+
+ v = jinja2.__version__
+ if v and versiontuple(v) < (2, 5, 0):
+ jinja = False
+ else:
+ jinja = True
+ except Exception:
+ jinja = False
+ jinja_error = "MISSING"
+ else:
+ jinja_error = "NOT OK"
+
+ self.print_dep("jinja2", jinja, false=jinja_error)
+
+ beaker = self.check_module("beaker")
+ self.print_dep("beaker", beaker)
+
+ bjoern = self.check_module("bjoern")
+ self.print_dep("bjoern", bjoern)
+
+ web = sqlite and beaker
+
+ js = True if JsEngine.find() else False
+ self.print_dep(_("JS engine"), js)
+
+ if not python:
+ print
+ print
+ if sys.version_info > (2, 7):
+ print _("WARNING: Your python version is too NEW!")
+ print _("Please use Python version 2.6/2.7 .")
+ else:
+ print _("WARNING: Your python version is too OLD!")
+ print _("Please use at least Python version 2.5 .")
+
+ if not jinja and jinja_error == "NOT OK":
+ print
+ print
+ print _("WARNING: Your installed jinja2 version %s is too OLD!") % jinja2.__version__
+ print _("You can safely continue but if the webinterface is not working,")
+ print _("please upgrade or uninstall it, because pyLoad self-includes jinja2 libary.")
+
+ return basic, ssl, captcha, web, js
+
+
+ def conf_basic(self):
+ print _("## Basic Setup ##")
+
+ print
+ print _("The following logindata is valid for CLI and webinterface.")
+
+ from pyload.database import DatabaseBackend
+
+ db = DatabaseBackend(None)
+ db.setup()
+ print _("NOTE: Consider a password of 10 or more symbols if you expect to access to your local network from outside (ex. internet).")
+ print
+ username = self.ask(_("Username"), "User")
+ password = self.ask("", "", password=True)
+ db.addUser(username, password)
+ db.shutdown()
+
+ print
+ print _("External clients (GUI, CLI or other) need remote access to work over the network.")
+ print _("However, if you only want to use the webinterface you may disable it to save ram.")
+ self.config.set("remote", "activated", self.ask(_("Enable remote access"), self.no, bool=True))
+
+ print
+ langs = sorted(self.config.getMetaData("general", "language")['type'].split(";"))
+ self.config.set("general", "language", self.ask(_("Choose system language"), self.lang, langs))
+
+ print
+ self.config.set("general", "download_folder", self.ask(_("Download folder"), "Downloads"))
+ print
+ self.config.set("download", "max_downloads", self.ask(_("Max parallel downloads"), "3"))
+ print
+ reconnect = self.ask(_("Use Reconnect?"), self.no, bool=True)
+ self.config.set("reconnect", "activated", reconnect)
+ if reconnect:
+ self.config.set("reconnect", "method", self.ask(_("Reconnect script location"), "./reconnect.sh"))
+
+
+ def conf_web(self):
+ print _("## Webinterface Setup ##")
+
+ print
+ print _("Listen address, if you use 127.0.0.1 or localhost, the webinterface will only accessible locally.")
+ self.config.set("webui", "host", self.ask(_("Address"), "0.0.0.0"))
+ self.config.set("webui", "port", self.ask(_("Port"), "8000"))
+ print
+ print _("pyLoad offers several server backends, now following a short explanation.")
+ print "- auto:", _("Automatically choose the best webserver for your platform.")
+ print "- builtin:", _("First choice if you plan to use pyLoad just for you.")
+ print "- threaded:", _("Support SSL connection and can serve simultaneously more client flawlessly.")
+ print "- fastcgi:", _(
+ "Can be used by apache, lighttpd, etc.; needs to be properly configured before.")
+ if os.name != "nt":
+ print "- lightweight:", _("Very fast alternative to builtin; requires libev and bjoern packages.")
+
+ print
+ print _("NOTE: In some rare cases the builtin server not works correctly, so if you have troubles with the web interface")
+ print _("run this setup assistant again and change the builtin server to the threaded.")
+
+ if os.name == "nt":
+ servers = ["auto", "builtin", "threaded", "fastcgi"]
+ else:
+ servers = ["auto", "builtin", "threaded", "fastcgi", "lightweight"]
+
+ self.config.set("webui", "server", self.ask(_("Choose webserver"), "auto", servers))
+
+
+ def conf_ssl(self):
+ print _("## SSL Setup ##")
+ print
+ print _("Execute these commands from pyLoad config folder to make ssl certificates:")
+ print
+ print "openssl genrsa -out ssl.key 1024"
+ print "openssl req -new -key ssl.key -out ssl.csr"
+ print "openssl req -days 36500 -x509 -key ssl.key -in ssl.csr > ssl.crt "
+ print
+ print _("If you're done and everything went fine, you can activate ssl now.")
+
+ ssl = self.ask(_("Activate SSL?"), self.yes, bool=True)
+ self.config.set("remote", "ssl", ssl)
+ self.config.set("webui", "ssl", ssl)
+
+
+ def set_user(self):
+ load_translation("setup", self.config.get("general", "language"))
+
+ from pyload.database import DatabaseBackend
+
+ db = DatabaseBackend(None)
+ db.setup()
+
+ noaction = True
+ try:
+ while True:
+ print _("Select action")
+ print _("1 - Create/Edit user")
+ print _("2 - List users")
+ print _("3 - Remove user")
+ print _("4 - Quit")
+ action = raw_input("[1]/2/3/4: ")
+ if not action in ("1", "2", "3", "4"):
+ continue
+ elif action == "1":
+ print
+ username = self.ask(_("Username"), "User")
+ password = self.ask("", "", password=True)
+ db.addUser(username, password)
+ noaction = False
+ elif action == "2":
+ print
+ print _("Users")
+ print "-----"
+ users = db.listUsers()
+ noaction = False
+ for user in users:
+ print user
+ print "-----"
+ print
+ elif action == "3":
+ print
+ username = self.ask(_("Username"), "")
+ if username:
+ db.removeUser(username)
+ noaction = False
+ elif action == "4":
+ break
+ finally:
+ if not noaction:
+ db.shutdown()
+
+
+ def set_configdir(self, configdir, persistent=False):
+ dirname = path.abspath(configdir)
+ try:
+ if not path.exists(dirname):
+ makedirs(dirname, 0700)
+
+ chdir(dirname)
+
+ if persistent:
+ c = path.join(rootdir, "config", "configdir")
+ if not path.exists(c):
+ makedirs(c, 0700)
+
+ with open(c, "wb") as f:
+ f.write(dirname)
+
+ except IOError:
+ return False
+
+ else:
+ __builtin__.configdir = dirname
+ return dirname #: return always abspath
+
+
+ def conf_path(self):
+ print _("Setting new config path.")
+ print _("NOTE: Current configuration will not be transfered!")
+
+ while True:
+ confdir = self.ask(_("CONFIG PATH"), configdir)
+ confpath = self.set_configdir(confdir)
+ print
+ if not confpath:
+ print _("Failed to change the current config path!")
+ print
+ else:
+ print _("pyLoad config path successfully changed.")
+ break
+
+
+ def print_dep(self, name, value, false="MISSING", true="OK"):
+ """ Print Status of dependency """
+ if value and isinstance(value, basestring):
+ msg = "%(dep)-12s %(bool)s (%(info)s)"
+ else:
+ msg = "%(dep)-12s %(bool)s"
+
+ print msg % {'dep': name + ':',
+ 'bool': _(true if value else false).upper(),
+ 'info': ", ".join(value)}
+
+
+ def check_module(self, module):
+ try:
+ __import__(module)
+ return True
+ except Exception:
+ return False
+
+
+ def check_prog(self, command):
+ pipe = PIPE
+ try:
+ call(command, stdout=pipe, stderr=pipe)
+ return True
+ except Exception:
+ return False
+
+
+ def ask(self, qst, default, answers=[], bool=False, password=False):
+ """ produce one line to asking for input """
+ if answers:
+ info = "("
+
+ for i, answer in enumerate(answers):
+ info += (", " if i != 0 else "") + str((answer == default and "[%s]" % answer) or answer)
+
+ info += ")"
+ elif bool:
+ if default == self.yes:
+ info = "([%s]/%s)" % (self.yes, self.no)
+ else:
+ info = "(%s/[%s])" % (self.yes, self.no)
+ else:
+ info = "[%s]" % default
+
+ if password:
+ p1 = True
+ p2 = False
+ pwlen = 8
+ while p1 != p2:
+ sys.stdout.write(_("Password: "))
+ p1 = getpass("")
+
+ if len(p1) < pwlen:
+ print _("Password too short! Use at least %s symbols." % pwlen)
+ continue
+ elif not p1.isalnum():
+ print _("Password must be alphanumeric.")
+ continue
+
+ sys.stdout.write(_("Password (again): "))
+ p2 = getpass("")
+
+ if p1 == p2:
+ return p1
+ else:
+ print _("Passwords did not match.")
+
+ while True:
+ try:
+ input = raw_input(qst + " %s: " % info)
+ except KeyboardInterrupt:
+ print "\nSetup interrupted"
+ sys.exit()
+
+ input = input.decode(self.stdin_encoding)
+
+ if input.strip() == "":
+ input = default
+
+ if bool:
+ # yes, true, t are inputs for booleans with value true
+ if input.lower().strip() in [self.yes, _("yes"), _("true"), _("t"), "yes"]:
+ return True
+ # no, false, f are inputs for booleans with value false
+ elif input.lower().strip() in [self.no, _("no"), _("false"), _("f"), "no"]:
+ return False
+ else:
+ print _("Invalid Input")
+ print
+ continue
+
+ if not answers or input in answers:
+ return input
+ else:
+ print _("Invalid Input")
diff --git a/module/config/__init__.py b/module/config/__init__.py
new file mode 100644
index 000000000..40a96afc6
--- /dev/null
+++ b/module/config/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/config/default.conf b/module/config/default.conf
index 2e9152ba2..6e2fb7749 100644
--- a/module/config/default.conf
+++ b/module/config/default.conf
@@ -1,43 +1,43 @@
version: 1
remote - "Remote":
- int port : "Port" = 7227
- ip listenaddr : "Adress" = 0.0.0.0
- bool nolocalauth : "No authentication on local connections" = True
- bool activated : "Activated" = True
+ int port : "Port" = 7227
+ ip listenaddr : "Address" = 0.0.0.0
+ bool nolocalauth : "No authentication on local connections" = True
+ bool activated : "Activated" = True
ssl - "SSL":
- bool activated : "Activated"= False
- file cert : "SSL Certificate" = ssl.crt
- file key : "SSL Key" = ssl.key
-webinterface - "Webinterface":
- bool activated : "Activated" = True
- builtin;threaded;fastcgi;lightweight server : "Server" = builtin
- bool https : "Use HTTPS" = False
- ip host : "IP" = 0.0.0.0
- int port : "Port" = 8001
- str template : "Template" = default
+ bool activated : "Activated"= False
+ file cert : "SSL Certificate" = ssl.crt
+ file key : "SSL Key" = ssl.key
+webinterface - "Web UI":
+ bool activated : "Activated" = True
+ builtin;threaded;fastcgi;lightweight server : "Server" = builtin
+ bool https : "Use HTTPS" = False
+ ip host : "IP" = 0.0.0.0
+ int port : "Port" = 8001
+ Default;Dark;Flat theme : "Theme" = Default
str prefix: "Path Prefix" =
log - "Log":
- bool file_log : "File Log" = True
- folder log_folder : "Folder" = Logs
- int log_count : "Count" = 5
- int log_size : "Size in kb" = 100
- bool log_rotate : "Log Rotate" = True
+ bool file_log : "File Log" = True
+ folder log_folder : "Folder" = Logs
+ int log_count : "Count" = 5
+ int log_size : "Size in kb" = 100
+ bool log_rotate : "Log Rotate" = True
general - "General":
- en;de;fr;it;es;nl;sv;ru;pl;cs;sr;pt_BR language : "Language" = en
- folder download_folder : "Download Folder" = Downloads
- bool debug_mode : "Debug Mode" = False
- bool checksum : "Use Checksum" = False
- int min_free_space : "Min Free Space (MB)" = 200
- bool folder_per_package : "Create folder for each package" = True
- int renice : "CPU Priority" = 0
+ en;de;fr;it;es;nl;sv;ru;pl;cs;sr;pt_BR language : "Language" = en
+ folder download_folder : "Download Folder" = Downloads
+ bool debug_mode : "Debug Mode" = False
+ int min_free_space : "Min Free Space (MB)" = 200
+ bool folder_per_package : "Create folder for each package" = True
+ int renice : "CPU Priority" = 0
+ auto;common;pyv8;node;rhino;jsc jsengine : "JS Engine" = auto
download - "Download":
int chunks : "Max connections for one download" = 3
int max_downloads : "Max Parallel Downloads" = 3
int max_speed : "Max Download Speed in kb/s" = -1
bool limit_speed : "Limit Download Speed" = False
str interface : "Download interface to bind (ip or Name)" = None
- bool ipv6 : "Allow IPv6" = False
+ bool ipv6: "Allow IPv6" = False
bool skip_existing : "Skip already existing files" = False
permission - "Permissions":
bool change_user : "Change user of running process" = False
@@ -49,17 +49,17 @@ permission - "Permissions":
str group : "Groupname" = users
bool change_dl : "Change Group and User of Downloads" = False
reconnect - "Reconnect":
- bool activated : "Use Reconnect" = False
- str method : "Method" = None
- time startTime : "Start" = 0:00
- time endTime : "End" = 0:00
+ bool activated : "Use Reconnect" = False
+ str method : "Method" = None
+ time startTime : "Start" = 0:00
+ time endTime : "End" = 0:00
downloadTime - "Download Time":
- time start : "Start" = 0:00
- time end : "End" = 0:00
+ time start : "Start" = 0:00
+ time end : "End" = 0:00
proxy - "Proxy":
- str address : "Address" = "localhost"
- int port : "Port" = 7070
- http;socks4;socks5 type : "Protocol" = http
- str username : "Username" = None
- password password : "Password" = None
+ str address : "Address" = "localhost"
+ int port : "Port" = 7070
+ http;socks4;socks5 type : "Protocol" = http
+ str username : "Username" = None
+ password password : "Password" = None
bool proxy : "Use Proxy" = False
diff --git a/module/config/gui_default.xml b/module/config/gui_default.xml
deleted file mode 100644
index 1faed776f..000000000
--- a/module/config/gui_default.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" ?>
-<root>
- <connections>
- <connection default="True" type="local" id="33965310e19b4a869112c43b39a16440">
- <name>Local</name>
- </connection>
- </connections>
- <mainWindow>
- <state></state>
- <geometry></geometry>
- </mainWindow>
- <language>en</language>
-</root>
diff --git a/module/database/DatabaseBackend.py b/module/database/DatabaseBackend.py
index 9530390c3..4b63dd284 100644
--- a/module/database/DatabaseBackend.py
+++ b/module/database/DatabaseBackend.py
@@ -1,23 +1,7 @@
-#!/usr/bin/env python
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
- @author: mkaay
-"""
-from threading import Thread
-from threading import Event
+# -*- coding: utf-8 -*-
+# @author: RaNaN, mkaay
+
+from threading import Event, Thread
from os import remove
from os.path import exists
from shutil import move
@@ -25,22 +9,22 @@ from shutil import move
from Queue import Queue
from traceback import print_exc
-from module.utils import chmod
+from pyload.utils import chmod
try:
from pysqlite2 import dbapi2 as sqlite3
-except:
+except Exception:
import sqlite3
DB_VERSION = 4
-class style():
+class style(object):
db = None
-
+
@classmethod
def setDB(cls, db):
cls.db = db
-
+
@classmethod
def inner(cls, f):
@staticmethod
@@ -48,7 +32,7 @@ class style():
if cls.db:
return f(cls.db, *args, **kwargs)
return x
-
+
@classmethod
def queue(cls, f):
@staticmethod
@@ -56,7 +40,7 @@ class style():
if cls.db:
return cls.db.queue(f, *args, **kwargs)
return x
-
+
@classmethod
def async(cls, f):
@staticmethod
@@ -65,14 +49,14 @@ class style():
return cls.db.async(f, *args, **kwargs)
return x
-class DatabaseJob():
+class DatabaseJob(object):
def __init__(self, f, *args, **kwargs):
self.done = Event()
-
+
self.f = f
self.args = args
self.kwargs = kwargs
-
+
self.result = None
self.exception = False
@@ -98,13 +82,13 @@ class DatabaseJob():
print_exc()
try:
print "Database Error @", self.f.__name__, self.args[1:], self.kwargs, e
- except:
+ except Exception:
pass
self.exception = e
finally:
self.done.set()
-
+
def wait(self):
self.done.wait()
@@ -116,34 +100,34 @@ class DatabaseBackend(Thread):
self.core = core
self.jobs = Queue()
-
+
self.setuplock = Event()
-
+
style.setDB(self)
-
+
def setup(self):
self.start()
self.setuplock.wait()
-
+
def run(self):
"""main loop, which executes commands"""
convert = self._checkVersion() #returns None or current version
-
+
self.conn = sqlite3.connect("files.db")
chmod("files.db", 0600)
self.c = self.conn.cursor() #compatibility
-
+
if convert is not None:
self._convertDB(convert)
-
+
self._createTables()
self._migrateUser()
self.conn.commit()
-
+
self.setuplock.set()
-
+
while True:
j = self.jobs.get()
if j == "quit":
@@ -164,7 +148,7 @@ class DatabaseBackend(Thread):
f.write(str(DB_VERSION))
f.close()
return
-
+
f = open("files.version", "rb")
v = int(f.read().strip())
f.close()
@@ -172,7 +156,7 @@ class DatabaseBackend(Thread):
if v < 2:
try:
self.manager.core.log.warning(_("Filedatabase was deleted due to incompatible version."))
- except:
+ except Exception:
print "Filedatabase was deleted due to incompatible version."
remove("files.version")
move("files.db", "files.backup.db")
@@ -180,35 +164,35 @@ class DatabaseBackend(Thread):
f.write(str(DB_VERSION))
f.close()
return v
-
+
def _convertDB(self, v):
try:
getattr(self, "_convertV%i" % v)()
- except:
+ except Exception:
try:
self.core.log.error(_("Filedatabase could NOT be converted."))
- except:
+ except Exception:
print "Filedatabase could NOT be converted."
-
- #--convert scripts start
-
+
+ #convert scripts start-----------------------------------------------------
+
def _convertV2(self):
self.c.execute('CREATE TABLE IF NOT EXISTS "storage" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "identifier" TEXT NOT NULL, "key" TEXT NOT NULL, "value" TEXT DEFAULT "")')
try:
self.manager.core.log.info(_("Database was converted from v2 to v3."))
- except:
+ except Exception:
print "Database was converted from v2 to v3."
self._convertV3()
-
+
def _convertV3(self):
self.c.execute('CREATE TABLE IF NOT EXISTS "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "name" TEXT NOT NULL, "email" TEXT DEFAULT "" NOT NULL, "password" TEXT NOT NULL, "role" INTEGER DEFAULT 0 NOT NULL, "permission" INTEGER DEFAULT 0 NOT NULL, "template" TEXT DEFAULT "default" NOT NULL)')
try:
self.manager.core.log.info(_("Database was converted from v3 to v4."))
- except:
+ except Exception:
print "Database was converted from v3 to v4."
-
- #--convert scripts end
-
+
+ #convert scripts end-------------------------------------------------------
+
def _createTables(self):
"""create tables for database"""
@@ -222,7 +206,7 @@ class DatabaseBackend(Thread):
SELECT p.id AS id, SUM(l.size) AS sizetotal, COUNT(l.id) AS linkstotal, linksdone, sizedone\
FROM packages p JOIN links l ON p.id = l.package LEFT OUTER JOIN\
(SELECT p.id AS id, COUNT(*) AS linksdone, SUM(l.size) AS sizedone \
- FROM packages p JOIN links l ON p.id = l.package AND l.status in (0,4,13) GROUP BY p.id) s ON s.id = p.id \
+ FROM packages p JOIN links l ON p.id = l.package AND l.status in (0, 4, 13) GROUP BY p.id) s ON s.id = p.id \
GROUP BY p.id')
#try to lower ids
@@ -250,7 +234,7 @@ class DatabaseBackend(Thread):
if exists("pyload.db"):
try:
self.core.log.info(_("Converting old Django DB"))
- except:
+ except Exception:
print "Converting old Django DB"
conn = sqlite3.connect('pyload.db')
c = conn.cursor()
@@ -264,10 +248,10 @@ class DatabaseBackend(Thread):
self.c.executemany("INSERT INTO users(name, password, email) VALUES (?, ?, ?)", users)
move("pyload.db", "pyload.old.db")
-
+
def createCursor(self):
return self.conn.cursor()
-
+
@style.async
def commit(self):
self.conn.commit()
@@ -275,78 +259,32 @@ class DatabaseBackend(Thread):
@style.queue
def syncSave(self):
self.conn.commit()
-
+
@style.async
def rollback(self):
self.conn.rollback()
-
+
def async(self, f, *args, **kwargs):
- args = (self, ) + args
+ args = (self,) + args
job = DatabaseJob(f, *args, **kwargs)
self.jobs.put(job)
-
+
def queue(self, f, *args, **kwargs):
- args = (self, ) + args
+ args = (self,) + args
job = DatabaseJob(f, *args, **kwargs)
self.jobs.put(job)
job.wait()
return job.result
-
+
@classmethod
def registerSub(cls, klass):
cls.subs.append(klass)
-
+
@classmethod
def unregisterSub(cls, klass):
cls.subs.remove(klass)
-
+
def __getattr__(self, attr):
for sub in DatabaseBackend.subs:
if hasattr(sub, attr):
return getattr(sub, attr)
-
-if __name__ == "__main__":
- db = DatabaseBackend()
- db.setup()
-
- class Test():
- @style.queue
- def insert(db):
- c = db.createCursor()
- for i in range(1000):
- c.execute("INSERT INTO storage (identifier, key, value) VALUES (?, ?, ?)", ("foo", i, "bar"))
- @style.async
- def insert2(db):
- c = db.createCursor()
- for i in range(1000*1000):
- c.execute("INSERT INTO storage (identifier, key, value) VALUES (?, ?, ?)", ("foo", i, "bar"))
-
- @style.queue
- def select(db):
- c = db.createCursor()
- for i in range(10):
- res = c.execute("SELECT value FROM storage WHERE identifier=? AND key=?", ("foo", i))
- print res.fetchone()
-
- @style.queue
- def error(db):
- c = db.createCursor()
- print "a"
- c.execute("SELECT myerror FROM storage WHERE identifier=? AND key=?", ("foo", i))
- print "e"
-
- db.registerSub(Test)
- from time import time
- start = time()
- for i in range(100):
- db.insert()
- end = time()
- print end-start
-
- start = time()
- db.insert2()
- end = time()
- print end-start
-
- db.error()
-
diff --git a/module/database/FileDatabase.py b/module/database/File.py
index 7e7efb028..2b7c6cad9 100644
--- a/module/database/FileDatabase.py
+++ b/module/database/File.py
@@ -1,39 +1,22 @@
-#!/usr/bin/env python
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
- @author: mkaay
-"""
-
+# -*- coding: utf-8 -*-
+# @author: RaNaN, mkaay
from threading import RLock
from time import time
-from module.utils import formatSize, lock
-from module.PullEvents import InsertEvent, ReloadAllEvent, RemoveEvent, UpdateEvent
-from module.PyPackage import PyPackage
-from module.PyFile import PyFile
-from module.database import style, DatabaseBackend
+from pyload.utils import formatSize, lock
+from pyload.manager.Event import InsertEvent, ReloadAllEvent, RemoveEvent, UpdateEvent
+from pyload.datatype.Package import PyPackage
+from pyload.datatype.File import PyFile
+from pyload.database import style, DatabaseBackend
try:
from pysqlite2 import dbapi2 as sqlite3
-except:
+except Exception:
import sqlite3
-class FileHandler:
+class FileHandler(object):
"""Handles all request made to obtain information,
modify status or other request for links or packages"""
@@ -68,12 +51,12 @@ class FileHandler:
return func(*args)
return new
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def save(self):
"""saves all data to backend"""
self.db.commit()
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def syncSave(self):
"""saves all data to backend and waits until all data are written"""
pyfiles = self.cache.values()
@@ -121,7 +104,7 @@ class FileHandler:
def addLinks(self, urls, package):
"""adds links"""
- self.core.hookManager.dispatchEvent("linksAdded", urls, package)
+ self.core.addonManager.dispatchEvent("links-added", urls, package)
data = self.core.pluginManager.parseUrls(urls)
@@ -131,7 +114,7 @@ class FileHandler:
#@TODO change from reloadAll event to package update event
self.core.pullManager.addEvent(ReloadAllEvent("collector"))
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
@lock
@change
def addPackage(self, name, folder, queue=0):
@@ -142,7 +125,7 @@ class FileHandler:
self.core.pullManager.addEvent(e)
return lastID
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
@lock
@change
def deletePackage(self, id):
@@ -167,7 +150,7 @@ class FileHandler:
self.db.deletePackage(p)
self.core.pullManager.addEvent(e)
- self.core.hookManager.dispatchEvent("packageDeleted", id)
+ self.core.addonManager.dispatchEvent("package-deleted", id)
if id in self.packageCache:
del self.packageCache[id]
@@ -178,7 +161,7 @@ class FileHandler:
pack.order -= 1
pack.notifyChange()
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
@lock
@change
def deleteLink(self, id):
@@ -190,7 +173,7 @@ class FileHandler:
pid = f.packageid
e = RemoveEvent("file", id, "collector" if not f.package().queue else "queue")
-
+
oldorder = f.order
if id in self.core.threadManager.processingIds():
@@ -206,26 +189,26 @@ class FileHandler:
p = self.getPackage(pid)
if not len(p.getChildren()):
p.delete()
-
+
pyfiles = self.cache.values()
for pyfile in pyfiles:
if pyfile.packageid == pid and pyfile.order > oldorder:
pyfile.order -= 1
pyfile.notifyChange()
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def releaseLink(self, id):
"""removes pyfile from cache"""
if id in self.cache:
del self.cache[id]
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def releasePackage(self, id):
"""removes package from cache"""
if id in self.packageCache:
del self.packageCache[id]
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def updateLink(self, pyfile):
"""updates link"""
self.db.updateLink(pyfile)
@@ -233,7 +216,7 @@ class FileHandler:
e = UpdateEvent("file", pyfile.id, "collector" if not pyfile.package().queue else "queue")
self.core.pullManager.addEvent(e)
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def updatePackage(self, pypack):
"""updates a package"""
self.db.updatePackage(pypack)
@@ -241,7 +224,7 @@ class FileHandler:
e = UpdateEvent("pack", pypack.id, "collector" if not pypack.queue else "queue")
self.core.pullManager.addEvent(e)
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def getPackage(self, id):
"""return package instance"""
@@ -250,7 +233,7 @@ class FileHandler:
else:
return self.db.getPackage(id)
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def getPackageData(self, id):
"""returns dict with package information"""
pack = self.getPackage(id)
@@ -274,7 +257,7 @@ class FileHandler:
return pack
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def getFileData(self, id):
"""returns dict with file information"""
if id in self.cache:
@@ -282,7 +265,7 @@ class FileHandler:
return self.db.getLinkData(id)
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def getFile(self, id):
"""returns pyfile instance"""
if id in self.cache:
@@ -290,7 +273,7 @@ class FileHandler:
else:
return self.db.getFile(id)
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
@lock
def getJob(self, occ):
"""get suitable job"""
@@ -369,7 +352,7 @@ class FileHandler:
"""checks if all files are finished and dispatch event"""
if not self.getQueueCount(True):
- self.core.hookManager.dispatchEvent("allDownloadsFinished")
+ self.core.addonManager.dispatchEvent("all_downloads-finished")
self.core.log.debug("All downloads finished")
return True
@@ -382,7 +365,7 @@ class FileHandler:
self.resetCount()
if not self.db.processcount(1, fid):
- self.core.hookManager.dispatchEvent("allDownloadsProcessed")
+ self.core.addonManager.dispatchEvent("all_downloads-processed")
self.core.log.debug("All downloads processed")
return True
@@ -434,7 +417,7 @@ class FileHandler:
e = RemoveEvent("pack", id, "collector" if not p.queue else "queue")
self.core.pullManager.addEvent(e)
-
+
self.db.clearPackageOrder(p)
p = self.db.getPackage(id)
@@ -443,7 +426,7 @@ class FileHandler:
self.db.updatePackage(p)
self.db.reorderPackage(p, -1, True)
-
+
packs = self.packageCache.values()
for pack in packs:
if pack.queue != queue and pack.order > oldorder:
@@ -453,7 +436,7 @@ class FileHandler:
self.db.commit()
self.releasePackage(id)
p = self.getPackage(id)
-
+
e = InsertEvent("pack", id, p.order, "collector" if not p.queue else "queue")
self.core.pullManager.addEvent(e)
@@ -523,13 +506,13 @@ class FileHandler:
self.core.pullManager.addEvent(e)
def checkPackageFinished(self, pyfile):
- """ checks if package is finished and calls hookmanager """
+ """ checks if package is finished and calls AddonManager """
ids = self.db.getUnfinished(pyfile.packageid)
if not ids or (pyfile.id in ids and len(ids) == 1):
if not pyfile.package().setFinished:
self.core.log.info(_("Package finished: %s") % pyfile.package().name)
- self.core.hookManager.packageFinished(pyfile.package())
+ self.core.addonManager.packageFinished(pyfile.package())
pyfile.package().setFinished = True
@@ -540,7 +523,7 @@ class FileHandler:
urls = []
for pyfile in data.itervalues():
- if pyfile["status"] not in (0, 12, 13):
+ if pyfile["status"] not in (0, 12, 13):
urls.append((pyfile["url"], pyfile["plugin"]))
self.core.threadManager.createInfoThread(urls, pid)
@@ -573,23 +556,23 @@ class FileHandler:
""" restart all failed links """
self.db.restartFailed()
-class FileMethods():
+class FileMethods(object):
@style.queue
def filecount(self, queue):
"""returns number of files in queue"""
- self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=?", (queue, ))
+ self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=?", (queue,))
return self.c.fetchone()[0]
@style.queue
def queuecount(self, queue):
""" number of files in queue not finished yet"""
- self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=? AND l.status NOT IN (0,4)", (queue, ))
+ self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=? AND l.status NOT IN (0, 4)", (queue,))
return self.c.fetchone()[0]
@style.queue
def processcount(self, queue, fid):
""" number of files which have to be proccessed """
- self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=? AND l.status IN (2,3,5,7,12) AND l.id != ?", (queue, str(fid)))
+ self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=? AND l.status IN (2, 3, 5, 7, 12) AND l.id != ?", (queue, str(fid)))
return self.c.fetchone()[0]
@style.inner
@@ -600,7 +583,7 @@ class FileMethods():
return max + 1
else:
return 0
-
+
@style.inner
def _nextFileOrder(self, package):
self.c.execute('SELECT MAX(linkorder) FROM links WHERE package=?', (package,))
@@ -609,19 +592,19 @@ class FileMethods():
return max + 1
else:
return 0
-
+
@style.queue
def addLink(self, url, name, plugin, package):
order = self._nextFileOrder(package)
- self.c.execute('INSERT INTO links(url, name, plugin, package, linkorder) VALUES(?,?,?,?,?)', (url, name, plugin, package, order))
+ self.c.execute('INSERT INTO links(url, name, plugin, package, linkorder) VALUES(?,?,?,?,?)', (url, name, (plugintype, pluginname), package, order))
return self.c.lastrowid
@style.queue
def addLinks(self, links, package):
- """ links is a list of tupels (url,plugin)"""
+ """ links is a list of tupels (url, plugin)"""
order = self._nextFileOrder(package)
orders = [order + x for x in range(len(links))]
- links = [(x[0], x[0], x[1], package, o) for x, o in zip(links, orders)]
+ links = [(x[0], x[0], (x[1], x[2]), package, o) for x, o in zip(links, orders)]
self.c.executemany('INSERT INTO links(url, name, plugin, package, linkorder) VALUES(?,?,?,?,?)', links)
@style.queue
@@ -658,7 +641,7 @@ class FileMethods():
}
"""
- self.c.execute('SELECT l.id,l.url,l.name,l.size,l.status,l.error,l.plugin,l.package,l.linkorder FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=? ORDER BY l.linkorder', (q,))
+ self.c.execute('SELECT l.id, l.url, l.name, l.size, l.status, l.error, l.plugin, l.package, l.linkorder FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=? ORDER BY l.linkorder', (q,))
data = {}
for r in self.c:
data[r[0]] = {
@@ -688,7 +671,7 @@ class FileMethods():
format:
{
- id: {'name': name ... 'links': {} }, ...
+ id: {'name': name ... 'links': {}}, ...
}
"""
self.c.execute('SELECT p.id, p.name, p.folder, p.site, p.password, p.queue, p.packageorder, s.sizetotal, s.sizedone, s.linksdone, s.linkstotal \
@@ -713,11 +696,11 @@ class FileMethods():
}
return data
-
+
@style.queue
def getLinkData(self, id):
"""get link information as dict"""
- self.c.execute('SELECT id,url,name,size,status,error,plugin,package,linkorder FROM links WHERE id=?', (str(id), ))
+ self.c.execute('SELECT id, url, name, size, status, error, plugin, package, linkorder FROM links WHERE id=?', (str(id),))
data = {}
r = self.c.fetchone()
if not r:
@@ -741,7 +724,7 @@ class FileMethods():
@style.queue
def getPackageData(self, id):
"""get data about links for a package"""
- self.c.execute('SELECT id,url,name,size,status,error,plugin,package,linkorder FROM links WHERE package=? ORDER BY linkorder', (str(id), ))
+ self.c.execute('SELECT id, url, name, size, status, error, plugin, package, linkorder FROM links WHERE package=? ORDER BY linkorder', (str(id),))
data = {}
for r in self.c:
@@ -764,22 +747,22 @@ class FileMethods():
@style.async
def updateLink(self, f):
- self.c.execute('UPDATE links SET url=?,name=?,size=?,status=?,error=?,package=? WHERE id=?', (f.url, f.name, f.size, f.status, f.error, str(f.packageid), str(f.id)))
+ self.c.execute('UPDATE links SET url=?, name=?, size=?, status=?, error=?, package=? WHERE id=?', (f.url, f.name, f.size, f.status, f.error, str(f.packageid), str(f.id)))
@style.queue
def updatePackage(self, p):
- self.c.execute('UPDATE packages SET name=?,folder=?,site=?,password=?,queue=? WHERE id=?', (p.name, p.folder, p.site, p.password, p.queue, str(p.id)))
-
- @style.queue
+ self.c.execute('UPDATE packages SET name=?, folder=?, site=?, password=?, queue=? WHERE id=?', (p.name, p.folder, p.site, p.password, p.queue, str(p.id)))
+
+ @style.queue
def updateLinkInfo(self, data):
""" data is list of tupels (name, size, status, url) """
- self.c.executemany('UPDATE links SET name=?, size=?, status=? WHERE url=? AND status IN (1,2,3,14)', data)
+ self.c.executemany('UPDATE links SET name=?, size=?, status=? WHERE url=? AND status IN (1, 2, 3, 14)', data)
ids = []
self.c.execute('SELECT id FROM links WHERE url IN (\'%s\')' % "','".join([x[3] for x in data]))
for r in self.c:
ids.append(int(r[0]))
return ids
-
+
@style.queue
def reorderPackage(self, p, position, noMove=False):
if position == -1:
@@ -791,7 +774,7 @@ class FileMethods():
self.c.execute('UPDATE packages SET packageorder=packageorder-1 WHERE packageorder <= ? AND packageorder > ? AND queue=? AND packageorder >= 0', (position, p.order, p.queue))
self.c.execute('UPDATE packages SET packageorder=? WHERE id=?', (position, str(p.id)))
-
+
@style.queue
def reorderLink(self, f, position):
""" reorder link with f as dict for pyfile """
@@ -801,34 +784,33 @@ class FileMethods():
self.c.execute('UPDATE links SET linkorder=linkorder-1 WHERE linkorder <= ? AND linkorder > ? AND package=?', (position, f["order"], f["package"]))
self.c.execute('UPDATE links SET linkorder=? WHERE id=?', (position, f["id"]))
-
-
+
@style.queue
def clearPackageOrder(self, p):
self.c.execute('UPDATE packages SET packageorder=? WHERE id=?', (-1, str(p.id)))
self.c.execute('UPDATE packages SET packageorder=packageorder-1 WHERE packageorder > ? AND queue=? AND id != ?', (p.order, p.queue, str(p.id)))
-
+
@style.async
def restartFile(self, id):
- self.c.execute('UPDATE links SET status=3,error="" WHERE id=?', (str(id),))
+ self.c.execute('UPDATE links SET status=3, error="" WHERE id=?', (str(id),))
@style.async
def restartPackage(self, id):
self.c.execute('UPDATE links SET status=3 WHERE package=?', (str(id),))
-
+
@style.queue
def getPackage(self, id):
"""return package instance from id"""
- self.c.execute("SELECT name,folder,site,password,queue,packageorder FROM packages WHERE id=?", (str(id), ))
+ self.c.execute("SELECT name, folder, site, password, queue, packageorder FROM packages WHERE id=?", (str(id),))
r = self.c.fetchone()
if not r: return None
return PyPackage(self.manager, id, * r)
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
@style.queue
def getFile(self, id):
"""return link instance from id"""
- self.c.execute("SELECT url, name, size, status, error, plugin, package, linkorder FROM links WHERE id=?", (str(id), ))
+ self.c.execute("SELECT url, name, size, status, error, plugin, package, linkorder FROM links WHERE id=?", (str(id),))
r = self.c.fetchone()
if not r: return None
return PyFile(self.manager, id, * r)
@@ -845,11 +827,11 @@ class FileMethods():
for i, item in enumerate(occ):
if i: cmd += ", "
cmd += "'%s'" % item
-
+
cmd += ")"
- cmd = "SELECT l.id FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE ((p.queue=1 AND l.plugin NOT IN %s) OR l.plugin IN %s) AND l.status IN (2,3,14) ORDER BY p.packageorder ASC, l.linkorder ASC LIMIT 5" % (cmd, pre)
-
+ cmd = "SELECT l.id FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE ((p.queue=1 AND l.plugin NOT IN %s) OR l.plugin IN %s) AND l.status IN (2, 3, 14) ORDER BY p.packageorder ASC, l.linkorder ASC LIMIT 5" % (cmd, pre)
+
self.c.execute(cmd) # very bad!
return [x[0] for x in self.c]
@@ -857,7 +839,7 @@ class FileMethods():
@style.queue
def getPluginJob(self, plugins):
"""returns pyfile ids with suited plugins"""
- cmd = "SELECT l.id FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE l.plugin IN %s AND l.status IN (2,3,14) ORDER BY p.packageorder ASC, l.linkorder ASC LIMIT 5" % plugins
+ cmd = "SELECT l.id FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE l.plugin IN %s AND l.status IN (2, 3, 14) ORDER BY p.packageorder ASC, l.linkorder ASC LIMIT 5" % plugins
self.c.execute(cmd) # very bad!
@@ -866,18 +848,18 @@ class FileMethods():
@style.queue
def getUnfinished(self, pid):
"""return list of max length 3 ids with pyfiles in package not finished or processed"""
-
+
self.c.execute("SELECT id FROM links WHERE package=? AND status NOT IN (0, 4, 13) LIMIT 3", (str(pid),))
return [r[0] for r in self.c]
@style.queue
def deleteFinished(self):
- self.c.execute("DELETE FROM links WHERE status IN (0,4)")
+ self.c.execute("DELETE FROM links WHERE status IN (0, 4)")
self.c.execute("DELETE FROM packages WHERE NOT EXISTS(SELECT 1 FROM links WHERE packages.id=links.package)")
@style.queue
def restartFailed(self):
- self.c.execute("UPDATE links SET status=3,error='' WHERE status IN (6, 8, 9)")
+ self.c.execute("UPDATE links SET status=3, error='' WHERE status IN (6, 8, 9)")
@style.queue
def findDuplicates(self, id, folder, filename):
@@ -891,54 +873,3 @@ class FileMethods():
self.c.execute("DELETE FROM packages;")
DatabaseBackend.registerSub(FileMethods)
-
-if __name__ == "__main__":
-
- pypath = "."
- _ = lambda x: x
-
- db = FileHandler(None)
-
- #p = PyFile(db, 5)
- #sleep(0.1)
-
- a = time()
-
- #print db.addPackage("package", "folder" , 1)
-
- pack = db.db.addPackage("package", "folder", 1)
-
- updates = []
-
-
- for x in range(0, 200):
- x = str(x)
- db.db.addLink("http://somehost.com/hoster/file/download?file_id=" + x, x, "BasePlugin", pack)
- updates.append(("new name" + x, 0, 3, "http://somehost.com/hoster/file/download?file_id=" + x))
-
-
- for x in range(0, 100):
- updates.append(("unimportant%s" % x, 0, 3, "a really long non existent url%s" % x))
-
- db.db.commit()
-
- b = time()
- print "adding 200 links, single sql execs, no commit", b-a
-
- print db.getCompleteData(1)
-
- c = time()
-
-
- db.db.updateLinkInfo(updates)
-
- d = time()
-
- print "updates", d-c
-
- print db.getCompleteData(1)
-
-
- e = time()
-
- print "complete data", e-d
diff --git a/module/database/StorageDatabase.py b/module/database/Storage.py
index 3ed29625f..75e166d39 100644
--- a/module/database/StorageDatabase.py
+++ b/module/database/Storage.py
@@ -1,25 +1,10 @@
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
+# @author: mkaay
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
+from pyload.database import style
+from pyload.database import DatabaseBackend
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from module.database import style
-from module.database import DatabaseBackend
-
-class StorageMethods():
+class StorageMethods(object):
@style.queue
def setStorage(db, identifier, key, value):
db.c.execute("SELECT id FROM storage WHERE identifier=? AND key=?", (identifier, key))
@@ -27,7 +12,7 @@ class StorageMethods():
db.c.execute("UPDATE storage SET value=? WHERE identifier=? AND key=?", (value, identifier, key))
else:
db.c.execute("INSERT INTO storage (identifier, key, value) VALUES (?, ?, ?)", (identifier, key, value))
-
+
@style.queue
def getStorage(db, identifier, key=None):
if key is not None:
@@ -36,12 +21,12 @@ class StorageMethods():
if row is not None:
return row[0]
else:
- db.c.execute("SELECT key, value FROM storage WHERE identifier=?", (identifier, ))
+ db.c.execute("SELECT key, value FROM storage WHERE identifier=?", (identifier,))
d = {}
for row in db.c:
d[row[0]] = row[1]
return d
-
+
@style.queue
def delStorage(db, identifier, key):
db.c.execute("DELETE FROM storage WHERE identifier=? AND key=?", (identifier, key))
diff --git a/module/database/UserDatabase.py b/module/database/User.py
index 0c781057d..67cb62ab9 100644
--- a/module/database/UserDatabase.py
+++ b/module/database/User.py
@@ -1,32 +1,17 @@
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
+# @author: mkaay
from hashlib import sha1
import random
-from DatabaseBackend import DatabaseBackend
-from DatabaseBackend import style
+from pyload.database.DatabaseBackend import DatabaseBackend
+from pyload.database.DatabaseBackend import style
-class UserMethods():
+class UserMethods(object):
@style.queue
def checkAuth(db, user, password):
c = db.c
- c.execute('SELECT id, name, password, role, permission, template, email FROM "users" WHERE name=?', (user, ))
+ c.execute('SELECT id, name, password, role, permission, template, email FROM "users" WHERE name=?', (user,))
r = c.fetchone()
if not r:
return {}
@@ -47,7 +32,7 @@ class UserMethods():
password = salt + h.hexdigest()
c = db.c
- c.execute('SELECT name FROM users WHERE name=?', (user, ))
+ c.execute('SELECT name FROM users WHERE name=?', (user,))
if c.fetchone() is not None:
c.execute('UPDATE users SET password=? WHERE name=?', (password, user))
else:
@@ -56,7 +41,7 @@ class UserMethods():
@style.queue
def changePassword(db, user, oldpw, newpw):
- db.c.execute('SELECT id, name, password FROM users WHERE name=?', (user, ))
+ db.c.execute('SELECT id, name, password FROM users WHERE name=?', (user,))
r = db.c.fetchone()
if not r:
return False
@@ -103,6 +88,6 @@ class UserMethods():
@style.queue
def removeUser(db, user):
- db.c.execute('DELETE FROM users WHERE name=?', (user, ))
+ db.c.execute('DELETE FROM users WHERE name=?', (user,))
DatabaseBackend.registerSub(UserMethods)
diff --git a/module/database/__init__.py b/module/database/__init__.py
index 545789c0c..64f049be1 100644
--- a/module/database/__init__.py
+++ b/module/database/__init__.py
@@ -1,6 +1,7 @@
-from DatabaseBackend import DatabaseBackend
-from DatabaseBackend import style
+# -*- coding: utf-8 -*-
-from FileDatabase import FileHandler
-from UserDatabase import UserMethods
-from StorageDatabase import StorageMethods \ No newline at end of file
+from pyload.database.DatabaseBackend import DatabaseBackend, style
+
+from pyload.database.File import FileHandler
+from pyload.database.User import UserMethods
+from pyload.database.Storage import StorageMethods
diff --git a/module/PyFile.py b/module/datatype/File.py
index 3dede9360..1df0a8590 100644
--- a/module/PyFile.py
+++ b/module/datatype/File.py
@@ -1,24 +1,8 @@
-#!/usr/bin/env python
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
+# -*- coding: utf-8 -*-
+# @author: RaNaN, mkaay
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
- @author: mkaay
-"""
-
-from module.PullEvents import UpdateEvent
-from module.utils import formatSize, lock
+from pyload.manager.Event import UpdateEvent
+from pyload.utils import formatSize, lock
from time import sleep, time
@@ -50,38 +34,39 @@ class PyFile(object):
"""
Represents a file object at runtime
"""
- __slots__ = ("m", "id", "url", "name", "size", "_size", "status", "pluginname", "packageid",
- "error", "order", "lock", "plugin", "waitUntil", "active", "abort", "statusname",
- "reconnected", "progress", "maxprogress", "pluginmodule", "pluginclass")
+ __slots__ = ("m", "id", "url", "name", "size", "_size", "status", "plugin",
+ "packageid", "error", "order", "lock", "plugin", "waitUntil",
+ "active", "abort", "statusname", "reconnected", "progress",
+ "maxprogress", "pluginmodule", "pluginclass")
- def __init__(self, manager, id, url, name, size, status, error, pluginname, package, order):
+ def __init__(self, manager, id, url, name, size, status, error, plugin, package, order):
self.m = manager
-
+
self.id = int(id)
self.url = url
self.name = name
self.size = size
self.status = status
- self.pluginname = pluginname
+ self.plugin = self.plugintype, self.pluginname = plugin
self.packageid = package #should not be used, use package() instead
self.error = error
self.order = order
# database information ends here
self.lock = RLock()
-
+
self.plugin = None
#self.download = None
-
+
self.waitUntil = 0 # time() + time to wait
-
+
# status attributes
self.active = False #obsolete?
self.abort = False
self.reconnected = False
self.statusname = None
-
+
self.progress = 0
self.maxprogress = 100
@@ -90,7 +75,7 @@ class PyFile(object):
# will convert all sizes to ints
size = property(lambda self: self._size, setSize)
-
+
def __repr__(self):
return "PyFile %s: %s@%s" % (self.id, self.name, self.pluginname)
@@ -98,9 +83,9 @@ class PyFile(object):
def initPlugin(self):
""" inits plugin instance """
if not self.plugin:
- self.pluginmodule = self.m.core.pluginManager.getPlugin(self.pluginname)
- self.pluginclass = getattr(self.pluginmodule, self.m.core.pluginManager.getPluginName(self.pluginname))
- self.plugin = self.pluginclass(self)
+ self.pluginmodule = self.m.core.pluginManager.getPlugin(self.plugintype, self.pluginname)
+ self.pluginclass = getattr(self.pluginmodule, self.m.core.pluginManager.getPluginName(self.plugintype, self.pluginname))
+ self.plugin = self.pluginclass(self)
@lock
def hasPlugin(self):
@@ -109,7 +94,7 @@ class PyFile(object):
:return:
"""
return hasattr(self, "plugin") and self.plugin
-
+
def package(self):
""" return package instance"""
return self.m.getPackage(self.packageid)
@@ -127,10 +112,10 @@ class PyFile(object):
return self.m.statusMsg[self.status]
else:
return self.statusname
-
+
def hasStatus(self, status):
return statusMap[status] == self.status
-
+
def sync(self):
"""sync PyFile instance with database"""
self.m.updateLink(self)
@@ -189,19 +174,19 @@ class PyFile(object):
if self.plugin and self.plugin.req:
self.plugin.req.abortDownloads()
sleep(0.1)
-
+
self.abort = False
if self.hasPlugin() and self.plugin.req:
self.plugin.req.abortDownloads()
self.release()
-
+
def finishIfDone(self):
"""set status to finish and release file if every thread is finished with it"""
if self.id in self.m.core.threadManager.processingIds():
return False
-
+
self.setStatus("finished")
self.release()
self.m.checkAllLinksFinished()
@@ -209,17 +194,17 @@ class PyFile(object):
def checkIfProcessed(self):
self.m.checkAllLinksProcessed(self.id)
-
+
def formatWait(self):
""" formats and return wait time in humanreadable format """
seconds = self.waitUntil - time()
-
+
if seconds < 0: return "00:00:00"
-
+
hours, seconds = divmod(seconds, 3600)
minutes, seconds = divmod(seconds, 60)
return "%.2i:%.2i:%.2i" % (hours, minutes, seconds)
-
+
def formatSize(self):
""" formats size to readable format """
return formatSize(self.getSize())
@@ -227,44 +212,44 @@ class PyFile(object):
def formatETA(self):
""" formats eta to readable format """
seconds = self.getETA()
-
+
if seconds < 0: return "00:00:00"
-
+
hours, seconds = divmod(seconds, 3600)
minutes, seconds = divmod(seconds, 60)
return "%.2i:%.2i:%.2i" % (hours, minutes, seconds)
-
+
def getSpeed(self):
""" calculates speed """
try:
return self.plugin.req.speed
- except:
+ except Exception:
return 0
-
+
def getETA(self):
""" gets established time of arrival"""
try:
return self.getBytesLeft() / self.getSpeed()
- except:
+ except Exception:
return 0
-
+
def getBytesLeft(self):
""" gets bytes left """
try:
- return self.plugin.req.size - self.plugin.req.arrived
- except:
+ return self.getSize() - self.plugin.req.arrived
+ except Exception:
return 0
-
+
def getPercent(self):
""" get % of download """
if self.status == 12:
try:
return self.plugin.req.percent
- except:
+ except Exception:
return 0
else:
return self.progress
-
+
def getSize(self):
""" get size of download """
try:
@@ -272,9 +257,9 @@ class PyFile(object):
return self.plugin.req.size
else:
return self.size
- except:
+ except Exception:
return self.size
-
+
def notifyChange(self):
e = UpdateEvent("file", self.id, "collector" if not self.package().queue else "queue")
self.m.core.pullManager.addEvent(e)
diff --git a/module/PyPackage.py b/module/datatype/Package.py
index f3be6c886..bf3edffea 100644
--- a/module/PyPackage.py
+++ b/module/datatype/Package.py
@@ -1,26 +1,10 @@
-#!/usr/bin/env python
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
+# -*- coding: utf-8 -*-
+# @author: RaNaN, mkaay
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
+from pyload.manager.Event import UpdateEvent
+from pyload.utils import safe_filename
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
- @author: mkaay
-"""
-
-from module.PullEvents import UpdateEvent
-from module.utils import save_path
-
-class PyPackage():
+class PyPackage(object):
"""
Represents a package object at runtime
"""
@@ -39,7 +23,7 @@ class PyPackage():
@property
def folder(self):
- return save_path(self._folder)
+ return safe_filename(self._folder)
def toDict(self):
""" Returns a dictionary representation of the data.
@@ -74,7 +58,7 @@ class PyPackage():
def delete(self):
self.m.deletePackage(self.id)
-
+
def notifyChange(self):
e = UpdateEvent("pack", self.id, "collector" if not self.queue else "queue")
self.m.core.pullManager.addEvent(e)
diff --git a/module/datatype/__init__.py b/module/datatype/__init__.py
new file mode 100644
index 000000000..40a96afc6
--- /dev/null
+++ b/module/datatype/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/debug.py b/module/debug.py
deleted file mode 100644
index 8d1ddd3d0..000000000
--- a/module/debug.py
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/usr/bin/env python
-#coding:utf-8
-
-import re
-import InitHomeDir
-from os import listdir
-
-class Wrapper(object):
- pass
-
-def filter_info(line):
- if "object at 0x" in line:
- return False
- elif " at line " in line:
- return False
- elif " <DownloadThread(" in line:
- return False
- elif "<class '" in line:
- return False
- elif "PyFile " in line:
- return False
- elif " <module '" in line:
- return False
-
- else:
- return True
-
-def appendName(lines, name):
- test = re.compile("^[a-zA-z0-9]+ = ")
-
- for i, line in enumerate(lines):
- if test.match(line):
- lines[i] = name+"."+line
-
- return lines
-
-def initReport():
- reports = []
- for f in listdir("."):
- if f.startswith("debug_"):
- reports.append(f)
-
- for i, f in enumerate(reports):
- print "%s. %s" % (i,f)
-
- choice = raw_input("Choose Report: ")
-
- report = reports[int(choice)]
-
- f = open(report, "rb")
-
- content = f.readlines()
- content = [x.strip() for x in content if x.strip()]
-
- frame = Wrapper()
- plugin = Wrapper()
- pyfile = Wrapper()
-
- frame_c = []
- plugin_c = []
- pyfile_c = []
-
- dest = None
-
- for line in content:
- if line == "FRAMESTACK:":
- dest = frame_c
- continue
- elif line == "PLUGIN OBJECT DUMP:":
- dest = plugin_c
- continue
- elif line == "PYFILE OBJECT DUMP:":
- dest = pyfile_c
- continue
- elif line == "CONFIG:":
- dest = None
-
- if dest is not None:
- dest.append(line)
-
-
- frame_c = filter(filter_info, frame_c)
- plugin_c = filter(filter_info, plugin_c)
- pyfile_c = filter(filter_info, pyfile_c)
-
- frame_c = appendName(frame_c, "frame")
- plugin_c = appendName(plugin_c, "plugin")
- pyfile_c = appendName(pyfile_c, "pyfile")
-
- exec("\n".join(frame_c+plugin_c+pyfile_c) )
-
- return frame, plugin, pyfile
-
-if __name__=='__main__':
- print "No main method, use this module with your python shell" \ No newline at end of file
diff --git a/module/forwarder.py b/module/forwarder.py
deleted file mode 100644
index eacb33c2b..000000000
--- a/module/forwarder.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
-
-from sys import argv
-from sys import exit
-
-import socket
-import thread
-
-from traceback import print_exc
-
-class Forwarder():
-
- def __init__(self, extip,extport=9666):
- print "Start portforwarding to %s:%s" % (extip, extport)
- proxy(extip, extport, 9666)
-
-
-def proxy(*settings):
- while True:
- server(*settings)
-
-def server(*settings):
- try:
- dock_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- dock_socket.bind(("127.0.0.1", settings[2]))
- dock_socket.listen(5)
- while True:
- client_socket = dock_socket.accept()[0]
- server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- server_socket.connect((settings[0], settings[1]))
- thread.start_new_thread(forward, (client_socket, server_socket))
- thread.start_new_thread(forward, (server_socket, client_socket))
- except Exception:
- print_exc()
-
-
-def forward(source, destination):
- string = ' '
- while string:
- string = source.recv(1024)
- if string:
- destination.sendall(string)
- else:
- #source.shutdown(socket.SHUT_RD)
- destination.shutdown(socket.SHUT_WR)
-
-if __name__ == "__main__":
- args = argv[1:]
- if not args:
- print "Usage: forwarder.py <remote ip> <remote port>"
- exit()
- if len(args) == 1:
- args.append(9666)
-
- f = Forwarder(args[0], int(args[1]))
- \ No newline at end of file
diff --git a/module/gui/AccountEdit.py b/module/gui/AccountEdit.py
deleted file mode 100644
index b22cfc49f..000000000
--- a/module/gui/AccountEdit.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from os.path import join
-
-class AccountEdit(QWidget):
- """
- account editor widget
- """
-
- def __init__(self):
- QMainWindow.__init__(self)
-
- self.setWindowTitle(_("Edit account"))
- self.setWindowIcon(QIcon(join(pypath, "icons","logo.png")))
-
- self.setLayout(QGridLayout())
- l = self.layout()
-
- typeLabel = QLabel(_("Type"))
- loginLabel = QLabel(_("Login"))
- passwordLabel = QLabel(_("New password"))
- changePw = QCheckBox()
- changePw.setChecked(False)
- self.changePw = changePw
- password = QLineEdit()
- password.setEnabled(False)
- password.setEchoMode(QLineEdit.Password)
- self.password = password
- login = QLineEdit()
- self.login = login
- acctype = QComboBox()
- self.acctype = acctype
-
- save = QPushButton(_("Save"))
-
- self.connect(changePw, SIGNAL("toggled(bool)"), password, SLOT("setEnabled(bool)"))
-
- l.addWidget(save, 3, 0, 1, 3)
- l.addWidget(acctype, 0, 1, 1, 2)
- l.addWidget(login, 1, 1, 1, 2)
- l.addWidget(password, 2, 2)
- l.addWidget(changePw, 2, 1)
- l.addWidget(passwordLabel, 2, 0)
- l.addWidget(loginLabel, 1, 0)
- l.addWidget(typeLabel, 0, 0)
-
- self.connect(save, SIGNAL("clicked()"), self.slotSave)
-
- def slotSave(self):
- """
- save entered data
- """
- data = {"login": str(self.login.text()), "acctype": str(self.acctype.currentText()), "password": False}
- if self.changePw.isChecked():
- data["password"] = str(self.password.text())
- self.emit(SIGNAL("done"), data)
-
- @staticmethod
- def newAccount(types):
- """
- create empty editor instance
- """
- w = AccountEdit()
- w.setWindowTitle(_("Create account"))
-
- w.changePw.setChecked(True)
- w.password.setEnabled(True)
-
- w.acctype.addItems(types)
-
- return w
-
- @staticmethod
- def editAccount(types, base):
- """
- create editor instance with given data
- """
- w = AccountEdit()
-
- w.acctype.addItems(types)
- w.acctype.setCurrentIndex(types.index(base["type"]))
-
- w.login.setText(base["login"])
-
- return w
diff --git a/module/gui/Accounts.py b/module/gui/Accounts.py
deleted file mode 100644
index 8db04dfa9..000000000
--- a/module/gui/Accounts.py
+++ /dev/null
@@ -1,213 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from time import strftime, gmtime
-
-class AccountModel(QAbstractItemModel):
- """
- model for account view
- """
-
- def __init__(self, view, connector):
- QAbstractItemModel.__init__(self)
- self.connector = connector
- self.view = view
- self._data = []
- self.cols = 4
- self.mutex = QMutex()
-
- def reloadData(self, force=False):
- """
- reload account list
- """
- accounts = self.connector.proxy.getAccounts(False)
-
- if self._data == accounts:
- return
-
- if len(self._data) > 0:
- self.beginRemoveRows(QModelIndex(), 0, len(self._data)-1)
- self._data = []
- self.endRemoveRows()
-
- if len(accounts) > 0:
- self.beginInsertRows(QModelIndex(), 0, len(accounts)-1)
- self._data = accounts
- self.endInsertRows()
-
- def toData(self, index):
- """
- return index pointer
- """
- return index.internalPointer()
-
- def data(self, index, role=Qt.DisplayRole):
- """
- return cell data
- """
- if not index.isValid():
- return QVariant()
- if role == Qt.DisplayRole:
- if index.column() == 0:
- return QVariant(self.toData(index).type)
- elif index.column() == 1:
- return QVariant(self.toData(index).login)
- elif index.column() == 2:
- if not self.toData(index).valid:
- return QVariant(_("not valid"))
- if not self.toData(index).validuntil:
- return QVariant(_("n/a"))
- until = int(self.toData(index).validuntil)
- if until > 0:
- fmtime = strftime(_("%a, %d %b %Y %H:%M"), gmtime(until))
- return QVariant(fmtime)
- else:
- return QVariant(_("unlimited"))
- #elif role == Qt.EditRole:
- # if index.column() == 0:
- # return QVariant(index.internalPointer().data["name"])
- return QVariant()
-
- def index(self, row, column, parent=QModelIndex()):
- """
- create index with data pointer
- """
- if parent == QModelIndex() and len(self._data) > row:
- pointer = self._data[row]
- index = self.createIndex(row, column, pointer)
- elif parent.isValid():
- pointer = parent.internalPointer().children[row]
- index = self.createIndex(row, column, pointer)
- else:
- index = QModelIndex()
- return index
-
- def parent(self, index):
- """
- no parents, everything on top level
- """
- return QModelIndex()
-
- def rowCount(self, parent=QModelIndex()):
- """
- account count
- """
- if parent == QModelIndex():
- return len(self._data)
- return 0
-
- def columnCount(self, parent=QModelIndex()):
- return self.cols
-
- def hasChildren(self, parent=QModelIndex()):
- """
- everything on top level
- """
- if parent == QModelIndex():
- return True
- else:
- return False
-
- def canFetchMore(self, parent):
- return False
-
- def headerData(self, section, orientation, role=Qt.DisplayRole):
- """
- returns column heading
- """
- if orientation == Qt.Horizontal and role == Qt.DisplayRole:
- if section == 0:
- return QVariant(_("Type"))
- elif section == 1:
- return QVariant(_("Login"))
- elif section == 2:
- return QVariant(_("Valid until"))
- elif section == 3:
- return QVariant(_("Traffic left"))
- return QVariant()
-
- def flags(self, index):
- """
- cell flags
- """
- return Qt.ItemIsSelectable | Qt.ItemIsEnabled
-
-class AccountView(QTreeView):
- """
- view component for accounts
- """
-
- def __init__(self, connector):
- QTreeView.__init__(self)
- self.setModel(AccountModel(self, connector))
-
- self.setColumnWidth(0, 150)
- self.setColumnWidth(1, 150)
- self.setColumnWidth(2, 150)
- self.setColumnWidth(3, 150)
-
- self.setEditTriggers(QAbstractItemView.NoEditTriggers)
-
- self.delegate = AccountDelegate(self, self.model())
- self.setItemDelegateForColumn(3, self.delegate)
-
-class AccountDelegate(QItemDelegate):
- """
- used to display a progressbar for the traffic in the traffic cell
- """
-
- def __init__(self, parent, model):
- QItemDelegate.__init__(self, parent)
- self.model = model
-
- def paint(self, painter, option, index):
- """
- paint the progressbar
- """
- if not index.isValid():
- return
- if index.column() == 3:
- data = self.model.toData(index)
- opts = QStyleOptionProgressBarV2()
- opts.minimum = 0
- if data.trafficleft:
- if data.trafficleft == -1 or data.trafficleft is None:
- opts.maximum = opts.progress = 1
- else:
- opts.maximum = opts.progress = data.trafficleft
- if data.maxtraffic:
- opts.maximum = data.maxtraffic
-
- opts.rect = option.rect
- opts.rect.setRight(option.rect.right()-1)
- opts.rect.setHeight(option.rect.height()-1)
- opts.textVisible = True
- opts.textAlignment = Qt.AlignCenter
- if data.trafficleft and data.trafficleft == -1:
- opts.text = QString(_("unlimited"))
- elif data.trafficleft is None:
- opts.text = QString(_("n/a"))
- else:
- opts.text = QString.number(round(float(opts.progress)/1024/1024, 2)) + " GB"
- QApplication.style().drawControl(QStyle.CE_ProgressBar, opts, painter)
- return
- QItemDelegate.paint(self, painter, option, index)
-
diff --git a/module/gui/CNLServer.py b/module/gui/CNLServer.py
deleted file mode 100644
index 5ecac8277..000000000
--- a/module/gui/CNLServer.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
-import re
-from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
-from threading import Thread
-from cgi import FieldStorage
-from os.path import abspath, dirname, join
-from urllib import unquote
-from base64 import standard_b64decode
-from binascii import unhexlify
-
-try:
- from Crypto.Cipher import AES
-except:
- pass
-
-try:
- from module.common import JsEngine
-except ImportError:
- import sys
- sys.path.append(join(abspath(dirname(__file__)), "..", ".."))
- from module.common import JsEngine
-
-js = JsEngine.JsEngine()
-core = None
-
-class CNLServer(Thread):
- def __init__(self):
- Thread.__init__(self)
- self.setDaemon(True)
-
- self.stop = False
- self.stopped = False
-
- def run(self):
- server_address = ('127.0.0.1', 9666)
- try:
- httpd = HTTPServer(server_address, CNLHandler)
- except:
- self.stopped = True
- return
-
- self.stopped = False
- while self.keep_running():
- httpd.handle_request()
- self.stopped = True
-
- def keep_running(self):
- return not self.stop
-
-
-class CNLHandler(BaseHTTPRequestHandler):
-
- #def log_message(self, *args):
- # pass
-
- def add_package(self, name, urls, queue=0):
- print "name", name
- print "urls", urls
- print "queue", queue
-
- def get_post(self, name, default=""):
- if name in self.post:
- return self.post[name]
- else:
- return default
-
- def start_response(self, string):
-
- self.send_response(200)
-
- self.send_header("Content-Length", len(string))
- self.send_header("Content-Language", "de")
- self.send_header("Vary", "Accept-Language, Cookie")
- self.send_header("Cache-Control", "no-cache, must-revalidate")
- self.send_header("Content-type", "text/html")
- self.end_headers()
-
- def do_GET(self):
- path = self.path.strip("/").lower()
- #self.wfile.write(path+"\n")
-
- self.map = [ (r"add$", self.add),
- (r"addcrypted$", self.addcrypted),
- (r"addcrypted2$", self.addcrypted2),
- (r"flashgot", self.flashgot),
- (r"crossdomain\.xml", self.crossdomain),
- (r"checkSupportForUrl", self.checksupport),
- (r"jdcheck.js", self.jdcheck),
- (r"", self.flash) ]
-
- func = None
- for r, f in self.map:
- if re.match(r"(flash(got)?/?)?"+r, path):
- func = f
- break
-
- if func:
- try:
- resp = func()
- if not resp: resp = "success"
- resp += "\r\n"
- self.start_response(resp)
- self.wfile.write(resp)
- except Exception,e :
- self.send_error(500, str(e))
- else:
- self.send_error(404, "Not Found")
-
- def do_POST(self):
- form = FieldStorage(
- fp=self.rfile,
- headers=self.headers,
- environ={'REQUEST_METHOD':'POST',
- 'CONTENT_TYPE':self.headers['Content-Type'],
- })
-
- self.post = {}
- for name in form.keys():
- self.post[name] = form[name].value
-
- return self.do_GET()
-
- def flash(self):
- return "JDownloader"
-
- def add(self):
- package = self.get_post('referer', 'ClickAndLoad Package')
- urls = filter(lambda x: x != "", self.get_post('urls').split("\n"))
-
- self.add_package(package, urls, 0)
-
- def addcrypted(self):
- package = self.get_post('referer', 'ClickAndLoad Package')
- dlc = self.get_post('crypted').replace(" ", "+")
-
- core.upload_container(package, dlc)
-
- def addcrypted2(self):
- package = self.get_post("source", "ClickAndLoad Package")
- crypted = self.get_post("crypted")
- jk = self.get_post("jk")
-
- crypted = standard_b64decode(unquote(crypted.replace(" ", "+")))
- jk = "%s f()" % jk
- jk = js.eval(jk)
- Key = unhexlify(jk)
- IV = Key
-
- obj = AES.new(Key, AES.MODE_CBC, IV)
- result = obj.decrypt(crypted).replace("\x00", "").replace("\r","").split("\n")
-
- result = filter(lambda x: x != "", result)
-
- self.add_package(package, result, 0)
-
-
- def flashgot(self):
- autostart = int(self.get_post('autostart', 0))
- package = self.get_post('package', "FlashGot")
- urls = filter(lambda x: x != "", self.get_post('urls').split("\n"))
-
- self.add_package(package, urls, autostart)
-
- def crossdomain(self):
- rep = "<?xml version=\"1.0\"?>\n"
- rep += "<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n"
- rep += "<cross-domain-policy>\n"
- rep += "<allow-access-from domain=\"*\" />\n"
- rep += "</cross-domain-policy>"
- return rep
-
- def checksupport(self):
- pass
-
- def jdcheck(self):
- rep = "jdownloader=true;\n"
- rep += "var version='10629';\n"
- return rep
-
-
-if __name__ == "__main__":
- import xmlrpclib
- from module import InitHomeDir
- from module.ConfigParser import ConfigParser
-
- config = ConfigParser()
-
- ssl = ""
- if config.get("ssl", "activated"):
- ssl = "s"
-
- server_url = "http%s://%s:%s@%s:%s/" % (
- ssl,
- config.username,
- config.password,
- config.get("remote", "listenaddr"),
- config.get("remote", "port")
- )
-
- core = xmlrpclib.ServerProxy(server_url, allow_none=True)
-
- s = CNLServer()
- s.start()
- while not s.stopped:
- try:
- s.join(1)
- except KeyboardInterrupt:
- s.stop = True
- s.stopped = True
- print "quiting.."
diff --git a/module/gui/CaptchaDock.py b/module/gui/CaptchaDock.py
deleted file mode 100644
index b88cb53ca..000000000
--- a/module/gui/CaptchaDock.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-class CaptchaDock(QDockWidget):
- """
- dock widget for captcha input
- """
-
- def __init__(self):
- QDockWidget.__init__(self, _("Captcha"))
- self.setObjectName("Captcha Dock")
- self.widget = CaptchaDockWidget(self)
- self.setWidget(self.widget)
- self.setAllowedAreas(Qt.BottomDockWidgetArea)
- self.setFeatures(QDockWidget.NoDockWidgetFeatures)
- self.hide()
- self.processing = False
- self.currentID = None
- self.connect(self, SIGNAL("setTask"), self.setTask)
-
- def isFree(self):
- return not self.processing
-
- def setTask(self, tid, img, imgType):
- self.processing = True
- data = QByteArray(img)
- self.currentID = tid
- self.widget.emit(SIGNAL("setImage"), data)
- self.widget.input.setText("")
- self.show()
-
-class CaptchaDockWidget(QWidget):
- """
- widget for the input widgets
- """
-
- def __init__(self, dock):
- QWidget.__init__(self)
- self.dock = dock
- self.setLayout(QHBoxLayout())
- layout = self.layout()
-
- imgLabel = QLabel()
- captchaInput = QLineEdit()
- okayButton = QPushButton(_("OK"))
- cancelButton = QPushButton(_("Cancel"))
-
- layout.addStretch()
- layout.addWidget(imgLabel)
- layout.addWidget(captchaInput)
- layout.addWidget(okayButton)
- layout.addWidget(cancelButton)
- layout.addStretch()
-
- self.input = captchaInput
-
- self.connect(okayButton, SIGNAL("clicked()"), self.slotSubmit)
- self.connect(captchaInput, SIGNAL("returnPressed()"), self.slotSubmit)
- self.connect(self, SIGNAL("setImage"), self.setImg)
- self.connect(self, SIGNAL("setPixmap(const QPixmap &)"), imgLabel, SLOT("setPixmap(const QPixmap &)"))
-
- def setImg(self, data):
- pixmap = QPixmap()
- pixmap.loadFromData(data)
- self.emit(SIGNAL("setPixmap(const QPixmap &)"), pixmap)
- self.input.setFocus(Qt.OtherFocusReason)
-
- def slotSubmit(self):
- text = self.input.text()
- tid = self.dock.currentID
- self.dock.currentID = None
- self.dock.emit(SIGNAL("done"), tid, str(text))
- self.dock.hide()
- self.dock.processing = False
-
diff --git a/module/gui/Collector.py b/module/gui/Collector.py
deleted file mode 100644
index 3ec4262f1..000000000
--- a/module/gui/Collector.py
+++ /dev/null
@@ -1,407 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from module.PyFile import statusMap
-from module.utils import formatSize
-
-from module.remote.thriftbackend.ThriftClient import Destination, FileDoesNotExists, ElementType
-
-statusMapReverse = dict((v,k) for k, v in statusMap.iteritems())
-
-translatedStatusMap = {} # -> CollectorModel.__init__
-
-class CollectorModel(QAbstractItemModel):
- """
- model for the collector view
- """
-
- def __init__(self, view, connector):
- QAbstractItemModel.__init__(self)
- self.connector = connector
- self.view = view
- self._data = []
- self.cols = 4
- self.interval = 1
- self.mutex = QMutex()
-
- global translatedStatusMap # workaround because i18n is not running at import time
- translatedStatusMap = {
- "finished": _("finished"),
- "offline": _("offline"),
- "online": _("online"),
- "queued": _("queued"),
- "skipped": _("skipped"),
- "waiting": _("waiting"),
- "temp. offline": _("temp. offline"),
- "starting": _("starting"),
- "failed": _("failed"),
- "aborted": _("aborted"),
- "decrypting": _("decrypting"),
- "custom": _("custom"),
- "downloading": _("downloading"),
- "processing": _("processing")
- }
-
- def translateStatus(self, string):
- """
- used to convert to locale specific status
- """
- return translatedStatusMap[string]
-
- def addEvent(self, event):
- """
- called from main loop, pass events to the correct methods
- """
- QMutexLocker(self.mutex)
- if event.eventname == "reload":
- self.fullReload()
- elif event.eventname == "remove":
- self.removeEvent(event)
- elif event.eventname == "insert":
- self.insertEvent(event)
- elif event.eventname == "update":
- self.updateEvent(event)
-
- def fullReload(self):
- """
- reload whole model, used at startup to load initial data
- """
- self._data = []
- order = self.connector.getPackageOrder(Destination.Collector)
- self.beginInsertRows(QModelIndex(), 0, len(order.values()))
- for position, pid in order.iteritems():
- pack = self.connector.getPackageData(pid)
- package = Package(pack)
- self._data.append(package)
- self._data = sorted(self._data, key=lambda p: p.data["order"])
- self.endInsertRows()
-
- def removeEvent(self, event):
- """
- remove an element from model
- """
- if event.type == ElementType.File:
- for p, package in enumerate(self._data):
- for k, child in enumerate(package.children):
- if child.id == event.id:
- self.beginRemoveRows(self.index(p, 0), k, k)
- del package.children[k]
- self.endRemoveRows()
- break
- else:
- for k, package in enumerate(self._data):
- if package.id == event.id:
- self.beginRemoveRows(QModelIndex(), k, k)
- del self._data[k]
- self.endRemoveRows()
- break
-
- def insertEvent(self, event):
- """
- inserts a new element in the model
- """
- if event.type == ElementType.File:
- try:
- info = self.connector.getFileData(event.id)
- except FileDoesNotExists:
- return
-
- for k, package in enumerate(self._data):
- if package.id == info.package:
- if package.getChild(info.fid):
- self.updateEvent(event)
- break
- self.beginInsertRows(self.index(k, 0), info.order, info.order)
- package.addChild(info)
- self.endInsertRows()
- break
- else:
- data = self.connector.getPackageData(event.id)
- package = Package(data)
- self.beginInsertRows(QModelIndex(), data.order, data.order)
- self._data.insert(data.order, package)
- self.endInsertRows()
-
- def updateEvent(self, event):
- """
- update an element in the model
- """
- if event.type == ElementType.File:
- try:
- info = self.connector.proxy.getFileData(event.id)
- except FileDoesNotExists:
- return
- for p, package in enumerate(self._data):
- if package.id == info.packageID:
- for k, child in enumerate(package.children):
- if child.id == event.id:
- child.update(info)
- if not info.status == 12:
- child.data["downloading"] = None
- self.emit(SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"), self.index(k, 0, self.index(p, 0)), self.index(k, self.cols, self.index(p, self.cols)))
- break
- else:
- data = self.connector.getPackageData(event.id)
- if not data:
- return
- for p, package in enumerate(self._data):
- if package.id == event.id:
- package.update(data)
- self.emit(SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"), self.index(p, 0), self.index(p, self.cols))
- break
-
- def data(self, index, role=Qt.DisplayRole):
- """
- return cell data
- """
- if not index.isValid():
- return QVariant()
- if role == Qt.DisplayRole:
- if index.column() == 0:
- return QVariant(index.internalPointer().data["name"])
- elif index.column() == 1:
- item = index.internalPointer()
- plugins = []
- if isinstance(item, Package):
- for child in item.children:
- if not child.data["plugin"] in plugins:
- plugins.append(child.data["plugin"])
- else:
- plugins.append(item.data["plugin"])
- return QVariant(", ".join(plugins))
- elif index.column() == 2:
- item = index.internalPointer()
- status = 0
- if isinstance(item, Package):
- for child in item.children:
- if child.data["status"] > status:
- status = child.data["status"]
- else:
- status = item.data["status"]
- return QVariant(self.translateStatus(statusMapReverse[status]))
- elif index.column() == 3:
- item = index.internalPointer()
- if isinstance(item, Link):
- return QVariant(formatSize(item.data["size"]))
- else:
- ms = 0
- for c in item.children:
- ms += c.data["size"]
- return QVariant(formatSize(ms))
- elif role == Qt.EditRole:
- if index.column() == 0:
- return QVariant(index.internalPointer().data["name"])
- return QVariant()
-
- def index(self, row, column, parent=QModelIndex()):
- """
- creates a cell index with pointer to the data
- """
- if parent == QModelIndex() and len(self._data) > row:
- pointer = self._data[row]
- index = self.createIndex(row, column, pointer)
- elif parent.isValid():
- try:
- pointer = parent.internalPointer().children[row]
- except:
- return QModelIndex()
- index = self.createIndex(row, column, pointer)
- else:
- index = QModelIndex()
- return index
-
- def parent(self, index):
- """
- return index of parent element
- only valid for links
- """
- if index == QModelIndex():
- return QModelIndex()
- if index.isValid():
- link = index.internalPointer()
- if isinstance(link, Link):
- for k, pack in enumerate(self._data):
- if pack == link.package:
- return self.createIndex(k, 0, link.package)
- return QModelIndex()
-
- def rowCount(self, parent=QModelIndex()):
- """
- returns row count for the element
- """
- if parent == QModelIndex():
- #return package count
- return len(self._data)
- else:
- if parent.isValid():
- #index is valid
- pack = parent.internalPointer()
- if isinstance(pack, Package):
- #index points to a package
- #return len of children
- return len(pack.children)
- else:
- #index is invalid
- return False
- #files have no children
- return 0
-
- def columnCount(self, parent=QModelIndex()):
- return self.cols
-
- def hasChildren(self, parent=QModelIndex()):
- if not parent.isValid():
- return True
- return self.rowCount(parent) > 0
-
- def canFetchMore(self, parent):
- return False
-
- def headerData(self, section, orientation, role=Qt.DisplayRole):
- """
- returns column heading
- """
- if orientation == Qt.Horizontal and role == Qt.DisplayRole:
- if section == 0:
- return QVariant(_("Name"))
- elif section == 1:
- return QVariant(_("Plugin"))
- elif section == 2:
- return QVariant(_("Status"))
- elif section == 3:
- return QVariant(_("Size"))
- return QVariant()
-
- def flags(self, index):
- """
- cell flags
- """
- if index.column() == 0 and self.parent(index) == QModelIndex():
- return Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled
- return Qt.ItemIsSelectable | Qt.ItemIsEnabled
-
- def setData(self, index, value, role=Qt.EditRole):
- """
- called if package name editing is finished, sets new name
- """
- if index.column() == 0 and self.parent(index) == QModelIndex() and role == Qt.EditRole:
- self.connector.setPackageName(index.internalPointer().id, str(value.toString()))
- return True
-
-class Package(object):
- """
- package object in the model
- """
-
- def __init__(self, pack):
- self.id = pack.pid
- self.children = []
- for f in pack.links:
- self.addChild(f)
- self.data = {}
- self.update(pack)
-
- def update(self, pack):
- """
- update data dict from thift object
- """
- data = {
- "name": pack.name,
- "folder": pack.folder,
- "site": pack.site,
- "password": pack.password,
- "order": pack.order,
- }
- self.data.update(data)
-
- def addChild(self, f):
- """
- add child (Link) to package
- """
- self.children.insert(f.order, Link(f, self))
- self.children = sorted(self.children, key=lambda l: l.data["order"])
-
- def getChild(self, fid):
- """
- get child from package
- """
- for child in self.children:
- if child.id == int(fid):
- return child
- return None
-
- def getChildKey(self, fid):
- """
- get child index
- """
- for k, child in enumerate(self.children):
- if child.id == int(fid):
- return k
- return None
-
- def removeChild(self, fid):
- """
- remove child
- """
- for k, child in enumerate(self.children):
- if child.id == int(fid):
- del self.children[k]
-
-class Link(object):
- def __init__(self, f, pack):
- self.data = {"downloading": None}
- self.update(f)
- self.id = f.fid
- self.package = pack
-
- def update(self, f):
- """
- update data dict from thift object
- """
- data = {
- "url": f.url,
- "name": f.name,
- "plugin": f.plugin,
- "size": f.size,
- "format_size": f.format_size,
- "status": f.status,
- "statusmsg": f.statusmsg,
- "package": f.packageID,
- "error": f.error,
- "order": f.order,
- }
- self.data.update(data)
-
-class CollectorView(QTreeView):
- """
- view component for collector
- """
-
- def __init__(self, connector):
- QTreeView.__init__(self)
- self.setModel(CollectorModel(self, connector))
- self.setColumnWidth(0, 500)
- self.setColumnWidth(1, 100)
- self.setColumnWidth(2, 200)
- self.setColumnWidth(3, 100)
-
- self.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed)
-
diff --git a/module/gui/ConnectionManager.py b/module/gui/ConnectionManager.py
deleted file mode 100644
index def575abc..000000000
--- a/module/gui/ConnectionManager.py
+++ /dev/null
@@ -1,302 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from os.path import join
-
-from uuid import uuid4 as uuid
-
-class ConnectionManager(QWidget):
-
-
- warningShown = False
-
- def __init__(self):
- QWidget.__init__(self)
-
- if not self.warningShown:
- QMessageBox.warning(self, 'Warning',
- "We are sorry but the GUI is not stable yet. Please use the webinterface for much better experience. \n", QMessageBox.Ok)
- ConnectionManager.warningShown = True
-
- mainLayout = QHBoxLayout()
- buttonLayout = QVBoxLayout()
-
- self.setWindowTitle(_("pyLoad ConnectionManager"))
- self.setWindowIcon(QIcon(join(pypath, "icons","logo.png")))
-
- connList = QListWidget()
-
- new = QPushButton(_("New"))
- edit = QPushButton(_("Edit"))
- remove = QPushButton(_("Remove"))
- connect = QPushButton(_("Connect"))
-
- #box = QFrame()
- boxLayout = QVBoxLayout()
- #box.setLayout(boxLayout)
-
- boxLayout.addWidget(QLabel(_("Connect:")))
- boxLayout.addWidget(connList)
-
- line = QFrame()
- #line.setFixedWidth(100)
- line.setFrameShape(line.HLine)
- line.setFrameShadow(line.Sunken)
- line.setFixedHeight(10)
-
- boxLayout.addWidget(line)
-
- form = QFormLayout()
- form.setMargin(5)
- form.setSpacing(20)
-
- form.setAlignment(Qt.AlignRight)
- checkbox = QCheckBox()
- form.addRow(_("Use internal Core:"), checkbox)
-
- boxLayout.addLayout(form)
-
- mainLayout.addLayout(boxLayout)
- mainLayout.addLayout(buttonLayout)
-
- buttonLayout.addWidget(new)
- buttonLayout.addWidget(edit)
- buttonLayout.addWidget(remove)
- buttonLayout.addStretch()
- buttonLayout.addWidget(connect)
-
- self.setLayout(mainLayout)
-
- self.internal = checkbox
- self.new = new
- self.connectb = connect
- self.remove = remove
- self.editb = edit
- self.connList = connList
- self.edit = self.EditWindow()
- self.connectSignals()
-
- self.defaultStates = {}
-
- def connectSignals(self):
- self.connect(self, SIGNAL("setConnections"), self.setConnections)
- self.connect(self.new, SIGNAL("clicked()"), self.slotNew)
- self.connect(self.editb, SIGNAL("clicked()"), self.slotEdit)
- self.connect(self.remove, SIGNAL("clicked()"), self.slotRemove)
- self.connect(self.connectb, SIGNAL("clicked()"), self.slotConnect)
- self.connect(self.edit, SIGNAL("save"), self.slotSave)
- self.connect(self.connList, SIGNAL("itemDoubleClicked(QListWidgetItem *)"), self.slotItemDoubleClicked)
- self.connect(self.internal, SIGNAL("clicked()"), self.slotInternal)
-
- def setConnections(self, connections):
- self.connList.clear()
- for conn in connections:
- item = QListWidgetItem()
- item.setData(Qt.DisplayRole, QVariant(conn["name"]))
- item.setData(Qt.UserRole, QVariant(conn))
- self.connList.addItem(item)
- if conn["default"]:
- item.setData(Qt.DisplayRole, QVariant(_("%s (Default)") % conn["name"]))
- self.connList.setCurrentItem(item)
-
- def slotNew(self):
- data = {"id":uuid().hex, "type":"remote", "default":False, "name":"", "host":"", "port":"7228", "user":"admin", "password":""}
- self.edit.setData(data)
- self.edit.show()
-
- def slotEdit(self):
- item = self.connList.currentItem()
- data = item.data(Qt.UserRole).toPyObject()
- data = self.cleanDict(data)
- self.edit.setData(data)
- self.edit.show()
-
- def slotRemove(self):
- item = self.connList.currentItem()
- data = item.data(Qt.UserRole).toPyObject()
- data = self.cleanDict(data)
- self.emit(SIGNAL("removeConnection"), data)
-
- def slotConnect(self):
- if self.internal.checkState() == 2:
- data = {"type": "internal"}
- self.emit(SIGNAL("connect"), data)
- else:
- item = self.connList.currentItem()
- data = item.data(Qt.UserRole).toPyObject()
- data = self.cleanDict(data)
- self.emit(SIGNAL("connect"), data)
-
- def cleanDict(self, data):
- tmp = {}
- for k, d in data.items():
- tmp[str(k)] = d
- return tmp
-
- def slotSave(self, data):
- self.emit(SIGNAL("saveConnection"), data)
-
- def slotItemDoubleClicked(self, defaultItem):
- data = defaultItem.data(Qt.UserRole).toPyObject()
- self.setDefault(data, True)
- did = self.cleanDict(data)["id"]
- #allItems = self.connList.findItems("*", Qt.MatchWildcard)
- count = self.connList.count()
- for i in range(count):
- item = self.connList.item(i)
- data = item.data(Qt.UserRole).toPyObject()
- if self.cleanDict(data)["id"] == did:
- continue
- self.setDefault(data, False)
-
- def slotInternal(self):
- if self.internal.checkState() == 2:
- self.connList.clearSelection()
-
- def setDefault(self, data, state):
- data = self.cleanDict(data)
- self.edit.setData(data)
- data = self.edit.getData()
- data["default"] = state
- self.edit.emit(SIGNAL("save"), data)
-
- class EditWindow(QWidget):
- def __init__(self):
- QWidget.__init__(self)
-
- self.setWindowTitle(_("pyLoad ConnectionManager"))
- self.setWindowIcon(QIcon(join(pypath, "icons","logo.png")))
-
- grid = QGridLayout()
-
- nameLabel = QLabel(_("Name:"))
- hostLabel = QLabel(_("Host:"))
- localLabel = QLabel(_("Local:"))
- userLabel = QLabel(_("User:"))
- pwLabel = QLabel(_("Password:"))
- portLabel = QLabel(_("Port:"))
-
- name = QLineEdit()
- host = QLineEdit()
- local = QCheckBox()
- user = QLineEdit()
- password = QLineEdit()
- password.setEchoMode(QLineEdit.Password)
- port = QSpinBox()
- port.setRange(1,10000)
-
- save = QPushButton(_("Save"))
- cancel = QPushButton(_("Cancel"))
-
- grid.addWidget(nameLabel, 0, 0)
- grid.addWidget(name, 0, 1)
- grid.addWidget(localLabel, 1, 0)
- grid.addWidget(local, 1, 1)
- grid.addWidget(hostLabel, 2, 0)
- grid.addWidget(host, 2, 1)
- grid.addWidget(portLabel, 3, 0)
- grid.addWidget(port, 3, 1)
- grid.addWidget(userLabel, 4, 0)
- grid.addWidget(user, 4, 1)
- grid.addWidget(pwLabel, 5, 0)
- grid.addWidget(password, 5, 1)
- grid.addWidget(cancel, 6, 0)
- grid.addWidget(save, 6, 1)
-
- self.setLayout(grid)
- self.controls = {"name": name,
- "host": host,
- "local": local,
- "user": user,
- "password": password,
- "port": port,
- "save": save,
- "cancel": cancel}
-
- self.connect(cancel, SIGNAL("clicked()"), self.hide)
- self.connect(save, SIGNAL("clicked()"), self.slotDone)
- self.connect(local, SIGNAL("stateChanged(int)"), self.slotLocalChanged)
-
- self.id = None
- self.default = None
-
- def setData(self, data):
- if not data: return
-
- self.id = data["id"]
- self.default = data["default"]
- self.controls["name"].setText(data["name"])
- if data["type"] == "local":
- data["local"] = True
- else:
- data["local"] = False
- self.controls["local"].setChecked(data["local"])
- if not data["local"]:
- self.controls["user"].setText(data["user"])
- self.controls["password"].setText(data["password"])
- self.controls["port"].setValue(int(data["port"]))
- self.controls["host"].setText(data["host"])
- self.controls["user"].setDisabled(False)
- self.controls["password"].setDisabled(False)
- self.controls["port"].setDisabled(False)
- self.controls["host"].setDisabled(False)
- else:
- self.controls["user"].setText("")
- self.controls["port"].setValue(1)
- self.controls["host"].setText("")
- self.controls["user"].setDisabled(True)
- self.controls["password"].setDisabled(True)
- self.controls["port"].setDisabled(True)
- self.controls["host"].setDisabled(True)
-
- def slotLocalChanged(self, val):
- if val == 2:
- self.controls["user"].setDisabled(True)
- self.controls["password"].setDisabled(True)
- self.controls["port"].setDisabled(True)
- self.controls["host"].setDisabled(True)
- elif val == 0:
- self.controls["user"].setDisabled(False)
- self.controls["password"].setDisabled(False)
- self.controls["port"].setDisabled(False)
- self.controls["host"].setDisabled(False)
-
- def getData(self):
- d = {}
- d["id"] = self.id
- d["default"] = self.default
- d["name"] = self.controls["name"].text()
- d["local"] = self.controls["local"].isChecked()
- d["user"] = self.controls["user"].text()
- d["password"] = self.controls["password"].text()
- d["host"] = self.controls["host"].text()
- d["port"] = self.controls["port"].value()
- if d["local"]:
- d["type"] = "local"
- else:
- d["type"] = "remote"
- return d
-
- def slotDone(self):
- data = self.getData()
- self.hide()
- self.emit(SIGNAL("save"), data)
-
diff --git a/module/gui/CoreConfigParser.py b/module/gui/CoreConfigParser.py
deleted file mode 100644
index 0d1d298c6..000000000
--- a/module/gui/CoreConfigParser.py
+++ /dev/null
@@ -1,165 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-from os.path import exists
-from os.path import join
-
-
-CONF_VERSION = 1
-
-########################################################################
-class ConfigParser:
-
- #----------------------------------------------------------------------
- def __init__(self, configdir):
- """Constructor"""
- self.configdir = configdir
- self.config = {}
-
- if self.checkVersion():
- self.readConfig()
-
- #----------------------------------------------------------------------
- def checkVersion(self):
-
- if not exists(join(self.configdir, "pyload.conf")):
- return False
- f = open(join(self.configdir, "pyload.conf"), "rb")
- v = f.readline()
- f.close()
- v = v[v.find(":")+1:].strip()
-
- if int(v) < CONF_VERSION:
- return False
-
- return True
-
- #----------------------------------------------------------------------
- def readConfig(self):
- """reads the config file"""
-
- self.config = self.parseConfig(join(self.configdir, "pyload.conf"))
-
-
- #----------------------------------------------------------------------
- def parseConfig(self, config):
- """parses a given configfile"""
-
- f = open(config)
-
- config = f.read()
-
- config = config.split("\n")[1:]
-
- conf = {}
-
- section, option, value, typ, desc = "","","","",""
-
- listmode = False
-
- for line in config:
-
- line = line.rpartition("#") # removes comments
-
- if line[1]:
- line = line[0]
- else:
- line = line[2]
-
- line = line.strip()
-
- try:
-
- if line == "":
- continue
- elif line.endswith(":"):
- section, none, desc = line[:-1].partition('-')
- section = section.strip()
- desc = desc.replace('"', "").strip()
- conf[section] = { "desc" : desc }
- else:
- if listmode:
-
- if line.endswith("]"):
- listmode = False
- line = line.replace("]","")
-
- value += [self.cast(typ, x.strip()) for x in line.split(",") if x]
-
- if not listmode:
- conf[section][option] = { "desc" : desc,
- "type" : typ,
- "value" : value}
-
-
- else:
- content, none, value = line.partition("=")
-
- content, none, desc = content.partition(":")
-
- desc = desc.replace('"', "").strip()
-
- typ, option = content.split()
-
- value = value.strip()
-
- if value.startswith("["):
- if value.endswith("]"):
- listmode = False
- value = value[:-1]
- else:
- listmode = True
-
- value = [self.cast(typ, x.strip()) for x in value[1:].split(",") if x]
- else:
- value = self.cast(typ, value)
-
- if not listmode:
- conf[section][option] = { "desc" : desc,
- "type" : typ,
- "value" : value}
-
- except:
- pass
-
-
- f.close()
- return conf
-
- #----------------------------------------------------------------------
- def cast(self, typ, value):
- """cast value to given format"""
- if type(value) not in (str, unicode):
- return value
-
- if typ == "int":
- return int(value)
- elif typ == "bool":
- return True if value.lower() in ("1","true", "on", "an","yes") else False
- else:
- return value
-
- #----------------------------------------------------------------------
- def get(self, section, option):
- """get value"""
- return self.config[section][option]["value"]
-
- #----------------------------------------------------------------------
- def __getitem__(self, section):
- """provides dictonary like access: c['section']['option']"""
- return Section(self, section)
-
-########################################################################
-class Section:
- """provides dictionary like access for configparser"""
-
- #----------------------------------------------------------------------
- def __init__(self, parser, section):
- """Constructor"""
- self.parser = parser
- self.section = section
-
- #----------------------------------------------------------------------
- def __getitem__(self, item):
- """getitem"""
- return self.parser.get(self.section, item)
diff --git a/module/gui/LinkDock.py b/module/gui/LinkDock.py
deleted file mode 100644
index ac2d4aae5..000000000
--- a/module/gui/LinkDock.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-class NewLinkDock(QDockWidget):
- def __init__(self):
- QDockWidget.__init__(self, "New Links")
- self.setObjectName("New Links Dock")
- self.widget = NewLinkWindow(self)
- self.setWidget(self.widget)
- self.setAllowedAreas(Qt.RightDockWidgetArea|Qt.LeftDockWidgetArea)
- self.hide()
-
- def slotDone(self):
- text = str(self.widget.box.toPlainText())
- lines = text.splitlines()
- self.emit(SIGNAL("done"), lines)
- self.widget.box.clear()
- self.hide()
-
-class NewLinkWindow(QWidget):
- def __init__(self, dock):
- QWidget.__init__(self)
- self.dock = dock
- self.setLayout(QVBoxLayout())
- layout = self.layout()
-
- explanationLabel = QLabel("Select a package and then click Add button.")
- boxLabel = QLabel("Paste URLs here:")
- self.box = QTextEdit()
-
- save = QPushButton("Add")
-
- layout.addWidget(explanationLabel)
- layout.addWidget(boxLabel)
- layout.addWidget(self.box)
- layout.addWidget(save)
-
- self.connect(save, SIGNAL("clicked()"), self.dock.slotDone)
diff --git a/module/gui/MainWindow.py b/module/gui/MainWindow.py
deleted file mode 100644
index c71112e9b..000000000
--- a/module/gui/MainWindow.py
+++ /dev/null
@@ -1,697 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from os.path import join
-
-from module.gui.PackageDock import *
-from module.gui.LinkDock import *
-from module.gui.CaptchaDock import CaptchaDock
-from module.gui.SettingsWidget import SettingsWidget
-
-from module.gui.Collector import CollectorView, Package, Link
-from module.gui.Queue import QueueView
-from module.gui.Overview import OverviewView
-from module.gui.Accounts import AccountView
-from module.gui.AccountEdit import AccountEdit
-
-from module.remote.thriftbackend.ThriftClient import AccountInfo
-
-class MainWindow(QMainWindow):
- def __init__(self, connector):
- """
- set up main window
- """
- QMainWindow.__init__(self)
- #window stuff
- self.setWindowTitle(_("pyLoad Client"))
- self.setWindowIcon(QIcon(join(pypath, "icons","logo.png")))
- self.resize(1000,600)
-
- #layout version
- self.version = 3
-
- #init docks
- self.newPackDock = NewPackageDock()
- self.addDockWidget(Qt.RightDockWidgetArea, self.newPackDock)
- self.connect(self.newPackDock, SIGNAL("done"), self.slotAddPackage)
- self.captchaDock = CaptchaDock()
- self.addDockWidget(Qt.BottomDockWidgetArea, self.captchaDock)
- self.newLinkDock = NewLinkDock()
- self.addDockWidget(Qt.RightDockWidgetArea, self.newLinkDock)
- self.connect(self.newLinkDock, SIGNAL("done"), self.slotAddLinksToPackage)
-
- #central widget, layout
- self.masterlayout = QVBoxLayout()
- lw = QWidget()
- lw.setLayout(self.masterlayout)
- self.setCentralWidget(lw)
-
- #status
- self.statusw = QFrame()
- self.statusw.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
- self.statusw.setLineWidth(2)
- self.statusw.setLayout(QGridLayout())
- #palette = self.statusw.palette()
- #palette.setColor(QPalette.Window, QColor(255, 255, 255))
- #self.statusw.setPalette(palette)
- #self.statusw.setAutoFillBackground(True)
- l = self.statusw.layout()
-
- class BoldLabel(QLabel):
- def __init__(self, text):
- QLabel.__init__(self, text)
- f = self.font()
- f.setBold(True)
- self.setFont(f)
- self.setAlignment(Qt.AlignRight)
-
- class Seperator(QFrame):
- def __init__(self):
- QFrame.__init__(self)
- self.setFrameShape(QFrame.VLine)
- self.setFrameShadow(QFrame.Sunken)
-
- l.addWidget(BoldLabel(_("Packages:")), 0, 0)
- self.packageCount = QLabel("0")
- l.addWidget(self.packageCount, 0, 1)
-
- l.addWidget(BoldLabel(_("Files:")), 0, 2)
- self.fileCount = QLabel("0")
- l.addWidget(self.fileCount, 0, 3)
-
- l.addWidget(BoldLabel(_("Status:")), 0, 4)
- self.status = QLabel("running")
- l.addWidget(self.status, 0, 5)
-
- l.addWidget(BoldLabel(_("Space:")), 0, 6)
- self.space = QLabel("")
- l.addWidget(self.space, 0, 7)
-
- l.addWidget(BoldLabel(_("Speed:")), 0, 8)
- self.speed = QLabel("")
- l.addWidget(self.speed, 0, 9)
-
- #l.addWidget(BoldLabel(_("Max. downloads:")), 0, 9)
- #l.addWidget(BoldLabel(_("Max. chunks:")), 1, 9)
- #self.maxDownloads = QSpinBox()
- #self.maxDownloads.setEnabled(False)
- #self.maxChunks = QSpinBox()
- #self.maxChunks.setEnabled(False)
- #l.addWidget(self.maxDownloads, 0, 10)
- #l.addWidget(self.maxChunks, 1, 10)
-
- #set menubar and statusbar
- self.menubar = self.menuBar()
- #self.statusbar = self.statusBar()
- #self.connect(self.statusbar, SIGNAL("showMsg"), self.statusbar.showMessage)
- #self.serverStatus = QLabel(_("Status: Not Connected"))
- #self.statusbar.addPermanentWidget(self.serverStatus)
-
- #menu
- self.menus = {"file": self.menubar.addMenu(_("File")),
- "connections": self.menubar.addMenu(_("Connections"))}
-
- #menu actions
- self.mactions = {"exit": QAction(_("Exit"), self.menus["file"]),
- "manager": QAction(_("Connection manager"), self.menus["connections"])}
-
- #add menu actions
- self.menus["file"].addAction(self.mactions["exit"])
- self.menus["connections"].addAction(self.mactions["manager"])
-
- #toolbar
- self.actions = {}
- self.init_toolbar()
-
- #tabs
- self.tabw = QTabWidget()
- self.tabs = {"overview": {"w": QWidget()},
- "queue": {"w": QWidget()},
- "collector": {"w": QWidget()},
- "accounts": {"w": QWidget()},
- "settings": {}}
- #self.tabs["settings"]["s"] = QScrollArea()
- self.tabs["settings"]["w"] = SettingsWidget()
- #self.tabs["settings"]["s"].setWidgetResizable(True)
- #self.tabs["settings"]["s"].setWidget(self.tabs["settings"]["w"])
- self.tabs["log"] = {"w":QWidget()}
- self.tabw.addTab(self.tabs["overview"]["w"], _("Overview"))
- self.tabw.addTab(self.tabs["queue"]["w"], _("Queue"))
- self.tabw.addTab(self.tabs["collector"]["w"], _("Collector"))
- self.tabw.addTab(self.tabs["accounts"]["w"], _("Accounts"))
- self.tabw.addTab(self.tabs["settings"]["w"], _("Settings"))
- self.tabw.addTab(self.tabs["log"]["w"], _("Log"))
-
- #init tabs
- self.init_tabs(connector)
-
- #context menus
- self.init_context()
-
- #layout
- self.masterlayout.addWidget(self.tabw)
- self.masterlayout.addWidget(self.statusw)
-
- #signals..
- self.connect(self.mactions["manager"], SIGNAL("triggered()"), self.slotShowConnector)
-
- self.connect(self.tabs["queue"]["view"], SIGNAL('customContextMenuRequested(const QPoint &)'), self.slotQueueContextMenu)
- self.connect(self.tabs["collector"]["package_view"], SIGNAL('customContextMenuRequested(const QPoint &)'), self.slotCollectorContextMenu)
- self.connect(self.tabs["accounts"]["view"], SIGNAL('customContextMenuRequested(const QPoint &)'), self.slotAccountContextMenu)
-
- self.connect(self.tabw, SIGNAL("currentChanged(int)"), self.slotTabChanged)
-
- self.lastAddedID = None
-
- self.connector = connector
-
- def init_toolbar(self):
- """
- create toolbar
- """
- self.toolbar = self.addToolBar(_("Hide Toolbar"))
- self.toolbar.setObjectName("Main Toolbar")
- self.toolbar.setIconSize(QSize(30,30))
- self.toolbar.setMovable(False)
- self.actions["toggle_status"] = self.toolbar.addAction(_("Toggle Pause/Resume"))
- pricon = QIcon()
- pricon.addFile(join(pypath, "icons","toolbar_start.png"), QSize(), QIcon.Normal, QIcon.Off)
- pricon.addFile(join(pypath, "icons","toolbar_pause.png"), QSize(), QIcon.Normal, QIcon.On)
- self.actions["toggle_status"].setIcon(pricon)
- self.actions["toggle_status"].setCheckable(True)
- self.actions["status_stop"] = self.toolbar.addAction(QIcon(join(pypath, "icons","toolbar_stop.png")), _("Stop"))
- self.toolbar.addSeparator()
- self.actions["add"] = self.toolbar.addAction(QIcon(join(pypath, "icons","toolbar_add.png")), _("Add"))
- self.toolbar.addSeparator()
- self.actions["clipboard"] = self.toolbar.addAction(QIcon(join(pypath, "icons","clipboard.png")), _("Check Clipboard"))
- self.actions["clipboard"].setCheckable(True)
-
- self.connect(self.actions["toggle_status"], SIGNAL("toggled(bool)"), self.slotToggleStatus)
- self.connect(self.actions["clipboard"], SIGNAL("toggled(bool)"), self.slotToggleClipboard)
- self.connect(self.actions["status_stop"], SIGNAL("triggered()"), self.slotStatusStop)
- self.addMenu = QMenu()
- packageAction = self.addMenu.addAction(_("Package"))
- containerAction = self.addMenu.addAction(_("Container"))
- accountAction = self.addMenu.addAction(_("Account"))
- linksAction = self.addMenu.addAction(_("Links"))
- self.connect(self.actions["add"], SIGNAL("triggered()"), self.slotAdd)
- self.connect(packageAction, SIGNAL("triggered()"), self.slotShowAddPackage)
- self.connect(containerAction, SIGNAL("triggered()"), self.slotShowAddContainer)
- self.connect(accountAction, SIGNAL("triggered()"), self.slotNewAccount)
- self.connect(linksAction, SIGNAL("triggered()"), self.slotShowAddLinks)
-
- def init_tabs(self, connector):
- """
- create tabs
- """
- #overview
- self.tabs["overview"]["l"] = QGridLayout()
- self.tabs["overview"]["w"].setLayout(self.tabs["overview"]["l"])
- self.tabs["overview"]["view"] = OverviewView(connector)
- self.tabs["overview"]["l"].addWidget(self.tabs["overview"]["view"])
-
- #queue
- self.tabs["queue"]["l"] = QGridLayout()
- self.tabs["queue"]["w"].setLayout(self.tabs["queue"]["l"])
- self.tabs["queue"]["view"] = QueueView(connector)
- self.tabs["queue"]["l"].addWidget(self.tabs["queue"]["view"])
-
- #collector
- toQueue = QPushButton(_("Push selected packages to queue"))
- self.tabs["collector"]["l"] = QGridLayout()
- self.tabs["collector"]["w"].setLayout(self.tabs["collector"]["l"])
- self.tabs["collector"]["package_view"] = CollectorView(connector)
- self.tabs["collector"]["l"].addWidget(self.tabs["collector"]["package_view"], 0, 0)
- self.tabs["collector"]["l"].addWidget(toQueue, 1, 0)
- self.connect(toQueue, SIGNAL("clicked()"), self.slotPushPackageToQueue)
- self.tabs["collector"]["package_view"].setContextMenuPolicy(Qt.CustomContextMenu)
- self.tabs["queue"]["view"].setContextMenuPolicy(Qt.CustomContextMenu)
-
- #log
- self.tabs["log"]["l"] = QGridLayout()
- self.tabs["log"]["w"].setLayout(self.tabs["log"]["l"])
- self.tabs["log"]["text"] = QTextEdit()
- self.tabs["log"]["text"].logOffset = 0
- self.tabs["log"]["text"].setReadOnly(True)
- self.connect(self.tabs["log"]["text"], SIGNAL("append(QString)"), self.tabs["log"]["text"].append)
- self.tabs["log"]["l"].addWidget(self.tabs["log"]["text"])
-
- #accounts
- self.tabs["accounts"]["view"] = AccountView(connector)
- self.tabs["accounts"]["w"].setLayout(QVBoxLayout())
- self.tabs["accounts"]["w"].layout().addWidget(self.tabs["accounts"]["view"])
- newbutton = QPushButton(_("New Account"))
- self.tabs["accounts"]["w"].layout().addWidget(newbutton)
- self.connect(newbutton, SIGNAL("clicked()"), self.slotNewAccount)
- self.tabs["accounts"]["view"].setContextMenuPolicy(Qt.CustomContextMenu)
-
- def init_context(self):
- """
- create context menus
- """
- self.activeMenu = None
- #queue
- self.queueContext = QMenu()
- self.queueContext.buttons = {}
- self.queueContext.item = (None, None)
- self.queueContext.buttons["remove"] = QAction(QIcon(join(pypath, "icons","remove_small.png")), _("Remove"), self.queueContext)
- self.queueContext.buttons["restart"] = QAction(QIcon(join(pypath, "icons","refresh_small.png")), _("Restart"), self.queueContext)
- self.queueContext.buttons["pull"] = QAction(QIcon(join(pypath, "icons","pull_small.png")), _("Pull out"), self.queueContext)
- self.queueContext.buttons["abort"] = QAction(QIcon(join(pypath, "icons","abort.png")), _("Abort"), self.queueContext)
- self.queueContext.buttons["edit"] = QAction(QIcon(join(pypath, "icons","edit_small.png")), _("Edit Name"), self.queueContext)
- self.queueContext.addAction(self.queueContext.buttons["pull"])
- self.queueContext.addAction(self.queueContext.buttons["edit"])
- self.queueContext.addAction(self.queueContext.buttons["remove"])
- self.queueContext.addAction(self.queueContext.buttons["restart"])
- self.queueContext.addAction(self.queueContext.buttons["abort"])
- self.connect(self.queueContext.buttons["remove"], SIGNAL("triggered()"), self.slotRemoveDownload)
- self.connect(self.queueContext.buttons["restart"], SIGNAL("triggered()"), self.slotRestartDownload)
- self.connect(self.queueContext.buttons["pull"], SIGNAL("triggered()"), self.slotPullOutPackage)
- self.connect(self.queueContext.buttons["abort"], SIGNAL("triggered()"), self.slotAbortDownload)
- self.connect(self.queueContext.buttons["edit"], SIGNAL("triggered()"), self.slotEditPackage)
-
- #collector
- self.collectorContext = QMenu()
- self.collectorContext.buttons = {}
- self.collectorContext.item = (None, None)
- self.collectorContext.buttons["remove"] = QAction(QIcon(join(pypath, "icons","remove_small.png")), _("Remove"), self.collectorContext)
- self.collectorContext.buttons["push"] = QAction(QIcon(join(pypath, "icons","push_small.png")), _("Push to queue"), self.collectorContext)
- self.collectorContext.buttons["edit"] = QAction(QIcon(join(pypath, "icons","edit_small.png")), _("Edit Name"), self.collectorContext)
- self.collectorContext.buttons["restart"] = QAction(QIcon(join(pypath, "icons","refresh_small.png")), _("Restart"), self.collectorContext)
- self.collectorContext.buttons["refresh"] = QAction(QIcon(join(pypath, "icons","refresh1_small.png")),_("Refresh Status"), self.collectorContext)
- self.collectorContext.addAction(self.collectorContext.buttons["push"])
- self.collectorContext.addSeparator()
- self.collectorContext.buttons["add"] = self.collectorContext.addMenu(QIcon(join(pypath, "icons","add_small.png")), _("Add"))
- self.collectorContext.addAction(self.collectorContext.buttons["edit"])
- self.collectorContext.addAction(self.collectorContext.buttons["remove"])
- self.collectorContext.addAction(self.collectorContext.buttons["restart"])
- self.collectorContext.addSeparator()
- self.collectorContext.addAction(self.collectorContext.buttons["refresh"])
- packageAction = self.collectorContext.buttons["add"].addAction(_("Package"))
- containerAction = self.collectorContext.buttons["add"].addAction(_("Container"))
- linkAction = self.collectorContext.buttons["add"].addAction(_("Links"))
- self.connect(self.collectorContext.buttons["remove"], SIGNAL("triggered()"), self.slotRemoveDownload)
- self.connect(self.collectorContext.buttons["push"], SIGNAL("triggered()"), self.slotPushPackageToQueue)
- self.connect(self.collectorContext.buttons["edit"], SIGNAL("triggered()"), self.slotEditPackage)
- self.connect(self.collectorContext.buttons["restart"], SIGNAL("triggered()"), self.slotRestartDownload)
- self.connect(self.collectorContext.buttons["refresh"], SIGNAL("triggered()"), self.slotRefreshPackage)
- self.connect(packageAction, SIGNAL("triggered()"), self.slotShowAddPackage)
- self.connect(containerAction, SIGNAL("triggered()"), self.slotShowAddContainer)
- self.connect(linkAction, SIGNAL("triggered()"), self.slotShowAddLinks)
-
- self.accountContext = QMenu()
- self.accountContext.buttons = {}
- self.accountContext.buttons["add"] = QAction(QIcon(join(pypath, "icons","add_small.png")), _("Add"), self.accountContext)
- self.accountContext.buttons["remove"] = QAction(QIcon(join(pypath, "icons","remove_small.png")), _("Remove"), self.accountContext)
- self.accountContext.buttons["edit"] = QAction(QIcon(join(pypath, "icons","edit_small.png")), _("Edit"), self.accountContext)
- self.accountContext.addAction(self.accountContext.buttons["add"])
- self.accountContext.addAction(self.accountContext.buttons["edit"])
- self.accountContext.addAction(self.accountContext.buttons["remove"])
- self.connect(self.accountContext.buttons["add"], SIGNAL("triggered()"), self.slotNewAccount)
- self.connect(self.accountContext.buttons["edit"], SIGNAL("triggered()"), self.slotEditAccount)
- self.connect(self.accountContext.buttons["remove"], SIGNAL("triggered()"), self.slotRemoveAccount)
-
- def slotToggleStatus(self, status):
- """
- pause/start toggle (toolbar)
- """
- self.emit(SIGNAL("setDownloadStatus"), status)
-
- def slotStatusStop(self):
- """
- stop button (toolbar)
- """
- self.emit(SIGNAL("stopAllDownloads"))
-
- def slotAdd(self):
- """
- add button (toolbar)
- show context menu (choice: links/package)
- """
- self.addMenu.exec_(QCursor.pos())
-
- def slotShowAddPackage(self):
- """
- action from add-menu
- show new-package dock
- """
- self.tabw.setCurrentIndex(1)
- self.newPackDock.show()
-
- def slotShowAddLinks(self):
- """
- action from add-menu
- show new-links dock
- """
- self.tabw.setCurrentIndex(1)
- self.newLinkDock.show()
-
- def slotShowConnector(self):
- """
- connectionmanager action triggered
- let main to the stuff
- """
- self.emit(SIGNAL("connector"))
-
- def slotAddPackage(self, name, links, password=None):
- """
- new package
- let main to the stuff
- """
- self.emit(SIGNAL("addPackage"), name, links, password)
-
- def slotAddLinksToPackage(self, links):
- """
- adds links to currently selected package
- only in collector
- """
- if self.tabw.currentIndex() != 1:
- return
-
- smodel = self.tabs["collector"]["package_view"].selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- if isinstance(item, Package):
- self.connector.proxy.addFiles(item.id, links)
- break
-
- def slotShowAddContainer(self):
- """
- action from add-menu
- show file selector, emit upload
- """
- typeStr = ";;".join([
- _("All Container Types (%s)") % "*.dlc *.ccf *.rsdf *.txt",
- _("DLC (%s)") % "*.dlc",
- _("CCF (%s)") % "*.ccf",
- _("RSDF (%s)") % "*.rsdf",
- _("Text Files (%s)") % "*.txt"
- ])
- fileNames = QFileDialog.getOpenFileNames(self, _("Open container"), "", typeStr)
- for name in fileNames:
- self.emit(SIGNAL("addContainer"), str(name))
-
- def slotPushPackageToQueue(self):
- """
- push collector pack to queue
- get child ids
- let main to the rest
- """
- smodel = self.tabs["collector"]["package_view"].selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- if isinstance(item, Package):
- self.emit(SIGNAL("pushPackageToQueue"), item.id)
- else:
- self.emit(SIGNAL("pushPackageToQueue"), item.package.id)
-
- def saveWindow(self):
- """
- get window state/geometry
- pass data to main
- """
- state_raw = self.saveState(self.version)
- geo_raw = self.saveGeometry()
-
- state = str(state_raw.toBase64())
- geo = str(geo_raw.toBase64())
-
- self.emit(SIGNAL("saveMainWindow"), state, geo)
-
- def closeEvent(self, event):
- """
- somebody wants to close me!
- let me first save my state
- """
- self.saveWindow()
- event.ignore()
- self.hide()
- self.emit(SIGNAL("hidden"))
-
- # quit when no tray is available
- if not QSystemTrayIcon.isSystemTrayAvailable():
- self.emit(SIGNAL("Quit"))
-
- def restoreWindow(self, state, geo):
- """
- restore window state/geometry
- """
- state = QByteArray(state)
- geo = QByteArray(geo)
-
- state_raw = QByteArray.fromBase64(state)
- geo_raw = QByteArray.fromBase64(geo)
-
- self.restoreState(state_raw, self.version)
- self.restoreGeometry(geo_raw)
-
- def slotQueueContextMenu(self, pos):
- """
- custom context menu in queue view requested
- """
- globalPos = self.tabs["queue"]["view"].mapToGlobal(pos)
- i = self.tabs["queue"]["view"].indexAt(pos)
- if not i:
- return
- item = i.internalPointer()
- menuPos = QCursor.pos()
- menuPos.setX(menuPos.x()+2)
- self.activeMenu = self.queueContext
- showAbort = False
- if isinstance(item, Link) and item.data["downloading"]:
- showAbort = True
- elif isinstance(item, Package):
- for child in item.children:
- if child.data["downloading"]:
- showAbort = True
- break
- if showAbort:
- self.queueContext.buttons["abort"].setEnabled(True)
- else:
- self.queueContext.buttons["abort"].setEnabled(False)
- if isinstance(item, Package):
- self.queueContext.index = i
- #self.queueContext.buttons["remove"].setEnabled(True)
- #self.queueContext.buttons["restart"].setEnabled(True)
- self.queueContext.buttons["pull"].setEnabled(True)
- self.queueContext.buttons["edit"].setEnabled(True)
- elif isinstance(item, Link):
- self.collectorContext.index = i
- self.collectorContext.buttons["edit"].setEnabled(False)
- self.collectorContext.buttons["remove"].setEnabled(True)
- self.collectorContext.buttons["push"].setEnabled(False)
- self.collectorContext.buttons["restart"].setEnabled(True)
- else:
- self.queueContext.index = None
- #self.queueContext.buttons["remove"].setEnabled(False)
- #self.queueContext.buttons["restart"].setEnabled(False)
- self.queueContext.buttons["pull"].setEnabled(False)
- self.queueContext.buttons["edit"].setEnabled(False)
- self.queueContext.exec_(menuPos)
-
- def slotCollectorContextMenu(self, pos):
- """
- custom context menu in package collector view requested
- """
- globalPos = self.tabs["collector"]["package_view"].mapToGlobal(pos)
- i = self.tabs["collector"]["package_view"].indexAt(pos)
- if not i:
- return
- item = i.internalPointer()
- menuPos = QCursor.pos()
- menuPos.setX(menuPos.x()+2)
- self.activeMenu = self.collectorContext
- if isinstance(item, Package):
- self.collectorContext.index = i
- self.collectorContext.buttons["edit"].setEnabled(True)
- self.collectorContext.buttons["remove"].setEnabled(True)
- self.collectorContext.buttons["push"].setEnabled(True)
- self.collectorContext.buttons["restart"].setEnabled(True)
- elif isinstance(item, Link):
- self.collectorContext.index = i
- self.collectorContext.buttons["edit"].setEnabled(False)
- self.collectorContext.buttons["remove"].setEnabled(True)
- self.collectorContext.buttons["push"].setEnabled(False)
- self.collectorContext.buttons["restart"].setEnabled(True)
- else:
- self.collectorContext.index = None
- self.collectorContext.buttons["edit"].setEnabled(False)
- self.collectorContext.buttons["remove"].setEnabled(False)
- self.collectorContext.buttons["push"].setEnabled(False)
- self.collectorContext.buttons["restart"].setEnabled(False)
- self.collectorContext.exec_(menuPos)
-
- def slotLinkCollectorContextMenu(self, pos):
- """
- custom context menu in link collector view requested
- """
- pass
-
- def slotRestartDownload(self):
- """
- restart download action is triggered
- """
- smodel = self.tabs["queue"]["view"].selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- self.emit(SIGNAL("restartDownload"), item.id, isinstance(item, Package))
-
- def slotRemoveDownload(self):
- """
- remove download action is triggered
- """
- if self.activeMenu == self.queueContext:
- view = self.tabs["queue"]["view"]
- else:
- view = self.tabs["collector"]["package_view"]
- smodel = view.selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- self.emit(SIGNAL("removeDownload"), item.id, isinstance(item, Package))
-
- def slotToggleClipboard(self, status):
- """
- check clipboard (toolbar)
- """
- self.emit(SIGNAL("setClipboardStatus"), status)
-
- def slotEditPackage(self):
- # in Queue, only edit name
- if self.activeMenu == self.queueContext:
- view = self.tabs["queue"]["view"]
- else:
- view = self.tabs["collector"]["package_view"]
- view.edit(self.activeMenu.index)
-
- def slotEditCommit(self, editor):
- self.emit(SIGNAL("changePackageName"), self.activeMenu.index.internalPointer().id, editor.text())
-
- def slotPullOutPackage(self):
- """
- pull package out of the queue
- """
- smodel = self.tabs["queue"]["view"].selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- if isinstance(item, Package):
- self.emit(SIGNAL("pullOutPackage"), item.id)
- else:
- self.emit(SIGNAL("pullOutPackage"), item.package.id)
-
- def slotAbortDownload(self):
- view = self.tabs["queue"]["view"]
- smodel = view.selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- self.emit(SIGNAL("abortDownload"), item.id, isinstance(item, Package))
-
- # TODO disabled because changing desktop on linux, main window disappears
- #def changeEvent(self, e):
- # if e.type() == QEvent.WindowStateChange and self.isMinimized():
- # e.ignore()
- # self.hide()
- # self.emit(SIGNAL("hidden"))
- # else:
- # super(MainWindow, self).changeEvent(e)
-
- def slotTabChanged(self, index):
- if index == 2:
- self.emit(SIGNAL("reloadAccounts"))
- elif index == 3:
- self.tabs["settings"]["w"].loadConfig()
-
- def slotRefreshPackage(self):
- smodel = self.tabs["collector"]["package_view"].selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- pid = item.id
- if isinstance(item, Link):
- pid = item.package.id
- self.emit(SIGNAL("refreshStatus"), pid)
-
- def slotNewAccount(self):
- types = self.connector.proxy.getAccountTypes()
- self.accountEdit = AccountEdit.newAccount(types)
-
- #TODO make more easy n1, n2, n3
- def save(data):
- if data["password"]:
- self.accountEdit.close()
- n1 = data["acctype"]
- n2 = data["login"]
- n3 = data["password"]
- self.connector.updateAccount(n1, n2, n3, None)
-
- self.accountEdit.connect(self.accountEdit, SIGNAL("done"), save)
- self.accountEdit.show()
-
- def slotEditAccount(self):
- types = self.connector.getAccountTypes()
-
- data = self.tabs["accounts"]["view"].selectedIndexes()
- if len(data) < 1:
- return
-
- data = data[0].internalPointer()
-
- self.accountEdit = AccountEdit.editAccount(types, data)
-
- #TODO make more easy n1, n2, n3
- #TODO reload accounts tab after insert of edit account
- #TODO if account does not exist give error
- def save(data):
- self.accountEdit.close()
- n1 = data["acctype"]
- n2 = data["login"]
- if data["password"]:
- n3 = data["password"]
- self.connector.updateAccount(n1, n2, n3, None)
-
- self.accountEdit.connect(self.accountEdit, SIGNAL("done"), save)
- self.accountEdit.show()
-
- def slotRemoveAccount(self):
- data = self.tabs["accounts"]["view"].selectedIndexes()
- if len(data) < 1:
- return
-
- data = data[0].internalPointer()
-
- self.connector.removeAccount(data.type, data.login)
-
- def slotAccountContextMenu(self, pos):
- globalPos = self.tabs["accounts"]["view"].mapToGlobal(pos)
- i = self.tabs["accounts"]["view"].indexAt(pos)
- if not i:
- return
-
- data = i.internalPointer()
-
- if data is None:
- self.accountContext.buttons["edit"].setEnabled(False)
- self.accountContext.buttons["remove"].setEnabled(False)
- else:
- self.accountContext.buttons["edit"].setEnabled(True)
- self.accountContext.buttons["remove"].setEnabled(True)
-
- menuPos = QCursor.pos()
- menuPos.setX(menuPos.x()+2)
- self.accountContext.exec_(menuPos)
diff --git a/module/gui/Overview.py b/module/gui/Overview.py
deleted file mode 100644
index 183383b5e..000000000
--- a/module/gui/Overview.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from module.utils import formatSpeed, formatSize
-
-class OverviewModel(QAbstractListModel):
- PackageName = 10
- Progress = 11
- PartsFinished = 12
- Parts = 13
- ETA = 14
- Speed = 15
- CurrentSize = 16
- MaxSize = 17
- Status = 18
-
- def __init__(self, view, connector):
- QAbstractListModel.__init__(self)
-
- self.packages = []
-
- def queueChanged(self): #dirty..
- self.beginResetModel()
-
- self.packages = []
-
- def partsFinished(p):
- f = 0
- for c in p.children:
- if c.data["status"] == 0:
- f += 1
- return f
-
- def maxSize(p):
- ms = 0
- cs = 0
- for c in p.children:
- try:
- s = c.data["downloading"]["size"]
- except:
- s = c.data["size"]
- if c.data["downloading"]:
- cs += s - c.data["downloading"]["bleft"]
- elif self.queue.getProgress(c, False) == 100:
- cs += s
- ms += s
- return ms, cs
-
- def getProgress(p):
- for c in p.children:
- if c.data["status"] == 13:
- pass # TODO return _("Unpacking"), int(c.data["progress"])
- return _("Downloading"), self.queue.getProgress(p)
-
- d = self.queue._data
- for p in d:
- status, progress = getProgress(p)
- maxsize, currentsize = maxSize(p)
- speed = self.queue.getSpeed(p)
- if speed:
- eta = (maxsize - (maxsize * (progress/100.0)))/speed
- else:
- eta = 0
- if not speed and not progress:
- status = _("Queued")
- info = {
- OverviewModel.PackageName: p.data["name"],
- OverviewModel.Progress: progress,
- OverviewModel.PartsFinished: partsFinished(p),
- OverviewModel.Parts: len(p.children),
- OverviewModel.ETA: int(eta),
- OverviewModel.Speed: speed,
- OverviewModel.CurrentSize: currentsize,
- OverviewModel.MaxSize: maxsize,
- OverviewModel.Status: status,
- }
-
- self.packages.append(info)
-
- self.endResetModel()
-
- def headerData(self, section, orientation, role=Qt.DisplayRole):
- return QVariant(_("Package"))
-
- def rowCount(self, parent=QModelIndex()):
- return len(self.packages)
-
- def data(self, index, role=Qt.DisplayRole):
- if role in [OverviewModel.PackageName, OverviewModel.Progress, OverviewModel.PartsFinished, OverviewModel.Parts, OverviewModel.ETA, OverviewModel.Speed, OverviewModel.CurrentSize, OverviewModel.MaxSize, OverviewModel.Status]:
- return QVariant(self.packages[index.row()][role])
- return QVariant()
-
-class OverviewView(QListView):
- def __init__(self, connector):
- QListView.__init__(self)
- self.setModel(OverviewModel(self, connector))
-
- self.setAlternatingRowColors(True)
- self.delegate = OverviewDelegate(self)
- self.setItemDelegate(self.delegate)
-
-class OverviewDelegate(QItemDelegate):
- def __init__(self, parent):
- QItemDelegate.__init__(self, parent)
- self.parent = parent
- self.model = parent.model()
-
- def paint(self, painter, option, index):
- option.rect.setHeight(59+16)
- option.rect.setWidth(self.parent.width()-20)
-
- #if option.state & QStyle.State_Selected:
- # painter.fillRect(option.rect, option.palette.color(QPalette.Highlight))
-
- packagename = index.data(OverviewModel.PackageName).toString()
- partsf = index.data(OverviewModel.PartsFinished).toString()
- parts = index.data(OverviewModel.Parts).toString()
- eta = int(index.data(OverviewModel.ETA).toString())
- speed = index.data(OverviewModel.Speed).toString() or 0
- progress = int(index.data(OverviewModel.Progress).toString())
- currentSize = int(index.data(OverviewModel.CurrentSize).toString())
- maxSize = int(index.data(OverviewModel.MaxSize).toString())
- status = index.data(OverviewModel.Status).toString()
-
- def formatEta(seconds): #TODO add to utils
- if seconds <= 0: return ""
- hours, seconds = divmod(seconds, 3600)
- minutes, seconds = divmod(seconds, 60)
- return _("ETA: ") + "%.2i:%.2i:%.2i" % (hours, minutes, seconds)
-
- statusline = QString(_("Parts: ") + "%s/%s" % (partsf, parts))
- if partsf == parts:
- speedline = _("Finished")
- elif not status == _("Downloading"):
- speedline = QString(status)
- else:
- speedline = QString(formatEta(eta) + " " + _("Speed: %s") % formatSpeed(speed))
-
- if progress in (0,100):
- sizeline = QString(_("Size:") + "%s" % formatSize(maxSize))
- else:
- sizeline = QString(_("Size:") + "%s / %s" % (formatSize(currentSize), formatSize(maxSize)))
-
- f = painter.font()
- f.setPointSize(12)
- f.setBold(True)
- painter.setFont(f)
-
- r = option.rect.adjusted(4, 4, -4, -4)
- painter.drawText(r.left(), r.top(), r.width(), r.height(), Qt.AlignTop | Qt.AlignLeft, packagename)
- newr = painter.boundingRect(r.left(), r.top(), r.width(), r.height(), Qt.AlignTop | Qt.AlignLeft, packagename)
-
- f.setPointSize(10)
- f.setBold(False)
- painter.setFont(f)
-
- painter.drawText(r.left(), newr.bottom()+5, r.width(), r.height(), Qt.AlignTop | Qt.AlignLeft, statusline)
- painter.drawText(r.left(), newr.bottom()+5, r.width(), r.height(), Qt.AlignTop | Qt.AlignHCenter, sizeline)
- painter.drawText(r.left(), newr.bottom()+5, r.width(), r.height(), Qt.AlignTop | Qt.AlignRight, speedline)
- newr = painter.boundingRect(r.left(), newr.bottom()+2, r.width(), r.height(), Qt.AlignTop | Qt.AlignLeft, statusline)
- newr.setTop(newr.bottom()+8)
- newr.setBottom(newr.top()+20)
- newr.setRight(self.parent.width()-25)
-
- f.setPointSize(10)
- painter.setFont(f)
-
- opts = QStyleOptionProgressBarV2()
- opts.maximum = 100
- opts.minimum = 0
- opts.progress = progress
- opts.rect = newr
- opts.textVisible = True
- opts.textAlignment = Qt.AlignCenter
- opts.text = QString.number(opts.progress) + "%"
- QApplication.style().drawControl(QStyle.CE_ProgressBar, opts, painter)
-
- def sizeHint(self, option, index):
- return QSize(self.parent.width()-22, 59+16)
diff --git a/module/gui/PackageDock.py b/module/gui/PackageDock.py
deleted file mode 100644
index 73db8f177..000000000
--- a/module/gui/PackageDock.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-import re
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-class NewPackageDock(QDockWidget):
- def __init__(self):
- QDockWidget.__init__(self, _("New Package"))
- self.setObjectName("New Package Dock")
- self.widget = NewPackageWindow(self)
- self.setWidget(self.widget)
- self.setAllowedAreas(Qt.RightDockWidgetArea|Qt.LeftDockWidgetArea)
- self.hide()
-
- def slotDone(self):
- text = str(self.widget.box.toPlainText())
- pw = str(self.widget.passwordInput.text())
- if not pw:
- pw = None
- lines = []
- for line in text.splitlines():
- line = line.strip()
- if not line:
- continue
- lines.append(line)
- self.emit(SIGNAL("done"), str(self.widget.nameInput.text()), lines, pw)
- self.widget.nameInput.setText("")
- self.widget.passwordInput.setText("")
- self.widget.box.clear()
- self.hide()
-
- def parseUri(self):
-
- text=str(self.widget.box.toPlainText())
- self.widget.box.setText("")
- result = re.findall(r"(?:ht|f)tps?:\/\/[a-zA-Z0-9\-\.\/\?=_&%#]+[<| |\"|\'|\r|\n|\t]{1}", text)
- for url in result:
- if "\n" or "\t" or "\r" or "\"" or "<" or "'" in url:
- url = url[:-1]
- self.widget.box.append("%s " % url)
-
-class NewPackageWindow(QWidget):
- def __init__(self, dock):
- QWidget.__init__(self)
- self.dock = dock
- self.setLayout(QGridLayout())
- layout = self.layout()
-
- nameLabel = QLabel(_("Name"))
- nameInput = QLineEdit()
- passwordLabel = QLabel(_("Password"))
- passwordInput = QLineEdit()
-
- linksLabel = QLabel(_("Links in this Package"))
-
- self.box = QTextEdit()
- self.nameInput = nameInput
- self.passwordInput = passwordInput
-
- save = QPushButton(_("Create"))
- parseUri = QPushButton(_("Filter URLs"))
-
- layout.addWidget(nameLabel, 0, 0)
- layout.addWidget(nameInput, 0, 1)
- layout.addWidget(passwordLabel, 1, 0)
- layout.addWidget(passwordInput, 1, 1)
- layout.addWidget(linksLabel, 2, 0, 1, 2)
- layout.addWidget(self.box, 3, 0, 1, 2)
- layout.addWidget(parseUri, 4, 0, 1, 2)
- layout.addWidget(save, 5, 0, 1, 2)
-
- self.connect(save, SIGNAL("clicked()"), self.dock.slotDone)
- self.connect(parseUri, SIGNAL("clicked()"), self.dock.parseUri) \ No newline at end of file
diff --git a/module/gui/Queue.py b/module/gui/Queue.py
deleted file mode 100644
index 0a0cbb810..000000000
--- a/module/gui/Queue.py
+++ /dev/null
@@ -1,390 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from time import time
-
-from module.remote.thriftbackend.ThriftClient import Destination
-from module.gui.Collector import CollectorModel, Package, Link, CollectorView, statusMapReverse
-from module.utils import formatSize, formatSpeed
-
-class QueueModel(CollectorModel):
- """
- model for the queue view, inherits from CollectorModel
- """
-
- def __init__(self, view, connector):
- CollectorModel.__init__(self, view, connector)
- self.cols = 6
- self.wait_dict = {}
-
- self.updater = self.QueueUpdater(self.interval)
- self.connect(self.updater, SIGNAL("update()"), self.update)
-
- class QueueUpdater(QObject):
- """
- timer which emits signal for a download status reload
- @TODO: make intervall configurable
- """
-
- def __init__(self, interval):
- QObject.__init__(self)
-
- self.interval = interval
- self.timer = QTimer()
- self.timer.connect(self.timer, SIGNAL("timeout()"), self, SIGNAL("update()"))
-
- def start(self):
- self.timer.start(1000)
-
- def stop(self):
- self.timer.stop()
-
- def start(self):
- self.updater.start()
-
- def stop(self):
- self.updater.stop()
-
- def fullReload(self):
- """
- reimplements CollectorModel.fullReload, because we want the Queue data
- """
- self._data = []
- order = self.connector.getPackageOrder(Destination.Queue)
- self.beginInsertRows(QModelIndex(), 0, len(order.values()))
- for position, pid in order.iteritems():
- pack = self.connector.getPackageData(pid)
- package = Package(pack)
- self._data.append(package)
- self._data = sorted(self._data, key=lambda p: p.data["order"])
- self.endInsertRows()
- self.updateCount()
-
- def insertEvent(self, event):
- """
- wrap CollectorModel.insertEvent to update the element count
- """
- CollectorModel.insertEvent(self, event)
- self.updateCount()
-
- def removeEvent(self, event):
- """
- wrap CollectorModel.removeEvent to update the element count
- """
- CollectorModel.removeEvent(self, event)
- self.updateCount()
-
- def updateEvent(self, event):
- """
- wrap CollectorModel.updateEvent to update the element count
- """
- CollectorModel.updateEvent(self, event)
- self.updateCount()
-
- def updateCount(self):
- """
- calculate package- and filecount for statusbar,
- ugly?: Overview connects to this signal for updating
- """
- packageCount = len(self._data)
- fileCount = 0
- for p in self._data:
- fileCount += len(p.children)
- self.mutex.unlock()
- self.emit(SIGNAL("updateCount"), packageCount, fileCount)
- self.mutex.lock()
-
- def update(self):
- """
- update slot for download status updating
- """
- locker = QMutexLocker(self.mutex)
- downloading = self.connector.statusDownloads()
- if not downloading:
- return
- for p, pack in enumerate(self._data):
- for d in downloading:
- child = pack.getChild(d.fid)
- if child:
- dd = {
- "name": d.name,
- "speed": d.speed,
- "eta": d.eta,
- "format_eta": d.format_eta,
- "bleft": d.bleft,
- "size": d.size,
- "format_size": d.format_size,
- "percent": d.percent,
- "status": d.status,
- "statusmsg": d.statusmsg,
- "format_wait": d.format_wait,
- "wait_until": d.wait_until
- }
- child.data["downloading"] = dd
- k = pack.getChildKey(d.fid)
- self.emit(SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"), self.index(k, 0, self.index(p, 0)), self.index(k, self.cols, self.index(p, self.cols)))
- self.updateCount()
-
- def headerData(self, section, orientation, role=Qt.DisplayRole):
- """
- returns column heading
- """
- if orientation == Qt.Horizontal and role == Qt.DisplayRole:
- if section == 0:
- return QVariant(_("Name"))
- elif section == 2:
- return QVariant(_("Status"))
- elif section == 1:
- return QVariant(_("Plugin"))
- elif section == 3:
- return QVariant(_("Size"))
- elif section == 4:
- return QVariant(_("ETA"))
- elif section == 5:
- return QVariant(_("Progress"))
- return QVariant()
-
- def getWaitingProgress(self, item):
- """
- returns time to wait, caches startingtime to provide progress
- """
- locker = QMutexLocker(self.mutex)
- if isinstance(item, Link):
- if item.data["status"] == 5 and item.data["downloading"]:
- until = float(item.data["downloading"]["wait_until"])
- try:
- since, until_old = self.wait_dict[item.id]
- if not until == until_old:
- raise Exception
- except:
- since = time()
- self.wait_dict[item.id] = since, until
- since = float(since)
- max_wait = float(until-since)
- rest = int(until-time())
- if rest < 0:
- return 0, None
- res = 100/max_wait
- perc = rest*res
- return perc, rest
- return None
-
- def getProgress(self, item, locked=True):
- """
- return download progress, locks by default
- since it's used in already locked calls,
- it provides an option to not lock
- """
- if locked:
- locker = QMutexLocker(self.mutex)
- if isinstance(item, Link):
- try:
- if item.data["status"] == 0:
- return 100
- return int(item.data["downloading"]["percent"])
- except:
- return 0
- elif isinstance(item, Package):
- count = len(item.children)
- perc_sum = 0
- for child in item.children:
- try:
- if child.data["status"] == 0: #completed
- perc_sum += 100
- perc_sum += int(child.data["downloading"]["percent"])
- except:
- pass
- if count == 0:
- return 0
- return perc_sum/count
- return 0
-
- def getSpeed(self, item):
- """
- calculate download speed
- """
- if isinstance(item, Link):
- if item.data["downloading"]:
- return int(item.data["downloading"]["speed"])
- elif isinstance(item, Package):
- count = len(item.children)
- speed_sum = 0
- all_waiting = True
- running = False
- for child in item.children:
- val = 0
- if child.data["downloading"]:
- if not child.data["statusmsg"] == "waiting":
- all_waiting = False
- val = int(child.data["downloading"]["speed"])
- running = True
- speed_sum += val
- if count == 0 or not running or all_waiting:
- return None
- return speed_sum
- return None
-
- def data(self, index, role=Qt.DisplayRole):
- """
- return cell data
- """
- if not index.isValid():
- return QVariant()
- if role == Qt.DisplayRole:
- if index.column() == 0:
- return QVariant(index.internalPointer().data["name"])
- elif index.column() == 1:
- item = index.internalPointer()
- plugins = []
- if isinstance(item, Package):
- for child in item.children:
- if not child.data["plugin"] in plugins:
- plugins.append(child.data["plugin"])
- else:
- plugins.append(item.data["plugin"])
- return QVariant(", ".join(plugins))
- elif index.column() == 2:
- item = index.internalPointer()
- status = 0
- speed = self.getSpeed(item)
- if isinstance(item, Package):
- for child in item.children:
- if child.data["status"] > status:
- status = child.data["status"]
- else:
- status = item.data["status"]
-
- if speed is None or status == 7 or status == 10 or status == 5:
- return QVariant(self.translateStatus(statusMapReverse[status]))
- else:
- return QVariant("%s (%s)" % (self.translateStatus(statusMapReverse[status]), formatSpeed(speed)))
- elif index.column() == 3:
- item = index.internalPointer()
- if isinstance(item, Link):
- if item.data["status"] == 0: #TODO needs change??
- #self.getProgress(item, False) == 100:
- return QVariant(formatSize(item.data["size"]))
- elif self.getProgress(item, False) == 0:
- try:
- return QVariant("%s / %s" % (formatSize(item.data["size"]-item.data["downloading"]["bleft"]), formatSize(item.data["size"])))
- except:
- return QVariant("0 B / %s" % formatSize(item.data["size"]))
- else:
- try:
- return QVariant("%s / %s" % (formatSize(item.data["size"]-item.data["downloading"]["bleft"]), formatSize(item.data["size"])))
- except:
- return QVariant("? / %s" % formatSize(item.data["size"]))
- else:
- ms = 0
- cs = 0
- for c in item.children:
- try:
- s = c.data["downloading"]["size"]
- except:
- s = c.data["size"]
- if c.data["downloading"]:
- cs += s - c.data["downloading"]["bleft"]
- elif self.getProgress(c, False) == 100:
- cs += s
- ms += s
- if cs == 0 or cs == ms:
- return QVariant(formatSize(ms))
- else:
- return QVariant("%s / %s" % (formatSize(cs), formatSize(ms)))
- elif index.column() == 4:
- item = index.internalPointer()
- if isinstance(item, Link):
- if item.data["downloading"]:
- return QVariant(item.data["downloading"]["format_eta"])
- elif role == Qt.EditRole:
- if index.column() == 0:
- return QVariant(index.internalPointer().data["name"])
- return QVariant()
-
- def flags(self, index):
- """
- cell flags
- """
- if index.column() == 0 and self.parent(index) == QModelIndex():
- return Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled
- return Qt.ItemIsSelectable | Qt.ItemIsEnabled
-
-class QueueView(CollectorView):
- """
- view component for queue
- """
-
- def __init__(self, connector):
- CollectorView.__init__(self, connector)
- self.setModel(QueueModel(self, connector))
-
- self.setColumnWidth(0, 300)
- self.setColumnWidth(1, 100)
- self.setColumnWidth(2, 140)
- self.setColumnWidth(3, 180)
- self.setColumnWidth(4, 70)
-
- self.setEditTriggers(QAbstractItemView.NoEditTriggers)
-
- self.delegate = QueueProgressBarDelegate(self, self.model())
- self.setItemDelegateForColumn(5, self.delegate)
-
-class QueueProgressBarDelegate(QItemDelegate):
- """
- used to display a progressbar in the progress cell
- """
-
- def __init__(self, parent, queue):
- QItemDelegate.__init__(self, parent)
- self.queue = queue
-
- def paint(self, painter, option, index):
- """
- paint the progressbar
- """
- if not index.isValid():
- return
- if index.column() == 5:
- item = index.internalPointer()
- w = self.queue.getWaitingProgress(item)
- wait = None
- if w:
- progress = w[0]
- wait = w[1]
- else:
- progress = self.queue.getProgress(item)
- opts = QStyleOptionProgressBarV2()
- opts.maximum = 100
- opts.minimum = 0
- opts.progress = progress
- opts.rect = option.rect
- opts.rect.setRight(option.rect.right()-1)
- opts.rect.setHeight(option.rect.height()-1)
- opts.textVisible = True
- opts.textAlignment = Qt.AlignCenter
- if not wait is None:
- opts.text = QString(_("waiting %d seconds") % (wait,))
- else:
- opts.text = QString.number(opts.progress) + "%"
- QApplication.style().drawControl(QStyle.CE_ProgressBar, opts, painter)
- return
- QItemDelegate.paint(self, painter, option, index)
-
diff --git a/module/gui/SettingsWidget.py b/module/gui/SettingsWidget.py
deleted file mode 100644
index cd22a7b9e..000000000
--- a/module/gui/SettingsWidget.py
+++ /dev/null
@@ -1,202 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-from sip import delete
-
-
-class SettingsWidget(QWidget):
- def __init__(self):
- QWidget.__init__(self)
- self.connector = None
- self.sections = {}
- self.psections = {}
- self.data = None
- self.pdata = None
- self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
-
- def setConnector(self, connector):
- self.connector = connector
-
- def loadConfig(self):
- if self.sections and self.psections:
- self.data = self.connector.getConfig()
- self.pdata = self.connector.getPluginConfig()
-
- self.reloadSection(self.sections, self.data)
- self.reloadSection(self.psections, self.pdata)
-
- return
-
- if self.layout():
- delete(self.layout())
-
- for s in self.sections.values()+self.psections.values():
- delete(s)
-
- self.sections = {}
- self.setLayout(QVBoxLayout())
- self.clearConfig()
- layout = self.layout()
- layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
-
- general = QTabWidget()
- self.general = general
-
- plugins = QTabWidget()
- self.plugins = plugins
-
- tab = QTabWidget()
- self.tab = tab
-
- gw = QWidget()
- gw.setLayout(QVBoxLayout())
- gw.layout().addWidget(self.general)
- pw = QWidget()
- pw.setLayout(QVBoxLayout())
- pw.layout().addWidget(self.plugins)
- tab.addTab(gw, _("General"))
- tab.addTab(pw, _("Plugins"))
-
- layout.addWidget(tab)
-
- self.data = self.connector.getConfig()
- self.pdata = self.connector.getPluginConfig()
- for k, section in self.data.iteritems():
- s = Section(section, general)
- self.sections[k] = s
-
- for k, section in self.pdata.iteritems():
- s = Section(section, plugins, "plugin")
- self.psections[k] = s
-
- rel = QPushButton(_("Reload"))
- save = QPushButton(_("Save"))
-
- layout.addWidget(save)
-
- cont = QHBoxLayout()
- cont.addWidget(rel)
- cont.addWidget(save)
-
- layout.addLayout(cont)
-
- self.connect(save, SIGNAL("clicked()"), self.saveConfig)
- self.connect(rel, SIGNAL("clicked()"), self.loadConfig)
-
- def clearConfig(self):
- self.sections = {}
-
- def reloadSection(self, sections, pdata):
-
- for k, section in enumerate(pdata):
- if k in sections:
- widget = sections[k]
- for item in section.items:
- if item.name in widget.inputs:
- i = widget.inputs[item.name]
-
- if item.type == "int":
- i.setValue(int(item.value))
- elif not item.type.find(";") == -1:
- i.setCurrentIndex(i.findText(item.value))
- elif item.type == "bool":
- if True if item.value.lower() in ("1","true", "on", "an","yes") else False:
- i.setCurrentIndex(0)
- else:
- i.setCurrentIndex(1)
- else:
- i.setText(item.value)
-
-
- def saveConfig(self):
- self.data = self.connector.getConfig()
- self.pdata = self.connector.getPluginConfig()
-
- self.saveSection(self.sections, self.data)
- self.saveSection(self.psections, self.pdata, "plugin")
-
-
- def saveSection(self, sections, pdata, sec="core"):
- for k, section in enumerate(pdata):
- if k in sections:
- widget = sections[k]
- for item in section.items:
- if item.name in widget.inputs:
- i = widget.inputs[item.name]
-
- #TODO : unresolved reference: option
-
- if item.type == "int":
- if i.value() != int(item.value):
- self.connector.setConfigValue(k, option, i.value(), sec)
- elif not item.type.find(";") == -1:
- if i.currentText() != item.value:
- self.connector.setConfigValue(k, option, i.currentText(), sec)
- elif item.type == "bool":
- if (True if item.value.lower() in ("1","true", "on", "an","yes") else False) ^ (not i.currentIndex()):
- self.connector.setConfigValue(k, option, not i.currentIndex(), sec)
- else:
- if i.text() != item.value:
- self.connector.setConfigValue(k, option, str(i.text()), sec)
-
-class Section(QGroupBox):
- def __init__(self, data, parent, ctype="core"):
- self.data = data
- QGroupBox.__init__(self, data.description, parent)
- self.labels = {}
- self.inputs = {}
- self.ctype = ctype
- layout = QFormLayout(self)
- self.setLayout(layout)
-
- sw = QWidget()
- sw.setLayout(QVBoxLayout())
- sw.layout().addWidget(self)
-
- sa = QScrollArea()
- sa.setWidgetResizable(True)
- sa.setWidget(sw)
- sa.setFrameShape(sa.NoFrame)
-
- parent.addTab(sa, data.description)
-
- for option in self.data.items:
- if option.type == "int":
- i = QSpinBox(self)
- i.setMaximum(999999)
- i.setValue(int(option.value))
- elif not option.type.find(";") == -1:
- choices = option.type.split(";")
- i = QComboBox(self)
- i.addItems(choices)
- i.setCurrentIndex(i.findText(option.value))
- elif option.type == "bool":
- i = QComboBox(self)
- i.addItem(_("Yes"), QVariant(True))
- i.addItem(_("No"), QVariant(False))
- if True if option.value.lower() in ("1","true", "on", "an","yes") else False:
- i.setCurrentIndex(0)
- else:
- i.setCurrentIndex(1)
- else:
- i = QLineEdit(self)
- i.setText(option.value)
- layout.addRow(option.description, i)
- layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
diff --git a/module/gui/XMLParser.py b/module/gui/XMLParser.py
deleted file mode 100644
index 5e3b7bf65..000000000
--- a/module/gui/XMLParser.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-from __future__ import with_statement
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-from PyQt4.QtXml import *
-
-import os
-
-class XMLParser():
- def __init__(self, data, dfile=""):
- self.mutex = QMutex()
- self.mutex.lock()
- self.xml = QDomDocument()
- self.file = data
- self.dfile = dfile
- self.mutex.unlock()
- self.loadData()
- self.root = self.xml.documentElement()
-
- def loadData(self):
- self.mutex.lock()
- f = self.file
- if not os.path.exists(f):
- f = self.dfile
- with open(f, 'r') as fh:
- content = fh.read()
- self.xml.setContent(content)
- self.mutex.unlock()
-
- def saveData(self):
- self.mutex.lock()
- content = self.xml.toString()
- with open(self.file, 'w') as fh:
- fh.write(content)
- self.mutex.unlock()
- return content
-
- def parseNode(self, node, ret_type="list"):
- if ret_type == "dict":
- childNodes = {}
- else:
- childNodes = []
- child = node.firstChild()
- while True:
- n = child.toElement()
- if n.isNull():
- break
- else:
- if ret_type == "dict":
- childNodes[str(n.tagName())] = n
- else:
- childNodes.append(n)
- child = child.nextSibling()
- return childNodes
diff --git a/module/gui/__init__.py b/module/gui/__init__.py
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/module/gui/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/module/gui/connector.py b/module/gui/connector.py
deleted file mode 100644
index c16ccd08e..000000000
--- a/module/gui/connector.py
+++ /dev/null
@@ -1,165 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-SERVER_VERSION = "0.4.9"
-
-from time import sleep
-from uuid import uuid4 as uuid
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-import socket
-
-from module.remote.thriftbackend.ThriftClient import ThriftClient, WrongLogin, NoSSL, NoConnection
-from thrift.Thrift import TException
-
-class Connector(QObject):
- """
- manages the connection to the pyload core via thrift
- """
-
- firstAttempt = True
-
- def __init__(self):
- QObject.__init__(self)
- self.mutex = QMutex()
- self.connectionID = None
- self.host = None
- self.port = None
- self.user = None
- self.password = None
- self.ssl = None
- self.running = True
- self.internal = False
- self.proxy = self.Dummy()
-
- def setConnectionData(self, host, port, user, password, ssl=False):
- """
- set connection data for connection attempt, called from slotConnect
- """
- self.host = host
- self.port = port
- self.user = user
- self.password = password
- self.ssl = ssl
-
- def connectProxy(self):
- """
- initialize thrift rpc client,
- check for ssl, check auth,
- setup dispatcher,
- connect error signals,
- check server version
- """
- if self.internal: return True
-
- err = None
- try:
- client = ThriftClient(self.host, self.port, self.user, self.password)
- except WrongLogin:
- err = _("bad login credentials")
- except NoSSL:
- err = _("no ssl support")
- except NoConnection:
- err = _("can't connect to host")
- if err:
- if not Connector.firstAttempt:
- self.emit(SIGNAL("errorBox"), err)
- Connector.firstAttempt = False
- return False
-
- self.proxy = DispatchRPC(self.mutex, client)
- self.connect(self.proxy, SIGNAL("connectionLost"), self, SIGNAL("connectionLost"))
-
- server_version = self.proxy.getServerVersion()
- self.connectionID = uuid().hex
-
- if not server_version == SERVER_VERSION:
- self.emit(SIGNAL("errorBox"), _("server is version %(new)s client accepts version %(current)s") % { "new": server_version, "current": SERVER_VERSION})
- return False
-
- return True
-
- def __getattr__(self, attr):
- """
- redirect rpc calls to dispatcher
- """
- return getattr(self.proxy, attr)
-
- class Dummy(object):
- """
- dummy rpc proxy, to prevent errors
- """
- def __nonzero__(self):
- return False
-
- def __getattr__(self, attr):
- def dummy(*args, **kwargs):
- return None
- return dummy
-
-class DispatchRPC(QObject):
- """
- wraps the thrift client, to catch critical exceptions (connection lost)
- adds thread safety
- """
-
- def __init__(self, mutex, server):
- QObject.__init__(self)
- self.mutex = mutex
- self.server = server
-
- def __getattr__(self, attr):
- """
- redirect and wrap call in Wrapper instance, locks dispatcher
- """
- self.mutex.lock()
- self.fname = attr
- f = self.Wrapper(getattr(self.server, attr), self.mutex, self)
- return f
-
- class Wrapper(object):
- """
- represents a rpc call
- """
-
- def __init__(self, f, mutex, dispatcher):
- self.f = f
- self.mutex = mutex
- self.dispatcher = dispatcher
-
- def __call__(self, *args, **kwargs):
- """
- instance is called, rpc is executed
- exceptions are processed
- finally dispatcher is unlocked
- """
- lost = False
- try:
- return self.f(*args, **kwargs)
- except socket.error: #necessary?
- lost = True
- except TException:
- lost = True
- finally:
- self.mutex.unlock()
- if lost:
- from traceback import print_exc
- print_exc()
- self.dispatcher.emit(SIGNAL("connectionLost"))
diff --git a/module/lib/BeautifulSoup.py b/module/lib/BeautifulSoup.py
deleted file mode 100644
index 55567f588..000000000
--- a/module/lib/BeautifulSoup.py
+++ /dev/null
@@ -1,2012 +0,0 @@
-"""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.0.8.1"
-__copyright__ = "Copyright (c) 2004-2010 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 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
-
-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):
- if encoding:
- return self.encode(encoding)
- else:
- return self
-
-class CData(NavigableString):
-
- def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
- return "<![CDATA[%s]]>" % 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 "<!--%s-->" % NavigableString.__str__(self, encoding)
-
-class Declaration(NavigableString):
- def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
- return "<!%s>" % NavigableString.__str__(self, encoding)
-
-class Tag(PageElement):
-
- """Represents a found HTML tag with its attributes and contents."""
-
- 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 _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'&amp;%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 = []
- 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)
-
- 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]] + ";"
-
- 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, "<foo><bar></foo>" actually means
- "<foo><bar></bar></foo>".
-
- [Another possible explanation is "<foo><bar /></foo>", but since
- this class defines no SELF_CLOSING_TAGS, it will never use that
- explanation.]
-
- This class is useful for parsing XML or made-up markup languages,
- or when BeautifulSoup makes an assumption counter to what you were
- expecting."""
-
- SELF_CLOSING_TAGS = {}
- NESTABLE_TAGS = {}
- RESET_NESTING_TAGS = {}
- QUOTE_TAGS = {}
- PRESERVE_WHITESPACE_TAGS = []
-
- MARKUP_MASSAGE = [(re.compile('(<[^<>]*)/>'),
- lambda x: x.group(1) + ' />'),
- (re.compile('<!\s+([^<>]*)>'),
- lambda x: '<!' + x.group(1) + '>')
- ]
-
- ROOT_TAG_NAME = u'[document]'
-
- HTML_ENTITIES = "html"
- XML_ENTITIES = "xml"
- XHTML_ENTITIES = "xhtml"
- # TODO: This only exists for backwards-compatibility
- ALL_ENTITIES = XHTML_ENTITIES
-
- # Used when determining whether a text node is all whitespace and
- # can be replaced with a single space. A text node that contains
- # fancy Unicode spaces (usually non-breaking) should be left
- # alone.
- STRIP_ASCII_SPACES = { 9: None, 10: None, 12: None, 13: None, 32: None, }
-
- def __init__(self, markup="", parseOnlyThese=None, fromEncoding=None,
- markupMassage=True, smartQuotesTo=XML_ENTITIES,
- convertEntities=None, selfClosingTags=None, isHTML=False):
- """The Soup object is initialized as the 'root tag', and the
- provided markup (which can be a string or a file-like object)
- is fed into the underlying parser.
-
- sgmllib will process most bad HTML, and the BeautifulSoup
- class has some tricks for dealing with some HTML that kills
- sgmllib, but Beautiful Soup can nonetheless choke or lose data
- if your data uses self-closing tags or declarations
- incorrectly.
-
- By default, Beautiful Soup uses regexes to sanitize input,
- avoiding the vast majority of these problems. If the problems
- don't apply to you, pass in False for markupMassage, and
- you'll get better performance.
-
- The default parser massage techniques fix the two most common
- instances of invalid HTML that choke sgmllib:
-
- <br/> (No space between name of closing tag and tag close)
- <! --Comment--> (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:
- <p>Foo<b>Bar *<p>* should pop to 'p', not 'b'.
- <p>Foo<table>Bar *<p>* should pop to 'table', not 'p'.
- <p>Foo<table><tr>Bar *<p>* should pop to 'tr', not 'p'.
-
- <li><ul><li> *<li>* should pop to 'ul', not the first 'li'.
- <tr><table><tr> *<tr>* should pop to 'table', not the first 'tr'
- <td><tr><td> *<td>* should pop to 'tr', not the first 'td'
- """
-
- nestingResetTriggers = self.NESTABLE_TAGS.get(name)
- isNestable = nestingResetTriggers is not 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 = "&amp;%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] == '<![CDATA[':
- k = self.rawdata.find(']]>', 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 <p> tag should implicitly close the previous <p> tag.
-
- <p>Para1<p>Para2
- should be transformed into:
- <p>Para1</p><p>Para2
-
- Some tags can be nested arbitrarily. For instance, the occurance
- of a <blockquote> tag should _not_ implicitly close the previous
- <blockquote> tag.
-
- Alice said: <blockquote>Bob said: <blockquote>Blah
- should NOT be transformed into:
- Alice said: <blockquote>Bob said: </blockquote><blockquote>Blah
-
- Some tags can be nested, but the nesting is reset by the
- interposition of other tags. For instance, a <tr> tag should
- implicitly close the previous <tr> tag within the same <table>,
- but not close a <tr> tag in another table.
-
- <table><tr>Blah<tr>Blah
- should be transformed into:
- <table><tr>Blah</tr><tr>Blah
- but,
- <tr>Blah<table><tr>Blah
- should NOT be transformed into
- <tr>Blah<table></tr><tr>Blah
-
- Differing assumptions about tag nesting rules are a major source
- of problems with the BeautifulSoup class. If BeautifulSoup is not
- treating as nestable a tag your page author treats as nestable,
- try ICantBelieveItsBeautifulSoup, MinimalSoup, or
- BeautifulStoneSoup before writing your own subclass."""
-
- def __init__(self, *args, **kwargs):
- if not kwargs.has_key('smartQuotesTo'):
- kwargs['smartQuotesTo'] = self.HTML_ENTITIES
- kwargs['isHTML'] = True
- BeautifulStoneSoup.__init__(self, *args, **kwargs)
-
- SELF_CLOSING_TAGS = buildTagMap(None,
- ('br' , 'hr', 'input', 'img', 'meta',
- 'spacer', 'link', 'frame', 'base', 'col'))
-
- PRESERVE_WHITESPACE_TAGS = set(['pre', 'textarea'])
-
- QUOTE_TAGS = {'script' : None, 'textarea' : None}
-
- #According to the HTML standard, each of these inline tags can
- #contain another tag of the same type. Furthermore, it's common
- #to actually use these tags this way.
- NESTABLE_INLINE_TAGS = ('span', 'font', 'q', 'object', 'bdo', 'sub', 'sup',
- 'center')
-
- #According to the HTML standard, these block tags can contain
- #another tag of the same type. Furthermore, it's common
- #to actually use these tags this way.
- NESTABLE_BLOCK_TAGS = ('blockquote', 'div', 'fieldset', 'ins', 'del')
-
- #Lists can contain other lists, but there are restrictions.
- NESTABLE_LIST_TAGS = { 'ol' : [],
- 'ul' : [],
- 'li' : ['ul', 'ol'],
- 'dl' : [],
- 'dd' : ['dl'],
- 'dt' : ['dl'] }
-
- #Tables can contain other tables, but there are restrictions.
- NESTABLE_TABLE_TAGS = {'table' : [],
- 'tr' : ['table', 'tbody', 'tfoot', 'thead'],
- 'td' : ['tr'],
- 'th' : ['tr'],
- 'thead' : ['table'],
- 'tbody' : ['table'],
- 'tfoot' : ['table'],
- }
-
- NON_NESTABLE_BLOCK_TAGS = ('address', 'form', 'p', 'pre')
-
- #If one of these tags is encountered, all tags up to the next tag of
- #this type are popped.
- RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript',
- NON_NESTABLE_BLOCK_TAGS,
- NESTABLE_LIST_TAGS,
- NESTABLE_TABLE_TAGS)
-
- NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS,
- NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
-
- # Used to detect the charset in a META tag; see start_meta
- CHARSET_RE = re.compile("((^|;)\s*charset=)([^;]*)", re.M)
-
- def start_meta(self, attrs):
- """Beautiful Soup can detect a charset included in a META tag,
- try to convert the document to that charset, and re-parse the
- document from the beginning."""
- httpEquiv = None
- contentType = None
- contentTypeIndex = None
- tagNeedsEncodingSubstitution = False
-
- for i in range(0, len(attrs)):
- key, value = attrs[i]
- key = key.lower()
- if key == 'http-equiv':
- httpEquiv = value
- elif key == 'content':
- contentType = value
- contentTypeIndex = i
-
- if httpEquiv and contentType: # It's an interesting meta tag.
- match = self.CHARSET_RE.search(contentType)
- if match:
- if (self.declaredHTMLEncoding is not None or
- self.originalEncoding == self.fromEncoding):
- # An HTML encoding was sniffed while converting
- # the document to Unicode, or an HTML encoding was
- # sniffed during a previous pass through the
- # document, or an encoding was specified
- # explicitly and it worked. Rewrite the meta tag.
- def rewrite(match):
- return match.group(1) + "%SOUP-ENCODING%"
- newAttr = self.CHARSET_RE.sub(rewrite, contentType)
- attrs[contentTypeIndex] = (attrs[contentTypeIndex][0],
- newAttr)
- tagNeedsEncodingSubstitution = True
- else:
- # This is our first pass through the document.
- # Go through it again with the encoding information.
- newCharset = match.group(3)
- if newCharset and newCharset != self.originalEncoding:
- self.declaredHTMLEncoding = newCharset
- self._feed(self.declaredHTMLEncoding)
- raise StopParsing
- pass
- tag = self.unknown_starttag("meta", attrs)
- if tag and tagNeedsEncodingSubstitution:
- tag.containsSubstitutions = True
-
-class StopParsing(Exception):
- pass
-
-class ICantBelieveItsBeautifulSoup(BeautifulSoup):
-
- """The BeautifulSoup class is oriented towards skipping over
- common HTML errors like unclosed tags. However, sometimes it makes
- errors of its own. For instance, consider this fragment:
-
- <b>Foo<b>Bar</b></b>
-
- This is perfectly valid (if bizarre) HTML. However, the
- BeautifulSoup class will implicitly close the first b tag when it
- encounters the second 'b'. It will think the author wrote
- "<b>Foo<b>Bar", and didn't close the first 'b' tag, because
- there's no real-world reason to bold something that's already
- bold. When it encounters '</b></b>' it will close two more 'b'
- tags, for a grand total of three tags closed instead of two. This
- can throw off the rest of your document structure. The same is
- true of a number of other tags, listed below.
-
- It's much more common for someone to forget to close a 'b' tag
- than to actually use nested 'b' tags, and the BeautifulSoup class
- handles the common case. This class handles the not-co-common
- case: where you can't believe someone wrote what they did, but
- it's valid HTML and BeautifulSoup screwed up by assuming it
- wouldn't be."""
-
- I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = \
- ('em', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'strong',
- 'cite', 'code', 'dfn', 'kbd', 'samp', 'strong', 'var', 'b',
- 'big')
-
- I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = ('noscript',)
-
- NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS,
- I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS,
- I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS)
-
-class MinimalSoup(BeautifulSoup):
- """The MinimalSoup class is for parsing HTML that contains
- pathologically bad markup. It makes no assumptions about tag
- nesting, but it does know which tags are self-closing, that
- <script> tags contain Javascript and should not be parsed, that
- META tags may contain encoding information, and so on.
-
- This also makes it better for subclassing than BeautifulStoneSoup
- or BeautifulSoup."""
-
- RESET_NESTING_TAGS = buildTagMap('noscript')
- NESTABLE_TAGS = {}
-
-class BeautifulSOAP(BeautifulStoneSoup):
- """This class will push a tag with only a single string child into
- the tag's parent as an attribute. The attribute's name is the tag
- name, and the value is the string child. An example should give
- the flavor of the change:
-
- <foo><bar>baz</bar></foo>
- =>
- <foo bar="baz"><bar>baz</bar></foo>
-
- You can then access fooTag['bar'] instead of fooTag.barTag.string.
-
- This is, of course, useful for scraping structures that tend to
- use subelements instead of attributes, such as SOAP messages. Note
- that it modifies its input, so don't print the modified version
- out.
-
- I'm not sure how many people really want to use this class; let me
- know if you do. Mainly I like the name."""
-
- def popTag(self):
- if len(self.tagStack) > 1:
- tag = self.tagStack[-1]
- parent = self.tagStack[-2]
- parent._getAttrMap()
- if (isinstance(tag, Tag) and len(tag.contents) == 1 and
- isinstance(tag.contents[0], NavigableString) and
- not parent.attrMap.has_key(tag.name)):
- parent[tag.name] = tag.contents[0]
- BeautifulStoneSoup.popTag(self)
-
-#Enterprise class names! It has come to our attention that some people
-#think the names of the Beautiful Soup parser classes are too silly
-#and "unprofessional" for use in enterprise screen-scraping. We feel
-#your pain! For such-minded folk, the Beautiful Soup Consortium And
-#All-Night Kosher Bakery recommends renaming this file to
-#"RobustParser.py" (or, in cases of extreme enterprisiness,
-#"RobustParserBeanInterface.class") and using the following
-#enterprise-friendly class aliases:
-class RobustXMLParser(BeautifulStoneSoup):
- pass
-class RobustHTMLParser(BeautifulSoup):
- pass
-class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup):
- pass
-class RobustInsanelyWackAssHTMLParser(MinimalSoup):
- pass
-class SimplifyingSOAPParser(BeautifulSOAP):
- pass
-
-######################################################
-#
-# Bonus library: Unicode, Dammit
-#
-# This class forces XML data into a standard format (usually to UTF-8
-# or Unicode). It is heavily based on code from Mark Pilgrim's
-# Universal Feed Parser. It does not rewrite the XML or HTML to
-# reflect a new encoding: that happens in BeautifulStoneSoup.handle_pi
-# (XML) and BeautifulSoup.start_meta (HTML).
-
-# Autodetects character encodings.
-# Download from http://chardet.feedparser.org/
-try:
- import chardet
-# import chardet.constants
-# chardet.constants._debug = 1
-except ImportError:
- chardet = None
-
-# cjkcodecs and iconv_codec make Python know about more character encodings.
-# Both are available from http://cjkpython.i18n.org/
-# They're built in if you use Python 2.4.
-try:
- import cjkcodecs.aliases
-except ImportError:
- pass
-try:
- import iconv_codec
-except ImportError:
- pass
-
-class UnicodeDammit:
- """A class for detecting the encoding of a *ML document and
- converting it to a Unicode string. If the source encoding is
- windows-1252, can replace MS smart quotes with their HTML or XML
- equivalents."""
-
- # This dictionary maps commonly seen values for "charset" in HTML
- # meta tags to the corresponding Python codec names. It only covers
- # values that aren't in Python's aliases and can't be determined
- # by the heuristics in find_codec.
- CHARSET_ALIASES = { "macintosh" : "mac-roman",
- "x-sjis" : "shift-jis" }
-
- def __init__(self, markup, overrideEncodings=[],
- smartQuotesTo='xml', isHTML=False):
- self.declaredHTMLEncoding = None
- self.markup, documentEncoding, sniffedEncoding = \
- self._detectEncoding(markup, isHTML)
- self.smartQuotesTo = smartQuotesTo
- self.triedEncodings = []
- if markup == '' or isinstance(markup, unicode):
- self.originalEncoding = None
- self.unicode = unicode(markup)
- return
-
- u = None
- for proposedEncoding in overrideEncodings:
- u = self._convertFrom(proposedEncoding)
- if u: break
- if not u:
- for proposedEncoding in (documentEncoding, sniffedEncoding):
- u = self._convertFrom(proposedEncoding)
- if u: break
-
- # If no luck and we have auto-detection library, try that:
- if not u and chardet and not isinstance(self.markup, unicode):
- u = self._convertFrom(chardet.detect(self.markup)['encoding'])
-
- # As a last resort, try utf-8 and windows-1252:
- if not u:
- for proposed_encoding in ("utf-8", "windows-1252"):
- u = self._convertFrom(proposed_encoding)
- if u: break
-
- self.unicode = u
- if not u: self.originalEncoding = None
-
- def _subMSChar(self, orig):
- """Changes a MS smart quote character to an XML or HTML
- entity."""
- sub = self.MS_CHARS.get(orig)
- if isinstance(sub, tuple):
- if self.smartQuotesTo == 'xml':
- sub = '&#x%s;' % sub[1]
- else:
- sub = '&%s;' % sub[0]
- return sub
-
- def _convertFrom(self, proposed):
- proposed = self.find_codec(proposed)
- if not proposed or proposed in self.triedEncodings:
- return None
- self.triedEncodings.append(proposed)
- markup = self.markup
-
- # Convert smart quotes to HTML if coming from an encoding
- # that might have them.
- if self.smartQuotesTo and proposed.lower() in("windows-1252",
- "iso-8859-1",
- "iso-8859-2"):
- markup = re.compile("([\x80-\x9f])").sub \
- (lambda(x): self._subMSChar(x.group(1)),
- markup)
-
- try:
- # print "Trying to convert document to %s" % proposed
- u = self._toUnicode(markup, proposed)
- self.markup = u
- self.originalEncoding = proposed
- except Exception, e:
- # print "That didn't work!"
- # print e
- return None
- #print "Correct encoding: %s" % proposed
- return self.markup
-
- def _toUnicode(self, data, encoding):
- '''Given a string and its encoding, decodes the string into Unicode.
- %encoding is a string recognized by encodings.aliases'''
-
- # strip Byte Order Mark (if present)
- if (len(data) >= 4) and (data[:2] == '\xfe\xff') \
- and (data[2:4] != '\x00\x00'):
- encoding = 'utf-16be'
- data = data[2:]
- elif (len(data) >= 4) and (data[:2] == '\xff\xfe') \
- and (data[2:4] != '\x00\x00'):
- encoding = 'utf-16le'
- data = data[2:]
- elif data[:3] == '\xef\xbb\xbf':
- encoding = 'utf-8'
- data = data[3:]
- elif data[:4] == '\x00\x00\xfe\xff':
- encoding = 'utf-32be'
- data = data[4:]
- elif data[:4] == '\xff\xfe\x00\x00':
- encoding = 'utf-32le'
- data = data[4:]
- newdata = unicode(data, encoding)
- return newdata
-
- def _detectEncoding(self, xml_data, isHTML=False):
- """Given a document, tries to detect its XML encoding."""
- xml_encoding = sniffed_xml_encoding = None
- try:
- if xml_data[:4] == '\x4c\x6f\xa7\x94':
- # EBCDIC
- xml_data = self._ebcdic_to_ascii(xml_data)
- elif xml_data[:4] == '\x00\x3c\x00\x3f':
- # UTF-16BE
- sniffed_xml_encoding = 'utf-16be'
- xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
- elif (len(xml_data) >= 4) and (xml_data[:2] == '\xfe\xff') \
- and (xml_data[2:4] != '\x00\x00'):
- # UTF-16BE with BOM
- sniffed_xml_encoding = 'utf-16be'
- xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
- elif xml_data[:4] == '\x3c\x00\x3f\x00':
- # UTF-16LE
- sniffed_xml_encoding = 'utf-16le'
- xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
- elif (len(xml_data) >= 4) and (xml_data[:2] == '\xff\xfe') and \
- (xml_data[2:4] != '\x00\x00'):
- # UTF-16LE with BOM
- sniffed_xml_encoding = 'utf-16le'
- xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
- elif xml_data[:4] == '\x00\x00\x00\x3c':
- # UTF-32BE
- sniffed_xml_encoding = 'utf-32be'
- xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
- elif xml_data[:4] == '\x3c\x00\x00\x00':
- # UTF-32LE
- sniffed_xml_encoding = 'utf-32le'
- xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
- elif xml_data[:4] == '\x00\x00\xfe\xff':
- # UTF-32BE with BOM
- sniffed_xml_encoding = 'utf-32be'
- xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
- elif xml_data[:4] == '\xff\xfe\x00\x00':
- # UTF-32LE with BOM
- sniffed_xml_encoding = 'utf-32le'
- xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
- elif xml_data[:3] == '\xef\xbb\xbf':
- # UTF-8 with BOM
- sniffed_xml_encoding = 'utf-8'
- xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
- else:
- sniffed_xml_encoding = 'ascii'
- pass
- except:
- xml_encoding_match = None
- xml_encoding_match = re.compile(
- '^<\?.*encoding=[\'"](.*?)[\'"].*\?>').match(xml_data)
- if not xml_encoding_match and isHTML:
- regexp = re.compile('<\s*meta[^>]+charset=([^>]*?)[;\'">]', re.I)
- xml_encoding_match = regexp.search(xml_data)
- if xml_encoding_match is not None:
- xml_encoding = xml_encoding_match.groups()[0].lower()
- if isHTML:
- self.declaredHTMLEncoding = xml_encoding
- if sniffed_xml_encoding and \
- (xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode',
- 'iso-10646-ucs-4', 'ucs-4', 'csucs4',
- 'utf-16', 'utf-32', 'utf_16', 'utf_32',
- 'utf16', 'u16')):
- xml_encoding = sniffed_xml_encoding
- return xml_data, xml_encoding, sniffed_xml_encoding
-
-
- def find_codec(self, charset):
- return self._codec(self.CHARSET_ALIASES.get(charset, charset)) \
- or (charset and self._codec(charset.replace("-", ""))) \
- or (charset and self._codec(charset.replace("-", "_"))) \
- or charset
-
- def _codec(self, charset):
- if not charset: return charset
- codec = None
- try:
- codecs.lookup(charset)
- codec = charset
- except (LookupError, ValueError):
- pass
- return codec
-
- EBCDIC_TO_ASCII_MAP = None
- def _ebcdic_to_ascii(self, s):
- c = self.__class__
- if not c.EBCDIC_TO_ASCII_MAP:
- emap = (0,1,2,3,156,9,134,127,151,141,142,11,12,13,14,15,
- 16,17,18,19,157,133,8,135,24,25,146,143,28,29,30,31,
- 128,129,130,131,132,10,23,27,136,137,138,139,140,5,6,7,
- 144,145,22,147,148,149,150,4,152,153,154,155,20,21,158,26,
- 32,160,161,162,163,164,165,166,167,168,91,46,60,40,43,33,
- 38,169,170,171,172,173,174,175,176,177,93,36,42,41,59,94,
- 45,47,178,179,180,181,182,183,184,185,124,44,37,95,62,63,
- 186,187,188,189,190,191,192,193,194,96,58,35,64,39,61,34,
- 195,97,98,99,100,101,102,103,104,105,196,197,198,199,200,
- 201,202,106,107,108,109,110,111,112,113,114,203,204,205,
- 206,207,208,209,126,115,116,117,118,119,120,121,122,210,
- 211,212,213,214,215,216,217,218,219,220,221,222,223,224,
- 225,226,227,228,229,230,231,123,65,66,67,68,69,70,71,72,
- 73,232,233,234,235,236,237,125,74,75,76,77,78,79,80,81,
- 82,238,239,240,241,242,243,92,159,83,84,85,86,87,88,89,
- 90,244,245,246,247,248,249,48,49,50,51,52,53,54,55,56,57,
- 250,251,252,253,254,255)
- import string
- c.EBCDIC_TO_ASCII_MAP = string.maketrans( \
- ''.join(map(chr, range(256))), ''.join(map(chr, emap)))
- return s.translate(c.EBCDIC_TO_ASCII_MAP)
-
- MS_CHARS = { '\x80' : ('euro', '20AC'),
- '\x81' : ' ',
- '\x82' : ('sbquo', '201A'),
- '\x83' : ('fnof', '192'),
- '\x84' : ('bdquo', '201E'),
- '\x85' : ('hellip', '2026'),
- '\x86' : ('dagger', '2020'),
- '\x87' : ('Dagger', '2021'),
- '\x88' : ('circ', '2C6'),
- '\x89' : ('permil', '2030'),
- '\x8A' : ('Scaron', '160'),
- '\x8B' : ('lsaquo', '2039'),
- '\x8C' : ('OElig', '152'),
- '\x8D' : '?',
- '\x8E' : ('#x17D', '17D'),
- '\x8F' : '?',
- '\x90' : '?',
- '\x91' : ('lsquo', '2018'),
- '\x92' : ('rsquo', '2019'),
- '\x93' : ('ldquo', '201C'),
- '\x94' : ('rdquo', '201D'),
- '\x95' : ('bull', '2022'),
- '\x96' : ('ndash', '2013'),
- '\x97' : ('mdash', '2014'),
- '\x98' : ('tilde', '2DC'),
- '\x99' : ('trade', '2122'),
- '\x9a' : ('scaron', '161'),
- '\x9b' : ('rsaquo', '203A'),
- '\x9c' : ('oelig', '153'),
- '\x9d' : '?',
- '\x9e' : ('#x17E', '17E'),
- '\x9f' : ('Yuml', ''),}
-
-#######################################################################
-
-
-#By default, act as an HTML pretty-printer.
-if __name__ == '__main__':
- import sys
- soup = BeautifulSoup(sys.stdin)
- print soup.prettify()
diff --git a/module/lib/Getch.py b/module/lib/Getch.py
deleted file mode 100644
index 22b7ea7f8..000000000
--- a/module/lib/Getch.py
+++ /dev/null
@@ -1,76 +0,0 @@
-class Getch:
- """
- Gets a single character from standard input. Does not echo to
- the screen.
- """
-
- def __init__(self):
- try:
- self.impl = _GetchWindows()
- except ImportError:
- try:
- self.impl = _GetchMacCarbon()
- except(AttributeError, ImportError):
- self.impl = _GetchUnix()
-
- def __call__(self): return self.impl()
-
-
-class _GetchUnix:
- def __init__(self):
- import tty
- import sys
-
- def __call__(self):
- import sys
- import tty
- import termios
-
- fd = sys.stdin.fileno()
- old_settings = termios.tcgetattr(fd)
- try:
- tty.setraw(sys.stdin.fileno())
- ch = sys.stdin.read(1)
- finally:
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
- return ch
-
-
-class _GetchWindows:
- def __init__(self):
- import msvcrt
-
- def __call__(self):
- import msvcrt
-
- return msvcrt.getch()
-
-class _GetchMacCarbon:
- """
- A function which returns the current ASCII key that is down;
- if no ASCII key is down, the null string is returned. The
- page http://www.mactech.com/macintosh-c/chap02-1.html was
- very helpful in figuring out how to do this.
- """
-
- def __init__(self):
- import Carbon
- Carbon.Evt #see if it has this (in Unix, it doesn't)
-
- def __call__(self):
- import Carbon
-
- if Carbon.Evt.EventAvail(0x0008)[0] == 0: # 0x0008 is the keyDownMask
- return ''
- else:
- #
- # The event contains the following info:
- # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
- #
- # The message (msg) contains the ASCII char which is
- # extracted with the 0x000000FF charCodeMask; this
- # number is converted to an ASCII character with chr() and
- # returned
- #
- (what, msg, when, where, mod) = Carbon.Evt.GetNextEvent(0x0008)[1]
- return chr(msg) \ No newline at end of file
diff --git a/module/lib/MultipartPostHandler.py b/module/lib/MultipartPostHandler.py
deleted file mode 100644
index 94aee0193..000000000
--- a/module/lib/MultipartPostHandler.py
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-####
-# 02/2006 Will Holcomb <wholcomb@gmail.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# 7/26/07 Slightly modified by Brian Schneider
-# in order to support unicode files ( multipart_encode function )
-"""
-Usage:
- Enables the use of multipart/form-data for posting forms
-
-Inspirations:
- Upload files in python:
- http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
- urllib2_file:
- Fabien Seisen: <fabien@seisen.org>
-
-Example:
- import MultipartPostHandler, urllib2, cookielib
-
- cookies = cookielib.CookieJar()
- opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies),
- MultipartPostHandler.MultipartPostHandler)
- params = { "username" : "bob", "password" : "riviera",
- "file" : open("filename", "rb") }
- opener.open("http://wwww.bobsite.com/upload/", params)
-
-Further Example:
- The main function of this file is a sample which downloads a page and
- then uploads it to the W3C validator.
-"""
-
-from urllib import urlencode
-from urllib2 import BaseHandler, HTTPHandler, build_opener
-import mimetools, mimetypes
-from os import write, remove
-from cStringIO import StringIO
-
-class Callable:
- def __init__(self, anycallable):
- self.__call__ = anycallable
-
-# Controls how sequences are uncoded. If true, elements may be given multiple values by
-# assigning a sequence.
-doseq = 1
-
-class MultipartPostHandler(BaseHandler):
- handler_order = HTTPHandler.handler_order - 10 # needs to run first
-
- def http_request(self, request):
- data = request.get_data()
- if data is not None and type(data) != str:
- v_files = []
- v_vars = []
- try:
- for(key, value) in data.items():
- if type(value) == file:
- v_files.append((key, value))
- else:
- v_vars.append((key, value))
- except TypeError:
- systype, value, traceback = sys.exc_info()
- raise TypeError, "not a valid non-string sequence or mapping object", traceback
-
- if len(v_files) == 0:
- data = urlencode(v_vars, doseq)
- else:
- boundary, data = self.multipart_encode(v_vars, v_files)
-
- contenttype = 'multipart/form-data; boundary=%s' % boundary
- if(request.has_header('Content-Type')
- and request.get_header('Content-Type').find('multipart/form-data') != 0):
- print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
- request.add_unredirected_header('Content-Type', contenttype)
-
- request.add_data(data)
-
- return request
-
- def multipart_encode(vars, files, boundary = None, buf = None):
- if boundary is None:
- boundary = mimetools.choose_boundary()
- if buf is None:
- buf = StringIO()
- for(key, value) in vars:
- buf.write('--%s\r\n' % boundary)
- buf.write('Content-Disposition: form-data; name="%s"' % key)
- buf.write('\r\n\r\n' + value + '\r\n')
- for(key, fd) in files:
- #file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
- filename = fd.name.split('/')[-1]
- contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
- buf.write('--%s\r\n' % boundary)
- buf.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
- buf.write('Content-Type: %s\r\n' % contenttype)
- # buffer += 'Content-Length: %s\r\n' % file_size
- fd.seek(0)
- buf.write('\r\n' + fd.read() + '\r\n')
- buf.write('--' + boundary + '--\r\n\r\n')
- buf = buf.getvalue()
- return boundary, buf
- multipart_encode = Callable(multipart_encode)
-
- https_request = http_request
-
-def main():
- import tempfile, sys
-
- validatorURL = "http://validator.w3.org/check"
- opener = build_opener(MultipartPostHandler)
-
- def validateFile(url):
- temp = tempfile.mkstemp(suffix=".html")
- write(temp[0], opener.open(url).read())
- params = { "ss" : "0", # show source
- "doctype" : "Inline",
- "uploaded_file" : open(temp[1], "rb") }
- print opener.open(validatorURL, params).read()
- remove(temp[1])
-
- if len(sys.argv[1:]) > 0:
- for arg in sys.argv[1:]:
- validateFile(arg)
- else:
- validateFile("http://www.google.com")
-
-if __name__=="__main__":
- main()
diff --git a/module/lib/SafeEval.py b/module/lib/SafeEval.py
deleted file mode 100644
index 8fc57f261..000000000
--- a/module/lib/SafeEval.py
+++ /dev/null
@@ -1,47 +0,0 @@
-## {{{ http://code.activestate.com/recipes/286134/ (r3) (modified)
-import dis
-
-_const_codes = map(dis.opmap.__getitem__, [
- 'POP_TOP','ROT_TWO','ROT_THREE','ROT_FOUR','DUP_TOP',
- 'BUILD_LIST','BUILD_MAP','BUILD_TUPLE',
- 'LOAD_CONST','RETURN_VALUE','STORE_SUBSCR'
- ])
-
-
-_load_names = ['False', 'True', 'null', 'true', 'false']
-
-_locals = {'null': None, 'true': True, 'false': False}
-
-def _get_opcodes(codeobj):
- i = 0
- opcodes = []
- s = codeobj.co_code
- names = codeobj.co_names
- while i < len(s):
- code = ord(s[i])
- opcodes.append(code)
- if code >= dis.HAVE_ARGUMENT:
- i += 3
- else:
- i += 1
- return opcodes, names
-
-def test_expr(expr, allowed_codes):
- try:
- c = compile(expr, "", "eval")
- except:
- raise ValueError, "%s is not a valid expression" % expr
- codes, names = _get_opcodes(c)
- for code in codes:
- if code not in allowed_codes:
- for n in names:
- if n not in _load_names:
- raise ValueError, "opcode %s not allowed" % dis.opname[code]
- return c
-
-
-def const_eval(expr):
- c = test_expr(expr, _const_codes)
- return eval(c, None, _locals)
-
-## end of http://code.activestate.com/recipes/286134/ }}}
diff --git a/module/lib/Unzip.py b/module/lib/Unzip.py
deleted file mode 100644
index f56fbe751..000000000
--- a/module/lib/Unzip.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import zipfile
-import os
-
-class Unzip:
- def __init__(self):
- pass
-
- def extract(self, file, dir):
- if not dir.endswith(':') and not os.path.exists(dir):
- os.mkdir(dir)
-
- zf = zipfile.ZipFile(file)
-
- # create directory structure to house files
- self._createstructure(file, dir)
-
- # extract files to directory structure
- for i, name in enumerate(zf.namelist()):
-
- if not name.endswith('/') and not name.endswith("config"):
- print "extracting", name.replace("pyload/","")
- outfile = open(os.path.join(dir, name.replace("pyload/","")), 'wb')
- outfile.write(zf.read(name))
- outfile.flush()
- outfile.close()
-
- def _createstructure(self, file, dir):
- self._makedirs(self._listdirs(file), dir)
-
- def _makedirs(self, directories, basedir):
- """ Create any directories that don't currently exist """
- for dir in directories:
- curdir = os.path.join(basedir, dir)
- if not os.path.exists(curdir):
- os.mkdir(curdir)
-
- def _listdirs(self, file):
- """ Grabs all the directories in the zip structure
- This is necessary to create the structure before trying
- to extract the file to it. """
- zf = zipfile.ZipFile(file)
-
- dirs = []
-
- for name in zf.namelist():
- if name.endswith('/'):
- dirs.append(name.replace("pyload/",""))
-
- dirs.sort()
- return dirs
diff --git a/module/lib/__init__.py b/module/lib/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/lib/__init__.py
+++ /dev/null
diff --git a/module/lib/beaker/__init__.py b/module/lib/beaker/__init__.py
deleted file mode 100644
index 792d60054..000000000
--- a/module/lib/beaker/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-#
diff --git a/module/lib/beaker/cache.py b/module/lib/beaker/cache.py
deleted file mode 100644
index 4a96537ff..000000000
--- a/module/lib/beaker/cache.py
+++ /dev/null
@@ -1,459 +0,0 @@
-"""Cache object
-
-The Cache object is used to manage a set of cache files and their
-associated backend. The backends can be rotated on the fly by
-specifying an alternate type when used.
-
-Advanced users can add new backends in beaker.backends
-
-"""
-
-import warnings
-
-import beaker.container as container
-import beaker.util as util
-from beaker.exceptions import BeakerException, InvalidCacheBackendError
-
-import beaker.ext.memcached as memcached
-import beaker.ext.database as database
-import beaker.ext.sqla as sqla
-import beaker.ext.google as google
-
-# Initialize the basic available backends
-clsmap = {
- 'memory':container.MemoryNamespaceManager,
- 'dbm':container.DBMNamespaceManager,
- 'file':container.FileNamespaceManager,
- 'ext:memcached':memcached.MemcachedNamespaceManager,
- 'ext:database':database.DatabaseNamespaceManager,
- 'ext:sqla': sqla.SqlaNamespaceManager,
- 'ext:google': google.GoogleNamespaceManager,
- }
-
-# Initialize the cache region dict
-cache_regions = {}
-cache_managers = {}
-
-try:
- import pkg_resources
-
- # Load up the additional entry point defined backends
- for entry_point in pkg_resources.iter_entry_points('beaker.backends'):
- try:
- NamespaceManager = entry_point.load()
- name = entry_point.name
- if name in clsmap:
- raise BeakerException("NamespaceManager name conflict,'%s' "
- "already loaded" % name)
- clsmap[name] = NamespaceManager
- except (InvalidCacheBackendError, SyntaxError):
- # Ignore invalid backends
- pass
- except:
- import sys
- from pkg_resources import DistributionNotFound
- # Warn when there's a problem loading a NamespaceManager
- if not isinstance(sys.exc_info()[1], DistributionNotFound):
- import traceback
- from StringIO import StringIO
- tb = StringIO()
- traceback.print_exc(file=tb)
- warnings.warn("Unable to load NamespaceManager entry point: '%s': "
- "%s" % (entry_point, tb.getvalue()), RuntimeWarning,
- 2)
-except ImportError:
- pass
-
-
-
-
-def cache_region(region, *deco_args):
- """Decorate a function to cache itself using a cache region
-
- The region decorator requires arguments if there are more than
- 2 of the same named function, in the same module. This is
- because the namespace used for the functions cache is based on
- the functions name and the module.
-
-
- Example::
-
- # Add cache region settings to beaker:
- beaker.cache.cache_regions.update(dict_of_config_region_options))
-
- @cache_region('short_term', 'some_data')
- def populate_things(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- return load('rabbits', 20, 0)
-
- .. note::
-
- The function being decorated must only be called with
- positional arguments.
-
- """
- cache = [None]
-
- def decorate(func):
- namespace = util.func_namespace(func)
- def cached(*args):
- reg = cache_regions[region]
- if not reg.get('enabled', True):
- return func(*args)
-
- if not cache[0]:
- if region not in cache_regions:
- raise BeakerException('Cache region not configured: %s' % region)
- cache[0] = Cache._get_cache(namespace, reg)
-
- cache_key = " ".join(map(str, deco_args + args))
- def go():
- return func(*args)
-
- return cache[0].get_value(cache_key, createfunc=go)
- cached._arg_namespace = namespace
- cached._arg_region = region
- return cached
- return decorate
-
-
-def region_invalidate(namespace, region, *args):
- """Invalidate a cache region namespace or decorated function
-
- This function only invalidates cache spaces created with the
- cache_region decorator.
-
- :param namespace: Either the namespace of the result to invalidate, or the
- cached function reference
-
- :param region: The region the function was cached to. If the function was
- cached to a single region then this argument can be None
-
- :param args: Arguments that were used to differentiate the cached
- function as well as the arguments passed to the decorated
- function
-
- Example::
-
- # Add cache region settings to beaker:
- beaker.cache.cache_regions.update(dict_of_config_region_options))
-
- def populate_things(invalidate=False):
-
- @cache_region('short_term', 'some_data')
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- # If the results should be invalidated first
- if invalidate:
- region_invalidate(load, None, 'some_data',
- 'rabbits', 20, 0)
- return load('rabbits', 20, 0)
-
- """
- if callable(namespace):
- if not region:
- region = namespace._arg_region
- namespace = namespace._arg_namespace
-
- if not region:
- raise BeakerException("Region or callable function "
- "namespace is required")
- else:
- region = cache_regions[region]
-
- cache = Cache._get_cache(namespace, region)
- cache_key = " ".join(str(x) for x in args)
- cache.remove_value(cache_key)
-
-
-class Cache(object):
- """Front-end to the containment API implementing a data cache.
-
- :param namespace: the namespace of this Cache
-
- :param type: type of cache to use
-
- :param expire: seconds to keep cached data
-
- :param expiretime: seconds to keep cached data (legacy support)
-
- :param starttime: time when cache was cache was
-
- """
- def __init__(self, namespace, type='memory', expiretime=None,
- starttime=None, expire=None, **nsargs):
- try:
- cls = clsmap[type]
- if isinstance(cls, InvalidCacheBackendError):
- raise cls
- except KeyError:
- raise TypeError("Unknown cache implementation %r" % type)
-
- self.namespace = cls(namespace, **nsargs)
- self.expiretime = expiretime or expire
- self.starttime = starttime
- self.nsargs = nsargs
-
- @classmethod
- def _get_cache(cls, namespace, kw):
- key = namespace + str(kw)
- try:
- return cache_managers[key]
- except KeyError:
- cache_managers[key] = cache = cls(namespace, **kw)
- return cache
-
- def put(self, key, value, **kw):
- self._get_value(key, **kw).set_value(value)
- set_value = put
-
- def get(self, key, **kw):
- """Retrieve a cached value from the container"""
- return self._get_value(key, **kw).get_value()
- get_value = get
-
- def remove_value(self, key, **kw):
- mycontainer = self._get_value(key, **kw)
- if mycontainer.has_current_value():
- mycontainer.clear_value()
- remove = remove_value
-
- def _get_value(self, key, **kw):
- if isinstance(key, unicode):
- key = key.encode('ascii', 'backslashreplace')
-
- if 'type' in kw:
- return self._legacy_get_value(key, **kw)
-
- kw.setdefault('expiretime', self.expiretime)
- kw.setdefault('starttime', self.starttime)
-
- return container.Value(key, self.namespace, **kw)
-
- @util.deprecated("Specifying a "
- "'type' and other namespace configuration with cache.get()/put()/etc. "
- "is deprecated. Specify 'type' and other namespace configuration to "
- "cache_manager.get_cache() and/or the Cache constructor instead.")
- def _legacy_get_value(self, key, type, **kw):
- expiretime = kw.pop('expiretime', self.expiretime)
- starttime = kw.pop('starttime', None)
- createfunc = kw.pop('createfunc', None)
- kwargs = self.nsargs.copy()
- kwargs.update(kw)
- c = Cache(self.namespace.namespace, type=type, **kwargs)
- return c._get_value(key, expiretime=expiretime, createfunc=createfunc,
- starttime=starttime)
-
- def clear(self):
- """Clear all the values from the namespace"""
- self.namespace.remove()
-
- # dict interface
- def __getitem__(self, key):
- return self.get(key)
-
- def __contains__(self, key):
- return self._get_value(key).has_current_value()
-
- def has_key(self, key):
- return key in self
-
- def __delitem__(self, key):
- self.remove_value(key)
-
- def __setitem__(self, key, value):
- self.put(key, value)
-
-
-class CacheManager(object):
- def __init__(self, **kwargs):
- """Initialize a CacheManager object with a set of options
-
- Options should be parsed with the
- :func:`~beaker.util.parse_cache_config_options` function to
- ensure only valid options are used.
-
- """
- self.kwargs = kwargs
- self.regions = kwargs.pop('cache_regions', {})
-
- # Add these regions to the module global
- cache_regions.update(self.regions)
-
- def get_cache(self, name, **kwargs):
- kw = self.kwargs.copy()
- kw.update(kwargs)
- return Cache._get_cache(name, kw)
-
- def get_cache_region(self, name, region):
- if region not in self.regions:
- raise BeakerException('Cache region not configured: %s' % region)
- kw = self.regions[region]
- return Cache._get_cache(name, kw)
-
- def region(self, region, *args):
- """Decorate a function to cache itself using a cache region
-
- The region decorator requires arguments if there are more than
- 2 of the same named function, in the same module. This is
- because the namespace used for the functions cache is based on
- the functions name and the module.
-
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
-
- def populate_things():
-
- @cache.region('short_term', 'some_data')
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- return load('rabbits', 20, 0)
-
- .. note::
-
- The function being decorated must only be called with
- positional arguments.
-
- """
- return cache_region(region, *args)
-
- def region_invalidate(self, namespace, region, *args):
- """Invalidate a cache region namespace or decorated function
-
- This function only invalidates cache spaces created with the
- cache_region decorator.
-
- :param namespace: Either the namespace of the result to invalidate, or the
- name of the cached function
-
- :param region: The region the function was cached to. If the function was
- cached to a single region then this argument can be None
-
- :param args: Arguments that were used to differentiate the cached
- function as well as the arguments passed to the decorated
- function
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
- def populate_things(invalidate=False):
-
- @cache.region('short_term', 'some_data')
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- # If the results should be invalidated first
- if invalidate:
- cache.region_invalidate(load, None, 'some_data',
- 'rabbits', 20, 0)
- return load('rabbits', 20, 0)
-
-
- """
- return region_invalidate(namespace, region, *args)
- if callable(namespace):
- if not region:
- region = namespace._arg_region
- namespace = namespace._arg_namespace
-
- if not region:
- raise BeakerException("Region or callable function "
- "namespace is required")
- else:
- region = self.regions[region]
-
- cache = self.get_cache(namespace, **region)
- cache_key = " ".join(str(x) for x in args)
- cache.remove_value(cache_key)
-
- def cache(self, *args, **kwargs):
- """Decorate a function to cache itself with supplied parameters
-
- :param args: Used to make the key unique for this function, as in region()
- above.
-
- :param kwargs: Parameters to be passed to get_cache(), will override defaults
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
-
- def populate_things():
-
- @cache.cache('mycache', expire=15)
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- return load('rabbits', 20, 0)
-
- .. note::
-
- The function being decorated must only be called with
- positional arguments.
-
- """
- cache = [None]
- key = " ".join(str(x) for x in args)
-
- def decorate(func):
- namespace = util.func_namespace(func)
- def cached(*args):
- if not cache[0]:
- cache[0] = self.get_cache(namespace, **kwargs)
- cache_key = key + " " + " ".join(str(x) for x in args)
- def go():
- return func(*args)
- return cache[0].get_value(cache_key, createfunc=go)
- cached._arg_namespace = namespace
- return cached
- return decorate
-
- def invalidate(self, func, *args, **kwargs):
- """Invalidate a cache decorated function
-
- This function only invalidates cache spaces created with the
- cache decorator.
-
- :param func: Decorated function to invalidate
-
- :param args: Used to make the key unique for this function, as in region()
- above.
-
- :param kwargs: Parameters that were passed for use by get_cache(), note that
- this is only required if a ``type`` was specified for the
- function
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
-
- def populate_things(invalidate=False):
-
- @cache.cache('mycache', type="file", expire=15)
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- # If the results should be invalidated first
- if invalidate:
- cache.invalidate(load, 'mycache', 'rabbits', 20, 0, type="file")
- return load('rabbits', 20, 0)
-
- """
- namespace = func._arg_namespace
-
- cache = self.get_cache(namespace, **kwargs)
- cache_key = " ".join(str(x) for x in args)
- cache.remove_value(cache_key)
diff --git a/module/lib/beaker/container.py b/module/lib/beaker/container.py
deleted file mode 100644
index 515e97af6..000000000
--- a/module/lib/beaker/container.py
+++ /dev/null
@@ -1,633 +0,0 @@
-"""Container and Namespace classes"""
-import anydbm
-import cPickle
-import logging
-import os
-import time
-
-import beaker.util as util
-from beaker.exceptions import CreationAbortedError, MissingCacheParameter
-from beaker.synchronization import _threading, file_synchronizer, \
- mutex_synchronizer, NameLock, null_synchronizer
-
-__all__ = ['Value', 'Container', 'ContainerContext',
- 'MemoryContainer', 'DBMContainer', 'NamespaceManager',
- 'MemoryNamespaceManager', 'DBMNamespaceManager', 'FileContainer',
- 'OpenResourceNamespaceManager',
- 'FileNamespaceManager', 'CreationAbortedError']
-
-
-logger = logging.getLogger('beaker.container')
-if logger.isEnabledFor(logging.DEBUG):
- debug = logger.debug
-else:
- def debug(message, *args):
- pass
-
-
-class NamespaceManager(object):
- """Handles dictionary operations and locking for a namespace of
- values.
-
- The implementation for setting and retrieving the namespace data is
- handled by subclasses.
-
- NamespaceManager may be used alone, or may be privately accessed by
- one or more Container objects. Container objects provide per-key
- services like expiration times and automatic recreation of values.
-
- Multiple NamespaceManagers created with a particular name will all
- share access to the same underlying datasource and will attempt to
- synchronize against a common mutex object. The scope of this
- sharing may be within a single process or across multiple
- processes, depending on the type of NamespaceManager used.
-
- The NamespaceManager itself is generally threadsafe, except in the
- case of the DBMNamespaceManager in conjunction with the gdbm dbm
- implementation.
-
- """
-
- @classmethod
- def _init_dependencies(cls):
- pass
-
- def __init__(self, namespace):
- self._init_dependencies()
- self.namespace = namespace
-
- def get_creation_lock(self, key):
- raise NotImplementedError()
-
- def do_remove(self):
- raise NotImplementedError()
-
- def acquire_read_lock(self):
- pass
-
- def release_read_lock(self):
- pass
-
- def acquire_write_lock(self, wait=True):
- return True
-
- def release_write_lock(self):
- pass
-
- def has_key(self, key):
- return self.__contains__(key)
-
- def __getitem__(self, key):
- raise NotImplementedError()
-
- def __setitem__(self, key, value):
- raise NotImplementedError()
-
- def set_value(self, key, value, expiretime=None):
- """Optional set_value() method called by Value.
-
- Allows an expiretime to be passed, for namespace
- implementations which can prune their collections
- using expiretime.
-
- """
- self[key] = value
-
- def __contains__(self, key):
- raise NotImplementedError()
-
- def __delitem__(self, key):
- raise NotImplementedError()
-
- def keys(self):
- raise NotImplementedError()
-
- def remove(self):
- self.do_remove()
-
-
-class OpenResourceNamespaceManager(NamespaceManager):
- """A NamespaceManager where read/write operations require opening/
- closing of a resource which is possibly mutexed.
-
- """
- def __init__(self, namespace):
- NamespaceManager.__init__(self, namespace)
- self.access_lock = self.get_access_lock()
- self.openers = 0
- self.mutex = _threading.Lock()
-
- def get_access_lock(self):
- raise NotImplementedError()
-
- def do_open(self, flags):
- raise NotImplementedError()
-
- def do_close(self):
- raise NotImplementedError()
-
- def acquire_read_lock(self):
- self.access_lock.acquire_read_lock()
- try:
- self.open('r', checkcount = True)
- except:
- self.access_lock.release_read_lock()
- raise
-
- def release_read_lock(self):
- try:
- self.close(checkcount = True)
- finally:
- self.access_lock.release_read_lock()
-
- def acquire_write_lock(self, wait=True):
- r = self.access_lock.acquire_write_lock(wait)
- try:
- if (wait or r):
- self.open('c', checkcount = True)
- return r
- except:
- self.access_lock.release_write_lock()
- raise
-
- def release_write_lock(self):
- try:
- self.close(checkcount=True)
- finally:
- self.access_lock.release_write_lock()
-
- def open(self, flags, checkcount=False):
- self.mutex.acquire()
- try:
- if checkcount:
- if self.openers == 0:
- self.do_open(flags)
- self.openers += 1
- else:
- self.do_open(flags)
- self.openers = 1
- finally:
- self.mutex.release()
-
- def close(self, checkcount=False):
- self.mutex.acquire()
- try:
- if checkcount:
- self.openers -= 1
- if self.openers == 0:
- self.do_close()
- else:
- if self.openers > 0:
- self.do_close()
- self.openers = 0
- finally:
- self.mutex.release()
-
- def remove(self):
- self.access_lock.acquire_write_lock()
- try:
- self.close(checkcount=False)
- self.do_remove()
- finally:
- self.access_lock.release_write_lock()
-
-class Value(object):
- __slots__ = 'key', 'createfunc', 'expiretime', 'expire_argument', 'starttime', 'storedtime',\
- 'namespace'
-
- def __init__(self, key, namespace, createfunc=None, expiretime=None, starttime=None):
- self.key = key
- self.createfunc = createfunc
- self.expire_argument = expiretime
- self.starttime = starttime
- self.storedtime = -1
- self.namespace = namespace
-
- def has_value(self):
- """return true if the container has a value stored.
-
- This is regardless of it being expired or not.
-
- """
- self.namespace.acquire_read_lock()
- try:
- return self.namespace.has_key(self.key)
- finally:
- self.namespace.release_read_lock()
-
- def can_have_value(self):
- return self.has_current_value() or self.createfunc is not None
-
- def has_current_value(self):
- self.namespace.acquire_read_lock()
- try:
- has_value = self.namespace.has_key(self.key)
- if has_value:
- try:
- stored, expired, value = self._get_value()
- return not self._is_expired(stored, expired)
- except KeyError:
- pass
- return False
- finally:
- self.namespace.release_read_lock()
-
- def _is_expired(self, storedtime, expiretime):
- """Return true if this container's value is expired."""
- return (
- (
- self.starttime is not None and
- storedtime < self.starttime
- )
- or
- (
- expiretime is not None and
- time.time() >= expiretime + storedtime
- )
- )
-
- def get_value(self):
- self.namespace.acquire_read_lock()
- try:
- has_value = self.has_value()
- if has_value:
- try:
- stored, expired, value = self._get_value()
- if not self._is_expired(stored, expired):
- return value
- except KeyError:
- # guard against un-mutexed backends raising KeyError
- has_value = False
-
- if not self.createfunc:
- raise KeyError(self.key)
- finally:
- self.namespace.release_read_lock()
-
- has_createlock = False
- creation_lock = self.namespace.get_creation_lock(self.key)
- if has_value:
- if not creation_lock.acquire(wait=False):
- debug("get_value returning old value while new one is created")
- return value
- else:
- debug("lock_creatfunc (didnt wait)")
- has_createlock = True
-
- if not has_createlock:
- debug("lock_createfunc (waiting)")
- creation_lock.acquire()
- debug("lock_createfunc (waited)")
-
- try:
- # see if someone created the value already
- self.namespace.acquire_read_lock()
- try:
- if self.has_value():
- try:
- stored, expired, value = self._get_value()
- if not self._is_expired(stored, expired):
- return value
- except KeyError:
- # guard against un-mutexed backends raising KeyError
- pass
- finally:
- self.namespace.release_read_lock()
-
- debug("get_value creating new value")
- v = self.createfunc()
- self.set_value(v)
- return v
- finally:
- creation_lock.release()
- debug("released create lock")
-
- def _get_value(self):
- value = self.namespace[self.key]
- try:
- stored, expired, value = value
- except ValueError:
- if not len(value) == 2:
- raise
- # Old format: upgrade
- stored, value = value
- expired = self.expire_argument
- debug("get_value upgrading time %r expire time %r", stored, self.expire_argument)
- self.namespace.release_read_lock()
- self.set_value(value, stored)
- self.namespace.acquire_read_lock()
- except TypeError:
- # occurs when the value is None. memcached
- # may yank the rug from under us in which case
- # that's the result
- raise KeyError(self.key)
- return stored, expired, value
-
- def set_value(self, value, storedtime=None):
- self.namespace.acquire_write_lock()
- try:
- if storedtime is None:
- storedtime = time.time()
- debug("set_value stored time %r expire time %r", storedtime, self.expire_argument)
- self.namespace.set_value(self.key, (storedtime, self.expire_argument, value))
- finally:
- self.namespace.release_write_lock()
-
- def clear_value(self):
- self.namespace.acquire_write_lock()
- try:
- debug("clear_value")
- if self.namespace.has_key(self.key):
- try:
- del self.namespace[self.key]
- except KeyError:
- # guard against un-mutexed backends raising KeyError
- pass
- self.storedtime = -1
- finally:
- self.namespace.release_write_lock()
-
-class AbstractDictionaryNSManager(NamespaceManager):
- """A subclassable NamespaceManager that places data in a dictionary.
-
- Subclasses should provide a "dictionary" attribute or descriptor
- which returns a dict-like object. The dictionary will store keys
- that are local to the "namespace" attribute of this manager, so
- ensure that the dictionary will not be used by any other namespace.
-
- e.g.::
-
- import collections
- cached_data = collections.defaultdict(dict)
-
- class MyDictionaryManager(AbstractDictionaryNSManager):
- def __init__(self, namespace):
- AbstractDictionaryNSManager.__init__(self, namespace)
- self.dictionary = cached_data[self.namespace]
-
- The above stores data in a global dictionary called "cached_data",
- which is structured as a dictionary of dictionaries, keyed
- first on namespace name to a sub-dictionary, then on actual
- cache key to value.
-
- """
-
- def get_creation_lock(self, key):
- return NameLock(
- identifier="memorynamespace/funclock/%s/%s" % (self.namespace, key),
- reentrant=True
- )
-
- def __getitem__(self, key):
- return self.dictionary[key]
-
- def __contains__(self, key):
- return self.dictionary.__contains__(key)
-
- def has_key(self, key):
- return self.dictionary.__contains__(key)
-
- def __setitem__(self, key, value):
- self.dictionary[key] = value
-
- def __delitem__(self, key):
- del self.dictionary[key]
-
- def do_remove(self):
- self.dictionary.clear()
-
- def keys(self):
- return self.dictionary.keys()
-
-class MemoryNamespaceManager(AbstractDictionaryNSManager):
- namespaces = util.SyncDict()
-
- def __init__(self, namespace, **kwargs):
- AbstractDictionaryNSManager.__init__(self, namespace)
- self.dictionary = MemoryNamespaceManager.namespaces.get(self.namespace,
- dict)
-
-class DBMNamespaceManager(OpenResourceNamespaceManager):
- def __init__(self, namespace, dbmmodule=None, data_dir=None,
- dbm_dir=None, lock_dir=None, digest_filenames=True, **kwargs):
- self.digest_filenames = digest_filenames
-
- if not dbm_dir and not data_dir:
- raise MissingCacheParameter("data_dir or dbm_dir is required")
- elif dbm_dir:
- self.dbm_dir = dbm_dir
- else:
- self.dbm_dir = data_dir + "/container_dbm"
- util.verify_directory(self.dbm_dir)
-
- if not lock_dir and not data_dir:
- raise MissingCacheParameter("data_dir or lock_dir is required")
- elif lock_dir:
- self.lock_dir = lock_dir
- else:
- self.lock_dir = data_dir + "/container_dbm_lock"
- util.verify_directory(self.lock_dir)
-
- self.dbmmodule = dbmmodule or anydbm
-
- self.dbm = None
- OpenResourceNamespaceManager.__init__(self, namespace)
-
- self.file = util.encoded_path(root= self.dbm_dir,
- identifiers=[self.namespace],
- extension='.dbm',
- digest_filenames=self.digest_filenames)
-
- debug("data file %s", self.file)
- self._checkfile()
-
- def get_access_lock(self):
- return file_synchronizer(identifier=self.namespace,
- lock_dir=self.lock_dir)
-
- def get_creation_lock(self, key):
- return file_synchronizer(
- identifier = "dbmcontainer/funclock/%s" % self.namespace,
- lock_dir=self.lock_dir
- )
-
- def file_exists(self, file):
- if os.access(file, os.F_OK):
- return True
- else:
- for ext in ('db', 'dat', 'pag', 'dir'):
- if os.access(file + os.extsep + ext, os.F_OK):
- return True
-
- return False
-
- def _checkfile(self):
- if not self.file_exists(self.file):
- g = self.dbmmodule.open(self.file, 'c')
- g.close()
-
- def get_filenames(self):
- list = []
- if os.access(self.file, os.F_OK):
- list.append(self.file)
-
- for ext in ('pag', 'dir', 'db', 'dat'):
- if os.access(self.file + os.extsep + ext, os.F_OK):
- list.append(self.file + os.extsep + ext)
- return list
-
- def do_open(self, flags):
- debug("opening dbm file %s", self.file)
- try:
- self.dbm = self.dbmmodule.open(self.file, flags)
- except:
- self._checkfile()
- self.dbm = self.dbmmodule.open(self.file, flags)
-
- def do_close(self):
- if self.dbm is not None:
- debug("closing dbm file %s", self.file)
- self.dbm.close()
-
- def do_remove(self):
- for f in self.get_filenames():
- os.remove(f)
-
- def __getitem__(self, key):
- return cPickle.loads(self.dbm[key])
-
- def __contains__(self, key):
- return self.dbm.has_key(key)
-
- def __setitem__(self, key, value):
- self.dbm[key] = cPickle.dumps(value)
-
- def __delitem__(self, key):
- del self.dbm[key]
-
- def keys(self):
- return self.dbm.keys()
-
-
-class FileNamespaceManager(OpenResourceNamespaceManager):
- def __init__(self, namespace, data_dir=None, file_dir=None, lock_dir=None,
- digest_filenames=True, **kwargs):
- self.digest_filenames = digest_filenames
-
- if not file_dir and not data_dir:
- raise MissingCacheParameter("data_dir or file_dir is required")
- elif file_dir:
- self.file_dir = file_dir
- else:
- self.file_dir = data_dir + "/container_file"
- util.verify_directory(self.file_dir)
-
- if not lock_dir and not data_dir:
- raise MissingCacheParameter("data_dir or lock_dir is required")
- elif lock_dir:
- self.lock_dir = lock_dir
- else:
- self.lock_dir = data_dir + "/container_file_lock"
- util.verify_directory(self.lock_dir)
- OpenResourceNamespaceManager.__init__(self, namespace)
-
- self.file = util.encoded_path(root=self.file_dir,
- identifiers=[self.namespace],
- extension='.cache',
- digest_filenames=self.digest_filenames)
- self.hash = {}
-
- debug("data file %s", self.file)
-
- def get_access_lock(self):
- return file_synchronizer(identifier=self.namespace,
- lock_dir=self.lock_dir)
-
- def get_creation_lock(self, key):
- return file_synchronizer(
- identifier = "filecontainer/funclock/%s" % self.namespace,
- lock_dir = self.lock_dir
- )
-
- def file_exists(self, file):
- return os.access(file, os.F_OK)
-
- def do_open(self, flags):
- if self.file_exists(self.file):
- fh = open(self.file, 'rb')
- try:
- self.hash = cPickle.load(fh)
- except (IOError, OSError, EOFError, cPickle.PickleError, ValueError):
- pass
- fh.close()
-
- self.flags = flags
-
- def do_close(self):
- if self.flags == 'c' or self.flags == 'w':
- fh = open(self.file, 'wb')
- cPickle.dump(self.hash, fh)
- fh.close()
-
- self.hash = {}
- self.flags = None
-
- def do_remove(self):
- try:
- os.remove(self.file)
- except OSError, err:
- # for instance, because we haven't yet used this cache,
- # but client code has asked for a clear() operation...
- pass
- self.hash = {}
-
- def __getitem__(self, key):
- return self.hash[key]
-
- def __contains__(self, key):
- return self.hash.has_key(key)
-
- def __setitem__(self, key, value):
- self.hash[key] = value
-
- def __delitem__(self, key):
- del self.hash[key]
-
- def keys(self):
- return self.hash.keys()
-
-
-#### legacy stuff to support the old "Container" class interface
-
-namespace_classes = {}
-
-ContainerContext = dict
-
-class ContainerMeta(type):
- def __init__(cls, classname, bases, dict_):
- namespace_classes[cls] = cls.namespace_class
- return type.__init__(cls, classname, bases, dict_)
- def __call__(self, key, context, namespace, createfunc=None,
- expiretime=None, starttime=None, **kwargs):
- if namespace in context:
- ns = context[namespace]
- else:
- nscls = namespace_classes[self]
- context[namespace] = ns = nscls(namespace, **kwargs)
- return Value(key, ns, createfunc=createfunc,
- expiretime=expiretime, starttime=starttime)
-
-class Container(object):
- __metaclass__ = ContainerMeta
- namespace_class = NamespaceManager
-
-class FileContainer(Container):
- namespace_class = FileNamespaceManager
-
-class MemoryContainer(Container):
- namespace_class = MemoryNamespaceManager
-
-class DBMContainer(Container):
- namespace_class = DBMNamespaceManager
-
-DbmContainer = DBMContainer
diff --git a/module/lib/beaker/converters.py b/module/lib/beaker/converters.py
deleted file mode 100644
index f0ad34963..000000000
--- a/module/lib/beaker/converters.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
-# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
-def asbool(obj):
- if isinstance(obj, (str, unicode)):
- obj = obj.strip().lower()
- if obj in ['true', 'yes', 'on', 'y', 't', '1']:
- return True
- elif obj in ['false', 'no', 'off', 'n', 'f', '0']:
- return False
- else:
- raise ValueError(
- "String is not true/false: %r" % obj)
- return bool(obj)
-
-def aslist(obj, sep=None, strip=True):
- if isinstance(obj, (str, unicode)):
- lst = obj.split(sep)
- if strip:
- lst = [v.strip() for v in lst]
- return lst
- elif isinstance(obj, (list, tuple)):
- return obj
- elif obj is None:
- return []
- else:
- return [obj]
diff --git a/module/lib/beaker/crypto/__init__.py b/module/lib/beaker/crypto/__init__.py
deleted file mode 100644
index 3e26b0c13..000000000
--- a/module/lib/beaker/crypto/__init__.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from warnings import warn
-
-from beaker.crypto.pbkdf2 import PBKDF2, strxor
-from beaker.crypto.util import hmac, sha1, hmac_sha1, md5
-from beaker import util
-
-keyLength = None
-
-if util.jython:
- try:
- from beaker.crypto.jcecrypto import getKeyLength, aesEncrypt
- keyLength = getKeyLength()
- except ImportError:
- pass
-else:
- try:
- from beaker.crypto.pycrypto import getKeyLength, aesEncrypt, aesDecrypt
- keyLength = getKeyLength()
- except ImportError:
- pass
-
-if not keyLength:
- has_aes = False
-else:
- has_aes = True
-
-if has_aes and keyLength < 32:
- warn('Crypto implementation only supports key lengths up to %d bits. '
- 'Generated session cookies may be incompatible with other '
- 'environments' % (keyLength * 8))
-
-
-def generateCryptoKeys(master_key, salt, iterations):
- # NB: We XOR parts of the keystream into the randomly-generated parts, just
- # in case os.urandom() isn't as random as it should be. Note that if
- # os.urandom() returns truly random data, this will have no effect on the
- # overall security.
- keystream = PBKDF2(master_key, salt, iterations=iterations)
- cipher_key = keystream.read(keyLength)
- return cipher_key
diff --git a/module/lib/beaker/crypto/jcecrypto.py b/module/lib/beaker/crypto/jcecrypto.py
deleted file mode 100644
index 4062d513e..000000000
--- a/module/lib/beaker/crypto/jcecrypto.py
+++ /dev/null
@@ -1,30 +0,0 @@
-"""
-Encryption module that uses the Java Cryptography Extensions (JCE).
-
-Note that in default installations of the Java Runtime Environment, the
-maximum key length is limited to 128 bits due to US export
-restrictions. This makes the generated keys incompatible with the ones
-generated by pycryptopp, which has no such restrictions. To fix this,
-download the "Unlimited Strength Jurisdiction Policy Files" from Sun,
-which will allow encryption using 256 bit AES keys.
-"""
-from javax.crypto import Cipher
-from javax.crypto.spec import SecretKeySpec, IvParameterSpec
-
-import jarray
-
-# Initialization vector filled with zeros
-_iv = IvParameterSpec(jarray.zeros(16, 'b'))
-
-def aesEncrypt(data, key):
- cipher = Cipher.getInstance('AES/CTR/NoPadding')
- skeySpec = SecretKeySpec(key, 'AES')
- cipher.init(Cipher.ENCRYPT_MODE, skeySpec, _iv)
- return cipher.doFinal(data).tostring()
-
-# magic.
-aesDecrypt = aesEncrypt
-
-def getKeyLength():
- maxlen = Cipher.getMaxAllowedKeyLength('AES/CTR/NoPadding')
- return min(maxlen, 256) / 8
diff --git a/module/lib/beaker/crypto/pbkdf2.py b/module/lib/beaker/crypto/pbkdf2.py
deleted file mode 100644
index 96dc5fbb2..000000000
--- a/module/lib/beaker/crypto/pbkdf2.py
+++ /dev/null
@@ -1,342 +0,0 @@
-#!/usr/bin/python
-# -*- coding: ascii -*-
-###########################################################################
-# PBKDF2.py - PKCS#5 v2.0 Password-Based Key Derivation
-#
-# Copyright (C) 2007 Dwayne C. Litzenberger <dlitz@dlitz.net>
-# All rights reserved.
-#
-# Permission to use, copy, modify, and distribute this software and its
-# documentation for any purpose and without fee is hereby granted,
-# provided that the above copyright notice appear in all copies and that
-# both that copyright notice and this permission notice appear in
-# supporting documentation.
-#
-# THE AUTHOR PROVIDES THIS SOFTWARE ``AS IS'' AND ANY EXPRESSED 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 AUTHOR 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.
-#
-# Country of origin: Canada
-#
-###########################################################################
-# Sample PBKDF2 usage:
-# from Crypto.Cipher import AES
-# from PBKDF2 import PBKDF2
-# import os
-#
-# salt = os.urandom(8) # 64-bit salt
-# key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
-# iv = os.urandom(16) # 128-bit IV
-# cipher = AES.new(key, AES.MODE_CBC, iv)
-# ...
-#
-# Sample crypt() usage:
-# from PBKDF2 import crypt
-# pwhash = crypt("secret")
-# alleged_pw = raw_input("Enter password: ")
-# if pwhash == crypt(alleged_pw, pwhash):
-# print "Password good"
-# else:
-# print "Invalid password"
-#
-###########################################################################
-# History:
-#
-# 2007-07-27 Dwayne C. Litzenberger <dlitz@dlitz.net>
-# - Initial Release (v1.0)
-#
-# 2007-07-31 Dwayne C. Litzenberger <dlitz@dlitz.net>
-# - Bugfix release (v1.1)
-# - SECURITY: The PyCrypto XOR cipher (used, if available, in the _strxor
-# function in the previous release) silently truncates all keys to 64
-# bytes. The way it was used in the previous release, this would only be
-# problem if the pseudorandom function that returned values larger than
-# 64 bytes (so SHA1, SHA256 and SHA512 are fine), but I don't like
-# anything that silently reduces the security margin from what is
-# expected.
-#
-###########################################################################
-
-__version__ = "1.1"
-
-from struct import pack
-from binascii import b2a_hex
-from random import randint
-
-from base64 import b64encode
-
-from beaker.crypto.util import hmac as HMAC, hmac_sha1 as SHA1
-
-def strxor(a, b):
- return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
-
-class PBKDF2(object):
- """PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
-
- This implementation takes a passphrase and a salt (and optionally an
- iteration count, a digest module, and a MAC module) and provides a
- file-like object from which an arbitrarily-sized key can be read.
-
- If the passphrase and/or salt are unicode objects, they are encoded as
- UTF-8 before they are processed.
-
- The idea behind PBKDF2 is to derive a cryptographic key from a
- passphrase and a salt.
-
- PBKDF2 may also be used as a strong salted password hash. The
- 'crypt' function is provided for that purpose.
-
- Remember: Keys generated using PBKDF2 are only as strong as the
- passphrases they are derived from.
- """
-
- def __init__(self, passphrase, salt, iterations=1000,
- digestmodule=SHA1, macmodule=HMAC):
- if not callable(macmodule):
- macmodule = macmodule.new
- self.__macmodule = macmodule
- self.__digestmodule = digestmodule
- self._setup(passphrase, salt, iterations, self._pseudorandom)
-
- def _pseudorandom(self, key, msg):
- """Pseudorandom function. e.g. HMAC-SHA1"""
- return self.__macmodule(key=key, msg=msg,
- digestmod=self.__digestmodule).digest()
-
- def read(self, bytes):
- """Read the specified number of key bytes."""
- if self.closed:
- raise ValueError("file-like object is closed")
-
- size = len(self.__buf)
- blocks = [self.__buf]
- i = self.__blockNum
- while size < bytes:
- i += 1
- if i > 0xffffffff:
- # We could return "" here, but
- raise OverflowError("derived key too long")
- block = self.__f(i)
- blocks.append(block)
- size += len(block)
- buf = "".join(blocks)
- retval = buf[:bytes]
- self.__buf = buf[bytes:]
- self.__blockNum = i
- return retval
-
- def __f(self, i):
- # i must fit within 32 bits
- assert (1 <= i <= 0xffffffff)
- U = self.__prf(self.__passphrase, self.__salt + pack("!L", i))
- result = U
- for j in xrange(2, 1+self.__iterations):
- U = self.__prf(self.__passphrase, U)
- result = strxor(result, U)
- return result
-
- def hexread(self, octets):
- """Read the specified number of octets. Return them as hexadecimal.
-
- Note that len(obj.hexread(n)) == 2*n.
- """
- return b2a_hex(self.read(octets))
-
- def _setup(self, passphrase, salt, iterations, prf):
- # Sanity checks:
-
- # passphrase and salt must be str or unicode (in the latter
- # case, we convert to UTF-8)
- if isinstance(passphrase, unicode):
- passphrase = passphrase.encode("UTF-8")
- if not isinstance(passphrase, str):
- raise TypeError("passphrase must be str or unicode")
- if isinstance(salt, unicode):
- salt = salt.encode("UTF-8")
- if not isinstance(salt, str):
- raise TypeError("salt must be str or unicode")
-
- # iterations must be an integer >= 1
- if not isinstance(iterations, (int, long)):
- raise TypeError("iterations must be an integer")
- if iterations < 1:
- raise ValueError("iterations must be at least 1")
-
- # prf must be callable
- if not callable(prf):
- raise TypeError("prf must be callable")
-
- self.__passphrase = passphrase
- self.__salt = salt
- self.__iterations = iterations
- self.__prf = prf
- self.__blockNum = 0
- self.__buf = ""
- self.closed = False
-
- def close(self):
- """Close the stream."""
- if not self.closed:
- del self.__passphrase
- del self.__salt
- del self.__iterations
- del self.__prf
- del self.__blockNum
- del self.__buf
- self.closed = True
-
-def crypt(word, salt=None, iterations=None):
- """PBKDF2-based unix crypt(3) replacement.
-
- The number of iterations specified in the salt overrides the 'iterations'
- parameter.
-
- The effective hash length is 192 bits.
- """
-
- # Generate a (pseudo-)random salt if the user hasn't provided one.
- if salt is None:
- salt = _makesalt()
-
- # salt must be a string or the us-ascii subset of unicode
- if isinstance(salt, unicode):
- salt = salt.encode("us-ascii")
- if not isinstance(salt, str):
- raise TypeError("salt must be a string")
-
- # word must be a string or unicode (in the latter case, we convert to UTF-8)
- if isinstance(word, unicode):
- word = word.encode("UTF-8")
- if not isinstance(word, str):
- raise TypeError("word must be a string or unicode")
-
- # Try to extract the real salt and iteration count from the salt
- if salt.startswith("$p5k2$"):
- (iterations, salt, dummy) = salt.split("$")[2:5]
- if iterations == "":
- iterations = 400
- else:
- converted = int(iterations, 16)
- if iterations != "%x" % converted: # lowercase hex, minimum digits
- raise ValueError("Invalid salt")
- iterations = converted
- if not (iterations >= 1):
- raise ValueError("Invalid salt")
-
- # Make sure the salt matches the allowed character set
- allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
- for ch in salt:
- if ch not in allowed:
- raise ValueError("Illegal character %r in salt" % (ch,))
-
- if iterations is None or iterations == 400:
- iterations = 400
- salt = "$p5k2$$" + salt
- else:
- salt = "$p5k2$%x$%s" % (iterations, salt)
- rawhash = PBKDF2(word, salt, iterations).read(24)
- return salt + "$" + b64encode(rawhash, "./")
-
-# Add crypt as a static method of the PBKDF2 class
-# This makes it easier to do "from PBKDF2 import PBKDF2" and still use
-# crypt.
-PBKDF2.crypt = staticmethod(crypt)
-
-def _makesalt():
- """Return a 48-bit pseudorandom salt for crypt().
-
- This function is not suitable for generating cryptographic secrets.
- """
- binarysalt = "".join([pack("@H", randint(0, 0xffff)) for i in range(3)])
- return b64encode(binarysalt, "./")
-
-def test_pbkdf2():
- """Module self-test"""
- from binascii import a2b_hex
-
- #
- # Test vectors from RFC 3962
- #
-
- # Test 1
- result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1).read(16)
- expected = a2b_hex("cdedb5281bb2f801565a1122b2563515")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # Test 2
- result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1200).hexread(32)
- expected = ("5c08eb61fdf71e4e4ec3cf6ba1f5512b"
- "a7e52ddbc5e5142f708a31e2e62b1e13")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # Test 3
- result = PBKDF2("X"*64, "pass phrase equals block size", 1200).hexread(32)
- expected = ("139c30c0966bc32ba55fdbf212530ac9"
- "c5ec59f1a452f5cc9ad940fea0598ed1")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # Test 4
- result = PBKDF2("X"*65, "pass phrase exceeds block size", 1200).hexread(32)
- expected = ("9ccad6d468770cd51b10e6a68721be61"
- "1a8b4d282601db3b36be9246915ec82a")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- #
- # Other test vectors
- #
-
- # Chunked read
- f = PBKDF2("kickstart", "workbench", 256)
- result = f.read(17)
- result += f.read(17)
- result += f.read(1)
- result += f.read(2)
- result += f.read(3)
- expected = PBKDF2("kickstart", "workbench", 256).read(40)
- if result != expected:
- raise RuntimeError("self-test failed")
-
- #
- # crypt() test vectors
- #
-
- # crypt 1
- result = crypt("cloadm", "exec")
- expected = '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # crypt 2
- result = crypt("gnu", '$p5k2$c$u9HvcT4d$.....')
- expected = '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # crypt 3
- result = crypt("dcl", "tUsch7fU", iterations=13)
- expected = "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL"
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # crypt 4 (unicode)
- result = crypt(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
- '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ')
- expected = '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'
- if result != expected:
- raise RuntimeError("self-test failed")
-
-if __name__ == '__main__':
- test_pbkdf2()
-
-# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/module/lib/beaker/crypto/pycrypto.py b/module/lib/beaker/crypto/pycrypto.py
deleted file mode 100644
index a3eb4d9db..000000000
--- a/module/lib/beaker/crypto/pycrypto.py
+++ /dev/null
@@ -1,31 +0,0 @@
-"""Encryption module that uses pycryptopp or pycrypto"""
-try:
- # Pycryptopp is preferred over Crypto because Crypto has had
- # various periods of not being maintained, and pycryptopp uses
- # the Crypto++ library which is generally considered the 'gold standard'
- # of crypto implementations
- from pycryptopp.cipher import aes
-
- def aesEncrypt(data, key):
- cipher = aes.AES(key)
- return cipher.process(data)
-
- # magic.
- aesDecrypt = aesEncrypt
-
-except ImportError:
- from Crypto.Cipher import AES
-
- def aesEncrypt(data, key):
- cipher = AES.new(key)
-
- data = data + (" " * (16 - (len(data) % 16)))
- return cipher.encrypt(data)
-
- def aesDecrypt(data, key):
- cipher = AES.new(key)
-
- return cipher.decrypt(data).rstrip()
-
-def getKeyLength():
- return 32
diff --git a/module/lib/beaker/crypto/util.py b/module/lib/beaker/crypto/util.py
deleted file mode 100644
index d97e8ce6f..000000000
--- a/module/lib/beaker/crypto/util.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from warnings import warn
-from beaker import util
-
-
-try:
- # Use PyCrypto (if available)
- from Crypto.Hash import HMAC as hmac, SHA as hmac_sha1
- sha1 = hmac_sha1.new
-
-except ImportError:
-
- # PyCrypto not available. Use the Python standard library.
- import hmac
-
- # When using the stdlib, we have to make sure the hmac version and sha
- # version are compatible
- if util.py24:
- from sha import sha as sha1
- import sha as hmac_sha1
- else:
- # NOTE: We have to use the callable with hashlib (hashlib.sha1),
- # otherwise hmac only accepts the sha module object itself
- from hashlib import sha1
- hmac_sha1 = sha1
-
-
-if util.py24:
- from md5 import md5
-else:
- from hashlib import md5
diff --git a/module/lib/beaker/exceptions.py b/module/lib/beaker/exceptions.py
deleted file mode 100644
index cc0eed286..000000000
--- a/module/lib/beaker/exceptions.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""Beaker exception classes"""
-
-class BeakerException(Exception):
- pass
-
-
-class CreationAbortedError(Exception):
- """Deprecated."""
-
-
-class InvalidCacheBackendError(BeakerException, ImportError):
- pass
-
-
-class MissingCacheParameter(BeakerException):
- pass
-
-
-class LockError(BeakerException):
- pass
-
-
-class InvalidCryptoBackendError(BeakerException):
- pass
diff --git a/module/lib/beaker/ext/__init__.py b/module/lib/beaker/ext/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/lib/beaker/ext/__init__.py
+++ /dev/null
diff --git a/module/lib/beaker/ext/database.py b/module/lib/beaker/ext/database.py
deleted file mode 100644
index 701e6f7d2..000000000
--- a/module/lib/beaker/ext/database.py
+++ /dev/null
@@ -1,165 +0,0 @@
-import cPickle
-import logging
-import pickle
-from datetime import datetime
-
-from beaker.container import OpenResourceNamespaceManager, Container
-from beaker.exceptions import InvalidCacheBackendError, MissingCacheParameter
-from beaker.synchronization import file_synchronizer, null_synchronizer
-from beaker.util import verify_directory, SyncDict
-
-log = logging.getLogger(__name__)
-
-sa = None
-pool = None
-types = None
-
-class DatabaseNamespaceManager(OpenResourceNamespaceManager):
- metadatas = SyncDict()
- tables = SyncDict()
-
- @classmethod
- def _init_dependencies(cls):
- global sa, pool, types
- if sa is not None:
- return
- try:
- import sqlalchemy as sa
- import sqlalchemy.pool as pool
- from sqlalchemy import types
- except ImportError:
- raise InvalidCacheBackendError("Database cache backend requires "
- "the 'sqlalchemy' library")
-
- def __init__(self, namespace, url=None, sa_opts=None, optimistic=False,
- table_name='beaker_cache', data_dir=None, lock_dir=None,
- **params):
- """Creates a database namespace manager
-
- ``url``
- SQLAlchemy compliant db url
- ``sa_opts``
- A dictionary of SQLAlchemy keyword options to initialize the engine
- with.
- ``optimistic``
- Use optimistic session locking, note that this will result in an
- additional select when updating a cache value to compare version
- numbers.
- ``table_name``
- The table name to use in the database for the cache.
- """
- OpenResourceNamespaceManager.__init__(self, namespace)
-
- if sa_opts is None:
- sa_opts = params
-
- if lock_dir:
- self.lock_dir = lock_dir
- elif data_dir:
- self.lock_dir = data_dir + "/container_db_lock"
- if self.lock_dir:
- verify_directory(self.lock_dir)
-
- # Check to see if the table's been created before
- url = url or sa_opts['sa.url']
- table_key = url + table_name
- def make_cache():
- # Check to see if we have a connection pool open already
- meta_key = url + table_name
- def make_meta():
- # SQLAlchemy pops the url, this ensures it sticks around
- # later
- sa_opts['sa.url'] = url
- engine = sa.engine_from_config(sa_opts, 'sa.')
- meta = sa.MetaData()
- meta.bind = engine
- return meta
- meta = DatabaseNamespaceManager.metadatas.get(meta_key, make_meta)
- # Create the table object and cache it now
- cache = sa.Table(table_name, meta,
- sa.Column('id', types.Integer, primary_key=True),
- sa.Column('namespace', types.String(255), nullable=False),
- sa.Column('accessed', types.DateTime, nullable=False),
- sa.Column('created', types.DateTime, nullable=False),
- sa.Column('data', types.PickleType, nullable=False),
- sa.UniqueConstraint('namespace')
- )
- cache.create(checkfirst=True)
- return cache
- self.hash = {}
- self._is_new = False
- self.loaded = False
- self.cache = DatabaseNamespaceManager.tables.get(table_key, make_cache)
-
- def get_access_lock(self):
- return null_synchronizer()
-
- def get_creation_lock(self, key):
- return file_synchronizer(
- identifier ="databasecontainer/funclock/%s" % self.namespace,
- lock_dir = self.lock_dir)
-
- def do_open(self, flags):
- # If we already loaded the data, don't bother loading it again
- if self.loaded:
- self.flags = flags
- return
-
- cache = self.cache
- result = sa.select([cache.c.data],
- cache.c.namespace==self.namespace
- ).execute().fetchone()
- if not result:
- self._is_new = True
- self.hash = {}
- else:
- self._is_new = False
- try:
- self.hash = result['data']
- except (IOError, OSError, EOFError, cPickle.PickleError,
- pickle.PickleError):
- log.debug("Couln't load pickle data, creating new storage")
- self.hash = {}
- self._is_new = True
- self.flags = flags
- self.loaded = True
-
- def do_close(self):
- if self.flags is not None and (self.flags == 'c' or self.flags == 'w'):
- cache = self.cache
- if self._is_new:
- cache.insert().execute(namespace=self.namespace, data=self.hash,
- accessed=datetime.now(),
- created=datetime.now())
- self._is_new = False
- else:
- cache.update(cache.c.namespace==self.namespace).execute(
- data=self.hash, accessed=datetime.now())
- self.flags = None
-
- def do_remove(self):
- cache = self.cache
- cache.delete(cache.c.namespace==self.namespace).execute()
- self.hash = {}
-
- # We can retain the fact that we did a load attempt, but since the
- # file is gone this will be a new namespace should it be saved.
- self._is_new = True
-
- def __getitem__(self, key):
- return self.hash[key]
-
- def __contains__(self, key):
- return self.hash.has_key(key)
-
- def __setitem__(self, key, value):
- self.hash[key] = value
-
- def __delitem__(self, key):
- del self.hash[key]
-
- def keys(self):
- return self.hash.keys()
-
-class DatabaseContainer(Container):
- namespace_manager = DatabaseNamespaceManager
diff --git a/module/lib/beaker/ext/google.py b/module/lib/beaker/ext/google.py
deleted file mode 100644
index dd8380d7f..000000000
--- a/module/lib/beaker/ext/google.py
+++ /dev/null
@@ -1,120 +0,0 @@
-import cPickle
-import logging
-from datetime import datetime
-
-from beaker.container import OpenResourceNamespaceManager, Container
-from beaker.exceptions import InvalidCacheBackendError
-from beaker.synchronization import null_synchronizer
-
-log = logging.getLogger(__name__)
-
-db = None
-
-class GoogleNamespaceManager(OpenResourceNamespaceManager):
- tables = {}
-
- @classmethod
- def _init_dependencies(cls):
- global db
- if db is not None:
- return
- try:
- db = __import__('google.appengine.ext.db').appengine.ext.db
- except ImportError:
- raise InvalidCacheBackendError("Datastore cache backend requires the "
- "'google.appengine.ext' library")
-
- def __init__(self, namespace, table_name='beaker_cache', **params):
- """Creates a datastore namespace manager"""
- OpenResourceNamespaceManager.__init__(self, namespace)
-
- def make_cache():
- table_dict = dict(created=db.DateTimeProperty(),
- accessed=db.DateTimeProperty(),
- data=db.BlobProperty())
- table = type(table_name, (db.Model,), table_dict)
- return table
- self.table_name = table_name
- self.cache = GoogleNamespaceManager.tables.setdefault(table_name, make_cache())
- self.hash = {}
- self._is_new = False
- self.loaded = False
- self.log_debug = logging.DEBUG >= log.getEffectiveLevel()
-
- # Google wants namespaces to start with letters, change the namespace
- # to start with a letter
- self.namespace = 'p%s' % self.namespace
-
- def get_access_lock(self):
- return null_synchronizer()
-
- def get_creation_lock(self, key):
- # this is weird, should probably be present
- return null_synchronizer()
-
- def do_open(self, flags):
- # If we already loaded the data, don't bother loading it again
- if self.loaded:
- self.flags = flags
- return
-
- item = self.cache.get_by_key_name(self.namespace)
-
- if not item:
- self._is_new = True
- self.hash = {}
- else:
- self._is_new = False
- try:
- self.hash = cPickle.loads(str(item.data))
- except (IOError, OSError, EOFError, cPickle.PickleError):
- if self.log_debug:
- log.debug("Couln't load pickle data, creating new storage")
- self.hash = {}
- self._is_new = True
- self.flags = flags
- self.loaded = True
-
- def do_close(self):
- if self.flags is not None and (self.flags == 'c' or self.flags == 'w'):
- if self._is_new:
- item = self.cache(key_name=self.namespace)
- item.data = cPickle.dumps(self.hash)
- item.created = datetime.now()
- item.accessed = datetime.now()
- item.put()
- self._is_new = False
- else:
- item = self.cache.get_by_key_name(self.namespace)
- item.data = cPickle.dumps(self.hash)
- item.accessed = datetime.now()
- item.put()
- self.flags = None
-
- def do_remove(self):
- item = self.cache.get_by_key_name(self.namespace)
- item.delete()
- self.hash = {}
-
- # We can retain the fact that we did a load attempt, but since the
- # file is gone this will be a new namespace should it be saved.
- self._is_new = True
-
- def __getitem__(self, key):
- return self.hash[key]
-
- def __contains__(self, key):
- return self.hash.has_key(key)
-
- def __setitem__(self, key, value):
- self.hash[key] = value
-
- def __delitem__(self, key):
- del self.hash[key]
-
- def keys(self):
- return self.hash.keys()
-
-
-class GoogleContainer(Container):
- namespace_class = GoogleNamespaceManager
diff --git a/module/lib/beaker/ext/memcached.py b/module/lib/beaker/ext/memcached.py
deleted file mode 100644
index 96516953f..000000000
--- a/module/lib/beaker/ext/memcached.py
+++ /dev/null
@@ -1,82 +0,0 @@
-from beaker.container import NamespaceManager, Container
-from beaker.exceptions import InvalidCacheBackendError, MissingCacheParameter
-from beaker.synchronization import file_synchronizer, null_synchronizer
-from beaker.util import verify_directory, SyncDict
-import warnings
-
-memcache = None
-
-class MemcachedNamespaceManager(NamespaceManager):
- clients = SyncDict()
-
- @classmethod
- def _init_dependencies(cls):
- global memcache
- if memcache is not None:
- return
- try:
- import pylibmc as memcache
- except ImportError:
- try:
- import cmemcache as memcache
- warnings.warn("cmemcache is known to have serious "
- "concurrency issues; consider using 'memcache' or 'pylibmc'")
- except ImportError:
- try:
- import memcache
- except ImportError:
- raise InvalidCacheBackendError("Memcached cache backend requires either "
- "the 'memcache' or 'cmemcache' library")
-
- def __init__(self, namespace, url=None, data_dir=None, lock_dir=None, **params):
- NamespaceManager.__init__(self, namespace)
-
- if not url:
- raise MissingCacheParameter("url is required")
-
- if lock_dir:
- self.lock_dir = lock_dir
- elif data_dir:
- self.lock_dir = data_dir + "/container_mcd_lock"
- if self.lock_dir:
- verify_directory(self.lock_dir)
-
- self.mc = MemcachedNamespaceManager.clients.get(url, memcache.Client, url.split(';'))
-
- def get_creation_lock(self, key):
- return file_synchronizer(
- identifier="memcachedcontainer/funclock/%s" % self.namespace,lock_dir = self.lock_dir)
-
- def _format_key(self, key):
- return self.namespace + '_' + key.replace(' ', '\302\267')
-
- def __getitem__(self, key):
- return self.mc.get(self._format_key(key))
-
- def __contains__(self, key):
- value = self.mc.get(self._format_key(key))
- return value is not None
-
- def has_key(self, key):
- return key in self
-
- def set_value(self, key, value, expiretime=None):
- if expiretime:
- self.mc.set(self._format_key(key), value, time=expiretime)
- else:
- self.mc.set(self._format_key(key), value)
-
- def __setitem__(self, key, value):
- self.set_value(key, value)
-
- def __delitem__(self, key):
- self.mc.delete(self._format_key(key))
-
- def do_remove(self):
- self.mc.flush_all()
-
- def keys(self):
- raise NotImplementedError("Memcache caching does not support iteration of all cache keys")
-
-class MemcachedContainer(Container):
- namespace_class = MemcachedNamespaceManager
diff --git a/module/lib/beaker/ext/sqla.py b/module/lib/beaker/ext/sqla.py
deleted file mode 100644
index 8c79633c1..000000000
--- a/module/lib/beaker/ext/sqla.py
+++ /dev/null
@@ -1,133 +0,0 @@
-import cPickle
-import logging
-import pickle
-from datetime import datetime
-
-from beaker.container import OpenResourceNamespaceManager, Container
-from beaker.exceptions import InvalidCacheBackendError, MissingCacheParameter
-from beaker.synchronization import file_synchronizer, null_synchronizer
-from beaker.util import verify_directory, SyncDict
-
-
-log = logging.getLogger(__name__)
-
-sa = None
-
-class SqlaNamespaceManager(OpenResourceNamespaceManager):
- binds = SyncDict()
- tables = SyncDict()
-
- @classmethod
- def _init_dependencies(cls):
- global sa
- if sa is not None:
- return
- try:
- import sqlalchemy as sa
- except ImportError:
- raise InvalidCacheBackendError("SQLAlchemy, which is required by "
- "this backend, is not installed")
-
- def __init__(self, namespace, bind, table, data_dir=None, lock_dir=None,
- **kwargs):
- """Create a namespace manager for use with a database table via
- SQLAlchemy.
-
- ``bind``
- SQLAlchemy ``Engine`` or ``Connection`` object
-
- ``table``
- SQLAlchemy ``Table`` object in which to store namespace data.
- This should usually be something created by ``make_cache_table``.
- """
- OpenResourceNamespaceManager.__init__(self, namespace)
-
- if lock_dir:
- self.lock_dir = lock_dir
- elif data_dir:
- self.lock_dir = data_dir + "/container_db_lock"
- if self.lock_dir:
- verify_directory(self.lock_dir)
-
- self.bind = self.__class__.binds.get(str(bind.url), lambda: bind)
- self.table = self.__class__.tables.get('%s:%s' % (bind.url, table.name),
- lambda: table)
- self.hash = {}
- self._is_new = False
- self.loaded = False
-
- def get_access_lock(self):
- return null_synchronizer()
-
- def get_creation_lock(self, key):
- return file_synchronizer(
- identifier ="databasecontainer/funclock/%s" % self.namespace,
- lock_dir=self.lock_dir)
-
- def do_open(self, flags):
- if self.loaded:
- self.flags = flags
- return
- select = sa.select([self.table.c.data],
- (self.table.c.namespace == self.namespace))
- result = self.bind.execute(select).fetchone()
- if not result:
- self._is_new = True
- self.hash = {}
- else:
- self._is_new = False
- try:
- self.hash = result['data']
- except (IOError, OSError, EOFError, cPickle.PickleError,
- pickle.PickleError):
- log.debug("Couln't load pickle data, creating new storage")
- self.hash = {}
- self._is_new = True
- self.flags = flags
- self.loaded = True
-
- def do_close(self):
- if self.flags is not None and (self.flags == 'c' or self.flags == 'w'):
- if self._is_new:
- insert = self.table.insert()
- self.bind.execute(insert, namespace=self.namespace, data=self.hash,
- accessed=datetime.now(), created=datetime.now())
- self._is_new = False
- else:
- update = self.table.update(self.table.c.namespace == self.namespace)
- self.bind.execute(update, data=self.hash, accessed=datetime.now())
- self.flags = None
-
- def do_remove(self):
- delete = self.table.delete(self.table.c.namespace == self.namespace)
- self.bind.execute(delete)
- self.hash = {}
- self._is_new = True
-
- def __getitem__(self, key):
- return self.hash[key]
-
- def __contains__(self, key):
- return self.hash.has_key(key)
-
- def __setitem__(self, key, value):
- self.hash[key] = value
-
- def __delitem__(self, key):
- del self.hash[key]
-
- def keys(self):
- return self.hash.keys()
-
-
-class SqlaContainer(Container):
- namespace_manager = SqlaNamespaceManager
-
-def make_cache_table(metadata, table_name='beaker_cache'):
- """Return a ``Table`` object suitable for storing cached values for the
- namespace manager. Do not create the table."""
- return sa.Table(table_name, metadata,
- sa.Column('namespace', sa.String(255), primary_key=True),
- sa.Column('accessed', sa.DateTime, nullable=False),
- sa.Column('created', sa.DateTime, nullable=False),
- sa.Column('data', sa.PickleType, nullable=False))
diff --git a/module/lib/beaker/middleware.py b/module/lib/beaker/middleware.py
deleted file mode 100644
index 7ba88b37d..000000000
--- a/module/lib/beaker/middleware.py
+++ /dev/null
@@ -1,165 +0,0 @@
-import warnings
-
-try:
- from paste.registry import StackedObjectProxy
- beaker_session = StackedObjectProxy(name="Beaker Session")
- beaker_cache = StackedObjectProxy(name="Cache Manager")
-except:
- beaker_cache = None
- beaker_session = None
-
-from beaker.cache import CacheManager
-from beaker.session import Session, SessionObject
-from beaker.util import coerce_cache_params, coerce_session_params, \
- parse_cache_config_options
-
-
-class CacheMiddleware(object):
- cache = beaker_cache
-
- def __init__(self, app, config=None, environ_key='beaker.cache', **kwargs):
- """Initialize the Cache Middleware
-
- The Cache middleware will make a Cache instance available
- every request under the ``environ['beaker.cache']`` key by
- default. The location in environ can be changed by setting
- ``environ_key``.
-
- ``config``
- dict All settings should be prefixed by 'cache.'. This
- method of passing variables is intended for Paste and other
- setups that accumulate multiple component settings in a
- single dictionary. If config contains *no cache. prefixed
- args*, then *all* of the config options will be used to
- intialize the Cache objects.
-
- ``environ_key``
- Location where the Cache instance will keyed in the WSGI
- environ
-
- ``**kwargs``
- All keyword arguments are assumed to be cache settings and
- will override any settings found in ``config``
-
- """
- self.app = app
- config = config or {}
-
- self.options = {}
-
- # Update the options with the parsed config
- self.options.update(parse_cache_config_options(config))
-
- # Add any options from kwargs, but leave out the defaults this
- # time
- self.options.update(
- parse_cache_config_options(kwargs, include_defaults=False))
-
- # Assume all keys are intended for cache if none are prefixed with
- # 'cache.'
- if not self.options and config:
- self.options = config
-
- self.options.update(kwargs)
- self.cache_manager = CacheManager(**self.options)
- self.environ_key = environ_key
-
- def __call__(self, environ, start_response):
- if environ.get('paste.registry'):
- if environ['paste.registry'].reglist:
- environ['paste.registry'].register(self.cache,
- self.cache_manager)
- environ[self.environ_key] = self.cache_manager
- return self.app(environ, start_response)
-
-
-class SessionMiddleware(object):
- session = beaker_session
-
- def __init__(self, wrap_app, config=None, environ_key='beaker.session',
- **kwargs):
- """Initialize the Session Middleware
-
- The Session middleware will make a lazy session instance
- available every request under the ``environ['beaker.session']``
- key by default. The location in environ can be changed by
- setting ``environ_key``.
-
- ``config``
- dict All settings should be prefixed by 'session.'. This
- method of passing variables is intended for Paste and other
- setups that accumulate multiple component settings in a
- single dictionary. If config contains *no cache. prefixed
- args*, then *all* of the config options will be used to
- intialize the Cache objects.
-
- ``environ_key``
- Location where the Session instance will keyed in the WSGI
- environ
-
- ``**kwargs``
- All keyword arguments are assumed to be session settings and
- will override any settings found in ``config``
-
- """
- config = config or {}
-
- # Load up the default params
- self.options = dict(invalidate_corrupt=True, type=None,
- data_dir=None, key='beaker.session.id',
- timeout=None, secret=None, log_file=None)
-
- # Pull out any config args meant for beaker session. if there are any
- for dct in [config, kwargs]:
- for key, val in dct.iteritems():
- if key.startswith('beaker.session.'):
- self.options[key[15:]] = val
- if key.startswith('session.'):
- self.options[key[8:]] = val
- if key.startswith('session_'):
- warnings.warn('Session options should start with session. '
- 'instead of session_.', DeprecationWarning, 2)
- self.options[key[8:]] = val
-
- # Coerce and validate session params
- coerce_session_params(self.options)
-
- # Assume all keys are intended for cache if none are prefixed with
- # 'cache.'
- if not self.options and config:
- self.options = config
-
- self.options.update(kwargs)
- self.wrap_app = wrap_app
- self.environ_key = environ_key
-
- def __call__(self, environ, start_response):
- session = SessionObject(environ, **self.options)
- if environ.get('paste.registry'):
- if environ['paste.registry'].reglist:
- environ['paste.registry'].register(self.session, session)
- environ[self.environ_key] = session
- environ['beaker.get_session'] = self._get_session
-
- def session_start_response(status, headers, exc_info = None):
- if session.accessed():
- session.persist()
- if session.__dict__['_headers']['set_cookie']:
- cookie = session.__dict__['_headers']['cookie_out']
- if cookie:
- headers.append(('Set-cookie', cookie))
- return start_response(status, headers, exc_info)
- return self.wrap_app(environ, session_start_response)
-
- def _get_session(self):
- return Session({}, use_cookies=False, **self.options)
-
-
-def session_filter_factory(global_conf, **kwargs):
- def filter(app):
- return SessionMiddleware(app, global_conf, **kwargs)
- return filter
-
-
-def session_filter_app_factory(app, global_conf, **kwargs):
- return SessionMiddleware(app, global_conf, **kwargs)
diff --git a/module/lib/beaker/session.py b/module/lib/beaker/session.py
deleted file mode 100644
index 7d465530b..000000000
--- a/module/lib/beaker/session.py
+++ /dev/null
@@ -1,618 +0,0 @@
-import Cookie
-import os
-import random
-import time
-from datetime import datetime, timedelta
-
-from beaker.crypto import hmac as HMAC, hmac_sha1 as SHA1, md5
-from beaker.util import pickle
-
-from beaker import crypto
-from beaker.cache import clsmap
-from beaker.exceptions import BeakerException, InvalidCryptoBackendError
-from base64 import b64encode, b64decode
-
-
-__all__ = ['SignedCookie', 'Session']
-
-getpid = hasattr(os, 'getpid') and os.getpid or (lambda : '')
-
-class SignedCookie(Cookie.BaseCookie):
- """Extends python cookie to give digital signature support"""
- def __init__(self, secret, input=None):
- self.secret = secret
- Cookie.BaseCookie.__init__(self, input)
-
- def value_decode(self, val):
- val = val.strip('"')
- sig = HMAC.new(self.secret, val[40:], SHA1).hexdigest()
-
- # Avoid timing attacks
- invalid_bits = 0
- input_sig = val[:40]
- if len(sig) != len(input_sig):
- return None, val
-
- for a, b in zip(sig, input_sig):
- invalid_bits += a != b
-
- if invalid_bits:
- return None, val
- else:
- return val[40:], val
-
- def value_encode(self, val):
- sig = HMAC.new(self.secret, val, SHA1).hexdigest()
- return str(val), ("%s%s" % (sig, val))
-
-
-class Session(dict):
- """Session object that uses container package for storage.
-
- ``key``
- The name the cookie should be set to.
- ``timeout``
- How long session data is considered valid. This is used
- regardless of the cookie being present or not to determine
- whether session data is still valid.
- ``cookie_domain``
- Domain to use for the cookie.
- ``secure``
- Whether or not the cookie should only be sent over SSL.
- """
- def __init__(self, request, id=None, invalidate_corrupt=False,
- use_cookies=True, type=None, data_dir=None,
- key='beaker.session.id', timeout=None, cookie_expires=True,
- cookie_domain=None, secret=None, secure=False,
- namespace_class=None, **namespace_args):
- if not type:
- if data_dir:
- self.type = 'file'
- else:
- self.type = 'memory'
- else:
- self.type = type
-
- self.namespace_class = namespace_class or clsmap[self.type]
-
- self.namespace_args = namespace_args
-
- self.request = request
- self.data_dir = data_dir
- self.key = key
-
- self.timeout = timeout
- self.use_cookies = use_cookies
- self.cookie_expires = cookie_expires
-
- # Default cookie domain/path
- self._domain = cookie_domain
- self._path = '/'
- self.was_invalidated = False
- self.secret = secret
- self.secure = secure
- self.id = id
- self.accessed_dict = {}
-
- if self.use_cookies:
- cookieheader = request.get('cookie', '')
- if secret:
- try:
- self.cookie = SignedCookie(secret, input=cookieheader)
- except Cookie.CookieError:
- self.cookie = SignedCookie(secret, input=None)
- else:
- self.cookie = Cookie.SimpleCookie(input=cookieheader)
-
- if not self.id and self.key in self.cookie:
- self.id = self.cookie[self.key].value
-
- self.is_new = self.id is None
- if self.is_new:
- self._create_id()
- self['_accessed_time'] = self['_creation_time'] = time.time()
- else:
- try:
- self.load()
- except:
- if invalidate_corrupt:
- self.invalidate()
- else:
- raise
-
- def _create_id(self):
- self.id = md5(
- md5("%f%s%f%s" % (time.time(), id({}), random.random(),
- getpid())).hexdigest(),
- ).hexdigest()
- self.is_new = True
- self.last_accessed = None
- if self.use_cookies:
- self.cookie[self.key] = self.id
- if self._domain:
- self.cookie[self.key]['domain'] = self._domain
- if self.secure:
- self.cookie[self.key]['secure'] = True
- self.cookie[self.key]['path'] = self._path
- if self.cookie_expires is not True:
- if self.cookie_expires is False:
- expires = datetime.fromtimestamp( 0x7FFFFFFF )
- elif isinstance(self.cookie_expires, timedelta):
- expires = datetime.today() + self.cookie_expires
- elif isinstance(self.cookie_expires, datetime):
- expires = self.cookie_expires
- else:
- raise ValueError("Invalid argument for cookie_expires: %s"
- % repr(self.cookie_expires))
- self.cookie[self.key]['expires'] = \
- expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT" )
- self.request['cookie_out'] = self.cookie[self.key].output(header='')
- self.request['set_cookie'] = False
-
- def created(self):
- return self['_creation_time']
- created = property(created)
-
- def _set_domain(self, domain):
- self['_domain'] = domain
- self.cookie[self.key]['domain'] = domain
- self.request['cookie_out'] = self.cookie[self.key].output(header='')
- self.request['set_cookie'] = True
-
- def _get_domain(self):
- return self._domain
-
- domain = property(_get_domain, _set_domain)
-
- def _set_path(self, path):
- self['_path'] = path
- self.cookie[self.key]['path'] = path
- self.request['cookie_out'] = self.cookie[self.key].output(header='')
- self.request['set_cookie'] = True
-
- def _get_path(self):
- return self._path
-
- path = property(_get_path, _set_path)
-
- def _delete_cookie(self):
- self.request['set_cookie'] = True
- self.cookie[self.key] = self.id
- if self._domain:
- self.cookie[self.key]['domain'] = self._domain
- if self.secure:
- self.cookie[self.key]['secure'] = True
- self.cookie[self.key]['path'] = '/'
- expires = datetime.today().replace(year=2003)
- self.cookie[self.key]['expires'] = \
- expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT" )
- self.request['cookie_out'] = self.cookie[self.key].output(header='')
- self.request['set_cookie'] = True
-
- def delete(self):
- """Deletes the session from the persistent storage, and sends
- an expired cookie out"""
- if self.use_cookies:
- self._delete_cookie()
- self.clear()
-
- def invalidate(self):
- """Invalidates this session, creates a new session id, returns
- to the is_new state"""
- self.clear()
- self.was_invalidated = True
- self._create_id()
- self.load()
-
- def load(self):
- "Loads the data from this session from persistent storage"
- self.namespace = self.namespace_class(self.id,
- data_dir=self.data_dir, digest_filenames=False,
- **self.namespace_args)
- now = time.time()
- self.request['set_cookie'] = True
-
- self.namespace.acquire_read_lock()
- timed_out = False
- try:
- self.clear()
- try:
- session_data = self.namespace['session']
-
- # Memcached always returns a key, its None when its not
- # present
- if session_data is None:
- session_data = {
- '_creation_time':now,
- '_accessed_time':now
- }
- self.is_new = True
- except (KeyError, TypeError):
- session_data = {
- '_creation_time':now,
- '_accessed_time':now
- }
- self.is_new = True
-
- if self.timeout is not None and \
- now - session_data['_accessed_time'] > self.timeout:
- timed_out= True
- else:
- # Properly set the last_accessed time, which is different
- # than the *currently* _accessed_time
- if self.is_new or '_accessed_time' not in session_data:
- self.last_accessed = None
- else:
- self.last_accessed = session_data['_accessed_time']
-
- # Update the current _accessed_time
- session_data['_accessed_time'] = now
- self.update(session_data)
- self.accessed_dict = session_data.copy()
- finally:
- self.namespace.release_read_lock()
- if timed_out:
- self.invalidate()
-
- def save(self, accessed_only=False):
- """Saves the data for this session to persistent storage
-
- If accessed_only is True, then only the original data loaded
- at the beginning of the request will be saved, with the updated
- last accessed time.
-
- """
- # Look to see if its a new session that was only accessed
- # Don't save it under that case
- if accessed_only and self.is_new:
- return None
-
- if not hasattr(self, 'namespace'):
- self.namespace = self.namespace_class(
- self.id,
- data_dir=self.data_dir,
- digest_filenames=False,
- **self.namespace_args)
-
- self.namespace.acquire_write_lock()
- try:
- if accessed_only:
- data = dict(self.accessed_dict.items())
- else:
- data = dict(self.items())
-
- # Save the data
- if not data and 'session' in self.namespace:
- del self.namespace['session']
- else:
- self.namespace['session'] = data
- finally:
- self.namespace.release_write_lock()
- if self.is_new:
- self.request['set_cookie'] = True
-
- def revert(self):
- """Revert the session to its original state from its first
- access in the request"""
- self.clear()
- self.update(self.accessed_dict)
-
- # TODO: I think both these methods should be removed. They're from
- # the original mod_python code i was ripping off but they really
- # have no use here.
- def lock(self):
- """Locks this session against other processes/threads. This is
- automatic when load/save is called.
-
- ***use with caution*** and always with a corresponding 'unlock'
- inside a "finally:" block, as a stray lock typically cannot be
- unlocked without shutting down the whole application.
-
- """
- self.namespace.acquire_write_lock()
-
- def unlock(self):
- """Unlocks this session against other processes/threads. This
- is automatic when load/save is called.
-
- ***use with caution*** and always within a "finally:" block, as
- a stray lock typically cannot be unlocked without shutting down
- the whole application.
-
- """
- self.namespace.release_write_lock()
-
-class CookieSession(Session):
- """Pure cookie-based session
-
- Options recognized when using cookie-based sessions are slightly
- more restricted than general sessions.
-
- ``key``
- The name the cookie should be set to.
- ``timeout``
- How long session data is considered valid. This is used
- regardless of the cookie being present or not to determine
- whether session data is still valid.
- ``encrypt_key``
- The key to use for the session encryption, if not provided the
- session will not be encrypted.
- ``validate_key``
- The key used to sign the encrypted session
- ``cookie_domain``
- Domain to use for the cookie.
- ``secure``
- Whether or not the cookie should only be sent over SSL.
-
- """
- def __init__(self, request, key='beaker.session.id', timeout=None,
- cookie_expires=True, cookie_domain=None, encrypt_key=None,
- validate_key=None, secure=False, **kwargs):
-
- if not crypto.has_aes and encrypt_key:
- raise InvalidCryptoBackendError("No AES library is installed, can't generate "
- "encrypted cookie-only Session.")
-
- self.request = request
- self.key = key
- self.timeout = timeout
- self.cookie_expires = cookie_expires
- self.encrypt_key = encrypt_key
- self.validate_key = validate_key
- self.request['set_cookie'] = False
- self.secure = secure
- self._domain = cookie_domain
- self._path = '/'
-
- try:
- cookieheader = request['cookie']
- except KeyError:
- cookieheader = ''
-
- if validate_key is None:
- raise BeakerException("No validate_key specified for Cookie only "
- "Session.")
-
- try:
- self.cookie = SignedCookie(validate_key, input=cookieheader)
- except Cookie.CookieError:
- self.cookie = SignedCookie(validate_key, input=None)
-
- self['_id'] = self._make_id()
- self.is_new = True
-
- # If we have a cookie, load it
- if self.key in self.cookie and self.cookie[self.key].value is not None:
- self.is_new = False
- try:
- self.update(self._decrypt_data())
- except:
- pass
- if self.timeout is not None and time.time() - \
- self['_accessed_time'] > self.timeout:
- self.clear()
- self.accessed_dict = self.copy()
- self._create_cookie()
-
- def created(self):
- return self['_creation_time']
- created = property(created)
-
- def id(self):
- return self['_id']
- id = property(id)
-
- def _set_domain(self, domain):
- self['_domain'] = domain
- self._domain = domain
-
- def _get_domain(self):
- return self._domain
-
- domain = property(_get_domain, _set_domain)
-
- def _set_path(self, path):
- self['_path'] = path
- self._path = path
-
- def _get_path(self):
- return self._path
-
- path = property(_get_path, _set_path)
-
- def _encrypt_data(self):
- """Serialize, encipher, and base64 the session dict"""
- if self.encrypt_key:
- nonce = b64encode(os.urandom(40))[:8]
- encrypt_key = crypto.generateCryptoKeys(self.encrypt_key,
- self.validate_key + nonce, 1)
- data = pickle.dumps(self.copy(), 2)
- return nonce + b64encode(crypto.aesEncrypt(data, encrypt_key))
- else:
- data = pickle.dumps(self.copy(), 2)
- return b64encode(data)
-
- def _decrypt_data(self):
- """Bas64, decipher, then un-serialize the data for the session
- dict"""
- if self.encrypt_key:
- nonce = self.cookie[self.key].value[:8]
- encrypt_key = crypto.generateCryptoKeys(self.encrypt_key,
- self.validate_key + nonce, 1)
- payload = b64decode(self.cookie[self.key].value[8:])
- data = crypto.aesDecrypt(payload, encrypt_key)
- return pickle.loads(data)
- else:
- data = b64decode(self.cookie[self.key].value)
- return pickle.loads(data)
-
- def _make_id(self):
- return md5(md5(
- "%f%s%f%s" % (time.time(), id({}), random.random(), getpid())
- ).hexdigest()
- ).hexdigest()
-
- def save(self, accessed_only=False):
- """Saves the data for this session to persistent storage"""
- if accessed_only and self.is_new:
- return
- if accessed_only:
- self.clear()
- self.update(self.accessed_dict)
- self._create_cookie()
-
- def expire(self):
- """Delete the 'expires' attribute on this Session, if any."""
-
- self.pop('_expires', None)
-
- def _create_cookie(self):
- if '_creation_time' not in self:
- self['_creation_time'] = time.time()
- if '_id' not in self:
- self['_id'] = self._make_id()
- self['_accessed_time'] = time.time()
-
- if self.cookie_expires is not True:
- if self.cookie_expires is False:
- expires = datetime.fromtimestamp( 0x7FFFFFFF )
- elif isinstance(self.cookie_expires, timedelta):
- expires = datetime.today() + self.cookie_expires
- elif isinstance(self.cookie_expires, datetime):
- expires = self.cookie_expires
- else:
- raise ValueError("Invalid argument for cookie_expires: %s"
- % repr(self.cookie_expires))
- self['_expires'] = expires
- elif '_expires' in self:
- expires = self['_expires']
- else:
- expires = None
-
- val = self._encrypt_data()
- if len(val) > 4064:
- raise BeakerException("Cookie value is too long to store")
-
- self.cookie[self.key] = val
- if '_domain' in self:
- self.cookie[self.key]['domain'] = self['_domain']
- elif self._domain:
- self.cookie[self.key]['domain'] = self._domain
- if self.secure:
- self.cookie[self.key]['secure'] = True
-
- self.cookie[self.key]['path'] = self.get('_path', '/')
-
- if expires:
- self.cookie[self.key]['expires'] = \
- expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT" )
- self.request['cookie_out'] = self.cookie[self.key].output(header='')
- self.request['set_cookie'] = True
-
- def delete(self):
- """Delete the cookie, and clear the session"""
- # Send a delete cookie request
- self._delete_cookie()
- self.clear()
-
- def invalidate(self):
- """Clear the contents and start a new session"""
- self.delete()
- self['_id'] = self._make_id()
-
-
-class SessionObject(object):
- """Session proxy/lazy creator
-
- This object proxies access to the actual session object, so that in
- the case that the session hasn't been used before, it will be
- setup. This avoid creating and loading the session from persistent
- storage unless its actually used during the request.
-
- """
- def __init__(self, environ, **params):
- self.__dict__['_params'] = params
- self.__dict__['_environ'] = environ
- self.__dict__['_sess'] = None
- self.__dict__['_headers'] = []
-
- def _session(self):
- """Lazy initial creation of session object"""
- if self.__dict__['_sess'] is None:
- params = self.__dict__['_params']
- environ = self.__dict__['_environ']
- self.__dict__['_headers'] = req = {'cookie_out':None}
- req['cookie'] = environ.get('HTTP_COOKIE')
- if params.get('type') == 'cookie':
- self.__dict__['_sess'] = CookieSession(req, **params)
- else:
- self.__dict__['_sess'] = Session(req, use_cookies=True,
- **params)
- return self.__dict__['_sess']
-
- def __getattr__(self, attr):
- return getattr(self._session(), attr)
-
- def __setattr__(self, attr, value):
- setattr(self._session(), attr, value)
-
- def __delattr__(self, name):
- self._session().__delattr__(name)
-
- def __getitem__(self, key):
- return self._session()[key]
-
- def __setitem__(self, key, value):
- self._session()[key] = value
-
- def __delitem__(self, key):
- self._session().__delitem__(key)
-
- def __repr__(self):
- return self._session().__repr__()
-
- def __iter__(self):
- """Only works for proxying to a dict"""
- return iter(self._session().keys())
-
- def __contains__(self, key):
- return self._session().has_key(key)
-
- def get_by_id(self, id):
- """Loads a session given a session ID"""
- params = self.__dict__['_params']
- session = Session({}, use_cookies=False, id=id, **params)
- if session.is_new:
- return None
- return session
-
- def save(self):
- self.__dict__['_dirty'] = True
-
- def delete(self):
- self.__dict__['_dirty'] = True
- self._session().delete()
-
- def persist(self):
- """Persist the session to the storage
-
- If its set to autosave, then the entire session will be saved
- regardless of if save() has been called. Otherwise, just the
- accessed time will be updated if save() was not called, or
- the session will be saved if save() was called.
-
- """
- if self.__dict__['_params'].get('auto'):
- self._session().save()
- else:
- if self.__dict__.get('_dirty'):
- self._session().save()
- else:
- self._session().save(accessed_only=True)
-
- def dirty(self):
- return self.__dict__.get('_dirty', False)
-
- def accessed(self):
- """Returns whether or not the session has been accessed"""
- return self.__dict__['_sess'] is not None
diff --git a/module/lib/beaker/synchronization.py b/module/lib/beaker/synchronization.py
deleted file mode 100644
index 761303707..000000000
--- a/module/lib/beaker/synchronization.py
+++ /dev/null
@@ -1,381 +0,0 @@
-"""Synchronization functions.
-
-File- and mutex-based mutual exclusion synchronizers are provided,
-as well as a name-based mutex which locks within an application
-based on a string name.
-
-"""
-
-import os
-import sys
-import tempfile
-
-try:
- import threading as _threading
-except ImportError:
- import dummy_threading as _threading
-
-# check for fcntl module
-try:
- sys.getwindowsversion()
- has_flock = False
-except:
- try:
- import fcntl
- has_flock = True
- except ImportError:
- has_flock = False
-
-from beaker import util
-from beaker.exceptions import LockError
-
-__all__ = ["file_synchronizer", "mutex_synchronizer", "null_synchronizer",
- "NameLock", "_threading"]
-
-
-class NameLock(object):
- """a proxy for an RLock object that is stored in a name based
- registry.
-
- Multiple threads can get a reference to the same RLock based on the
- name alone, and synchronize operations related to that name.
-
- """
- locks = util.WeakValuedRegistry()
-
- class NLContainer(object):
- def __init__(self, reentrant):
- if reentrant:
- self.lock = _threading.RLock()
- else:
- self.lock = _threading.Lock()
- def __call__(self):
- return self.lock
-
- def __init__(self, identifier = None, reentrant = False):
- if identifier is None:
- self._lock = NameLock.NLContainer(reentrant)
- else:
- self._lock = NameLock.locks.get(identifier, NameLock.NLContainer,
- reentrant)
-
- def acquire(self, wait = True):
- return self._lock().acquire(wait)
-
- def release(self):
- self._lock().release()
-
-
-_synchronizers = util.WeakValuedRegistry()
-def _synchronizer(identifier, cls, **kwargs):
- return _synchronizers.sync_get((identifier, cls), cls, identifier, **kwargs)
-
-
-def file_synchronizer(identifier, **kwargs):
- if not has_flock or 'lock_dir' not in kwargs:
- return mutex_synchronizer(identifier)
- else:
- return _synchronizer(identifier, FileSynchronizer, **kwargs)
-
-
-def mutex_synchronizer(identifier, **kwargs):
- return _synchronizer(identifier, ConditionSynchronizer, **kwargs)
-
-
-class null_synchronizer(object):
- def acquire_write_lock(self, wait=True):
- return True
- def acquire_read_lock(self):
- pass
- def release_write_lock(self):
- pass
- def release_read_lock(self):
- pass
- acquire = acquire_write_lock
- release = release_write_lock
-
-
-class SynchronizerImpl(object):
- def __init__(self):
- self._state = util.ThreadLocal()
-
- class SyncState(object):
- __slots__ = 'reentrantcount', 'writing', 'reading'
-
- def __init__(self):
- self.reentrantcount = 0
- self.writing = False
- self.reading = False
-
- def state(self):
- if not self._state.has():
- state = SynchronizerImpl.SyncState()
- self._state.put(state)
- return state
- else:
- return self._state.get()
- state = property(state)
-
- def release_read_lock(self):
- state = self.state
-
- if state.writing:
- raise LockError("lock is in writing state")
- if not state.reading:
- raise LockError("lock is not in reading state")
-
- if state.reentrantcount == 1:
- self.do_release_read_lock()
- state.reading = False
-
- state.reentrantcount -= 1
-
- def acquire_read_lock(self, wait = True):
- state = self.state
-
- if state.writing:
- raise LockError("lock is in writing state")
-
- if state.reentrantcount == 0:
- x = self.do_acquire_read_lock(wait)
- if (wait or x):
- state.reentrantcount += 1
- state.reading = True
- return x
- elif state.reading:
- state.reentrantcount += 1
- return True
-
- def release_write_lock(self):
- state = self.state
-
- if state.reading:
- raise LockError("lock is in reading state")
- if not state.writing:
- raise LockError("lock is not in writing state")
-
- if state.reentrantcount == 1:
- self.do_release_write_lock()
- state.writing = False
-
- state.reentrantcount -= 1
-
- release = release_write_lock
-
- def acquire_write_lock(self, wait = True):
- state = self.state
-
- if state.reading:
- raise LockError("lock is in reading state")
-
- if state.reentrantcount == 0:
- x = self.do_acquire_write_lock(wait)
- if (wait or x):
- state.reentrantcount += 1
- state.writing = True
- return x
- elif state.writing:
- state.reentrantcount += 1
- return True
-
- acquire = acquire_write_lock
-
- def do_release_read_lock(self):
- raise NotImplementedError()
-
- def do_acquire_read_lock(self):
- raise NotImplementedError()
-
- def do_release_write_lock(self):
- raise NotImplementedError()
-
- def do_acquire_write_lock(self):
- raise NotImplementedError()
-
-
-class FileSynchronizer(SynchronizerImpl):
- """a synchronizer which locks using flock().
-
- Adapted for Python/multithreads from Apache::Session::Lock::File,
- http://search.cpan.org/src/CWEST/Apache-Session-1.81/Session/Lock/File.pm
-
- This module does not unlink temporary files,
- because it interferes with proper locking. This can cause
- problems on certain systems (Linux) whose file systems (ext2) do not
- perform well with lots of files in one directory. To prevent this
- you should use a script to clean out old files from your lock directory.
-
- """
- def __init__(self, identifier, lock_dir):
- super(FileSynchronizer, self).__init__()
- self._filedescriptor = util.ThreadLocal()
-
- if lock_dir is None:
- lock_dir = tempfile.gettempdir()
- else:
- lock_dir = lock_dir
-
- self.filename = util.encoded_path(
- lock_dir,
- [identifier],
- extension='.lock'
- )
-
- def _filedesc(self):
- return self._filedescriptor.get()
- _filedesc = property(_filedesc)
-
- def _open(self, mode):
- filedescriptor = self._filedesc
- if filedescriptor is None:
- filedescriptor = os.open(self.filename, mode)
- self._filedescriptor.put(filedescriptor)
- return filedescriptor
-
- def do_acquire_read_lock(self, wait):
- filedescriptor = self._open(os.O_CREAT | os.O_RDONLY)
- if not wait:
- try:
- fcntl.flock(filedescriptor, fcntl.LOCK_SH | fcntl.LOCK_NB)
- return True
- except IOError:
- os.close(filedescriptor)
- self._filedescriptor.remove()
- return False
- else:
- fcntl.flock(filedescriptor, fcntl.LOCK_SH)
- return True
-
- def do_acquire_write_lock(self, wait):
- filedescriptor = self._open(os.O_CREAT | os.O_WRONLY)
- if not wait:
- try:
- fcntl.flock(filedescriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
- return True
- except IOError:
- os.close(filedescriptor)
- self._filedescriptor.remove()
- return False
- else:
- fcntl.flock(filedescriptor, fcntl.LOCK_EX)
- return True
-
- def do_release_read_lock(self):
- self._release_all_locks()
-
- def do_release_write_lock(self):
- self._release_all_locks()
-
- def _release_all_locks(self):
- filedescriptor = self._filedesc
- if filedescriptor is not None:
- fcntl.flock(filedescriptor, fcntl.LOCK_UN)
- os.close(filedescriptor)
- self._filedescriptor.remove()
-
-
-class ConditionSynchronizer(SynchronizerImpl):
- """a synchronizer using a Condition."""
-
- def __init__(self, identifier):
- super(ConditionSynchronizer, self).__init__()
-
- # counts how many asynchronous methods are executing
- self.async = 0
-
- # pointer to thread that is the current sync operation
- self.current_sync_operation = None
-
- # condition object to lock on
- self.condition = _threading.Condition(_threading.Lock())
-
- def do_acquire_read_lock(self, wait = True):
- self.condition.acquire()
- try:
- # see if a synchronous operation is waiting to start
- # or is already running, in which case we wait (or just
- # give up and return)
- if wait:
- while self.current_sync_operation is not None:
- self.condition.wait()
- else:
- if self.current_sync_operation is not None:
- return False
-
- self.async += 1
- finally:
- self.condition.release()
-
- if not wait:
- return True
-
- def do_release_read_lock(self):
- self.condition.acquire()
- try:
- self.async -= 1
-
- # check if we are the last asynchronous reader thread
- # out the door.
- if self.async == 0:
- # yes. so if a sync operation is waiting, notifyAll to wake
- # it up
- if self.current_sync_operation is not None:
- self.condition.notifyAll()
- elif self.async < 0:
- raise LockError("Synchronizer error - too many "
- "release_read_locks called")
- finally:
- self.condition.release()
-
- def do_acquire_write_lock(self, wait = True):
- self.condition.acquire()
- try:
- # here, we are not a synchronous reader, and after returning,
- # assuming waiting or immediate availability, we will be.
-
- if wait:
- # if another sync is working, wait
- while self.current_sync_operation is not None:
- self.condition.wait()
- else:
- # if another sync is working,
- # we dont want to wait, so forget it
- if self.current_sync_operation is not None:
- return False
-
- # establish ourselves as the current sync
- # this indicates to other read/write operations
- # that they should wait until this is None again
- self.current_sync_operation = _threading.currentThread()
-
- # now wait again for asyncs to finish
- if self.async > 0:
- if wait:
- # wait
- self.condition.wait()
- else:
- # we dont want to wait, so forget it
- self.current_sync_operation = None
- return False
- finally:
- self.condition.release()
-
- if not wait:
- return True
-
- def do_release_write_lock(self):
- self.condition.acquire()
- try:
- if self.current_sync_operation is not _threading.currentThread():
- raise LockError("Synchronizer error - current thread doesnt "
- "have the write lock")
-
- # reset the current sync operation so
- # another can get it
- self.current_sync_operation = None
-
- # tell everyone to get ready
- self.condition.notifyAll()
- finally:
- # everyone go !!
- self.condition.release()
diff --git a/module/lib/beaker/util.py b/module/lib/beaker/util.py
deleted file mode 100644
index 04c9617c5..000000000
--- a/module/lib/beaker/util.py
+++ /dev/null
@@ -1,302 +0,0 @@
-"""Beaker utilities"""
-
-try:
- import thread as _thread
- import threading as _threading
-except ImportError:
- import dummy_thread as _thread
- import dummy_threading as _threading
-
-from datetime import datetime, timedelta
-import os
-import string
-import types
-import weakref
-import warnings
-import sys
-
-py3k = getattr(sys, 'py3kwarning', False) or sys.version_info >= (3, 0)
-py24 = sys.version_info < (2,5)
-jython = sys.platform.startswith('java')
-
-if py3k or jython:
- import pickle
-else:
- import cPickle as pickle
-
-from beaker.converters import asbool
-from threading import local as _tlocal
-
-
-__all__ = ["ThreadLocal", "Registry", "WeakValuedRegistry", "SyncDict",
- "encoded_path", "verify_directory"]
-
-
-def verify_directory(dir):
- """verifies and creates a directory. tries to
- ignore collisions with other threads and processes."""
-
- tries = 0
- while not os.access(dir, os.F_OK):
- try:
- tries += 1
- os.makedirs(dir)
- except:
- if tries > 5:
- raise
-
-
-def deprecated(message):
- def wrapper(fn):
- def deprecated_method(*args, **kargs):
- warnings.warn(message, DeprecationWarning, 2)
- return fn(*args, **kargs)
- # TODO: use decorator ? functools.wrapper ?
- deprecated_method.__name__ = fn.__name__
- deprecated_method.__doc__ = "%s\n\n%s" % (message, fn.__doc__)
- return deprecated_method
- return wrapper
-
-class ThreadLocal(object):
- """stores a value on a per-thread basis"""
-
- __slots__ = '_tlocal'
-
- def __init__(self):
- self._tlocal = _tlocal()
-
- def put(self, value):
- self._tlocal.value = value
-
- def has(self):
- return hasattr(self._tlocal, 'value')
-
- def get(self, default=None):
- return getattr(self._tlocal, 'value', default)
-
- def remove(self):
- del self._tlocal.value
-
-class SyncDict(object):
- """
- An efficient/threadsafe singleton map algorithm, a.k.a.
- "get a value based on this key, and create if not found or not
- valid" paradigm:
-
- exists && isvalid ? get : create
-
- Designed to work with weakref dictionaries to expect items
- to asynchronously disappear from the dictionary.
-
- Use python 2.3.3 or greater ! a major bug was just fixed in Nov.
- 2003 that was driving me nuts with garbage collection/weakrefs in
- this section.
-
- """
- def __init__(self):
- self.mutex = _thread.allocate_lock()
- self.dict = {}
-
- def get(self, key, createfunc, *args, **kwargs):
- try:
- if self.has_key(key):
- return self.dict[key]
- else:
- return self.sync_get(key, createfunc, *args, **kwargs)
- except KeyError:
- return self.sync_get(key, createfunc, *args, **kwargs)
-
- def sync_get(self, key, createfunc, *args, **kwargs):
- self.mutex.acquire()
- try:
- try:
- if self.has_key(key):
- return self.dict[key]
- else:
- return self._create(key, createfunc, *args, **kwargs)
- except KeyError:
- return self._create(key, createfunc, *args, **kwargs)
- finally:
- self.mutex.release()
-
- def _create(self, key, createfunc, *args, **kwargs):
- self[key] = obj = createfunc(*args, **kwargs)
- return obj
-
- def has_key(self, key):
- return self.dict.has_key(key)
-
- def __contains__(self, key):
- return self.dict.__contains__(key)
- def __getitem__(self, key):
- return self.dict.__getitem__(key)
- def __setitem__(self, key, value):
- self.dict.__setitem__(key, value)
- def __delitem__(self, key):
- return self.dict.__delitem__(key)
- def clear(self):
- self.dict.clear()
-
-
-class WeakValuedRegistry(SyncDict):
- def __init__(self):
- self.mutex = _threading.RLock()
- self.dict = weakref.WeakValueDictionary()
-
-sha1 = None
-def encoded_path(root, identifiers, extension = ".enc", depth = 3,
- digest_filenames=True):
-
- """Generate a unique file-accessible path from the given list of
- identifiers starting at the given root directory."""
- ident = "_".join(identifiers)
-
- global sha1
- if sha1 is None:
- from beaker.crypto import sha1
-
- if digest_filenames:
- if py3k:
- ident = sha1(ident.encode('utf-8')).hexdigest()
- else:
- ident = sha1(ident).hexdigest()
-
- ident = os.path.basename(ident)
-
- tokens = []
- for d in range(1, depth):
- tokens.append(ident[0:d])
-
- dir = os.path.join(root, *tokens)
- verify_directory(dir)
-
- return os.path.join(dir, ident + extension)
-
-
-def verify_options(opt, types, error):
- if not isinstance(opt, types):
- if not isinstance(types, tuple):
- types = (types,)
- coerced = False
- for typ in types:
- try:
- if typ in (list, tuple):
- opt = [x.strip() for x in opt.split(',')]
- else:
- if typ == bool:
- typ = asbool
- opt = typ(opt)
- coerced = True
- except:
- pass
- if coerced:
- break
- if not coerced:
- raise Exception(error)
- elif isinstance(opt, str) and not opt.strip():
- raise Exception("Empty strings are invalid for: %s" % error)
- return opt
-
-
-def verify_rules(params, ruleset):
- for key, types, message in ruleset:
- if key in params:
- params[key] = verify_options(params[key], types, message)
- return params
-
-
-def coerce_session_params(params):
- rules = [
- ('data_dir', (str, types.NoneType), "data_dir must be a string "
- "referring to a directory."),
- ('lock_dir', (str, types.NoneType), "lock_dir must be a string referring to a "
- "directory."),
- ('type', (str, types.NoneType), "Session type must be a string."),
- ('cookie_expires', (bool, datetime, timedelta), "Cookie expires was "
- "not a boolean, datetime, or timedelta instance."),
- ('cookie_domain', (str, types.NoneType), "Cookie domain must be a "
- "string."),
- ('id', (str,), "Session id must be a string."),
- ('key', (str,), "Session key must be a string."),
- ('secret', (str, types.NoneType), "Session secret must be a string."),
- ('validate_key', (str, types.NoneType), "Session encrypt_key must be "
- "a string."),
- ('encrypt_key', (str, types.NoneType), "Session validate_key must be "
- "a string."),
- ('secure', (bool, types.NoneType), "Session secure must be a boolean."),
- ('timeout', (int, types.NoneType), "Session timeout must be an "
- "integer."),
- ('auto', (bool, types.NoneType), "Session is created if accessed."),
- ]
- return verify_rules(params, rules)
-
-
-def coerce_cache_params(params):
- rules = [
- ('data_dir', (str, types.NoneType), "data_dir must be a string "
- "referring to a directory."),
- ('lock_dir', (str, types.NoneType), "lock_dir must be a string referring to a "
- "directory."),
- ('type', (str,), "Cache type must be a string."),
- ('enabled', (bool, types.NoneType), "enabled must be true/false "
- "if present."),
- ('expire', (int, types.NoneType), "expire must be an integer representing "
- "how many seconds the cache is valid for"),
- ('regions', (list, tuple, types.NoneType), "Regions must be a "
- "comma seperated list of valid regions")
- ]
- return verify_rules(params, rules)
-
-
-def parse_cache_config_options(config, include_defaults=True):
- """Parse configuration options and validate for use with the
- CacheManager"""
-
- # Load default cache options
- if include_defaults:
- options= dict(type='memory', data_dir=None, expire=None,
- log_file=None)
- else:
- options = {}
- for key, val in config.iteritems():
- if key.startswith('beaker.cache.'):
- options[key[13:]] = val
- if key.startswith('cache.'):
- options[key[6:]] = val
- coerce_cache_params(options)
-
- # Set cache to enabled if not turned off
- if 'enabled' not in options:
- options['enabled'] = True
-
- # Configure region dict if regions are available
- regions = options.pop('regions', None)
- if regions:
- region_configs = {}
- for region in regions:
- # Setup the default cache options
- region_options = dict(data_dir=options.get('data_dir'),
- lock_dir=options.get('lock_dir'),
- type=options.get('type'),
- enabled=options['enabled'],
- expire=options.get('expire'))
- region_len = len(region) + 1
- for key in options.keys():
- if key.startswith('%s.' % region):
- region_options[key[region_len:]] = options.pop(key)
- coerce_cache_params(region_options)
- region_configs[region] = region_options
- options['cache_regions'] = region_configs
- return options
-
-def func_namespace(func):
- """Generates a unique namespace for a function"""
- kls = None
- if hasattr(func, 'im_func'):
- kls = func.im_class
- func = func.im_func
-
- if kls:
- return '%s.%s' % (kls.__module__, kls.__name__)
- else:
- return '%s.%s' % (func.__module__, func.__name__)
diff --git a/module/lib/bottle.py b/module/lib/bottle.py
deleted file mode 100644
index 2c243278e..000000000
--- a/module/lib/bottle.py
+++ /dev/null
@@ -1,2922 +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) 2011, Marcel Hellkamp.
-License: MIT (see LICENSE.txt for details)
-"""
-
-from __future__ import with_statement
-
-__author__ = 'Marcel Hellkamp'
-__version__ = '0.10.2'
-__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 sys
-import base64
-import cgi
-import email.utils
-import functools
-import hmac
-import httplib
-import imp
-import itertools
-import mimetypes
-import os
-import re
-import subprocess
-import tempfile
-import thread
-import threading
-import time
-import warnings
-
-from Cookie import SimpleCookie
-from datetime import date as datedate, datetime, timedelta
-from tempfile import TemporaryFile
-from traceback import format_exc, print_exc
-from urlparse import urljoin, SplitResult as UrlSplitResult
-
-# Workaround for a bug in some versions of lib2to3 (fixed on CPython 2.7 and 3.2)
-import urllib
-urlencode = urllib.urlencode
-urlquote = urllib.quote
-urlunquote = urllib.unquote
-
-try: from collections import MutableMapping as DictMixin
-except ImportError: # pragma: no cover
- from UserDict import DictMixin
-
-try: from urlparse import parse_qs
-except ImportError: # pragma: no cover
- from cgi import parse_qs
-
-try: import cPickle as pickle
-except ImportError: # pragma: no cover
- import pickle
-
-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: # pragma: no cover
- try: from django.utils.simplejson import dumps as json_dumps, loads as json_lds
- except ImportError: # pragma: no cover
- def json_dumps(data):
- raise ImportError("JSON support requires Python 2.6 or simplejson.")
- json_lds = json_dumps
-
-py3k = sys.version_info >= (3,0,0)
-NCTextIOWrapper = None
-
-if py3k: # pragma: no cover
- json_loads = lambda s: json_lds(touni(s))
- # See Request.POST
- from io import BytesIO
- def touni(x, enc='utf8', err='strict'):
- """ Convert anything to unicode """
- return str(x, enc, err) if isinstance(x, bytes) else str(x)
- if sys.version_info < (3,2,0):
- from io import TextIOWrapper
- class NCTextIOWrapper(TextIOWrapper):
- ''' Garbage collecting an io.TextIOWrapper(buffer) instance closes
- the wrapped buffer. This subclass keeps it open. '''
- def close(self): pass
-else:
- json_loads = json_lds
- from StringIO import StringIO as BytesIO
- bytes = str
- def touni(x, enc='utf8', err='strict'):
- """ Convert anything to unicode """
- return x if isinstance(x, unicode) else unicode(str(x), enc, err)
-
-def tob(data, enc='utf8'):
- """ Convert anything to bytes """
- return data.encode(enc) if isinstance(data, unicode) else bytes(data)
-
-tonat = touni if py3k else tob
-tonat.__doc__ = """ Convert anything to native strings """
-
-def try_update_wrapper(wrapper, wrapped, *a, **ka):
- try: # Bug: functools breaks if wrapper is an instane method
- functools.update_wrapper(wrapper, wrapped, *a, **ka)
- except AttributeError: pass
-
-# Backward compatibility
-def depr(message):
- warnings.warn(message, DeprecationWarning, stacklevel=3)
-
-
-# Small helpers
-def makelist(data):
- 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 CachedProperty(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
-
-cached_property = CachedProperty
-
-
-class lazy_attribute(object): # Does not need configuration -> lower-case name
- ''' 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: These 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.iterallitems():
- 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 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/<page>`). 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.re_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*\.\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, e:
- 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), e: # 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, e:
- 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:
- try_update_wrapper(callback, self.callback)
- return callback
-
-
-
-
-
-
-###############################################################################
-# Application Object ###########################################################
-###############################################################################
-
-
-class Bottle(object):
- """ WSGI application """
-
- def __init__(self, catchall=True, autojson=True, config=None):
- """ Create a new bottle instance.
- You usually don't do that. Use `bottle.app.push()` instead.
- """
- 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 = {}
- #: If true, most exceptions are catched and returned as :exc:`HTTPError`
- self.config = ConfigDict(config or {})
- 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 = filter(None, prefix.split('/'))
- 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, rs.headers)
- finally:
- request.path_shift(-path_depth)
-
- if not prefix.endswith('/'):
- self.route('/' + '/'.join(parts), callback=mountpoint, **options)
-
- 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.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 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.routes.append(route)
- self.router.add(rule, verb, route, name=name)
- if DEBUG: route.prepare()
- 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. """
- 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:
- route, args = self.router.match(environ)
- environ['route.handle'] = environ['bottle.route'] = route
- environ['route.url_args'] = args
- return route.call(**args)
- except HTTPResponse, r:
- return r
- except RouteReset:
- route.reset()
- return self._handle(environ)
- except (KeyboardInterrupt, SystemExit, MemoryError):
- raise
- except Exception, e:
- 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, request, response, 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, request, response)
- if isinstance(out, HTTPResponse):
- out.apply(response)
- return self._cast(out.output, request, response)
-
- # 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 = out.next()
- while not first:
- first = out.next()
- except StopIteration:
- return self._cast('', request, response)
- except HTTPResponse, e:
- first = e
- except Exception, e:
- first = HTTPError(500, 'Unhandled exception', e, format_exc(10))
- if isinstance(e, (KeyboardInterrupt, SystemExit, MemoryError))\
- or not self.catchall:
- raise
- # These are the inner types allowed in iterator or generator objects.
- if isinstance(first, HTTPResponse):
- return self._cast(first, request, response)
- if isinstance(first, bytes):
- return itertools.chain([first], out)
- if isinstance(first, unicode):
- return itertools.imap(lambda x: x.encode(response.charset),
- itertools.chain([first], out))
- return self._cast(HTTPError(500, 'Unsupported response type: %s'\
- % type(first)), request, response)
-
- def wsgi(self, environ, start_response):
- """ The bottle WSGI-interface. """
- try:
- environ['bottle.app'] = self
- request.bind(environ)
- response.bind()
- out = self._cast(self._handle(environ), request, response)
- # rfc2616 section 4.3
- if response._status_code in (100, 101, 204, 304)\
- or request.method == 'HEAD':
- if hasattr(out, 'close'): out.close()
- out = []
- start_response(response._status_line, list(response.iter_headers()))
- return out
- except (KeyboardInterrupt, SystemExit, MemoryError):
- raise
- except Exception, e:
- if not self.catchall: raise
- err = '<h1>Critical error while processing request: %s</h1>' \
- % environ.get('PATH_INFO', '/')
- if DEBUG:
- err += '<h2>Error:</h2>\n<pre>%s</pre>\n' % repr(e)
- err += '<h2>Traceback:</h2>\n<pre>%s</pre>\n' % format_exc(10)
- environ['wsgi.errors'].write(err) #TODO: wsgi.error should not get html
- start_response('500 INTERNAL SERVER ERROR', [('Content-Type', 'text/html')])
- 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(DictMixin):
- """ 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
-
- 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
-
- @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',''))
- return FormsDict((c.key, c.value) for c in cookies.itervalues())
-
- 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`. '''
- data = parse_qs(self.query_string, keep_blank_values=True)
- get = self.environ['bottle.get'] = FormsDict()
- for key, values in data.iteritems():
- for value in values:
- 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.iterallitems():
- 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.iterallitems():
- params[key] = value
- for key, value in self.forms.iterallitems():
- 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.iterallitems():
- 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 []:
- 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 __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.body = body
- self._cookies = None
- self._headers = {'Content-Type': [self.default_content_type]}
- 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):
- depr('BaseReuqest.status will change to return a string in 0.11. Use'\
- ' status_line and status_code to make sure.') #0.10
- return self._status_code
-
- 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 updates accordingly. The return value is
- always a numeric code. ''')
- 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.iteritems()
- 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.iteritems():
- 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
-
-
-class LocalRequest(BaseRequest, threading.local):
- ''' A thread-local subclass of :class:`BaseRequest`. '''
- def __init__(self): pass
- bind = BaseRequest.__init__
-
-
-class LocalResponse(BaseResponse, threading.local):
- ''' A thread-local subclass of :class:`BaseResponse`. '''
- bind = BaseResponse.__init__
-
-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).iteritems())
- 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 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):
- for key, values in self.dict.iteritems():
- for value in values:
- yield key, value
-
- # 2to3 is not able to fix these automatically.
- keys = iterkeys if py3k else lambda self: list(self.iterkeys())
- values = itervalues if py3k else lambda self: list(self.itervalues())
- items = iteritems if py3k else lambda self: list(self.iteritems())
- allitems = iterallitems if py3k else lambda self: list(self.iterallitems())
-
- 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, e:
- 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. Attribues are automatiically 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'
-
- def getunicode(self, name, default=None, encoding=None):
- value, enc = self.get(name, default), encoding or self.input_encoding
- try:
- if isinstance(value, bytes): # Python 2 WSGI
- return value.decode(enc)
- elif isinstance(value, unicode): # Python 3 WSGI
- return value.encode('latin1').decode(enc)
- return value
- except UnicodeError, e:
- return default
-
- def __getattr__(self, name): return self.getunicode(name, default=u'')
-
-
-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 map(_hkey, 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).iteritems(): 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'):
- if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr))
-
- def __iter__(self):
- read, buff = self.fp.read, self.buffer_size
- while True:
- part = read(buff)
- if not part: break
- 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 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'] = 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')
- 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':
- #TODO: Add 2to3 save base64[encode/decode] functions.
- user, pwd = touni(base64.b64decode(tob(data))).split(':',1)
- return user, pwd
- except (KeyError, ValueError):
- return None
-
-
-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('&','&amp;').replace('<','&lt;').replace('>','&gt;')\
- .replace('"','&quot;').replace("'",'&#039;')
-
-
-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','&#13;').replace('\t','&#9;')
-
-
-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.iteritems():
- 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
-
-
-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
-
-
-for name in '''route get post put delete error mount
- hook install uninstall'''.split():
- globals()[name] = make_default_app_wrapper(name)
-url = make_default_app_wrapper('get_url')
-del name
-
-
-
-
-
-
-###############################################################################
-# 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 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:
- print "WARNING: Auto-reloading does not work with Fapws3."
- print " (Fapws3 breaks python thread support)"
- 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
- 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 = [PasteServer, CherryPyServer, TwistedServer, 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,
- '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
-
-def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,
- interval=1, reloader=False, quiet=False, plugins=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:
- 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
-
- stderr = sys.stderr.write
-
- try:
- 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 server starting up (using %s)...\n" % 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 (SyntaxError, ImportError):
- if not reloader: raise
- if not getattr(server, 'quiet', False): print_exc()
- sys.exit(3)
- finally:
- if not getattr(server, 'quiet', False): stderr('Shutdown...\n')
-
-
-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 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 files.iteritems():
- 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 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 = map(os.path.abspath, 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 fname:
- with open(fname, "rb") as f:
- return f.read().decode(self.encoding)
-
-
-class SimpleTALTemplate(BaseTemplate):
- ''' Untested! '''
- def prepare(self, **options):
- from simpletal import simpleTAL
- # TODO: add option to load METAL files during render
- 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)
- # TODO: maybe reuse a context instead of always creating one
- 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 '<string>', '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 = line if isinstance(line, unicode)\
- else unicode(line, encoding=self.encoding)
- if lineno <= 2:
- m = re.search(r"%.*coding[:=]\s*([-\w\.]+)", line)
- if m: self.encoding = m.group(1)
- if m: line = line.replace('coding','coding (removed)')
- if line.strip()[:2].count('%') == 1:
- 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.iteritems())
-
-#: 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()
- <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
- <html>
- <head>
- <title>Error {{e.status}}: {{status_name}}</title>
- <style type="text/css">
- html {background-color: #eee; font-family: sans;}
- body {background-color: #fff; border: 1px solid #ddd;
- padding: 15px; margin: 15px;}
- pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;}
- </style>
- </head>
- <body>
- <h1>Error {{e.status}}: {{status_name}}</h1>
- <p>Sorry, the requested URL <tt>{{repr(request.url)}}</tt>
- caused an error:</p>
- <pre>{{e.output}}</pre>
- %if DEBUG and e.exception:
- <h2>Exception:</h2>
- <pre>{{repr(e.exception)}}</pre>
- %end
- %if DEBUG and e.traceback:
- <h2>Traceback:</h2>
- <pre>{{e.traceback}}</pre>
- %end
- </body>
- </html>
-%except ImportError:
- <b>ImportError:</b> Could not generate the error page. Please add bottle to
- the import path.
-%end
-"""
-
-#: A thread-safe instance of :class:`Request` representing the `current` request.
-request = Request()
-
-#: A thread-safe instance of :class:`Response` used to build the HTTP response.
-response = Response()
-
-#: 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:
- print 'Bottle', __version__; sys.exit(0)
- if not args:
- parser.print_help()
- print '\nError: No application specified.\n'
- sys.exit(1)
-
- try:
- sys.path.insert(0, '.')
- sys.modules.setdefault('bottle', sys.modules['__main__'])
- except (AttributeError, ImportError), e:
- parser.error(e.args[0])
-
- if opt.bind and ':' in opt.bind:
- host, port = opt.bind.rsplit(':', 1)
- else:
- host, port = (opt.bind or 'localhost'), 8080
-
- debug(opt.debug)
- run(args[0], host=host, port=port, server=opt.server, reloader=opt.reload, plugins=opt.plugin)
-
-# THE END
diff --git a/module/lib/feedparser.py b/module/lib/feedparser.py
deleted file mode 100644
index a746ed8f5..000000000
--- a/module/lib/feedparser.py
+++ /dev/null
@@ -1,3885 +0,0 @@
-#!/usr/bin/env python
-"""Universal feed parser
-
-Handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds
-
-Visit http://feedparser.org/ for the latest version
-Visit http://feedparser.org/docs/ for the latest documentation
-
-Required: Python 2.4 or later
-Recommended: CJKCodecs and iconv_codec <http://cjkpython.i18n.org/>
-"""
-
-__version__ = "5.0"
-__license__ = """Copyright (c) 2002-2008, Mark Pilgrim, 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.
-
-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."""
-__author__ = "Mark Pilgrim <http://diveintomark.org/>"
-__contributors__ = ["Jason Diamond <http://injektilo.org/>",
- "John Beimler <http://john.beimler.org/>",
- "Fazal Majid <http://www.majid.info/mylos/weblog/>",
- "Aaron Swartz <http://aaronsw.com/>",
- "Kevin Marks <http://epeus.blogspot.com/>",
- "Sam Ruby <http://intertwingly.net/>",
- "Ade Oshineye <http://blog.oshineye.com/>",
- "Martin Pool <http://sourcefrog.net/>",
- "Kurt McKee <http://kurtmckee.org/>"]
-_debug = 0
-
-# HTTP "User-Agent" header to send to servers when downloading feeds.
-# If you are embedding feedparser in a larger application, you should
-# change this to your application name and URL.
-USER_AGENT = "UniversalFeedParser/%s +http://feedparser.org/" % __version__
-
-# HTTP "Accept" header to send to servers when downloading feeds. If you don't
-# want to send an Accept header, set this to None.
-ACCEPT_HEADER = "application/atom+xml,application/rdf+xml,application/rss+xml,application/x-netcdf,application/xml;q=0.9,text/xml;q=0.2,*/*;q=0.1"
-
-# List of preferred XML parsers, by SAX driver name. These will be tried first,
-# but if they're not installed, Python will keep searching through its own list
-# of pre-installed parsers until it finds one that supports everything we need.
-PREFERRED_XML_PARSERS = ["drv_libxml2"]
-
-# If you want feedparser to automatically run HTML markup through HTML Tidy, set
-# this to 1. Requires mxTidy <http://www.egenix.com/files/python/mxTidy.html>
-# or utidylib <http://utidylib.berlios.de/>.
-TIDY_MARKUP = 0
-
-# List of Python interfaces for HTML Tidy, in order of preference. Only useful
-# if TIDY_MARKUP = 1
-PREFERRED_TIDY_INTERFACES = ["uTidy", "mxTidy"]
-
-# If you want feedparser to automatically resolve all relative URIs, set this
-# to 1.
-RESOLVE_RELATIVE_URIS = 1
-
-# If you want feedparser to automatically sanitize all potentially unsafe
-# HTML content, set this to 1.
-SANITIZE_HTML = 1
-
-# ---------- Python 3 modules (make it work if possible) ----------
-try:
- import rfc822
-except ImportError:
- from email import _parseaddr as rfc822
-
-try:
- # Python 3.1 introduces bytes.maketrans and simultaneously
- # deprecates string.maketrans; use bytes.maketrans if possible
- _maketrans = bytes.maketrans
-except (NameError, AttributeError):
- import string
- _maketrans = string.maketrans
-
-# base64 support for Atom feeds that contain embedded binary data
-try:
- import base64, binascii
- # Python 3.1 deprecates decodestring in favor of decodebytes
- _base64decode = getattr(base64, 'decodebytes', base64.decodestring)
-except:
- base64 = binascii = None
-
-def _s2bytes(s):
- # Convert a UTF-8 str to bytes if the interpreter is Python 3
- try:
- return bytes(s, 'utf8')
- except (NameError, TypeError):
- # In Python 2.5 and below, bytes doesn't exist (NameError)
- # In Python 2.6 and above, bytes and str are the same (TypeError)
- return s
-
-def _l2bytes(l):
- # Convert a list of ints to bytes if the interpreter is Python 3
- try:
- if bytes is not str:
- # In Python 2.6 and above, this call won't raise an exception
- # but it will return bytes([65]) as '[65]' instead of 'A'
- return bytes(l)
- raise NameError
- except NameError:
- return ''.join(map(chr, l))
-
-# If you want feedparser to allow all URL schemes, set this to ()
-# List culled from Python's urlparse documentation at:
-# http://docs.python.org/library/urlparse.html
-# as well as from "URI scheme" at Wikipedia:
-# https://secure.wikimedia.org/wikipedia/en/wiki/URI_scheme
-# Many more will likely need to be added!
-ACCEPTABLE_URI_SCHEMES = (
- 'file', 'ftp', 'gopher', 'h323', 'hdl', 'http', 'https', 'imap', 'mailto',
- 'mms', 'news', 'nntp', 'prospero', 'rsync', 'rtsp', 'rtspu', 'sftp',
- 'shttp', 'sip', 'sips', 'snews', 'svn', 'svn+ssh', 'telnet', 'wais',
- # Additional common-but-unofficial schemes
- 'aim', 'callto', 'cvs', 'facetime', 'feed', 'git', 'gtalk', 'irc', 'ircs',
- 'irc6', 'itms', 'mms', 'msnim', 'skype', 'ssh', 'smb', 'svn', 'ymsg',
-)
-#ACCEPTABLE_URI_SCHEMES = ()
-
-# ---------- required modules (should come with any Python distribution) ----------
-import sgmllib, re, sys, copy, urlparse, time, types, cgi, urllib, urllib2, datetime
-try:
- from io import BytesIO as _StringIO
-except ImportError:
- try:
- from cStringIO import StringIO as _StringIO
- except:
- from StringIO import StringIO as _StringIO
-
-# ---------- optional modules (feedparser will work without these, but with reduced functionality) ----------
-
-# gzip is included with most Python distributions, but may not be available if you compiled your own
-try:
- import gzip
-except:
- gzip = None
-try:
- import zlib
-except:
- zlib = None
-
-# If a real XML parser is available, feedparser will attempt to use it. feedparser has
-# been tested with the built-in SAX parser, PyXML, and libxml2. On platforms where the
-# Python distribution does not come with an XML parser (such as Mac OS X 10.2 and some
-# versions of FreeBSD), feedparser will quietly fall back on regex-based parsing.
-try:
- import xml.sax
- xml.sax.make_parser(PREFERRED_XML_PARSERS) # test for valid parsers
- from xml.sax.saxutils import escape as _xmlescape
- _XML_AVAILABLE = 1
-except:
- _XML_AVAILABLE = 0
- def _xmlescape(data,entities={}):
- data = data.replace('&', '&amp;')
- data = data.replace('>', '&gt;')
- data = data.replace('<', '&lt;')
- for char, entity in entities:
- data = data.replace(char, entity)
- return data
-
-# cjkcodecs and iconv_codec provide support for more character encodings.
-# Both are available from http://cjkpython.i18n.org/
-try:
- import cjkcodecs.aliases
-except:
- pass
-try:
- import iconv_codec
-except:
- pass
-
-# chardet library auto-detects character encodings
-# Download from http://chardet.feedparser.org/
-try:
- import chardet
- if _debug:
- import chardet.constants
- chardet.constants._debug = 1
-except:
- chardet = None
-
-# reversable htmlentitydefs mappings for Python 2.2
-try:
- from htmlentitydefs import name2codepoint, codepoint2name
-except:
- import htmlentitydefs
- name2codepoint={}
- codepoint2name={}
- for (name,codepoint) in htmlentitydefs.entitydefs.iteritems():
- if codepoint.startswith('&#'): codepoint=unichr(int(codepoint[2:-1]))
- name2codepoint[name]=ord(codepoint)
- codepoint2name[ord(codepoint)]=name
-
-# BeautifulSoup parser used for parsing microformats from embedded HTML content
-# http://www.crummy.com/software/BeautifulSoup/
-# feedparser is tested with BeautifulSoup 3.0.x, but it might work with the
-# older 2.x series. If it doesn't, and you can figure out why, I'll accept a
-# patch and modify the compatibility statement accordingly.
-try:
- import BeautifulSoup
-except:
- BeautifulSoup = None
-
-# ---------- don't touch these ----------
-class ThingsNobodyCaresAboutButMe(Exception): pass
-class CharacterEncodingOverride(ThingsNobodyCaresAboutButMe): pass
-class CharacterEncodingUnknown(ThingsNobodyCaresAboutButMe): pass
-class NonXMLContentType(ThingsNobodyCaresAboutButMe): pass
-class UndeclaredNamespace(Exception): pass
-
-sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*')
-sgmllib.special = re.compile('<!')
-sgmllib.charref = re.compile('&#(\d+|[xX][0-9a-fA-F]+);')
-
-if sgmllib.endbracket.search(' <').start(0):
- class EndBracketRegEx:
- def __init__(self):
- # Overriding the built-in sgmllib.endbracket regex allows the
- # parser to find angle brackets embedded in element attributes.
- self.endbracket = re.compile('''([^'"<>]|"[^"]*"(?=>|/|\s|\w+=)|'[^']*'(?=>|/|\s|\w+=))*(?=[<>])|.*?(?=[<>])''')
- def search(self,string,index=0):
- match = self.endbracket.match(string,index)
- if match is not None:
- # Returning a new object in the calling thread's context
- # resolves a thread-safety.
- return EndBracketMatch(match)
- return None
- class EndBracketMatch:
- def __init__(self, match):
- self.match = match
- def start(self, n):
- return self.match.end(n)
- sgmllib.endbracket = EndBracketRegEx()
-
-SUPPORTED_VERSIONS = {'': 'unknown',
- 'rss090': 'RSS 0.90',
- 'rss091n': 'RSS 0.91 (Netscape)',
- 'rss091u': 'RSS 0.91 (Userland)',
- 'rss092': 'RSS 0.92',
- 'rss093': 'RSS 0.93',
- 'rss094': 'RSS 0.94',
- 'rss20': 'RSS 2.0',
- 'rss10': 'RSS 1.0',
- 'rss': 'RSS (unknown version)',
- 'atom01': 'Atom 0.1',
- 'atom02': 'Atom 0.2',
- 'atom03': 'Atom 0.3',
- 'atom10': 'Atom 1.0',
- 'atom': 'Atom (unknown version)',
- 'cdf': 'CDF',
- 'hotrss': 'Hot RSS'
- }
-
-try:
- UserDict = dict
-except NameError:
- # Python 2.1 does not have dict
- from UserDict import UserDict
- def dict(aList):
- rc = {}
- for k, v in aList:
- rc[k] = v
- return rc
-
-class FeedParserDict(UserDict):
- keymap = {'channel': 'feed',
- 'items': 'entries',
- 'guid': 'id',
- 'date': 'updated',
- 'date_parsed': 'updated_parsed',
- 'description': ['summary', 'subtitle'],
- 'url': ['href'],
- 'modified': 'updated',
- 'modified_parsed': 'updated_parsed',
- 'issued': 'published',
- 'issued_parsed': 'published_parsed',
- 'copyright': 'rights',
- 'copyright_detail': 'rights_detail',
- 'tagline': 'subtitle',
- 'tagline_detail': 'subtitle_detail'}
- def __getitem__(self, key):
- if key == 'category':
- return UserDict.__getitem__(self, 'tags')[0]['term']
- if key == 'enclosures':
- norel = lambda link: FeedParserDict([(name,value) for (name,value) in link.items() if name!='rel'])
- return [norel(link) for link in UserDict.__getitem__(self, 'links') if link['rel']=='enclosure']
- if key == 'license':
- for link in UserDict.__getitem__(self, 'links'):
- if link['rel']=='license' and link.has_key('href'):
- return link['href']
- if key == 'categories':
- return [(tag['scheme'], tag['term']) for tag in UserDict.__getitem__(self, 'tags')]
- realkey = self.keymap.get(key, key)
- if type(realkey) == types.ListType:
- for k in realkey:
- if UserDict.__contains__(self, k):
- return UserDict.__getitem__(self, k)
- if UserDict.__contains__(self, key):
- return UserDict.__getitem__(self, key)
- return UserDict.__getitem__(self, realkey)
-
- def __setitem__(self, key, value):
- for k in self.keymap.keys():
- if key == k:
- key = self.keymap[k]
- if type(key) == types.ListType:
- key = key[0]
- return UserDict.__setitem__(self, key, value)
-
- def get(self, key, default=None):
- if self.has_key(key):
- return self[key]
- else:
- return default
-
- def setdefault(self, key, value):
- if not self.has_key(key):
- self[key] = value
- return self[key]
-
- def has_key(self, key):
- try:
- return hasattr(self, key) or UserDict.__contains__(self, key)
- except AttributeError:
- return False
- # This alias prevents the 2to3 tool from changing the semantics of the
- # __contains__ function below and exhausting the maximum recursion depth
- __has_key = has_key
-
- def __getattr__(self, key):
- try:
- return self.__dict__[key]
- except KeyError:
- pass
- try:
- assert not key.startswith('_')
- return self.__getitem__(key)
- except:
- raise AttributeError, "object has no attribute '%s'" % key
-
- def __setattr__(self, key, value):
- if key.startswith('_') or key == 'data':
- self.__dict__[key] = value
- else:
- return self.__setitem__(key, value)
-
- def __contains__(self, key):
- return self.__has_key(key)
-
-def zopeCompatibilityHack():
- global FeedParserDict
- del FeedParserDict
- def FeedParserDict(aDict=None):
- rc = {}
- if aDict:
- rc.update(aDict)
- return rc
-
-_ebcdic_to_ascii_map = None
-def _ebcdic_to_ascii(s):
- global _ebcdic_to_ascii_map
- if not _ebcdic_to_ascii_map:
- emap = (
- 0,1,2,3,156,9,134,127,151,141,142,11,12,13,14,15,
- 16,17,18,19,157,133,8,135,24,25,146,143,28,29,30,31,
- 128,129,130,131,132,10,23,27,136,137,138,139,140,5,6,7,
- 144,145,22,147,148,149,150,4,152,153,154,155,20,21,158,26,
- 32,160,161,162,163,164,165,166,167,168,91,46,60,40,43,33,
- 38,169,170,171,172,173,174,175,176,177,93,36,42,41,59,94,
- 45,47,178,179,180,181,182,183,184,185,124,44,37,95,62,63,
- 186,187,188,189,190,191,192,193,194,96,58,35,64,39,61,34,
- 195,97,98,99,100,101,102,103,104,105,196,197,198,199,200,201,
- 202,106,107,108,109,110,111,112,113,114,203,204,205,206,207,208,
- 209,126,115,116,117,118,119,120,121,122,210,211,212,213,214,215,
- 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,
- 123,65,66,67,68,69,70,71,72,73,232,233,234,235,236,237,
- 125,74,75,76,77,78,79,80,81,82,238,239,240,241,242,243,
- 92,159,83,84,85,86,87,88,89,90,244,245,246,247,248,249,
- 48,49,50,51,52,53,54,55,56,57,250,251,252,253,254,255
- )
- _ebcdic_to_ascii_map = _maketrans( \
- _l2bytes(range(256)), _l2bytes(emap))
- return s.translate(_ebcdic_to_ascii_map)
-
-_cp1252 = {
- unichr(128): unichr(8364), # euro sign
- unichr(130): unichr(8218), # single low-9 quotation mark
- unichr(131): unichr( 402), # latin small letter f with hook
- unichr(132): unichr(8222), # double low-9 quotation mark
- unichr(133): unichr(8230), # horizontal ellipsis
- unichr(134): unichr(8224), # dagger
- unichr(135): unichr(8225), # double dagger
- unichr(136): unichr( 710), # modifier letter circumflex accent
- unichr(137): unichr(8240), # per mille sign
- unichr(138): unichr( 352), # latin capital letter s with caron
- unichr(139): unichr(8249), # single left-pointing angle quotation mark
- unichr(140): unichr( 338), # latin capital ligature oe
- unichr(142): unichr( 381), # latin capital letter z with caron
- unichr(145): unichr(8216), # left single quotation mark
- unichr(146): unichr(8217), # right single quotation mark
- unichr(147): unichr(8220), # left double quotation mark
- unichr(148): unichr(8221), # right double quotation mark
- unichr(149): unichr(8226), # bullet
- unichr(150): unichr(8211), # en dash
- unichr(151): unichr(8212), # em dash
- unichr(152): unichr( 732), # small tilde
- unichr(153): unichr(8482), # trade mark sign
- unichr(154): unichr( 353), # latin small letter s with caron
- unichr(155): unichr(8250), # single right-pointing angle quotation mark
- unichr(156): unichr( 339), # latin small ligature oe
- unichr(158): unichr( 382), # latin small letter z with caron
- unichr(159): unichr( 376)} # latin capital letter y with diaeresis
-
-_urifixer = re.compile('^([A-Za-z][A-Za-z0-9+-.]*://)(/*)(.*?)')
-def _urljoin(base, uri):
- uri = _urifixer.sub(r'\1\3', uri)
- try:
- return urlparse.urljoin(base, uri)
- except:
- uri = urlparse.urlunparse([urllib.quote(part) for part in urlparse.urlparse(uri)])
- return urlparse.urljoin(base, uri)
-
-class _FeedParserMixin:
- namespaces = {'': '',
- 'http://backend.userland.com/rss': '',
- 'http://blogs.law.harvard.edu/tech/rss': '',
- 'http://purl.org/rss/1.0/': '',
- 'http://my.netscape.com/rdf/simple/0.9/': '',
- 'http://example.com/newformat#': '',
- 'http://example.com/necho': '',
- 'http://purl.org/echo/': '',
- 'uri/of/echo/namespace#': '',
- 'http://purl.org/pie/': '',
- 'http://purl.org/atom/ns#': '',
- 'http://www.w3.org/2005/Atom': '',
- 'http://purl.org/rss/1.0/modules/rss091#': '',
-
- 'http://webns.net/mvcb/': 'admin',
- 'http://purl.org/rss/1.0/modules/aggregation/': 'ag',
- 'http://purl.org/rss/1.0/modules/annotate/': 'annotate',
- 'http://media.tangent.org/rss/1.0/': 'audio',
- 'http://backend.userland.com/blogChannelModule': 'blogChannel',
- 'http://web.resource.org/cc/': 'cc',
- 'http://backend.userland.com/creativeCommonsRssModule': 'creativeCommons',
- 'http://purl.org/rss/1.0/modules/company': 'co',
- 'http://purl.org/rss/1.0/modules/content/': 'content',
- 'http://my.theinfo.org/changed/1.0/rss/': 'cp',
- 'http://purl.org/dc/elements/1.1/': 'dc',
- 'http://purl.org/dc/terms/': 'dcterms',
- 'http://purl.org/rss/1.0/modules/email/': 'email',
- 'http://purl.org/rss/1.0/modules/event/': 'ev',
- 'http://rssnamespace.org/feedburner/ext/1.0': 'feedburner',
- 'http://freshmeat.net/rss/fm/': 'fm',
- 'http://xmlns.com/foaf/0.1/': 'foaf',
- 'http://www.w3.org/2003/01/geo/wgs84_pos#': 'geo',
- 'http://postneo.com/icbm/': 'icbm',
- 'http://purl.org/rss/1.0/modules/image/': 'image',
- 'http://www.itunes.com/DTDs/PodCast-1.0.dtd': 'itunes',
- 'http://example.com/DTDs/PodCast-1.0.dtd': 'itunes',
- 'http://purl.org/rss/1.0/modules/link/': 'l',
- 'http://search.yahoo.com/mrss': 'media',
- #Version 1.1.2 of the Media RSS spec added the trailing slash on the namespace
- 'http://search.yahoo.com/mrss/': 'media',
- 'http://madskills.com/public/xml/rss/module/pingback/': 'pingback',
- 'http://prismstandard.org/namespaces/1.2/basic/': 'prism',
- 'http://www.w3.org/1999/02/22-rdf-syntax-ns#': 'rdf',
- 'http://www.w3.org/2000/01/rdf-schema#': 'rdfs',
- 'http://purl.org/rss/1.0/modules/reference/': 'ref',
- 'http://purl.org/rss/1.0/modules/richequiv/': 'reqv',
- 'http://purl.org/rss/1.0/modules/search/': 'search',
- 'http://purl.org/rss/1.0/modules/slash/': 'slash',
- 'http://schemas.xmlsoap.org/soap/envelope/': 'soap',
- 'http://purl.org/rss/1.0/modules/servicestatus/': 'ss',
- 'http://hacks.benhammersley.com/rss/streaming/': 'str',
- 'http://purl.org/rss/1.0/modules/subscription/': 'sub',
- 'http://purl.org/rss/1.0/modules/syndication/': 'sy',
- 'http://schemas.pocketsoap.com/rss/myDescModule/': 'szf',
- 'http://purl.org/rss/1.0/modules/taxonomy/': 'taxo',
- 'http://purl.org/rss/1.0/modules/threading/': 'thr',
- 'http://purl.org/rss/1.0/modules/textinput/': 'ti',
- 'http://madskills.com/public/xml/rss/module/trackback/':'trackback',
- 'http://wellformedweb.org/commentAPI/': 'wfw',
- 'http://purl.org/rss/1.0/modules/wiki/': 'wiki',
- 'http://www.w3.org/1999/xhtml': 'xhtml',
- 'http://www.w3.org/1999/xlink': 'xlink',
- 'http://www.w3.org/XML/1998/namespace': 'xml'
-}
- _matchnamespaces = {}
-
- can_be_relative_uri = ['link', 'id', 'wfw_comment', 'wfw_commentrss', 'docs', 'url', 'href', 'comments', 'icon', 'logo']
- can_contain_relative_uris = ['content', 'title', 'summary', 'info', 'tagline', 'subtitle', 'copyright', 'rights', 'description']
- can_contain_dangerous_markup = ['content', 'title', 'summary', 'info', 'tagline', 'subtitle', 'copyright', 'rights', 'description']
- html_types = ['text/html', 'application/xhtml+xml']
-
- def __init__(self, baseuri=None, baselang=None, encoding='utf-8'):
- if _debug: sys.stderr.write('initializing FeedParser\n')
- if not self._matchnamespaces:
- for k, v in self.namespaces.items():
- self._matchnamespaces[k.lower()] = v
- self.feeddata = FeedParserDict() # feed-level data
- self.encoding = encoding # character encoding
- self.entries = [] # list of entry-level data
- self.version = '' # feed type/version, see SUPPORTED_VERSIONS
- self.namespacesInUse = {} # dictionary of namespaces defined by the feed
-
- # the following are used internally to track state;
- # this is really out of control and should be refactored
- self.infeed = 0
- self.inentry = 0
- self.incontent = 0
- self.intextinput = 0
- self.inimage = 0
- self.inauthor = 0
- self.incontributor = 0
- self.inpublisher = 0
- self.insource = 0
- self.sourcedata = FeedParserDict()
- self.contentparams = FeedParserDict()
- self._summaryKey = None
- self.namespacemap = {}
- self.elementstack = []
- self.basestack = []
- self.langstack = []
- self.baseuri = baseuri or ''
- self.lang = baselang or None
- self.svgOK = 0
- self.hasTitle = 0
- if baselang:
- self.feeddata['language'] = baselang.replace('_','-')
-
- def unknown_starttag(self, tag, attrs):
- if _debug: sys.stderr.write('start %s with %s\n' % (tag, attrs))
- # normalize attrs
- attrs = [(k.lower(), v) for k, v in attrs]
- attrs = [(k, k in ('rel', 'type') and v.lower() or v) for k, v in attrs]
- # the sgml parser doesn't handle entities in attributes, but
- # strict xml parsers do -- account for this difference
- if isinstance(self, _LooseFeedParser):
- attrs = [(k, v.replace('&amp;', '&')) for k, v in attrs]
-
- # track xml:base and xml:lang
- attrsD = dict(attrs)
- baseuri = attrsD.get('xml:base', attrsD.get('base')) or self.baseuri
- if type(baseuri) != type(u''):
- try:
- baseuri = unicode(baseuri, self.encoding)
- except:
- baseuri = unicode(baseuri, 'iso-8859-1')
- # ensure that self.baseuri is always an absolute URI that
- # uses a whitelisted URI scheme (e.g. not `javscript:`)
- if self.baseuri:
- self.baseuri = _makeSafeAbsoluteURI(self.baseuri, baseuri) or self.baseuri
- else:
- self.baseuri = _urljoin(self.baseuri, baseuri)
- lang = attrsD.get('xml:lang', attrsD.get('lang'))
- if lang == '':
- # xml:lang could be explicitly set to '', we need to capture that
- lang = None
- elif lang is None:
- # if no xml:lang is specified, use parent lang
- lang = self.lang
- if lang:
- if tag in ('feed', 'rss', 'rdf:RDF'):
- self.feeddata['language'] = lang.replace('_','-')
- self.lang = lang
- self.basestack.append(self.baseuri)
- self.langstack.append(lang)
-
- # track namespaces
- for prefix, uri in attrs:
- if prefix.startswith('xmlns:'):
- self.trackNamespace(prefix[6:], uri)
- elif prefix == 'xmlns':
- self.trackNamespace(None, uri)
-
- # track inline content
- if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
- if tag in ['xhtml:div', 'div']: return # typepad does this 10/2007
- # element declared itself as escaped markup, but it isn't really
- self.contentparams['type'] = 'application/xhtml+xml'
- if self.incontent and self.contentparams.get('type') == 'application/xhtml+xml':
- if tag.find(':') <> -1:
- prefix, tag = tag.split(':', 1)
- namespace = self.namespacesInUse.get(prefix, '')
- if tag=='math' and namespace=='http://www.w3.org/1998/Math/MathML':
- attrs.append(('xmlns',namespace))
- if tag=='svg' and namespace=='http://www.w3.org/2000/svg':
- attrs.append(('xmlns',namespace))
- if tag == 'svg': self.svgOK += 1
- return self.handle_data('<%s%s>' % (tag, self.strattrs(attrs)), escape=0)
-
- # match namespaces
- if tag.find(':') <> -1:
- prefix, suffix = tag.split(':', 1)
- else:
- prefix, suffix = '', tag
- prefix = self.namespacemap.get(prefix, prefix)
- if prefix:
- prefix = prefix + '_'
-
- # special hack for better tracking of empty textinput/image elements in illformed feeds
- if (not prefix) and tag not in ('title', 'link', 'description', 'name'):
- self.intextinput = 0
- if (not prefix) and tag not in ('title', 'link', 'description', 'url', 'href', 'width', 'height'):
- self.inimage = 0
-
- # call special handler (if defined) or default handler
- methodname = '_start_' + prefix + suffix
- try:
- method = getattr(self, methodname)
- return method(attrsD)
- except AttributeError:
- # Since there's no handler or something has gone wrong we explicitly add the element and its attributes
- unknown_tag = prefix + suffix
- if len(attrsD) == 0:
- # No attributes so merge it into the encosing dictionary
- return self.push(unknown_tag, 1)
- else:
- # Has attributes so create it in its own dictionary
- context = self._getContext()
- context[unknown_tag] = attrsD
-
- def unknown_endtag(self, tag):
- if _debug: sys.stderr.write('end %s\n' % tag)
- # match namespaces
- if tag.find(':') <> -1:
- prefix, suffix = tag.split(':', 1)
- else:
- prefix, suffix = '', tag
- prefix = self.namespacemap.get(prefix, prefix)
- if prefix:
- prefix = prefix + '_'
- if suffix == 'svg' and self.svgOK: self.svgOK -= 1
-
- # call special handler (if defined) or default handler
- methodname = '_end_' + prefix + suffix
- try:
- if self.svgOK: raise AttributeError()
- method = getattr(self, methodname)
- method()
- except AttributeError:
- self.pop(prefix + suffix)
-
- # track inline content
- if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
- # element declared itself as escaped markup, but it isn't really
- if tag in ['xhtml:div', 'div']: return # typepad does this 10/2007
- self.contentparams['type'] = 'application/xhtml+xml'
- if self.incontent and self.contentparams.get('type') == 'application/xhtml+xml':
- tag = tag.split(':')[-1]
- self.handle_data('</%s>' % tag, escape=0)
-
- # track xml:base and xml:lang going out of scope
- if self.basestack:
- self.basestack.pop()
- if self.basestack and self.basestack[-1]:
- self.baseuri = self.basestack[-1]
- if self.langstack:
- self.langstack.pop()
- if self.langstack: # and (self.langstack[-1] is not None):
- self.lang = self.langstack[-1]
-
- def handle_charref(self, ref):
- # called for each character reference, e.g. for '&#160;', ref will be '160'
- if not self.elementstack: return
- ref = ref.lower()
- if ref in ('34', '38', '39', '60', '62', 'x22', 'x26', 'x27', 'x3c', 'x3e'):
- text = '&#%s;' % ref
- else:
- if ref[0] == 'x':
- c = int(ref[1:], 16)
- else:
- c = int(ref)
- text = unichr(c).encode('utf-8')
- self.elementstack[-1][2].append(text)
-
- def handle_entityref(self, ref):
- # called for each entity reference, e.g. for '&copy;', ref will be 'copy'
- if not self.elementstack: return
- if _debug: sys.stderr.write('entering handle_entityref with %s\n' % ref)
- if ref in ('lt', 'gt', 'quot', 'amp', 'apos'):
- text = '&%s;' % ref
- elif ref in self.entities.keys():
- text = self.entities[ref]
- if text.startswith('&#') and text.endswith(';'):
- return self.handle_entityref(text)
- else:
- try: name2codepoint[ref]
- except KeyError: text = '&%s;' % ref
- else: text = unichr(name2codepoint[ref]).encode('utf-8')
- self.elementstack[-1][2].append(text)
-
- def handle_data(self, text, escape=1):
- # called for each block of plain text, i.e. outside of any tag and
- # not containing any character or entity references
- if not self.elementstack: return
- if escape and self.contentparams.get('type') == 'application/xhtml+xml':
- text = _xmlescape(text)
- self.elementstack[-1][2].append(text)
-
- def handle_comment(self, text):
- # called for each comment, e.g. <!-- insert message here -->
- pass
-
- def handle_pi(self, text):
- # called for each processing instruction, e.g. <?instruction>
- pass
-
- def handle_decl(self, text):
- pass
-
- def parse_declaration(self, i):
- # override internal declaration handler to handle CDATA blocks
- if _debug: sys.stderr.write('entering parse_declaration\n')
- if self.rawdata[i:i+9] == '<![CDATA[':
- k = self.rawdata.find(']]>', i)
- if k == -1:
- # CDATA block began but didn't finish
- k = len(self.rawdata)
- return k
- self.handle_data(_xmlescape(self.rawdata[i+9:k]), 0)
- return k+3
- else:
- k = self.rawdata.find('>', i)
- if k >= 0:
- return k+1
- else:
- # We have an incomplete CDATA block.
- return k
-
- def mapContentType(self, contentType):
- contentType = contentType.lower()
- if contentType == 'text':
- contentType = 'text/plain'
- elif contentType == 'html':
- contentType = 'text/html'
- elif contentType == 'xhtml':
- contentType = 'application/xhtml+xml'
- return contentType
-
- def trackNamespace(self, prefix, uri):
- loweruri = uri.lower()
- if (prefix, loweruri) == (None, 'http://my.netscape.com/rdf/simple/0.9/') and not self.version:
- self.version = 'rss090'
- if loweruri == 'http://purl.org/rss/1.0/' and not self.version:
- self.version = 'rss10'
- if loweruri == 'http://www.w3.org/2005/atom' and not self.version:
- self.version = 'atom10'
- if loweruri.find('backend.userland.com/rss') <> -1:
- # match any backend.userland.com namespace
- uri = 'http://backend.userland.com/rss'
- loweruri = uri
- if self._matchnamespaces.has_key(loweruri):
- self.namespacemap[prefix] = self._matchnamespaces[loweruri]
- self.namespacesInUse[self._matchnamespaces[loweruri]] = uri
- else:
- self.namespacesInUse[prefix or ''] = uri
-
- def resolveURI(self, uri):
- return _urljoin(self.baseuri or '', uri)
-
- def decodeEntities(self, element, data):
- return data
-
- def strattrs(self, attrs):
- return ''.join([' %s="%s"' % (t[0],_xmlescape(t[1],{'"':'&quot;'})) for t in attrs])
-
- def push(self, element, expectingText):
- self.elementstack.append([element, expectingText, []])
-
- def pop(self, element, stripWhitespace=1):
- if not self.elementstack: return
- if self.elementstack[-1][0] != element: return
-
- element, expectingText, pieces = self.elementstack.pop()
-
- if self.version == 'atom10' and self.contentparams.get('type','text') == 'application/xhtml+xml':
- # remove enclosing child element, but only if it is a <div> and
- # only if all the remaining content is nested underneath it.
- # This means that the divs would be retained in the following:
- # <div>foo</div><div>bar</div>
- while pieces and len(pieces)>1 and not pieces[-1].strip():
- del pieces[-1]
- while pieces and len(pieces)>1 and not pieces[0].strip():
- del pieces[0]
- if pieces and (pieces[0] == '<div>' or pieces[0].startswith('<div ')) and pieces[-1]=='</div>':
- depth = 0
- for piece in pieces[:-1]:
- if piece.startswith('</'):
- depth -= 1
- if depth == 0: break
- elif piece.startswith('<') and not piece.endswith('/>'):
- depth += 1
- else:
- pieces = pieces[1:-1]
-
- # Ensure each piece is a str for Python 3
- for (i, v) in enumerate(pieces):
- if not isinstance(v, basestring):
- pieces[i] = v.decode('utf-8')
-
- output = ''.join(pieces)
- if stripWhitespace:
- output = output.strip()
- if not expectingText: return output
-
- # decode base64 content
- if base64 and self.contentparams.get('base64', 0):
- try:
- output = _base64decode(output)
- except binascii.Error:
- pass
- except binascii.Incomplete:
- pass
- except TypeError:
- # In Python 3, base64 takes and outputs bytes, not str
- # This may not be the most correct way to accomplish this
- output = _base64decode(output.encode('utf-8')).decode('utf-8')
-
- # resolve relative URIs
- if (element in self.can_be_relative_uri) and output:
- output = self.resolveURI(output)
-
- # decode entities within embedded markup
- if not self.contentparams.get('base64', 0):
- output = self.decodeEntities(element, output)
-
- if self.lookslikehtml(output):
- self.contentparams['type']='text/html'
-
- # remove temporary cruft from contentparams
- try:
- del self.contentparams['mode']
- except KeyError:
- pass
- try:
- del self.contentparams['base64']
- except KeyError:
- pass
-
- is_htmlish = self.mapContentType(self.contentparams.get('type', 'text/html')) in self.html_types
- # resolve relative URIs within embedded markup
- if is_htmlish and RESOLVE_RELATIVE_URIS:
- if element in self.can_contain_relative_uris:
- output = _resolveRelativeURIs(output, self.baseuri, self.encoding, self.contentparams.get('type', 'text/html'))
-
- # parse microformats
- # (must do this before sanitizing because some microformats
- # rely on elements that we sanitize)
- if is_htmlish and element in ['content', 'description', 'summary']:
- mfresults = _parseMicroformats(output, self.baseuri, self.encoding)
- if mfresults:
- for tag in mfresults.get('tags', []):
- self._addTag(tag['term'], tag['scheme'], tag['label'])
- for enclosure in mfresults.get('enclosures', []):
- self._start_enclosure(enclosure)
- for xfn in mfresults.get('xfn', []):
- self._addXFN(xfn['relationships'], xfn['href'], xfn['name'])
- vcard = mfresults.get('vcard')
- if vcard:
- self._getContext()['vcard'] = vcard
-
- # sanitize embedded markup
- if is_htmlish and SANITIZE_HTML:
- if element in self.can_contain_dangerous_markup:
- output = _sanitizeHTML(output, self.encoding, self.contentparams.get('type', 'text/html'))
-
- if self.encoding and type(output) != type(u''):
- try:
- output = unicode(output, self.encoding)
- except:
- pass
-
- # address common error where people take data that is already
- # utf-8, presume that it is iso-8859-1, and re-encode it.
- if self.encoding in ('utf-8', 'utf-8_INVALID_PYTHON_3') and type(output) == type(u''):
- try:
- output = unicode(output.encode('iso-8859-1'), 'utf-8')
- except:
- pass
-
- # map win-1252 extensions to the proper code points
- if type(output) == type(u''):
- output = u''.join([c in _cp1252.keys() and _cp1252[c] or c for c in output])
-
- # categories/tags/keywords/whatever are handled in _end_category
- if element == 'category':
- return output
-
- if element == 'title' and self.hasTitle:
- return output
-
- # store output in appropriate place(s)
- if self.inentry and not self.insource:
- if element == 'content':
- self.entries[-1].setdefault(element, [])
- contentparams = copy.deepcopy(self.contentparams)
- contentparams['value'] = output
- self.entries[-1][element].append(contentparams)
- elif element == 'link':
- if not self.inimage:
- # query variables in urls in link elements are improperly
- # converted from `?a=1&b=2` to `?a=1&b;=2` as if they're
- # unhandled character references. fix this special case.
- output = re.sub("&([A-Za-z0-9_]+);", "&\g<1>", output)
- self.entries[-1][element] = output
- if output:
- self.entries[-1]['links'][-1]['href'] = output
- else:
- if element == 'description':
- element = 'summary'
- self.entries[-1][element] = output
- if self.incontent:
- contentparams = copy.deepcopy(self.contentparams)
- contentparams['value'] = output
- self.entries[-1][element + '_detail'] = contentparams
- elif (self.infeed or self.insource):# and (not self.intextinput) and (not self.inimage):
- context = self._getContext()
- if element == 'description':
- element = 'subtitle'
- context[element] = output
- if element == 'link':
- # fix query variables; see above for the explanation
- output = re.sub("&([A-Za-z0-9_]+);", "&\g<1>", output)
- context[element] = output
- context['links'][-1]['href'] = output
- elif self.incontent:
- contentparams = copy.deepcopy(self.contentparams)
- contentparams['value'] = output
- context[element + '_detail'] = contentparams
- return output
-
- def pushContent(self, tag, attrsD, defaultContentType, expectingText):
- self.incontent += 1
- if self.lang: self.lang=self.lang.replace('_','-')
- self.contentparams = FeedParserDict({
- 'type': self.mapContentType(attrsD.get('type', defaultContentType)),
- 'language': self.lang,
- 'base': self.baseuri})
- self.contentparams['base64'] = self._isBase64(attrsD, self.contentparams)
- self.push(tag, expectingText)
-
- def popContent(self, tag):
- value = self.pop(tag)
- self.incontent -= 1
- self.contentparams.clear()
- return value
-
- # a number of elements in a number of RSS variants are nominally plain
- # text, but this is routinely ignored. This is an attempt to detect
- # the most common cases. As false positives often result in silent
- # data loss, this function errs on the conservative side.
- def lookslikehtml(self, s):
- if self.version.startswith('atom'): return
- if self.contentparams.get('type','text/html') != 'text/plain': return
-
- # must have a close tag or a entity reference to qualify
- if not (re.search(r'</(\w+)>',s) or re.search("&#?\w+;",s)): return
-
- # all tags must be in a restricted subset of valid HTML tags
- if filter(lambda t: t.lower() not in _HTMLSanitizer.acceptable_elements,
- re.findall(r'</?(\w+)',s)): return
-
- # all entities must have been defined as valid HTML entities
- from htmlentitydefs import entitydefs
- if filter(lambda e: e not in entitydefs.keys(),
- re.findall(r'&(\w+);',s)): return
-
- return 1
-
- def _mapToStandardPrefix(self, name):
- colonpos = name.find(':')
- if colonpos <> -1:
- prefix = name[:colonpos]
- suffix = name[colonpos+1:]
- prefix = self.namespacemap.get(prefix, prefix)
- name = prefix + ':' + suffix
- return name
-
- def _getAttribute(self, attrsD, name):
- return attrsD.get(self._mapToStandardPrefix(name))
-
- def _isBase64(self, attrsD, contentparams):
- if attrsD.get('mode', '') == 'base64':
- return 1
- if self.contentparams['type'].startswith('text/'):
- return 0
- if self.contentparams['type'].endswith('+xml'):
- return 0
- if self.contentparams['type'].endswith('/xml'):
- return 0
- return 1
-
- def _itsAnHrefDamnIt(self, attrsD):
- href = attrsD.get('url', attrsD.get('uri', attrsD.get('href', None)))
- if href:
- try:
- del attrsD['url']
- except KeyError:
- pass
- try:
- del attrsD['uri']
- except KeyError:
- pass
- attrsD['href'] = href
- return attrsD
-
- def _save(self, key, value, overwrite=False):
- context = self._getContext()
- if overwrite:
- context[key] = value
- else:
- context.setdefault(key, value)
-
- def _start_rss(self, attrsD):
- versionmap = {'0.91': 'rss091u',
- '0.92': 'rss092',
- '0.93': 'rss093',
- '0.94': 'rss094'}
- #If we're here then this is an RSS feed.
- #If we don't have a version or have a version that starts with something
- #other than RSS then there's been a mistake. Correct it.
- if not self.version or not self.version.startswith('rss'):
- attr_version = attrsD.get('version', '')
- version = versionmap.get(attr_version)
- if version:
- self.version = version
- elif attr_version.startswith('2.'):
- self.version = 'rss20'
- else:
- self.version = 'rss'
-
- def _start_dlhottitles(self, attrsD):
- self.version = 'hotrss'
-
- def _start_channel(self, attrsD):
- self.infeed = 1
- self._cdf_common(attrsD)
- _start_feedinfo = _start_channel
-
- def _cdf_common(self, attrsD):
- if attrsD.has_key('lastmod'):
- self._start_modified({})
- self.elementstack[-1][-1] = attrsD['lastmod']
- self._end_modified()
- if attrsD.has_key('href'):
- self._start_link({})
- self.elementstack[-1][-1] = attrsD['href']
- self._end_link()
-
- def _start_feed(self, attrsD):
- self.infeed = 1
- versionmap = {'0.1': 'atom01',
- '0.2': 'atom02',
- '0.3': 'atom03'}
- if not self.version:
- attr_version = attrsD.get('version')
- version = versionmap.get(attr_version)
- if version:
- self.version = version
- else:
- self.version = 'atom'
-
- def _end_channel(self):
- self.infeed = 0
- _end_feed = _end_channel
-
- def _start_image(self, attrsD):
- context = self._getContext()
- if not self.inentry:
- context.setdefault('image', FeedParserDict())
- self.inimage = 1
- self.hasTitle = 0
- self.push('image', 0)
-
- def _end_image(self):
- self.pop('image')
- self.inimage = 0
-
- def _start_textinput(self, attrsD):
- context = self._getContext()
- context.setdefault('textinput', FeedParserDict())
- self.intextinput = 1
- self.hasTitle = 0
- self.push('textinput', 0)
- _start_textInput = _start_textinput
-
- def _end_textinput(self):
- self.pop('textinput')
- self.intextinput = 0
- _end_textInput = _end_textinput
-
- def _start_author(self, attrsD):
- self.inauthor = 1
- self.push('author', 1)
- # Append a new FeedParserDict when expecting an author
- context = self._getContext()
- context.setdefault('authors', [])
- context['authors'].append(FeedParserDict())
- _start_managingeditor = _start_author
- _start_dc_author = _start_author
- _start_dc_creator = _start_author
- _start_itunes_author = _start_author
-
- def _end_author(self):
- self.pop('author')
- self.inauthor = 0
- self._sync_author_detail()
- _end_managingeditor = _end_author
- _end_dc_author = _end_author
- _end_dc_creator = _end_author
- _end_itunes_author = _end_author
-
- def _start_itunes_owner(self, attrsD):
- self.inpublisher = 1
- self.push('publisher', 0)
-
- def _end_itunes_owner(self):
- self.pop('publisher')
- self.inpublisher = 0
- self._sync_author_detail('publisher')
-
- def _start_contributor(self, attrsD):
- self.incontributor = 1
- context = self._getContext()
- context.setdefault('contributors', [])
- context['contributors'].append(FeedParserDict())
- self.push('contributor', 0)
-
- def _end_contributor(self):
- self.pop('contributor')
- self.incontributor = 0
-
- def _start_dc_contributor(self, attrsD):
- self.incontributor = 1
- context = self._getContext()
- context.setdefault('contributors', [])
- context['contributors'].append(FeedParserDict())
- self.push('name', 0)
-
- def _end_dc_contributor(self):
- self._end_name()
- self.incontributor = 0
-
- def _start_name(self, attrsD):
- self.push('name', 0)
- _start_itunes_name = _start_name
-
- def _end_name(self):
- value = self.pop('name')
- if self.inpublisher:
- self._save_author('name', value, 'publisher')
- elif self.inauthor:
- self._save_author('name', value)
- elif self.incontributor:
- self._save_contributor('name', value)
- elif self.intextinput:
- context = self._getContext()
- context['name'] = value
- _end_itunes_name = _end_name
-
- def _start_width(self, attrsD):
- self.push('width', 0)
-
- def _end_width(self):
- value = self.pop('width')
- try:
- value = int(value)
- except:
- value = 0
- if self.inimage:
- context = self._getContext()
- context['width'] = value
-
- def _start_height(self, attrsD):
- self.push('height', 0)
-
- def _end_height(self):
- value = self.pop('height')
- try:
- value = int(value)
- except:
- value = 0
- if self.inimage:
- context = self._getContext()
- context['height'] = value
-
- def _start_url(self, attrsD):
- self.push('href', 1)
- _start_homepage = _start_url
- _start_uri = _start_url
-
- def _end_url(self):
- value = self.pop('href')
- if self.inauthor:
- self._save_author('href', value)
- elif self.incontributor:
- self._save_contributor('href', value)
- _end_homepage = _end_url
- _end_uri = _end_url
-
- def _start_email(self, attrsD):
- self.push('email', 0)
- _start_itunes_email = _start_email
-
- def _end_email(self):
- value = self.pop('email')
- if self.inpublisher:
- self._save_author('email', value, 'publisher')
- elif self.inauthor:
- self._save_author('email', value)
- elif self.incontributor:
- self._save_contributor('email', value)
- _end_itunes_email = _end_email
-
- def _getContext(self):
- if self.insource:
- context = self.sourcedata
- elif self.inimage and self.feeddata.has_key('image'):
- context = self.feeddata['image']
- elif self.intextinput:
- context = self.feeddata['textinput']
- elif self.inentry:
- context = self.entries[-1]
- else:
- context = self.feeddata
- return context
-
- def _save_author(self, key, value, prefix='author'):
- context = self._getContext()
- context.setdefault(prefix + '_detail', FeedParserDict())
- context[prefix + '_detail'][key] = value
- self._sync_author_detail()
- context.setdefault('authors', [FeedParserDict()])
- context['authors'][-1][key] = value
-
- def _save_contributor(self, key, value):
- context = self._getContext()
- context.setdefault('contributors', [FeedParserDict()])
- context['contributors'][-1][key] = value
-
- def _sync_author_detail(self, key='author'):
- context = self._getContext()
- detail = context.get('%s_detail' % key)
- if detail:
- name = detail.get('name')
- email = detail.get('email')
- if name and email:
- context[key] = '%s (%s)' % (name, email)
- elif name:
- context[key] = name
- elif email:
- context[key] = email
- else:
- author, email = context.get(key), None
- if not author: return
- emailmatch = re.search(r'''(([a-zA-Z0-9\_\-\.\+]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?))(\?subject=\S+)?''', author)
- if emailmatch:
- email = emailmatch.group(0)
- # probably a better way to do the following, but it passes all the tests
- author = author.replace(email, '')
- author = author.replace('()', '')
- author = author.replace('<>', '')
- author = author.replace('&lt;&gt;', '')
- author = author.strip()
- if author and (author[0] == '('):
- author = author[1:]
- if author and (author[-1] == ')'):
- author = author[:-1]
- author = author.strip()
- if author or email:
- context.setdefault('%s_detail' % key, FeedParserDict())
- if author:
- context['%s_detail' % key]['name'] = author
- if email:
- context['%s_detail' % key]['email'] = email
-
- def _start_subtitle(self, attrsD):
- self.pushContent('subtitle', attrsD, 'text/plain', 1)
- _start_tagline = _start_subtitle
- _start_itunes_subtitle = _start_subtitle
-
- def _end_subtitle(self):
- self.popContent('subtitle')
- _end_tagline = _end_subtitle
- _end_itunes_subtitle = _end_subtitle
-
- def _start_rights(self, attrsD):
- self.pushContent('rights', attrsD, 'text/plain', 1)
- _start_dc_rights = _start_rights
- _start_copyright = _start_rights
-
- def _end_rights(self):
- self.popContent('rights')
- _end_dc_rights = _end_rights
- _end_copyright = _end_rights
-
- def _start_item(self, attrsD):
- self.entries.append(FeedParserDict())
- self.push('item', 0)
- self.inentry = 1
- self.guidislink = 0
- self.hasTitle = 0
- id = self._getAttribute(attrsD, 'rdf:about')
- if id:
- context = self._getContext()
- context['id'] = id
- self._cdf_common(attrsD)
- _start_entry = _start_item
- _start_product = _start_item
-
- def _end_item(self):
- self.pop('item')
- self.inentry = 0
- _end_entry = _end_item
-
- def _start_dc_language(self, attrsD):
- self.push('language', 1)
- _start_language = _start_dc_language
-
- def _end_dc_language(self):
- self.lang = self.pop('language')
- _end_language = _end_dc_language
-
- def _start_dc_publisher(self, attrsD):
- self.push('publisher', 1)
- _start_webmaster = _start_dc_publisher
-
- def _end_dc_publisher(self):
- self.pop('publisher')
- self._sync_author_detail('publisher')
- _end_webmaster = _end_dc_publisher
-
- def _start_published(self, attrsD):
- self.push('published', 1)
- _start_dcterms_issued = _start_published
- _start_issued = _start_published
-
- def _end_published(self):
- value = self.pop('published')
- self._save('published_parsed', _parse_date(value), overwrite=True)
- _end_dcterms_issued = _end_published
- _end_issued = _end_published
-
- def _start_updated(self, attrsD):
- self.push('updated', 1)
- _start_modified = _start_updated
- _start_dcterms_modified = _start_updated
- _start_pubdate = _start_updated
- _start_dc_date = _start_updated
- _start_lastbuilddate = _start_updated
-
- def _end_updated(self):
- value = self.pop('updated')
- parsed_value = _parse_date(value)
- self._save('updated_parsed', parsed_value, overwrite=True)
- _end_modified = _end_updated
- _end_dcterms_modified = _end_updated
- _end_pubdate = _end_updated
- _end_dc_date = _end_updated
- _end_lastbuilddate = _end_updated
-
- def _start_created(self, attrsD):
- self.push('created', 1)
- _start_dcterms_created = _start_created
-
- def _end_created(self):
- value = self.pop('created')
- self._save('created_parsed', _parse_date(value), overwrite=True)
- _end_dcterms_created = _end_created
-
- def _start_expirationdate(self, attrsD):
- self.push('expired', 1)
-
- def _end_expirationdate(self):
- self._save('expired_parsed', _parse_date(self.pop('expired')), overwrite=True)
-
- def _start_cc_license(self, attrsD):
- context = self._getContext()
- value = self._getAttribute(attrsD, 'rdf:resource')
- attrsD = FeedParserDict()
- attrsD['rel']='license'
- if value: attrsD['href']=value
- context.setdefault('links', []).append(attrsD)
-
- def _start_creativecommons_license(self, attrsD):
- self.push('license', 1)
- _start_creativeCommons_license = _start_creativecommons_license
-
- def _end_creativecommons_license(self):
- value = self.pop('license')
- context = self._getContext()
- attrsD = FeedParserDict()
- attrsD['rel']='license'
- if value: attrsD['href']=value
- context.setdefault('links', []).append(attrsD)
- del context['license']
- _end_creativeCommons_license = _end_creativecommons_license
-
- def _addXFN(self, relationships, href, name):
- context = self._getContext()
- xfn = context.setdefault('xfn', [])
- value = FeedParserDict({'relationships': relationships, 'href': href, 'name': name})
- if value not in xfn:
- xfn.append(value)
-
- def _addTag(self, term, scheme, label):
- context = self._getContext()
- tags = context.setdefault('tags', [])
- if (not term) and (not scheme) and (not label): return
- value = FeedParserDict({'term': term, 'scheme': scheme, 'label': label})
- if value not in tags:
- tags.append(value)
-
- def _start_category(self, attrsD):
- if _debug: sys.stderr.write('entering _start_category with %s\n' % repr(attrsD))
- term = attrsD.get('term')
- scheme = attrsD.get('scheme', attrsD.get('domain'))
- label = attrsD.get('label')
- self._addTag(term, scheme, label)
- self.push('category', 1)
- _start_dc_subject = _start_category
- _start_keywords = _start_category
-
- def _start_media_category(self, attrsD):
- attrsD.setdefault('scheme', 'http://search.yahoo.com/mrss/category_schema')
- self._start_category(attrsD)
-
- def _end_itunes_keywords(self):
- for term in self.pop('itunes_keywords').split():
- self._addTag(term, 'http://www.itunes.com/', None)
-
- def _start_itunes_category(self, attrsD):
- self._addTag(attrsD.get('text'), 'http://www.itunes.com/', None)
- self.push('category', 1)
-
- def _end_category(self):
- value = self.pop('category')
- if not value: return
- context = self._getContext()
- tags = context['tags']
- if value and len(tags) and not tags[-1]['term']:
- tags[-1]['term'] = value
- else:
- self._addTag(value, None, None)
- _end_dc_subject = _end_category
- _end_keywords = _end_category
- _end_itunes_category = _end_category
- _end_media_category = _end_category
-
- def _start_cloud(self, attrsD):
- self._getContext()['cloud'] = FeedParserDict(attrsD)
-
- def _start_link(self, attrsD):
- attrsD.setdefault('rel', 'alternate')
- if attrsD['rel'] == 'self':
- attrsD.setdefault('type', 'application/atom+xml')
- else:
- attrsD.setdefault('type', 'text/html')
- context = self._getContext()
- attrsD = self._itsAnHrefDamnIt(attrsD)
- if attrsD.has_key('href'):
- attrsD['href'] = self.resolveURI(attrsD['href'])
- expectingText = self.infeed or self.inentry or self.insource
- context.setdefault('links', [])
- if not (self.inentry and self.inimage):
- context['links'].append(FeedParserDict(attrsD))
- if attrsD.has_key('href'):
- expectingText = 0
- if (attrsD.get('rel') == 'alternate') and (self.mapContentType(attrsD.get('type')) in self.html_types):
- context['link'] = attrsD['href']
- else:
- self.push('link', expectingText)
- _start_producturl = _start_link
-
- def _end_link(self):
- value = self.pop('link')
- context = self._getContext()
- _end_producturl = _end_link
-
- def _start_guid(self, attrsD):
- self.guidislink = (attrsD.get('ispermalink', 'true') == 'true')
- self.push('id', 1)
-
- def _end_guid(self):
- value = self.pop('id')
- self._save('guidislink', self.guidislink and not self._getContext().has_key('link'))
- if self.guidislink:
- # guid acts as link, but only if 'ispermalink' is not present or is 'true',
- # and only if the item doesn't already have a link element
- self._save('link', value)
-
- def _start_title(self, attrsD):
- if self.svgOK: return self.unknown_starttag('title', attrsD.items())
- self.pushContent('title', attrsD, 'text/plain', self.infeed or self.inentry or self.insource)
- _start_dc_title = _start_title
- _start_media_title = _start_title
-
- def _end_title(self):
- if self.svgOK: return
- value = self.popContent('title')
- if not value: return
- context = self._getContext()
- self.hasTitle = 1
- _end_dc_title = _end_title
-
- def _end_media_title(self):
- hasTitle = self.hasTitle
- self._end_title()
- self.hasTitle = hasTitle
-
- def _start_description(self, attrsD):
- context = self._getContext()
- if context.has_key('summary'):
- self._summaryKey = 'content'
- self._start_content(attrsD)
- else:
- self.pushContent('description', attrsD, 'text/html', self.infeed or self.inentry or self.insource)
- _start_dc_description = _start_description
-
- def _start_abstract(self, attrsD):
- self.pushContent('description', attrsD, 'text/plain', self.infeed or self.inentry or self.insource)
-
- def _end_description(self):
- if self._summaryKey == 'content':
- self._end_content()
- else:
- value = self.popContent('description')
- self._summaryKey = None
- _end_abstract = _end_description
- _end_dc_description = _end_description
-
- def _start_info(self, attrsD):
- self.pushContent('info', attrsD, 'text/plain', 1)
- _start_feedburner_browserfriendly = _start_info
-
- def _end_info(self):
- self.popContent('info')
- _end_feedburner_browserfriendly = _end_info
-
- def _start_generator(self, attrsD):
- if attrsD:
- attrsD = self._itsAnHrefDamnIt(attrsD)
- if attrsD.has_key('href'):
- attrsD['href'] = self.resolveURI(attrsD['href'])
- self._getContext()['generator_detail'] = FeedParserDict(attrsD)
- self.push('generator', 1)
-
- def _end_generator(self):
- value = self.pop('generator')
- context = self._getContext()
- if context.has_key('generator_detail'):
- context['generator_detail']['name'] = value
-
- def _start_admin_generatoragent(self, attrsD):
- self.push('generator', 1)
- value = self._getAttribute(attrsD, 'rdf:resource')
- if value:
- self.elementstack[-1][2].append(value)
- self.pop('generator')
- self._getContext()['generator_detail'] = FeedParserDict({'href': value})
-
- def _start_admin_errorreportsto(self, attrsD):
- self.push('errorreportsto', 1)
- value = self._getAttribute(attrsD, 'rdf:resource')
- if value:
- self.elementstack[-1][2].append(value)
- self.pop('errorreportsto')
-
- def _start_summary(self, attrsD):
- context = self._getContext()
- if context.has_key('summary'):
- self._summaryKey = 'content'
- self._start_content(attrsD)
- else:
- self._summaryKey = 'summary'
- self.pushContent(self._summaryKey, attrsD, 'text/plain', 1)
- _start_itunes_summary = _start_summary
-
- def _end_summary(self):
- if self._summaryKey == 'content':
- self._end_content()
- else:
- self.popContent(self._summaryKey or 'summary')
- self._summaryKey = None
- _end_itunes_summary = _end_summary
-
- def _start_enclosure(self, attrsD):
- attrsD = self._itsAnHrefDamnIt(attrsD)
- context = self._getContext()
- attrsD['rel']='enclosure'
- context.setdefault('links', []).append(FeedParserDict(attrsD))
-
- def _start_source(self, attrsD):
- if 'url' in attrsD:
- # This means that we're processing a source element from an RSS 2.0 feed
- self.sourcedata['href'] = attrsD[u'url']
- self.push('source', 1)
- self.insource = 1
- self.hasTitle = 0
-
- def _end_source(self):
- self.insource = 0
- value = self.pop('source')
- if value:
- self.sourcedata['title'] = value
- self._getContext()['source'] = copy.deepcopy(self.sourcedata)
- self.sourcedata.clear()
-
- def _start_content(self, attrsD):
- self.pushContent('content', attrsD, 'text/plain', 1)
- src = attrsD.get('src')
- if src:
- self.contentparams['src'] = src
- self.push('content', 1)
-
- def _start_prodlink(self, attrsD):
- self.pushContent('content', attrsD, 'text/html', 1)
-
- def _start_body(self, attrsD):
- self.pushContent('content', attrsD, 'application/xhtml+xml', 1)
- _start_xhtml_body = _start_body
-
- def _start_content_encoded(self, attrsD):
- self.pushContent('content', attrsD, 'text/html', 1)
- _start_fullitem = _start_content_encoded
-
- def _end_content(self):
- copyToSummary = self.mapContentType(self.contentparams.get('type')) in (['text/plain'] + self.html_types)
- value = self.popContent('content')
- if copyToSummary:
- self._save('summary', value)
-
- _end_body = _end_content
- _end_xhtml_body = _end_content
- _end_content_encoded = _end_content
- _end_fullitem = _end_content
- _end_prodlink = _end_content
-
- def _start_itunes_image(self, attrsD):
- self.push('itunes_image', 0)
- if attrsD.get('href'):
- self._getContext()['image'] = FeedParserDict({'href': attrsD.get('href')})
- _start_itunes_link = _start_itunes_image
-
- def _end_itunes_block(self):
- value = self.pop('itunes_block', 0)
- self._getContext()['itunes_block'] = (value == 'yes') and 1 or 0
-
- def _end_itunes_explicit(self):
- value = self.pop('itunes_explicit', 0)
- # Convert 'yes' -> True, 'clean' to False, and any other value to None
- # False and None both evaluate as False, so the difference can be ignored
- # by applications that only need to know if the content is explicit.
- self._getContext()['itunes_explicit'] = (None, False, True)[(value == 'yes' and 2) or value == 'clean' or 0]
-
- def _start_media_content(self, attrsD):
- context = self._getContext()
- context.setdefault('media_content', [])
- context['media_content'].append(attrsD)
-
- def _start_media_thumbnail(self, attrsD):
- context = self._getContext()
- context.setdefault('media_thumbnail', [])
- self.push('url', 1) # new
- context['media_thumbnail'].append(attrsD)
-
- def _end_media_thumbnail(self):
- url = self.pop('url')
- context = self._getContext()
- if url is not None and len(url.strip()) != 0:
- if not context['media_thumbnail'][-1].has_key('url'):
- context['media_thumbnail'][-1]['url'] = url
-
- def _start_media_player(self, attrsD):
- self.push('media_player', 0)
- self._getContext()['media_player'] = FeedParserDict(attrsD)
-
- def _end_media_player(self):
- value = self.pop('media_player')
- context = self._getContext()
- context['media_player']['content'] = value
-
- def _start_newlocation(self, attrsD):
- self.push('newlocation', 1)
-
- def _end_newlocation(self):
- url = self.pop('newlocation')
- context = self._getContext()
- # don't set newlocation if the context isn't right
- if context is not self.feeddata:
- return
- context['newlocation'] = _makeSafeAbsoluteURI(self.baseuri, url.strip())
-
-if _XML_AVAILABLE:
- class _StrictFeedParser(_FeedParserMixin, xml.sax.handler.ContentHandler):
- def __init__(self, baseuri, baselang, encoding):
- if _debug: sys.stderr.write('trying StrictFeedParser\n')
- xml.sax.handler.ContentHandler.__init__(self)
- _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
- self.bozo = 0
- self.exc = None
- self.decls = {}
-
- def startPrefixMapping(self, prefix, uri):
- self.trackNamespace(prefix, uri)
- if uri == 'http://www.w3.org/1999/xlink':
- self.decls['xmlns:'+prefix] = uri
-
- def startElementNS(self, name, qname, attrs):
- namespace, localname = name
- lowernamespace = str(namespace or '').lower()
- if lowernamespace.find('backend.userland.com/rss') <> -1:
- # match any backend.userland.com namespace
- namespace = 'http://backend.userland.com/rss'
- lowernamespace = namespace
- if qname and qname.find(':') > 0:
- givenprefix = qname.split(':')[0]
- else:
- givenprefix = None
- prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
- if givenprefix and (prefix is None or (prefix == '' and lowernamespace == '')) and not self.namespacesInUse.has_key(givenprefix):
- raise UndeclaredNamespace, "'%s' is not associated with a namespace" % givenprefix
- localname = str(localname).lower()
-
- # qname implementation is horribly broken in Python 2.1 (it
- # doesn't report any), and slightly broken in Python 2.2 (it
- # doesn't report the xml: namespace). So we match up namespaces
- # with a known list first, and then possibly override them with
- # the qnames the SAX parser gives us (if indeed it gives us any
- # at all). Thanks to MatejC for helping me test this and
- # tirelessly telling me that it didn't work yet.
- attrsD, self.decls = self.decls, {}
- if localname=='math' and namespace=='http://www.w3.org/1998/Math/MathML':
- attrsD['xmlns']=namespace
- if localname=='svg' and namespace=='http://www.w3.org/2000/svg':
- attrsD['xmlns']=namespace
-
- if prefix:
- localname = prefix.lower() + ':' + localname
- elif namespace and not qname: #Expat
- for name,value in self.namespacesInUse.items():
- if name and value == namespace:
- localname = name + ':' + localname
- break
- if _debug: sys.stderr.write('startElementNS: qname = %s, namespace = %s, givenprefix = %s, prefix = %s, attrs = %s, localname = %s\n' % (qname, namespace, givenprefix, prefix, attrs.items(), localname))
-
- for (namespace, attrlocalname), attrvalue in attrs._attrs.items():
- lowernamespace = (namespace or '').lower()
- prefix = self._matchnamespaces.get(lowernamespace, '')
- if prefix:
- attrlocalname = prefix + ':' + attrlocalname
- attrsD[str(attrlocalname).lower()] = attrvalue
- for qname in attrs.getQNames():
- attrsD[str(qname).lower()] = attrs.getValueByQName(qname)
- self.unknown_starttag(localname, attrsD.items())
-
- def characters(self, text):
- self.handle_data(text)
-
- def endElementNS(self, name, qname):
- namespace, localname = name
- lowernamespace = str(namespace or '').lower()
- if qname and qname.find(':') > 0:
- givenprefix = qname.split(':')[0]
- else:
- givenprefix = ''
- prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
- if prefix:
- localname = prefix + ':' + localname
- elif namespace and not qname: #Expat
- for name,value in self.namespacesInUse.items():
- if name and value == namespace:
- localname = name + ':' + localname
- break
- localname = str(localname).lower()
- self.unknown_endtag(localname)
-
- def error(self, exc):
- self.bozo = 1
- self.exc = exc
-
- def fatalError(self, exc):
- self.error(exc)
- raise exc
-
-class _BaseHTMLProcessor(sgmllib.SGMLParser):
- special = re.compile('''[<>'"]''')
- bare_ampersand = re.compile("&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)")
- elements_no_end_tag = [
- 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame',
- 'hr', 'img', 'input', 'isindex', 'keygen', 'link', 'meta', 'param',
- 'source', 'track', 'wbr'
- ]
-
- def __init__(self, encoding, _type):
- self.encoding = encoding
- self._type = _type
- if _debug: sys.stderr.write('entering BaseHTMLProcessor, encoding=%s\n' % self.encoding)
- sgmllib.SGMLParser.__init__(self)
-
- def reset(self):
- self.pieces = []
- sgmllib.SGMLParser.reset(self)
-
- def _shorttag_replace(self, match):
- tag = match.group(1)
- if tag in self.elements_no_end_tag:
- return '<' + tag + ' />'
- else:
- return '<' + tag + '></' + tag + '>'
-
- def parse_starttag(self,i):
- j=sgmllib.SGMLParser.parse_starttag(self, i)
- if self._type == 'application/xhtml+xml':
- if j>2 and self.rawdata[j-2:j]=='/>':
- self.unknown_endtag(self.lasttag)
- return j
-
- def feed(self, data):
- data = re.compile(r'<!((?!DOCTYPE|--|\[))', re.IGNORECASE).sub(r'&lt;!\1', data)
- #data = re.sub(r'<(\S+?)\s*?/>', self._shorttag_replace, data) # bug [ 1399464 ] Bad regexp for _shorttag_replace
- data = re.sub(r'<([^<>\s]+?)\s*/>', self._shorttag_replace, data)
- data = data.replace('&#39;', "'")
- data = data.replace('&#34;', '"')
- try:
- bytes
- if bytes is str:
- raise NameError
- self.encoding = self.encoding + '_INVALID_PYTHON_3'
- except NameError:
- if self.encoding and type(data) == type(u''):
- data = data.encode(self.encoding)
- sgmllib.SGMLParser.feed(self, data)
- sgmllib.SGMLParser.close(self)
-
- def normalize_attrs(self, attrs):
- if not attrs: return attrs
- # utility method to be called by descendants
- attrs = dict([(k.lower(), v) for k, v in attrs]).items()
- attrs = [(k, k in ('rel', 'type') and v.lower() or v) for k, v in attrs]
- attrs.sort()
- return attrs
-
- def unknown_starttag(self, tag, attrs):
- # called for each start tag
- # attrs is a list of (attr, value) tuples
- # e.g. for <pre class='screen'>, tag='pre', attrs=[('class', 'screen')]
- if _debug: sys.stderr.write('_BaseHTMLProcessor, unknown_starttag, tag=%s\n' % tag)
- uattrs = []
- strattrs=''
- if attrs:
- for key, value in attrs:
- value=value.replace('>','&gt;').replace('<','&lt;').replace('"','&quot;')
- value = self.bare_ampersand.sub("&amp;", value)
- # thanks to Kevin Marks for this breathtaking hack to deal with (valid) high-bit attribute values in UTF-8 feeds
- if type(value) != type(u''):
- try:
- value = unicode(value, self.encoding)
- except:
- value = unicode(value, 'iso-8859-1')
- try:
- # Currently, in Python 3 the key is already a str, and cannot be decoded again
- uattrs.append((unicode(key, self.encoding), value))
- except TypeError:
- uattrs.append((key, value))
- strattrs = u''.join([u' %s="%s"' % (key, value) for key, value in uattrs])
- if self.encoding:
- try:
- strattrs=strattrs.encode(self.encoding)
- except:
- pass
- if tag in self.elements_no_end_tag:
- self.pieces.append('<%(tag)s%(strattrs)s />' % locals())
- else:
- self.pieces.append('<%(tag)s%(strattrs)s>' % locals())
-
- def unknown_endtag(self, tag):
- # called for each end tag, e.g. for </pre>, tag will be 'pre'
- # Reconstruct the original end tag.
- if tag not in self.elements_no_end_tag:
- self.pieces.append("</%(tag)s>" % locals())
-
- def handle_charref(self, ref):
- # called for each character reference, e.g. for '&#160;', ref will be '160'
- # Reconstruct the original character reference.
- if ref.startswith('x'):
- value = unichr(int(ref[1:],16))
- else:
- value = unichr(int(ref))
-
- if value in _cp1252.keys():
- self.pieces.append('&#%s;' % hex(ord(_cp1252[value]))[1:])
- else:
- self.pieces.append('&#%(ref)s;' % locals())
-
- def handle_entityref(self, ref):
- # called for each entity reference, e.g. for '&copy;', ref will be 'copy'
- # Reconstruct the original entity reference.
- if name2codepoint.has_key(ref):
- self.pieces.append('&%(ref)s;' % locals())
- else:
- self.pieces.append('&amp;%(ref)s' % locals())
-
- def handle_data(self, text):
- # called for each block of plain text, i.e. outside of any tag and
- # not containing any character or entity references
- # Store the original text verbatim.
- if _debug: sys.stderr.write('_BaseHTMLProcessor, handle_data, text=%s\n' % text)
- self.pieces.append(text)
-
- def handle_comment(self, text):
- # called for each HTML comment, e.g. <!-- insert Javascript code here -->
- # Reconstruct the original comment.
- self.pieces.append('<!--%(text)s-->' % locals())
-
- def handle_pi(self, text):
- # called for each processing instruction, e.g. <?instruction>
- # Reconstruct original processing instruction.
- self.pieces.append('<?%(text)s>' % locals())
-
- def handle_decl(self, text):
- # called for the DOCTYPE, if present, e.g.
- # <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
- # "http://www.w3.org/TR/html4/loose.dtd">
- # Reconstruct original DOCTYPE
- self.pieces.append('<!%(text)s>' % locals())
-
- _new_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9:]*\s*').match
- def _scan_name(self, i, declstartpos):
- rawdata = self.rawdata
- n = len(rawdata)
- if i == n:
- return None, -1
- m = self._new_declname_match(rawdata, i)
- if m:
- s = m.group()
- name = s.strip()
- if (i + len(s)) == n:
- return None, -1 # end of buffer
- return name.lower(), m.end()
- else:
- self.handle_data(rawdata)
-# self.updatepos(declstartpos, i)
- return None, -1
-
- def convert_charref(self, name):
- return '&#%s;' % name
-
- def convert_entityref(self, name):
- return '&%s;' % name
-
- def output(self):
- '''Return processed HTML as a single string'''
- return ''.join([str(p) for p in self.pieces])
-
-class _LooseFeedParser(_FeedParserMixin, _BaseHTMLProcessor):
- def __init__(self, baseuri, baselang, encoding, entities):
- sgmllib.SGMLParser.__init__(self)
- _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
- _BaseHTMLProcessor.__init__(self, encoding, 'application/xhtml+xml')
- self.entities=entities
-
- def decodeEntities(self, element, data):
- data = data.replace('&#60;', '&lt;')
- data = data.replace('&#x3c;', '&lt;')
- data = data.replace('&#x3C;', '&lt;')
- data = data.replace('&#62;', '&gt;')
- data = data.replace('&#x3e;', '&gt;')
- data = data.replace('&#x3E;', '&gt;')
- data = data.replace('&#38;', '&amp;')
- data = data.replace('&#x26;', '&amp;')
- data = data.replace('&#34;', '&quot;')
- data = data.replace('&#x22;', '&quot;')
- data = data.replace('&#39;', '&apos;')
- data = data.replace('&#x27;', '&apos;')
- if self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
- data = data.replace('&lt;', '<')
- data = data.replace('&gt;', '>')
- data = data.replace('&amp;', '&')
- data = data.replace('&quot;', '"')
- data = data.replace('&apos;', "'")
- return data
-
- def strattrs(self, attrs):
- return ''.join([' %s="%s"' % (n,v.replace('"','&quot;')) for n,v in attrs])
-
-class _MicroformatsParser:
- STRING = 1
- DATE = 2
- URI = 3
- NODE = 4
- EMAIL = 5
-
- known_xfn_relationships = ['contact', 'acquaintance', 'friend', 'met', 'co-worker', 'coworker', 'colleague', 'co-resident', 'coresident', 'neighbor', 'child', 'parent', 'sibling', 'brother', 'sister', 'spouse', 'wife', 'husband', 'kin', 'relative', 'muse', 'crush', 'date', 'sweetheart', 'me']
- known_binary_extensions = ['zip','rar','exe','gz','tar','tgz','tbz2','bz2','z','7z','dmg','img','sit','sitx','hqx','deb','rpm','bz2','jar','rar','iso','bin','msi','mp2','mp3','ogg','ogm','mp4','m4v','m4a','avi','wma','wmv']
-
- def __init__(self, data, baseuri, encoding):
- self.document = BeautifulSoup.BeautifulSoup(data)
- self.baseuri = baseuri
- self.encoding = encoding
- if type(data) == type(u''):
- data = data.encode(encoding)
- self.tags = []
- self.enclosures = []
- self.xfn = []
- self.vcard = None
-
- def vcardEscape(self, s):
- if type(s) in (type(''), type(u'')):
- s = s.replace(',', '\\,').replace(';', '\\;').replace('\n', '\\n')
- return s
-
- def vcardFold(self, s):
- s = re.sub(';+$', '', s)
- sFolded = ''
- iMax = 75
- sPrefix = ''
- while len(s) > iMax:
- sFolded += sPrefix + s[:iMax] + '\n'
- s = s[iMax:]
- sPrefix = ' '
- iMax = 74
- sFolded += sPrefix + s
- return sFolded
-
- def normalize(self, s):
- return re.sub(r'\s+', ' ', s).strip()
-
- def unique(self, aList):
- results = []
- for element in aList:
- if element not in results:
- results.append(element)
- return results
-
- def toISO8601(self, dt):
- return time.strftime('%Y-%m-%dT%H:%M:%SZ', dt)
-
- def getPropertyValue(self, elmRoot, sProperty, iPropertyType=4, bAllowMultiple=0, bAutoEscape=0):
- all = lambda x: 1
- sProperty = sProperty.lower()
- bFound = 0
- bNormalize = 1
- propertyMatch = {'class': re.compile(r'\b%s\b' % sProperty)}
- if bAllowMultiple and (iPropertyType != self.NODE):
- snapResults = []
- containers = elmRoot(['ul', 'ol'], propertyMatch)
- for container in containers:
- snapResults.extend(container('li'))
- bFound = (len(snapResults) != 0)
- if not bFound:
- snapResults = elmRoot(all, propertyMatch)
- bFound = (len(snapResults) != 0)
- if (not bFound) and (sProperty == 'value'):
- snapResults = elmRoot('pre')
- bFound = (len(snapResults) != 0)
- bNormalize = not bFound
- if not bFound:
- snapResults = [elmRoot]
- bFound = (len(snapResults) != 0)
- arFilter = []
- if sProperty == 'vcard':
- snapFilter = elmRoot(all, propertyMatch)
- for node in snapFilter:
- if node.findParent(all, propertyMatch):
- arFilter.append(node)
- arResults = []
- for node in snapResults:
- if node not in arFilter:
- arResults.append(node)
- bFound = (len(arResults) != 0)
- if not bFound:
- if bAllowMultiple: return []
- elif iPropertyType == self.STRING: return ''
- elif iPropertyType == self.DATE: return None
- elif iPropertyType == self.URI: return ''
- elif iPropertyType == self.NODE: return None
- else: return None
- arValues = []
- for elmResult in arResults:
- sValue = None
- if iPropertyType == self.NODE:
- if bAllowMultiple:
- arValues.append(elmResult)
- continue
- else:
- return elmResult
- sNodeName = elmResult.name.lower()
- if (iPropertyType == self.EMAIL) and (sNodeName == 'a'):
- sValue = (elmResult.get('href') or '').split('mailto:').pop().split('?')[0]
- if sValue:
- sValue = bNormalize and self.normalize(sValue) or sValue.strip()
- if (not sValue) and (sNodeName == 'abbr'):
- sValue = elmResult.get('title')
- if sValue:
- sValue = bNormalize and self.normalize(sValue) or sValue.strip()
- if (not sValue) and (iPropertyType == self.URI):
- if sNodeName == 'a': sValue = elmResult.get('href')
- elif sNodeName == 'img': sValue = elmResult.get('src')
- elif sNodeName == 'object': sValue = elmResult.get('data')
- if sValue:
- sValue = bNormalize and self.normalize(sValue) or sValue.strip()
- if (not sValue) and (sNodeName == 'img'):
- sValue = elmResult.get('alt')
- if sValue:
- sValue = bNormalize and self.normalize(sValue) or sValue.strip()
- if not sValue:
- sValue = elmResult.renderContents()
- sValue = re.sub(r'<\S[^>]*>', '', sValue)
- sValue = sValue.replace('\r\n', '\n')
- sValue = sValue.replace('\r', '\n')
- if sValue:
- sValue = bNormalize and self.normalize(sValue) or sValue.strip()
- if not sValue: continue
- if iPropertyType == self.DATE:
- sValue = _parse_date_iso8601(sValue)
- if bAllowMultiple:
- arValues.append(bAutoEscape and self.vcardEscape(sValue) or sValue)
- else:
- return bAutoEscape and self.vcardEscape(sValue) or sValue
- return arValues
-
- def findVCards(self, elmRoot, bAgentParsing=0):
- sVCards = ''
-
- if not bAgentParsing:
- arCards = self.getPropertyValue(elmRoot, 'vcard', bAllowMultiple=1)
- else:
- arCards = [elmRoot]
-
- for elmCard in arCards:
- arLines = []
-
- def processSingleString(sProperty):
- sValue = self.getPropertyValue(elmCard, sProperty, self.STRING, bAutoEscape=1).decode(self.encoding)
- if sValue:
- arLines.append(self.vcardFold(sProperty.upper() + ':' + sValue))
- return sValue or u''
-
- def processSingleURI(sProperty):
- sValue = self.getPropertyValue(elmCard, sProperty, self.URI)
- if sValue:
- sContentType = ''
- sEncoding = ''
- sValueKey = ''
- if sValue.startswith('data:'):
- sEncoding = ';ENCODING=b'
- sContentType = sValue.split(';')[0].split('/').pop()
- sValue = sValue.split(',', 1).pop()
- else:
- elmValue = self.getPropertyValue(elmCard, sProperty)
- if elmValue:
- if sProperty != 'url':
- sValueKey = ';VALUE=uri'
- sContentType = elmValue.get('type', '').strip().split('/').pop().strip()
- sContentType = sContentType.upper()
- if sContentType == 'OCTET-STREAM':
- sContentType = ''
- if sContentType:
- sContentType = ';TYPE=' + sContentType.upper()
- arLines.append(self.vcardFold(sProperty.upper() + sEncoding + sContentType + sValueKey + ':' + sValue))
-
- def processTypeValue(sProperty, arDefaultType, arForceType=None):
- arResults = self.getPropertyValue(elmCard, sProperty, bAllowMultiple=1)
- for elmResult in arResults:
- arType = self.getPropertyValue(elmResult, 'type', self.STRING, 1, 1)
- if arForceType:
- arType = self.unique(arForceType + arType)
- if not arType:
- arType = arDefaultType
- sValue = self.getPropertyValue(elmResult, 'value', self.EMAIL, 0)
- if sValue:
- arLines.append(self.vcardFold(sProperty.upper() + ';TYPE=' + ','.join(arType) + ':' + sValue))
-
- # AGENT
- # must do this before all other properties because it is destructive
- # (removes nested class="vcard" nodes so they don't interfere with
- # this vcard's other properties)
- arAgent = self.getPropertyValue(elmCard, 'agent', bAllowMultiple=1)
- for elmAgent in arAgent:
- if re.compile(r'\bvcard\b').search(elmAgent.get('class')):
- sAgentValue = self.findVCards(elmAgent, 1) + '\n'
- sAgentValue = sAgentValue.replace('\n', '\\n')
- sAgentValue = sAgentValue.replace(';', '\\;')
- if sAgentValue:
- arLines.append(self.vcardFold('AGENT:' + sAgentValue))
- # Completely remove the agent element from the parse tree
- elmAgent.extract()
- else:
- sAgentValue = self.getPropertyValue(elmAgent, 'value', self.URI, bAutoEscape=1);
- if sAgentValue:
- arLines.append(self.vcardFold('AGENT;VALUE=uri:' + sAgentValue))
-
- # FN (full name)
- sFN = processSingleString('fn')
-
- # N (name)
- elmName = self.getPropertyValue(elmCard, 'n')
- if elmName:
- sFamilyName = self.getPropertyValue(elmName, 'family-name', self.STRING, bAutoEscape=1)
- sGivenName = self.getPropertyValue(elmName, 'given-name', self.STRING, bAutoEscape=1)
- arAdditionalNames = self.getPropertyValue(elmName, 'additional-name', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'additional-names', self.STRING, 1, 1)
- arHonorificPrefixes = self.getPropertyValue(elmName, 'honorific-prefix', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'honorific-prefixes', self.STRING, 1, 1)
- arHonorificSuffixes = self.getPropertyValue(elmName, 'honorific-suffix', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'honorific-suffixes', self.STRING, 1, 1)
- arLines.append(self.vcardFold('N:' + sFamilyName + ';' +
- sGivenName + ';' +
- ','.join(arAdditionalNames) + ';' +
- ','.join(arHonorificPrefixes) + ';' +
- ','.join(arHonorificSuffixes)))
- elif sFN:
- # implied "N" optimization
- # http://microformats.org/wiki/hcard#Implied_.22N.22_Optimization
- arNames = self.normalize(sFN).split()
- if len(arNames) == 2:
- bFamilyNameFirst = (arNames[0].endswith(',') or
- len(arNames[1]) == 1 or
- ((len(arNames[1]) == 2) and (arNames[1].endswith('.'))))
- if bFamilyNameFirst:
- arLines.append(self.vcardFold('N:' + arNames[0] + ';' + arNames[1]))
- else:
- arLines.append(self.vcardFold('N:' + arNames[1] + ';' + arNames[0]))
-
- # SORT-STRING
- sSortString = self.getPropertyValue(elmCard, 'sort-string', self.STRING, bAutoEscape=1)
- if sSortString:
- arLines.append(self.vcardFold('SORT-STRING:' + sSortString))
-
- # NICKNAME
- arNickname = self.getPropertyValue(elmCard, 'nickname', self.STRING, 1, 1)
- if arNickname:
- arLines.append(self.vcardFold('NICKNAME:' + ','.join(arNickname)))
-
- # PHOTO
- processSingleURI('photo')
-
- # BDAY
- dtBday = self.getPropertyValue(elmCard, 'bday', self.DATE)
- if dtBday:
- arLines.append(self.vcardFold('BDAY:' + self.toISO8601(dtBday)))
-
- # ADR (address)
- arAdr = self.getPropertyValue(elmCard, 'adr', bAllowMultiple=1)
- for elmAdr in arAdr:
- arType = self.getPropertyValue(elmAdr, 'type', self.STRING, 1, 1)
- if not arType:
- arType = ['intl','postal','parcel','work'] # default adr types, see RFC 2426 section 3.2.1
- sPostOfficeBox = self.getPropertyValue(elmAdr, 'post-office-box', self.STRING, 0, 1)
- sExtendedAddress = self.getPropertyValue(elmAdr, 'extended-address', self.STRING, 0, 1)
- sStreetAddress = self.getPropertyValue(elmAdr, 'street-address', self.STRING, 0, 1)
- sLocality = self.getPropertyValue(elmAdr, 'locality', self.STRING, 0, 1)
- sRegion = self.getPropertyValue(elmAdr, 'region', self.STRING, 0, 1)
- sPostalCode = self.getPropertyValue(elmAdr, 'postal-code', self.STRING, 0, 1)
- sCountryName = self.getPropertyValue(elmAdr, 'country-name', self.STRING, 0, 1)
- arLines.append(self.vcardFold('ADR;TYPE=' + ','.join(arType) + ':' +
- sPostOfficeBox + ';' +
- sExtendedAddress + ';' +
- sStreetAddress + ';' +
- sLocality + ';' +
- sRegion + ';' +
- sPostalCode + ';' +
- sCountryName))
-
- # LABEL
- processTypeValue('label', ['intl','postal','parcel','work'])
-
- # TEL (phone number)
- processTypeValue('tel', ['voice'])
-
- # EMAIL
- processTypeValue('email', ['internet'], ['internet'])
-
- # MAILER
- processSingleString('mailer')
-
- # TZ (timezone)
- processSingleString('tz')
-
- # GEO (geographical information)
- elmGeo = self.getPropertyValue(elmCard, 'geo')
- if elmGeo:
- sLatitude = self.getPropertyValue(elmGeo, 'latitude', self.STRING, 0, 1)
- sLongitude = self.getPropertyValue(elmGeo, 'longitude', self.STRING, 0, 1)
- arLines.append(self.vcardFold('GEO:' + sLatitude + ';' + sLongitude))
-
- # TITLE
- processSingleString('title')
-
- # ROLE
- processSingleString('role')
-
- # LOGO
- processSingleURI('logo')
-
- # ORG (organization)
- elmOrg = self.getPropertyValue(elmCard, 'org')
- if elmOrg:
- sOrganizationName = self.getPropertyValue(elmOrg, 'organization-name', self.STRING, 0, 1)
- if not sOrganizationName:
- # implied "organization-name" optimization
- # http://microformats.org/wiki/hcard#Implied_.22organization-name.22_Optimization
- sOrganizationName = self.getPropertyValue(elmCard, 'org', self.STRING, 0, 1)
- if sOrganizationName:
- arLines.append(self.vcardFold('ORG:' + sOrganizationName))
- else:
- arOrganizationUnit = self.getPropertyValue(elmOrg, 'organization-unit', self.STRING, 1, 1)
- arLines.append(self.vcardFold('ORG:' + sOrganizationName + ';' + ';'.join(arOrganizationUnit)))
-
- # CATEGORY
- arCategory = self.getPropertyValue(elmCard, 'category', self.STRING, 1, 1) + self.getPropertyValue(elmCard, 'categories', self.STRING, 1, 1)
- if arCategory:
- arLines.append(self.vcardFold('CATEGORIES:' + ','.join(arCategory)))
-
- # NOTE
- processSingleString('note')
-
- # REV
- processSingleString('rev')
-
- # SOUND
- processSingleURI('sound')
-
- # UID
- processSingleString('uid')
-
- # URL
- processSingleURI('url')
-
- # CLASS
- processSingleString('class')
-
- # KEY
- processSingleURI('key')
-
- if arLines:
- arLines = [u'BEGIN:vCard',u'VERSION:3.0'] + arLines + [u'END:vCard']
- sVCards += u'\n'.join(arLines) + u'\n'
-
- return sVCards.strip()
-
- def isProbablyDownloadable(self, elm):
- attrsD = elm.attrMap
- if not attrsD.has_key('href'): return 0
- linktype = attrsD.get('type', '').strip()
- if linktype.startswith('audio/') or \
- linktype.startswith('video/') or \
- (linktype.startswith('application/') and not linktype.endswith('xml')):
- return 1
- path = urlparse.urlparse(attrsD['href'])[2]
- if path.find('.') == -1: return 0
- fileext = path.split('.').pop().lower()
- return fileext in self.known_binary_extensions
-
- def findTags(self):
- all = lambda x: 1
- for elm in self.document(all, {'rel': re.compile(r'\btag\b')}):
- href = elm.get('href')
- if not href: continue
- urlscheme, domain, path, params, query, fragment = \
- urlparse.urlparse(_urljoin(self.baseuri, href))
- segments = path.split('/')
- tag = segments.pop()
- if not tag:
- tag = segments.pop()
- tagscheme = urlparse.urlunparse((urlscheme, domain, '/'.join(segments), '', '', ''))
- if not tagscheme.endswith('/'):
- tagscheme += '/'
- self.tags.append(FeedParserDict({"term": tag, "scheme": tagscheme, "label": elm.string or ''}))
-
- def findEnclosures(self):
- all = lambda x: 1
- enclosure_match = re.compile(r'\benclosure\b')
- for elm in self.document(all, {'href': re.compile(r'.+')}):
- if not enclosure_match.search(elm.get('rel', '')) and not self.isProbablyDownloadable(elm): continue
- if elm.attrMap not in self.enclosures:
- self.enclosures.append(elm.attrMap)
- if elm.string and not elm.get('title'):
- self.enclosures[-1]['title'] = elm.string
-
- def findXFN(self):
- all = lambda x: 1
- for elm in self.document(all, {'rel': re.compile('.+'), 'href': re.compile('.+')}):
- rels = elm.get('rel', '').split()
- xfn_rels = []
- for rel in rels:
- if rel in self.known_xfn_relationships:
- xfn_rels.append(rel)
- if xfn_rels:
- self.xfn.append({"relationships": xfn_rels, "href": elm.get('href', ''), "name": elm.string})
-
-def _parseMicroformats(htmlSource, baseURI, encoding):
- if not BeautifulSoup: return
- if _debug: sys.stderr.write('entering _parseMicroformats\n')
- try:
- p = _MicroformatsParser(htmlSource, baseURI, encoding)
- except UnicodeEncodeError:
- # sgmllib throws this exception when performing lookups of tags
- # with non-ASCII characters in them.
- return
- p.vcard = p.findVCards(p.document)
- p.findTags()
- p.findEnclosures()
- p.findXFN()
- return {"tags": p.tags, "enclosures": p.enclosures, "xfn": p.xfn, "vcard": p.vcard}
-
-class _RelativeURIResolver(_BaseHTMLProcessor):
- relative_uris = [('a', 'href'),
- ('applet', 'codebase'),
- ('area', 'href'),
- ('blockquote', 'cite'),
- ('body', 'background'),
- ('del', 'cite'),
- ('form', 'action'),
- ('frame', 'longdesc'),
- ('frame', 'src'),
- ('iframe', 'longdesc'),
- ('iframe', 'src'),
- ('head', 'profile'),
- ('img', 'longdesc'),
- ('img', 'src'),
- ('img', 'usemap'),
- ('input', 'src'),
- ('input', 'usemap'),
- ('ins', 'cite'),
- ('link', 'href'),
- ('object', 'classid'),
- ('object', 'codebase'),
- ('object', 'data'),
- ('object', 'usemap'),
- ('q', 'cite'),
- ('script', 'src')]
-
- def __init__(self, baseuri, encoding, _type):
- _BaseHTMLProcessor.__init__(self, encoding, _type)
- self.baseuri = baseuri
-
- def resolveURI(self, uri):
- return _makeSafeAbsoluteURI(_urljoin(self.baseuri, uri.strip()))
-
- def unknown_starttag(self, tag, attrs):
- if _debug:
- sys.stderr.write('tag: [%s] with attributes: [%s]\n' % (tag, str(attrs)))
- attrs = self.normalize_attrs(attrs)
- attrs = [(key, ((tag, key) in self.relative_uris) and self.resolveURI(value) or value) for key, value in attrs]
- _BaseHTMLProcessor.unknown_starttag(self, tag, attrs)
-
-def _resolveRelativeURIs(htmlSource, baseURI, encoding, _type):
- if _debug:
- sys.stderr.write('entering _resolveRelativeURIs\n')
-
- p = _RelativeURIResolver(baseURI, encoding, _type)
- p.feed(htmlSource)
- return p.output()
-
-def _makeSafeAbsoluteURI(base, rel=None):
- # bail if ACCEPTABLE_URI_SCHEMES is empty
- if not ACCEPTABLE_URI_SCHEMES:
- return _urljoin(base, rel or u'')
- if not base:
- return rel or u''
- if not rel:
- if base.strip().split(':', 1)[0] not in ACCEPTABLE_URI_SCHEMES:
- return u''
- return base
- uri = _urljoin(base, rel)
- if uri.strip().split(':', 1)[0] not in ACCEPTABLE_URI_SCHEMES:
- return u''
- return uri
-
-class _HTMLSanitizer(_BaseHTMLProcessor):
- acceptable_elements = ['a', 'abbr', 'acronym', 'address', 'area',
- 'article', 'aside', 'audio', 'b', 'big', 'blockquote', 'br', 'button',
- 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup',
- 'command', 'datagrid', 'datalist', 'dd', 'del', 'details', 'dfn',
- 'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'event-source', 'fieldset',
- 'figcaption', 'figure', 'footer', 'font', 'form', 'header', 'h1',
- 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'input', 'ins',
- 'keygen', 'kbd', 'label', 'legend', 'li', 'm', 'map', 'menu', 'meter',
- 'multicol', 'nav', 'nextid', 'ol', 'output', 'optgroup', 'option',
- 'p', 'pre', 'progress', 'q', 's', 'samp', 'section', 'select',
- 'small', 'sound', 'source', 'spacer', 'span', 'strike', 'strong',
- 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'time', 'tfoot',
- 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'video', 'noscript']
-
- acceptable_attributes = ['abbr', 'accept', 'accept-charset', 'accesskey',
- 'action', 'align', 'alt', 'autocomplete', 'autofocus', 'axis',
- 'background', 'balance', 'bgcolor', 'bgproperties', 'border',
- 'bordercolor', 'bordercolordark', 'bordercolorlight', 'bottompadding',
- 'cellpadding', 'cellspacing', 'ch', 'challenge', 'char', 'charoff',
- 'choff', 'charset', 'checked', 'cite', 'class', 'clear', 'color', 'cols',
- 'colspan', 'compact', 'contenteditable', 'controls', 'coords', 'data',
- 'datafld', 'datapagesize', 'datasrc', 'datetime', 'default', 'delay',
- 'dir', 'disabled', 'draggable', 'dynsrc', 'enctype', 'end', 'face', 'for',
- 'form', 'frame', 'galleryimg', 'gutter', 'headers', 'height', 'hidefocus',
- 'hidden', 'high', 'href', 'hreflang', 'hspace', 'icon', 'id', 'inputmode',
- 'ismap', 'keytype', 'label', 'leftspacing', 'lang', 'list', 'longdesc',
- 'loop', 'loopcount', 'loopend', 'loopstart', 'low', 'lowsrc', 'max',
- 'maxlength', 'media', 'method', 'min', 'multiple', 'name', 'nohref',
- 'noshade', 'nowrap', 'open', 'optimum', 'pattern', 'ping', 'point-size',
- 'prompt', 'pqg', 'radiogroup', 'readonly', 'rel', 'repeat-max',
- 'repeat-min', 'replace', 'required', 'rev', 'rightspacing', 'rows',
- 'rowspan', 'rules', 'scope', 'selected', 'shape', 'size', 'span', 'src',
- 'start', 'step', 'summary', 'suppress', 'tabindex', 'target', 'template',
- 'title', 'toppadding', 'type', 'unselectable', 'usemap', 'urn', 'valign',
- 'value', 'variable', 'volume', 'vspace', 'vrml', 'width', 'wrap',
- 'xml:lang']
-
- unacceptable_elements_with_end_tag = ['script', 'applet', 'style']
-
- acceptable_css_properties = ['azimuth', 'background-color',
- 'border-bottom-color', 'border-collapse', 'border-color',
- 'border-left-color', 'border-right-color', 'border-top-color', 'clear',
- 'color', 'cursor', 'direction', 'display', 'elevation', 'float', 'font',
- 'font-family', 'font-size', 'font-style', 'font-variant', 'font-weight',
- 'height', 'letter-spacing', 'line-height', 'overflow', 'pause',
- 'pause-after', 'pause-before', 'pitch', 'pitch-range', 'richness',
- 'speak', 'speak-header', 'speak-numeral', 'speak-punctuation',
- 'speech-rate', 'stress', 'text-align', 'text-decoration', 'text-indent',
- 'unicode-bidi', 'vertical-align', 'voice-family', 'volume',
- 'white-space', 'width']
-
- # survey of common keywords found in feeds
- acceptable_css_keywords = ['auto', 'aqua', 'black', 'block', 'blue',
- 'bold', 'both', 'bottom', 'brown', 'center', 'collapse', 'dashed',
- 'dotted', 'fuchsia', 'gray', 'green', '!important', 'italic', 'left',
- 'lime', 'maroon', 'medium', 'none', 'navy', 'normal', 'nowrap', 'olive',
- 'pointer', 'purple', 'red', 'right', 'solid', 'silver', 'teal', 'top',
- 'transparent', 'underline', 'white', 'yellow']
-
- valid_css_values = re.compile('^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|' +
- '\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$')
-
- mathml_elements = ['annotation', 'annotation-xml', 'maction', 'math',
- 'merror', 'mfenced', 'mfrac', 'mi', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded',
- 'mphantom', 'mprescripts', 'mroot', 'mrow', 'mspace', 'msqrt', 'mstyle',
- 'msub', 'msubsup', 'msup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder',
- 'munderover', 'none', 'semantics']
-
- mathml_attributes = ['actiontype', 'align', 'columnalign', 'columnalign',
- 'columnalign', 'close', 'columnlines', 'columnspacing', 'columnspan', 'depth',
- 'display', 'displaystyle', 'encoding', 'equalcolumns', 'equalrows',
- 'fence', 'fontstyle', 'fontweight', 'frame', 'height', 'linethickness',
- 'lspace', 'mathbackground', 'mathcolor', 'mathvariant', 'mathvariant',
- 'maxsize', 'minsize', 'open', 'other', 'rowalign', 'rowalign', 'rowalign',
- 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'scriptlevel', 'selection',
- 'separator', 'separators', 'stretchy', 'width', 'width', 'xlink:href',
- 'xlink:show', 'xlink:type', 'xmlns', 'xmlns:xlink']
-
- # svgtiny - foreignObject + linearGradient + radialGradient + stop
- svg_elements = ['a', 'animate', 'animateColor', 'animateMotion',
- 'animateTransform', 'circle', 'defs', 'desc', 'ellipse', 'foreignObject',
- 'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern',
- 'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph', 'mpath',
- 'path', 'polygon', 'polyline', 'radialGradient', 'rect', 'set', 'stop',
- 'svg', 'switch', 'text', 'title', 'tspan', 'use']
-
- # svgtiny + class + opacity + offset + xmlns + xmlns:xlink
- svg_attributes = ['accent-height', 'accumulate', 'additive', 'alphabetic',
- 'arabic-form', 'ascent', 'attributeName', 'attributeType',
- 'baseProfile', 'bbox', 'begin', 'by', 'calcMode', 'cap-height',
- 'class', 'color', 'color-rendering', 'content', 'cx', 'cy', 'd', 'dx',
- 'dy', 'descent', 'display', 'dur', 'end', 'fill', 'fill-opacity',
- 'fill-rule', 'font-family', 'font-size', 'font-stretch', 'font-style',
- 'font-variant', 'font-weight', 'from', 'fx', 'fy', 'g1', 'g2',
- 'glyph-name', 'gradientUnits', 'hanging', 'height', 'horiz-adv-x',
- 'horiz-origin-x', 'id', 'ideographic', 'k', 'keyPoints', 'keySplines',
- 'keyTimes', 'lang', 'mathematical', 'marker-end', 'marker-mid',
- 'marker-start', 'markerHeight', 'markerUnits', 'markerWidth', 'max',
- 'min', 'name', 'offset', 'opacity', 'orient', 'origin',
- 'overline-position', 'overline-thickness', 'panose-1', 'path',
- 'pathLength', 'points', 'preserveAspectRatio', 'r', 'refX', 'refY',
- 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures',
- 'restart', 'rotate', 'rx', 'ry', 'slope', 'stemh', 'stemv',
- 'stop-color', 'stop-opacity', 'strikethrough-position',
- 'strikethrough-thickness', 'stroke', 'stroke-dasharray',
- 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin',
- 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage',
- 'target', 'text-anchor', 'to', 'transform', 'type', 'u1', 'u2',
- 'underline-position', 'underline-thickness', 'unicode', 'unicode-range',
- 'units-per-em', 'values', 'version', 'viewBox', 'visibility', 'width',
- 'widths', 'x', 'x-height', 'x1', 'x2', 'xlink:actuate', 'xlink:arcrole',
- 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type',
- 'xml:base', 'xml:lang', 'xml:space', 'xmlns', 'xmlns:xlink', 'y', 'y1',
- 'y2', 'zoomAndPan']
-
- svg_attr_map = None
- svg_elem_map = None
-
- acceptable_svg_properties = [ 'fill', 'fill-opacity', 'fill-rule',
- 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin',
- 'stroke-opacity']
-
- def reset(self):
- _BaseHTMLProcessor.reset(self)
- self.unacceptablestack = 0
- self.mathmlOK = 0
- self.svgOK = 0
-
- def unknown_starttag(self, tag, attrs):
- acceptable_attributes = self.acceptable_attributes
- keymap = {}
- if not tag in self.acceptable_elements or self.svgOK:
- if tag in self.unacceptable_elements_with_end_tag:
- self.unacceptablestack += 1
-
- # add implicit namespaces to html5 inline svg/mathml
- if self._type.endswith('html'):
- if not dict(attrs).get('xmlns'):
- if tag=='svg':
- attrs.append( ('xmlns','http://www.w3.org/2000/svg') )
- if tag=='math':
- attrs.append( ('xmlns','http://www.w3.org/1998/Math/MathML') )
-
- # not otherwise acceptable, perhaps it is MathML or SVG?
- if tag=='math' and ('xmlns','http://www.w3.org/1998/Math/MathML') in attrs:
- self.mathmlOK += 1
- if tag=='svg' and ('xmlns','http://www.w3.org/2000/svg') in attrs:
- self.svgOK += 1
-
- # chose acceptable attributes based on tag class, else bail
- if self.mathmlOK and tag in self.mathml_elements:
- acceptable_attributes = self.mathml_attributes
- elif self.svgOK and tag in self.svg_elements:
- # for most vocabularies, lowercasing is a good idea. Many
- # svg elements, however, are camel case
- if not self.svg_attr_map:
- lower=[attr.lower() for attr in self.svg_attributes]
- mix=[a for a in self.svg_attributes if a not in lower]
- self.svg_attributes = lower
- self.svg_attr_map = dict([(a.lower(),a) for a in mix])
-
- lower=[attr.lower() for attr in self.svg_elements]
- mix=[a for a in self.svg_elements if a not in lower]
- self.svg_elements = lower
- self.svg_elem_map = dict([(a.lower(),a) for a in mix])
- acceptable_attributes = self.svg_attributes
- tag = self.svg_elem_map.get(tag,tag)
- keymap = self.svg_attr_map
- elif not tag in self.acceptable_elements:
- return
-
- # declare xlink namespace, if needed
- if self.mathmlOK or self.svgOK:
- if filter(lambda (n,v): n.startswith('xlink:'),attrs):
- if not ('xmlns:xlink','http://www.w3.org/1999/xlink') in attrs:
- attrs.append(('xmlns:xlink','http://www.w3.org/1999/xlink'))
-
- clean_attrs = []
- for key, value in self.normalize_attrs(attrs):
- if key in acceptable_attributes:
- key=keymap.get(key,key)
- clean_attrs.append((key,value))
- elif key=='style':
- clean_value = self.sanitize_style(value)
- if clean_value: clean_attrs.append((key,clean_value))
- _BaseHTMLProcessor.unknown_starttag(self, tag, clean_attrs)
-
- def unknown_endtag(self, tag):
- if not tag in self.acceptable_elements:
- if tag in self.unacceptable_elements_with_end_tag:
- self.unacceptablestack -= 1
- if self.mathmlOK and tag in self.mathml_elements:
- if tag == 'math' and self.mathmlOK: self.mathmlOK -= 1
- elif self.svgOK and tag in self.svg_elements:
- tag = self.svg_elem_map.get(tag,tag)
- if tag == 'svg' and self.svgOK: self.svgOK -= 1
- else:
- return
- _BaseHTMLProcessor.unknown_endtag(self, tag)
-
- def handle_pi(self, text):
- pass
-
- def handle_decl(self, text):
- pass
-
- def handle_data(self, text):
- if not self.unacceptablestack:
- _BaseHTMLProcessor.handle_data(self, text)
-
- def sanitize_style(self, style):
- # disallow urls
- style=re.compile('url\s*\(\s*[^\s)]+?\s*\)\s*').sub(' ',style)
-
- # gauntlet
- if not re.match("""^([:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$""", style): return ''
- # This replaced a regexp that used re.match and was prone to pathological back-tracking.
- if re.sub("\s*[-\w]+\s*:\s*[^:;]*;?", '', style).strip(): return ''
-
- clean = []
- for prop,value in re.findall("([-\w]+)\s*:\s*([^:;]*)",style):
- if not value: continue
- if prop.lower() in self.acceptable_css_properties:
- clean.append(prop + ': ' + value + ';')
- elif prop.split('-')[0].lower() in ['background','border','margin','padding']:
- for keyword in value.split():
- if not keyword in self.acceptable_css_keywords and \
- not self.valid_css_values.match(keyword):
- break
- else:
- clean.append(prop + ': ' + value + ';')
- elif self.svgOK and prop.lower() in self.acceptable_svg_properties:
- clean.append(prop + ': ' + value + ';')
-
- return ' '.join(clean)
-
-
-def _sanitizeHTML(htmlSource, encoding, _type):
- p = _HTMLSanitizer(encoding, _type)
- htmlSource = htmlSource.replace('<![CDATA[', '&lt;![CDATA[')
- p.feed(htmlSource)
- data = p.output()
- if TIDY_MARKUP:
- # loop through list of preferred Tidy interfaces looking for one that's installed,
- # then set up a common _tidy function to wrap the interface-specific API.
- _tidy = None
- for tidy_interface in PREFERRED_TIDY_INTERFACES:
- try:
- if tidy_interface == "uTidy":
- from tidy import parseString as _utidy
- def _tidy(data, **kwargs):
- return str(_utidy(data, **kwargs))
- break
- elif tidy_interface == "mxTidy":
- from mx.Tidy import Tidy as _mxtidy
- def _tidy(data, **kwargs):
- nerrors, nwarnings, data, errordata = _mxtidy.tidy(data, **kwargs)
- return data
- break
- except:
- pass
- if _tidy:
- utf8 = type(data) == type(u'')
- if utf8:
- data = data.encode('utf-8')
- data = _tidy(data, output_xhtml=1, numeric_entities=1, wrap=0, char_encoding="utf8")
- if utf8:
- data = unicode(data, 'utf-8')
- if data.count('<body'):
- data = data.split('<body', 1)[1]
- if data.count('>'):
- data = data.split('>', 1)[1]
- if data.count('</body'):
- data = data.split('</body', 1)[0]
- data = data.strip().replace('\r\n', '\n')
- return data
-
-class _FeedURLHandler(urllib2.HTTPDigestAuthHandler, urllib2.HTTPRedirectHandler, urllib2.HTTPDefaultErrorHandler):
- def http_error_default(self, req, fp, code, msg, headers):
- if ((code / 100) == 3) and (code != 304):
- return self.http_error_302(req, fp, code, msg, headers)
- infourl = urllib.addinfourl(fp, headers, req.get_full_url())
- infourl.status = code
- return infourl
-
- def http_error_302(self, req, fp, code, msg, headers):
- if headers.dict.has_key('location'):
- infourl = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
- else:
- infourl = urllib.addinfourl(fp, headers, req.get_full_url())
- if not hasattr(infourl, 'status'):
- infourl.status = code
- return infourl
-
- def http_error_301(self, req, fp, code, msg, headers):
- if headers.dict.has_key('location'):
- infourl = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers)
- else:
- infourl = urllib.addinfourl(fp, headers, req.get_full_url())
- if not hasattr(infourl, 'status'):
- infourl.status = code
- return infourl
-
- http_error_300 = http_error_302
- http_error_303 = http_error_302
- http_error_307 = http_error_302
-
- def http_error_401(self, req, fp, code, msg, headers):
- # Check if
- # - server requires digest auth, AND
- # - we tried (unsuccessfully) with basic auth, AND
- # - we're using Python 2.3.3 or later (digest auth is irreparably broken in earlier versions)
- # If all conditions hold, parse authentication information
- # out of the Authorization header we sent the first time
- # (for the username and password) and the WWW-Authenticate
- # header the server sent back (for the realm) and retry
- # the request with the appropriate digest auth headers instead.
- # This evil genius hack has been brought to you by Aaron Swartz.
- host = urlparse.urlparse(req.get_full_url())[1]
- try:
- assert sys.version.split()[0] >= '2.3.3'
- assert base64 is not None
- user, passw = _base64decode(req.headers['Authorization'].split(' ')[1]).split(':')
- realm = re.findall('realm="([^"]*)"', headers['WWW-Authenticate'])[0]
- self.add_password(realm, host, user, passw)
- retry = self.http_error_auth_reqed('www-authenticate', host, req, headers)
- self.reset_retry_count()
- return retry
- except:
- return self.http_error_default(req, fp, code, msg, headers)
-
-def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers, request_headers):
- """URL, filename, or string --> stream
-
- This function lets you define parsers that take any input source
- (URL, pathname to local or network file, or actual data as a string)
- and deal with it in a uniform manner. Returned object is guaranteed
- to have all the basic stdio read methods (read, readline, readlines).
- Just .close() the object when you're done with it.
-
- If the etag argument is supplied, it will be used as the value of an
- If-None-Match request header.
-
- If the modified argument is supplied, it can be a tuple of 9 integers
- (as returned by gmtime() in the standard Python time module) or a date
- string in any format supported by feedparser. Regardless, it MUST
- be in GMT (Greenwich Mean Time). It will be reformatted into an
- RFC 1123-compliant date and used as the value of an If-Modified-Since
- request header.
-
- If the agent argument is supplied, it will be used as the value of a
- User-Agent request header.
-
- If the referrer argument is supplied, it will be used as the value of a
- Referer[sic] request header.
-
- If handlers is supplied, it is a list of handlers used to build a
- urllib2 opener.
-
- if request_headers is supplied it is a dictionary of HTTP request headers
- that will override the values generated by FeedParser.
- """
-
- if hasattr(url_file_stream_or_string, 'read'):
- return url_file_stream_or_string
-
- if url_file_stream_or_string == '-':
- return sys.stdin
-
- if urlparse.urlparse(url_file_stream_or_string)[0] in ('http', 'https', 'ftp', 'file', 'feed'):
- # Deal with the feed URI scheme
- if url_file_stream_or_string.startswith('feed:http'):
- url_file_stream_or_string = url_file_stream_or_string[5:]
- elif url_file_stream_or_string.startswith('feed:'):
- url_file_stream_or_string = 'http:' + url_file_stream_or_string[5:]
- if not agent:
- agent = USER_AGENT
- # test for inline user:password for basic auth
- auth = None
- if base64:
- urltype, rest = urllib.splittype(url_file_stream_or_string)
- realhost, rest = urllib.splithost(rest)
- if realhost:
- user_passwd, realhost = urllib.splituser(realhost)
- if user_passwd:
- url_file_stream_or_string = '%s://%s%s' % (urltype, realhost, rest)
- auth = base64.standard_b64encode(user_passwd).strip()
-
- # iri support
- try:
- if isinstance(url_file_stream_or_string,unicode):
- url_file_stream_or_string = url_file_stream_or_string.encode('idna').decode('utf-8')
- else:
- url_file_stream_or_string = url_file_stream_or_string.decode('utf-8').encode('idna').decode('utf-8')
- except:
- pass
-
- # try to open with urllib2 (to use optional headers)
- request = _build_urllib2_request(url_file_stream_or_string, agent, etag, modified, referrer, auth, request_headers)
- opener = apply(urllib2.build_opener, tuple(handlers + [_FeedURLHandler()]))
- opener.addheaders = [] # RMK - must clear so we only send our custom User-Agent
- try:
- return opener.open(request)
- finally:
- opener.close() # JohnD
-
- # try to open with native open function (if url_file_stream_or_string is a filename)
- try:
- return open(url_file_stream_or_string, 'rb')
- except:
- pass
-
- # treat url_file_stream_or_string as string
- return _StringIO(str(url_file_stream_or_string))
-
-def _build_urllib2_request(url, agent, etag, modified, referrer, auth, request_headers):
- request = urllib2.Request(url)
- request.add_header('User-Agent', agent)
- if etag:
- request.add_header('If-None-Match', etag)
- if type(modified) == type(''):
- modified = _parse_date(modified)
- elif isinstance(modified, datetime.datetime):
- modified = modified.utctimetuple()
- if modified:
- # format into an RFC 1123-compliant timestamp. We can't use
- # time.strftime() since the %a and %b directives can be affected
- # by the current locale, but RFC 2616 states that dates must be
- # in English.
- short_weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
- months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
- request.add_header('If-Modified-Since', '%s, %02d %s %04d %02d:%02d:%02d GMT' % (short_weekdays[modified[6]], modified[2], months[modified[1] - 1], modified[0], modified[3], modified[4], modified[5]))
- if referrer:
- request.add_header('Referer', referrer)
- if gzip and zlib:
- request.add_header('Accept-encoding', 'gzip, deflate')
- elif gzip:
- request.add_header('Accept-encoding', 'gzip')
- elif zlib:
- request.add_header('Accept-encoding', 'deflate')
- else:
- request.add_header('Accept-encoding', '')
- if auth:
- request.add_header('Authorization', 'Basic %s' % auth)
- if ACCEPT_HEADER:
- request.add_header('Accept', ACCEPT_HEADER)
- # use this for whatever -- cookies, special headers, etc
- # [('Cookie','Something'),('x-special-header','Another Value')]
- for header_name, header_value in request_headers.items():
- request.add_header(header_name, header_value)
- request.add_header('A-IM', 'feed') # RFC 3229 support
- return request
-
-_date_handlers = []
-def registerDateHandler(func):
- '''Register a date handler function (takes string, returns 9-tuple date in GMT)'''
- _date_handlers.insert(0, func)
-
-# ISO-8601 date parsing routines written by Fazal Majid.
-# The ISO 8601 standard is very convoluted and irregular - a full ISO 8601
-# parser is beyond the scope of feedparser and would be a worthwhile addition
-# to the Python library.
-# A single regular expression cannot parse ISO 8601 date formats into groups
-# as the standard is highly irregular (for instance is 030104 2003-01-04 or
-# 0301-04-01), so we use templates instead.
-# Please note the order in templates is significant because we need a
-# greedy match.
-_iso8601_tmpl = ['YYYY-?MM-?DD', 'YYYY-0MM?-?DD', 'YYYY-MM', 'YYYY-?OOO',
- 'YY-?MM-?DD', 'YY-?OOO', 'YYYY',
- '-YY-?MM', '-OOO', '-YY',
- '--MM-?DD', '--MM',
- '---DD',
- 'CC', '']
-_iso8601_re = [
- tmpl.replace(
- 'YYYY', r'(?P<year>\d{4})').replace(
- 'YY', r'(?P<year>\d\d)').replace(
- 'MM', r'(?P<month>[01]\d)').replace(
- 'DD', r'(?P<day>[0123]\d)').replace(
- 'OOO', r'(?P<ordinal>[0123]\d\d)').replace(
- 'CC', r'(?P<century>\d\d$)')
- + r'(T?(?P<hour>\d{2}):(?P<minute>\d{2})'
- + r'(:(?P<second>\d{2}))?'
- + r'(\.(?P<fracsecond>\d+))?'
- + r'(?P<tz>[+-](?P<tzhour>\d{2})(:(?P<tzmin>\d{2}))?|Z)?)?'
- for tmpl in _iso8601_tmpl]
-try:
- del tmpl
-except NameError:
- pass
-_iso8601_matches = [re.compile(regex).match for regex in _iso8601_re]
-try:
- del regex
-except NameError:
- pass
-def _parse_date_iso8601(dateString):
- '''Parse a variety of ISO-8601-compatible formats like 20040105'''
- m = None
- for _iso8601_match in _iso8601_matches:
- m = _iso8601_match(dateString)
- if m: break
- if not m: return
- if m.span() == (0, 0): return
- params = m.groupdict()
- ordinal = params.get('ordinal', 0)
- if ordinal:
- ordinal = int(ordinal)
- else:
- ordinal = 0
- year = params.get('year', '--')
- if not year or year == '--':
- year = time.gmtime()[0]
- elif len(year) == 2:
- # ISO 8601 assumes current century, i.e. 93 -> 2093, NOT 1993
- year = 100 * int(time.gmtime()[0] / 100) + int(year)
- else:
- year = int(year)
- month = params.get('month', '-')
- if not month or month == '-':
- # ordinals are NOT normalized by mktime, we simulate them
- # by setting month=1, day=ordinal
- if ordinal:
- month = 1
- else:
- month = time.gmtime()[1]
- month = int(month)
- day = params.get('day', 0)
- if not day:
- # see above
- if ordinal:
- day = ordinal
- elif params.get('century', 0) or \
- params.get('year', 0) or params.get('month', 0):
- day = 1
- else:
- day = time.gmtime()[2]
- else:
- day = int(day)
- # special case of the century - is the first year of the 21st century
- # 2000 or 2001 ? The debate goes on...
- if 'century' in params.keys():
- year = (int(params['century']) - 1) * 100 + 1
- # in ISO 8601 most fields are optional
- for field in ['hour', 'minute', 'second', 'tzhour', 'tzmin']:
- if not params.get(field, None):
- params[field] = 0
- hour = int(params.get('hour', 0))
- minute = int(params.get('minute', 0))
- second = int(float(params.get('second', 0)))
- # weekday is normalized by mktime(), we can ignore it
- weekday = 0
- daylight_savings_flag = -1
- tm = [year, month, day, hour, minute, second, weekday,
- ordinal, daylight_savings_flag]
- # ISO 8601 time zone adjustments
- tz = params.get('tz')
- if tz and tz != 'Z':
- if tz[0] == '-':
- tm[3] += int(params.get('tzhour', 0))
- tm[4] += int(params.get('tzmin', 0))
- elif tz[0] == '+':
- tm[3] -= int(params.get('tzhour', 0))
- tm[4] -= int(params.get('tzmin', 0))
- else:
- return None
- # Python's time.mktime() is a wrapper around the ANSI C mktime(3c)
- # which is guaranteed to normalize d/m/y/h/m/s.
- # Many implementations have bugs, but we'll pretend they don't.
- return time.localtime(time.mktime(tuple(tm)))
-registerDateHandler(_parse_date_iso8601)
-
-# 8-bit date handling routines written by ytrewq1.
-_korean_year = u'\ub144' # b3e2 in euc-kr
-_korean_month = u'\uc6d4' # bff9 in euc-kr
-_korean_day = u'\uc77c' # c0cf in euc-kr
-_korean_am = u'\uc624\uc804' # bfc0 c0fc in euc-kr
-_korean_pm = u'\uc624\ud6c4' # bfc0 c8c4 in euc-kr
-
-_korean_onblog_date_re = \
- re.compile('(\d{4})%s\s+(\d{2})%s\s+(\d{2})%s\s+(\d{2}):(\d{2}):(\d{2})' % \
- (_korean_year, _korean_month, _korean_day))
-_korean_nate_date_re = \
- re.compile(u'(\d{4})-(\d{2})-(\d{2})\s+(%s|%s)\s+(\d{,2}):(\d{,2}):(\d{,2})' % \
- (_korean_am, _korean_pm))
-def _parse_date_onblog(dateString):
- '''Parse a string according to the OnBlog 8-bit date format'''
- m = _korean_onblog_date_re.match(dateString)
- if not m: return
- w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % \
- {'year': m.group(1), 'month': m.group(2), 'day': m.group(3),\
- 'hour': m.group(4), 'minute': m.group(5), 'second': m.group(6),\
- 'zonediff': '+09:00'}
- if _debug: sys.stderr.write('OnBlog date parsed as: %s\n' % w3dtfdate)
- return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_onblog)
-
-def _parse_date_nate(dateString):
- '''Parse a string according to the Nate 8-bit date format'''
- m = _korean_nate_date_re.match(dateString)
- if not m: return
- hour = int(m.group(5))
- ampm = m.group(4)
- if (ampm == _korean_pm):
- hour += 12
- hour = str(hour)
- if len(hour) == 1:
- hour = '0' + hour
- w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % \
- {'year': m.group(1), 'month': m.group(2), 'day': m.group(3),\
- 'hour': hour, 'minute': m.group(6), 'second': m.group(7),\
- 'zonediff': '+09:00'}
- if _debug: sys.stderr.write('Nate date parsed as: %s\n' % w3dtfdate)
- return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_nate)
-
-_mssql_date_re = \
- re.compile('(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})(\.\d+)?')
-def _parse_date_mssql(dateString):
- '''Parse a string according to the MS SQL date format'''
- m = _mssql_date_re.match(dateString)
- if not m: return
- w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % \
- {'year': m.group(1), 'month': m.group(2), 'day': m.group(3),\
- 'hour': m.group(4), 'minute': m.group(5), 'second': m.group(6),\
- 'zonediff': '+09:00'}
- if _debug: sys.stderr.write('MS SQL date parsed as: %s\n' % w3dtfdate)
- return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_mssql)
-
-# Unicode strings for Greek date strings
-_greek_months = \
- { \
- u'\u0399\u03b1\u03bd': u'Jan', # c9e1ed in iso-8859-7
- u'\u03a6\u03b5\u03b2': u'Feb', # d6e5e2 in iso-8859-7
- u'\u039c\u03ac\u03ce': u'Mar', # ccdcfe in iso-8859-7
- u'\u039c\u03b1\u03ce': u'Mar', # cce1fe in iso-8859-7
- u'\u0391\u03c0\u03c1': u'Apr', # c1f0f1 in iso-8859-7
- u'\u039c\u03ac\u03b9': u'May', # ccdce9 in iso-8859-7
- u'\u039c\u03b1\u03ca': u'May', # cce1fa in iso-8859-7
- u'\u039c\u03b1\u03b9': u'May', # cce1e9 in iso-8859-7
- u'\u0399\u03bf\u03cd\u03bd': u'Jun', # c9effded in iso-8859-7
- u'\u0399\u03bf\u03bd': u'Jun', # c9efed in iso-8859-7
- u'\u0399\u03bf\u03cd\u03bb': u'Jul', # c9effdeb in iso-8859-7
- u'\u0399\u03bf\u03bb': u'Jul', # c9f9eb in iso-8859-7
- u'\u0391\u03cd\u03b3': u'Aug', # c1fde3 in iso-8859-7
- u'\u0391\u03c5\u03b3': u'Aug', # c1f5e3 in iso-8859-7
- u'\u03a3\u03b5\u03c0': u'Sep', # d3e5f0 in iso-8859-7
- u'\u039f\u03ba\u03c4': u'Oct', # cfeaf4 in iso-8859-7
- u'\u039d\u03bf\u03ad': u'Nov', # cdefdd in iso-8859-7
- u'\u039d\u03bf\u03b5': u'Nov', # cdefe5 in iso-8859-7
- u'\u0394\u03b5\u03ba': u'Dec', # c4e5ea in iso-8859-7
- }
-
-_greek_wdays = \
- { \
- u'\u039a\u03c5\u03c1': u'Sun', # caf5f1 in iso-8859-7
- u'\u0394\u03b5\u03c5': u'Mon', # c4e5f5 in iso-8859-7
- u'\u03a4\u03c1\u03b9': u'Tue', # d4f1e9 in iso-8859-7
- u'\u03a4\u03b5\u03c4': u'Wed', # d4e5f4 in iso-8859-7
- u'\u03a0\u03b5\u03bc': u'Thu', # d0e5ec in iso-8859-7
- u'\u03a0\u03b1\u03c1': u'Fri', # d0e1f1 in iso-8859-7
- u'\u03a3\u03b1\u03b2': u'Sat', # d3e1e2 in iso-8859-7
- }
-
-_greek_date_format_re = \
- re.compile(u'([^,]+),\s+(\d{2})\s+([^\s]+)\s+(\d{4})\s+(\d{2}):(\d{2}):(\d{2})\s+([^\s]+)')
-
-def _parse_date_greek(dateString):
- '''Parse a string according to a Greek 8-bit date format.'''
- m = _greek_date_format_re.match(dateString)
- if not m: return
- try:
- wday = _greek_wdays[m.group(1)]
- month = _greek_months[m.group(3)]
- except:
- return
- rfc822date = '%(wday)s, %(day)s %(month)s %(year)s %(hour)s:%(minute)s:%(second)s %(zonediff)s' % \
- {'wday': wday, 'day': m.group(2), 'month': month, 'year': m.group(4),\
- 'hour': m.group(5), 'minute': m.group(6), 'second': m.group(7),\
- 'zonediff': m.group(8)}
- if _debug: sys.stderr.write('Greek date parsed as: %s\n' % rfc822date)
- return _parse_date_rfc822(rfc822date)
-registerDateHandler(_parse_date_greek)
-
-# Unicode strings for Hungarian date strings
-_hungarian_months = \
- { \
- u'janu\u00e1r': u'01', # e1 in iso-8859-2
- u'febru\u00e1ri': u'02', # e1 in iso-8859-2
- u'm\u00e1rcius': u'03', # e1 in iso-8859-2
- u'\u00e1prilis': u'04', # e1 in iso-8859-2
- u'm\u00e1ujus': u'05', # e1 in iso-8859-2
- u'j\u00fanius': u'06', # fa in iso-8859-2
- u'j\u00falius': u'07', # fa in iso-8859-2
- u'augusztus': u'08',
- u'szeptember': u'09',
- u'okt\u00f3ber': u'10', # f3 in iso-8859-2
- u'november': u'11',
- u'december': u'12',
- }
-
-_hungarian_date_format_re = \
- re.compile(u'(\d{4})-([^-]+)-(\d{,2})T(\d{,2}):(\d{2})((\+|-)(\d{,2}:\d{2}))')
-
-def _parse_date_hungarian(dateString):
- '''Parse a string according to a Hungarian 8-bit date format.'''
- m = _hungarian_date_format_re.match(dateString)
- if not m: return
- try:
- month = _hungarian_months[m.group(2)]
- day = m.group(3)
- if len(day) == 1:
- day = '0' + day
- hour = m.group(4)
- if len(hour) == 1:
- hour = '0' + hour
- except:
- return
- w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s%(zonediff)s' % \
- {'year': m.group(1), 'month': month, 'day': day,\
- 'hour': hour, 'minute': m.group(5),\
- 'zonediff': m.group(6)}
- if _debug: sys.stderr.write('Hungarian date parsed as: %s\n' % w3dtfdate)
- return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_hungarian)
-
-# W3DTF-style date parsing adapted from PyXML xml.utils.iso8601, written by
-# Drake and licensed under the Python license. Removed all range checking
-# for month, day, hour, minute, and second, since mktime will normalize
-# these later
-def _parse_date_w3dtf(dateString):
- def __extract_date(m):
- year = int(m.group('year'))
- if year < 100:
- year = 100 * int(time.gmtime()[0] / 100) + int(year)
- if year < 1000:
- return 0, 0, 0
- julian = m.group('julian')
- if julian:
- julian = int(julian)
- month = julian / 30 + 1
- day = julian % 30 + 1
- jday = None
- while jday != julian:
- t = time.mktime((year, month, day, 0, 0, 0, 0, 0, 0))
- jday = time.gmtime(t)[-2]
- diff = abs(jday - julian)
- if jday > julian:
- if diff < day:
- day = day - diff
- else:
- month = month - 1
- day = 31
- elif jday < julian:
- if day + diff < 28:
- day = day + diff
- else:
- month = month + 1
- return year, month, day
- month = m.group('month')
- day = 1
- if month is None:
- month = 1
- else:
- month = int(month)
- day = m.group('day')
- if day:
- day = int(day)
- else:
- day = 1
- return year, month, day
-
- def __extract_time(m):
- if not m:
- return 0, 0, 0
- hours = m.group('hours')
- if not hours:
- return 0, 0, 0
- hours = int(hours)
- minutes = int(m.group('minutes'))
- seconds = m.group('seconds')
- if seconds:
- seconds = int(seconds)
- else:
- seconds = 0
- return hours, minutes, seconds
-
- def __extract_tzd(m):
- '''Return the Time Zone Designator as an offset in seconds from UTC.'''
- if not m:
- return 0
- tzd = m.group('tzd')
- if not tzd:
- return 0
- if tzd == 'Z':
- return 0
- hours = int(m.group('tzdhours'))
- minutes = m.group('tzdminutes')
- if minutes:
- minutes = int(minutes)
- else:
- minutes = 0
- offset = (hours*60 + minutes) * 60
- if tzd[0] == '+':
- return -offset
- return offset
-
- __date_re = ('(?P<year>\d\d\d\d)'
- '(?:(?P<dsep>-|)'
- '(?:(?P<month>\d\d)(?:(?P=dsep)(?P<day>\d\d))?'
- '|(?P<julian>\d\d\d)))?')
- __tzd_re = '(?P<tzd>[-+](?P<tzdhours>\d\d)(?::?(?P<tzdminutes>\d\d))|Z)'
- __tzd_rx = re.compile(__tzd_re)
- __time_re = ('(?P<hours>\d\d)(?P<tsep>:|)(?P<minutes>\d\d)'
- '(?:(?P=tsep)(?P<seconds>\d\d)(?:[.,]\d+)?)?'
- + __tzd_re)
- __datetime_re = '%s(?:T%s)?' % (__date_re, __time_re)
- __datetime_rx = re.compile(__datetime_re)
- m = __datetime_rx.match(dateString)
- if (m is None) or (m.group() != dateString): return
- gmt = __extract_date(m) + __extract_time(m) + (0, 0, 0)
- if gmt[0] == 0: return
- return time.gmtime(time.mktime(gmt) + __extract_tzd(m) - time.timezone)
-registerDateHandler(_parse_date_w3dtf)
-
-def _parse_date_rfc822(dateString):
- '''Parse an RFC822, RFC1123, RFC2822, or asctime-style date'''
- data = dateString.split()
- if data[0][-1] in (',', '.') or data[0].lower() in rfc822._daynames:
- del data[0]
- if len(data) == 4:
- s = data[3]
- i = s.find('+')
- if i > 0:
- data[3:] = [s[:i], s[i+1:]]
- else:
- data.append('')
- dateString = " ".join(data)
- # Account for the Etc/GMT timezone by stripping 'Etc/'
- elif len(data) == 5 and data[4].lower().startswith('etc/'):
- data[4] = data[4][4:]
- dateString = " ".join(data)
- if len(data) < 5:
- dateString += ' 00:00:00 GMT'
- tm = rfc822.parsedate_tz(dateString)
- if tm:
- return time.gmtime(rfc822.mktime_tz(tm))
-# rfc822.py defines several time zones, but we define some extra ones.
-# 'ET' is equivalent to 'EST', etc.
-_additional_timezones = {'AT': -400, 'ET': -500, 'CT': -600, 'MT': -700, 'PT': -800}
-rfc822._timezones.update(_additional_timezones)
-registerDateHandler(_parse_date_rfc822)
-
-def _parse_date_perforce(aDateString):
- """parse a date in yyyy/mm/dd hh:mm:ss TTT format"""
- # Fri, 2006/09/15 08:19:53 EDT
- _my_date_pattern = re.compile( \
- r'(\w{,3}), (\d{,4})/(\d{,2})/(\d{2}) (\d{,2}):(\d{2}):(\d{2}) (\w{,3})')
-
- dow, year, month, day, hour, minute, second, tz = \
- _my_date_pattern.search(aDateString).groups()
- months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
- dateString = "%s, %s %s %s %s:%s:%s %s" % (dow, day, months[int(month) - 1], year, hour, minute, second, tz)
- tm = rfc822.parsedate_tz(dateString)
- if tm:
- return time.gmtime(rfc822.mktime_tz(tm))
-registerDateHandler(_parse_date_perforce)
-
-def _parse_date(dateString):
- '''Parses a variety of date formats into a 9-tuple in GMT'''
- for handler in _date_handlers:
- try:
- date9tuple = handler(dateString)
- if not date9tuple: continue
- if len(date9tuple) != 9:
- if _debug: sys.stderr.write('date handler function must return 9-tuple\n')
- raise ValueError
- map(int, date9tuple)
- return date9tuple
- except Exception, e:
- if _debug: sys.stderr.write('%s raised %s\n' % (handler.__name__, repr(e)))
- pass
- return None
-
-def _getCharacterEncoding(http_headers, xml_data):
- '''Get the character encoding of the XML document
-
- http_headers is a dictionary
- xml_data is a raw string (not Unicode)
-
- This is so much trickier than it sounds, it's not even funny.
- According to RFC 3023 ('XML Media Types'), if the HTTP Content-Type
- is application/xml, application/*+xml,
- application/xml-external-parsed-entity, or application/xml-dtd,
- the encoding given in the charset parameter of the HTTP Content-Type
- takes precedence over the encoding given in the XML prefix within the
- document, and defaults to 'utf-8' if neither are specified. But, if
- the HTTP Content-Type is text/xml, text/*+xml, or
- text/xml-external-parsed-entity, the encoding given in the XML prefix
- within the document is ALWAYS IGNORED and only the encoding given in
- the charset parameter of the HTTP Content-Type header should be
- respected, and it defaults to 'us-ascii' if not specified.
-
- Furthermore, discussion on the atom-syntax mailing list with the
- author of RFC 3023 leads me to the conclusion that any document
- served with a Content-Type of text/* and no charset parameter
- must be treated as us-ascii. (We now do this.) And also that it
- must always be flagged as non-well-formed. (We now do this too.)
-
- If Content-Type is unspecified (input was local file or non-HTTP source)
- or unrecognized (server just got it totally wrong), then go by the
- encoding given in the XML prefix of the document and default to
- 'iso-8859-1' as per the HTTP specification (RFC 2616).
-
- Then, assuming we didn't find a character encoding in the HTTP headers
- (and the HTTP Content-type allowed us to look in the body), we need
- to sniff the first few bytes of the XML data and try to determine
- whether the encoding is ASCII-compatible. Section F of the XML
- specification shows the way here:
- http://www.w3.org/TR/REC-xml/#sec-guessing-no-ext-info
-
- If the sniffed encoding is not ASCII-compatible, we need to make it
- ASCII compatible so that we can sniff further into the XML declaration
- to find the encoding attribute, which will tell us the true encoding.
-
- Of course, none of this guarantees that we will be able to parse the
- feed in the declared character encoding (assuming it was declared
- correctly, which many are not). CJKCodecs and iconv_codec help a lot;
- you should definitely install them if you can.
- http://cjkpython.i18n.org/
- '''
-
- def _parseHTTPContentType(content_type):
- '''takes HTTP Content-Type header and returns (content type, charset)
-
- If no charset is specified, returns (content type, '')
- If no content type is specified, returns ('', '')
- Both return parameters are guaranteed to be lowercase strings
- '''
- content_type = content_type or ''
- content_type, params = cgi.parse_header(content_type)
- return content_type, params.get('charset', '').replace("'", '')
-
- sniffed_xml_encoding = ''
- xml_encoding = ''
- true_encoding = ''
- http_content_type, http_encoding = _parseHTTPContentType(http_headers.get('content-type', http_headers.get('Content-type')))
- # Must sniff for non-ASCII-compatible character encodings before
- # searching for XML declaration. This heuristic is defined in
- # section F of the XML specification:
- # http://www.w3.org/TR/REC-xml/#sec-guessing-no-ext-info
- try:
- if xml_data[:4] == _l2bytes([0x4c, 0x6f, 0xa7, 0x94]):
- # EBCDIC
- xml_data = _ebcdic_to_ascii(xml_data)
- elif xml_data[:4] == _l2bytes([0x00, 0x3c, 0x00, 0x3f]):
- # UTF-16BE
- sniffed_xml_encoding = 'utf-16be'
- xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
- elif (len(xml_data) >= 4) and (xml_data[:2] == _l2bytes([0xfe, 0xff])) and (xml_data[2:4] != _l2bytes([0x00, 0x00])):
- # UTF-16BE with BOM
- sniffed_xml_encoding = 'utf-16be'
- xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
- elif xml_data[:4] == _l2bytes([0x3c, 0x00, 0x3f, 0x00]):
- # UTF-16LE
- sniffed_xml_encoding = 'utf-16le'
- xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
- elif (len(xml_data) >= 4) and (xml_data[:2] == _l2bytes([0xff, 0xfe])) and (xml_data[2:4] != _l2bytes([0x00, 0x00])):
- # UTF-16LE with BOM
- sniffed_xml_encoding = 'utf-16le'
- xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
- elif xml_data[:4] == _l2bytes([0x00, 0x00, 0x00, 0x3c]):
- # UTF-32BE
- sniffed_xml_encoding = 'utf-32be'
- xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
- elif xml_data[:4] == _l2bytes([0x3c, 0x00, 0x00, 0x00]):
- # UTF-32LE
- sniffed_xml_encoding = 'utf-32le'
- xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
- elif xml_data[:4] == _l2bytes([0x00, 0x00, 0xfe, 0xff]):
- # UTF-32BE with BOM
- sniffed_xml_encoding = 'utf-32be'
- xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
- elif xml_data[:4] == _l2bytes([0xff, 0xfe, 0x00, 0x00]):
- # UTF-32LE with BOM
- sniffed_xml_encoding = 'utf-32le'
- xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
- elif xml_data[:3] == _l2bytes([0xef, 0xbb, 0xbf]):
- # UTF-8 with BOM
- sniffed_xml_encoding = 'utf-8'
- xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
- else:
- # ASCII-compatible
- pass
- xml_encoding_match = re.compile(_s2bytes('^<\?.*encoding=[\'"](.*?)[\'"].*\?>')).match(xml_data)
- except:
- xml_encoding_match = None
- if xml_encoding_match:
- xml_encoding = xml_encoding_match.groups()[0].decode('utf-8').lower()
- if sniffed_xml_encoding and (xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode', 'iso-10646-ucs-4', 'ucs-4', 'csucs4', 'utf-16', 'utf-32', 'utf_16', 'utf_32', 'utf16', 'u16')):
- xml_encoding = sniffed_xml_encoding
- acceptable_content_type = 0
- application_content_types = ('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity')
- text_content_types = ('text/xml', 'text/xml-external-parsed-entity')
- if (http_content_type in application_content_types) or \
- (http_content_type.startswith('application/') and http_content_type.endswith('+xml')):
- acceptable_content_type = 1
- true_encoding = http_encoding or xml_encoding or 'utf-8'
- elif (http_content_type in text_content_types) or \
- (http_content_type.startswith('text/')) and http_content_type.endswith('+xml'):
- acceptable_content_type = 1
- true_encoding = http_encoding or 'us-ascii'
- elif http_content_type.startswith('text/'):
- true_encoding = http_encoding or 'us-ascii'
- elif http_headers and (not (http_headers.has_key('content-type') or http_headers.has_key('Content-type'))):
- true_encoding = xml_encoding or 'iso-8859-1'
- else:
- true_encoding = xml_encoding or 'utf-8'
- # some feeds claim to be gb2312 but are actually gb18030.
- # apparently MSIE and Firefox both do the following switch:
- if true_encoding.lower() == 'gb2312':
- true_encoding = 'gb18030'
- return true_encoding, http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type
-
-def _toUTF8(data, encoding):
- '''Changes an XML data stream on the fly to specify a new encoding
-
- data is a raw sequence of bytes (not Unicode) that is presumed to be in %encoding already
- encoding is a string recognized by encodings.aliases
- '''
- if _debug: sys.stderr.write('entering _toUTF8, trying encoding %s\n' % encoding)
- # strip Byte Order Mark (if present)
- if (len(data) >= 4) and (data[:2] == _l2bytes([0xfe, 0xff])) and (data[2:4] != _l2bytes([0x00, 0x00])):
- if _debug:
- sys.stderr.write('stripping BOM\n')
- if encoding != 'utf-16be':
- sys.stderr.write('trying utf-16be instead\n')
- encoding = 'utf-16be'
- data = data[2:]
- elif (len(data) >= 4) and (data[:2] == _l2bytes([0xff, 0xfe])) and (data[2:4] != _l2bytes([0x00, 0x00])):
- if _debug:
- sys.stderr.write('stripping BOM\n')
- if encoding != 'utf-16le':
- sys.stderr.write('trying utf-16le instead\n')
- encoding = 'utf-16le'
- data = data[2:]
- elif data[:3] == _l2bytes([0xef, 0xbb, 0xbf]):
- if _debug:
- sys.stderr.write('stripping BOM\n')
- if encoding != 'utf-8':
- sys.stderr.write('trying utf-8 instead\n')
- encoding = 'utf-8'
- data = data[3:]
- elif data[:4] == _l2bytes([0x00, 0x00, 0xfe, 0xff]):
- if _debug:
- sys.stderr.write('stripping BOM\n')
- if encoding != 'utf-32be':
- sys.stderr.write('trying utf-32be instead\n')
- encoding = 'utf-32be'
- data = data[4:]
- elif data[:4] == _l2bytes([0xff, 0xfe, 0x00, 0x00]):
- if _debug:
- sys.stderr.write('stripping BOM\n')
- if encoding != 'utf-32le':
- sys.stderr.write('trying utf-32le instead\n')
- encoding = 'utf-32le'
- data = data[4:]
- newdata = unicode(data, encoding)
- if _debug: sys.stderr.write('successfully converted %s data to unicode\n' % encoding)
- declmatch = re.compile('^<\?xml[^>]*?>')
- newdecl = '''<?xml version='1.0' encoding='utf-8'?>'''
- if declmatch.search(newdata):
- newdata = declmatch.sub(newdecl, newdata)
- else:
- newdata = newdecl + u'\n' + newdata
- return newdata.encode('utf-8')
-
-def _stripDoctype(data):
- '''Strips DOCTYPE from XML document, returns (rss_version, stripped_data)
-
- rss_version may be 'rss091n' or None
- stripped_data is the same XML document, minus the DOCTYPE
- '''
- start = re.search(_s2bytes('<\w'), data)
- start = start and start.start() or -1
- head,data = data[:start+1], data[start+1:]
-
- entity_pattern = re.compile(_s2bytes(r'^\s*<!ENTITY([^>]*?)>'), re.MULTILINE)
- entity_results=entity_pattern.findall(head)
- head = entity_pattern.sub(_s2bytes(''), head)
- doctype_pattern = re.compile(_s2bytes(r'^\s*<!DOCTYPE([^>]*?)>'), re.MULTILINE)
- doctype_results = doctype_pattern.findall(head)
- doctype = doctype_results and doctype_results[0] or _s2bytes('')
- if doctype.lower().count(_s2bytes('netscape')):
- version = 'rss091n'
- else:
- version = None
-
- # only allow in 'safe' inline entity definitions
- replacement=_s2bytes('')
- if len(doctype_results)==1 and entity_results:
- safe_pattern=re.compile(_s2bytes('\s+(\w+)\s+"(&#\w+;|[^&"]*)"'))
- safe_entities=filter(lambda e: safe_pattern.match(e),entity_results)
- if safe_entities:
- replacement=_s2bytes('<!DOCTYPE feed [\n <!ENTITY') + _s2bytes('>\n <!ENTITY ').join(safe_entities) + _s2bytes('>\n]>')
- data = doctype_pattern.sub(replacement, head) + data
-
- return version, data, dict(replacement and [(k.decode('utf-8'), v.decode('utf-8')) for k, v in safe_pattern.findall(replacement)])
-
-def parse(url_file_stream_or_string, etag=None, modified=None, agent=None, referrer=None, handlers=[], request_headers={}, response_headers={}):
- '''Parse a feed from a URL, file, stream, or string.
-
- request_headers, if given, is a dict from http header name to value to add
- to the request; this overrides internally generated values.
- '''
- result = FeedParserDict()
- result['feed'] = FeedParserDict()
- result['entries'] = []
- if _XML_AVAILABLE:
- result['bozo'] = 0
- if not isinstance(handlers, list):
- handlers = [handlers]
- try:
- f = _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers, request_headers)
- data = f.read()
- except Exception, e:
- result['bozo'] = 1
- result['bozo_exception'] = e
- data = None
- f = None
-
- if hasattr(f, 'headers'):
- result['headers'] = dict(f.headers)
- # overwrite existing headers using response_headers
- if 'headers' in result:
- result['headers'].update(response_headers)
- elif response_headers:
- result['headers'] = copy.deepcopy(response_headers)
-
- # if feed is gzip-compressed, decompress it
- if f and data and 'headers' in result:
- if gzip and result['headers'].get('content-encoding') == 'gzip':
- try:
- data = gzip.GzipFile(fileobj=_StringIO(data)).read()
- except Exception, e:
- # Some feeds claim to be gzipped but they're not, so
- # we get garbage. Ideally, we should re-request the
- # feed without the 'Accept-encoding: gzip' header,
- # but we don't.
- result['bozo'] = 1
- result['bozo_exception'] = e
- data = ''
- elif zlib and result['headers'].get('content-encoding') == 'deflate':
- try:
- data = zlib.decompress(data, -zlib.MAX_WBITS)
- except Exception, e:
- result['bozo'] = 1
- result['bozo_exception'] = e
- data = ''
-
- # save HTTP headers
- if 'headers' in result:
- if 'etag' in result['headers'] or 'ETag' in result['headers']:
- etag = result['headers'].get('etag', result['headers'].get('ETag'))
- if etag:
- result['etag'] = etag
- if 'last-modified' in result['headers'] or 'Last-Modified' in result['headers']:
- modified = result['headers'].get('last-modified', result['headers'].get('Last-Modified'))
- if modified:
- result['modified'] = _parse_date(modified)
- if hasattr(f, 'url'):
- result['href'] = f.url
- result['status'] = 200
- if hasattr(f, 'status'):
- result['status'] = f.status
- if hasattr(f, 'close'):
- f.close()
-
- # there are four encodings to keep track of:
- # - http_encoding is the encoding declared in the Content-Type HTTP header
- # - xml_encoding is the encoding declared in the <?xml declaration
- # - sniffed_encoding is the encoding sniffed from the first 4 bytes of the XML data
- # - result['encoding'] is the actual encoding, as per RFC 3023 and a variety of other conflicting specifications
- http_headers = result.get('headers', {})
- result['encoding'], http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type = \
- _getCharacterEncoding(http_headers, data)
- if http_headers and (not acceptable_content_type):
- if http_headers.has_key('content-type') or http_headers.has_key('Content-type'):
- bozo_message = '%s is not an XML media type' % http_headers.get('content-type', http_headers.get('Content-type'))
- else:
- bozo_message = 'no Content-type specified'
- result['bozo'] = 1
- result['bozo_exception'] = NonXMLContentType(bozo_message)
-
- if data is not None:
- result['version'], data, entities = _stripDoctype(data)
-
- # ensure that baseuri is an absolute uri using an acceptable URI scheme
- contentloc = http_headers.get('content-location', http_headers.get('Content-Location', ''))
- href = result.get('href', '')
- baseuri = _makeSafeAbsoluteURI(href, contentloc) or _makeSafeAbsoluteURI(contentloc) or href
-
- baselang = http_headers.get('content-language', http_headers.get('Content-Language', None))
-
- # if server sent 304, we're done
- if result.get('status', 0) == 304:
- result['version'] = ''
- result['debug_message'] = 'The feed has not changed since you last checked, ' + \
- 'so the server sent no data. This is a feature, not a bug!'
- return result
-
- # if there was a problem downloading, we're done
- if data is None:
- return result
-
- # determine character encoding
- use_strict_parser = 0
- known_encoding = 0
- tried_encodings = []
- # try: HTTP encoding, declared XML encoding, encoding sniffed from BOM
- for proposed_encoding in (result['encoding'], xml_encoding, sniffed_xml_encoding):
- if not proposed_encoding: continue
- if proposed_encoding in tried_encodings: continue
- tried_encodings.append(proposed_encoding)
- try:
- data = _toUTF8(data, proposed_encoding)
- known_encoding = use_strict_parser = 1
- break
- except:
- pass
- # if no luck and we have auto-detection library, try that
- if (not known_encoding) and chardet:
- try:
- proposed_encoding = chardet.detect(data)['encoding']
- if proposed_encoding and (proposed_encoding not in tried_encodings):
- tried_encodings.append(proposed_encoding)
- data = _toUTF8(data, proposed_encoding)
- known_encoding = use_strict_parser = 1
- except:
- pass
- # if still no luck and we haven't tried utf-8 yet, try that
- if (not known_encoding) and ('utf-8' not in tried_encodings):
- try:
- proposed_encoding = 'utf-8'
- tried_encodings.append(proposed_encoding)
- data = _toUTF8(data, proposed_encoding)
- known_encoding = use_strict_parser = 1
- except:
- pass
- # if still no luck and we haven't tried windows-1252 yet, try that
- if (not known_encoding) and ('windows-1252' not in tried_encodings):
- try:
- proposed_encoding = 'windows-1252'
- tried_encodings.append(proposed_encoding)
- data = _toUTF8(data, proposed_encoding)
- known_encoding = use_strict_parser = 1
- except:
- pass
- # if still no luck and we haven't tried iso-8859-2 yet, try that.
- if (not known_encoding) and ('iso-8859-2' not in tried_encodings):
- try:
- proposed_encoding = 'iso-8859-2'
- tried_encodings.append(proposed_encoding)
- data = _toUTF8(data, proposed_encoding)
- known_encoding = use_strict_parser = 1
- except:
- pass
- # if still no luck, give up
- if not known_encoding:
- result['bozo'] = 1
- result['bozo_exception'] = CharacterEncodingUnknown( \
- 'document encoding unknown, I tried ' + \
- '%s, %s, utf-8, windows-1252, and iso-8859-2 but nothing worked' % \
- (result['encoding'], xml_encoding))
- result['encoding'] = ''
- elif proposed_encoding != result['encoding']:
- result['bozo'] = 1
- result['bozo_exception'] = CharacterEncodingOverride( \
- 'document declared as %s, but parsed as %s' % \
- (result['encoding'], proposed_encoding))
- result['encoding'] = proposed_encoding
-
- if not _XML_AVAILABLE:
- use_strict_parser = 0
- if use_strict_parser:
- # initialize the SAX parser
- feedparser = _StrictFeedParser(baseuri, baselang, 'utf-8')
- saxparser = xml.sax.make_parser(PREFERRED_XML_PARSERS)
- saxparser.setFeature(xml.sax.handler.feature_namespaces, 1)
- saxparser.setContentHandler(feedparser)
- saxparser.setErrorHandler(feedparser)
- source = xml.sax.xmlreader.InputSource()
- source.setByteStream(_StringIO(data))
- if hasattr(saxparser, '_ns_stack'):
- # work around bug in built-in SAX parser (doesn't recognize xml: namespace)
- # PyXML doesn't have this problem, and it doesn't have _ns_stack either
- saxparser._ns_stack.append({'http://www.w3.org/XML/1998/namespace':'xml'})
- try:
- saxparser.parse(source)
- except Exception, e:
- if _debug:
- import traceback
- traceback.print_stack()
- traceback.print_exc()
- sys.stderr.write('xml parsing failed\n')
- result['bozo'] = 1
- result['bozo_exception'] = feedparser.exc or e
- use_strict_parser = 0
- if not use_strict_parser:
- feedparser = _LooseFeedParser(baseuri, baselang, 'utf-8', entities)
- feedparser.feed(data.decode('utf-8', 'replace'))
- result['feed'] = feedparser.feeddata
- result['entries'] = feedparser.entries
- result['version'] = result['version'] or feedparser.version
- result['namespaces'] = feedparser.namespacesInUse
- return result
-
-class Serializer:
- def __init__(self, results):
- self.results = results
-
-class TextSerializer(Serializer):
- def write(self, stream=sys.stdout):
- self._writer(stream, self.results, '')
-
- def _writer(self, stream, node, prefix):
- if not node: return
- if hasattr(node, 'keys'):
- keys = node.keys()
- keys.sort()
- for k in keys:
- if k in ('description', 'link'): continue
- if node.has_key(k + '_detail'): continue
- if node.has_key(k + '_parsed'): continue
- self._writer(stream, node[k], prefix + k + '.')
- elif type(node) == types.ListType:
- index = 0
- for n in node:
- self._writer(stream, n, prefix[:-1] + '[' + str(index) + '].')
- index += 1
- else:
- try:
- s = str(node).encode('utf-8')
- s = s.replace('\\', '\\\\')
- s = s.replace('\r', '')
- s = s.replace('\n', r'\n')
- stream.write(prefix[:-1])
- stream.write('=')
- stream.write(s)
- stream.write('\n')
- except:
- pass
-
-class PprintSerializer(Serializer):
- def write(self, stream=sys.stdout):
- if self.results.has_key('href'):
- stream.write(self.results['href'] + '\n\n')
- from pprint import pprint
- pprint(self.results, stream)
- stream.write('\n')
-
-if __name__ == '__main__':
- try:
- from optparse import OptionParser
- except:
- OptionParser = None
-
- if OptionParser:
- optionParser = OptionParser(version=__version__, usage="%prog [options] url_or_filename_or_-")
- optionParser.set_defaults(format="pprint")
- optionParser.add_option("-A", "--user-agent", dest="agent", metavar="AGENT", help="User-Agent for HTTP URLs")
- optionParser.add_option("-e", "--referer", "--referrer", dest="referrer", metavar="URL", help="Referrer for HTTP URLs")
- optionParser.add_option("-t", "--etag", dest="etag", metavar="TAG", help="ETag/If-None-Match for HTTP URLs")
- optionParser.add_option("-m", "--last-modified", dest="modified", metavar="DATE", help="Last-modified/If-Modified-Since for HTTP URLs (any supported date format)")
- optionParser.add_option("-f", "--format", dest="format", metavar="FORMAT", help="output results in FORMAT (text, pprint)")
- optionParser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="write debugging information to stderr")
- (options, urls) = optionParser.parse_args()
- if options.verbose:
- _debug = 1
- if not urls:
- optionParser.print_help()
- sys.exit(0)
- else:
- if not sys.argv[1:]:
- print __doc__
- sys.exit(0)
- class _Options:
- etag = modified = agent = referrer = None
- format = 'pprint'
- options = _Options()
- urls = sys.argv[1:]
-
- zopeCompatibilityHack()
-
- serializer = globals().get(options.format.capitalize() + 'Serializer', Serializer)
- for url in urls:
- results = parse(url, etag=options.etag, modified=options.modified, agent=options.agent, referrer=options.referrer)
- serializer(results).write(sys.stdout)
diff --git a/module/lib/jinja2/__init__.py b/module/lib/jinja2/__init__.py
deleted file mode 100644
index f944e11b6..000000000
--- a/module/lib/jinja2/__init__.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2
- ~~~~~~
-
- Jinja2 is a template engine written in pure Python. It provides a
- Django inspired non-XML syntax but supports inline expressions and
- an optional sandboxed environment.
-
- Nutshell
- --------
-
- Here a small example of a Jinja2 template::
-
- {% extends 'base.html' %}
- {% block title %}Memberlist{% endblock %}
- {% block content %}
- <ul>
- {% for user in users %}
- <li><a href="{{ user.url }}">{{ user.username }}</a></li>
- {% endfor %}
- </ul>
- {% endblock %}
-
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-__docformat__ = 'restructuredtext en'
-try:
- __version__ = __import__('pkg_resources') \
- .get_distribution('Jinja2').version
-except:
- __version__ = 'unknown'
-
-# high level interface
-from jinja2.environment import Environment, Template
-
-# loaders
-from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \
- DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \
- ModuleLoader
-
-# bytecode caches
-from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \
- MemcachedBytecodeCache
-
-# undefined types
-from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined
-
-# exceptions
-from jinja2.exceptions import TemplateError, UndefinedError, \
- TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \
- TemplateAssertionError
-
-# decorators and public utilities
-from jinja2.filters import environmentfilter, contextfilter, \
- evalcontextfilter
-from jinja2.utils import Markup, escape, clear_caches, \
- environmentfunction, evalcontextfunction, contextfunction, \
- is_undefined
-
-__all__ = [
- 'Environment', 'Template', 'BaseLoader', 'FileSystemLoader',
- 'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader',
- 'ChoiceLoader', 'BytecodeCache', 'FileSystemBytecodeCache',
- 'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined',
- 'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound',
- 'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError',
- 'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape',
- 'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined',
- 'evalcontextfilter', 'evalcontextfunction'
-]
diff --git a/module/lib/jinja2/_markupsafe/__init__.py b/module/lib/jinja2/_markupsafe/__init__.py
deleted file mode 100644
index ec7bd572d..000000000
--- a/module/lib/jinja2/_markupsafe/__init__.py
+++ /dev/null
@@ -1,225 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- markupsafe
- ~~~~~~~~~~
-
- Implements a Markup string.
-
- :copyright: (c) 2010 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-"""
-import re
-from itertools import imap
-
-
-__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
-
-
-_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
-_entity_re = re.compile(r'&([^;]+);')
-
-
-class Markup(unicode):
- r"""Marks a string as being safe for inclusion in HTML/XML output without
- needing to be escaped. This implements the `__html__` interface a couple
- of frameworks and web applications use. :class:`Markup` is a direct
- subclass of `unicode` and provides all the methods of `unicode` just that
- it escapes arguments passed and always returns `Markup`.
-
- The `escape` function returns markup objects so that double escaping can't
- happen.
-
- The constructor of the :class:`Markup` class can be used for three
- different things: When passed an unicode object it's assumed to be safe,
- when passed an object with an HTML representation (has an `__html__`
- method) that representation is used, otherwise the object passed is
- converted into a unicode string and then assumed to be safe:
-
- >>> Markup("Hello <em>World</em>!")
- Markup(u'Hello <em>World</em>!')
- >>> class Foo(object):
- ... def __html__(self):
- ... return '<a href="#">foo</a>'
- ...
- >>> Markup(Foo())
- Markup(u'<a href="#">foo</a>')
-
- If you want object passed being always treated as unsafe you can use the
- :meth:`escape` classmethod to create a :class:`Markup` object:
-
- >>> Markup.escape("Hello <em>World</em>!")
- Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
-
- Operations on a markup string are markup aware which means that all
- arguments are passed through the :func:`escape` function:
-
- >>> em = Markup("<em>%s</em>")
- >>> em % "foo & bar"
- Markup(u'<em>foo &amp; bar</em>')
- >>> strong = Markup("<strong>%(text)s</strong>")
- >>> strong % {'text': '<blink>hacker here</blink>'}
- Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
- >>> Markup("<em>Hello</em> ") + "<foo>"
- Markup(u'<em>Hello</em> &lt;foo&gt;')
- """
- __slots__ = ()
-
- def __new__(cls, base=u'', encoding=None, errors='strict'):
- if hasattr(base, '__html__'):
- base = base.__html__()
- if encoding is None:
- return unicode.__new__(cls, base)
- return unicode.__new__(cls, base, encoding, errors)
-
- def __html__(self):
- return self
-
- def __add__(self, other):
- if hasattr(other, '__html__') or isinstance(other, basestring):
- return self.__class__(unicode(self) + unicode(escape(other)))
- return NotImplemented
-
- def __radd__(self, other):
- if hasattr(other, '__html__') or isinstance(other, basestring):
- return self.__class__(unicode(escape(other)) + unicode(self))
- return NotImplemented
-
- def __mul__(self, num):
- if isinstance(num, (int, long)):
- return self.__class__(unicode.__mul__(self, num))
- return NotImplemented
- __rmul__ = __mul__
-
- def __mod__(self, arg):
- if isinstance(arg, tuple):
- arg = tuple(imap(_MarkupEscapeHelper, arg))
- else:
- arg = _MarkupEscapeHelper(arg)
- return self.__class__(unicode.__mod__(self, arg))
-
- def __repr__(self):
- return '%s(%s)' % (
- self.__class__.__name__,
- unicode.__repr__(self)
- )
-
- def join(self, seq):
- return self.__class__(unicode.join(self, imap(escape, seq)))
- join.__doc__ = unicode.join.__doc__
-
- def split(self, *args, **kwargs):
- return map(self.__class__, unicode.split(self, *args, **kwargs))
- split.__doc__ = unicode.split.__doc__
-
- def rsplit(self, *args, **kwargs):
- return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
- rsplit.__doc__ = unicode.rsplit.__doc__
-
- def splitlines(self, *args, **kwargs):
- return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
- splitlines.__doc__ = unicode.splitlines.__doc__
-
- def unescape(self):
- r"""Unescape markup again into an unicode string. This also resolves
- known HTML4 and XHTML entities:
-
- >>> Markup("Main &raquo; <em>About</em>").unescape()
- u'Main \xbb <em>About</em>'
- """
- from jinja2._markupsafe._constants import HTML_ENTITIES
- def handle_match(m):
- name = m.group(1)
- if name in HTML_ENTITIES:
- return unichr(HTML_ENTITIES[name])
- try:
- if name[:2] in ('#x', '#X'):
- return unichr(int(name[2:], 16))
- elif name.startswith('#'):
- return unichr(int(name[1:]))
- except ValueError:
- pass
- return u''
- return _entity_re.sub(handle_match, unicode(self))
-
- def striptags(self):
- r"""Unescape markup into an unicode string and strip all tags. This
- also resolves known HTML4 and XHTML entities. Whitespace is
- normalized to one:
-
- >>> Markup("Main &raquo; <em>About</em>").striptags()
- u'Main \xbb About'
- """
- stripped = u' '.join(_striptags_re.sub('', self).split())
- return Markup(stripped).unescape()
-
- @classmethod
- def escape(cls, s):
- """Escape the string. Works like :func:`escape` with the difference
- that for subclasses of :class:`Markup` this function would return the
- correct subclass.
- """
- rv = escape(s)
- if rv.__class__ is not cls:
- return cls(rv)
- return rv
-
- def make_wrapper(name):
- orig = getattr(unicode, name)
- def func(self, *args, **kwargs):
- args = _escape_argspec(list(args), enumerate(args))
- _escape_argspec(kwargs, kwargs.iteritems())
- return self.__class__(orig(self, *args, **kwargs))
- func.__name__ = orig.__name__
- func.__doc__ = orig.__doc__
- return func
-
- for method in '__getitem__', 'capitalize', \
- 'title', 'lower', 'upper', 'replace', 'ljust', \
- 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
- 'translate', 'expandtabs', 'swapcase', 'zfill':
- locals()[method] = make_wrapper(method)
-
- # new in python 2.5
- if hasattr(unicode, 'partition'):
- partition = make_wrapper('partition'),
- rpartition = make_wrapper('rpartition')
-
- # new in python 2.6
- if hasattr(unicode, 'format'):
- format = make_wrapper('format')
-
- # not in python 3
- if hasattr(unicode, '__getslice__'):
- __getslice__ = make_wrapper('__getslice__')
-
- del method, make_wrapper
-
-
-def _escape_argspec(obj, iterable):
- """Helper for various string-wrapped functions."""
- for key, value in iterable:
- if hasattr(value, '__html__') or isinstance(value, basestring):
- obj[key] = escape(value)
- return obj
-
-
-class _MarkupEscapeHelper(object):
- """Helper for Markup.__mod__"""
-
- def __init__(self, obj):
- self.obj = obj
-
- __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
- __str__ = lambda s: str(escape(s.obj))
- __unicode__ = lambda s: unicode(escape(s.obj))
- __repr__ = lambda s: str(escape(repr(s.obj)))
- __int__ = lambda s: int(s.obj)
- __float__ = lambda s: float(s.obj)
-
-
-# we have to import it down here as the speedups and native
-# modules imports the markup type which is define above.
-try:
- from jinja2._markupsafe._speedups import escape, escape_silent, soft_unicode
-except ImportError:
- from jinja2._markupsafe._native import escape, escape_silent, soft_unicode
diff --git a/module/lib/jinja2/_markupsafe/_bundle.py b/module/lib/jinja2/_markupsafe/_bundle.py
deleted file mode 100644
index e694faf23..000000000
--- a/module/lib/jinja2/_markupsafe/_bundle.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2._markupsafe._bundle
- ~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- This script pulls in markupsafe from a source folder and
- bundles it with Jinja2. It does not pull in the speedups
- module though.
-
- :copyright: Copyright 2010 by the Jinja team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-import sys
-import os
-import re
-
-
-def rewrite_imports(lines):
- for idx, line in enumerate(lines):
- new_line = re.sub(r'(import|from)\s+markupsafe\b',
- r'\1 jinja2._markupsafe', line)
- if new_line != line:
- lines[idx] = new_line
-
-
-def main():
- if len(sys.argv) != 2:
- print 'error: only argument is path to markupsafe'
- sys.exit(1)
- basedir = os.path.dirname(__file__)
- markupdir = sys.argv[1]
- for filename in os.listdir(markupdir):
- if filename.endswith('.py'):
- f = open(os.path.join(markupdir, filename))
- try:
- lines = list(f)
- finally:
- f.close()
- rewrite_imports(lines)
- f = open(os.path.join(basedir, filename), 'w')
- try:
- for line in lines:
- f.write(line)
- finally:
- f.close()
-
-
-if __name__ == '__main__':
- main()
diff --git a/module/lib/jinja2/_markupsafe/_constants.py b/module/lib/jinja2/_markupsafe/_constants.py
deleted file mode 100644
index 919bf03c5..000000000
--- a/module/lib/jinja2/_markupsafe/_constants.py
+++ /dev/null
@@ -1,267 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- markupsafe._constants
- ~~~~~~~~~~~~~~~~~~~~~
-
- Highlevel implementation of the Markup string.
-
- :copyright: (c) 2010 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-"""
-
-
-HTML_ENTITIES = {
- 'AElig': 198,
- 'Aacute': 193,
- 'Acirc': 194,
- 'Agrave': 192,
- 'Alpha': 913,
- 'Aring': 197,
- 'Atilde': 195,
- 'Auml': 196,
- 'Beta': 914,
- 'Ccedil': 199,
- 'Chi': 935,
- 'Dagger': 8225,
- 'Delta': 916,
- 'ETH': 208,
- 'Eacute': 201,
- 'Ecirc': 202,
- 'Egrave': 200,
- 'Epsilon': 917,
- 'Eta': 919,
- 'Euml': 203,
- 'Gamma': 915,
- 'Iacute': 205,
- 'Icirc': 206,
- 'Igrave': 204,
- 'Iota': 921,
- 'Iuml': 207,
- 'Kappa': 922,
- 'Lambda': 923,
- 'Mu': 924,
- 'Ntilde': 209,
- 'Nu': 925,
- 'OElig': 338,
- 'Oacute': 211,
- 'Ocirc': 212,
- 'Ograve': 210,
- 'Omega': 937,
- 'Omicron': 927,
- 'Oslash': 216,
- 'Otilde': 213,
- 'Ouml': 214,
- 'Phi': 934,
- 'Pi': 928,
- 'Prime': 8243,
- 'Psi': 936,
- 'Rho': 929,
- 'Scaron': 352,
- 'Sigma': 931,
- 'THORN': 222,
- 'Tau': 932,
- 'Theta': 920,
- 'Uacute': 218,
- 'Ucirc': 219,
- 'Ugrave': 217,
- 'Upsilon': 933,
- 'Uuml': 220,
- 'Xi': 926,
- 'Yacute': 221,
- 'Yuml': 376,
- 'Zeta': 918,
- 'aacute': 225,
- 'acirc': 226,
- 'acute': 180,
- 'aelig': 230,
- 'agrave': 224,
- 'alefsym': 8501,
- 'alpha': 945,
- 'amp': 38,
- 'and': 8743,
- 'ang': 8736,
- 'apos': 39,
- 'aring': 229,
- 'asymp': 8776,
- 'atilde': 227,
- 'auml': 228,
- 'bdquo': 8222,
- 'beta': 946,
- 'brvbar': 166,
- 'bull': 8226,
- 'cap': 8745,
- 'ccedil': 231,
- 'cedil': 184,
- 'cent': 162,
- 'chi': 967,
- 'circ': 710,
- 'clubs': 9827,
- 'cong': 8773,
- 'copy': 169,
- 'crarr': 8629,
- 'cup': 8746,
- 'curren': 164,
- 'dArr': 8659,
- 'dagger': 8224,
- 'darr': 8595,
- 'deg': 176,
- 'delta': 948,
- 'diams': 9830,
- 'divide': 247,
- 'eacute': 233,
- 'ecirc': 234,
- 'egrave': 232,
- 'empty': 8709,
- 'emsp': 8195,
- 'ensp': 8194,
- 'epsilon': 949,
- 'equiv': 8801,
- 'eta': 951,
- 'eth': 240,
- 'euml': 235,
- 'euro': 8364,
- 'exist': 8707,
- 'fnof': 402,
- 'forall': 8704,
- 'frac12': 189,
- 'frac14': 188,
- 'frac34': 190,
- 'frasl': 8260,
- 'gamma': 947,
- 'ge': 8805,
- 'gt': 62,
- 'hArr': 8660,
- 'harr': 8596,
- 'hearts': 9829,
- 'hellip': 8230,
- 'iacute': 237,
- 'icirc': 238,
- 'iexcl': 161,
- 'igrave': 236,
- 'image': 8465,
- 'infin': 8734,
- 'int': 8747,
- 'iota': 953,
- 'iquest': 191,
- 'isin': 8712,
- 'iuml': 239,
- 'kappa': 954,
- 'lArr': 8656,
- 'lambda': 955,
- 'lang': 9001,
- 'laquo': 171,
- 'larr': 8592,
- 'lceil': 8968,
- 'ldquo': 8220,
- 'le': 8804,
- 'lfloor': 8970,
- 'lowast': 8727,
- 'loz': 9674,
- 'lrm': 8206,
- 'lsaquo': 8249,
- 'lsquo': 8216,
- 'lt': 60,
- 'macr': 175,
- 'mdash': 8212,
- 'micro': 181,
- 'middot': 183,
- 'minus': 8722,
- 'mu': 956,
- 'nabla': 8711,
- 'nbsp': 160,
- 'ndash': 8211,
- 'ne': 8800,
- 'ni': 8715,
- 'not': 172,
- 'notin': 8713,
- 'nsub': 8836,
- 'ntilde': 241,
- 'nu': 957,
- 'oacute': 243,
- 'ocirc': 244,
- 'oelig': 339,
- 'ograve': 242,
- 'oline': 8254,
- 'omega': 969,
- 'omicron': 959,
- 'oplus': 8853,
- 'or': 8744,
- 'ordf': 170,
- 'ordm': 186,
- 'oslash': 248,
- 'otilde': 245,
- 'otimes': 8855,
- 'ouml': 246,
- 'para': 182,
- 'part': 8706,
- 'permil': 8240,
- 'perp': 8869,
- 'phi': 966,
- 'pi': 960,
- 'piv': 982,
- 'plusmn': 177,
- 'pound': 163,
- 'prime': 8242,
- 'prod': 8719,
- 'prop': 8733,
- 'psi': 968,
- 'quot': 34,
- 'rArr': 8658,
- 'radic': 8730,
- 'rang': 9002,
- 'raquo': 187,
- 'rarr': 8594,
- 'rceil': 8969,
- 'rdquo': 8221,
- 'real': 8476,
- 'reg': 174,
- 'rfloor': 8971,
- 'rho': 961,
- 'rlm': 8207,
- 'rsaquo': 8250,
- 'rsquo': 8217,
- 'sbquo': 8218,
- 'scaron': 353,
- 'sdot': 8901,
- 'sect': 167,
- 'shy': 173,
- 'sigma': 963,
- 'sigmaf': 962,
- 'sim': 8764,
- 'spades': 9824,
- 'sub': 8834,
- 'sube': 8838,
- 'sum': 8721,
- 'sup': 8835,
- 'sup1': 185,
- 'sup2': 178,
- 'sup3': 179,
- 'supe': 8839,
- 'szlig': 223,
- 'tau': 964,
- 'there4': 8756,
- 'theta': 952,
- 'thetasym': 977,
- 'thinsp': 8201,
- 'thorn': 254,
- 'tilde': 732,
- 'times': 215,
- 'trade': 8482,
- 'uArr': 8657,
- 'uacute': 250,
- 'uarr': 8593,
- 'ucirc': 251,
- 'ugrave': 249,
- 'uml': 168,
- 'upsih': 978,
- 'upsilon': 965,
- 'uuml': 252,
- 'weierp': 8472,
- 'xi': 958,
- 'yacute': 253,
- 'yen': 165,
- 'yuml': 255,
- 'zeta': 950,
- 'zwj': 8205,
- 'zwnj': 8204
-}
diff --git a/module/lib/jinja2/_markupsafe/_native.py b/module/lib/jinja2/_markupsafe/_native.py
deleted file mode 100644
index 7b95828ec..000000000
--- a/module/lib/jinja2/_markupsafe/_native.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- markupsafe._native
- ~~~~~~~~~~~~~~~~~~
-
- Native Python implementation the C module is not compiled.
-
- :copyright: (c) 2010 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-"""
-from jinja2._markupsafe import Markup
-
-
-def escape(s):
- """Convert the characters &, <, >, ' and " in string s to HTML-safe
- sequences. Use this if you need to display text that might contain
- such characters in HTML. Marks return value as markup string.
- """
- if hasattr(s, '__html__'):
- return s.__html__()
- return Markup(unicode(s)
- .replace('&', '&amp;')
- .replace('>', '&gt;')
- .replace('<', '&lt;')
- .replace("'", '&#39;')
- .replace('"', '&#34;')
- )
-
-
-def escape_silent(s):
- """Like :func:`escape` but converts `None` into an empty
- markup string.
- """
- if s is None:
- return Markup()
- return escape(s)
-
-
-def soft_unicode(s):
- """Make a string unicode if it isn't already. That way a markup
- string is not converted back to unicode.
- """
- if not isinstance(s, unicode):
- s = unicode(s)
- return s
diff --git a/module/lib/jinja2/_markupsafe/tests.py b/module/lib/jinja2/_markupsafe/tests.py
deleted file mode 100644
index c1ce3943a..000000000
--- a/module/lib/jinja2/_markupsafe/tests.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import gc
-import unittest
-from jinja2._markupsafe import Markup, escape, escape_silent
-
-
-class MarkupTestCase(unittest.TestCase):
-
- def test_markup_operations(self):
- # adding two strings should escape the unsafe one
- unsafe = '<script type="application/x-some-script">alert("foo");</script>'
- safe = Markup('<em>username</em>')
- assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe)
-
- # string interpolations are safe to use too
- assert Markup('<em>%s</em>') % '<bad user>' == \
- '<em>&lt;bad user&gt;</em>'
- assert Markup('<em>%(username)s</em>') % {
- 'username': '<bad user>'
- } == '<em>&lt;bad user&gt;</em>'
-
- # an escaped object is markup too
- assert type(Markup('foo') + 'bar') is Markup
-
- # and it implements __html__ by returning itself
- x = Markup("foo")
- assert x.__html__() is x
-
- # it also knows how to treat __html__ objects
- class Foo(object):
- def __html__(self):
- return '<em>awesome</em>'
- def __unicode__(self):
- return 'awesome'
- assert Markup(Foo()) == '<em>awesome</em>'
- assert Markup('<strong>%s</strong>') % Foo() == \
- '<strong><em>awesome</em></strong>'
-
- # escaping and unescaping
- assert escape('"<>&\'') == '&#34;&lt;&gt;&amp;&#39;'
- assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
- assert Markup("&lt;test&gt;").unescape() == "<test>"
-
- def test_all_set(self):
- import jinja2._markupsafe as markup
- for item in markup.__all__:
- getattr(markup, item)
-
- def test_escape_silent(self):
- assert escape_silent(None) == Markup()
- assert escape(None) == Markup(None)
- assert escape_silent('<foo>') == Markup(u'&lt;foo&gt;')
-
-
-class MarkupLeakTestCase(unittest.TestCase):
-
- def test_markup_leaks(self):
- counts = set()
- for count in xrange(20):
- for item in xrange(1000):
- escape("foo")
- escape("<foo>")
- escape(u"foo")
- escape(u"<foo>")
- counts.add(len(gc.get_objects()))
- assert len(counts) == 1, 'ouch, c extension seems to leak objects'
-
-
-def suite():
- suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(MarkupTestCase))
-
- # this test only tests the c extension
- if not hasattr(escape, 'func_code'):
- suite.addTest(unittest.makeSuite(MarkupLeakTestCase))
-
- return suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/module/lib/jinja2/_stringdefs.py b/module/lib/jinja2/_stringdefs.py
deleted file mode 100644
index 1161b7f4a..000000000
--- a/module/lib/jinja2/_stringdefs.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2._stringdefs
- ~~~~~~~~~~~~~~~~~~
-
- Strings of all Unicode characters of a certain category.
- Used for matching in Unicode-aware languages. Run to regenerate.
-
- Inspired by chartypes_create.py from the MoinMoin project, original
- implementation from Pygments.
-
- :copyright: Copyright 2006-2009 by the Jinja team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-Cc = u'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f'
-
-Cf = u'\xad\u0600\u0601\u0602\u0603\u06dd\u070f\u17b4\u17b5\u200b\u200c\u200d\u200e\u200f\u202a\u202b\u202c\u202d\u202e\u2060\u2061\u2062\u2063\u206a\u206b\u206c\u206d\u206e\u206f\ufeff\ufff9\ufffa\ufffb'
-
-Cn = u'\u0242\u0243\u0244\u0245\u0246\u0247\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u0370\u0371\u0372\u0373\u0376\u0377\u0378\u0379\u037b\u037c\u037d\u037f\u0380\u0381\u0382\u0383\u038b\u038d\u03a2\u03cf\u0487\u04cf\u04fa\u04fb\u04fc\u04fd\u04fe\u04ff\u0510\u0511\u0512\u0513\u0514\u0515\u0516\u0517\u0518\u0519\u051a\u051b\u051c\u051d\u051e\u051f\u0520\u0521\u0522\u0523\u0524\u0525\u0526\u0527\u0528\u0529\u052a\u052b\u052c\u052d\u052e\u052f\u0530\u0557\u0558\u0560\u0588\u058b\u058c\u058d\u058e\u058f\u0590\u05ba\u05c8\u05c9\u05ca\u05cb\u05cc\u05cd\u05ce\u05cf\u05eb\u05ec\u05ed\u05ee\u05ef\u05f5\u05f6\u05f7\u05f8\u05f9\u05fa\u05fb\u05fc\u05fd\u05fe\u05ff\u0604\u0605\u0606\u0607\u0608\u0609\u060a\u0616\u0617\u0618\u0619\u061a\u061c\u061d\u0620\u063b\u063c\u063d\u063e\u063f\u065f\u070e\u074b\u074c\u076e\u076f\u0770\u0771\u0772\u0773\u0774\u0775\u0776\u0777\u0778\u0779\u077a\u077b\u077c\u077d\u077e\u077f\u07b2\u07b3\u07b4\u07b5\u07b6\u07b7\u07b8\u07b9\u07ba\u07bb\u07bc\u07bd\u07be\u07bf\u07c0\u07c1\u07c2\u07c3\u07c4\u07c5\u07c6\u07c7\u07c8\u07c9\u07ca\u07cb\u07cc\u07cd\u07ce\u07cf\u07d0\u07d1\u07d2\u07d3\u07d4\u07d5\u07d6\u07d7\u07d8\u07d9\u07da\u07db\u07dc\u07dd\u07de\u07df\u07e0\u07e1\u07e2\u07e3\u07e4\u07e5\u07e6\u07e7\u07e8\u07e9\u07ea\u07eb\u07ec\u07ed\u07ee\u07ef\u07f0\u07f1\u07f2\u07f3\u07f4\u07f5\u07f6\u07f7\u07f8\u07f9\u07fa\u07fb\u07fc\u07fd\u07fe\u07ff\u0800\u0801\u0802\u0803\u0804\u0805\u0806\u0807\u0808\u0809\u080a\u080b\u080c\u080d\u080e\u080f\u0810\u0811\u0812\u0813\u0814\u0815\u0816\u0817\u0818\u0819\u081a\u081b\u081c\u081d\u081e\u081f\u0820\u0821\u0822\u0823\u0824\u0825\u0826\u0827\u0828\u0829\u082a\u082b\u082c\u082d\u082e\u082f\u0830\u0831\u0832\u0833\u0834\u0835\u0836\u0837\u0838\u0839\u083a\u083b\u083c\u083d\u083e\u083f\u0840\u0841\u0842\u0843\u0844\u0845\u0846\u0847\u0848\u0849\u084a\u084b\u084c\u084d\u084e\u084f\u0850\u0851\u0852\u0853\u0854\u0855\u0856\u0857\u0858\u0859\u085a\u085b\u085c\u085d\u085e\u085f\u0860\u0861\u0862\u0863\u0864\u0865\u0866\u0867\u0868\u0869\u086a\u086b\u086c\u086d\u086e\u086f\u0870\u0871\u0872\u0873\u0874\u0875\u0876\u0877\u0878\u0879\u087a\u087b\u087c\u087d\u087e\u087f\u0880\u0881\u0882\u0883\u0884\u0885\u0886\u0887\u0888\u0889\u088a\u088b\u088c\u088d\u088e\u088f\u0890\u0891\u0892\u0893\u0894\u0895\u0896\u0897\u0898\u0899\u089a\u089b\u089c\u089d\u089e\u089f\u08a0\u08a1\u08a2\u08a3\u08a4\u08a5\u08a6\u08a7\u08a8\u08a9\u08aa\u08ab\u08ac\u08ad\u08ae\u08af\u08b0\u08b1\u08b2\u08b3\u08b4\u08b5\u08b6\u08b7\u08b8\u08b9\u08ba\u08bb\u08bc\u08bd\u08be\u08bf\u08c0\u08c1\u08c2\u08c3\u08c4\u08c5\u08c6\u08c7\u08c8\u08c9\u08ca\u08cb\u08cc\u08cd\u08ce\u08cf\u08d0\u08d1\u08d2\u08d3\u08d4\u08d5\u08d6\u08d7\u08d8\u08d9\u08da\u08db\u08dc\u08dd\u08de\u08df\u08e0\u08e1\u08e2\u08e3\u08e4\u08e5\u08e6\u08e7\u08e8\u08e9\u08ea\u08eb\u08ec\u08ed\u08ee\u08ef\u08f0\u08f1\u08f2\u08f3\u08f4\u08f5\u08f6\u08f7\u08f8\u08f9\u08fa\u08fb\u08fc\u08fd\u08fe\u08ff\u0900\u093a\u093b\u094e\u094f\u0955\u0956\u0957\u0971\u0972\u0973\u0974\u0975\u0976\u0977\u0978\u0979\u097a\u097b\u097c\u097e\u097f\u0980\u0984\u098d\u098e\u0991\u0992\u09a9\u09b1\u09b3\u09b4\u09b5\u09ba\u09bb\u09c5\u09c6\u09c9\u09ca\u09cf\u09d0\u09d1\u09d2\u09d3\u09d4\u09d5\u09d6\u09d8\u09d9\u09da\u09db\u09de\u09e4\u09e5\u09fb\u09fc\u09fd\u09fe\u09ff\u0a00\u0a04\u0a0b\u0a0c\u0a0d\u0a0e\u0a11\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a\u0a3b\u0a3d\u0a43\u0a44\u0a45\u0a46\u0a49\u0a4a\u0a4e\u0a4f\u0a50\u0a51\u0a52\u0a53\u0a54\u0a55\u0a56\u0a57\u0a58\u0a5d\u0a5f\u0a60\u0a61\u0a62\u0a63\u0a64\u0a65\u0a75\u0a76\u0a77\u0a78\u0a79\u0a7a\u0a7b\u0a7c\u0a7d\u0a7e\u0a7f\u0a80\u0a84\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba\u0abb\u0ac6\u0aca\u0ace\u0acf\u0ad1\u0ad2\u0ad3\u0ad4\u0ad5\u0ad6\u0ad7\u0ad8\u0ad9\u0ada\u0adb\u0adc\u0add\u0ade\u0adf\u0ae4\u0ae5\u0af0\u0af2\u0af3\u0af4\u0af5\u0af6\u0af7\u0af8\u0af9\u0afa\u0afb\u0afc\u0afd\u0afe\u0aff\u0b00\u0b04\u0b0d\u0b0e\u0b11\u0b12\u0b29\u0b31\u0b34\u0b3a\u0b3b\u0b44\u0b45\u0b46\u0b49\u0b4a\u0b4e\u0b4f\u0b50\u0b51\u0b52\u0b53\u0b54\u0b55\u0b58\u0b59\u0b5a\u0b5b\u0b5e\u0b62\u0b63\u0b64\u0b65\u0b72\u0b73\u0b74\u0b75\u0b76\u0b77\u0b78\u0b79\u0b7a\u0b7b\u0b7c\u0b7d\u0b7e\u0b7f\u0b80\u0b81\u0b84\u0b8b\u0b8c\u0b8d\u0b91\u0b96\u0b97\u0b98\u0b9b\u0b9d\u0ba0\u0ba1\u0ba2\u0ba5\u0ba6\u0ba7\u0bab\u0bac\u0bad\u0bba\u0bbb\u0bbc\u0bbd\u0bc3\u0bc4\u0bc5\u0bc9\u0bce\u0bcf\u0bd0\u0bd1\u0bd2\u0bd3\u0bd4\u0bd5\u0bd6\u0bd8\u0bd9\u0bda\u0bdb\u0bdc\u0bdd\u0bde\u0bdf\u0be0\u0be1\u0be2\u0be3\u0be4\u0be5\u0bfb\u0bfc\u0bfd\u0bfe\u0bff\u0c00\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a\u0c3b\u0c3c\u0c3d\u0c45\u0c49\u0c4e\u0c4f\u0c50\u0c51\u0c52\u0c53\u0c54\u0c57\u0c58\u0c59\u0c5a\u0c5b\u0c5c\u0c5d\u0c5e\u0c5f\u0c62\u0c63\u0c64\u0c65\u0c70\u0c71\u0c72\u0c73\u0c74\u0c75\u0c76\u0c77\u0c78\u0c79\u0c7a\u0c7b\u0c7c\u0c7d\u0c7e\u0c7f\u0c80\u0c81\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba\u0cbb\u0cc5\u0cc9\u0cce\u0ccf\u0cd0\u0cd1\u0cd2\u0cd3\u0cd4\u0cd7\u0cd8\u0cd9\u0cda\u0cdb\u0cdc\u0cdd\u0cdf\u0ce2\u0ce3\u0ce4\u0ce5\u0cf0\u0cf1\u0cf2\u0cf3\u0cf4\u0cf5\u0cf6\u0cf7\u0cf8\u0cf9\u0cfa\u0cfb\u0cfc\u0cfd\u0cfe\u0cff\u0d00\u0d01\u0d04\u0d0d\u0d11\u0d29\u0d3a\u0d3b\u0d3c\u0d3d\u0d44\u0d45\u0d49\u0d4e\u0d4f\u0d50\u0d51\u0d52\u0d53\u0d54\u0d55\u0d56\u0d58\u0d59\u0d5a\u0d5b\u0d5c\u0d5d\u0d5e\u0d5f\u0d62\u0d63\u0d64\u0d65\u0d70\u0d71\u0d72\u0d73\u0d74\u0d75\u0d76\u0d77\u0d78\u0d79\u0d7a\u0d7b\u0d7c\u0d7d\u0d7e\u0d7f\u0d80\u0d81\u0d84\u0d97\u0d98\u0d99\u0db2\u0dbc\u0dbe\u0dbf\u0dc7\u0dc8\u0dc9\u0dcb\u0dcc\u0dcd\u0dce\u0dd5\u0dd7\u0de0\u0de1\u0de2\u0de3\u0de4\u0de5\u0de6\u0de7\u0de8\u0de9\u0dea\u0deb\u0dec\u0ded\u0dee\u0def\u0df0\u0df1\u0df5\u0df6\u0df7\u0df8\u0df9\u0dfa\u0dfb\u0dfc\u0dfd\u0dfe\u0dff\u0e00\u0e3b\u0e3c\u0e3d\u0e3e\u0e5c\u0e5d\u0e5e\u0e5f\u0e60\u0e61\u0e62\u0e63\u0e64\u0e65\u0e66\u0e67\u0e68\u0e69\u0e6a\u0e6b\u0e6c\u0e6d\u0e6e\u0e6f\u0e70\u0e71\u0e72\u0e73\u0e74\u0e75\u0e76\u0e77\u0e78\u0e79\u0e7a\u0e7b\u0e7c\u0e7d\u0e7e\u0e7f\u0e80\u0e83\u0e85\u0e86\u0e89\u0e8b\u0e8c\u0e8e\u0e8f\u0e90\u0e91\u0e92\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8\u0ea9\u0eac\u0eba\u0ebe\u0ebf\u0ec5\u0ec7\u0ece\u0ecf\u0eda\u0edb\u0ede\u0edf\u0ee0\u0ee1\u0ee2\u0ee3\u0ee4\u0ee5\u0ee6\u0ee7\u0ee8\u0ee9\u0eea\u0eeb\u0eec\u0eed\u0eee\u0eef\u0ef0\u0ef1\u0ef2\u0ef3\u0ef4\u0ef5\u0ef6\u0ef7\u0ef8\u0ef9\u0efa\u0efb\u0efc\u0efd\u0efe\u0eff\u0f48\u0f6b\u0f6c\u0f6d\u0f6e\u0f6f\u0f70\u0f8c\u0f8d\u0f8e\u0f8f\u0f98\u0fbd\u0fcd\u0fce\u0fd2\u0fd3\u0fd4\u0fd5\u0fd6\u0fd7\u0fd8\u0fd9\u0fda\u0fdb\u0fdc\u0fdd\u0fde\u0fdf\u0fe0\u0fe1\u0fe2\u0fe3\u0fe4\u0fe5\u0fe6\u0fe7\u0fe8\u0fe9\u0fea\u0feb\u0fec\u0fed\u0fee\u0fef\u0ff0\u0ff1\u0ff2\u0ff3\u0ff4\u0ff5\u0ff6\u0ff7\u0ff8\u0ff9\u0ffa\u0ffb\u0ffc\u0ffd\u0ffe\u0fff\u1022\u1028\u102b\u1033\u1034\u1035\u103a\u103b\u103c\u103d\u103e\u103f\u105a\u105b\u105c\u105d\u105e\u105f\u1060\u1061\u1062\u1063\u1064\u1065\u1066\u1067\u1068\u1069\u106a\u106b\u106c\u106d\u106e\u106f\u1070\u1071\u1072\u1073\u1074\u1075\u1076\u1077\u1078\u1079\u107a\u107b\u107c\u107d\u107e\u107f\u1080\u1081\u1082\u1083\u1084\u1085\u1086\u1087\u1088\u1089\u108a\u108b\u108c\u108d\u108e\u108f\u1090\u1091\u1092\u1093\u1094\u1095\u1096\u1097\u1098\u1099\u109a\u109b\u109c\u109d\u109e\u109f\u10c6\u10c7\u10c8\u10c9\u10ca\u10cb\u10cc\u10cd\u10ce\u10cf\u10fd\u10fe\u10ff\u115a\u115b\u115c\u115d\u115e\u11a3\u11a4\u11a5\u11a6\u11a7\u11fa\u11fb\u11fc\u11fd\u11fe\u11ff\u1249\u124e\u124f\u1257\u1259\u125e\u125f\u1289\u128e\u128f\u12b1\u12b6\u12b7\u12bf\u12c1\u12c6\u12c7\u12d7\u1311\u1316\u1317\u135b\u135c\u135d\u135e\u137d\u137e\u137f\u139a\u139b\u139c\u139d\u139e\u139f\u13f5\u13f6\u13f7\u13f8\u13f9\u13fa\u13fb\u13fc\u13fd\u13fe\u13ff\u1400\u1677\u1678\u1679\u167a\u167b\u167c\u167d\u167e\u167f\u169d\u169e\u169f\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u16f9\u16fa\u16fb\u16fc\u16fd\u16fe\u16ff\u170d\u1715\u1716\u1717\u1718\u1719\u171a\u171b\u171c\u171d\u171e\u171f\u1737\u1738\u1739\u173a\u173b\u173c\u173d\u173e\u173f\u1754\u1755\u1756\u1757\u1758\u1759\u175a\u175b\u175c\u175d\u175e\u175f\u176d\u1771\u1774\u1775\u1776\u1777\u1778\u1779\u177a\u177b\u177c\u177d\u177e\u177f\u17de\u17df\u17ea\u17eb\u17ec\u17ed\u17ee\u17ef\u17fa\u17fb\u17fc\u17fd\u17fe\u17ff\u180f\u181a\u181b\u181c\u181d\u181e\u181f\u1878\u1879\u187a\u187b\u187c\u187d\u187e\u187f\u18aa\u18ab\u18ac\u18ad\u18ae\u18af\u18b0\u18b1\u18b2\u18b3\u18b4\u18b5\u18b6\u18b7\u18b8\u18b9\u18ba\u18bb\u18bc\u18bd\u18be\u18bf\u18c0\u18c1\u18c2\u18c3\u18c4\u18c5\u18c6\u18c7\u18c8\u18c9\u18ca\u18cb\u18cc\u18cd\u18ce\u18cf\u18d0\u18d1\u18d2\u18d3\u18d4\u18d5\u18d6\u18d7\u18d8\u18d9\u18da\u18db\u18dc\u18dd\u18de\u18df\u18e0\u18e1\u18e2\u18e3\u18e4\u18e5\u18e6\u18e7\u18e8\u18e9\u18ea\u18eb\u18ec\u18ed\u18ee\u18ef\u18f0\u18f1\u18f2\u18f3\u18f4\u18f5\u18f6\u18f7\u18f8\u18f9\u18fa\u18fb\u18fc\u18fd\u18fe\u18ff\u191d\u191e\u191f\u192c\u192d\u192e\u192f\u193c\u193d\u193e\u193f\u1941\u1942\u1943\u196e\u196f\u1975\u1976\u1977\u1978\u1979\u197a\u197b\u197c\u197d\u197e\u197f\u19aa\u19ab\u19ac\u19ad\u19ae\u19af\u19ca\u19cb\u19cc\u19cd\u19ce\u19cf\u19da\u19db\u19dc\u19dd\u1a1c\u1a1d\u1a20\u1a21\u1a22\u1a23\u1a24\u1a25\u1a26\u1a27\u1a28\u1a29\u1a2a\u1a2b\u1a2c\u1a2d\u1a2e\u1a2f\u1a30\u1a31\u1a32\u1a33\u1a34\u1a35\u1a36\u1a37\u1a38\u1a39\u1a3a\u1a3b\u1a3c\u1a3d\u1a3e\u1a3f\u1a40\u1a41\u1a42\u1a43\u1a44\u1a45\u1a46\u1a47\u1a48\u1a49\u1a4a\u1a4b\u1a4c\u1a4d\u1a4e\u1a4f\u1a50\u1a51\u1a52\u1a53\u1a54\u1a55\u1a56\u1a57\u1a58\u1a59\u1a5a\u1a5b\u1a5c\u1a5d\u1a5e\u1a5f\u1a60\u1a61\u1a62\u1a63\u1a64\u1a65\u1a66\u1a67\u1a68\u1a69\u1a6a\u1a6b\u1a6c\u1a6d\u1a6e\u1a6f\u1a70\u1a71\u1a72\u1a73\u1a74\u1a75\u1a76\u1a77\u1a78\u1a79\u1a7a\u1a7b\u1a7c\u1a7d\u1a7e\u1a7f\u1a80\u1a81\u1a82\u1a83\u1a84\u1a85\u1a86\u1a87\u1a88\u1a89\u1a8a\u1a8b\u1a8c\u1a8d\u1a8e\u1a8f\u1a90\u1a91\u1a92\u1a93\u1a94\u1a95\u1a96\u1a97\u1a98\u1a99\u1a9a\u1a9b\u1a9c\u1a9d\u1a9e\u1a9f\u1aa0\u1aa1\u1aa2\u1aa3\u1aa4\u1aa5\u1aa6\u1aa7\u1aa8\u1aa9\u1aaa\u1aab\u1aac\u1aad\u1aae\u1aaf\u1ab0\u1ab1\u1ab2\u1ab3\u1ab4\u1ab5\u1ab6\u1ab7\u1ab8\u1ab9\u1aba\u1abb\u1abc\u1abd\u1abe\u1abf\u1ac0\u1ac1\u1ac2\u1ac3\u1ac4\u1ac5\u1ac6\u1ac7\u1ac8\u1ac9\u1aca\u1acb\u1acc\u1acd\u1ace\u1acf\u1ad0\u1ad1\u1ad2\u1ad3\u1ad4\u1ad5\u1ad6\u1ad7\u1ad8\u1ad9\u1ada\u1adb\u1adc\u1add\u1ade\u1adf\u1ae0\u1ae1\u1ae2\u1ae3\u1ae4\u1ae5\u1ae6\u1ae7\u1ae8\u1ae9\u1aea\u1aeb\u1aec\u1aed\u1aee\u1aef\u1af0\u1af1\u1af2\u1af3\u1af4\u1af5\u1af6\u1af7\u1af8\u1af9\u1afa\u1afb\u1afc\u1afd\u1afe\u1aff\u1b00\u1b01\u1b02\u1b03\u1b04\u1b05\u1b06\u1b07\u1b08\u1b09\u1b0a\u1b0b\u1b0c\u1b0d\u1b0e\u1b0f\u1b10\u1b11\u1b12\u1b13\u1b14\u1b15\u1b16\u1b17\u1b18\u1b19\u1b1a\u1b1b\u1b1c\u1b1d\u1b1e\u1b1f\u1b20\u1b21\u1b22\u1b23\u1b24\u1b25\u1b26\u1b27\u1b28\u1b29\u1b2a\u1b2b\u1b2c\u1b2d\u1b2e\u1b2f\u1b30\u1b31\u1b32\u1b33\u1b34\u1b35\u1b36\u1b37\u1b38\u1b39\u1b3a\u1b3b\u1b3c\u1b3d\u1b3e\u1b3f\u1b40\u1b41\u1b42\u1b43\u1b44\u1b45\u1b46\u1b47\u1b48\u1b49\u1b4a\u1b4b\u1b4c\u1b4d\u1b4e\u1b4f\u1b50\u1b51\u1b52\u1b53\u1b54\u1b55\u1b56\u1b57\u1b58\u1b59\u1b5a\u1b5b\u1b5c\u1b5d\u1b5e\u1b5f\u1b60\u1b61\u1b62\u1b63\u1b64\u1b65\u1b66\u1b67\u1b68\u1b69\u1b6a\u1b6b\u1b6c\u1b6d\u1b6e\u1b6f\u1b70\u1b71\u1b72\u1b73\u1b74\u1b75\u1b76\u1b77\u1b78\u1b79\u1b7a\u1b7b\u1b7c\u1b7d\u1b7e\u1b7f\u1b80\u1b81\u1b82\u1b83\u1b84\u1b85\u1b86\u1b87\u1b88\u1b89\u1b8a\u1b8b\u1b8c\u1b8d\u1b8e\u1b8f\u1b90\u1b91\u1b92\u1b93\u1b94\u1b95\u1b96\u1b97\u1b98\u1b99\u1b9a\u1b9b\u1b9c\u1b9d\u1b9e\u1b9f\u1ba0\u1ba1\u1ba2\u1ba3\u1ba4\u1ba5\u1ba6\u1ba7\u1ba8\u1ba9\u1baa\u1bab\u1bac\u1bad\u1bae\u1baf\u1bb0\u1bb1\u1bb2\u1bb3\u1bb4\u1bb5\u1bb6\u1bb7\u1bb8\u1bb9\u1bba\u1bbb\u1bbc\u1bbd\u1bbe\u1bbf\u1bc0\u1bc1\u1bc2\u1bc3\u1bc4\u1bc5\u1bc6\u1bc7\u1bc8\u1bc9\u1bca\u1bcb\u1bcc\u1bcd\u1bce\u1bcf\u1bd0\u1bd1\u1bd2\u1bd3\u1bd4\u1bd5\u1bd6\u1bd7\u1bd8\u1bd9\u1bda\u1bdb\u1bdc\u1bdd\u1bde\u1bdf\u1be0\u1be1\u1be2\u1be3\u1be4\u1be5\u1be6\u1be7\u1be8\u1be9\u1bea\u1beb\u1bec\u1bed\u1bee\u1bef\u1bf0\u1bf1\u1bf2\u1bf3\u1bf4\u1bf5\u1bf6\u1bf7\u1bf8\u1bf9\u1bfa\u1bfb\u1bfc\u1bfd\u1bfe\u1bff\u1c00\u1c01\u1c02\u1c03\u1c04\u1c05\u1c06\u1c07\u1c08\u1c09\u1c0a\u1c0b\u1c0c\u1c0d\u1c0e\u1c0f\u1c10\u1c11\u1c12\u1c13\u1c14\u1c15\u1c16\u1c17\u1c18\u1c19\u1c1a\u1c1b\u1c1c\u1c1d\u1c1e\u1c1f\u1c20\u1c21\u1c22\u1c23\u1c24\u1c25\u1c26\u1c27\u1c28\u1c29\u1c2a\u1c2b\u1c2c\u1c2d\u1c2e\u1c2f\u1c30\u1c31\u1c32\u1c33\u1c34\u1c35\u1c36\u1c37\u1c38\u1c39\u1c3a\u1c3b\u1c3c\u1c3d\u1c3e\u1c3f\u1c40\u1c41\u1c42\u1c43\u1c44\u1c45\u1c46\u1c47\u1c48\u1c49\u1c4a\u1c4b\u1c4c\u1c4d\u1c4e\u1c4f\u1c50\u1c51\u1c52\u1c53\u1c54\u1c55\u1c56\u1c57\u1c58\u1c59\u1c5a\u1c5b\u1c5c\u1c5d\u1c5e\u1c5f\u1c60\u1c61\u1c62\u1c63\u1c64\u1c65\u1c66\u1c67\u1c68\u1c69\u1c6a\u1c6b\u1c6c\u1c6d\u1c6e\u1c6f\u1c70\u1c71\u1c72\u1c73\u1c74\u1c75\u1c76\u1c77\u1c78\u1c79\u1c7a\u1c7b\u1c7c\u1c7d\u1c7e\u1c7f\u1c80\u1c81\u1c82\u1c83\u1c84\u1c85\u1c86\u1c87\u1c88\u1c89\u1c8a\u1c8b\u1c8c\u1c8d\u1c8e\u1c8f\u1c90\u1c91\u1c92\u1c93\u1c94\u1c95\u1c96\u1c97\u1c98\u1c99\u1c9a\u1c9b\u1c9c\u1c9d\u1c9e\u1c9f\u1ca0\u1ca1\u1ca2\u1ca3\u1ca4\u1ca5\u1ca6\u1ca7\u1ca8\u1ca9\u1caa\u1cab\u1cac\u1cad\u1cae\u1caf\u1cb0\u1cb1\u1cb2\u1cb3\u1cb4\u1cb5\u1cb6\u1cb7\u1cb8\u1cb9\u1cba\u1cbb\u1cbc\u1cbd\u1cbe\u1cbf\u1cc0\u1cc1\u1cc2\u1cc3\u1cc4\u1cc5\u1cc6\u1cc7\u1cc8\u1cc9\u1cca\u1ccb\u1ccc\u1ccd\u1cce\u1ccf\u1cd0\u1cd1\u1cd2\u1cd3\u1cd4\u1cd5\u1cd6\u1cd7\u1cd8\u1cd9\u1cda\u1cdb\u1cdc\u1cdd\u1cde\u1cdf\u1ce0\u1ce1\u1ce2\u1ce3\u1ce4\u1ce5\u1ce6\u1ce7\u1ce8\u1ce9\u1cea\u1ceb\u1cec\u1ced\u1cee\u1cef\u1cf0\u1cf1\u1cf2\u1cf3\u1cf4\u1cf5\u1cf6\u1cf7\u1cf8\u1cf9\u1cfa\u1cfb\u1cfc\u1cfd\u1cfe\u1cff\u1dc4\u1dc5\u1dc6\u1dc7\u1dc8\u1dc9\u1dca\u1dcb\u1dcc\u1dcd\u1dce\u1dcf\u1dd0\u1dd1\u1dd2\u1dd3\u1dd4\u1dd5\u1dd6\u1dd7\u1dd8\u1dd9\u1dda\u1ddb\u1ddc\u1ddd\u1dde\u1ddf\u1de0\u1de1\u1de2\u1de3\u1de4\u1de5\u1de6\u1de7\u1de8\u1de9\u1dea\u1deb\u1dec\u1ded\u1dee\u1def\u1df0\u1df1\u1df2\u1df3\u1df4\u1df5\u1df6\u1df7\u1df8\u1df9\u1dfa\u1dfb\u1dfc\u1dfd\u1dfe\u1dff\u1e9c\u1e9d\u1e9e\u1e9f\u1efa\u1efb\u1efc\u1efd\u1efe\u1eff\u1f16\u1f17\u1f1e\u1f1f\u1f46\u1f47\u1f4e\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e\u1f7f\u1fb5\u1fc5\u1fd4\u1fd5\u1fdc\u1ff0\u1ff1\u1ff5\u1fff\u2064\u2065\u2066\u2067\u2068\u2069\u2072\u2073\u208f\u2095\u2096\u2097\u2098\u2099\u209a\u209b\u209c\u209d\u209e\u209f\u20b6\u20b7\u20b8\u20b9\u20ba\u20bb\u20bc\u20bd\u20be\u20bf\u20c0\u20c1\u20c2\u20c3\u20c4\u20c5\u20c6\u20c7\u20c8\u20c9\u20ca\u20cb\u20cc\u20cd\u20ce\u20cf\u20ec\u20ed\u20ee\u20ef\u20f0\u20f1\u20f2\u20f3\u20f4\u20f5\u20f6\u20f7\u20f8\u20f9\u20fa\u20fb\u20fc\u20fd\u20fe\u20ff\u214d\u214e\u214f\u2150\u2151\u2152\u2184\u2185\u2186\u2187\u2188\u2189\u218a\u218b\u218c\u218d\u218e\u218f\u23dc\u23dd\u23de\u23df\u23e0\u23e1\u23e2\u23e3\u23e4\u23e5\u23e6\u23e7\u23e8\u23e9\u23ea\u23eb\u23ec\u23ed\u23ee\u23ef\u23f0\u23f1\u23f2\u23f3\u23f4\u23f5\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u23fe\u23ff\u2427\u2428\u2429\u242a\u242b\u242c\u242d\u242e\u242f\u2430\u2431\u2432\u2433\u2434\u2435\u2436\u2437\u2438\u2439\u243a\u243b\u243c\u243d\u243e\u243f\u244b\u244c\u244d\u244e\u244f\u2450\u2451\u2452\u2453\u2454\u2455\u2456\u2457\u2458\u2459\u245a\u245b\u245c\u245d\u245e\u245f\u269d\u269e\u269f\u26b2\u26b3\u26b4\u26b5\u26b6\u26b7\u26b8\u26b9\u26ba\u26bb\u26bc\u26bd\u26be\u26bf\u26c0\u26c1\u26c2\u26c3\u26c4\u26c5\u26c6\u26c7\u26c8\u26c9\u26ca\u26cb\u26cc\u26cd\u26ce\u26cf\u26d0\u26d1\u26d2\u26d3\u26d4\u26d5\u26d6\u26d7\u26d8\u26d9\u26da\u26db\u26dc\u26dd\u26de\u26df\u26e0\u26e1\u26e2\u26e3\u26e4\u26e5\u26e6\u26e7\u26e8\u26e9\u26ea\u26eb\u26ec\u26ed\u26ee\u26ef\u26f0\u26f1\u26f2\u26f3\u26f4\u26f5\u26f6\u26f7\u26f8\u26f9\u26fa\u26fb\u26fc\u26fd\u26fe\u26ff\u2700\u2705\u270a\u270b\u2728\u274c\u274e\u2753\u2754\u2755\u2757\u275f\u2760\u2795\u2796\u2797\u27b0\u27bf\u27c7\u27c8\u27c9\u27ca\u27cb\u27cc\u27cd\u27ce\u27cf\u27ec\u27ed\u27ee\u27ef\u2b14\u2b15\u2b16\u2b17\u2b18\u2b19\u2b1a\u2b1b\u2b1c\u2b1d\u2b1e\u2b1f\u2b20\u2b21\u2b22\u2b23\u2b24\u2b25\u2b26\u2b27\u2b28\u2b29\u2b2a\u2b2b\u2b2c\u2b2d\u2b2e\u2b2f\u2b30\u2b31\u2b32\u2b33\u2b34\u2b35\u2b36\u2b37\u2b38\u2b39\u2b3a\u2b3b\u2b3c\u2b3d\u2b3e\u2b3f\u2b40\u2b41\u2b42\u2b43\u2b44\u2b45\u2b46\u2b47\u2b48\u2b49\u2b4a\u2b4b\u2b4c\u2b4d\u2b4e\u2b4f\u2b50\u2b51\u2b52\u2b53\u2b54\u2b55\u2b56\u2b57\u2b58\u2b59\u2b5a\u2b5b\u2b5c\u2b5d\u2b5e\u2b5f\u2b60\u2b61\u2b62\u2b63\u2b64\u2b65\u2b66\u2b67\u2b68\u2b69\u2b6a\u2b6b\u2b6c\u2b6d\u2b6e\u2b6f\u2b70\u2b71\u2b72\u2b73\u2b74\u2b75\u2b76\u2b77\u2b78\u2b79\u2b7a\u2b7b\u2b7c\u2b7d\u2b7e\u2b7f\u2b80\u2b81\u2b82\u2b83\u2b84\u2b85\u2b86\u2b87\u2b88\u2b89\u2b8a\u2b8b\u2b8c\u2b8d\u2b8e\u2b8f\u2b90\u2b91\u2b92\u2b93\u2b94\u2b95\u2b96\u2b97\u2b98\u2b99\u2b9a\u2b9b\u2b9c\u2b9d\u2b9e\u2b9f\u2ba0\u2ba1\u2ba2\u2ba3\u2ba4\u2ba5\u2ba6\u2ba7\u2ba8\u2ba9\u2baa\u2bab\u2bac\u2bad\u2bae\u2baf\u2bb0\u2bb1\u2bb2\u2bb3\u2bb4\u2bb5\u2bb6\u2bb7\u2bb8\u2bb9\u2bba\u2bbb\u2bbc\u2bbd\u2bbe\u2bbf\u2bc0\u2bc1\u2bc2\u2bc3\u2bc4\u2bc5\u2bc6\u2bc7\u2bc8\u2bc9\u2bca\u2bcb\u2bcc\u2bcd\u2bce\u2bcf\u2bd0\u2bd1\u2bd2\u2bd3\u2bd4\u2bd5\u2bd6\u2bd7\u2bd8\u2bd9\u2bda\u2bdb\u2bdc\u2bdd\u2bde\u2bdf\u2be0\u2be1\u2be2\u2be3\u2be4\u2be5\u2be6\u2be7\u2be8\u2be9\u2bea\u2beb\u2bec\u2bed\u2bee\u2bef\u2bf0\u2bf1\u2bf2\u2bf3\u2bf4\u2bf5\u2bf6\u2bf7\u2bf8\u2bf9\u2bfa\u2bfb\u2bfc\u2bfd\u2bfe\u2bff\u2c2f\u2c5f\u2c60\u2c61\u2c62\u2c63\u2c64\u2c65\u2c66\u2c67\u2c68\u2c69\u2c6a\u2c6b\u2c6c\u2c6d\u2c6e\u2c6f\u2c70\u2c71\u2c72\u2c73\u2c74\u2c75\u2c76\u2c77\u2c78\u2c79\u2c7a\u2c7b\u2c7c\u2c7d\u2c7e\u2c7f\u2ceb\u2cec\u2ced\u2cee\u2cef\u2cf0\u2cf1\u2cf2\u2cf3\u2cf4\u2cf5\u2cf6\u2cf7\u2cf8\u2d26\u2d27\u2d28\u2d29\u2d2a\u2d2b\u2d2c\u2d2d\u2d2e\u2d2f\u2d66\u2d67\u2d68\u2d69\u2d6a\u2d6b\u2d6c\u2d6d\u2d6e\u2d70\u2d71\u2d72\u2d73\u2d74\u2d75\u2d76\u2d77\u2d78\u2d79\u2d7a\u2d7b\u2d7c\u2d7d\u2d7e\u2d7f\u2d97\u2d98\u2d99\u2d9a\u2d9b\u2d9c\u2d9d\u2d9e\u2d9f\u2da7\u2daf\u2db7\u2dbf\u2dc7\u2dcf\u2dd7\u2ddf\u2de0\u2de1\u2de2\u2de3\u2de4\u2de5\u2de6\u2de7\u2de8\u2de9\u2dea\u2deb\u2dec\u2ded\u2dee\u2def\u2df0\u2df1\u2df2\u2df3\u2df4\u2df5\u2df6\u2df7\u2df8\u2df9\u2dfa\u2dfb\u2dfc\u2dfd\u2dfe\u2dff\u2e18\u2e19\u2e1a\u2e1b\u2e1e\u2e1f\u2e20\u2e21\u2e22\u2e23\u2e24\u2e25\u2e26\u2e27\u2e28\u2e29\u2e2a\u2e2b\u2e2c\u2e2d\u2e2e\u2e2f\u2e30\u2e31\u2e32\u2e33\u2e34\u2e35\u2e36\u2e37\u2e38\u2e39\u2e3a\u2e3b\u2e3c\u2e3d\u2e3e\u2e3f\u2e40\u2e41\u2e42\u2e43\u2e44\u2e45\u2e46\u2e47\u2e48\u2e49\u2e4a\u2e4b\u2e4c\u2e4d\u2e4e\u2e4f\u2e50\u2e51\u2e52\u2e53\u2e54\u2e55\u2e56\u2e57\u2e58\u2e59\u2e5a\u2e5b\u2e5c\u2e5d\u2e5e\u2e5f\u2e60\u2e61\u2e62\u2e63\u2e64\u2e65\u2e66\u2e67\u2e68\u2e69\u2e6a\u2e6b\u2e6c\u2e6d\u2e6e\u2e6f\u2e70\u2e71\u2e72\u2e73\u2e74\u2e75\u2e76\u2e77\u2e78\u2e79\u2e7a\u2e7b\u2e7c\u2e7d\u2e7e\u2e7f\u2e9a\u2ef4\u2ef5\u2ef6\u2ef7\u2ef8\u2ef9\u2efa\u2efb\u2efc\u2efd\u2efe\u2eff\u2fd6\u2fd7\u2fd8\u2fd9\u2fda\u2fdb\u2fdc\u2fdd\u2fde\u2fdf\u2fe0\u2fe1\u2fe2\u2fe3\u2fe4\u2fe5\u2fe6\u2fe7\u2fe8\u2fe9\u2fea\u2feb\u2fec\u2fed\u2fee\u2fef\u2ffc\u2ffd\u2ffe\u2fff\u3040\u3097\u3098\u3100\u3101\u3102\u3103\u3104\u312d\u312e\u312f\u3130\u318f\u31b8\u31b9\u31ba\u31bb\u31bc\u31bd\u31be\u31bf\u31d0\u31d1\u31d2\u31d3\u31d4\u31d5\u31d6\u31d7\u31d8\u31d9\u31da\u31db\u31dc\u31dd\u31de\u31df\u31e0\u31e1\u31e2\u31e3\u31e4\u31e5\u31e6\u31e7\u31e8\u31e9\u31ea\u31eb\u31ec\u31ed\u31ee\u31ef\u321f\u3244\u3245\u3246\u3247\u3248\u3249\u324a\u324b\u324c\u324d\u324e\u324f\u32ff\u4db6\u4db7\u4db8\u4db9\u4dba\u4dbb\u4dbc\u4dbd\u4dbe\u4dbf\u9fbc\u9fbd\u9fbe\u9fbf\u9fc0\u9fc1\u9fc2\u9fc3\u9fc4\u9fc5\u9fc6\u9fc7\u9fc8\u9fc9\u9fca\u9fcb\u9fcc\u9fcd\u9fce\u9fcf\u9fd0\u9fd1\u9fd2\u9fd3\u9fd4\u9fd5\u9fd6\u9fd7\u9fd8\u9fd9\u9fda\u9fdb\u9fdc\u9fdd\u9fde\u9fdf\u9fe0\u9fe1\u9fe2\u9fe3\u9fe4\u9fe5\u9fe6\u9fe7\u9fe8\u9fe9\u9fea\u9feb\u9fec\u9fed\u9fee\u9fef\u9ff0\u9ff1\u9ff2\u9ff3\u9ff4\u9ff5\u9ff6\u9ff7\u9ff8\u9ff9\u9ffa\u9ffb\u9ffc\u9ffd\u9ffe\u9fff\ua48d\ua48e\ua48f\ua4c7\ua4c8\ua4c9\ua4ca\ua4cb\ua4cc\ua4cd\ua4ce\ua4cf\ua4d0\ua4d1\ua4d2\ua4d3\ua4d4\ua4d5\ua4d6\ua4d7\ua4d8\ua4d9\ua4da\ua4db\ua4dc\ua4dd\ua4de\ua4df\ua4e0\ua4e1\ua4e2\ua4e3\ua4e4\ua4e5\ua4e6\ua4e7\ua4e8\ua4e9\ua4ea\ua4eb\ua4ec\ua4ed\ua4ee\ua4ef\ua4f0\ua4f1\ua4f2\ua4f3\ua4f4\ua4f5\ua4f6\ua4f7\ua4f8\ua4f9\ua4fa\ua4fb\ua4fc\ua4fd\ua4fe\ua4ff\ua500\ua501\ua502\ua503\ua504\ua505\ua506\ua507\ua508\ua509\ua50a\ua50b\ua50c\ua50d\ua50e\ua50f\ua510\ua511\ua512\ua513\ua514\ua515\ua516\ua517\ua518\ua519\ua51a\ua51b\ua51c\ua51d\ua51e\ua51f\ua520\ua521\ua522\ua523\ua524\ua525\ua526\ua527\ua528\ua529\ua52a\ua52b\ua52c\ua52d\ua52e\ua52f\ua530\ua531\ua532\ua533\ua534\ua535\ua536\ua537\ua538\ua539\ua53a\ua53b\ua53c\ua53d\ua53e\ua53f\ua540\ua541\ua542\ua543\ua544\ua545\ua546\ua547\ua548\ua549\ua54a\ua54b\ua54c\ua54d\ua54e\ua54f\ua550\ua551\ua552\ua553\ua554\ua555\ua556\ua557\ua558\ua559\ua55a\ua55b\ua55c\ua55d\ua55e\ua55f\ua560\ua561\ua562\ua563\ua564\ua565\ua566\ua567\ua568\ua569\ua56a\ua56b\ua56c\ua56d\ua56e\ua56f\ua570\ua571\ua572\ua573\ua574\ua575\ua576\ua577\ua578\ua579\ua57a\ua57b\ua57c\ua57d\ua57e\ua57f\ua580\ua581\ua582\ua583\ua584\ua585\ua586\ua587\ua588\ua589\ua58a\ua58b\ua58c\ua58d\ua58e\ua58f\ua590\ua591\ua592\ua593\ua594\ua595\ua596\ua597\ua598\ua599\ua59a\ua59b\ua59c\ua59d\ua59e\ua59f\ua5a0\ua5a1\ua5a2\ua5a3\ua5a4\ua5a5\ua5a6\ua5a7\ua5a8\ua5a9\ua5aa\ua5ab\ua5ac\ua5ad\ua5ae\ua5af\ua5b0\ua5b1\ua5b2\ua5b3\ua5b4\ua5b5\ua5b6\ua5b7\ua5b8\ua5b9\ua5ba\ua5bb\ua5bc\ua5bd\ua5be\ua5bf\ua5c0\ua5c1\ua5c2\ua5c3\ua5c4\ua5c5\ua5c6\ua5c7\ua5c8\ua5c9\ua5ca\ua5cb\ua5cc\ua5cd\ua5ce\ua5cf\ua5d0\ua5d1\ua5d2\ua5d3\ua5d4\ua5d5\ua5d6\ua5d7\ua5d8\ua5d9\ua5da\ua5db\ua5dc\ua5dd\ua5de\ua5df\ua5e0\ua5e1\ua5e2\ua5e3\ua5e4\ua5e5\ua5e6\ua5e7\ua5e8\ua5e9\ua5ea\ua5eb\ua5ec\ua5ed\ua5ee\ua5ef\ua5f0\ua5f1\ua5f2\ua5f3\ua5f4\ua5f5\ua5f6\ua5f7\ua5f8\ua5f9\ua5fa\ua5fb\ua5fc\ua5fd\ua5fe\ua5ff\ua600\ua601\ua602\ua603\ua604\ua605\ua606\ua607\ua608\ua609\ua60a\ua60b\ua60c\ua60d\ua60e\ua60f\ua610\ua611\ua612\ua613\ua614\ua615\ua616\ua617\ua618\ua619\ua61a\ua61b\ua61c\ua61d\ua61e\ua61f\ua620\ua621\ua622\ua623\ua624\ua625\ua626\ua627\ua628\ua629\ua62a\ua62b\ua62c\ua62d\ua62e\ua62f\ua630\ua631\ua632\ua633\ua634\ua635\ua636\ua637\ua638\ua639\ua63a\ua63b\ua63c\ua63d\ua63e\ua63f\ua640\ua641\ua642\ua643\ua644\ua645\ua646\ua647\ua648\ua649\ua64a\ua64b\ua64c\ua64d\ua64e\ua64f\ua650\ua651\ua652\ua653\ua654\ua655\ua656\ua657\ua658\ua659\ua65a\ua65b\ua65c\ua65d\ua65e\ua65f\ua660\ua661\ua662\ua663\ua664\ua665\ua666\ua667\ua668\ua669\ua66a\ua66b\ua66c\ua66d\ua66e\ua66f\ua670\ua671\ua672\ua673\ua674\ua675\ua676\ua677\ua678\ua679\ua67a\ua67b\ua67c\ua67d\ua67e\ua67f\ua680\ua681\ua682\ua683\ua684\ua685\ua686\ua687\ua688\ua689\ua68a\ua68b\ua68c\ua68d\ua68e\ua68f\ua690\ua691\ua692\ua693\ua694\ua695\ua696\ua697\ua698\ua699\ua69a\ua69b\ua69c\ua69d\ua69e\ua69f\ua6a0\ua6a1\ua6a2\ua6a3\ua6a4\ua6a5\ua6a6\ua6a7\ua6a8\ua6a9\ua6aa\ua6ab\ua6ac\ua6ad\ua6ae\ua6af\ua6b0\ua6b1\ua6b2\ua6b3\ua6b4\ua6b5\ua6b6\ua6b7\ua6b8\ua6b9\ua6ba\ua6bb\ua6bc\ua6bd\ua6be\ua6bf\ua6c0\ua6c1\ua6c2\ua6c3\ua6c4\ua6c5\ua6c6\ua6c7\ua6c8\ua6c9\ua6ca\ua6cb\ua6cc\ua6cd\ua6ce\ua6cf\ua6d0\ua6d1\ua6d2\ua6d3\ua6d4\ua6d5\ua6d6\ua6d7\ua6d8\ua6d9\ua6da\ua6db\ua6dc\ua6dd\ua6de\ua6df\ua6e0\ua6e1\ua6e2\ua6e3\ua6e4\ua6e5\ua6e6\ua6e7\ua6e8\ua6e9\ua6ea\ua6eb\ua6ec\ua6ed\ua6ee\ua6ef\ua6f0\ua6f1\ua6f2\ua6f3\ua6f4\ua6f5\ua6f6\ua6f7\ua6f8\ua6f9\ua6fa\ua6fb\ua6fc\ua6fd\ua6fe\ua6ff\ua717\ua718\ua719\ua71a\ua71b\ua71c\ua71d\ua71e\ua71f\ua720\ua721\ua722\ua723\ua724\ua725\ua726\ua727\ua728\ua729\ua72a\ua72b\ua72c\ua72d\ua72e\ua72f\ua730\ua731\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua739\ua73a\ua73b\ua73c\ua73d\ua73e\ua73f\ua740\ua741\ua742\ua743\ua744\ua745\ua746\ua747\ua748\ua749\ua74a\ua74b\ua74c\ua74d\ua74e\ua74f\ua750\ua751\ua752\ua753\ua754\ua755\ua756\ua757\ua758\ua759\ua75a\ua75b\ua75c\ua75d\ua75e\ua75f\ua760\ua761\ua762\ua763\ua764\ua765\ua766\ua767\ua768\ua769\ua76a\ua76b\ua76c\ua76d\ua76e\ua76f\ua770\ua771\ua772\ua773\ua774\ua775\ua776\ua777\ua778\ua779\ua77a\ua77b\ua77c\ua77d\ua77e\ua77f\ua780\ua781\ua782\ua783\ua784\ua785\ua786\ua787\ua788\ua789\ua78a\ua78b\ua78c\ua78d\ua78e\ua78f\ua790\ua791\ua792\ua793\ua794\ua795\ua796\ua797\ua798\ua799\ua79a\ua79b\ua79c\ua79d\ua79e\ua79f\ua7a0\ua7a1\ua7a2\ua7a3\ua7a4\ua7a5\ua7a6\ua7a7\ua7a8\ua7a9\ua7aa\ua7ab\ua7ac\ua7ad\ua7ae\ua7af\ua7b0\ua7b1\ua7b2\ua7b3\ua7b4\ua7b5\ua7b6\ua7b7\ua7b8\ua7b9\ua7ba\ua7bb\ua7bc\ua7bd\ua7be\ua7bf\ua7c0\ua7c1\ua7c2\ua7c3\ua7c4\ua7c5\ua7c6\ua7c7\ua7c8\ua7c9\ua7ca\ua7cb\ua7cc\ua7cd\ua7ce\ua7cf\ua7d0\ua7d1\ua7d2\ua7d3\ua7d4\ua7d5\ua7d6\ua7d7\ua7d8\ua7d9\ua7da\ua7db\ua7dc\ua7dd\ua7de\ua7df\ua7e0\ua7e1\ua7e2\ua7e3\ua7e4\ua7e5\ua7e6\ua7e7\ua7e8\ua7e9\ua7ea\ua7eb\ua7ec\ua7ed\ua7ee\ua7ef\ua7f0\ua7f1\ua7f2\ua7f3\ua7f4\ua7f5\ua7f6\ua7f7\ua7f8\ua7f9\ua7fa\ua7fb\ua7fc\ua7fd\ua7fe\ua7ff\ua82c\ua82d\ua82e\ua82f\ua830\ua831\ua832\ua833\ua834\ua835\ua836\ua837\ua838\ua839\ua83a\ua83b\ua83c\ua83d\ua83e\ua83f\ua840\ua841\ua842\ua843\ua844\ua845\ua846\ua847\ua848\ua849\ua84a\ua84b\ua84c\ua84d\ua84e\ua84f\ua850\ua851\ua852\ua853\ua854\ua855\ua856\ua857\ua858\ua859\ua85a\ua85b\ua85c\ua85d\ua85e\ua85f\ua860\ua861\ua862\ua863\ua864\ua865\ua866\ua867\ua868\ua869\ua86a\ua86b\ua86c\ua86d\ua86e\ua86f\ua870\ua871\ua872\ua873\ua874\ua875\ua876\ua877\ua878\ua879\ua87a\ua87b\ua87c\ua87d\ua87e\ua87f\ua880\ua881\ua882\ua883\ua884\ua885\ua886\ua887\ua888\ua889\ua88a\ua88b\ua88c\ua88d\ua88e\ua88f\ua890\ua891\ua892\ua893\ua894\ua895\ua896\ua897\ua898\ua899\ua89a\ua89b\ua89c\ua89d\ua89e\ua89f\ua8a0\ua8a1\ua8a2\ua8a3\ua8a4\ua8a5\ua8a6\ua8a7\ua8a8\ua8a9\ua8aa\ua8ab\ua8ac\ua8ad\ua8ae\ua8af\ua8b0\ua8b1\ua8b2\ua8b3\ua8b4\ua8b5\ua8b6\ua8b7\ua8b8\ua8b9\ua8ba\ua8bb\ua8bc\ua8bd\ua8be\ua8bf\ua8c0\ua8c1\ua8c2\ua8c3\ua8c4\ua8c5\ua8c6\ua8c7\ua8c8\ua8c9\ua8ca\ua8cb\ua8cc\ua8cd\ua8ce\ua8cf\ua8d0\ua8d1\ua8d2\ua8d3\ua8d4\ua8d5\ua8d6\ua8d7\ua8d8\ua8d9\ua8da\ua8db\ua8dc\ua8dd\ua8de\ua8df\ua8e0\ua8e1\ua8e2\ua8e3\ua8e4\ua8e5\ua8e6\ua8e7\ua8e8\ua8e9\ua8ea\ua8eb\ua8ec\ua8ed\ua8ee\ua8ef\ua8f0\ua8f1\ua8f2\ua8f3\ua8f4\ua8f5\ua8f6\ua8f7\ua8f8\ua8f9\ua8fa\ua8fb\ua8fc\ua8fd\ua8fe\ua8ff\ua900\ua901\ua902\ua903\ua904\ua905\ua906\ua907\ua908\ua909\ua90a\ua90b\ua90c\ua90d\ua90e\ua90f\ua910\ua911\ua912\ua913\ua914\ua915\ua916\ua917\ua918\ua919\ua91a\ua91b\ua91c\ua91d\ua91e\ua91f\ua920\ua921\ua922\ua923\ua924\ua925\ua926\ua927\ua928\ua929\ua92a\ua92b\ua92c\ua92d\ua92e\ua92f\ua930\ua931\ua932\ua933\ua934\ua935\ua936\ua937\ua938\ua939\ua93a\ua93b\ua93c\ua93d\ua93e\ua93f\ua940\ua941\ua942\ua943\ua944\ua945\ua946\ua947\ua948\ua949\ua94a\ua94b\ua94c\ua94d\ua94e\ua94f\ua950\ua951\ua952\ua953\ua954\ua955\ua956\ua957\ua958\ua959\ua95a\ua95b\ua95c\ua95d\ua95e\ua95f\ua960\ua961\ua962\ua963\ua964\ua965\ua966\ua967\ua968\ua969\ua96a\ua96b\ua96c\ua96d\ua96e\ua96f\ua970\ua971\ua972\ua973\ua974\ua975\ua976\ua977\ua978\ua979\ua97a\ua97b\ua97c\ua97d\ua97e\ua97f\ua980\ua981\ua982\ua983\ua984\ua985\ua986\ua987\ua988\ua989\ua98a\ua98b\ua98c\ua98d\ua98e\ua98f\ua990\ua991\ua992\ua993\ua994\ua995\ua996\ua997\ua998\ua999\ua99a\ua99b\ua99c\ua99d\ua99e\ua99f\ua9a0\ua9a1\ua9a2\ua9a3\ua9a4\ua9a5\ua9a6\ua9a7\ua9a8\ua9a9\ua9aa\ua9ab\ua9ac\ua9ad\ua9ae\ua9af\ua9b0\ua9b1\ua9b2\ua9b3\ua9b4\ua9b5\ua9b6\ua9b7\ua9b8\ua9b9\ua9ba\ua9bb\ua9bc\ua9bd\ua9be\ua9bf\ua9c0\ua9c1\ua9c2\ua9c3\ua9c4\ua9c5\ua9c6\ua9c7\ua9c8\ua9c9\ua9ca\ua9cb\ua9cc\ua9cd\ua9ce\ua9cf\ua9d0\ua9d1\ua9d2\ua9d3\ua9d4\ua9d5\ua9d6\ua9d7\ua9d8\ua9d9\ua9da\ua9db\ua9dc\ua9dd\ua9de\ua9df\ua9e0\ua9e1\ua9e2\ua9e3\ua9e4\ua9e5\ua9e6\ua9e7\ua9e8\ua9e9\ua9ea\ua9eb\ua9ec\ua9ed\ua9ee\ua9ef\ua9f0\ua9f1\ua9f2\ua9f3\ua9f4\ua9f5\ua9f6\ua9f7\ua9f8\ua9f9\ua9fa\ua9fb\ua9fc\ua9fd\ua9fe\ua9ff\uaa00\uaa01\uaa02\uaa03\uaa04\uaa05\uaa06\uaa07\uaa08\uaa09\uaa0a\uaa0b\uaa0c\uaa0d\uaa0e\uaa0f\uaa10\uaa11\uaa12\uaa13\uaa14\uaa15\uaa16\uaa17\uaa18\uaa19\uaa1a\uaa1b\uaa1c\uaa1d\uaa1e\uaa1f\uaa20\uaa21\uaa22\uaa23\uaa24\uaa25\uaa26\uaa27\uaa28\uaa29\uaa2a\uaa2b\uaa2c\uaa2d\uaa2e\uaa2f\uaa30\uaa31\uaa32\uaa33\uaa34\uaa35\uaa36\uaa37\uaa38\uaa39\uaa3a\uaa3b\uaa3c\uaa3d\uaa3e\uaa3f\uaa40\uaa41\uaa42\uaa43\uaa44\uaa45\uaa46\uaa47\uaa48\uaa49\uaa4a\uaa4b\uaa4c\uaa4d\uaa4e\uaa4f\uaa50\uaa51\uaa52\uaa53\uaa54\uaa55\uaa56\uaa57\uaa58\uaa59\uaa5a\uaa5b\uaa5c\uaa5d\uaa5e\uaa5f\uaa60\uaa61\uaa62\uaa63\uaa64\uaa65\uaa66\uaa67\uaa68\uaa69\uaa6a\uaa6b\uaa6c\uaa6d\uaa6e\uaa6f\uaa70\uaa71\uaa72\uaa73\uaa74\uaa75\uaa76\uaa77\uaa78\uaa79\uaa7a\uaa7b\uaa7c\uaa7d\uaa7e\uaa7f\uaa80\uaa81\uaa82\uaa83\uaa84\uaa85\uaa86\uaa87\uaa88\uaa89\uaa8a\uaa8b\uaa8c\uaa8d\uaa8e\uaa8f\uaa90\uaa91\uaa92\uaa93\uaa94\uaa95\uaa96\uaa97\uaa98\uaa99\uaa9a\uaa9b\uaa9c\uaa9d\uaa9e\uaa9f\uaaa0\uaaa1\uaaa2\uaaa3\uaaa4\uaaa5\uaaa6\uaaa7\uaaa8\uaaa9\uaaaa\uaaab\uaaac\uaaad\uaaae\uaaaf\uaab0\uaab1\uaab2\uaab3\uaab4\uaab5\uaab6\uaab7\uaab8\uaab9\uaaba\uaabb\uaabc\uaabd\uaabe\uaabf\uaac0\uaac1\uaac2\uaac3\uaac4\uaac5\uaac6\uaac7\uaac8\uaac9\uaaca\uaacb\uaacc\uaacd\uaace\uaacf\uaad0\uaad1\uaad2\uaad3\uaad4\uaad5\uaad6\uaad7\uaad8\uaad9\uaada\uaadb\uaadc\uaadd\uaade\uaadf\uaae0\uaae1\uaae2\uaae3\uaae4\uaae5\uaae6\uaae7\uaae8\uaae9\uaaea\uaaeb\uaaec\uaaed\uaaee\uaaef\uaaf0\uaaf1\uaaf2\uaaf3\uaaf4\uaaf5\uaaf6\uaaf7\uaaf8\uaaf9\uaafa\uaafb\uaafc\uaafd\uaafe\uaaff\uab00\uab01\uab02\uab03\uab04\uab05\uab06\uab07\uab08\uab09\uab0a\uab0b\uab0c\uab0d\uab0e\uab0f\uab10\uab11\uab12\uab13\uab14\uab15\uab16\uab17\uab18\uab19\uab1a\uab1b\uab1c\uab1d\uab1e\uab1f\uab20\uab21\uab22\uab23\uab24\uab25\uab26\uab27\uab28\uab29\uab2a\uab2b\uab2c\uab2d\uab2e\uab2f\uab30\uab31\uab32\uab33\uab34\uab35\uab36\uab37\uab38\uab39\uab3a\uab3b\uab3c\uab3d\uab3e\uab3f\uab40\uab41\uab42\uab43\uab44\uab45\uab46\uab47\uab48\uab49\uab4a\uab4b\uab4c\uab4d\uab4e\uab4f\uab50\uab51\uab52\uab53\uab54\uab55\uab56\uab57\uab58\uab59\uab5a\uab5b\uab5c\uab5d\uab5e\uab5f\uab60\uab61\uab62\uab63\uab64\uab65\uab66\uab67\uab68\uab69\uab6a\uab6b\uab6c\uab6d\uab6e\uab6f\uab70\uab71\uab72\uab73\uab74\uab75\uab76\uab77\uab78\uab79\uab7a\uab7b\uab7c\uab7d\uab7e\uab7f\uab80\uab81\uab82\uab83\uab84\uab85\uab86\uab87\uab88\uab89\uab8a\uab8b\uab8c\uab8d\uab8e\uab8f\uab90\uab91\uab92\uab93\uab94\uab95\uab96\uab97\uab98\uab99\uab9a\uab9b\uab9c\uab9d\uab9e\uab9f\uaba0\uaba1\uaba2\uaba3\uaba4\uaba5\uaba6\uaba7\uaba8\uaba9\uabaa\uabab\uabac\uabad\uabae\uabaf\uabb0\uabb1\uabb2\uabb3\uabb4\uabb5\uabb6\uabb7\uabb8\uabb9\uabba\uabbb\uabbc\uabbd\uabbe\uabbf\uabc0\uabc1\uabc2\uabc3\uabc4\uabc5\uabc6\uabc7\uabc8\uabc9\uabca\uabcb\uabcc\uabcd\uabce\uabcf\uabd0\uabd1\uabd2\uabd3\uabd4\uabd5\uabd6\uabd7\uabd8\uabd9\uabda\uabdb\uabdc\uabdd\uabde\uabdf\uabe0\uabe1\uabe2\uabe3\uabe4\uabe5\uabe6\uabe7\uabe8\uabe9\uabea\uabeb\uabec\uabed\uabee\uabef\uabf0\uabf1\uabf2\uabf3\uabf4\uabf5\uabf6\uabf7\uabf8\uabf9\uabfa\uabfb\uabfc\uabfd\uabfe\uabff\ud7a4\ud7a5\ud7a6\ud7a7\ud7a8\ud7a9\ud7aa\ud7ab\ud7ac\ud7ad\ud7ae\ud7af\ud7b0\ud7b1\ud7b2\ud7b3\ud7b4\ud7b5\ud7b6\ud7b7\ud7b8\ud7b9\ud7ba\ud7bb\ud7bc\ud7bd\ud7be\ud7bf\ud7c0\ud7c1\ud7c2\ud7c3\ud7c4\ud7c5\ud7c6\ud7c7\ud7c8\ud7c9\ud7ca\ud7cb\ud7cc\ud7cd\ud7ce\ud7cf\ud7d0\ud7d1\ud7d2\ud7d3\ud7d4\ud7d5\ud7d6\ud7d7\ud7d8\ud7d9\ud7da\ud7db\ud7dc\ud7dd\ud7de\ud7df\ud7e0\ud7e1\ud7e2\ud7e3\ud7e4\ud7e5\ud7e6\ud7e7\ud7e8\ud7e9\ud7ea\ud7eb\ud7ec\ud7ed\ud7ee\ud7ef\ud7f0\ud7f1\ud7f2\ud7f3\ud7f4\ud7f5\ud7f6\ud7f7\ud7f8\ud7f9\ud7fa\ud7fb\ud7fc\ud7fd\ud7fe\ud7ff\ufa2e\ufa2f\ufa6b\ufa6c\ufa6d\ufa6e\ufa6f\ufada\ufadb\ufadc\ufadd\ufade\ufadf\ufae0\ufae1\ufae2\ufae3\ufae4\ufae5\ufae6\ufae7\ufae8\ufae9\ufaea\ufaeb\ufaec\ufaed\ufaee\ufaef\ufaf0\ufaf1\ufaf2\ufaf3\ufaf4\ufaf5\ufaf6\ufaf7\ufaf8\ufaf9\ufafa\ufafb\ufafc\ufafd\ufafe\ufaff\ufb07\ufb08\ufb09\ufb0a\ufb0b\ufb0c\ufb0d\ufb0e\ufb0f\ufb10\ufb11\ufb12\ufb18\ufb19\ufb1a\ufb1b\ufb1c\ufb37\ufb3d\ufb3f\ufb42\ufb45\ufbb2\ufbb3\ufbb4\ufbb5\ufbb6\ufbb7\ufbb8\ufbb9\ufbba\ufbbb\ufbbc\ufbbd\ufbbe\ufbbf\ufbc0\ufbc1\ufbc2\ufbc3\ufbc4\ufbc5\ufbc6\ufbc7\ufbc8\ufbc9\ufbca\ufbcb\ufbcc\ufbcd\ufbce\ufbcf\ufbd0\ufbd1\ufbd2\ufd40\ufd41\ufd42\ufd43\ufd44\ufd45\ufd46\ufd47\ufd48\ufd49\ufd4a\ufd4b\ufd4c\ufd4d\ufd4e\ufd4f\ufd90\ufd91\ufdc8\ufdc9\ufdca\ufdcb\ufdcc\ufdcd\ufdce\ufdcf\ufdd0\ufdd1\ufdd2\ufdd3\ufdd4\ufdd5\ufdd6\ufdd7\ufdd8\ufdd9\ufdda\ufddb\ufddc\ufddd\ufdde\ufddf\ufde0\ufde1\ufde2\ufde3\ufde4\ufde5\ufde6\ufde7\ufde8\ufde9\ufdea\ufdeb\ufdec\ufded\ufdee\ufdef\ufdfe\ufdff\ufe1a\ufe1b\ufe1c\ufe1d\ufe1e\ufe1f\ufe24\ufe25\ufe26\ufe27\ufe28\ufe29\ufe2a\ufe2b\ufe2c\ufe2d\ufe2e\ufe2f\ufe53\ufe67\ufe6c\ufe6d\ufe6e\ufe6f\ufe75\ufefd\ufefe\uff00\uffbf\uffc0\uffc1\uffc8\uffc9\uffd0\uffd1\uffd8\uffd9\uffdd\uffde\uffdf\uffe7\uffef\ufff0\ufff1\ufff2\ufff3\ufff4\ufff5\ufff6\ufff7\ufff8\ufffe'
-
-Co = u'\ue000\ue001\ue002\ue003\ue004\ue005\ue006\ue007\ue008\ue009\ue00a\ue00b\ue00c\ue00d\ue00e\ue00f\ue010\ue011\ue012\ue013\ue014\ue015\ue016\ue017\ue018\ue019\ue01a\ue01b\ue01c\ue01d\ue01e\ue01f\ue020\ue021\ue022\ue023\ue024\ue025\ue026\ue027\ue028\ue029\ue02a\ue02b\ue02c\ue02d\ue02e\ue02f\ue030\ue031\ue032\ue033\ue034\ue035\ue036\ue037\ue038\ue039\ue03a\ue03b\ue03c\ue03d\ue03e\ue03f\ue040\ue041\ue042\ue043\ue044\ue045\ue046\ue047\ue048\ue049\ue04a\ue04b\ue04c\ue04d\ue04e\ue04f\ue050\ue051\ue052\ue053\ue054\ue055\ue056\ue057\ue058\ue059\ue05a\ue05b\ue05c\ue05d\ue05e\ue05f\ue060\ue061\ue062\ue063\ue064\ue065\ue066\ue067\ue068\ue069\ue06a\ue06b\ue06c\ue06d\ue06e\ue06f\ue070\ue071\ue072\ue073\ue074\ue075\ue076\ue077\ue078\ue079\ue07a\ue07b\ue07c\ue07d\ue07e\ue07f\ue080\ue081\ue082\ue083\ue084\ue085\ue086\ue087\ue088\ue089\ue08a\ue08b\ue08c\ue08d\ue08e\ue08f\ue090\ue091\ue092\ue093\ue094\ue095\ue096\ue097\ue098\ue099\ue09a\ue09b\ue09c\ue09d\ue09e\ue09f\ue0a0\ue0a1\ue0a2\ue0a3\ue0a4\ue0a5\ue0a6\ue0a7\ue0a8\ue0a9\ue0aa\ue0ab\ue0ac\ue0ad\ue0ae\ue0af\ue0b0\ue0b1\ue0b2\ue0b3\ue0b4\ue0b5\ue0b6\ue0b7\ue0b8\ue0b9\ue0ba\ue0bb\ue0bc\ue0bd\ue0be\ue0bf\ue0c0\ue0c1\ue0c2\ue0c3\ue0c4\ue0c5\ue0c6\ue0c7\ue0c8\ue0c9\ue0ca\ue0cb\ue0cc\ue0cd\ue0ce\ue0cf\ue0d0\ue0d1\ue0d2\ue0d3\ue0d4\ue0d5\ue0d6\ue0d7\ue0d8\ue0d9\ue0da\ue0db\ue0dc\ue0dd\ue0de\ue0df\ue0e0\ue0e1\ue0e2\ue0e3\ue0e4\ue0e5\ue0e6\ue0e7\ue0e8\ue0e9\ue0ea\ue0eb\ue0ec\ue0ed\ue0ee\ue0ef\ue0f0\ue0f1\ue0f2\ue0f3\ue0f4\ue0f5\ue0f6\ue0f7\ue0f8\ue0f9\ue0fa\ue0fb\ue0fc\ue0fd\ue0fe\ue0ff\ue100\ue101\ue102\ue103\ue104\ue105\ue106\ue107\ue108\ue109\ue10a\ue10b\ue10c\ue10d\ue10e\ue10f\ue110\ue111\ue112\ue113\ue114\ue115\ue116\ue117\ue118\ue119\ue11a\ue11b\ue11c\ue11d\ue11e\ue11f\ue120\ue121\ue122\ue123\ue124\ue125\ue126\ue127\ue128\ue129\ue12a\ue12b\ue12c\ue12d\ue12e\ue12f\ue130\ue131\ue132\ue133\ue134\ue135\ue136\ue137\ue138\ue139\ue13a\ue13b\ue13c\ue13d\ue13e\ue13f\ue140\ue141\ue142\ue143\ue144\ue145\ue146\ue147\ue148\ue149\ue14a\ue14b\ue14c\ue14d\ue14e\ue14f\ue150\ue151\ue152\ue153\ue154\ue155\ue156\ue157\ue158\ue159\ue15a\ue15b\ue15c\ue15d\ue15e\ue15f\ue160\ue161\ue162\ue163\ue164\ue165\ue166\ue167\ue168\ue169\ue16a\ue16b\ue16c\ue16d\ue16e\ue16f\ue170\ue171\ue172\ue173\ue174\ue175\ue176\ue177\ue178\ue179\ue17a\ue17b\ue17c\ue17d\ue17e\ue17f\ue180\ue181\ue182\ue183\ue184\ue185\ue186\ue187\ue188\ue189\ue18a\ue18b\ue18c\ue18d\ue18e\ue18f\ue190\ue191\ue192\ue193\ue194\ue195\ue196\ue197\ue198\ue199\ue19a\ue19b\ue19c\ue19d\ue19e\ue19f\ue1a0\ue1a1\ue1a2\ue1a3\ue1a4\ue1a5\ue1a6\ue1a7\ue1a8\ue1a9\ue1aa\ue1ab\ue1ac\ue1ad\ue1ae\ue1af\ue1b0\ue1b1\ue1b2\ue1b3\ue1b4\ue1b5\ue1b6\ue1b7\ue1b8\ue1b9\ue1ba\ue1bb\ue1bc\ue1bd\ue1be\ue1bf\ue1c0\ue1c1\ue1c2\ue1c3\ue1c4\ue1c5\ue1c6\ue1c7\ue1c8\ue1c9\ue1ca\ue1cb\ue1cc\ue1cd\ue1ce\ue1cf\ue1d0\ue1d1\ue1d2\ue1d3\ue1d4\ue1d5\ue1d6\ue1d7\ue1d8\ue1d9\ue1da\ue1db\ue1dc\ue1dd\ue1de\ue1df\ue1e0\ue1e1\ue1e2\ue1e3\ue1e4\ue1e5\ue1e6\ue1e7\ue1e8\ue1e9\ue1ea\ue1eb\ue1ec\ue1ed\ue1ee\ue1ef\ue1f0\ue1f1\ue1f2\ue1f3\ue1f4\ue1f5\ue1f6\ue1f7\ue1f8\ue1f9\ue1fa\ue1fb\ue1fc\ue1fd\ue1fe\ue1ff\ue200\ue201\ue202\ue203\ue204\ue205\ue206\ue207\ue208\ue209\ue20a\ue20b\ue20c\ue20d\ue20e\ue20f\ue210\ue211\ue212\ue213\ue214\ue215\ue216\ue217\ue218\ue219\ue21a\ue21b\ue21c\ue21d\ue21e\ue21f\ue220\ue221\ue222\ue223\ue224\ue225\ue226\ue227\ue228\ue229\ue22a\ue22b\ue22c\ue22d\ue22e\ue22f\ue230\ue231\ue232\ue233\ue234\ue235\ue236\ue237\ue238\ue239\ue23a\ue23b\ue23c\ue23d\ue23e\ue23f\ue240\ue241\ue242\ue243\ue244\ue245\ue246\ue247\ue248\ue249\ue24a\ue24b\ue24c\ue24d\ue24e\ue24f\ue250\ue251\ue252\ue253\ue254\ue255\ue256\ue257\ue258\ue259\ue25a\ue25b\ue25c\ue25d\ue25e\ue25f\ue260\ue261\ue262\ue263\ue264\ue265\ue266\ue267\ue268\ue269\ue26a\ue26b\ue26c\ue26d\ue26e\ue26f\ue270\ue271\ue272\ue273\ue274\ue275\ue276\ue277\ue278\ue279\ue27a\ue27b\ue27c\ue27d\ue27e\ue27f\ue280\ue281\ue282\ue283\ue284\ue285\ue286\ue287\ue288\ue289\ue28a\ue28b\ue28c\ue28d\ue28e\ue28f\ue290\ue291\ue292\ue293\ue294\ue295\ue296\ue297\ue298\ue299\ue29a\ue29b\ue29c\ue29d\ue29e\ue29f\ue2a0\ue2a1\ue2a2\ue2a3\ue2a4\ue2a5\ue2a6\ue2a7\ue2a8\ue2a9\ue2aa\ue2ab\ue2ac\ue2ad\ue2ae\ue2af\ue2b0\ue2b1\ue2b2\ue2b3\ue2b4\ue2b5\ue2b6\ue2b7\ue2b8\ue2b9\ue2ba\ue2bb\ue2bc\ue2bd\ue2be\ue2bf\ue2c0\ue2c1\ue2c2\ue2c3\ue2c4\ue2c5\ue2c6\ue2c7\ue2c8\ue2c9\ue2ca\ue2cb\ue2cc\ue2cd\ue2ce\ue2cf\ue2d0\ue2d1\ue2d2\ue2d3\ue2d4\ue2d5\ue2d6\ue2d7\ue2d8\ue2d9\ue2da\ue2db\ue2dc\ue2dd\ue2de\ue2df\ue2e0\ue2e1\ue2e2\ue2e3\ue2e4\ue2e5\ue2e6\ue2e7\ue2e8\ue2e9\ue2ea\ue2eb\ue2ec\ue2ed\ue2ee\ue2ef\ue2f0\ue2f1\ue2f2\ue2f3\ue2f4\ue2f5\ue2f6\ue2f7\ue2f8\ue2f9\ue2fa\ue2fb\ue2fc\ue2fd\ue2fe\ue2ff\ue300\ue301\ue302\ue303\ue304\ue305\ue306\ue307\ue308\ue309\ue30a\ue30b\ue30c\ue30d\ue30e\ue30f\ue310\ue311\ue312\ue313\ue314\ue315\ue316\ue317\ue318\ue319\ue31a\ue31b\ue31c\ue31d\ue31e\ue31f\ue320\ue321\ue322\ue323\ue324\ue325\ue326\ue327\ue328\ue329\ue32a\ue32b\ue32c\ue32d\ue32e\ue32f\ue330\ue331\ue332\ue333\ue334\ue335\ue336\ue337\ue338\ue339\ue33a\ue33b\ue33c\ue33d\ue33e\ue33f\ue340\ue341\ue342\ue343\ue344\ue345\ue346\ue347\ue348\ue349\ue34a\ue34b\ue34c\ue34d\ue34e\ue34f\ue350\ue351\ue352\ue353\ue354\ue355\ue356\ue357\ue358\ue359\ue35a\ue35b\ue35c\ue35d\ue35e\ue35f\ue360\ue361\ue362\ue363\ue364\ue365\ue366\ue367\ue368\ue369\ue36a\ue36b\ue36c\ue36d\ue36e\ue36f\ue370\ue371\ue372\ue373\ue374\ue375\ue376\ue377\ue378\ue379\ue37a\ue37b\ue37c\ue37d\ue37e\ue37f\ue380\ue381\ue382\ue383\ue384\ue385\ue386\ue387\ue388\ue389\ue38a\ue38b\ue38c\ue38d\ue38e\ue38f\ue390\ue391\ue392\ue393\ue394\ue395\ue396\ue397\ue398\ue399\ue39a\ue39b\ue39c\ue39d\ue39e\ue39f\ue3a0\ue3a1\ue3a2\ue3a3\ue3a4\ue3a5\ue3a6\ue3a7\ue3a8\ue3a9\ue3aa\ue3ab\ue3ac\ue3ad\ue3ae\ue3af\ue3b0\ue3b1\ue3b2\ue3b3\ue3b4\ue3b5\ue3b6\ue3b7\ue3b8\ue3b9\ue3ba\ue3bb\ue3bc\ue3bd\ue3be\ue3bf\ue3c0\ue3c1\ue3c2\ue3c3\ue3c4\ue3c5\ue3c6\ue3c7\ue3c8\ue3c9\ue3ca\ue3cb\ue3cc\ue3cd\ue3ce\ue3cf\ue3d0\ue3d1\ue3d2\ue3d3\ue3d4\ue3d5\ue3d6\ue3d7\ue3d8\ue3d9\ue3da\ue3db\ue3dc\ue3dd\ue3de\ue3df\ue3e0\ue3e1\ue3e2\ue3e3\ue3e4\ue3e5\ue3e6\ue3e7\ue3e8\ue3e9\ue3ea\ue3eb\ue3ec\ue3ed\ue3ee\ue3ef\ue3f0\ue3f1\ue3f2\ue3f3\ue3f4\ue3f5\ue3f6\ue3f7\ue3f8\ue3f9\ue3fa\ue3fb\ue3fc\ue3fd\ue3fe\ue3ff\ue400\ue401\ue402\ue403\ue404\ue405\ue406\ue407\ue408\ue409\ue40a\ue40b\ue40c\ue40d\ue40e\ue40f\ue410\ue411\ue412\ue413\ue414\ue415\ue416\ue417\ue418\ue419\ue41a\ue41b\ue41c\ue41d\ue41e\ue41f\ue420\ue421\ue422\ue423\ue424\ue425\ue426\ue427\ue428\ue429\ue42a\ue42b\ue42c\ue42d\ue42e\ue42f\ue430\ue431\ue432\ue433\ue434\ue435\ue436\ue437\ue438\ue439\ue43a\ue43b\ue43c\ue43d\ue43e\ue43f\ue440\ue441\ue442\ue443\ue444\ue445\ue446\ue447\ue448\ue449\ue44a\ue44b\ue44c\ue44d\ue44e\ue44f\ue450\ue451\ue452\ue453\ue454\ue455\ue456\ue457\ue458\ue459\ue45a\ue45b\ue45c\ue45d\ue45e\ue45f\ue460\ue461\ue462\ue463\ue464\ue465\ue466\ue467\ue468\ue469\ue46a\ue46b\ue46c\ue46d\ue46e\ue46f\ue470\ue471\ue472\ue473\ue474\ue475\ue476\ue477\ue478\ue479\ue47a\ue47b\ue47c\ue47d\ue47e\ue47f\ue480\ue481\ue482\ue483\ue484\ue485\ue486\ue487\ue488\ue489\ue48a\ue48b\ue48c\ue48d\ue48e\ue48f\ue490\ue491\ue492\ue493\ue494\ue495\ue496\ue497\ue498\ue499\ue49a\ue49b\ue49c\ue49d\ue49e\ue49f\ue4a0\ue4a1\ue4a2\ue4a3\ue4a4\ue4a5\ue4a6\ue4a7\ue4a8\ue4a9\ue4aa\ue4ab\ue4ac\ue4ad\ue4ae\ue4af\ue4b0\ue4b1\ue4b2\ue4b3\ue4b4\ue4b5\ue4b6\ue4b7\ue4b8\ue4b9\ue4ba\ue4bb\ue4bc\ue4bd\ue4be\ue4bf\ue4c0\ue4c1\ue4c2\ue4c3\ue4c4\ue4c5\ue4c6\ue4c7\ue4c8\ue4c9\ue4ca\ue4cb\ue4cc\ue4cd\ue4ce\ue4cf\ue4d0\ue4d1\ue4d2\ue4d3\ue4d4\ue4d5\ue4d6\ue4d7\ue4d8\ue4d9\ue4da\ue4db\ue4dc\ue4dd\ue4de\ue4df\ue4e0\ue4e1\ue4e2\ue4e3\ue4e4\ue4e5\ue4e6\ue4e7\ue4e8\ue4e9\ue4ea\ue4eb\ue4ec\ue4ed\ue4ee\ue4ef\ue4f0\ue4f1\ue4f2\ue4f3\ue4f4\ue4f5\ue4f6\ue4f7\ue4f8\ue4f9\ue4fa\ue4fb\ue4fc\ue4fd\ue4fe\ue4ff\ue500\ue501\ue502\ue503\ue504\ue505\ue506\ue507\ue508\ue509\ue50a\ue50b\ue50c\ue50d\ue50e\ue50f\ue510\ue511\ue512\ue513\ue514\ue515\ue516\ue517\ue518\ue519\ue51a\ue51b\ue51c\ue51d\ue51e\ue51f\ue520\ue521\ue522\ue523\ue524\ue525\ue526\ue527\ue528\ue529\ue52a\ue52b\ue52c\ue52d\ue52e\ue52f\ue530\ue531\ue532\ue533\ue534\ue535\ue536\ue537\ue538\ue539\ue53a\ue53b\ue53c\ue53d\ue53e\ue53f\ue540\ue541\ue542\ue543\ue544\ue545\ue546\ue547\ue548\ue549\ue54a\ue54b\ue54c\ue54d\ue54e\ue54f\ue550\ue551\ue552\ue553\ue554\ue555\ue556\ue557\ue558\ue559\ue55a\ue55b\ue55c\ue55d\ue55e\ue55f\ue560\ue561\ue562\ue563\ue564\ue565\ue566\ue567\ue568\ue569\ue56a\ue56b\ue56c\ue56d\ue56e\ue56f\ue570\ue571\ue572\ue573\ue574\ue575\ue576\ue577\ue578\ue579\ue57a\ue57b\ue57c\ue57d\ue57e\ue57f\ue580\ue581\ue582\ue583\ue584\ue585\ue586\ue587\ue588\ue589\ue58a\ue58b\ue58c\ue58d\ue58e\ue58f\ue590\ue591\ue592\ue593\ue594\ue595\ue596\ue597\ue598\ue599\ue59a\ue59b\ue59c\ue59d\ue59e\ue59f\ue5a0\ue5a1\ue5a2\ue5a3\ue5a4\ue5a5\ue5a6\ue5a7\ue5a8\ue5a9\ue5aa\ue5ab\ue5ac\ue5ad\ue5ae\ue5af\ue5b0\ue5b1\ue5b2\ue5b3\ue5b4\ue5b5\ue5b6\ue5b7\ue5b8\ue5b9\ue5ba\ue5bb\ue5bc\ue5bd\ue5be\ue5bf\ue5c0\ue5c1\ue5c2\ue5c3\ue5c4\ue5c5\ue5c6\ue5c7\ue5c8\ue5c9\ue5ca\ue5cb\ue5cc\ue5cd\ue5ce\ue5cf\ue5d0\ue5d1\ue5d2\ue5d3\ue5d4\ue5d5\ue5d6\ue5d7\ue5d8\ue5d9\ue5da\ue5db\ue5dc\ue5dd\ue5de\ue5df\ue5e0\ue5e1\ue5e2\ue5e3\ue5e4\ue5e5\ue5e6\ue5e7\ue5e8\ue5e9\ue5ea\ue5eb\ue5ec\ue5ed\ue5ee\ue5ef\ue5f0\ue5f1\ue5f2\ue5f3\ue5f4\ue5f5\ue5f6\ue5f7\ue5f8\ue5f9\ue5fa\ue5fb\ue5fc\ue5fd\ue5fe\ue5ff\ue600\ue601\ue602\ue603\ue604\ue605\ue606\ue607\ue608\ue609\ue60a\ue60b\ue60c\ue60d\ue60e\ue60f\ue610\ue611\ue612\ue613\ue614\ue615\ue616\ue617\ue618\ue619\ue61a\ue61b\ue61c\ue61d\ue61e\ue61f\ue620\ue621\ue622\ue623\ue624\ue625\ue626\ue627\ue628\ue629\ue62a\ue62b\ue62c\ue62d\ue62e\ue62f\ue630\ue631\ue632\ue633\ue634\ue635\ue636\ue637\ue638\ue639\ue63a\ue63b\ue63c\ue63d\ue63e\ue63f\ue640\ue641\ue642\ue643\ue644\ue645\ue646\ue647\ue648\ue649\ue64a\ue64b\ue64c\ue64d\ue64e\ue64f\ue650\ue651\ue652\ue653\ue654\ue655\ue656\ue657\ue658\ue659\ue65a\ue65b\ue65c\ue65d\ue65e\ue65f\ue660\ue661\ue662\ue663\ue664\ue665\ue666\ue667\ue668\ue669\ue66a\ue66b\ue66c\ue66d\ue66e\ue66f\ue670\ue671\ue672\ue673\ue674\ue675\ue676\ue677\ue678\ue679\ue67a\ue67b\ue67c\ue67d\ue67e\ue67f\ue680\ue681\ue682\ue683\ue684\ue685\ue686\ue687\ue688\ue689\ue68a\ue68b\ue68c\ue68d\ue68e\ue68f\ue690\ue691\ue692\ue693\ue694\ue695\ue696\ue697\ue698\ue699\ue69a\ue69b\ue69c\ue69d\ue69e\ue69f\ue6a0\ue6a1\ue6a2\ue6a3\ue6a4\ue6a5\ue6a6\ue6a7\ue6a8\ue6a9\ue6aa\ue6ab\ue6ac\ue6ad\ue6ae\ue6af\ue6b0\ue6b1\ue6b2\ue6b3\ue6b4\ue6b5\ue6b6\ue6b7\ue6b8\ue6b9\ue6ba\ue6bb\ue6bc\ue6bd\ue6be\ue6bf\ue6c0\ue6c1\ue6c2\ue6c3\ue6c4\ue6c5\ue6c6\ue6c7\ue6c8\ue6c9\ue6ca\ue6cb\ue6cc\ue6cd\ue6ce\ue6cf\ue6d0\ue6d1\ue6d2\ue6d3\ue6d4\ue6d5\ue6d6\ue6d7\ue6d8\ue6d9\ue6da\ue6db\ue6dc\ue6dd\ue6de\ue6df\ue6e0\ue6e1\ue6e2\ue6e3\ue6e4\ue6e5\ue6e6\ue6e7\ue6e8\ue6e9\ue6ea\ue6eb\ue6ec\ue6ed\ue6ee\ue6ef\ue6f0\ue6f1\ue6f2\ue6f3\ue6f4\ue6f5\ue6f6\ue6f7\ue6f8\ue6f9\ue6fa\ue6fb\ue6fc\ue6fd\ue6fe\ue6ff\ue700\ue701\ue702\ue703\ue704\ue705\ue706\ue707\ue708\ue709\ue70a\ue70b\ue70c\ue70d\ue70e\ue70f\ue710\ue711\ue712\ue713\ue714\ue715\ue716\ue717\ue718\ue719\ue71a\ue71b\ue71c\ue71d\ue71e\ue71f\ue720\ue721\ue722\ue723\ue724\ue725\ue726\ue727\ue728\ue729\ue72a\ue72b\ue72c\ue72d\ue72e\ue72f\ue730\ue731\ue732\ue733\ue734\ue735\ue736\ue737\ue738\ue739\ue73a\ue73b\ue73c\ue73d\ue73e\ue73f\ue740\ue741\ue742\ue743\ue744\ue745\ue746\ue747\ue748\ue749\ue74a\ue74b\ue74c\ue74d\ue74e\ue74f\ue750\ue751\ue752\ue753\ue754\ue755\ue756\ue757\ue758\ue759\ue75a\ue75b\ue75c\ue75d\ue75e\ue75f\ue760\ue761\ue762\ue763\ue764\ue765\ue766\ue767\ue768\ue769\ue76a\ue76b\ue76c\ue76d\ue76e\ue76f\ue770\ue771\ue772\ue773\ue774\ue775\ue776\ue777\ue778\ue779\ue77a\ue77b\ue77c\ue77d\ue77e\ue77f\ue780\ue781\ue782\ue783\ue784\ue785\ue786\ue787\ue788\ue789\ue78a\ue78b\ue78c\ue78d\ue78e\ue78f\ue790\ue791\ue792\ue793\ue794\ue795\ue796\ue797\ue798\ue799\ue79a\ue79b\ue79c\ue79d\ue79e\ue79f\ue7a0\ue7a1\ue7a2\ue7a3\ue7a4\ue7a5\ue7a6\ue7a7\ue7a8\ue7a9\ue7aa\ue7ab\ue7ac\ue7ad\ue7ae\ue7af\ue7b0\ue7b1\ue7b2\ue7b3\ue7b4\ue7b5\ue7b6\ue7b7\ue7b8\ue7b9\ue7ba\ue7bb\ue7bc\ue7bd\ue7be\ue7bf\ue7c0\ue7c1\ue7c2\ue7c3\ue7c4\ue7c5\ue7c6\ue7c7\ue7c8\ue7c9\ue7ca\ue7cb\ue7cc\ue7cd\ue7ce\ue7cf\ue7d0\ue7d1\ue7d2\ue7d3\ue7d4\ue7d5\ue7d6\ue7d7\ue7d8\ue7d9\ue7da\ue7db\ue7dc\ue7dd\ue7de\ue7df\ue7e0\ue7e1\ue7e2\ue7e3\ue7e4\ue7e5\ue7e6\ue7e7\ue7e8\ue7e9\ue7ea\ue7eb\ue7ec\ue7ed\ue7ee\ue7ef\ue7f0\ue7f1\ue7f2\ue7f3\ue7f4\ue7f5\ue7f6\ue7f7\ue7f8\ue7f9\ue7fa\ue7fb\ue7fc\ue7fd\ue7fe\ue7ff\ue800\ue801\ue802\ue803\ue804\ue805\ue806\ue807\ue808\ue809\ue80a\ue80b\ue80c\ue80d\ue80e\ue80f\ue810\ue811\ue812\ue813\ue814\ue815\ue816\ue817\ue818\ue819\ue81a\ue81b\ue81c\ue81d\ue81e\ue81f\ue820\ue821\ue822\ue823\ue824\ue825\ue826\ue827\ue828\ue829\ue82a\ue82b\ue82c\ue82d\ue82e\ue82f\ue830\ue831\ue832\ue833\ue834\ue835\ue836\ue837\ue838\ue839\ue83a\ue83b\ue83c\ue83d\ue83e\ue83f\ue840\ue841\ue842\ue843\ue844\ue845\ue846\ue847\ue848\ue849\ue84a\ue84b\ue84c\ue84d\ue84e\ue84f\ue850\ue851\ue852\ue853\ue854\ue855\ue856\ue857\ue858\ue859\ue85a\ue85b\ue85c\ue85d\ue85e\ue85f\ue860\ue861\ue862\ue863\ue864\ue865\ue866\ue867\ue868\ue869\ue86a\ue86b\ue86c\ue86d\ue86e\ue86f\ue870\ue871\ue872\ue873\ue874\ue875\ue876\ue877\ue878\ue879\ue87a\ue87b\ue87c\ue87d\ue87e\ue87f\ue880\ue881\ue882\ue883\ue884\ue885\ue886\ue887\ue888\ue889\ue88a\ue88b\ue88c\ue88d\ue88e\ue88f\ue890\ue891\ue892\ue893\ue894\ue895\ue896\ue897\ue898\ue899\ue89a\ue89b\ue89c\ue89d\ue89e\ue89f\ue8a0\ue8a1\ue8a2\ue8a3\ue8a4\ue8a5\ue8a6\ue8a7\ue8a8\ue8a9\ue8aa\ue8ab\ue8ac\ue8ad\ue8ae\ue8af\ue8b0\ue8b1\ue8b2\ue8b3\ue8b4\ue8b5\ue8b6\ue8b7\ue8b8\ue8b9\ue8ba\ue8bb\ue8bc\ue8bd\ue8be\ue8bf\ue8c0\ue8c1\ue8c2\ue8c3\ue8c4\ue8c5\ue8c6\ue8c7\ue8c8\ue8c9\ue8ca\ue8cb\ue8cc\ue8cd\ue8ce\ue8cf\ue8d0\ue8d1\ue8d2\ue8d3\ue8d4\ue8d5\ue8d6\ue8d7\ue8d8\ue8d9\ue8da\ue8db\ue8dc\ue8dd\ue8de\ue8df\ue8e0\ue8e1\ue8e2\ue8e3\ue8e4\ue8e5\ue8e6\ue8e7\ue8e8\ue8e9\ue8ea\ue8eb\ue8ec\ue8ed\ue8ee\ue8ef\ue8f0\ue8f1\ue8f2\ue8f3\ue8f4\ue8f5\ue8f6\ue8f7\ue8f8\ue8f9\ue8fa\ue8fb\ue8fc\ue8fd\ue8fe\ue8ff\ue900\ue901\ue902\ue903\ue904\ue905\ue906\ue907\ue908\ue909\ue90a\ue90b\ue90c\ue90d\ue90e\ue90f\ue910\ue911\ue912\ue913\ue914\ue915\ue916\ue917\ue918\ue919\ue91a\ue91b\ue91c\ue91d\ue91e\ue91f\ue920\ue921\ue922\ue923\ue924\ue925\ue926\ue927\ue928\ue929\ue92a\ue92b\ue92c\ue92d\ue92e\ue92f\ue930\ue931\ue932\ue933\ue934\ue935\ue936\ue937\ue938\ue939\ue93a\ue93b\ue93c\ue93d\ue93e\ue93f\ue940\ue941\ue942\ue943\ue944\ue945\ue946\ue947\ue948\ue949\ue94a\ue94b\ue94c\ue94d\ue94e\ue94f\ue950\ue951\ue952\ue953\ue954\ue955\ue956\ue957\ue958\ue959\ue95a\ue95b\ue95c\ue95d\ue95e\ue95f\ue960\ue961\ue962\ue963\ue964\ue965\ue966\ue967\ue968\ue969\ue96a\ue96b\ue96c\ue96d\ue96e\ue96f\ue970\ue971\ue972\ue973\ue974\ue975\ue976\ue977\ue978\ue979\ue97a\ue97b\ue97c\ue97d\ue97e\ue97f\ue980\ue981\ue982\ue983\ue984\ue985\ue986\ue987\ue988\ue989\ue98a\ue98b\ue98c\ue98d\ue98e\ue98f\ue990\ue991\ue992\ue993\ue994\ue995\ue996\ue997\ue998\ue999\ue99a\ue99b\ue99c\ue99d\ue99e\ue99f\ue9a0\ue9a1\ue9a2\ue9a3\ue9a4\ue9a5\ue9a6\ue9a7\ue9a8\ue9a9\ue9aa\ue9ab\ue9ac\ue9ad\ue9ae\ue9af\ue9b0\ue9b1\ue9b2\ue9b3\ue9b4\ue9b5\ue9b6\ue9b7\ue9b8\ue9b9\ue9ba\ue9bb\ue9bc\ue9bd\ue9be\ue9bf\ue9c0\ue9c1\ue9c2\ue9c3\ue9c4\ue9c5\ue9c6\ue9c7\ue9c8\ue9c9\ue9ca\ue9cb\ue9cc\ue9cd\ue9ce\ue9cf\ue9d0\ue9d1\ue9d2\ue9d3\ue9d4\ue9d5\ue9d6\ue9d7\ue9d8\ue9d9\ue9da\ue9db\ue9dc\ue9dd\ue9de\ue9df\ue9e0\ue9e1\ue9e2\ue9e3\ue9e4\ue9e5\ue9e6\ue9e7\ue9e8\ue9e9\ue9ea\ue9eb\ue9ec\ue9ed\ue9ee\ue9ef\ue9f0\ue9f1\ue9f2\ue9f3\ue9f4\ue9f5\ue9f6\ue9f7\ue9f8\ue9f9\ue9fa\ue9fb\ue9fc\ue9fd\ue9fe\ue9ff\uea00\uea01\uea02\uea03\uea04\uea05\uea06\uea07\uea08\uea09\uea0a\uea0b\uea0c\uea0d\uea0e\uea0f\uea10\uea11\uea12\uea13\uea14\uea15\uea16\uea17\uea18\uea19\uea1a\uea1b\uea1c\uea1d\uea1e\uea1f\uea20\uea21\uea22\uea23\uea24\uea25\uea26\uea27\uea28\uea29\uea2a\uea2b\uea2c\uea2d\uea2e\uea2f\uea30\uea31\uea32\uea33\uea34\uea35\uea36\uea37\uea38\uea39\uea3a\uea3b\uea3c\uea3d\uea3e\uea3f\uea40\uea41\uea42\uea43\uea44\uea45\uea46\uea47\uea48\uea49\uea4a\uea4b\uea4c\uea4d\uea4e\uea4f\uea50\uea51\uea52\uea53\uea54\uea55\uea56\uea57\uea58\uea59\uea5a\uea5b\uea5c\uea5d\uea5e\uea5f\uea60\uea61\uea62\uea63\uea64\uea65\uea66\uea67\uea68\uea69\uea6a\uea6b\uea6c\uea6d\uea6e\uea6f\uea70\uea71\uea72\uea73\uea74\uea75\uea76\uea77\uea78\uea79\uea7a\uea7b\uea7c\uea7d\uea7e\uea7f\uea80\uea81\uea82\uea83\uea84\uea85\uea86\uea87\uea88\uea89\uea8a\uea8b\uea8c\uea8d\uea8e\uea8f\uea90\uea91\uea92\uea93\uea94\uea95\uea96\uea97\uea98\uea99\uea9a\uea9b\uea9c\uea9d\uea9e\uea9f\ueaa0\ueaa1\ueaa2\ueaa3\ueaa4\ueaa5\ueaa6\ueaa7\ueaa8\ueaa9\ueaaa\ueaab\ueaac\ueaad\ueaae\ueaaf\ueab0\ueab1\ueab2\ueab3\ueab4\ueab5\ueab6\ueab7\ueab8\ueab9\ueaba\ueabb\ueabc\ueabd\ueabe\ueabf\ueac0\ueac1\ueac2\ueac3\ueac4\ueac5\ueac6\ueac7\ueac8\ueac9\ueaca\ueacb\ueacc\ueacd\ueace\ueacf\uead0\uead1\uead2\uead3\uead4\uead5\uead6\uead7\uead8\uead9\ueada\ueadb\ueadc\ueadd\ueade\ueadf\ueae0\ueae1\ueae2\ueae3\ueae4\ueae5\ueae6\ueae7\ueae8\ueae9\ueaea\ueaeb\ueaec\ueaed\ueaee\ueaef\ueaf0\ueaf1\ueaf2\ueaf3\ueaf4\ueaf5\ueaf6\ueaf7\ueaf8\ueaf9\ueafa\ueafb\ueafc\ueafd\ueafe\ueaff\ueb00\ueb01\ueb02\ueb03\ueb04\ueb05\ueb06\ueb07\ueb08\ueb09\ueb0a\ueb0b\ueb0c\ueb0d\ueb0e\ueb0f\ueb10\ueb11\ueb12\ueb13\ueb14\ueb15\ueb16\ueb17\ueb18\ueb19\ueb1a\ueb1b\ueb1c\ueb1d\ueb1e\ueb1f\ueb20\ueb21\ueb22\ueb23\ueb24\ueb25\ueb26\ueb27\ueb28\ueb29\ueb2a\ueb2b\ueb2c\ueb2d\ueb2e\ueb2f\ueb30\ueb31\ueb32\ueb33\ueb34\ueb35\ueb36\ueb37\ueb38\ueb39\ueb3a\ueb3b\ueb3c\ueb3d\ueb3e\ueb3f\ueb40\ueb41\ueb42\ueb43\ueb44\ueb45\ueb46\ueb47\ueb48\ueb49\ueb4a\ueb4b\ueb4c\ueb4d\ueb4e\ueb4f\ueb50\ueb51\ueb52\ueb53\ueb54\ueb55\ueb56\ueb57\ueb58\ueb59\ueb5a\ueb5b\ueb5c\ueb5d\ueb5e\ueb5f\ueb60\ueb61\ueb62\ueb63\ueb64\ueb65\ueb66\ueb67\ueb68\ueb69\ueb6a\ueb6b\ueb6c\ueb6d\ueb6e\ueb6f\ueb70\ueb71\ueb72\ueb73\ueb74\ueb75\ueb76\ueb77\ueb78\ueb79\ueb7a\ueb7b\ueb7c\ueb7d\ueb7e\ueb7f\ueb80\ueb81\ueb82\ueb83\ueb84\ueb85\ueb86\ueb87\ueb88\ueb89\ueb8a\ueb8b\ueb8c\ueb8d\ueb8e\ueb8f\ueb90\ueb91\ueb92\ueb93\ueb94\ueb95\ueb96\ueb97\ueb98\ueb99\ueb9a\ueb9b\ueb9c\ueb9d\ueb9e\ueb9f\ueba0\ueba1\ueba2\ueba3\ueba4\ueba5\ueba6\ueba7\ueba8\ueba9\uebaa\uebab\uebac\uebad\uebae\uebaf\uebb0\uebb1\uebb2\uebb3\uebb4\uebb5\uebb6\uebb7\uebb8\uebb9\uebba\uebbb\uebbc\uebbd\uebbe\uebbf\uebc0\uebc1\uebc2\uebc3\uebc4\uebc5\uebc6\uebc7\uebc8\uebc9\uebca\uebcb\uebcc\uebcd\uebce\uebcf\uebd0\uebd1\uebd2\uebd3\uebd4\uebd5\uebd6\uebd7\uebd8\uebd9\uebda\uebdb\uebdc\uebdd\uebde\uebdf\uebe0\uebe1\uebe2\uebe3\uebe4\uebe5\uebe6\uebe7\uebe8\uebe9\uebea\uebeb\uebec\uebed\uebee\uebef\uebf0\uebf1\uebf2\uebf3\uebf4\uebf5\uebf6\uebf7\uebf8\uebf9\uebfa\uebfb\uebfc\uebfd\uebfe\uebff\uec00\uec01\uec02\uec03\uec04\uec05\uec06\uec07\uec08\uec09\uec0a\uec0b\uec0c\uec0d\uec0e\uec0f\uec10\uec11\uec12\uec13\uec14\uec15\uec16\uec17\uec18\uec19\uec1a\uec1b\uec1c\uec1d\uec1e\uec1f\uec20\uec21\uec22\uec23\uec24\uec25\uec26\uec27\uec28\uec29\uec2a\uec2b\uec2c\uec2d\uec2e\uec2f\uec30\uec31\uec32\uec33\uec34\uec35\uec36\uec37\uec38\uec39\uec3a\uec3b\uec3c\uec3d\uec3e\uec3f\uec40\uec41\uec42\uec43\uec44\uec45\uec46\uec47\uec48\uec49\uec4a\uec4b\uec4c\uec4d\uec4e\uec4f\uec50\uec51\uec52\uec53\uec54\uec55\uec56\uec57\uec58\uec59\uec5a\uec5b\uec5c\uec5d\uec5e\uec5f\uec60\uec61\uec62\uec63\uec64\uec65\uec66\uec67\uec68\uec69\uec6a\uec6b\uec6c\uec6d\uec6e\uec6f\uec70\uec71\uec72\uec73\uec74\uec75\uec76\uec77\uec78\uec79\uec7a\uec7b\uec7c\uec7d\uec7e\uec7f\uec80\uec81\uec82\uec83\uec84\uec85\uec86\uec87\uec88\uec89\uec8a\uec8b\uec8c\uec8d\uec8e\uec8f\uec90\uec91\uec92\uec93\uec94\uec95\uec96\uec97\uec98\uec99\uec9a\uec9b\uec9c\uec9d\uec9e\uec9f\ueca0\ueca1\ueca2\ueca3\ueca4\ueca5\ueca6\ueca7\ueca8\ueca9\uecaa\uecab\uecac\uecad\uecae\uecaf\uecb0\uecb1\uecb2\uecb3\uecb4\uecb5\uecb6\uecb7\uecb8\uecb9\uecba\uecbb\uecbc\uecbd\uecbe\uecbf\uecc0\uecc1\uecc2\uecc3\uecc4\uecc5\uecc6\uecc7\uecc8\uecc9\uecca\ueccb\ueccc\ueccd\uecce\ueccf\uecd0\uecd1\uecd2\uecd3\uecd4\uecd5\uecd6\uecd7\uecd8\uecd9\uecda\uecdb\uecdc\uecdd\uecde\uecdf\uece0\uece1\uece2\uece3\uece4\uece5\uece6\uece7\uece8\uece9\uecea\ueceb\uecec\ueced\uecee\uecef\uecf0\uecf1\uecf2\uecf3\uecf4\uecf5\uecf6\uecf7\uecf8\uecf9\uecfa\uecfb\uecfc\uecfd\uecfe\uecff\ued00\ued01\ued02\ued03\ued04\ued05\ued06\ued07\ued08\ued09\ued0a\ued0b\ued0c\ued0d\ued0e\ued0f\ued10\ued11\ued12\ued13\ued14\ued15\ued16\ued17\ued18\ued19\ued1a\ued1b\ued1c\ued1d\ued1e\ued1f\ued20\ued21\ued22\ued23\ued24\ued25\ued26\ued27\ued28\ued29\ued2a\ued2b\ued2c\ued2d\ued2e\ued2f\ued30\ued31\ued32\ued33\ued34\ued35\ued36\ued37\ued38\ued39\ued3a\ued3b\ued3c\ued3d\ued3e\ued3f\ued40\ued41\ued42\ued43\ued44\ued45\ued46\ued47\ued48\ued49\ued4a\ued4b\ued4c\ued4d\ued4e\ued4f\ued50\ued51\ued52\ued53\ued54\ued55\ued56\ued57\ued58\ued59\ued5a\ued5b\ued5c\ued5d\ued5e\ued5f\ued60\ued61\ued62\ued63\ued64\ued65\ued66\ued67\ued68\ued69\ued6a\ued6b\ued6c\ued6d\ued6e\ued6f\ued70\ued71\ued72\ued73\ued74\ued75\ued76\ued77\ued78\ued79\ued7a\ued7b\ued7c\ued7d\ued7e\ued7f\ued80\ued81\ued82\ued83\ued84\ued85\ued86\ued87\ued88\ued89\ued8a\ued8b\ued8c\ued8d\ued8e\ued8f\ued90\ued91\ued92\ued93\ued94\ued95\ued96\ued97\ued98\ued99\ued9a\ued9b\ued9c\ued9d\ued9e\ued9f\ueda0\ueda1\ueda2\ueda3\ueda4\ueda5\ueda6\ueda7\ueda8\ueda9\uedaa\uedab\uedac\uedad\uedae\uedaf\uedb0\uedb1\uedb2\uedb3\uedb4\uedb5\uedb6\uedb7\uedb8\uedb9\uedba\uedbb\uedbc\uedbd\uedbe\uedbf\uedc0\uedc1\uedc2\uedc3\uedc4\uedc5\uedc6\uedc7\uedc8\uedc9\uedca\uedcb\uedcc\uedcd\uedce\uedcf\uedd0\uedd1\uedd2\uedd3\uedd4\uedd5\uedd6\uedd7\uedd8\uedd9\uedda\ueddb\ueddc\ueddd\uedde\ueddf\uede0\uede1\uede2\uede3\uede4\uede5\uede6\uede7\uede8\uede9\uedea\uedeb\uedec\ueded\uedee\uedef\uedf0\uedf1\uedf2\uedf3\uedf4\uedf5\uedf6\uedf7\uedf8\uedf9\uedfa\uedfb\uedfc\uedfd\uedfe\uedff\uee00\uee01\uee02\uee03\uee04\uee05\uee06\uee07\uee08\uee09\uee0a\uee0b\uee0c\uee0d\uee0e\uee0f\uee10\uee11\uee12\uee13\uee14\uee15\uee16\uee17\uee18\uee19\uee1a\uee1b\uee1c\uee1d\uee1e\uee1f\uee20\uee21\uee22\uee23\uee24\uee25\uee26\uee27\uee28\uee29\uee2a\uee2b\uee2c\uee2d\uee2e\uee2f\uee30\uee31\uee32\uee33\uee34\uee35\uee36\uee37\uee38\uee39\uee3a\uee3b\uee3c\uee3d\uee3e\uee3f\uee40\uee41\uee42\uee43\uee44\uee45\uee46\uee47\uee48\uee49\uee4a\uee4b\uee4c\uee4d\uee4e\uee4f\uee50\uee51\uee52\uee53\uee54\uee55\uee56\uee57\uee58\uee59\uee5a\uee5b\uee5c\uee5d\uee5e\uee5f\uee60\uee61\uee62\uee63\uee64\uee65\uee66\uee67\uee68\uee69\uee6a\uee6b\uee6c\uee6d\uee6e\uee6f\uee70\uee71\uee72\uee73\uee74\uee75\uee76\uee77\uee78\uee79\uee7a\uee7b\uee7c\uee7d\uee7e\uee7f\uee80\uee81\uee82\uee83\uee84\uee85\uee86\uee87\uee88\uee89\uee8a\uee8b\uee8c\uee8d\uee8e\uee8f\uee90\uee91\uee92\uee93\uee94\uee95\uee96\uee97\uee98\uee99\uee9a\uee9b\uee9c\uee9d\uee9e\uee9f\ueea0\ueea1\ueea2\ueea3\ueea4\ueea5\ueea6\ueea7\ueea8\ueea9\ueeaa\ueeab\ueeac\ueead\ueeae\ueeaf\ueeb0\ueeb1\ueeb2\ueeb3\ueeb4\ueeb5\ueeb6\ueeb7\ueeb8\ueeb9\ueeba\ueebb\ueebc\ueebd\ueebe\ueebf\ueec0\ueec1\ueec2\ueec3\ueec4\ueec5\ueec6\ueec7\ueec8\ueec9\ueeca\ueecb\ueecc\ueecd\ueece\ueecf\ueed0\ueed1\ueed2\ueed3\ueed4\ueed5\ueed6\ueed7\ueed8\ueed9\ueeda\ueedb\ueedc\ueedd\ueede\ueedf\ueee0\ueee1\ueee2\ueee3\ueee4\ueee5\ueee6\ueee7\ueee8\ueee9\ueeea\ueeeb\ueeec\ueeed\ueeee\ueeef\ueef0\ueef1\ueef2\ueef3\ueef4\ueef5\ueef6\ueef7\ueef8\ueef9\ueefa\ueefb\ueefc\ueefd\ueefe\ueeff\uef00\uef01\uef02\uef03\uef04\uef05\uef06\uef07\uef08\uef09\uef0a\uef0b\uef0c\uef0d\uef0e\uef0f\uef10\uef11\uef12\uef13\uef14\uef15\uef16\uef17\uef18\uef19\uef1a\uef1b\uef1c\uef1d\uef1e\uef1f\uef20\uef21\uef22\uef23\uef24\uef25\uef26\uef27\uef28\uef29\uef2a\uef2b\uef2c\uef2d\uef2e\uef2f\uef30\uef31\uef32\uef33\uef34\uef35\uef36\uef37\uef38\uef39\uef3a\uef3b\uef3c\uef3d\uef3e\uef3f\uef40\uef41\uef42\uef43\uef44\uef45\uef46\uef47\uef48\uef49\uef4a\uef4b\uef4c\uef4d\uef4e\uef4f\uef50\uef51\uef52\uef53\uef54\uef55\uef56\uef57\uef58\uef59\uef5a\uef5b\uef5c\uef5d\uef5e\uef5f\uef60\uef61\uef62\uef63\uef64\uef65\uef66\uef67\uef68\uef69\uef6a\uef6b\uef6c\uef6d\uef6e\uef6f\uef70\uef71\uef72\uef73\uef74\uef75\uef76\uef77\uef78\uef79\uef7a\uef7b\uef7c\uef7d\uef7e\uef7f\uef80\uef81\uef82\uef83\uef84\uef85\uef86\uef87\uef88\uef89\uef8a\uef8b\uef8c\uef8d\uef8e\uef8f\uef90\uef91\uef92\uef93\uef94\uef95\uef96\uef97\uef98\uef99\uef9a\uef9b\uef9c\uef9d\uef9e\uef9f\uefa0\uefa1\uefa2\uefa3\uefa4\uefa5\uefa6\uefa7\uefa8\uefa9\uefaa\uefab\uefac\uefad\uefae\uefaf\uefb0\uefb1\uefb2\uefb3\uefb4\uefb5\uefb6\uefb7\uefb8\uefb9\uefba\uefbb\uefbc\uefbd\uefbe\uefbf\uefc0\uefc1\uefc2\uefc3\uefc4\uefc5\uefc6\uefc7\uefc8\uefc9\uefca\uefcb\uefcc\uefcd\uefce\uefcf\uefd0\uefd1\uefd2\uefd3\uefd4\uefd5\uefd6\uefd7\uefd8\uefd9\uefda\uefdb\uefdc\uefdd\uefde\uefdf\uefe0\uefe1\uefe2\uefe3\uefe4\uefe5\uefe6\uefe7\uefe8\uefe9\uefea\uefeb\uefec\uefed\uefee\uefef\ueff0\ueff1\ueff2\ueff3\ueff4\ueff5\ueff6\ueff7\ueff8\ueff9\ueffa\ueffb\ueffc\ueffd\ueffe\uefff\uf000\uf001\uf002\uf003\uf004\uf005\uf006\uf007\uf008\uf009\uf00a\uf00b\uf00c\uf00d\uf00e\uf00f\uf010\uf011\uf012\uf013\uf014\uf015\uf016\uf017\uf018\uf019\uf01a\uf01b\uf01c\uf01d\uf01e\uf01f\uf020\uf021\uf022\uf023\uf024\uf025\uf026\uf027\uf028\uf029\uf02a\uf02b\uf02c\uf02d\uf02e\uf02f\uf030\uf031\uf032\uf033\uf034\uf035\uf036\uf037\uf038\uf039\uf03a\uf03b\uf03c\uf03d\uf03e\uf03f\uf040\uf041\uf042\uf043\uf044\uf045\uf046\uf047\uf048\uf049\uf04a\uf04b\uf04c\uf04d\uf04e\uf04f\uf050\uf051\uf052\uf053\uf054\uf055\uf056\uf057\uf058\uf059\uf05a\uf05b\uf05c\uf05d\uf05e\uf05f\uf060\uf061\uf062\uf063\uf064\uf065\uf066\uf067\uf068\uf069\uf06a\uf06b\uf06c\uf06d\uf06e\uf06f\uf070\uf071\uf072\uf073\uf074\uf075\uf076\uf077\uf078\uf079\uf07a\uf07b\uf07c\uf07d\uf07e\uf07f\uf080\uf081\uf082\uf083\uf084\uf085\uf086\uf087\uf088\uf089\uf08a\uf08b\uf08c\uf08d\uf08e\uf08f\uf090\uf091\uf092\uf093\uf094\uf095\uf096\uf097\uf098\uf099\uf09a\uf09b\uf09c\uf09d\uf09e\uf09f\uf0a0\uf0a1\uf0a2\uf0a3\uf0a4\uf0a5\uf0a6\uf0a7\uf0a8\uf0a9\uf0aa\uf0ab\uf0ac\uf0ad\uf0ae\uf0af\uf0b0\uf0b1\uf0b2\uf0b3\uf0b4\uf0b5\uf0b6\uf0b7\uf0b8\uf0b9\uf0ba\uf0bb\uf0bc\uf0bd\uf0be\uf0bf\uf0c0\uf0c1\uf0c2\uf0c3\uf0c4\uf0c5\uf0c6\uf0c7\uf0c8\uf0c9\uf0ca\uf0cb\uf0cc\uf0cd\uf0ce\uf0cf\uf0d0\uf0d1\uf0d2\uf0d3\uf0d4\uf0d5\uf0d6\uf0d7\uf0d8\uf0d9\uf0da\uf0db\uf0dc\uf0dd\uf0de\uf0df\uf0e0\uf0e1\uf0e2\uf0e3\uf0e4\uf0e5\uf0e6\uf0e7\uf0e8\uf0e9\uf0ea\uf0eb\uf0ec\uf0ed\uf0ee\uf0ef\uf0f0\uf0f1\uf0f2\uf0f3\uf0f4\uf0f5\uf0f6\uf0f7\uf0f8\uf0f9\uf0fa\uf0fb\uf0fc\uf0fd\uf0fe\uf0ff\uf100\uf101\uf102\uf103\uf104\uf105\uf106\uf107\uf108\uf109\uf10a\uf10b\uf10c\uf10d\uf10e\uf10f\uf110\uf111\uf112\uf113\uf114\uf115\uf116\uf117\uf118\uf119\uf11a\uf11b\uf11c\uf11d\uf11e\uf11f\uf120\uf121\uf122\uf123\uf124\uf125\uf126\uf127\uf128\uf129\uf12a\uf12b\uf12c\uf12d\uf12e\uf12f\uf130\uf131\uf132\uf133\uf134\uf135\uf136\uf137\uf138\uf139\uf13a\uf13b\uf13c\uf13d\uf13e\uf13f\uf140\uf141\uf142\uf143\uf144\uf145\uf146\uf147\uf148\uf149\uf14a\uf14b\uf14c\uf14d\uf14e\uf14f\uf150\uf151\uf152\uf153\uf154\uf155\uf156\uf157\uf158\uf159\uf15a\uf15b\uf15c\uf15d\uf15e\uf15f\uf160\uf161\uf162\uf163\uf164\uf165\uf166\uf167\uf168\uf169\uf16a\uf16b\uf16c\uf16d\uf16e\uf16f\uf170\uf171\uf172\uf173\uf174\uf175\uf176\uf177\uf178\uf179\uf17a\uf17b\uf17c\uf17d\uf17e\uf17f\uf180\uf181\uf182\uf183\uf184\uf185\uf186\uf187\uf188\uf189\uf18a\uf18b\uf18c\uf18d\uf18e\uf18f\uf190\uf191\uf192\uf193\uf194\uf195\uf196\uf197\uf198\uf199\uf19a\uf19b\uf19c\uf19d\uf19e\uf19f\uf1a0\uf1a1\uf1a2\uf1a3\uf1a4\uf1a5\uf1a6\uf1a7\uf1a8\uf1a9\uf1aa\uf1ab\uf1ac\uf1ad\uf1ae\uf1af\uf1b0\uf1b1\uf1b2\uf1b3\uf1b4\uf1b5\uf1b6\uf1b7\uf1b8\uf1b9\uf1ba\uf1bb\uf1bc\uf1bd\uf1be\uf1bf\uf1c0\uf1c1\uf1c2\uf1c3\uf1c4\uf1c5\uf1c6\uf1c7\uf1c8\uf1c9\uf1ca\uf1cb\uf1cc\uf1cd\uf1ce\uf1cf\uf1d0\uf1d1\uf1d2\uf1d3\uf1d4\uf1d5\uf1d6\uf1d7\uf1d8\uf1d9\uf1da\uf1db\uf1dc\uf1dd\uf1de\uf1df\uf1e0\uf1e1\uf1e2\uf1e3\uf1e4\uf1e5\uf1e6\uf1e7\uf1e8\uf1e9\uf1ea\uf1eb\uf1ec\uf1ed\uf1ee\uf1ef\uf1f0\uf1f1\uf1f2\uf1f3\uf1f4\uf1f5\uf1f6\uf1f7\uf1f8\uf1f9\uf1fa\uf1fb\uf1fc\uf1fd\uf1fe\uf1ff\uf200\uf201\uf202\uf203\uf204\uf205\uf206\uf207\uf208\uf209\uf20a\uf20b\uf20c\uf20d\uf20e\uf20f\uf210\uf211\uf212\uf213\uf214\uf215\uf216\uf217\uf218\uf219\uf21a\uf21b\uf21c\uf21d\uf21e\uf21f\uf220\uf221\uf222\uf223\uf224\uf225\uf226\uf227\uf228\uf229\uf22a\uf22b\uf22c\uf22d\uf22e\uf22f\uf230\uf231\uf232\uf233\uf234\uf235\uf236\uf237\uf238\uf239\uf23a\uf23b\uf23c\uf23d\uf23e\uf23f\uf240\uf241\uf242\uf243\uf244\uf245\uf246\uf247\uf248\uf249\uf24a\uf24b\uf24c\uf24d\uf24e\uf24f\uf250\uf251\uf252\uf253\uf254\uf255\uf256\uf257\uf258\uf259\uf25a\uf25b\uf25c\uf25d\uf25e\uf25f\uf260\uf261\uf262\uf263\uf264\uf265\uf266\uf267\uf268\uf269\uf26a\uf26b\uf26c\uf26d\uf26e\uf26f\uf270\uf271\uf272\uf273\uf274\uf275\uf276\uf277\uf278\uf279\uf27a\uf27b\uf27c\uf27d\uf27e\uf27f\uf280\uf281\uf282\uf283\uf284\uf285\uf286\uf287\uf288\uf289\uf28a\uf28b\uf28c\uf28d\uf28e\uf28f\uf290\uf291\uf292\uf293\uf294\uf295\uf296\uf297\uf298\uf299\uf29a\uf29b\uf29c\uf29d\uf29e\uf29f\uf2a0\uf2a1\uf2a2\uf2a3\uf2a4\uf2a5\uf2a6\uf2a7\uf2a8\uf2a9\uf2aa\uf2ab\uf2ac\uf2ad\uf2ae\uf2af\uf2b0\uf2b1\uf2b2\uf2b3\uf2b4\uf2b5\uf2b6\uf2b7\uf2b8\uf2b9\uf2ba\uf2bb\uf2bc\uf2bd\uf2be\uf2bf\uf2c0\uf2c1\uf2c2\uf2c3\uf2c4\uf2c5\uf2c6\uf2c7\uf2c8\uf2c9\uf2ca\uf2cb\uf2cc\uf2cd\uf2ce\uf2cf\uf2d0\uf2d1\uf2d2\uf2d3\uf2d4\uf2d5\uf2d6\uf2d7\uf2d8\uf2d9\uf2da\uf2db\uf2dc\uf2dd\uf2de\uf2df\uf2e0\uf2e1\uf2e2\uf2e3\uf2e4\uf2e5\uf2e6\uf2e7\uf2e8\uf2e9\uf2ea\uf2eb\uf2ec\uf2ed\uf2ee\uf2ef\uf2f0\uf2f1\uf2f2\uf2f3\uf2f4\uf2f5\uf2f6\uf2f7\uf2f8\uf2f9\uf2fa\uf2fb\uf2fc\uf2fd\uf2fe\uf2ff\uf300\uf301\uf302\uf303\uf304\uf305\uf306\uf307\uf308\uf309\uf30a\uf30b\uf30c\uf30d\uf30e\uf30f\uf310\uf311\uf312\uf313\uf314\uf315\uf316\uf317\uf318\uf319\uf31a\uf31b\uf31c\uf31d\uf31e\uf31f\uf320\uf321\uf322\uf323\uf324\uf325\uf326\uf327\uf328\uf329\uf32a\uf32b\uf32c\uf32d\uf32e\uf32f\uf330\uf331\uf332\uf333\uf334\uf335\uf336\uf337\uf338\uf339\uf33a\uf33b\uf33c\uf33d\uf33e\uf33f\uf340\uf341\uf342\uf343\uf344\uf345\uf346\uf347\uf348\uf349\uf34a\uf34b\uf34c\uf34d\uf34e\uf34f\uf350\uf351\uf352\uf353\uf354\uf355\uf356\uf357\uf358\uf359\uf35a\uf35b\uf35c\uf35d\uf35e\uf35f\uf360\uf361\uf362\uf363\uf364\uf365\uf366\uf367\uf368\uf369\uf36a\uf36b\uf36c\uf36d\uf36e\uf36f\uf370\uf371\uf372\uf373\uf374\uf375\uf376\uf377\uf378\uf379\uf37a\uf37b\uf37c\uf37d\uf37e\uf37f\uf380\uf381\uf382\uf383\uf384\uf385\uf386\uf387\uf388\uf389\uf38a\uf38b\uf38c\uf38d\uf38e\uf38f\uf390\uf391\uf392\uf393\uf394\uf395\uf396\uf397\uf398\uf399\uf39a\uf39b\uf39c\uf39d\uf39e\uf39f\uf3a0\uf3a1\uf3a2\uf3a3\uf3a4\uf3a5\uf3a6\uf3a7\uf3a8\uf3a9\uf3aa\uf3ab\uf3ac\uf3ad\uf3ae\uf3af\uf3b0\uf3b1\uf3b2\uf3b3\uf3b4\uf3b5\uf3b6\uf3b7\uf3b8\uf3b9\uf3ba\uf3bb\uf3bc\uf3bd\uf3be\uf3bf\uf3c0\uf3c1\uf3c2\uf3c3\uf3c4\uf3c5\uf3c6\uf3c7\uf3c8\uf3c9\uf3ca\uf3cb\uf3cc\uf3cd\uf3ce\uf3cf\uf3d0\uf3d1\uf3d2\uf3d3\uf3d4\uf3d5\uf3d6\uf3d7\uf3d8\uf3d9\uf3da\uf3db\uf3dc\uf3dd\uf3de\uf3df\uf3e0\uf3e1\uf3e2\uf3e3\uf3e4\uf3e5\uf3e6\uf3e7\uf3e8\uf3e9\uf3ea\uf3eb\uf3ec\uf3ed\uf3ee\uf3ef\uf3f0\uf3f1\uf3f2\uf3f3\uf3f4\uf3f5\uf3f6\uf3f7\uf3f8\uf3f9\uf3fa\uf3fb\uf3fc\uf3fd\uf3fe\uf3ff\uf400\uf401\uf402\uf403\uf404\uf405\uf406\uf407\uf408\uf409\uf40a\uf40b\uf40c\uf40d\uf40e\uf40f\uf410\uf411\uf412\uf413\uf414\uf415\uf416\uf417\uf418\uf419\uf41a\uf41b\uf41c\uf41d\uf41e\uf41f\uf420\uf421\uf422\uf423\uf424\uf425\uf426\uf427\uf428\uf429\uf42a\uf42b\uf42c\uf42d\uf42e\uf42f\uf430\uf431\uf432\uf433\uf434\uf435\uf436\uf437\uf438\uf439\uf43a\uf43b\uf43c\uf43d\uf43e\uf43f\uf440\uf441\uf442\uf443\uf444\uf445\uf446\uf447\uf448\uf449\uf44a\uf44b\uf44c\uf44d\uf44e\uf44f\uf450\uf451\uf452\uf453\uf454\uf455\uf456\uf457\uf458\uf459\uf45a\uf45b\uf45c\uf45d\uf45e\uf45f\uf460\uf461\uf462\uf463\uf464\uf465\uf466\uf467\uf468\uf469\uf46a\uf46b\uf46c\uf46d\uf46e\uf46f\uf470\uf471\uf472\uf473\uf474\uf475\uf476\uf477\uf478\uf479\uf47a\uf47b\uf47c\uf47d\uf47e\uf47f\uf480\uf481\uf482\uf483\uf484\uf485\uf486\uf487\uf488\uf489\uf48a\uf48b\uf48c\uf48d\uf48e\uf48f\uf490\uf491\uf492\uf493\uf494\uf495\uf496\uf497\uf498\uf499\uf49a\uf49b\uf49c\uf49d\uf49e\uf49f\uf4a0\uf4a1\uf4a2\uf4a3\uf4a4\uf4a5\uf4a6\uf4a7\uf4a8\uf4a9\uf4aa\uf4ab\uf4ac\uf4ad\uf4ae\uf4af\uf4b0\uf4b1\uf4b2\uf4b3\uf4b4\uf4b5\uf4b6\uf4b7\uf4b8\uf4b9\uf4ba\uf4bb\uf4bc\uf4bd\uf4be\uf4bf\uf4c0\uf4c1\uf4c2\uf4c3\uf4c4\uf4c5\uf4c6\uf4c7\uf4c8\uf4c9\uf4ca\uf4cb\uf4cc\uf4cd\uf4ce\uf4cf\uf4d0\uf4d1\uf4d2\uf4d3\uf4d4\uf4d5\uf4d6\uf4d7\uf4d8\uf4d9\uf4da\uf4db\uf4dc\uf4dd\uf4de\uf4df\uf4e0\uf4e1\uf4e2\uf4e3\uf4e4\uf4e5\uf4e6\uf4e7\uf4e8\uf4e9\uf4ea\uf4eb\uf4ec\uf4ed\uf4ee\uf4ef\uf4f0\uf4f1\uf4f2\uf4f3\uf4f4\uf4f5\uf4f6\uf4f7\uf4f8\uf4f9\uf4fa\uf4fb\uf4fc\uf4fd\uf4fe\uf4ff\uf500\uf501\uf502\uf503\uf504\uf505\uf506\uf507\uf508\uf509\uf50a\uf50b\uf50c\uf50d\uf50e\uf50f\uf510\uf511\uf512\uf513\uf514\uf515\uf516\uf517\uf518\uf519\uf51a\uf51b\uf51c\uf51d\uf51e\uf51f\uf520\uf521\uf522\uf523\uf524\uf525\uf526\uf527\uf528\uf529\uf52a\uf52b\uf52c\uf52d\uf52e\uf52f\uf530\uf531\uf532\uf533\uf534\uf535\uf536\uf537\uf538\uf539\uf53a\uf53b\uf53c\uf53d\uf53e\uf53f\uf540\uf541\uf542\uf543\uf544\uf545\uf546\uf547\uf548\uf549\uf54a\uf54b\uf54c\uf54d\uf54e\uf54f\uf550\uf551\uf552\uf553\uf554\uf555\uf556\uf557\uf558\uf559\uf55a\uf55b\uf55c\uf55d\uf55e\uf55f\uf560\uf561\uf562\uf563\uf564\uf565\uf566\uf567\uf568\uf569\uf56a\uf56b\uf56c\uf56d\uf56e\uf56f\uf570\uf571\uf572\uf573\uf574\uf575\uf576\uf577\uf578\uf579\uf57a\uf57b\uf57c\uf57d\uf57e\uf57f\uf580\uf581\uf582\uf583\uf584\uf585\uf586\uf587\uf588\uf589\uf58a\uf58b\uf58c\uf58d\uf58e\uf58f\uf590\uf591\uf592\uf593\uf594\uf595\uf596\uf597\uf598\uf599\uf59a\uf59b\uf59c\uf59d\uf59e\uf59f\uf5a0\uf5a1\uf5a2\uf5a3\uf5a4\uf5a5\uf5a6\uf5a7\uf5a8\uf5a9\uf5aa\uf5ab\uf5ac\uf5ad\uf5ae\uf5af\uf5b0\uf5b1\uf5b2\uf5b3\uf5b4\uf5b5\uf5b6\uf5b7\uf5b8\uf5b9\uf5ba\uf5bb\uf5bc\uf5bd\uf5be\uf5bf\uf5c0\uf5c1\uf5c2\uf5c3\uf5c4\uf5c5\uf5c6\uf5c7\uf5c8\uf5c9\uf5ca\uf5cb\uf5cc\uf5cd\uf5ce\uf5cf\uf5d0\uf5d1\uf5d2\uf5d3\uf5d4\uf5d5\uf5d6\uf5d7\uf5d8\uf5d9\uf5da\uf5db\uf5dc\uf5dd\uf5de\uf5df\uf5e0\uf5e1\uf5e2\uf5e3\uf5e4\uf5e5\uf5e6\uf5e7\uf5e8\uf5e9\uf5ea\uf5eb\uf5ec\uf5ed\uf5ee\uf5ef\uf5f0\uf5f1\uf5f2\uf5f3\uf5f4\uf5f5\uf5f6\uf5f7\uf5f8\uf5f9\uf5fa\uf5fb\uf5fc\uf5fd\uf5fe\uf5ff\uf600\uf601\uf602\uf603\uf604\uf605\uf606\uf607\uf608\uf609\uf60a\uf60b\uf60c\uf60d\uf60e\uf60f\uf610\uf611\uf612\uf613\uf614\uf615\uf616\uf617\uf618\uf619\uf61a\uf61b\uf61c\uf61d\uf61e\uf61f\uf620\uf621\uf622\uf623\uf624\uf625\uf626\uf627\uf628\uf629\uf62a\uf62b\uf62c\uf62d\uf62e\uf62f\uf630\uf631\uf632\uf633\uf634\uf635\uf636\uf637\uf638\uf639\uf63a\uf63b\uf63c\uf63d\uf63e\uf63f\uf640\uf641\uf642\uf643\uf644\uf645\uf646\uf647\uf648\uf649\uf64a\uf64b\uf64c\uf64d\uf64e\uf64f\uf650\uf651\uf652\uf653\uf654\uf655\uf656\uf657\uf658\uf659\uf65a\uf65b\uf65c\uf65d\uf65e\uf65f\uf660\uf661\uf662\uf663\uf664\uf665\uf666\uf667\uf668\uf669\uf66a\uf66b\uf66c\uf66d\uf66e\uf66f\uf670\uf671\uf672\uf673\uf674\uf675\uf676\uf677\uf678\uf679\uf67a\uf67b\uf67c\uf67d\uf67e\uf67f\uf680\uf681\uf682\uf683\uf684\uf685\uf686\uf687\uf688\uf689\uf68a\uf68b\uf68c\uf68d\uf68e\uf68f\uf690\uf691\uf692\uf693\uf694\uf695\uf696\uf697\uf698\uf699\uf69a\uf69b\uf69c\uf69d\uf69e\uf69f\uf6a0\uf6a1\uf6a2\uf6a3\uf6a4\uf6a5\uf6a6\uf6a7\uf6a8\uf6a9\uf6aa\uf6ab\uf6ac\uf6ad\uf6ae\uf6af\uf6b0\uf6b1\uf6b2\uf6b3\uf6b4\uf6b5\uf6b6\uf6b7\uf6b8\uf6b9\uf6ba\uf6bb\uf6bc\uf6bd\uf6be\uf6bf\uf6c0\uf6c1\uf6c2\uf6c3\uf6c4\uf6c5\uf6c6\uf6c7\uf6c8\uf6c9\uf6ca\uf6cb\uf6cc\uf6cd\uf6ce\uf6cf\uf6d0\uf6d1\uf6d2\uf6d3\uf6d4\uf6d5\uf6d6\uf6d7\uf6d8\uf6d9\uf6da\uf6db\uf6dc\uf6dd\uf6de\uf6df\uf6e0\uf6e1\uf6e2\uf6e3\uf6e4\uf6e5\uf6e6\uf6e7\uf6e8\uf6e9\uf6ea\uf6eb\uf6ec\uf6ed\uf6ee\uf6ef\uf6f0\uf6f1\uf6f2\uf6f3\uf6f4\uf6f5\uf6f6\uf6f7\uf6f8\uf6f9\uf6fa\uf6fb\uf6fc\uf6fd\uf6fe\uf6ff\uf700\uf701\uf702\uf703\uf704\uf705\uf706\uf707\uf708\uf709\uf70a\uf70b\uf70c\uf70d\uf70e\uf70f\uf710\uf711\uf712\uf713\uf714\uf715\uf716\uf717\uf718\uf719\uf71a\uf71b\uf71c\uf71d\uf71e\uf71f\uf720\uf721\uf722\uf723\uf724\uf725\uf726\uf727\uf728\uf729\uf72a\uf72b\uf72c\uf72d\uf72e\uf72f\uf730\uf731\uf732\uf733\uf734\uf735\uf736\uf737\uf738\uf739\uf73a\uf73b\uf73c\uf73d\uf73e\uf73f\uf740\uf741\uf742\uf743\uf744\uf745\uf746\uf747\uf748\uf749\uf74a\uf74b\uf74c\uf74d\uf74e\uf74f\uf750\uf751\uf752\uf753\uf754\uf755\uf756\uf757\uf758\uf759\uf75a\uf75b\uf75c\uf75d\uf75e\uf75f\uf760\uf761\uf762\uf763\uf764\uf765\uf766\uf767\uf768\uf769\uf76a\uf76b\uf76c\uf76d\uf76e\uf76f\uf770\uf771\uf772\uf773\uf774\uf775\uf776\uf777\uf778\uf779\uf77a\uf77b\uf77c\uf77d\uf77e\uf77f\uf780\uf781\uf782\uf783\uf784\uf785\uf786\uf787\uf788\uf789\uf78a\uf78b\uf78c\uf78d\uf78e\uf78f\uf790\uf791\uf792\uf793\uf794\uf795\uf796\uf797\uf798\uf799\uf79a\uf79b\uf79c\uf79d\uf79e\uf79f\uf7a0\uf7a1\uf7a2\uf7a3\uf7a4\uf7a5\uf7a6\uf7a7\uf7a8\uf7a9\uf7aa\uf7ab\uf7ac\uf7ad\uf7ae\uf7af\uf7b0\uf7b1\uf7b2\uf7b3\uf7b4\uf7b5\uf7b6\uf7b7\uf7b8\uf7b9\uf7ba\uf7bb\uf7bc\uf7bd\uf7be\uf7bf\uf7c0\uf7c1\uf7c2\uf7c3\uf7c4\uf7c5\uf7c6\uf7c7\uf7c8\uf7c9\uf7ca\uf7cb\uf7cc\uf7cd\uf7ce\uf7cf\uf7d0\uf7d1\uf7d2\uf7d3\uf7d4\uf7d5\uf7d6\uf7d7\uf7d8\uf7d9\uf7da\uf7db\uf7dc\uf7dd\uf7de\uf7df\uf7e0\uf7e1\uf7e2\uf7e3\uf7e4\uf7e5\uf7e6\uf7e7\uf7e8\uf7e9\uf7ea\uf7eb\uf7ec\uf7ed\uf7ee\uf7ef\uf7f0\uf7f1\uf7f2\uf7f3\uf7f4\uf7f5\uf7f6\uf7f7\uf7f8\uf7f9\uf7fa\uf7fb\uf7fc\uf7fd\uf7fe\uf7ff\uf800\uf801\uf802\uf803\uf804\uf805\uf806\uf807\uf808\uf809\uf80a\uf80b\uf80c\uf80d\uf80e\uf80f\uf810\uf811\uf812\uf813\uf814\uf815\uf816\uf817\uf818\uf819\uf81a\uf81b\uf81c\uf81d\uf81e\uf81f\uf820\uf821\uf822\uf823\uf824\uf825\uf826\uf827\uf828\uf829\uf82a\uf82b\uf82c\uf82d\uf82e\uf82f\uf830\uf831\uf832\uf833\uf834\uf835\uf836\uf837\uf838\uf839\uf83a\uf83b\uf83c\uf83d\uf83e\uf83f\uf840\uf841\uf842\uf843\uf844\uf845\uf846\uf847\uf848\uf849\uf84a\uf84b\uf84c\uf84d\uf84e\uf84f\uf850\uf851\uf852\uf853\uf854\uf855\uf856\uf857\uf858\uf859\uf85a\uf85b\uf85c\uf85d\uf85e\uf85f\uf860\uf861\uf862\uf863\uf864\uf865\uf866\uf867\uf868\uf869\uf86a\uf86b\uf86c\uf86d\uf86e\uf86f\uf870\uf871\uf872\uf873\uf874\uf875\uf876\uf877\uf878\uf879\uf87a\uf87b\uf87c\uf87d\uf87e\uf87f\uf880\uf881\uf882\uf883\uf884\uf885\uf886\uf887\uf888\uf889\uf88a\uf88b\uf88c\uf88d\uf88e\uf88f\uf890\uf891\uf892\uf893\uf894\uf895\uf896\uf897\uf898\uf899\uf89a\uf89b\uf89c\uf89d\uf89e\uf89f\uf8a0\uf8a1\uf8a2\uf8a3\uf8a4\uf8a5\uf8a6\uf8a7\uf8a8\uf8a9\uf8aa\uf8ab\uf8ac\uf8ad\uf8ae\uf8af\uf8b0\uf8b1\uf8b2\uf8b3\uf8b4\uf8b5\uf8b6\uf8b7\uf8b8\uf8b9\uf8ba\uf8bb\uf8bc\uf8bd\uf8be\uf8bf\uf8c0\uf8c1\uf8c2\uf8c3\uf8c4\uf8c5\uf8c6\uf8c7\uf8c8\uf8c9\uf8ca\uf8cb\uf8cc\uf8cd\uf8ce\uf8cf\uf8d0\uf8d1\uf8d2\uf8d3\uf8d4\uf8d5\uf8d6\uf8d7\uf8d8\uf8d9\uf8da\uf8db\uf8dc\uf8dd\uf8de\uf8df\uf8e0\uf8e1\uf8e2\uf8e3\uf8e4\uf8e5\uf8e6\uf8e7\uf8e8\uf8e9\uf8ea\uf8eb\uf8ec\uf8ed\uf8ee\uf8ef\uf8f0\uf8f1\uf8f2\uf8f3\uf8f4\uf8f5\uf8f6\uf8f7\uf8f8\uf8f9\uf8fa\uf8fb\uf8fc\uf8fd\uf8fe\uf8ff'
-
-try:
- Cs = eval(r"'\ud800\ud801\ud802\ud803\ud804\ud805\ud806\ud807\ud808\ud809\ud80a\ud80b\ud80c\ud80d\ud80e\ud80f\ud810\ud811\ud812\ud813\ud814\ud815\ud816\ud817\ud818\ud819\ud81a\ud81b\ud81c\ud81d\ud81e\ud81f\ud820\ud821\ud822\ud823\ud824\ud825\ud826\ud827\ud828\ud829\ud82a\ud82b\ud82c\ud82d\ud82e\ud82f\ud830\ud831\ud832\ud833\ud834\ud835\ud836\ud837\ud838\ud839\ud83a\ud83b\ud83c\ud83d\ud83e\ud83f\ud840\ud841\ud842\ud843\ud844\ud845\ud846\ud847\ud848\ud849\ud84a\ud84b\ud84c\ud84d\ud84e\ud84f\ud850\ud851\ud852\ud853\ud854\ud855\ud856\ud857\ud858\ud859\ud85a\ud85b\ud85c\ud85d\ud85e\ud85f\ud860\ud861\ud862\ud863\ud864\ud865\ud866\ud867\ud868\ud869\ud86a\ud86b\ud86c\ud86d\ud86e\ud86f\ud870\ud871\ud872\ud873\ud874\ud875\ud876\ud877\ud878\ud879\ud87a\ud87b\ud87c\ud87d\ud87e\ud87f\ud880\ud881\ud882\ud883\ud884\ud885\ud886\ud887\ud888\ud889\ud88a\ud88b\ud88c\ud88d\ud88e\ud88f\ud890\ud891\ud892\ud893\ud894\ud895\ud896\ud897\ud898\ud899\ud89a\ud89b\ud89c\ud89d\ud89e\ud89f\ud8a0\ud8a1\ud8a2\ud8a3\ud8a4\ud8a5\ud8a6\ud8a7\ud8a8\ud8a9\ud8aa\ud8ab\ud8ac\ud8ad\ud8ae\ud8af\ud8b0\ud8b1\ud8b2\ud8b3\ud8b4\ud8b5\ud8b6\ud8b7\ud8b8\ud8b9\ud8ba\ud8bb\ud8bc\ud8bd\ud8be\ud8bf\ud8c0\ud8c1\ud8c2\ud8c3\ud8c4\ud8c5\ud8c6\ud8c7\ud8c8\ud8c9\ud8ca\ud8cb\ud8cc\ud8cd\ud8ce\ud8cf\ud8d0\ud8d1\ud8d2\ud8d3\ud8d4\ud8d5\ud8d6\ud8d7\ud8d8\ud8d9\ud8da\ud8db\ud8dc\ud8dd\ud8de\ud8df\ud8e0\ud8e1\ud8e2\ud8e3\ud8e4\ud8e5\ud8e6\ud8e7\ud8e8\ud8e9\ud8ea\ud8eb\ud8ec\ud8ed\ud8ee\ud8ef\ud8f0\ud8f1\ud8f2\ud8f3\ud8f4\ud8f5\ud8f6\ud8f7\ud8f8\ud8f9\ud8fa\ud8fb\ud8fc\ud8fd\ud8fe\ud8ff\ud900\ud901\ud902\ud903\ud904\ud905\ud906\ud907\ud908\ud909\ud90a\ud90b\ud90c\ud90d\ud90e\ud90f\ud910\ud911\ud912\ud913\ud914\ud915\ud916\ud917\ud918\ud919\ud91a\ud91b\ud91c\ud91d\ud91e\ud91f\ud920\ud921\ud922\ud923\ud924\ud925\ud926\ud927\ud928\ud929\ud92a\ud92b\ud92c\ud92d\ud92e\ud92f\ud930\ud931\ud932\ud933\ud934\ud935\ud936\ud937\ud938\ud939\ud93a\ud93b\ud93c\ud93d\ud93e\ud93f\ud940\ud941\ud942\ud943\ud944\ud945\ud946\ud947\ud948\ud949\ud94a\ud94b\ud94c\ud94d\ud94e\ud94f\ud950\ud951\ud952\ud953\ud954\ud955\ud956\ud957\ud958\ud959\ud95a\ud95b\ud95c\ud95d\ud95e\ud95f\ud960\ud961\ud962\ud963\ud964\ud965\ud966\ud967\ud968\ud969\ud96a\ud96b\ud96c\ud96d\ud96e\ud96f\ud970\ud971\ud972\ud973\ud974\ud975\ud976\ud977\ud978\ud979\ud97a\ud97b\ud97c\ud97d\ud97e\ud97f\ud980\ud981\ud982\ud983\ud984\ud985\ud986\ud987\ud988\ud989\ud98a\ud98b\ud98c\ud98d\ud98e\ud98f\ud990\ud991\ud992\ud993\ud994\ud995\ud996\ud997\ud998\ud999\ud99a\ud99b\ud99c\ud99d\ud99e\ud99f\ud9a0\ud9a1\ud9a2\ud9a3\ud9a4\ud9a5\ud9a6\ud9a7\ud9a8\ud9a9\ud9aa\ud9ab\ud9ac\ud9ad\ud9ae\ud9af\ud9b0\ud9b1\ud9b2\ud9b3\ud9b4\ud9b5\ud9b6\ud9b7\ud9b8\ud9b9\ud9ba\ud9bb\ud9bc\ud9bd\ud9be\ud9bf\ud9c0\ud9c1\ud9c2\ud9c3\ud9c4\ud9c5\ud9c6\ud9c7\ud9c8\ud9c9\ud9ca\ud9cb\ud9cc\ud9cd\ud9ce\ud9cf\ud9d0\ud9d1\ud9d2\ud9d3\ud9d4\ud9d5\ud9d6\ud9d7\ud9d8\ud9d9\ud9da\ud9db\ud9dc\ud9dd\ud9de\ud9df\ud9e0\ud9e1\ud9e2\ud9e3\ud9e4\ud9e5\ud9e6\ud9e7\ud9e8\ud9e9\ud9ea\ud9eb\ud9ec\ud9ed\ud9ee\ud9ef\ud9f0\ud9f1\ud9f2\ud9f3\ud9f4\ud9f5\ud9f6\ud9f7\ud9f8\ud9f9\ud9fa\ud9fb\ud9fc\ud9fd\ud9fe\ud9ff\uda00\uda01\uda02\uda03\uda04\uda05\uda06\uda07\uda08\uda09\uda0a\uda0b\uda0c\uda0d\uda0e\uda0f\uda10\uda11\uda12\uda13\uda14\uda15\uda16\uda17\uda18\uda19\uda1a\uda1b\uda1c\uda1d\uda1e\uda1f\uda20\uda21\uda22\uda23\uda24\uda25\uda26\uda27\uda28\uda29\uda2a\uda2b\uda2c\uda2d\uda2e\uda2f\uda30\uda31\uda32\uda33\uda34\uda35\uda36\uda37\uda38\uda39\uda3a\uda3b\uda3c\uda3d\uda3e\uda3f\uda40\uda41\uda42\uda43\uda44\uda45\uda46\uda47\uda48\uda49\uda4a\uda4b\uda4c\uda4d\uda4e\uda4f\uda50\uda51\uda52\uda53\uda54\uda55\uda56\uda57\uda58\uda59\uda5a\uda5b\uda5c\uda5d\uda5e\uda5f\uda60\uda61\uda62\uda63\uda64\uda65\uda66\uda67\uda68\uda69\uda6a\uda6b\uda6c\uda6d\uda6e\uda6f\uda70\uda71\uda72\uda73\uda74\uda75\uda76\uda77\uda78\uda79\uda7a\uda7b\uda7c\uda7d\uda7e\uda7f\uda80\uda81\uda82\uda83\uda84\uda85\uda86\uda87\uda88\uda89\uda8a\uda8b\uda8c\uda8d\uda8e\uda8f\uda90\uda91\uda92\uda93\uda94\uda95\uda96\uda97\uda98\uda99\uda9a\uda9b\uda9c\uda9d\uda9e\uda9f\udaa0\udaa1\udaa2\udaa3\udaa4\udaa5\udaa6\udaa7\udaa8\udaa9\udaaa\udaab\udaac\udaad\udaae\udaaf\udab0\udab1\udab2\udab3\udab4\udab5\udab6\udab7\udab8\udab9\udaba\udabb\udabc\udabd\udabe\udabf\udac0\udac1\udac2\udac3\udac4\udac5\udac6\udac7\udac8\udac9\udaca\udacb\udacc\udacd\udace\udacf\udad0\udad1\udad2\udad3\udad4\udad5\udad6\udad7\udad8\udad9\udada\udadb\udadc\udadd\udade\udadf\udae0\udae1\udae2\udae3\udae4\udae5\udae6\udae7\udae8\udae9\udaea\udaeb\udaec\udaed\udaee\udaef\udaf0\udaf1\udaf2\udaf3\udaf4\udaf5\udaf6\udaf7\udaf8\udaf9\udafa\udafb\udafc\udafd\udafe\udaff\udb00\udb01\udb02\udb03\udb04\udb05\udb06\udb07\udb08\udb09\udb0a\udb0b\udb0c\udb0d\udb0e\udb0f\udb10\udb11\udb12\udb13\udb14\udb15\udb16\udb17\udb18\udb19\udb1a\udb1b\udb1c\udb1d\udb1e\udb1f\udb20\udb21\udb22\udb23\udb24\udb25\udb26\udb27\udb28\udb29\udb2a\udb2b\udb2c\udb2d\udb2e\udb2f\udb30\udb31\udb32\udb33\udb34\udb35\udb36\udb37\udb38\udb39\udb3a\udb3b\udb3c\udb3d\udb3e\udb3f\udb40\udb41\udb42\udb43\udb44\udb45\udb46\udb47\udb48\udb49\udb4a\udb4b\udb4c\udb4d\udb4e\udb4f\udb50\udb51\udb52\udb53\udb54\udb55\udb56\udb57\udb58\udb59\udb5a\udb5b\udb5c\udb5d\udb5e\udb5f\udb60\udb61\udb62\udb63\udb64\udb65\udb66\udb67\udb68\udb69\udb6a\udb6b\udb6c\udb6d\udb6e\udb6f\udb70\udb71\udb72\udb73\udb74\udb75\udb76\udb77\udb78\udb79\udb7a\udb7b\udb7c\udb7d\udb7e\udb7f\udb80\udb81\udb82\udb83\udb84\udb85\udb86\udb87\udb88\udb89\udb8a\udb8b\udb8c\udb8d\udb8e\udb8f\udb90\udb91\udb92\udb93\udb94\udb95\udb96\udb97\udb98\udb99\udb9a\udb9b\udb9c\udb9d\udb9e\udb9f\udba0\udba1\udba2\udba3\udba4\udba5\udba6\udba7\udba8\udba9\udbaa\udbab\udbac\udbad\udbae\udbaf\udbb0\udbb1\udbb2\udbb3\udbb4\udbb5\udbb6\udbb7\udbb8\udbb9\udbba\udbbb\udbbc\udbbd\udbbe\udbbf\udbc0\udbc1\udbc2\udbc3\udbc4\udbc5\udbc6\udbc7\udbc8\udbc9\udbca\udbcb\udbcc\udbcd\udbce\udbcf\udbd0\udbd1\udbd2\udbd3\udbd4\udbd5\udbd6\udbd7\udbd8\udbd9\udbda\udbdb\udbdc\udbdd\udbde\udbdf\udbe0\udbe1\udbe2\udbe3\udbe4\udbe5\udbe6\udbe7\udbe8\udbe9\udbea\udbeb\udbec\udbed\udbee\udbef\udbf0\udbf1\udbf2\udbf3\udbf4\udbf5\udbf6\udbf7\udbf8\udbf9\udbfa\udbfb\udbfc\udbfd\udbfe\U0010fc00\udc01\udc02\udc03\udc04\udc05\udc06\udc07\udc08\udc09\udc0a\udc0b\udc0c\udc0d\udc0e\udc0f\udc10\udc11\udc12\udc13\udc14\udc15\udc16\udc17\udc18\udc19\udc1a\udc1b\udc1c\udc1d\udc1e\udc1f\udc20\udc21\udc22\udc23\udc24\udc25\udc26\udc27\udc28\udc29\udc2a\udc2b\udc2c\udc2d\udc2e\udc2f\udc30\udc31\udc32\udc33\udc34\udc35\udc36\udc37\udc38\udc39\udc3a\udc3b\udc3c\udc3d\udc3e\udc3f\udc40\udc41\udc42\udc43\udc44\udc45\udc46\udc47\udc48\udc49\udc4a\udc4b\udc4c\udc4d\udc4e\udc4f\udc50\udc51\udc52\udc53\udc54\udc55\udc56\udc57\udc58\udc59\udc5a\udc5b\udc5c\udc5d\udc5e\udc5f\udc60\udc61\udc62\udc63\udc64\udc65\udc66\udc67\udc68\udc69\udc6a\udc6b\udc6c\udc6d\udc6e\udc6f\udc70\udc71\udc72\udc73\udc74\udc75\udc76\udc77\udc78\udc79\udc7a\udc7b\udc7c\udc7d\udc7e\udc7f\udc80\udc81\udc82\udc83\udc84\udc85\udc86\udc87\udc88\udc89\udc8a\udc8b\udc8c\udc8d\udc8e\udc8f\udc90\udc91\udc92\udc93\udc94\udc95\udc96\udc97\udc98\udc99\udc9a\udc9b\udc9c\udc9d\udc9e\udc9f\udca0\udca1\udca2\udca3\udca4\udca5\udca6\udca7\udca8\udca9\udcaa\udcab\udcac\udcad\udcae\udcaf\udcb0\udcb1\udcb2\udcb3\udcb4\udcb5\udcb6\udcb7\udcb8\udcb9\udcba\udcbb\udcbc\udcbd\udcbe\udcbf\udcc0\udcc1\udcc2\udcc3\udcc4\udcc5\udcc6\udcc7\udcc8\udcc9\udcca\udccb\udccc\udccd\udcce\udccf\udcd0\udcd1\udcd2\udcd3\udcd4\udcd5\udcd6\udcd7\udcd8\udcd9\udcda\udcdb\udcdc\udcdd\udcde\udcdf\udce0\udce1\udce2\udce3\udce4\udce5\udce6\udce7\udce8\udce9\udcea\udceb\udcec\udced\udcee\udcef\udcf0\udcf1\udcf2\udcf3\udcf4\udcf5\udcf6\udcf7\udcf8\udcf9\udcfa\udcfb\udcfc\udcfd\udcfe\udcff\udd00\udd01\udd02\udd03\udd04\udd05\udd06\udd07\udd08\udd09\udd0a\udd0b\udd0c\udd0d\udd0e\udd0f\udd10\udd11\udd12\udd13\udd14\udd15\udd16\udd17\udd18\udd19\udd1a\udd1b\udd1c\udd1d\udd1e\udd1f\udd20\udd21\udd22\udd23\udd24\udd25\udd26\udd27\udd28\udd29\udd2a\udd2b\udd2c\udd2d\udd2e\udd2f\udd30\udd31\udd32\udd33\udd34\udd35\udd36\udd37\udd38\udd39\udd3a\udd3b\udd3c\udd3d\udd3e\udd3f\udd40\udd41\udd42\udd43\udd44\udd45\udd46\udd47\udd48\udd49\udd4a\udd4b\udd4c\udd4d\udd4e\udd4f\udd50\udd51\udd52\udd53\udd54\udd55\udd56\udd57\udd58\udd59\udd5a\udd5b\udd5c\udd5d\udd5e\udd5f\udd60\udd61\udd62\udd63\udd64\udd65\udd66\udd67\udd68\udd69\udd6a\udd6b\udd6c\udd6d\udd6e\udd6f\udd70\udd71\udd72\udd73\udd74\udd75\udd76\udd77\udd78\udd79\udd7a\udd7b\udd7c\udd7d\udd7e\udd7f\udd80\udd81\udd82\udd83\udd84\udd85\udd86\udd87\udd88\udd89\udd8a\udd8b\udd8c\udd8d\udd8e\udd8f\udd90\udd91\udd92\udd93\udd94\udd95\udd96\udd97\udd98\udd99\udd9a\udd9b\udd9c\udd9d\udd9e\udd9f\udda0\udda1\udda2\udda3\udda4\udda5\udda6\udda7\udda8\udda9\uddaa\uddab\uddac\uddad\uddae\uddaf\uddb0\uddb1\uddb2\uddb3\uddb4\uddb5\uddb6\uddb7\uddb8\uddb9\uddba\uddbb\uddbc\uddbd\uddbe\uddbf\uddc0\uddc1\uddc2\uddc3\uddc4\uddc5\uddc6\uddc7\uddc8\uddc9\uddca\uddcb\uddcc\uddcd\uddce\uddcf\uddd0\uddd1\uddd2\uddd3\uddd4\uddd5\uddd6\uddd7\uddd8\uddd9\uddda\udddb\udddc\udddd\uddde\udddf\udde0\udde1\udde2\udde3\udde4\udde5\udde6\udde7\udde8\udde9\uddea\uddeb\uddec\udded\uddee\uddef\uddf0\uddf1\uddf2\uddf3\uddf4\uddf5\uddf6\uddf7\uddf8\uddf9\uddfa\uddfb\uddfc\uddfd\uddfe\uddff\ude00\ude01\ude02\ude03\ude04\ude05\ude06\ude07\ude08\ude09\ude0a\ude0b\ude0c\ude0d\ude0e\ude0f\ude10\ude11\ude12\ude13\ude14\ude15\ude16\ude17\ude18\ude19\ude1a\ude1b\ude1c\ude1d\ude1e\ude1f\ude20\ude21\ude22\ude23\ude24\ude25\ude26\ude27\ude28\ude29\ude2a\ude2b\ude2c\ude2d\ude2e\ude2f\ude30\ude31\ude32\ude33\ude34\ude35\ude36\ude37\ude38\ude39\ude3a\ude3b\ude3c\ude3d\ude3e\ude3f\ude40\ude41\ude42\ude43\ude44\ude45\ude46\ude47\ude48\ude49\ude4a\ude4b\ude4c\ude4d\ude4e\ude4f\ude50\ude51\ude52\ude53\ude54\ude55\ude56\ude57\ude58\ude59\ude5a\ude5b\ude5c\ude5d\ude5e\ude5f\ude60\ude61\ude62\ude63\ude64\ude65\ude66\ude67\ude68\ude69\ude6a\ude6b\ude6c\ude6d\ude6e\ude6f\ude70\ude71\ude72\ude73\ude74\ude75\ude76\ude77\ude78\ude79\ude7a\ude7b\ude7c\ude7d\ude7e\ude7f\ude80\ude81\ude82\ude83\ude84\ude85\ude86\ude87\ude88\ude89\ude8a\ude8b\ude8c\ude8d\ude8e\ude8f\ude90\ude91\ude92\ude93\ude94\ude95\ude96\ude97\ude98\ude99\ude9a\ude9b\ude9c\ude9d\ude9e\ude9f\udea0\udea1\udea2\udea3\udea4\udea5\udea6\udea7\udea8\udea9\udeaa\udeab\udeac\udead\udeae\udeaf\udeb0\udeb1\udeb2\udeb3\udeb4\udeb5\udeb6\udeb7\udeb8\udeb9\udeba\udebb\udebc\udebd\udebe\udebf\udec0\udec1\udec2\udec3\udec4\udec5\udec6\udec7\udec8\udec9\udeca\udecb\udecc\udecd\udece\udecf\uded0\uded1\uded2\uded3\uded4\uded5\uded6\uded7\uded8\uded9\udeda\udedb\udedc\udedd\udede\udedf\udee0\udee1\udee2\udee3\udee4\udee5\udee6\udee7\udee8\udee9\udeea\udeeb\udeec\udeed\udeee\udeef\udef0\udef1\udef2\udef3\udef4\udef5\udef6\udef7\udef8\udef9\udefa\udefb\udefc\udefd\udefe\udeff\udf00\udf01\udf02\udf03\udf04\udf05\udf06\udf07\udf08\udf09\udf0a\udf0b\udf0c\udf0d\udf0e\udf0f\udf10\udf11\udf12\udf13\udf14\udf15\udf16\udf17\udf18\udf19\udf1a\udf1b\udf1c\udf1d\udf1e\udf1f\udf20\udf21\udf22\udf23\udf24\udf25\udf26\udf27\udf28\udf29\udf2a\udf2b\udf2c\udf2d\udf2e\udf2f\udf30\udf31\udf32\udf33\udf34\udf35\udf36\udf37\udf38\udf39\udf3a\udf3b\udf3c\udf3d\udf3e\udf3f\udf40\udf41\udf42\udf43\udf44\udf45\udf46\udf47\udf48\udf49\udf4a\udf4b\udf4c\udf4d\udf4e\udf4f\udf50\udf51\udf52\udf53\udf54\udf55\udf56\udf57\udf58\udf59\udf5a\udf5b\udf5c\udf5d\udf5e\udf5f\udf60\udf61\udf62\udf63\udf64\udf65\udf66\udf67\udf68\udf69\udf6a\udf6b\udf6c\udf6d\udf6e\udf6f\udf70\udf71\udf72\udf73\udf74\udf75\udf76\udf77\udf78\udf79\udf7a\udf7b\udf7c\udf7d\udf7e\udf7f\udf80\udf81\udf82\udf83\udf84\udf85\udf86\udf87\udf88\udf89\udf8a\udf8b\udf8c\udf8d\udf8e\udf8f\udf90\udf91\udf92\udf93\udf94\udf95\udf96\udf97\udf98\udf99\udf9a\udf9b\udf9c\udf9d\udf9e\udf9f\udfa0\udfa1\udfa2\udfa3\udfa4\udfa5\udfa6\udfa7\udfa8\udfa9\udfaa\udfab\udfac\udfad\udfae\udfaf\udfb0\udfb1\udfb2\udfb3\udfb4\udfb5\udfb6\udfb7\udfb8\udfb9\udfba\udfbb\udfbc\udfbd\udfbe\udfbf\udfc0\udfc1\udfc2\udfc3\udfc4\udfc5\udfc6\udfc7\udfc8\udfc9\udfca\udfcb\udfcc\udfcd\udfce\udfcf\udfd0\udfd1\udfd2\udfd3\udfd4\udfd5\udfd6\udfd7\udfd8\udfd9\udfda\udfdb\udfdc\udfdd\udfde\udfdf\udfe0\udfe1\udfe2\udfe3\udfe4\udfe5\udfe6\udfe7\udfe8\udfe9\udfea\udfeb\udfec\udfed\udfee\udfef\udff0\udff1\udff2\udff3\udff4\udff5\udff6\udff7\udff8\udff9\udffa\udffb\udffc\udffd\udffe\udfff'")
-except UnicodeDecodeError:
- Cs = '' # Jython can't handle isolated surrogates
-
-Ll = u'abcdefghijklmnopqrstuvwxyz\xaa\xb5\xba\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e\u017f\u0180\u0183\u0185\u0188\u018c\u018d\u0192\u0195\u0199\u019a\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9\u01ba\u01bd\u01be\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233\u0234\u0235\u0236\u0237\u0238\u0239\u023c\u023f\u0240\u0250\u0251\u0252\u0253\u0254\u0255\u0256\u0257\u0258\u0259\u025a\u025b\u025c\u025d\u025e\u025f\u0260\u0261\u0262\u0263\u0264\u0265\u0266\u0267\u0268\u0269\u026a\u026b\u026c\u026d\u026e\u026f\u0270\u0271\u0272\u0273\u0274\u0275\u0276\u0277\u0278\u0279\u027a\u027b\u027c\u027d\u027e\u027f\u0280\u0281\u0282\u0283\u0284\u0285\u0286\u0287\u0288\u0289\u028a\u028b\u028c\u028d\u028e\u028f\u0290\u0291\u0292\u0293\u0294\u0295\u0296\u0297\u0298\u0299\u029a\u029b\u029c\u029d\u029e\u029f\u02a0\u02a1\u02a2\u02a3\u02a4\u02a5\u02a6\u02a7\u02a8\u02a9\u02aa\u02ab\u02ac\u02ad\u02ae\u02af\u0390\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce\u03d0\u03d1\u03d5\u03d6\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef\u03f0\u03f1\u03f2\u03f3\u03f5\u03f8\u03fb\u03fc\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0450\u0451\u0452\u0453\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u045d\u045e\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u1d00\u1d01\u1d02\u1d03\u1d04\u1d05\u1d06\u1d07\u1d08\u1d09\u1d0a\u1d0b\u1d0c\u1d0d\u1d0e\u1d0f\u1d10\u1d11\u1d12\u1d13\u1d14\u1d15\u1d16\u1d17\u1d18\u1d19\u1d1a\u1d1b\u1d1c\u1d1d\u1d1e\u1d1f\u1d20\u1d21\u1d22\u1d23\u1d24\u1d25\u1d26\u1d27\u1d28\u1d29\u1d2a\u1d2b\u1d62\u1d63\u1d64\u1d65\u1d66\u1d67\u1d68\u1d69\u1d6a\u1d6b\u1d6c\u1d6d\u1d6e\u1d6f\u1d70\u1d71\u1d72\u1d73\u1d74\u1d75\u1d76\u1d77\u1d79\u1d7a\u1d7b\u1d7c\u1d7d\u1d7e\u1d7f\u1d80\u1d81\u1d82\u1d83\u1d84\u1d85\u1d86\u1d87\u1d88\u1d89\u1d8a\u1d8b\u1d8c\u1d8d\u1d8e\u1d8f\u1d90\u1d91\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95\u1e96\u1e97\u1e98\u1e99\u1e9a\u1e9b\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1f00\u1f01\u1f02\u1f03\u1f04\u1f05\u1f06\u1f07\u1f10\u1f11\u1f12\u1f13\u1f14\u1f15\u1f20\u1f21\u1f22\u1f23\u1f24\u1f25\u1f26\u1f27\u1f30\u1f31\u1f32\u1f33\u1f34\u1f35\u1f36\u1f37\u1f40\u1f41\u1f42\u1f43\u1f44\u1f45\u1f50\u1f51\u1f52\u1f53\u1f54\u1f55\u1f56\u1f57\u1f60\u1f61\u1f62\u1f63\u1f64\u1f65\u1f66\u1f67\u1f70\u1f71\u1f72\u1f73\u1f74\u1f75\u1f76\u1f77\u1f78\u1f79\u1f7a\u1f7b\u1f7c\u1f7d\u1f80\u1f81\u1f82\u1f83\u1f84\u1f85\u1f86\u1f87\u1f90\u1f91\u1f92\u1f93\u1f94\u1f95\u1f96\u1f97\u1fa0\u1fa1\u1fa2\u1fa3\u1fa4\u1fa5\u1fa6\u1fa7\u1fb0\u1fb1\u1fb2\u1fb3\u1fb4\u1fb6\u1fb7\u1fbe\u1fc2\u1fc3\u1fc4\u1fc6\u1fc7\u1fd0\u1fd1\u1fd2\u1fd3\u1fd6\u1fd7\u1fe0\u1fe1\u1fe2\u1fe3\u1fe4\u1fe5\u1fe6\u1fe7\u1ff2\u1ff3\u1ff4\u1ff6\u1ff7\u2071\u207f\u210a\u210e\u210f\u2113\u212f\u2134\u2139\u213c\u213d\u2146\u2147\u2148\u2149\u2c30\u2c31\u2c32\u2c33\u2c34\u2c35\u2c36\u2c37\u2c38\u2c39\u2c3a\u2c3b\u2c3c\u2c3d\u2c3e\u2c3f\u2c40\u2c41\u2c42\u2c43\u2c44\u2c45\u2c46\u2c47\u2c48\u2c49\u2c4a\u2c4b\u2c4c\u2c4d\u2c4e\u2c4f\u2c50\u2c51\u2c52\u2c53\u2c54\u2c55\u2c56\u2c57\u2c58\u2c59\u2c5a\u2c5b\u2c5c\u2c5d\u2c5e\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3\u2ce4\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d07\u2d08\u2d09\u2d0a\u2d0b\u2d0c\u2d0d\u2d0e\u2d0f\u2d10\u2d11\u2d12\u2d13\u2d14\u2d15\u2d16\u2d17\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d1f\u2d20\u2d21\u2d22\u2d23\u2d24\u2d25\ufb00\ufb01\ufb02\ufb03\ufb04\ufb05\ufb06\ufb13\ufb14\ufb15\ufb16\ufb17\uff41\uff42\uff43\uff44\uff45\uff46\uff47\uff48\uff49\uff4a\uff4b\uff4c\uff4d\uff4e\uff4f\uff50\uff51\uff52\uff53\uff54\uff55\uff56\uff57\uff58\uff59\uff5a'
-
-Lm = u'\u02b0\u02b1\u02b2\u02b3\u02b4\u02b5\u02b6\u02b7\u02b8\u02b9\u02ba\u02bb\u02bc\u02bd\u02be\u02bf\u02c0\u02c1\u02c6\u02c7\u02c8\u02c9\u02ca\u02cb\u02cc\u02cd\u02ce\u02cf\u02d0\u02d1\u02e0\u02e1\u02e2\u02e3\u02e4\u02ee\u037a\u0559\u0640\u06e5\u06e6\u0e46\u0ec6\u10fc\u17d7\u1843\u1d2c\u1d2d\u1d2e\u1d2f\u1d30\u1d31\u1d32\u1d33\u1d34\u1d35\u1d36\u1d37\u1d38\u1d39\u1d3a\u1d3b\u1d3c\u1d3d\u1d3e\u1d3f\u1d40\u1d41\u1d42\u1d43\u1d44\u1d45\u1d46\u1d47\u1d48\u1d49\u1d4a\u1d4b\u1d4c\u1d4d\u1d4e\u1d4f\u1d50\u1d51\u1d52\u1d53\u1d54\u1d55\u1d56\u1d57\u1d58\u1d59\u1d5a\u1d5b\u1d5c\u1d5d\u1d5e\u1d5f\u1d60\u1d61\u1d78\u1d9b\u1d9c\u1d9d\u1d9e\u1d9f\u1da0\u1da1\u1da2\u1da3\u1da4\u1da5\u1da6\u1da7\u1da8\u1da9\u1daa\u1dab\u1dac\u1dad\u1dae\u1daf\u1db0\u1db1\u1db2\u1db3\u1db4\u1db5\u1db6\u1db7\u1db8\u1db9\u1dba\u1dbb\u1dbc\u1dbd\u1dbe\u1dbf\u2090\u2091\u2092\u2093\u2094\u2d6f\u3005\u3031\u3032\u3033\u3034\u3035\u303b\u309d\u309e\u30fc\u30fd\u30fe\ua015\uff70\uff9e\uff9f'
-
-Lo = u'\u01bb\u01c0\u01c1\u01c2\u01c3\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u05f0\u05f1\u05f2\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u066e\u066f\u0671\u0672\u0673\u0674\u0675\u0676\u0677\u0678\u0679\u067a\u067b\u067c\u067d\u067e\u067f\u0680\u0681\u0682\u0683\u0684\u0685\u0686\u0687\u0688\u0689\u068a\u068b\u068c\u068d\u068e\u068f\u0690\u0691\u0692\u0693\u0694\u0695\u0696\u0697\u0698\u0699\u069a\u069b\u069c\u069d\u069e\u069f\u06a0\u06a1\u06a2\u06a3\u06a4\u06a5\u06a6\u06a7\u06a8\u06a9\u06aa\u06ab\u06ac\u06ad\u06ae\u06af\u06b0\u06b1\u06b2\u06b3\u06b4\u06b5\u06b6\u06b7\u06b8\u06b9\u06ba\u06bb\u06bc\u06bd\u06be\u06bf\u06c0\u06c1\u06c2\u06c3\u06c4\u06c5\u06c6\u06c7\u06c8\u06c9\u06ca\u06cb\u06cc\u06cd\u06ce\u06cf\u06d0\u06d1\u06d2\u06d3\u06d5\u06ee\u06ef\u06fa\u06fb\u06fc\u06ff\u0710\u0712\u0713\u0714\u0715\u0716\u0717\u0718\u0719\u071a\u071b\u071c\u071d\u071e\u071f\u0720\u0721\u0722\u0723\u0724\u0725\u0726\u0727\u0728\u0729\u072a\u072b\u072c\u072d\u072e\u072f\u074d\u074e\u074f\u0750\u0751\u0752\u0753\u0754\u0755\u0756\u0757\u0758\u0759\u075a\u075b\u075c\u075d\u075e\u075f\u0760\u0761\u0762\u0763\u0764\u0765\u0766\u0767\u0768\u0769\u076a\u076b\u076c\u076d\u0780\u0781\u0782\u0783\u0784\u0785\u0786\u0787\u0788\u0789\u078a\u078b\u078c\u078d\u078e\u078f\u0790\u0791\u0792\u0793\u0794\u0795\u0796\u0797\u0798\u0799\u079a\u079b\u079c\u079d\u079e\u079f\u07a0\u07a1\u07a2\u07a3\u07a4\u07a5\u07b1\u0904\u0905\u0906\u0907\u0908\u0909\u090a\u090b\u090c\u090d\u090e\u090f\u0910\u0911\u0912\u0913\u0914\u0915\u0916\u0917\u0918\u0919\u091a\u091b\u091c\u091d\u091e\u091f\u0920\u0921\u0922\u0923\u0924\u0925\u0926\u0927\u0928\u0929\u092a\u092b\u092c\u092d\u092e\u092f\u0930\u0931\u0932\u0933\u0934\u0935\u0936\u0937\u0938\u0939\u093d\u0950\u0958\u0959\u095a\u095b\u095c\u095d\u095e\u095f\u0960\u0961\u097d\u0985\u0986\u0987\u0988\u0989\u098a\u098b\u098c\u098f\u0990\u0993\u0994\u0995\u0996\u0997\u0998\u0999\u099a\u099b\u099c\u099d\u099e\u099f\u09a0\u09a1\u09a2\u09a3\u09a4\u09a5\u09a6\u09a7\u09a8\u09aa\u09ab\u09ac\u09ad\u09ae\u09af\u09b0\u09b2\u09b6\u09b7\u09b8\u09b9\u09bd\u09ce\u09dc\u09dd\u09df\u09e0\u09e1\u09f0\u09f1\u0a05\u0a06\u0a07\u0a08\u0a09\u0a0a\u0a0f\u0a10\u0a13\u0a14\u0a15\u0a16\u0a17\u0a18\u0a19\u0a1a\u0a1b\u0a1c\u0a1d\u0a1e\u0a1f\u0a20\u0a21\u0a22\u0a23\u0a24\u0a25\u0a26\u0a27\u0a28\u0a2a\u0a2b\u0a2c\u0a2d\u0a2e\u0a2f\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59\u0a5a\u0a5b\u0a5c\u0a5e\u0a72\u0a73\u0a74\u0a85\u0a86\u0a87\u0a88\u0a89\u0a8a\u0a8b\u0a8c\u0a8d\u0a8f\u0a90\u0a91\u0a93\u0a94\u0a95\u0a96\u0a97\u0a98\u0a99\u0a9a\u0a9b\u0a9c\u0a9d\u0a9e\u0a9f\u0aa0\u0aa1\u0aa2\u0aa3\u0aa4\u0aa5\u0aa6\u0aa7\u0aa8\u0aaa\u0aab\u0aac\u0aad\u0aae\u0aaf\u0ab0\u0ab2\u0ab3\u0ab5\u0ab6\u0ab7\u0ab8\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05\u0b06\u0b07\u0b08\u0b09\u0b0a\u0b0b\u0b0c\u0b0f\u0b10\u0b13\u0b14\u0b15\u0b16\u0b17\u0b18\u0b19\u0b1a\u0b1b\u0b1c\u0b1d\u0b1e\u0b1f\u0b20\u0b21\u0b22\u0b23\u0b24\u0b25\u0b26\u0b27\u0b28\u0b2a\u0b2b\u0b2c\u0b2d\u0b2e\u0b2f\u0b30\u0b32\u0b33\u0b35\u0b36\u0b37\u0b38\u0b39\u0b3d\u0b5c\u0b5d\u0b5f\u0b60\u0b61\u0b71\u0b83\u0b85\u0b86\u0b87\u0b88\u0b89\u0b8a\u0b8e\u0b8f\u0b90\u0b92\u0b93\u0b94\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8\u0ba9\u0baa\u0bae\u0baf\u0bb0\u0bb1\u0bb2\u0bb3\u0bb4\u0bb5\u0bb6\u0bb7\u0bb8\u0bb9\u0c05\u0c06\u0c07\u0c08\u0c09\u0c0a\u0c0b\u0c0c\u0c0e\u0c0f\u0c10\u0c12\u0c13\u0c14\u0c15\u0c16\u0c17\u0c18\u0c19\u0c1a\u0c1b\u0c1c\u0c1d\u0c1e\u0c1f\u0c20\u0c21\u0c22\u0c23\u0c24\u0c25\u0c26\u0c27\u0c28\u0c2a\u0c2b\u0c2c\u0c2d\u0c2e\u0c2f\u0c30\u0c31\u0c32\u0c33\u0c35\u0c36\u0c37\u0c38\u0c39\u0c60\u0c61\u0c85\u0c86\u0c87\u0c88\u0c89\u0c8a\u0c8b\u0c8c\u0c8e\u0c8f\u0c90\u0c92\u0c93\u0c94\u0c95\u0c96\u0c97\u0c98\u0c99\u0c9a\u0c9b\u0c9c\u0c9d\u0c9e\u0c9f\u0ca0\u0ca1\u0ca2\u0ca3\u0ca4\u0ca5\u0ca6\u0ca7\u0ca8\u0caa\u0cab\u0cac\u0cad\u0cae\u0caf\u0cb0\u0cb1\u0cb2\u0cb3\u0cb5\u0cb6\u0cb7\u0cb8\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0d05\u0d06\u0d07\u0d08\u0d09\u0d0a\u0d0b\u0d0c\u0d0e\u0d0f\u0d10\u0d12\u0d13\u0d14\u0d15\u0d16\u0d17\u0d18\u0d19\u0d1a\u0d1b\u0d1c\u0d1d\u0d1e\u0d1f\u0d20\u0d21\u0d22\u0d23\u0d24\u0d25\u0d26\u0d27\u0d28\u0d2a\u0d2b\u0d2c\u0d2d\u0d2e\u0d2f\u0d30\u0d31\u0d32\u0d33\u0d34\u0d35\u0d36\u0d37\u0d38\u0d39\u0d60\u0d61\u0d85\u0d86\u0d87\u0d88\u0d89\u0d8a\u0d8b\u0d8c\u0d8d\u0d8e\u0d8f\u0d90\u0d91\u0d92\u0d93\u0d94\u0d95\u0d96\u0d9a\u0d9b\u0d9c\u0d9d\u0d9e\u0d9f\u0da0\u0da1\u0da2\u0da3\u0da4\u0da5\u0da6\u0da7\u0da8\u0da9\u0daa\u0dab\u0dac\u0dad\u0dae\u0daf\u0db0\u0db1\u0db3\u0db4\u0db5\u0db6\u0db7\u0db8\u0db9\u0dba\u0dbb\u0dbd\u0dc0\u0dc1\u0dc2\u0dc3\u0dc4\u0dc5\u0dc6\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e32\u0e33\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94\u0e95\u0e96\u0e97\u0e99\u0e9a\u0e9b\u0e9c\u0e9d\u0e9e\u0e9f\u0ea1\u0ea2\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead\u0eae\u0eaf\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0\u0ec1\u0ec2\u0ec3\u0ec4\u0edc\u0edd\u0f00\u0f40\u0f41\u0f42\u0f43\u0f44\u0f45\u0f46\u0f47\u0f49\u0f4a\u0f4b\u0f4c\u0f4d\u0f4e\u0f4f\u0f50\u0f51\u0f52\u0f53\u0f54\u0f55\u0f56\u0f57\u0f58\u0f59\u0f5a\u0f5b\u0f5c\u0f5d\u0f5e\u0f5f\u0f60\u0f61\u0f62\u0f63\u0f64\u0f65\u0f66\u0f67\u0f68\u0f69\u0f6a\u0f88\u0f89\u0f8a\u0f8b\u1000\u1001\u1002\u1003\u1004\u1005\u1006\u1007\u1008\u1009\u100a\u100b\u100c\u100d\u100e\u100f\u1010\u1011\u1012\u1013\u1014\u1015\u1016\u1017\u1018\u1019\u101a\u101b\u101c\u101d\u101e\u101f\u1020\u1021\u1023\u1024\u1025\u1026\u1027\u1029\u102a\u1050\u1051\u1052\u1053\u1054\u1055\u10d0\u10d1\u10d2\u10d3\u10d4\u10d5\u10d6\u10d7\u10d8\u10d9\u10da\u10db\u10dc\u10dd\u10de\u10df\u10e0\u10e1\u10e2\u10e3\u10e4\u10e5\u10e6\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef\u10f0\u10f1\u10f2\u10f3\u10f4\u10f5\u10f6\u10f7\u10f8\u10f9\u10fa\u1100\u1101\u1102\u1103\u1104\u1105\u1106\u1107\u1108\u1109\u110a\u110b\u110c\u110d\u110e\u110f\u1110\u1111\u1112\u1113\u1114\u1115\u1116\u1117\u1118\u1119\u111a\u111b\u111c\u111d\u111e\u111f\u1120\u1121\u1122\u1123\u1124\u1125\u1126\u1127\u1128\u1129\u112a\u112b\u112c\u112d\u112e\u112f\u1130\u1131\u1132\u1133\u1134\u1135\u1136\u1137\u1138\u1139\u113a\u113b\u113c\u113d\u113e\u113f\u1140\u1141\u1142\u1143\u1144\u1145\u1146\u1147\u1148\u1149\u114a\u114b\u114c\u114d\u114e\u114f\u1150\u1151\u1152\u1153\u1154\u1155\u1156\u1157\u1158\u1159\u115f\u1160\u1161\u1162\u1163\u1164\u1165\u1166\u1167\u1168\u1169\u116a\u116b\u116c\u116d\u116e\u116f\u1170\u1171\u1172\u1173\u1174\u1175\u1176\u1177\u1178\u1179\u117a\u117b\u117c\u117d\u117e\u117f\u1180\u1181\u1182\u1183\u1184\u1185\u1186\u1187\u1188\u1189\u118a\u118b\u118c\u118d\u118e\u118f\u1190\u1191\u1192\u1193\u1194\u1195\u1196\u1197\u1198\u1199\u119a\u119b\u119c\u119d\u119e\u119f\u11a0\u11a1\u11a2\u11a8\u11a9\u11aa\u11ab\u11ac\u11ad\u11ae\u11af\u11b0\u11b1\u11b2\u11b3\u11b4\u11b5\u11b6\u11b7\u11b8\u11b9\u11ba\u11bb\u11bc\u11bd\u11be\u11bf\u11c0\u11c1\u11c2\u11c3\u11c4\u11c5\u11c6\u11c7\u11c8\u11c9\u11ca\u11cb\u11cc\u11cd\u11ce\u11cf\u11d0\u11d1\u11d2\u11d3\u11d4\u11d5\u11d6\u11d7\u11d8\u11d9\u11da\u11db\u11dc\u11dd\u11de\u11df\u11e0\u11e1\u11e2\u11e3\u11e4\u11e5\u11e6\u11e7\u11e8\u11e9\u11ea\u11eb\u11ec\u11ed\u11ee\u11ef\u11f0\u11f1\u11f2\u11f3\u11f4\u11f5\u11f6\u11f7\u11f8\u11f9\u1200\u1201\u1202\u1203\u1204\u1205\u1206\u1207\u1208\u1209\u120a\u120b\u120c\u120d\u120e\u120f\u1210\u1211\u1212\u1213\u1214\u1215\u1216\u1217\u1218\u1219\u121a\u121b\u121c\u121d\u121e\u121f\u1220\u1221\u1222\u1223\u1224\u1225\u1226\u1227\u1228\u1229\u122a\u122b\u122c\u122d\u122e\u122f\u1230\u1231\u1232\u1233\u1234\u1235\u1236\u1237\u1238\u1239\u123a\u123b\u123c\u123d\u123e\u123f\u1240\u1241\u1242\u1243\u1244\u1245\u1246\u1247\u1248\u124a\u124b\u124c\u124d\u1250\u1251\u1252\u1253\u1254\u1255\u1256\u1258\u125a\u125b\u125c\u125d\u1260\u1261\u1262\u1263\u1264\u1265\u1266\u1267\u1268\u1269\u126a\u126b\u126c\u126d\u126e\u126f\u1270\u1271\u1272\u1273\u1274\u1275\u1276\u1277\u1278\u1279\u127a\u127b\u127c\u127d\u127e\u127f\u1280\u1281\u1282\u1283\u1284\u1285\u1286\u1287\u1288\u128a\u128b\u128c\u128d\u1290\u1291\u1292\u1293\u1294\u1295\u1296\u1297\u1298\u1299\u129a\u129b\u129c\u129d\u129e\u129f\u12a0\u12a1\u12a2\u12a3\u12a4\u12a5\u12a6\u12a7\u12a8\u12a9\u12aa\u12ab\u12ac\u12ad\u12ae\u12af\u12b0\u12b2\u12b3\u12b4\u12b5\u12b8\u12b9\u12ba\u12bb\u12bc\u12bd\u12be\u12c0\u12c2\u12c3\u12c4\u12c5\u12c8\u12c9\u12ca\u12cb\u12cc\u12cd\u12ce\u12cf\u12d0\u12d1\u12d2\u12d3\u12d4\u12d5\u12d6\u12d8\u12d9\u12da\u12db\u12dc\u12dd\u12de\u12df\u12e0\u12e1\u12e2\u12e3\u12e4\u12e5\u12e6\u12e7\u12e8\u12e9\u12ea\u12eb\u12ec\u12ed\u12ee\u12ef\u12f0\u12f1\u12f2\u12f3\u12f4\u12f5\u12f6\u12f7\u12f8\u12f9\u12fa\u12fb\u12fc\u12fd\u12fe\u12ff\u1300\u1301\u1302\u1303\u1304\u1305\u1306\u1307\u1308\u1309\u130a\u130b\u130c\u130d\u130e\u130f\u1310\u1312\u1313\u1314\u1315\u1318\u1319\u131a\u131b\u131c\u131d\u131e\u131f\u1320\u1321\u1322\u1323\u1324\u1325\u1326\u1327\u1328\u1329\u132a\u132b\u132c\u132d\u132e\u132f\u1330\u1331\u1332\u1333\u1334\u1335\u1336\u1337\u1338\u1339\u133a\u133b\u133c\u133d\u133e\u133f\u1340\u1341\u1342\u1343\u1344\u1345\u1346\u1347\u1348\u1349\u134a\u134b\u134c\u134d\u134e\u134f\u1350\u1351\u1352\u1353\u1354\u1355\u1356\u1357\u1358\u1359\u135a\u1380\u1381\u1382\u1383\u1384\u1385\u1386\u1387\u1388\u1389\u138a\u138b\u138c\u138d\u138e\u138f\u13a0\u13a1\u13a2\u13a3\u13a4\u13a5\u13a6\u13a7\u13a8\u13a9\u13aa\u13ab\u13ac\u13ad\u13ae\u13af\u13b0\u13b1\u13b2\u13b3\u13b4\u13b5\u13b6\u13b7\u13b8\u13b9\u13ba\u13bb\u13bc\u13bd\u13be\u13bf\u13c0\u13c1\u13c2\u13c3\u13c4\u13c5\u13c6\u13c7\u13c8\u13c9\u13ca\u13cb\u13cc\u13cd\u13ce\u13cf\u13d0\u13d1\u13d2\u13d3\u13d4\u13d5\u13d6\u13d7\u13d8\u13d9\u13da\u13db\u13dc\u13dd\u13de\u13df\u13e0\u13e1\u13e2\u13e3\u13e4\u13e5\u13e6\u13e7\u13e8\u13e9\u13ea\u13eb\u13ec\u13ed\u13ee\u13ef\u13f0\u13f1\u13f2\u13f3\u13f4\u1401\u1402\u1403\u1404\u1405\u1406\u1407\u1408\u1409\u140a\u140b\u140c\u140d\u140e\u140f\u1410\u1411\u1412\u1413\u1414\u1415\u1416\u1417\u1418\u1419\u141a\u141b\u141c\u141d\u141e\u141f\u1420\u1421\u1422\u1423\u1424\u1425\u1426\u1427\u1428\u1429\u142a\u142b\u142c\u142d\u142e\u142f\u1430\u1431\u1432\u1433\u1434\u1435\u1436\u1437\u1438\u1439\u143a\u143b\u143c\u143d\u143e\u143f\u1440\u1441\u1442\u1443\u1444\u1445\u1446\u1447\u1448\u1449\u144a\u144b\u144c\u144d\u144e\u144f\u1450\u1451\u1452\u1453\u1454\u1455\u1456\u1457\u1458\u1459\u145a\u145b\u145c\u145d\u145e\u145f\u1460\u1461\u1462\u1463\u1464\u1465\u1466\u1467\u1468\u1469\u146a\u146b\u146c\u146d\u146e\u146f\u1470\u1471\u1472\u1473\u1474\u1475\u1476\u1477\u1478\u1479\u147a\u147b\u147c\u147d\u147e\u147f\u1480\u1481\u1482\u1483\u1484\u1485\u1486\u1487\u1488\u1489\u148a\u148b\u148c\u148d\u148e\u148f\u1490\u1491\u1492\u1493\u1494\u1495\u1496\u1497\u1498\u1499\u149a\u149b\u149c\u149d\u149e\u149f\u14a0\u14a1\u14a2\u14a3\u14a4\u14a5\u14a6\u14a7\u14a8\u14a9\u14aa\u14ab\u14ac\u14ad\u14ae\u14af\u14b0\u14b1\u14b2\u14b3\u14b4\u14b5\u14b6\u14b7\u14b8\u14b9\u14ba\u14bb\u14bc\u14bd\u14be\u14bf\u14c0\u14c1\u14c2\u14c3\u14c4\u14c5\u14c6\u14c7\u14c8\u14c9\u14ca\u14cb\u14cc\u14cd\u14ce\u14cf\u14d0\u14d1\u14d2\u14d3\u14d4\u14d5\u14d6\u14d7\u14d8\u14d9\u14da\u14db\u14dc\u14dd\u14de\u14df\u14e0\u14e1\u14e2\u14e3\u14e4\u14e5\u14e6\u14e7\u14e8\u14e9\u14ea\u14eb\u14ec\u14ed\u14ee\u14ef\u14f0\u14f1\u14f2\u14f3\u14f4\u14f5\u14f6\u14f7\u14f8\u14f9\u14fa\u14fb\u14fc\u14fd\u14fe\u14ff\u1500\u1501\u1502\u1503\u1504\u1505\u1506\u1507\u1508\u1509\u150a\u150b\u150c\u150d\u150e\u150f\u1510\u1511\u1512\u1513\u1514\u1515\u1516\u1517\u1518\u1519\u151a\u151b\u151c\u151d\u151e\u151f\u1520\u1521\u1522\u1523\u1524\u1525\u1526\u1527\u1528\u1529\u152a\u152b\u152c\u152d\u152e\u152f\u1530\u1531\u1532\u1533\u1534\u1535\u1536\u1537\u1538\u1539\u153a\u153b\u153c\u153d\u153e\u153f\u1540\u1541\u1542\u1543\u1544\u1545\u1546\u1547\u1548\u1549\u154a\u154b\u154c\u154d\u154e\u154f\u1550\u1551\u1552\u1553\u1554\u1555\u1556\u1557\u1558\u1559\u155a\u155b\u155c\u155d\u155e\u155f\u1560\u1561\u1562\u1563\u1564\u1565\u1566\u1567\u1568\u1569\u156a\u156b\u156c\u156d\u156e\u156f\u1570\u1571\u1572\u1573\u1574\u1575\u1576\u1577\u1578\u1579\u157a\u157b\u157c\u157d\u157e\u157f\u1580\u1581\u1582\u1583\u1584\u1585\u1586\u1587\u1588\u1589\u158a\u158b\u158c\u158d\u158e\u158f\u1590\u1591\u1592\u1593\u1594\u1595\u1596\u1597\u1598\u1599\u159a\u159b\u159c\u159d\u159e\u159f\u15a0\u15a1\u15a2\u15a3\u15a4\u15a5\u15a6\u15a7\u15a8\u15a9\u15aa\u15ab\u15ac\u15ad\u15ae\u15af\u15b0\u15b1\u15b2\u15b3\u15b4\u15b5\u15b6\u15b7\u15b8\u15b9\u15ba\u15bb\u15bc\u15bd\u15be\u15bf\u15c0\u15c1\u15c2\u15c3\u15c4\u15c5\u15c6\u15c7\u15c8\u15c9\u15ca\u15cb\u15cc\u15cd\u15ce\u15cf\u15d0\u15d1\u15d2\u15d3\u15d4\u15d5\u15d6\u15d7\u15d8\u15d9\u15da\u15db\u15dc\u15dd\u15de\u15df\u15e0\u15e1\u15e2\u15e3\u15e4\u15e5\u15e6\u15e7\u15e8\u15e9\u15ea\u15eb\u15ec\u15ed\u15ee\u15ef\u15f0\u15f1\u15f2\u15f3\u15f4\u15f5\u15f6\u15f7\u15f8\u15f9\u15fa\u15fb\u15fc\u15fd\u15fe\u15ff\u1600\u1601\u1602\u1603\u1604\u1605\u1606\u1607\u1608\u1609\u160a\u160b\u160c\u160d\u160e\u160f\u1610\u1611\u1612\u1613\u1614\u1615\u1616\u1617\u1618\u1619\u161a\u161b\u161c\u161d\u161e\u161f\u1620\u1621\u1622\u1623\u1624\u1625\u1626\u1627\u1628\u1629\u162a\u162b\u162c\u162d\u162e\u162f\u1630\u1631\u1632\u1633\u1634\u1635\u1636\u1637\u1638\u1639\u163a\u163b\u163c\u163d\u163e\u163f\u1640\u1641\u1642\u1643\u1644\u1645\u1646\u1647\u1648\u1649\u164a\u164b\u164c\u164d\u164e\u164f\u1650\u1651\u1652\u1653\u1654\u1655\u1656\u1657\u1658\u1659\u165a\u165b\u165c\u165d\u165e\u165f\u1660\u1661\u1662\u1663\u1664\u1665\u1666\u1667\u1668\u1669\u166a\u166b\u166c\u166f\u1670\u1671\u1672\u1673\u1674\u1675\u1676\u1681\u1682\u1683\u1684\u1685\u1686\u1687\u1688\u1689\u168a\u168b\u168c\u168d\u168e\u168f\u1690\u1691\u1692\u1693\u1694\u1695\u1696\u1697\u1698\u1699\u169a\u16a0\u16a1\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af\u16b0\u16b1\u16b2\u16b3\u16b4\u16b5\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u1700\u1701\u1702\u1703\u1704\u1705\u1706\u1707\u1708\u1709\u170a\u170b\u170c\u170e\u170f\u1710\u1711\u1720\u1721\u1722\u1723\u1724\u1725\u1726\u1727\u1728\u1729\u172a\u172b\u172c\u172d\u172e\u172f\u1730\u1731\u1740\u1741\u1742\u1743\u1744\u1745\u1746\u1747\u1748\u1749\u174a\u174b\u174c\u174d\u174e\u174f\u1750\u1751\u1760\u1761\u1762\u1763\u1764\u1765\u1766\u1767\u1768\u1769\u176a\u176b\u176c\u176e\u176f\u1770\u1780\u1781\u1782\u1783\u1784\u1785\u1786\u1787\u1788\u1789\u178a\u178b\u178c\u178d\u178e\u178f\u1790\u1791\u1792\u1793\u1794\u1795\u1796\u1797\u1798\u1799\u179a\u179b\u179c\u179d\u179e\u179f\u17a0\u17a1\u17a2\u17a3\u17a4\u17a5\u17a6\u17a7\u17a8\u17a9\u17aa\u17ab\u17ac\u17ad\u17ae\u17af\u17b0\u17b1\u17b2\u17b3\u17dc\u1820\u1821\u1822\u1823\u1824\u1825\u1826\u1827\u1828\u1829\u182a\u182b\u182c\u182d\u182e\u182f\u1830\u1831\u1832\u1833\u1834\u1835\u1836\u1837\u1838\u1839\u183a\u183b\u183c\u183d\u183e\u183f\u1840\u1841\u1842\u1844\u1845\u1846\u1847\u1848\u1849\u184a\u184b\u184c\u184d\u184e\u184f\u1850\u1851\u1852\u1853\u1854\u1855\u1856\u1857\u1858\u1859\u185a\u185b\u185c\u185d\u185e\u185f\u1860\u1861\u1862\u1863\u1864\u1865\u1866\u1867\u1868\u1869\u186a\u186b\u186c\u186d\u186e\u186f\u1870\u1871\u1872\u1873\u1874\u1875\u1876\u1877\u1880\u1881\u1882\u1883\u1884\u1885\u1886\u1887\u1888\u1889\u188a\u188b\u188c\u188d\u188e\u188f\u1890\u1891\u1892\u1893\u1894\u1895\u1896\u1897\u1898\u1899\u189a\u189b\u189c\u189d\u189e\u189f\u18a0\u18a1\u18a2\u18a3\u18a4\u18a5\u18a6\u18a7\u18a8\u1900\u1901\u1902\u1903\u1904\u1905\u1906\u1907\u1908\u1909\u190a\u190b\u190c\u190d\u190e\u190f\u1910\u1911\u1912\u1913\u1914\u1915\u1916\u1917\u1918\u1919\u191a\u191b\u191c\u1950\u1951\u1952\u1953\u1954\u1955\u1956\u1957\u1958\u1959\u195a\u195b\u195c\u195d\u195e\u195f\u1960\u1961\u1962\u1963\u1964\u1965\u1966\u1967\u1968\u1969\u196a\u196b\u196c\u196d\u1970\u1971\u1972\u1973\u1974\u1980\u1981\u1982\u1983\u1984\u1985\u1986\u1987\u1988\u1989\u198a\u198b\u198c\u198d\u198e\u198f\u1990\u1991\u1992\u1993\u1994\u1995\u1996\u1997\u1998\u1999\u199a\u199b\u199c\u199d\u199e\u199f\u19a0\u19a1\u19a2\u19a3\u19a4\u19a5\u19a6\u19a7\u19a8\u19a9\u19c1\u19c2\u19c3\u19c4\u19c5\u19c6\u19c7\u1a00\u1a01\u1a02\u1a03\u1a04\u1a05\u1a06\u1a07\u1a08\u1a09\u1a0a\u1a0b\u1a0c\u1a0d\u1a0e\u1a0f\u1a10\u1a11\u1a12\u1a13\u1a14\u1a15\u1a16\u2135\u2136\u2137\u2138\u2d30\u2d31\u2d32\u2d33\u2d34\u2d35\u2d36\u2d37\u2d38\u2d39\u2d3a\u2d3b\u2d3c\u2d3d\u2d3e\u2d3f\u2d40\u2d41\u2d42\u2d43\u2d44\u2d45\u2d46\u2d47\u2d48\u2d49\u2d4a\u2d4b\u2d4c\u2d4d\u2d4e\u2d4f\u2d50\u2d51\u2d52\u2d53\u2d54\u2d55\u2d56\u2d57\u2d58\u2d59\u2d5a\u2d5b\u2d5c\u2d5d\u2d5e\u2d5f\u2d60\u2d61\u2d62\u2d63\u2d64\u2d65\u2d80\u2d81\u2d82\u2d83\u2d84\u2d85\u2d86\u2d87\u2d88\u2d89\u2d8a\u2d8b\u2d8c\u2d8d\u2d8e\u2d8f\u2d90\u2d91\u2d92\u2d93\u2d94\u2d95\u2d96\u2da0\u2da1\u2da2\u2da3\u2da4\u2da5\u2da6\u2da8\u2da9\u2daa\u2dab\u2dac\u2dad\u2dae\u2db0\u2db1\u2db2\u2db3\u2db4\u2db5\u2db6\u2db8\u2db9\u2dba\u2dbb\u2dbc\u2dbd\u2dbe\u2dc0\u2dc1\u2dc2\u2dc3\u2dc4\u2dc5\u2dc6\u2dc8\u2dc9\u2dca\u2dcb\u2dcc\u2dcd\u2dce\u2dd0\u2dd1\u2dd2\u2dd3\u2dd4\u2dd5\u2dd6\u2dd8\u2dd9\u2dda\u2ddb\u2ddc\u2ddd\u2dde\u3006\u303c\u3041\u3042\u3043\u3044\u3045\u3046\u3047\u3048\u3049\u304a\u304b\u304c\u304d\u304e\u304f\u3050\u3051\u3052\u3053\u3054\u3055\u3056\u3057\u3058\u3059\u305a\u305b\u305c\u305d\u305e\u305f\u3060\u3061\u3062\u3063\u3064\u3065\u3066\u3067\u3068\u3069\u306a\u306b\u306c\u306d\u306e\u306f\u3070\u3071\u3072\u3073\u3074\u3075\u3076\u3077\u3078\u3079\u307a\u307b\u307c\u307d\u307e\u307f\u3080\u3081\u3082\u3083\u3084\u3085\u3086\u3087\u3088\u3089\u308a\u308b\u308c\u308d\u308e\u308f\u3090\u3091\u3092\u3093\u3094\u3095\u3096\u309f\u30a1\u30a2\u30a3\u30a4\u30a5\u30a6\u30a7\u30a8\u30a9\u30aa\u30ab\u30ac\u30ad\u30ae\u30af\u30b0\u30b1\u30b2\u30b3\u30b4\u30b5\u30b6\u30b7\u30b8\u30b9\u30ba\u30bb\u30bc\u30bd\u30be\u30bf\u30c0\u30c1\u30c2\u30c3\u30c4\u30c5\u30c6\u30c7\u30c8\u30c9\u30ca\u30cb\u30cc\u30cd\u30ce\u30cf\u30d0\u30d1\u30d2\u30d3\u30d4\u30d5\u30d6\u30d7\u30d8\u30d9\u30da\u30db\u30dc\u30dd\u30de\u30df\u30e0\u30e1\u30e2\u30e3\u30e4\u30e5\u30e6\u30e7\u30e8\u30e9\u30ea\u30eb\u30ec\u30ed\u30ee\u30ef\u30f0\u30f1\u30f2\u30f3\u30f4\u30f5\u30f6\u30f7\u30f8\u30f9\u30fa\u30ff\u3105\u3106\u3107\u3108\u3109\u310a\u310b\u310c\u310d\u310e\u310f\u3110\u3111\u3112\u3113\u3114\u3115\u3116\u3117\u3118\u3119\u311a\u311b\u311c\u311d\u311e\u311f\u3120\u3121\u3122\u3123\u3124\u3125\u3126\u3127\u3128\u3129\u312a\u312b\u312c\u3131\u3132\u3133\u3134\u3135\u3136\u3137\u3138\u3139\u313a\u313b\u313c\u313d\u313e\u313f\u3140\u3141\u3142\u3143\u3144\u3145\u3146\u3147\u3148\u3149\u314a\u314b\u314c\u314d\u314e\u314f\u3150\u3151\u3152\u3153\u3154\u3155\u3156\u3157\u3158\u3159\u315a\u315b\u315c\u315d\u315e\u315f\u3160\u3161\u3162\u3163\u3164\u3165\u3166\u3167\u3168\u3169\u316a\u316b\u316c\u316d\u316e\u316f\u3170\u3171\u3172\u3173\u3174\u3175\u3176\u3177\u3178\u3179\u317a\u317b\u317c\u317d\u317e\u317f\u3180\u3181\u3182\u3183\u3184\u3185\u3186\u3187\u3188\u3189\u318a\u318b\u318c\u318d\u318e\u31a0\u31a1\u31a2\u31a3\u31a4\u31a5\u31a6\u31a7\u31a8\u31a9\u31aa\u31ab\u31ac\u31ad\u31ae\u31af\u31b0\u31b1\u31b2\u31b3\u31b4\u31b5\u31b6\u31b7\u31f0\u31f1\u31f2\u31f3\u31f4\u31f5\u31f6\u31f7\u31f8\u31f9\u31fa\u31fb\u31fc\u31fd\u31fe\u31ff\u3400\u3401\u3402\u3403\u3404\u3405\u3406\u3407\u3408\u3409\u340a\u340b\u340c\u340d\u340e\u340f\u3410\u3411\u3412\u3413\u3414\u3415\u3416\u3417\u3418\u3419\u341a\u341b\u341c\u341d\u341e\u341f\u3420\u3421\u3422\u3423\u3424\u3425\u3426\u3427\u3428\u3429\u342a\u342b\u342c\u342d\u342e\u342f\u3430\u3431\u3432\u3433\u3434\u3435\u3436\u3437\u3438\u3439\u343a\u343b\u343c\u343d\u343e\u343f\u3440\u3441\u3442\u3443\u3444\u3445\u3446\u3447\u3448\u3449\u344a\u344b\u344c\u344d\u344e\u344f\u3450\u3451\u3452\u3453\u3454\u3455\u3456\u3457\u3458\u3459\u345a\u345b\u345c\u345d\u345e\u345f\u3460\u3461\u3462\u3463\u3464\u3465\u3466\u3467\u3468\u3469\u346a\u346b\u346c\u346d\u346e\u346f\u3470\u3471\u3472\u3473\u3474\u3475\u3476\u3477\u3478\u3479\u347a\u347b\u347c\u347d\u347e\u347f\u3480\u3481\u3482\u3483\u3484\u3485\u3486\u3487\u3488\u3489\u348a\u348b\u348c\u348d\u348e\u348f\u3490\u3491\u3492\u3493\u3494\u3495\u3496\u3497\u3498\u3499\u349a\u349b\u349c\u349d\u349e\u349f\u34a0\u34a1\u34a2\u34a3\u34a4\u34a5\u34a6\u34a7\u34a8\u34a9\u34aa\u34ab\u34ac\u34ad\u34ae\u34af\u34b0\u34b1\u34b2\u34b3\u34b4\u34b5\u34b6\u34b7\u34b8\u34b9\u34ba\u34bb\u34bc\u34bd\u34be\u34bf\u34c0\u34c1\u34c2\u34c3\u34c4\u34c5\u34c6\u34c7\u34c8\u34c9\u34ca\u34cb\u34cc\u34cd\u34ce\u34cf\u34d0\u34d1\u34d2\u34d3\u34d4\u34d5\u34d6\u34d7\u34d8\u34d9\u34da\u34db\u34dc\u34dd\u34de\u34df\u34e0\u34e1\u34e2\u34e3\u34e4\u34e5\u34e6\u34e7\u34e8\u34e9\u34ea\u34eb\u34ec\u34ed\u34ee\u34ef\u34f0\u34f1\u34f2\u34f3\u34f4\u34f5\u34f6\u34f7\u34f8\u34f9\u34fa\u34fb\u34fc\u34fd\u34fe\u34ff\u3500\u3501\u3502\u3503\u3504\u3505\u3506\u3507\u3508\u3509\u350a\u350b\u350c\u350d\u350e\u350f\u3510\u3511\u3512\u3513\u3514\u3515\u3516\u3517\u3518\u3519\u351a\u351b\u351c\u351d\u351e\u351f\u3520\u3521\u3522\u3523\u3524\u3525\u3526\u3527\u3528\u3529\u352a\u352b\u352c\u352d\u352e\u352f\u3530\u3531\u3532\u3533\u3534\u3535\u3536\u3537\u3538\u3539\u353a\u353b\u353c\u353d\u353e\u353f\u3540\u3541\u3542\u3543\u3544\u3545\u3546\u3547\u3548\u3549\u354a\u354b\u354c\u354d\u354e\u354f\u3550\u3551\u3552\u3553\u3554\u3555\u3556\u3557\u3558\u3559\u355a\u355b\u355c\u355d\u355e\u355f\u3560\u3561\u3562\u3563\u3564\u3565\u3566\u3567\u3568\u3569\u356a\u356b\u356c\u356d\u356e\u356f\u3570\u3571\u3572\u3573\u3574\u3575\u3576\u3577\u3578\u3579\u357a\u357b\u357c\u357d\u357e\u357f\u3580\u3581\u3582\u3583\u3584\u3585\u3586\u3587\u3588\u3589\u358a\u358b\u358c\u358d\u358e\u358f\u3590\u3591\u3592\u3593\u3594\u3595\u3596\u3597\u3598\u3599\u359a\u359b\u359c\u359d\u359e\u359f\u35a0\u35a1\u35a2\u35a3\u35a4\u35a5\u35a6\u35a7\u35a8\u35a9\u35aa\u35ab\u35ac\u35ad\u35ae\u35af\u35b0\u35b1\u35b2\u35b3\u35b4\u35b5\u35b6\u35b7\u35b8\u35b9\u35ba\u35bb\u35bc\u35bd\u35be\u35bf\u35c0\u35c1\u35c2\u35c3\u35c4\u35c5\u35c6\u35c7\u35c8\u35c9\u35ca\u35cb\u35cc\u35cd\u35ce\u35cf\u35d0\u35d1\u35d2\u35d3\u35d4\u35d5\u35d6\u35d7\u35d8\u35d9\u35da\u35db\u35dc\u35dd\u35de\u35df\u35e0\u35e1\u35e2\u35e3\u35e4\u35e5\u35e6\u35e7\u35e8\u35e9\u35ea\u35eb\u35ec\u35ed\u35ee\u35ef\u35f0\u35f1\u35f2\u35f3\u35f4\u35f5\u35f6\u35f7\u35f8\u35f9\u35fa\u35fb\u35fc\u35fd\u35fe\u35ff\u3600\u3601\u3602\u3603\u3604\u3605\u3606\u3607\u3608\u3609\u360a\u360b\u360c\u360d\u360e\u360f\u3610\u3611\u3612\u3613\u3614\u3615\u3616\u3617\u3618\u3619\u361a\u361b\u361c\u361d\u361e\u361f\u3620\u3621\u3622\u3623\u3624\u3625\u3626\u3627\u3628\u3629\u362a\u362b\u362c\u362d\u362e\u362f\u3630\u3631\u3632\u3633\u3634\u3635\u3636\u3637\u3638\u3639\u363a\u363b\u363c\u363d\u363e\u363f\u3640\u3641\u3642\u3643\u3644\u3645\u3646\u3647\u3648\u3649\u364a\u364b\u364c\u364d\u364e\u364f\u3650\u3651\u3652\u3653\u3654\u3655\u3656\u3657\u3658\u3659\u365a\u365b\u365c\u365d\u365e\u365f\u3660\u3661\u3662\u3663\u3664\u3665\u3666\u3667\u3668\u3669\u366a\u366b\u366c\u366d\u366e\u366f\u3670\u3671\u3672\u3673\u3674\u3675\u3676\u3677\u3678\u3679\u367a\u367b\u367c\u367d\u367e\u367f\u3680\u3681\u3682\u3683\u3684\u3685\u3686\u3687\u3688\u3689\u368a\u368b\u368c\u368d\u368e\u368f\u3690\u3691\u3692\u3693\u3694\u3695\u3696\u3697\u3698\u3699\u369a\u369b\u369c\u369d\u369e\u369f\u36a0\u36a1\u36a2\u36a3\u36a4\u36a5\u36a6\u36a7\u36a8\u36a9\u36aa\u36ab\u36ac\u36ad\u36ae\u36af\u36b0\u36b1\u36b2\u36b3\u36b4\u36b5\u36b6\u36b7\u36b8\u36b9\u36ba\u36bb\u36bc\u36bd\u36be\u36bf\u36c0\u36c1\u36c2\u36c3\u36c4\u36c5\u36c6\u36c7\u36c8\u36c9\u36ca\u36cb\u36cc\u36cd\u36ce\u36cf\u36d0\u36d1\u36d2\u36d3\u36d4\u36d5\u36d6\u36d7\u36d8\u36d9\u36da\u36db\u36dc\u36dd\u36de\u36df\u36e0\u36e1\u36e2\u36e3\u36e4\u36e5\u36e6\u36e7\u36e8\u36e9\u36ea\u36eb\u36ec\u36ed\u36ee\u36ef\u36f0\u36f1\u36f2\u36f3\u36f4\u36f5\u36f6\u36f7\u36f8\u36f9\u36fa\u36fb\u36fc\u36fd\u36fe\u36ff\u3700\u3701\u3702\u3703\u3704\u3705\u3706\u3707\u3708\u3709\u370a\u370b\u370c\u370d\u370e\u370f\u3710\u3711\u3712\u3713\u3714\u3715\u3716\u3717\u3718\u3719\u371a\u371b\u371c\u371d\u371e\u371f\u3720\u3721\u3722\u3723\u3724\u3725\u3726\u3727\u3728\u3729\u372a\u372b\u372c\u372d\u372e\u372f\u3730\u3731\u3732\u3733\u3734\u3735\u3736\u3737\u3738\u3739\u373a\u373b\u373c\u373d\u373e\u373f\u3740\u3741\u3742\u3743\u3744\u3745\u3746\u3747\u3748\u3749\u374a\u374b\u374c\u374d\u374e\u374f\u3750\u3751\u3752\u3753\u3754\u3755\u3756\u3757\u3758\u3759\u375a\u375b\u375c\u375d\u375e\u375f\u3760\u3761\u3762\u3763\u3764\u3765\u3766\u3767\u3768\u3769\u376a\u376b\u376c\u376d\u376e\u376f\u3770\u3771\u3772\u3773\u3774\u3775\u3776\u3777\u3778\u3779\u377a\u377b\u377c\u377d\u377e\u377f\u3780\u3781\u3782\u3783\u3784\u3785\u3786\u3787\u3788\u3789\u378a\u378b\u378c\u378d\u378e\u378f\u3790\u3791\u3792\u3793\u3794\u3795\u3796\u3797\u3798\u3799\u379a\u379b\u379c\u379d\u379e\u379f\u37a0\u37a1\u37a2\u37a3\u37a4\u37a5\u37a6\u37a7\u37a8\u37a9\u37aa\u37ab\u37ac\u37ad\u37ae\u37af\u37b0\u37b1\u37b2\u37b3\u37b4\u37b5\u37b6\u37b7\u37b8\u37b9\u37ba\u37bb\u37bc\u37bd\u37be\u37bf\u37c0\u37c1\u37c2\u37c3\u37c4\u37c5\u37c6\u37c7\u37c8\u37c9\u37ca\u37cb\u37cc\u37cd\u37ce\u37cf\u37d0\u37d1\u37d2\u37d3\u37d4\u37d5\u37d6\u37d7\u37d8\u37d9\u37da\u37db\u37dc\u37dd\u37de\u37df\u37e0\u37e1\u37e2\u37e3\u37e4\u37e5\u37e6\u37e7\u37e8\u37e9\u37ea\u37eb\u37ec\u37ed\u37ee\u37ef\u37f0\u37f1\u37f2\u37f3\u37f4\u37f5\u37f6\u37f7\u37f8\u37f9\u37fa\u37fb\u37fc\u37fd\u37fe\u37ff\u3800\u3801\u3802\u3803\u3804\u3805\u3806\u3807\u3808\u3809\u380a\u380b\u380c\u380d\u380e\u380f\u3810\u3811\u3812\u3813\u3814\u3815\u3816\u3817\u3818\u3819\u381a\u381b\u381c\u381d\u381e\u381f\u3820\u3821\u3822\u3823\u3824\u3825\u3826\u3827\u3828\u3829\u382a\u382b\u382c\u382d\u382e\u382f\u3830\u3831\u3832\u3833\u3834\u3835\u3836\u3837\u3838\u3839\u383a\u383b\u383c\u383d\u383e\u383f\u3840\u3841\u3842\u3843\u3844\u3845\u3846\u3847\u3848\u3849\u384a\u384b\u384c\u384d\u384e\u384f\u3850\u3851\u3852\u3853\u3854\u3855\u3856\u3857\u3858\u3859\u385a\u385b\u385c\u385d\u385e\u385f\u3860\u3861\u3862\u3863\u3864\u3865\u3866\u3867\u3868\u3869\u386a\u386b\u386c\u386d\u386e\u386f\u3870\u3871\u3872\u3873\u3874\u3875\u3876\u3877\u3878\u3879\u387a\u387b\u387c\u387d\u387e\u387f\u3880\u3881\u3882\u3883\u3884\u3885\u3886\u3887\u3888\u3889\u388a\u388b\u388c\u388d\u388e\u388f\u3890\u3891\u3892\u3893\u3894\u3895\u3896\u3897\u3898\u3899\u389a\u389b\u389c\u389d\u389e\u389f\u38a0\u38a1\u38a2\u38a3\u38a4\u38a5\u38a6\u38a7\u38a8\u38a9\u38aa\u38ab\u38ac\u38ad\u38ae\u38af\u38b0\u38b1\u38b2\u38b3\u38b4\u38b5\u38b6\u38b7\u38b8\u38b9\u38ba\u38bb\u38bc\u38bd\u38be\u38bf\u38c0\u38c1\u38c2\u38c3\u38c4\u38c5\u38c6\u38c7\u38c8\u38c9\u38ca\u38cb\u38cc\u38cd\u38ce\u38cf\u38d0\u38d1\u38d2\u38d3\u38d4\u38d5\u38d6\u38d7\u38d8\u38d9\u38da\u38db\u38dc\u38dd\u38de\u38df\u38e0\u38e1\u38e2\u38e3\u38e4\u38e5\u38e6\u38e7\u38e8\u38e9\u38ea\u38eb\u38ec\u38ed\u38ee\u38ef\u38f0\u38f1\u38f2\u38f3\u38f4\u38f5\u38f6\u38f7\u38f8\u38f9\u38fa\u38fb\u38fc\u38fd\u38fe\u38ff\u3900\u3901\u3902\u3903\u3904\u3905\u3906\u3907\u3908\u3909\u390a\u390b\u390c\u390d\u390e\u390f\u3910\u3911\u3912\u3913\u3914\u3915\u3916\u3917\u3918\u3919\u391a\u391b\u391c\u391d\u391e\u391f\u3920\u3921\u3922\u3923\u3924\u3925\u3926\u3927\u3928\u3929\u392a\u392b\u392c\u392d\u392e\u392f\u3930\u3931\u3932\u3933\u3934\u3935\u3936\u3937\u3938\u3939\u393a\u393b\u393c\u393d\u393e\u393f\u3940\u3941\u3942\u3943\u3944\u3945\u3946\u3947\u3948\u3949\u394a\u394b\u394c\u394d\u394e\u394f\u3950\u3951\u3952\u3953\u3954\u3955\u3956\u3957\u3958\u3959\u395a\u395b\u395c\u395d\u395e\u395f\u3960\u3961\u3962\u3963\u3964\u3965\u3966\u3967\u3968\u3969\u396a\u396b\u396c\u396d\u396e\u396f\u3970\u3971\u3972\u3973\u3974\u3975\u3976\u3977\u3978\u3979\u397a\u397b\u397c\u397d\u397e\u397f\u3980\u3981\u3982\u3983\u3984\u3985\u3986\u3987\u3988\u3989\u398a\u398b\u398c\u398d\u398e\u398f\u3990\u3991\u3992\u3993\u3994\u3995\u3996\u3997\u3998\u3999\u399a\u399b\u399c\u399d\u399e\u399f\u39a0\u39a1\u39a2\u39a3\u39a4\u39a5\u39a6\u39a7\u39a8\u39a9\u39aa\u39ab\u39ac\u39ad\u39ae\u39af\u39b0\u39b1\u39b2\u39b3\u39b4\u39b5\u39b6\u39b7\u39b8\u39b9\u39ba\u39bb\u39bc\u39bd\u39be\u39bf\u39c0\u39c1\u39c2\u39c3\u39c4\u39c5\u39c6\u39c7\u39c8\u39c9\u39ca\u39cb\u39cc\u39cd\u39ce\u39cf\u39d0\u39d1\u39d2\u39d3\u39d4\u39d5\u39d6\u39d7\u39d8\u39d9\u39da\u39db\u39dc\u39dd\u39de\u39df\u39e0\u39e1\u39e2\u39e3\u39e4\u39e5\u39e6\u39e7\u39e8\u39e9\u39ea\u39eb\u39ec\u39ed\u39ee\u39ef\u39f0\u39f1\u39f2\u39f3\u39f4\u39f5\u39f6\u39f7\u39f8\u39f9\u39fa\u39fb\u39fc\u39fd\u39fe\u39ff\u3a00\u3a01\u3a02\u3a03\u3a04\u3a05\u3a06\u3a07\u3a08\u3a09\u3a0a\u3a0b\u3a0c\u3a0d\u3a0e\u3a0f\u3a10\u3a11\u3a12\u3a13\u3a14\u3a15\u3a16\u3a17\u3a18\u3a19\u3a1a\u3a1b\u3a1c\u3a1d\u3a1e\u3a1f\u3a20\u3a21\u3a22\u3a23\u3a24\u3a25\u3a26\u3a27\u3a28\u3a29\u3a2a\u3a2b\u3a2c\u3a2d\u3a2e\u3a2f\u3a30\u3a31\u3a32\u3a33\u3a34\u3a35\u3a36\u3a37\u3a38\u3a39\u3a3a\u3a3b\u3a3c\u3a3d\u3a3e\u3a3f\u3a40\u3a41\u3a42\u3a43\u3a44\u3a45\u3a46\u3a47\u3a48\u3a49\u3a4a\u3a4b\u3a4c\u3a4d\u3a4e\u3a4f\u3a50\u3a51\u3a52\u3a53\u3a54\u3a55\u3a56\u3a57\u3a58\u3a59\u3a5a\u3a5b\u3a5c\u3a5d\u3a5e\u3a5f\u3a60\u3a61\u3a62\u3a63\u3a64\u3a65\u3a66\u3a67\u3a68\u3a69\u3a6a\u3a6b\u3a6c\u3a6d\u3a6e\u3a6f\u3a70\u3a71\u3a72\u3a73\u3a74\u3a75\u3a76\u3a77\u3a78\u3a79\u3a7a\u3a7b\u3a7c\u3a7d\u3a7e\u3a7f\u3a80\u3a81\u3a82\u3a83\u3a84\u3a85\u3a86\u3a87\u3a88\u3a89\u3a8a\u3a8b\u3a8c\u3a8d\u3a8e\u3a8f\u3a90\u3a91\u3a92\u3a93\u3a94\u3a95\u3a96\u3a97\u3a98\u3a99\u3a9a\u3a9b\u3a9c\u3a9d\u3a9e\u3a9f\u3aa0\u3aa1\u3aa2\u3aa3\u3aa4\u3aa5\u3aa6\u3aa7\u3aa8\u3aa9\u3aaa\u3aab\u3aac\u3aad\u3aae\u3aaf\u3ab0\u3ab1\u3ab2\u3ab3\u3ab4\u3ab5\u3ab6\u3ab7\u3ab8\u3ab9\u3aba\u3abb\u3abc\u3abd\u3abe\u3abf\u3ac0\u3ac1\u3ac2\u3ac3\u3ac4\u3ac5\u3ac6\u3ac7\u3ac8\u3ac9\u3aca\u3acb\u3acc\u3acd\u3ace\u3acf\u3ad0\u3ad1\u3ad2\u3ad3\u3ad4\u3ad5\u3ad6\u3ad7\u3ad8\u3ad9\u3ada\u3adb\u3adc\u3add\u3ade\u3adf\u3ae0\u3ae1\u3ae2\u3ae3\u3ae4\u3ae5\u3ae6\u3ae7\u3ae8\u3ae9\u3aea\u3aeb\u3aec\u3aed\u3aee\u3aef\u3af0\u3af1\u3af2\u3af3\u3af4\u3af5\u3af6\u3af7\u3af8\u3af9\u3afa\u3afb\u3afc\u3afd\u3afe\u3aff\u3b00\u3b01\u3b02\u3b03\u3b04\u3b05\u3b06\u3b07\u3b08\u3b09\u3b0a\u3b0b\u3b0c\u3b0d\u3b0e\u3b0f\u3b10\u3b11\u3b12\u3b13\u3b14\u3b15\u3b16\u3b17\u3b18\u3b19\u3b1a\u3b1b\u3b1c\u3b1d\u3b1e\u3b1f\u3b20\u3b21\u3b22\u3b23\u3b24\u3b25\u3b26\u3b27\u3b28\u3b29\u3b2a\u3b2b\u3b2c\u3b2d\u3b2e\u3b2f\u3b30\u3b31\u3b32\u3b33\u3b34\u3b35\u3b36\u3b37\u3b38\u3b39\u3b3a\u3b3b\u3b3c\u3b3d\u3b3e\u3b3f\u3b40\u3b41\u3b42\u3b43\u3b44\u3b45\u3b46\u3b47\u3b48\u3b49\u3b4a\u3b4b\u3b4c\u3b4d\u3b4e\u3b4f\u3b50\u3b51\u3b52\u3b53\u3b54\u3b55\u3b56\u3b57\u3b58\u3b59\u3b5a\u3b5b\u3b5c\u3b5d\u3b5e\u3b5f\u3b60\u3b61\u3b62\u3b63\u3b64\u3b65\u3b66\u3b67\u3b68\u3b69\u3b6a\u3b6b\u3b6c\u3b6d\u3b6e\u3b6f\u3b70\u3b71\u3b72\u3b73\u3b74\u3b75\u3b76\u3b77\u3b78\u3b79\u3b7a\u3b7b\u3b7c\u3b7d\u3b7e\u3b7f\u3b80\u3b81\u3b82\u3b83\u3b84\u3b85\u3b86\u3b87\u3b88\u3b89\u3b8a\u3b8b\u3b8c\u3b8d\u3b8e\u3b8f\u3b90\u3b91\u3b92\u3b93\u3b94\u3b95\u3b96\u3b97\u3b98\u3b99\u3b9a\u3b9b\u3b9c\u3b9d\u3b9e\u3b9f\u3ba0\u3ba1\u3ba2\u3ba3\u3ba4\u3ba5\u3ba6\u3ba7\u3ba8\u3ba9\u3baa\u3bab\u3bac\u3bad\u3bae\u3baf\u3bb0\u3bb1\u3bb2\u3bb3\u3bb4\u3bb5\u3bb6\u3bb7\u3bb8\u3bb9\u3bba\u3bbb\u3bbc\u3bbd\u3bbe\u3bbf\u3bc0\u3bc1\u3bc2\u3bc3\u3bc4\u3bc5\u3bc6\u3bc7\u3bc8\u3bc9\u3bca\u3bcb\u3bcc\u3bcd\u3bce\u3bcf\u3bd0\u3bd1\u3bd2\u3bd3\u3bd4\u3bd5\u3bd6\u3bd7\u3bd8\u3bd9\u3bda\u3bdb\u3bdc\u3bdd\u3bde\u3bdf\u3be0\u3be1\u3be2\u3be3\u3be4\u3be5\u3be6\u3be7\u3be8\u3be9\u3bea\u3beb\u3bec\u3bed\u3bee\u3bef\u3bf0\u3bf1\u3bf2\u3bf3\u3bf4\u3bf5\u3bf6\u3bf7\u3bf8\u3bf9\u3bfa\u3bfb\u3bfc\u3bfd\u3bfe\u3bff\u3c00\u3c01\u3c02\u3c03\u3c04\u3c05\u3c06\u3c07\u3c08\u3c09\u3c0a\u3c0b\u3c0c\u3c0d\u3c0e\u3c0f\u3c10\u3c11\u3c12\u3c13\u3c14\u3c15\u3c16\u3c17\u3c18\u3c19\u3c1a\u3c1b\u3c1c\u3c1d\u3c1e\u3c1f\u3c20\u3c21\u3c22\u3c23\u3c24\u3c25\u3c26\u3c27\u3c28\u3c29\u3c2a\u3c2b\u3c2c\u3c2d\u3c2e\u3c2f\u3c30\u3c31\u3c32\u3c33\u3c34\u3c35\u3c36\u3c37\u3c38\u3c39\u3c3a\u3c3b\u3c3c\u3c3d\u3c3e\u3c3f\u3c40\u3c41\u3c42\u3c43\u3c44\u3c45\u3c46\u3c47\u3c48\u3c49\u3c4a\u3c4b\u3c4c\u3c4d\u3c4e\u3c4f\u3c50\u3c51\u3c52\u3c53\u3c54\u3c55\u3c56\u3c57\u3c58\u3c59\u3c5a\u3c5b\u3c5c\u3c5d\u3c5e\u3c5f\u3c60\u3c61\u3c62\u3c63\u3c64\u3c65\u3c66\u3c67\u3c68\u3c69\u3c6a\u3c6b\u3c6c\u3c6d\u3c6e\u3c6f\u3c70\u3c71\u3c72\u3c73\u3c74\u3c75\u3c76\u3c77\u3c78\u3c79\u3c7a\u3c7b\u3c7c\u3c7d\u3c7e\u3c7f\u3c80\u3c81\u3c82\u3c83\u3c84\u3c85\u3c86\u3c87\u3c88\u3c89\u3c8a\u3c8b\u3c8c\u3c8d\u3c8e\u3c8f\u3c90\u3c91\u3c92\u3c93\u3c94\u3c95\u3c96\u3c97\u3c98\u3c99\u3c9a\u3c9b\u3c9c\u3c9d\u3c9e\u3c9f\u3ca0\u3ca1\u3ca2\u3ca3\u3ca4\u3ca5\u3ca6\u3ca7\u3ca8\u3ca9\u3caa\u3cab\u3cac\u3cad\u3cae\u3caf\u3cb0\u3cb1\u3cb2\u3cb3\u3cb4\u3cb5\u3cb6\u3cb7\u3cb8\u3cb9\u3cba\u3cbb\u3cbc\u3cbd\u3cbe\u3cbf\u3cc0\u3cc1\u3cc2\u3cc3\u3cc4\u3cc5\u3cc6\u3cc7\u3cc8\u3cc9\u3cca\u3ccb\u3ccc\u3ccd\u3cce\u3ccf\u3cd0\u3cd1\u3cd2\u3cd3\u3cd4\u3cd5\u3cd6\u3cd7\u3cd8\u3cd9\u3cda\u3cdb\u3cdc\u3cdd\u3cde\u3cdf\u3ce0\u3ce1\u3ce2\u3ce3\u3ce4\u3ce5\u3ce6\u3ce7\u3ce8\u3ce9\u3cea\u3ceb\u3cec\u3ced\u3cee\u3cef\u3cf0\u3cf1\u3cf2\u3cf3\u3cf4\u3cf5\u3cf6\u3cf7\u3cf8\u3cf9\u3cfa\u3cfb\u3cfc\u3cfd\u3cfe\u3cff\u3d00\u3d01\u3d02\u3d03\u3d04\u3d05\u3d06\u3d07\u3d08\u3d09\u3d0a\u3d0b\u3d0c\u3d0d\u3d0e\u3d0f\u3d10\u3d11\u3d12\u3d13\u3d14\u3d15\u3d16\u3d17\u3d18\u3d19\u3d1a\u3d1b\u3d1c\u3d1d\u3d1e\u3d1f\u3d20\u3d21\u3d22\u3d23\u3d24\u3d25\u3d26\u3d27\u3d28\u3d29\u3d2a\u3d2b\u3d2c\u3d2d\u3d2e\u3d2f\u3d30\u3d31\u3d32\u3d33\u3d34\u3d35\u3d36\u3d37\u3d38\u3d39\u3d3a\u3d3b\u3d3c\u3d3d\u3d3e\u3d3f\u3d40\u3d41\u3d42\u3d43\u3d44\u3d45\u3d46\u3d47\u3d48\u3d49\u3d4a\u3d4b\u3d4c\u3d4d\u3d4e\u3d4f\u3d50\u3d51\u3d52\u3d53\u3d54\u3d55\u3d56\u3d57\u3d58\u3d59\u3d5a\u3d5b\u3d5c\u3d5d\u3d5e\u3d5f\u3d60\u3d61\u3d62\u3d63\u3d64\u3d65\u3d66\u3d67\u3d68\u3d69\u3d6a\u3d6b\u3d6c\u3d6d\u3d6e\u3d6f\u3d70\u3d71\u3d72\u3d73\u3d74\u3d75\u3d76\u3d77\u3d78\u3d79\u3d7a\u3d7b\u3d7c\u3d7d\u3d7e\u3d7f\u3d80\u3d81\u3d82\u3d83\u3d84\u3d85\u3d86\u3d87\u3d88\u3d89\u3d8a\u3d8b\u3d8c\u3d8d\u3d8e\u3d8f\u3d90\u3d91\u3d92\u3d93\u3d94\u3d95\u3d96\u3d97\u3d98\u3d99\u3d9a\u3d9b\u3d9c\u3d9d\u3d9e\u3d9f\u3da0\u3da1\u3da2\u3da3\u3da4\u3da5\u3da6\u3da7\u3da8\u3da9\u3daa\u3dab\u3dac\u3dad\u3dae\u3daf\u3db0\u3db1\u3db2\u3db3\u3db4\u3db5\u3db6\u3db7\u3db8\u3db9\u3dba\u3dbb\u3dbc\u3dbd\u3dbe\u3dbf\u3dc0\u3dc1\u3dc2\u3dc3\u3dc4\u3dc5\u3dc6\u3dc7\u3dc8\u3dc9\u3dca\u3dcb\u3dcc\u3dcd\u3dce\u3dcf\u3dd0\u3dd1\u3dd2\u3dd3\u3dd4\u3dd5\u3dd6\u3dd7\u3dd8\u3dd9\u3dda\u3ddb\u3ddc\u3ddd\u3dde\u3ddf\u3de0\u3de1\u3de2\u3de3\u3de4\u3de5\u3de6\u3de7\u3de8\u3de9\u3dea\u3deb\u3dec\u3ded\u3dee\u3def\u3df0\u3df1\u3df2\u3df3\u3df4\u3df5\u3df6\u3df7\u3df8\u3df9\u3dfa\u3dfb\u3dfc\u3dfd\u3dfe\u3dff\u3e00\u3e01\u3e02\u3e03\u3e04\u3e05\u3e06\u3e07\u3e08\u3e09\u3e0a\u3e0b\u3e0c\u3e0d\u3e0e\u3e0f\u3e10\u3e11\u3e12\u3e13\u3e14\u3e15\u3e16\u3e17\u3e18\u3e19\u3e1a\u3e1b\u3e1c\u3e1d\u3e1e\u3e1f\u3e20\u3e21\u3e22\u3e23\u3e24\u3e25\u3e26\u3e27\u3e28\u3e29\u3e2a\u3e2b\u3e2c\u3e2d\u3e2e\u3e2f\u3e30\u3e31\u3e32\u3e33\u3e34\u3e35\u3e36\u3e37\u3e38\u3e39\u3e3a\u3e3b\u3e3c\u3e3d\u3e3e\u3e3f\u3e40\u3e41\u3e42\u3e43\u3e44\u3e45\u3e46\u3e47\u3e48\u3e49\u3e4a\u3e4b\u3e4c\u3e4d\u3e4e\u3e4f\u3e50\u3e51\u3e52\u3e53\u3e54\u3e55\u3e56\u3e57\u3e58\u3e59\u3e5a\u3e5b\u3e5c\u3e5d\u3e5e\u3e5f\u3e60\u3e61\u3e62\u3e63\u3e64\u3e65\u3e66\u3e67\u3e68\u3e69\u3e6a\u3e6b\u3e6c\u3e6d\u3e6e\u3e6f\u3e70\u3e71\u3e72\u3e73\u3e74\u3e75\u3e76\u3e77\u3e78\u3e79\u3e7a\u3e7b\u3e7c\u3e7d\u3e7e\u3e7f\u3e80\u3e81\u3e82\u3e83\u3e84\u3e85\u3e86\u3e87\u3e88\u3e89\u3e8a\u3e8b\u3e8c\u3e8d\u3e8e\u3e8f\u3e90\u3e91\u3e92\u3e93\u3e94\u3e95\u3e96\u3e97\u3e98\u3e99\u3e9a\u3e9b\u3e9c\u3e9d\u3e9e\u3e9f\u3ea0\u3ea1\u3ea2\u3ea3\u3ea4\u3ea5\u3ea6\u3ea7\u3ea8\u3ea9\u3eaa\u3eab\u3eac\u3ead\u3eae\u3eaf\u3eb0\u3eb1\u3eb2\u3eb3\u3eb4\u3eb5\u3eb6\u3eb7\u3eb8\u3eb9\u3eba\u3ebb\u3ebc\u3ebd\u3ebe\u3ebf\u3ec0\u3ec1\u3ec2\u3ec3\u3ec4\u3ec5\u3ec6\u3ec7\u3ec8\u3ec9\u3eca\u3ecb\u3ecc\u3ecd\u3ece\u3ecf\u3ed0\u3ed1\u3ed2\u3ed3\u3ed4\u3ed5\u3ed6\u3ed7\u3ed8\u3ed9\u3eda\u3edb\u3edc\u3edd\u3ede\u3edf\u3ee0\u3ee1\u3ee2\u3ee3\u3ee4\u3ee5\u3ee6\u3ee7\u3ee8\u3ee9\u3eea\u3eeb\u3eec\u3eed\u3eee\u3eef\u3ef0\u3ef1\u3ef2\u3ef3\u3ef4\u3ef5\u3ef6\u3ef7\u3ef8\u3ef9\u3efa\u3efb\u3efc\u3efd\u3efe\u3eff\u3f00\u3f01\u3f02\u3f03\u3f04\u3f05\u3f06\u3f07\u3f08\u3f09\u3f0a\u3f0b\u3f0c\u3f0d\u3f0e\u3f0f\u3f10\u3f11\u3f12\u3f13\u3f14\u3f15\u3f16\u3f17\u3f18\u3f19\u3f1a\u3f1b\u3f1c\u3f1d\u3f1e\u3f1f\u3f20\u3f21\u3f22\u3f23\u3f24\u3f25\u3f26\u3f27\u3f28\u3f29\u3f2a\u3f2b\u3f2c\u3f2d\u3f2e\u3f2f\u3f30\u3f31\u3f32\u3f33\u3f34\u3f35\u3f36\u3f37\u3f38\u3f39\u3f3a\u3f3b\u3f3c\u3f3d\u3f3e\u3f3f\u3f40\u3f41\u3f42\u3f43\u3f44\u3f45\u3f46\u3f47\u3f48\u3f49\u3f4a\u3f4b\u3f4c\u3f4d\u3f4e\u3f4f\u3f50\u3f51\u3f52\u3f53\u3f54\u3f55\u3f56\u3f57\u3f58\u3f59\u3f5a\u3f5b\u3f5c\u3f5d\u3f5e\u3f5f\u3f60\u3f61\u3f62\u3f63\u3f64\u3f65\u3f66\u3f67\u3f68\u3f69\u3f6a\u3f6b\u3f6c\u3f6d\u3f6e\u3f6f\u3f70\u3f71\u3f72\u3f73\u3f74\u3f75\u3f76\u3f77\u3f78\u3f79\u3f7a\u3f7b\u3f7c\u3f7d\u3f7e\u3f7f\u3f80\u3f81\u3f82\u3f83\u3f84\u3f85\u3f86\u3f87\u3f88\u3f89\u3f8a\u3f8b\u3f8c\u3f8d\u3f8e\u3f8f\u3f90\u3f91\u3f92\u3f93\u3f94\u3f95\u3f96\u3f97\u3f98\u3f99\u3f9a\u3f9b\u3f9c\u3f9d\u3f9e\u3f9f\u3fa0\u3fa1\u3fa2\u3fa3\u3fa4\u3fa5\u3fa6\u3fa7\u3fa8\u3fa9\u3faa\u3fab\u3fac\u3fad\u3fae\u3faf\u3fb0\u3fb1\u3fb2\u3fb3\u3fb4\u3fb5\u3fb6\u3fb7\u3fb8\u3fb9\u3fba\u3fbb\u3fbc\u3fbd\u3fbe\u3fbf\u3fc0\u3fc1\u3fc2\u3fc3\u3fc4\u3fc5\u3fc6\u3fc7\u3fc8\u3fc9\u3fca\u3fcb\u3fcc\u3fcd\u3fce\u3fcf\u3fd0\u3fd1\u3fd2\u3fd3\u3fd4\u3fd5\u3fd6\u3fd7\u3fd8\u3fd9\u3fda\u3fdb\u3fdc\u3fdd\u3fde\u3fdf\u3fe0\u3fe1\u3fe2\u3fe3\u3fe4\u3fe5\u3fe6\u3fe7\u3fe8\u3fe9\u3fea\u3feb\u3fec\u3fed\u3fee\u3fef\u3ff0\u3ff1\u3ff2\u3ff3\u3ff4\u3ff5\u3ff6\u3ff7\u3ff8\u3ff9\u3ffa\u3ffb\u3ffc\u3ffd\u3ffe\u3fff\u4000\u4001\u4002\u4003\u4004\u4005\u4006\u4007\u4008\u4009\u400a\u400b\u400c\u400d\u400e\u400f\u4010\u4011\u4012\u4013\u4014\u4015\u4016\u4017\u4018\u4019\u401a\u401b\u401c\u401d\u401e\u401f\u4020\u4021\u4022\u4023\u4024\u4025\u4026\u4027\u4028\u4029\u402a\u402b\u402c\u402d\u402e\u402f\u4030\u4031\u4032\u4033\u4034\u4035\u4036\u4037\u4038\u4039\u403a\u403b\u403c\u403d\u403e\u403f\u4040\u4041\u4042\u4043\u4044\u4045\u4046\u4047\u4048\u4049\u404a\u404b\u404c\u404d\u404e\u404f\u4050\u4051\u4052\u4053\u4054\u4055\u4056\u4057\u4058\u4059\u405a\u405b\u405c\u405d\u405e\u405f\u4060\u4061\u4062\u4063\u4064\u4065\u4066\u4067\u4068\u4069\u406a\u406b\u406c\u406d\u406e\u406f\u4070\u4071\u4072\u4073\u4074\u4075\u4076\u4077\u4078\u4079\u407a\u407b\u407c\u407d\u407e\u407f\u4080\u4081\u4082\u4083\u4084\u4085\u4086\u4087\u4088\u4089\u408a\u408b\u408c\u408d\u408e\u408f\u4090\u4091\u4092\u4093\u4094\u4095\u4096\u4097\u4098\u4099\u409a\u409b\u409c\u409d\u409e\u409f\u40a0\u40a1\u40a2\u40a3\u40a4\u40a5\u40a6\u40a7\u40a8\u40a9\u40aa\u40ab\u40ac\u40ad\u40ae\u40af\u40b0\u40b1\u40b2\u40b3\u40b4\u40b5\u40b6\u40b7\u40b8\u40b9\u40ba\u40bb\u40bc\u40bd\u40be\u40bf\u40c0\u40c1\u40c2\u40c3\u40c4\u40c5\u40c6\u40c7\u40c8\u40c9\u40ca\u40cb\u40cc\u40cd\u40ce\u40cf\u40d0\u40d1\u40d2\u40d3\u40d4\u40d5\u40d6\u40d7\u40d8\u40d9\u40da\u40db\u40dc\u40dd\u40de\u40df\u40e0\u40e1\u40e2\u40e3\u40e4\u40e5\u40e6\u40e7\u40e8\u40e9\u40ea\u40eb\u40ec\u40ed\u40ee\u40ef\u40f0\u40f1\u40f2\u40f3\u40f4\u40f5\u40f6\u40f7\u40f8\u40f9\u40fa\u40fb\u40fc\u40fd\u40fe\u40ff\u4100\u4101\u4102\u4103\u4104\u4105\u4106\u4107\u4108\u4109\u410a\u410b\u410c\u410d\u410e\u410f\u4110\u4111\u4112\u4113\u4114\u4115\u4116\u4117\u4118\u4119\u411a\u411b\u411c\u411d\u411e\u411f\u4120\u4121\u4122\u4123\u4124\u4125\u4126\u4127\u4128\u4129\u412a\u412b\u412c\u412d\u412e\u412f\u4130\u4131\u4132\u4133\u4134\u4135\u4136\u4137\u4138\u4139\u413a\u413b\u413c\u413d\u413e\u413f\u4140\u4141\u4142\u4143\u4144\u4145\u4146\u4147\u4148\u4149\u414a\u414b\u414c\u414d\u414e\u414f\u4150\u4151\u4152\u4153\u4154\u4155\u4156\u4157\u4158\u4159\u415a\u415b\u415c\u415d\u415e\u415f\u4160\u4161\u4162\u4163\u4164\u4165\u4166\u4167\u4168\u4169\u416a\u416b\u416c\u416d\u416e\u416f\u4170\u4171\u4172\u4173\u4174\u4175\u4176\u4177\u4178\u4179\u417a\u417b\u417c\u417d\u417e\u417f\u4180\u4181\u4182\u4183\u4184\u4185\u4186\u4187\u4188\u4189\u418a\u418b\u418c\u418d\u418e\u418f\u4190\u4191\u4192\u4193\u4194\u4195\u4196\u4197\u4198\u4199\u419a\u419b\u419c\u419d\u419e\u419f\u41a0\u41a1\u41a2\u41a3\u41a4\u41a5\u41a6\u41a7\u41a8\u41a9\u41aa\u41ab\u41ac\u41ad\u41ae\u41af\u41b0\u41b1\u41b2\u41b3\u41b4\u41b5\u41b6\u41b7\u41b8\u41b9\u41ba\u41bb\u41bc\u41bd\u41be\u41bf\u41c0\u41c1\u41c2\u41c3\u41c4\u41c5\u41c6\u41c7\u41c8\u41c9\u41ca\u41cb\u41cc\u41cd\u41ce\u41cf\u41d0\u41d1\u41d2\u41d3\u41d4\u41d5\u41d6\u41d7\u41d8\u41d9\u41da\u41db\u41dc\u41dd\u41de\u41df\u41e0\u41e1\u41e2\u41e3\u41e4\u41e5\u41e6\u41e7\u41e8\u41e9\u41ea\u41eb\u41ec\u41ed\u41ee\u41ef\u41f0\u41f1\u41f2\u41f3\u41f4\u41f5\u41f6\u41f7\u41f8\u41f9\u41fa\u41fb\u41fc\u41fd\u41fe\u41ff\u4200\u4201\u4202\u4203\u4204\u4205\u4206\u4207\u4208\u4209\u420a\u420b\u420c\u420d\u420e\u420f\u4210\u4211\u4212\u4213\u4214\u4215\u4216\u4217\u4218\u4219\u421a\u421b\u421c\u421d\u421e\u421f\u4220\u4221\u4222\u4223\u4224\u4225\u4226\u4227\u4228\u4229\u422a\u422b\u422c\u422d\u422e\u422f\u4230\u4231\u4232\u4233\u4234\u4235\u4236\u4237\u4238\u4239\u423a\u423b\u423c\u423d\u423e\u423f\u4240\u4241\u4242\u4243\u4244\u4245\u4246\u4247\u4248\u4249\u424a\u424b\u424c\u424d\u424e\u424f\u4250\u4251\u4252\u4253\u4254\u4255\u4256\u4257\u4258\u4259\u425a\u425b\u425c\u425d\u425e\u425f\u4260\u4261\u4262\u4263\u4264\u4265\u4266\u4267\u4268\u4269\u426a\u426b\u426c\u426d\u426e\u426f\u4270\u4271\u4272\u4273\u4274\u4275\u4276\u4277\u4278\u4279\u427a\u427b\u427c\u427d\u427e\u427f\u4280\u4281\u4282\u4283\u4284\u4285\u4286\u4287\u4288\u4289\u428a\u428b\u428c\u428d\u428e\u428f\u4290\u4291\u4292\u4293\u4294\u4295\u4296\u4297\u4298\u4299\u429a\u429b\u429c\u429d\u429e\u429f\u42a0\u42a1\u42a2\u42a3\u42a4\u42a5\u42a6\u42a7\u42a8\u42a9\u42aa\u42ab\u42ac\u42ad\u42ae\u42af\u42b0\u42b1\u42b2\u42b3\u42b4\u42b5\u42b6\u42b7\u42b8\u42b9\u42ba\u42bb\u42bc\u42bd\u42be\u42bf\u42c0\u42c1\u42c2\u42c3\u42c4\u42c5\u42c6\u42c7\u42c8\u42c9\u42ca\u42cb\u42cc\u42cd\u42ce\u42cf\u42d0\u42d1\u42d2\u42d3\u42d4\u42d5\u42d6\u42d7\u42d8\u42d9\u42da\u42db\u42dc\u42dd\u42de\u42df\u42e0\u42e1\u42e2\u42e3\u42e4\u42e5\u42e6\u42e7\u42e8\u42e9\u42ea\u42eb\u42ec\u42ed\u42ee\u42ef\u42f0\u42f1\u42f2\u42f3\u42f4\u42f5\u42f6\u42f7\u42f8\u42f9\u42fa\u42fb\u42fc\u42fd\u42fe\u42ff\u4300\u4301\u4302\u4303\u4304\u4305\u4306\u4307\u4308\u4309\u430a\u430b\u430c\u430d\u430e\u430f\u4310\u4311\u4312\u4313\u4314\u4315\u4316\u4317\u4318\u4319\u431a\u431b\u431c\u431d\u431e\u431f\u4320\u4321\u4322\u4323\u4324\u4325\u4326\u4327\u4328\u4329\u432a\u432b\u432c\u432d\u432e\u432f\u4330\u4331\u4332\u4333\u4334\u4335\u4336\u4337\u4338\u4339\u433a\u433b\u433c\u433d\u433e\u433f\u4340\u4341\u4342\u4343\u4344\u4345\u4346\u4347\u4348\u4349\u434a\u434b\u434c\u434d\u434e\u434f\u4350\u4351\u4352\u4353\u4354\u4355\u4356\u4357\u4358\u4359\u435a\u435b\u435c\u435d\u435e\u435f\u4360\u4361\u4362\u4363\u4364\u4365\u4366\u4367\u4368\u4369\u436a\u436b\u436c\u436d\u436e\u436f\u4370\u4371\u4372\u4373\u4374\u4375\u4376\u4377\u4378\u4379\u437a\u437b\u437c\u437d\u437e\u437f\u4380\u4381\u4382\u4383\u4384\u4385\u4386\u4387\u4388\u4389\u438a\u438b\u438c\u438d\u438e\u438f\u4390\u4391\u4392\u4393\u4394\u4395\u4396\u4397\u4398\u4399\u439a\u439b\u439c\u439d\u439e\u439f\u43a0\u43a1\u43a2\u43a3\u43a4\u43a5\u43a6\u43a7\u43a8\u43a9\u43aa\u43ab\u43ac\u43ad\u43ae\u43af\u43b0\u43b1\u43b2\u43b3\u43b4\u43b5\u43b6\u43b7\u43b8\u43b9\u43ba\u43bb\u43bc\u43bd\u43be\u43bf\u43c0\u43c1\u43c2\u43c3\u43c4\u43c5\u43c6\u43c7\u43c8\u43c9\u43ca\u43cb\u43cc\u43cd\u43ce\u43cf\u43d0\u43d1\u43d2\u43d3\u43d4\u43d5\u43d6\u43d7\u43d8\u43d9\u43da\u43db\u43dc\u43dd\u43de\u43df\u43e0\u43e1\u43e2\u43e3\u43e4\u43e5\u43e6\u43e7\u43e8\u43e9\u43ea\u43eb\u43ec\u43ed\u43ee\u43ef\u43f0\u43f1\u43f2\u43f3\u43f4\u43f5\u43f6\u43f7\u43f8\u43f9\u43fa\u43fb\u43fc\u43fd\u43fe\u43ff\u4400\u4401\u4402\u4403\u4404\u4405\u4406\u4407\u4408\u4409\u440a\u440b\u440c\u440d\u440e\u440f\u4410\u4411\u4412\u4413\u4414\u4415\u4416\u4417\u4418\u4419\u441a\u441b\u441c\u441d\u441e\u441f\u4420\u4421\u4422\u4423\u4424\u4425\u4426\u4427\u4428\u4429\u442a\u442b\u442c\u442d\u442e\u442f\u4430\u4431\u4432\u4433\u4434\u4435\u4436\u4437\u4438\u4439\u443a\u443b\u443c\u443d\u443e\u443f\u4440\u4441\u4442\u4443\u4444\u4445\u4446\u4447\u4448\u4449\u444a\u444b\u444c\u444d\u444e\u444f\u4450\u4451\u4452\u4453\u4454\u4455\u4456\u4457\u4458\u4459\u445a\u445b\u445c\u445d\u445e\u445f\u4460\u4461\u4462\u4463\u4464\u4465\u4466\u4467\u4468\u4469\u446a\u446b\u446c\u446d\u446e\u446f\u4470\u4471\u4472\u4473\u4474\u4475\u4476\u4477\u4478\u4479\u447a\u447b\u447c\u447d\u447e\u447f\u4480\u4481\u4482\u4483\u4484\u4485\u4486\u4487\u4488\u4489\u448a\u448b\u448c\u448d\u448e\u448f\u4490\u4491\u4492\u4493\u4494\u4495\u4496\u4497\u4498\u4499\u449a\u449b\u449c\u449d\u449e\u449f\u44a0\u44a1\u44a2\u44a3\u44a4\u44a5\u44a6\u44a7\u44a8\u44a9\u44aa\u44ab\u44ac\u44ad\u44ae\u44af\u44b0\u44b1\u44b2\u44b3\u44b4\u44b5\u44b6\u44b7\u44b8\u44b9\u44ba\u44bb\u44bc\u44bd\u44be\u44bf\u44c0\u44c1\u44c2\u44c3\u44c4\u44c5\u44c6\u44c7\u44c8\u44c9\u44ca\u44cb\u44cc\u44cd\u44ce\u44cf\u44d0\u44d1\u44d2\u44d3\u44d4\u44d5\u44d6\u44d7\u44d8\u44d9\u44da\u44db\u44dc\u44dd\u44de\u44df\u44e0\u44e1\u44e2\u44e3\u44e4\u44e5\u44e6\u44e7\u44e8\u44e9\u44ea\u44eb\u44ec\u44ed\u44ee\u44ef\u44f0\u44f1\u44f2\u44f3\u44f4\u44f5\u44f6\u44f7\u44f8\u44f9\u44fa\u44fb\u44fc\u44fd\u44fe\u44ff\u4500\u4501\u4502\u4503\u4504\u4505\u4506\u4507\u4508\u4509\u450a\u450b\u450c\u450d\u450e\u450f\u4510\u4511\u4512\u4513\u4514\u4515\u4516\u4517\u4518\u4519\u451a\u451b\u451c\u451d\u451e\u451f\u4520\u4521\u4522\u4523\u4524\u4525\u4526\u4527\u4528\u4529\u452a\u452b\u452c\u452d\u452e\u452f\u4530\u4531\u4532\u4533\u4534\u4535\u4536\u4537\u4538\u4539\u453a\u453b\u453c\u453d\u453e\u453f\u4540\u4541\u4542\u4543\u4544\u4545\u4546\u4547\u4548\u4549\u454a\u454b\u454c\u454d\u454e\u454f\u4550\u4551\u4552\u4553\u4554\u4555\u4556\u4557\u4558\u4559\u455a\u455b\u455c\u455d\u455e\u455f\u4560\u4561\u4562\u4563\u4564\u4565\u4566\u4567\u4568\u4569\u456a\u456b\u456c\u456d\u456e\u456f\u4570\u4571\u4572\u4573\u4574\u4575\u4576\u4577\u4578\u4579\u457a\u457b\u457c\u457d\u457e\u457f\u4580\u4581\u4582\u4583\u4584\u4585\u4586\u4587\u4588\u4589\u458a\u458b\u458c\u458d\u458e\u458f\u4590\u4591\u4592\u4593\u4594\u4595\u4596\u4597\u4598\u4599\u459a\u459b\u459c\u459d\u459e\u459f\u45a0\u45a1\u45a2\u45a3\u45a4\u45a5\u45a6\u45a7\u45a8\u45a9\u45aa\u45ab\u45ac\u45ad\u45ae\u45af\u45b0\u45b1\u45b2\u45b3\u45b4\u45b5\u45b6\u45b7\u45b8\u45b9\u45ba\u45bb\u45bc\u45bd\u45be\u45bf\u45c0\u45c1\u45c2\u45c3\u45c4\u45c5\u45c6\u45c7\u45c8\u45c9\u45ca\u45cb\u45cc\u45cd\u45ce\u45cf\u45d0\u45d1\u45d2\u45d3\u45d4\u45d5\u45d6\u45d7\u45d8\u45d9\u45da\u45db\u45dc\u45dd\u45de\u45df\u45e0\u45e1\u45e2\u45e3\u45e4\u45e5\u45e6\u45e7\u45e8\u45e9\u45ea\u45eb\u45ec\u45ed\u45ee\u45ef\u45f0\u45f1\u45f2\u45f3\u45f4\u45f5\u45f6\u45f7\u45f8\u45f9\u45fa\u45fb\u45fc\u45fd\u45fe\u45ff\u4600\u4601\u4602\u4603\u4604\u4605\u4606\u4607\u4608\u4609\u460a\u460b\u460c\u460d\u460e\u460f\u4610\u4611\u4612\u4613\u4614\u4615\u4616\u4617\u4618\u4619\u461a\u461b\u461c\u461d\u461e\u461f\u4620\u4621\u4622\u4623\u4624\u4625\u4626\u4627\u4628\u4629\u462a\u462b\u462c\u462d\u462e\u462f\u4630\u4631\u4632\u4633\u4634\u4635\u4636\u4637\u4638\u4639\u463a\u463b\u463c\u463d\u463e\u463f\u4640\u4641\u4642\u4643\u4644\u4645\u4646\u4647\u4648\u4649\u464a\u464b\u464c\u464d\u464e\u464f\u4650\u4651\u4652\u4653\u4654\u4655\u4656\u4657\u4658\u4659\u465a\u465b\u465c\u465d\u465e\u465f\u4660\u4661\u4662\u4663\u4664\u4665\u4666\u4667\u4668\u4669\u466a\u466b\u466c\u466d\u466e\u466f\u4670\u4671\u4672\u4673\u4674\u4675\u4676\u4677\u4678\u4679\u467a\u467b\u467c\u467d\u467e\u467f\u4680\u4681\u4682\u4683\u4684\u4685\u4686\u4687\u4688\u4689\u468a\u468b\u468c\u468d\u468e\u468f\u4690\u4691\u4692\u4693\u4694\u4695\u4696\u4697\u4698\u4699\u469a\u469b\u469c\u469d\u469e\u469f\u46a0\u46a1\u46a2\u46a3\u46a4\u46a5\u46a6\u46a7\u46a8\u46a9\u46aa\u46ab\u46ac\u46ad\u46ae\u46af\u46b0\u46b1\u46b2\u46b3\u46b4\u46b5\u46b6\u46b7\u46b8\u46b9\u46ba\u46bb\u46bc\u46bd\u46be\u46bf\u46c0\u46c1\u46c2\u46c3\u46c4\u46c5\u46c6\u46c7\u46c8\u46c9\u46ca\u46cb\u46cc\u46cd\u46ce\u46cf\u46d0\u46d1\u46d2\u46d3\u46d4\u46d5\u46d6\u46d7\u46d8\u46d9\u46da\u46db\u46dc\u46dd\u46de\u46df\u46e0\u46e1\u46e2\u46e3\u46e4\u46e5\u46e6\u46e7\u46e8\u46e9\u46ea\u46eb\u46ec\u46ed\u46ee\u46ef\u46f0\u46f1\u46f2\u46f3\u46f4\u46f5\u46f6\u46f7\u46f8\u46f9\u46fa\u46fb\u46fc\u46fd\u46fe\u46ff\u4700\u4701\u4702\u4703\u4704\u4705\u4706\u4707\u4708\u4709\u470a\u470b\u470c\u470d\u470e\u470f\u4710\u4711\u4712\u4713\u4714\u4715\u4716\u4717\u4718\u4719\u471a\u471b\u471c\u471d\u471e\u471f\u4720\u4721\u4722\u4723\u4724\u4725\u4726\u4727\u4728\u4729\u472a\u472b\u472c\u472d\u472e\u472f\u4730\u4731\u4732\u4733\u4734\u4735\u4736\u4737\u4738\u4739\u473a\u473b\u473c\u473d\u473e\u473f\u4740\u4741\u4742\u4743\u4744\u4745\u4746\u4747\u4748\u4749\u474a\u474b\u474c\u474d\u474e\u474f\u4750\u4751\u4752\u4753\u4754\u4755\u4756\u4757\u4758\u4759\u475a\u475b\u475c\u475d\u475e\u475f\u4760\u4761\u4762\u4763\u4764\u4765\u4766\u4767\u4768\u4769\u476a\u476b\u476c\u476d\u476e\u476f\u4770\u4771\u4772\u4773\u4774\u4775\u4776\u4777\u4778\u4779\u477a\u477b\u477c\u477d\u477e\u477f\u4780\u4781\u4782\u4783\u4784\u4785\u4786\u4787\u4788\u4789\u478a\u478b\u478c\u478d\u478e\u478f\u4790\u4791\u4792\u4793\u4794\u4795\u4796\u4797\u4798\u4799\u479a\u479b\u479c\u479d\u479e\u479f\u47a0\u47a1\u47a2\u47a3\u47a4\u47a5\u47a6\u47a7\u47a8\u47a9\u47aa\u47ab\u47ac\u47ad\u47ae\u47af\u47b0\u47b1\u47b2\u47b3\u47b4\u47b5\u47b6\u47b7\u47b8\u47b9\u47ba\u47bb\u47bc\u47bd\u47be\u47bf\u47c0\u47c1\u47c2\u47c3\u47c4\u47c5\u47c6\u47c7\u47c8\u47c9\u47ca\u47cb\u47cc\u47cd\u47ce\u47cf\u47d0\u47d1\u47d2\u47d3\u47d4\u47d5\u47d6\u47d7\u47d8\u47d9\u47da\u47db\u47dc\u47dd\u47de\u47df\u47e0\u47e1\u47e2\u47e3\u47e4\u47e5\u47e6\u47e7\u47e8\u47e9\u47ea\u47eb\u47ec\u47ed\u47ee\u47ef\u47f0\u47f1\u47f2\u47f3\u47f4\u47f5\u47f6\u47f7\u47f8\u47f9\u47fa\u47fb\u47fc\u47fd\u47fe\u47ff\u4800\u4801\u4802\u4803\u4804\u4805\u4806\u4807\u4808\u4809\u480a\u480b\u480c\u480d\u480e\u480f\u4810\u4811\u4812\u4813\u4814\u4815\u4816\u4817\u4818\u4819\u481a\u481b\u481c\u481d\u481e\u481f\u4820\u4821\u4822\u4823\u4824\u4825\u4826\u4827\u4828\u4829\u482a\u482b\u482c\u482d\u482e\u482f\u4830\u4831\u4832\u4833\u4834\u4835\u4836\u4837\u4838\u4839\u483a\u483b\u483c\u483d\u483e\u483f\u4840\u4841\u4842\u4843\u4844\u4845\u4846\u4847\u4848\u4849\u484a\u484b\u484c\u484d\u484e\u484f\u4850\u4851\u4852\u4853\u4854\u4855\u4856\u4857\u4858\u4859\u485a\u485b\u485c\u485d\u485e\u485f\u4860\u4861\u4862\u4863\u4864\u4865\u4866\u4867\u4868\u4869\u486a\u486b\u486c\u486d\u486e\u486f\u4870\u4871\u4872\u4873\u4874\u4875\u4876\u4877\u4878\u4879\u487a\u487b\u487c\u487d\u487e\u487f\u4880\u4881\u4882\u4883\u4884\u4885\u4886\u4887\u4888\u4889\u488a\u488b\u488c\u488d\u488e\u488f\u4890\u4891\u4892\u4893\u4894\u4895\u4896\u4897\u4898\u4899\u489a\u489b\u489c\u489d\u489e\u489f\u48a0\u48a1\u48a2\u48a3\u48a4\u48a5\u48a6\u48a7\u48a8\u48a9\u48aa\u48ab\u48ac\u48ad\u48ae\u48af\u48b0\u48b1\u48b2\u48b3\u48b4\u48b5\u48b6\u48b7\u48b8\u48b9\u48ba\u48bb\u48bc\u48bd\u48be\u48bf\u48c0\u48c1\u48c2\u48c3\u48c4\u48c5\u48c6\u48c7\u48c8\u48c9\u48ca\u48cb\u48cc\u48cd\u48ce\u48cf\u48d0\u48d1\u48d2\u48d3\u48d4\u48d5\u48d6\u48d7\u48d8\u48d9\u48da\u48db\u48dc\u48dd\u48de\u48df\u48e0\u48e1\u48e2\u48e3\u48e4\u48e5\u48e6\u48e7\u48e8\u48e9\u48ea\u48eb\u48ec\u48ed\u48ee\u48ef\u48f0\u48f1\u48f2\u48f3\u48f4\u48f5\u48f6\u48f7\u48f8\u48f9\u48fa\u48fb\u48fc\u48fd\u48fe\u48ff\u4900\u4901\u4902\u4903\u4904\u4905\u4906\u4907\u4908\u4909\u490a\u490b\u490c\u490d\u490e\u490f\u4910\u4911\u4912\u4913\u4914\u4915\u4916\u4917\u4918\u4919\u491a\u491b\u491c\u491d\u491e\u491f\u4920\u4921\u4922\u4923\u4924\u4925\u4926\u4927\u4928\u4929\u492a\u492b\u492c\u492d\u492e\u492f\u4930\u4931\u4932\u4933\u4934\u4935\u4936\u4937\u4938\u4939\u493a\u493b\u493c\u493d\u493e\u493f\u4940\u4941\u4942\u4943\u4944\u4945\u4946\u4947\u4948\u4949\u494a\u494b\u494c\u494d\u494e\u494f\u4950\u4951\u4952\u4953\u4954\u4955\u4956\u4957\u4958\u4959\u495a\u495b\u495c\u495d\u495e\u495f\u4960\u4961\u4962\u4963\u4964\u4965\u4966\u4967\u4968\u4969\u496a\u496b\u496c\u496d\u496e\u496f\u4970\u4971\u4972\u4973\u4974\u4975\u4976\u4977\u4978\u4979\u497a\u497b\u497c\u497d\u497e\u497f\u4980\u4981\u4982\u4983\u4984\u4985\u4986\u4987\u4988\u4989\u498a\u498b\u498c\u498d\u498e\u498f\u4990\u4991\u4992\u4993\u4994\u4995\u4996\u4997\u4998\u4999\u499a\u499b\u499c\u499d\u499e\u499f\u49a0\u49a1\u49a2\u49a3\u49a4\u49a5\u49a6\u49a7\u49a8\u49a9\u49aa\u49ab\u49ac\u49ad\u49ae\u49af\u49b0\u49b1\u49b2\u49b3\u49b4\u49b5\u49b6\u49b7\u49b8\u49b9\u49ba\u49bb\u49bc\u49bd\u49be\u49bf\u49c0\u49c1\u49c2\u49c3\u49c4\u49c5\u49c6\u49c7\u49c8\u49c9\u49ca\u49cb\u49cc\u49cd\u49ce\u49cf\u49d0\u49d1\u49d2\u49d3\u49d4\u49d5\u49d6\u49d7\u49d8\u49d9\u49da\u49db\u49dc\u49dd\u49de\u49df\u49e0\u49e1\u49e2\u49e3\u49e4\u49e5\u49e6\u49e7\u49e8\u49e9\u49ea\u49eb\u49ec\u49ed\u49ee\u49ef\u49f0\u49f1\u49f2\u49f3\u49f4\u49f5\u49f6\u49f7\u49f8\u49f9\u49fa\u49fb\u49fc\u49fd\u49fe\u49ff\u4a00\u4a01\u4a02\u4a03\u4a04\u4a05\u4a06\u4a07\u4a08\u4a09\u4a0a\u4a0b\u4a0c\u4a0d\u4a0e\u4a0f\u4a10\u4a11\u4a12\u4a13\u4a14\u4a15\u4a16\u4a17\u4a18\u4a19\u4a1a\u4a1b\u4a1c\u4a1d\u4a1e\u4a1f\u4a20\u4a21\u4a22\u4a23\u4a24\u4a25\u4a26\u4a27\u4a28\u4a29\u4a2a\u4a2b\u4a2c\u4a2d\u4a2e\u4a2f\u4a30\u4a31\u4a32\u4a33\u4a34\u4a35\u4a36\u4a37\u4a38\u4a39\u4a3a\u4a3b\u4a3c\u4a3d\u4a3e\u4a3f\u4a40\u4a41\u4a42\u4a43\u4a44\u4a45\u4a46\u4a47\u4a48\u4a49\u4a4a\u4a4b\u4a4c\u4a4d\u4a4e\u4a4f\u4a50\u4a51\u4a52\u4a53\u4a54\u4a55\u4a56\u4a57\u4a58\u4a59\u4a5a\u4a5b\u4a5c\u4a5d\u4a5e\u4a5f\u4a60\u4a61\u4a62\u4a63\u4a64\u4a65\u4a66\u4a67\u4a68\u4a69\u4a6a\u4a6b\u4a6c\u4a6d\u4a6e\u4a6f\u4a70\u4a71\u4a72\u4a73\u4a74\u4a75\u4a76\u4a77\u4a78\u4a79\u4a7a\u4a7b\u4a7c\u4a7d\u4a7e\u4a7f\u4a80\u4a81\u4a82\u4a83\u4a84\u4a85\u4a86\u4a87\u4a88\u4a89\u4a8a\u4a8b\u4a8c\u4a8d\u4a8e\u4a8f\u4a90\u4a91\u4a92\u4a93\u4a94\u4a95\u4a96\u4a97\u4a98\u4a99\u4a9a\u4a9b\u4a9c\u4a9d\u4a9e\u4a9f\u4aa0\u4aa1\u4aa2\u4aa3\u4aa4\u4aa5\u4aa6\u4aa7\u4aa8\u4aa9\u4aaa\u4aab\u4aac\u4aad\u4aae\u4aaf\u4ab0\u4ab1\u4ab2\u4ab3\u4ab4\u4ab5\u4ab6\u4ab7\u4ab8\u4ab9\u4aba\u4abb\u4abc\u4abd\u4abe\u4abf\u4ac0\u4ac1\u4ac2\u4ac3\u4ac4\u4ac5\u4ac6\u4ac7\u4ac8\u4ac9\u4aca\u4acb\u4acc\u4acd\u4ace\u4acf\u4ad0\u4ad1\u4ad2\u4ad3\u4ad4\u4ad5\u4ad6\u4ad7\u4ad8\u4ad9\u4ada\u4adb\u4adc\u4add\u4ade\u4adf\u4ae0\u4ae1\u4ae2\u4ae3\u4ae4\u4ae5\u4ae6\u4ae7\u4ae8\u4ae9\u4aea\u4aeb\u4aec\u4aed\u4aee\u4aef\u4af0\u4af1\u4af2\u4af3\u4af4\u4af5\u4af6\u4af7\u4af8\u4af9\u4afa\u4afb\u4afc\u4afd\u4afe\u4aff\u4b00\u4b01\u4b02\u4b03\u4b04\u4b05\u4b06\u4b07\u4b08\u4b09\u4b0a\u4b0b\u4b0c\u4b0d\u4b0e\u4b0f\u4b10\u4b11\u4b12\u4b13\u4b14\u4b15\u4b16\u4b17\u4b18\u4b19\u4b1a\u4b1b\u4b1c\u4b1d\u4b1e\u4b1f\u4b20\u4b21\u4b22\u4b23\u4b24\u4b25\u4b26\u4b27\u4b28\u4b29\u4b2a\u4b2b\u4b2c\u4b2d\u4b2e\u4b2f\u4b30\u4b31\u4b32\u4b33\u4b34\u4b35\u4b36\u4b37\u4b38\u4b39\u4b3a\u4b3b\u4b3c\u4b3d\u4b3e\u4b3f\u4b40\u4b41\u4b42\u4b43\u4b44\u4b45\u4b46\u4b47\u4b48\u4b49\u4b4a\u4b4b\u4b4c\u4b4d\u4b4e\u4b4f\u4b50\u4b51\u4b52\u4b53\u4b54\u4b55\u4b56\u4b57\u4b58\u4b59\u4b5a\u4b5b\u4b5c\u4b5d\u4b5e\u4b5f\u4b60\u4b61\u4b62\u4b63\u4b64\u4b65\u4b66\u4b67\u4b68\u4b69\u4b6a\u4b6b\u4b6c\u4b6d\u4b6e\u4b6f\u4b70\u4b71\u4b72\u4b73\u4b74\u4b75\u4b76\u4b77\u4b78\u4b79\u4b7a\u4b7b\u4b7c\u4b7d\u4b7e\u4b7f\u4b80\u4b81\u4b82\u4b83\u4b84\u4b85\u4b86\u4b87\u4b88\u4b89\u4b8a\u4b8b\u4b8c\u4b8d\u4b8e\u4b8f\u4b90\u4b91\u4b92\u4b93\u4b94\u4b95\u4b96\u4b97\u4b98\u4b99\u4b9a\u4b9b\u4b9c\u4b9d\u4b9e\u4b9f\u4ba0\u4ba1\u4ba2\u4ba3\u4ba4\u4ba5\u4ba6\u4ba7\u4ba8\u4ba9\u4baa\u4bab\u4bac\u4bad\u4bae\u4baf\u4bb0\u4bb1\u4bb2\u4bb3\u4bb4\u4bb5\u4bb6\u4bb7\u4bb8\u4bb9\u4bba\u4bbb\u4bbc\u4bbd\u4bbe\u4bbf\u4bc0\u4bc1\u4bc2\u4bc3\u4bc4\u4bc5\u4bc6\u4bc7\u4bc8\u4bc9\u4bca\u4bcb\u4bcc\u4bcd\u4bce\u4bcf\u4bd0\u4bd1\u4bd2\u4bd3\u4bd4\u4bd5\u4bd6\u4bd7\u4bd8\u4bd9\u4bda\u4bdb\u4bdc\u4bdd\u4bde\u4bdf\u4be0\u4be1\u4be2\u4be3\u4be4\u4be5\u4be6\u4be7\u4be8\u4be9\u4bea\u4beb\u4bec\u4bed\u4bee\u4bef\u4bf0\u4bf1\u4bf2\u4bf3\u4bf4\u4bf5\u4bf6\u4bf7\u4bf8\u4bf9\u4bfa\u4bfb\u4bfc\u4bfd\u4bfe\u4bff\u4c00\u4c01\u4c02\u4c03\u4c04\u4c05\u4c06\u4c07\u4c08\u4c09\u4c0a\u4c0b\u4c0c\u4c0d\u4c0e\u4c0f\u4c10\u4c11\u4c12\u4c13\u4c14\u4c15\u4c16\u4c17\u4c18\u4c19\u4c1a\u4c1b\u4c1c\u4c1d\u4c1e\u4c1f\u4c20\u4c21\u4c22\u4c23\u4c24\u4c25\u4c26\u4c27\u4c28\u4c29\u4c2a\u4c2b\u4c2c\u4c2d\u4c2e\u4c2f\u4c30\u4c31\u4c32\u4c33\u4c34\u4c35\u4c36\u4c37\u4c38\u4c39\u4c3a\u4c3b\u4c3c\u4c3d\u4c3e\u4c3f\u4c40\u4c41\u4c42\u4c43\u4c44\u4c45\u4c46\u4c47\u4c48\u4c49\u4c4a\u4c4b\u4c4c\u4c4d\u4c4e\u4c4f\u4c50\u4c51\u4c52\u4c53\u4c54\u4c55\u4c56\u4c57\u4c58\u4c59\u4c5a\u4c5b\u4c5c\u4c5d\u4c5e\u4c5f\u4c60\u4c61\u4c62\u4c63\u4c64\u4c65\u4c66\u4c67\u4c68\u4c69\u4c6a\u4c6b\u4c6c\u4c6d\u4c6e\u4c6f\u4c70\u4c71\u4c72\u4c73\u4c74\u4c75\u4c76\u4c77\u4c78\u4c79\u4c7a\u4c7b\u4c7c\u4c7d\u4c7e\u4c7f\u4c80\u4c81\u4c82\u4c83\u4c84\u4c85\u4c86\u4c87\u4c88\u4c89\u4c8a\u4c8b\u4c8c\u4c8d\u4c8e\u4c8f\u4c90\u4c91\u4c92\u4c93\u4c94\u4c95\u4c96\u4c97\u4c98\u4c99\u4c9a\u4c9b\u4c9c\u4c9d\u4c9e\u4c9f\u4ca0\u4ca1\u4ca2\u4ca3\u4ca4\u4ca5\u4ca6\u4ca7\u4ca8\u4ca9\u4caa\u4cab\u4cac\u4cad\u4cae\u4caf\u4cb0\u4cb1\u4cb2\u4cb3\u4cb4\u4cb5\u4cb6\u4cb7\u4cb8\u4cb9\u4cba\u4cbb\u4cbc\u4cbd\u4cbe\u4cbf\u4cc0\u4cc1\u4cc2\u4cc3\u4cc4\u4cc5\u4cc6\u4cc7\u4cc8\u4cc9\u4cca\u4ccb\u4ccc\u4ccd\u4cce\u4ccf\u4cd0\u4cd1\u4cd2\u4cd3\u4cd4\u4cd5\u4cd6\u4cd7\u4cd8\u4cd9\u4cda\u4cdb\u4cdc\u4cdd\u4cde\u4cdf\u4ce0\u4ce1\u4ce2\u4ce3\u4ce4\u4ce5\u4ce6\u4ce7\u4ce8\u4ce9\u4cea\u4ceb\u4cec\u4ced\u4cee\u4cef\u4cf0\u4cf1\u4cf2\u4cf3\u4cf4\u4cf5\u4cf6\u4cf7\u4cf8\u4cf9\u4cfa\u4cfb\u4cfc\u4cfd\u4cfe\u4cff\u4d00\u4d01\u4d02\u4d03\u4d04\u4d05\u4d06\u4d07\u4d08\u4d09\u4d0a\u4d0b\u4d0c\u4d0d\u4d0e\u4d0f\u4d10\u4d11\u4d12\u4d13\u4d14\u4d15\u4d16\u4d17\u4d18\u4d19\u4d1a\u4d1b\u4d1c\u4d1d\u4d1e\u4d1f\u4d20\u4d21\u4d22\u4d23\u4d24\u4d25\u4d26\u4d27\u4d28\u4d29\u4d2a\u4d2b\u4d2c\u4d2d\u4d2e\u4d2f\u4d30\u4d31\u4d32\u4d33\u4d34\u4d35\u4d36\u4d37\u4d38\u4d39\u4d3a\u4d3b\u4d3c\u4d3d\u4d3e\u4d3f\u4d40\u4d41\u4d42\u4d43\u4d44\u4d45\u4d46\u4d47\u4d48\u4d49\u4d4a\u4d4b\u4d4c\u4d4d\u4d4e\u4d4f\u4d50\u4d51\u4d52\u4d53\u4d54\u4d55\u4d56\u4d57\u4d58\u4d59\u4d5a\u4d5b\u4d5c\u4d5d\u4d5e\u4d5f\u4d60\u4d61\u4d62\u4d63\u4d64\u4d65\u4d66\u4d67\u4d68\u4d69\u4d6a\u4d6b\u4d6c\u4d6d\u4d6e\u4d6f\u4d70\u4d71\u4d72\u4d73\u4d74\u4d75\u4d76\u4d77\u4d78\u4d79\u4d7a\u4d7b\u4d7c\u4d7d\u4d7e\u4d7f\u4d80\u4d81\u4d82\u4d83\u4d84\u4d85\u4d86\u4d87\u4d88\u4d89\u4d8a\u4d8b\u4d8c\u4d8d\u4d8e\u4d8f\u4d90\u4d91\u4d92\u4d93\u4d94\u4d95\u4d96\u4d97\u4d98\u4d99\u4d9a\u4d9b\u4d9c\u4d9d\u4d9e\u4d9f\u4da0\u4da1\u4da2\u4da3\u4da4\u4da5\u4da6\u4da7\u4da8\u4da9\u4daa\u4dab\u4dac\u4dad\u4dae\u4daf\u4db0\u4db1\u4db2\u4db3\u4db4\u4db5\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d\u4e0e\u4e0f\u4e10\u4e11\u4e12\u4e13\u4e14\u4e15\u4e16\u4e17\u4e18\u4e19\u4e1a\u4e1b\u4e1c\u4e1d\u4e1e\u4e1f\u4e20\u4e21\u4e22\u4e23\u4e24\u4e25\u4e26\u4e27\u4e28\u4e29\u4e2a\u4e2b\u4e2c\u4e2d\u4e2e\u4e2f\u4e30\u4e31\u4e32\u4e33\u4e34\u4e35\u4e36\u4e37\u4e38\u4e39\u4e3a\u4e3b\u4e3c\u4e3d\u4e3e\u4e3f\u4e40\u4e41\u4e42\u4e43\u4e44\u4e45\u4e46\u4e47\u4e48\u4e49\u4e4a\u4e4b\u4e4c\u4e4d\u4e4e\u4e4f\u4e50\u4e51\u4e52\u4e53\u4e54\u4e55\u4e56\u4e57\u4e58\u4e59\u4e5a\u4e5b\u4e5c\u4e5d\u4e5e\u4e5f\u4e60\u4e61\u4e62\u4e63\u4e64\u4e65\u4e66\u4e67\u4e68\u4e69\u4e6a\u4e6b\u4e6c\u4e6d\u4e6e\u4e6f\u4e70\u4e71\u4e72\u4e73\u4e74\u4e75\u4e76\u4e77\u4e78\u4e79\u4e7a\u4e7b\u4e7c\u4e7d\u4e7e\u4e7f\u4e80\u4e81\u4e82\u4e83\u4e84\u4e85\u4e86\u4e87\u4e88\u4e89\u4e8a\u4e8b\u4e8c\u4e8d\u4e8e\u4e8f\u4e90\u4e91\u4e92\u4e93\u4e94\u4e95\u4e96\u4e97\u4e98\u4e99\u4e9a\u4e9b\u4e9c\u4e9d\u4e9e\u4e9f\u4ea0\u4ea1\u4ea2\u4ea3\u4ea4\u4ea5\u4ea6\u4ea7\u4ea8\u4ea9\u4eaa\u4eab\u4eac\u4ead\u4eae\u4eaf\u4eb0\u4eb1\u4eb2\u4eb3\u4eb4\u4eb5\u4eb6\u4eb7\u4eb8\u4eb9\u4eba\u4ebb\u4ebc\u4ebd\u4ebe\u4ebf\u4ec0\u4ec1\u4ec2\u4ec3\u4ec4\u4ec5\u4ec6\u4ec7\u4ec8\u4ec9\u4eca\u4ecb\u4ecc\u4ecd\u4ece\u4ecf\u4ed0\u4ed1\u4ed2\u4ed3\u4ed4\u4ed5\u4ed6\u4ed7\u4ed8\u4ed9\u4eda\u4edb\u4edc\u4edd\u4ede\u4edf\u4ee0\u4ee1\u4ee2\u4ee3\u4ee4\u4ee5\u4ee6\u4ee7\u4ee8\u4ee9\u4eea\u4eeb\u4eec\u4eed\u4eee\u4eef\u4ef0\u4ef1\u4ef2\u4ef3\u4ef4\u4ef5\u4ef6\u4ef7\u4ef8\u4ef9\u4efa\u4efb\u4efc\u4efd\u4efe\u4eff\u4f00\u4f01\u4f02\u4f03\u4f04\u4f05\u4f06\u4f07\u4f08\u4f09\u4f0a\u4f0b\u4f0c\u4f0d\u4f0e\u4f0f\u4f10\u4f11\u4f12\u4f13\u4f14\u4f15\u4f16\u4f17\u4f18\u4f19\u4f1a\u4f1b\u4f1c\u4f1d\u4f1e\u4f1f\u4f20\u4f21\u4f22\u4f23\u4f24\u4f25\u4f26\u4f27\u4f28\u4f29\u4f2a\u4f2b\u4f2c\u4f2d\u4f2e\u4f2f\u4f30\u4f31\u4f32\u4f33\u4f34\u4f35\u4f36\u4f37\u4f38\u4f39\u4f3a\u4f3b\u4f3c\u4f3d\u4f3e\u4f3f\u4f40\u4f41\u4f42\u4f43\u4f44\u4f45\u4f46\u4f47\u4f48\u4f49\u4f4a\u4f4b\u4f4c\u4f4d\u4f4e\u4f4f\u4f50\u4f51\u4f52\u4f53\u4f54\u4f55\u4f56\u4f57\u4f58\u4f59\u4f5a\u4f5b\u4f5c\u4f5d\u4f5e\u4f5f\u4f60\u4f61\u4f62\u4f63\u4f64\u4f65\u4f66\u4f67\u4f68\u4f69\u4f6a\u4f6b\u4f6c\u4f6d\u4f6e\u4f6f\u4f70\u4f71\u4f72\u4f73\u4f74\u4f75\u4f76\u4f77\u4f78\u4f79\u4f7a\u4f7b\u4f7c\u4f7d\u4f7e\u4f7f\u4f80\u4f81\u4f82\u4f83\u4f84\u4f85\u4f86\u4f87\u4f88\u4f89\u4f8a\u4f8b\u4f8c\u4f8d\u4f8e\u4f8f\u4f90\u4f91\u4f92\u4f93\u4f94\u4f95\u4f96\u4f97\u4f98\u4f99\u4f9a\u4f9b\u4f9c\u4f9d\u4f9e\u4f9f\u4fa0\u4fa1\u4fa2\u4fa3\u4fa4\u4fa5\u4fa6\u4fa7\u4fa8\u4fa9\u4faa\u4fab\u4fac\u4fad\u4fae\u4faf\u4fb0\u4fb1\u4fb2\u4fb3\u4fb4\u4fb5\u4fb6\u4fb7\u4fb8\u4fb9\u4fba\u4fbb\u4fbc\u4fbd\u4fbe\u4fbf\u4fc0\u4fc1\u4fc2\u4fc3\u4fc4\u4fc5\u4fc6\u4fc7\u4fc8\u4fc9\u4fca\u4fcb\u4fcc\u4fcd\u4fce\u4fcf\u4fd0\u4fd1\u4fd2\u4fd3\u4fd4\u4fd5\u4fd6\u4fd7\u4fd8\u4fd9\u4fda\u4fdb\u4fdc\u4fdd\u4fde\u4fdf\u4fe0\u4fe1\u4fe2\u4fe3\u4fe4\u4fe5\u4fe6\u4fe7\u4fe8\u4fe9\u4fea\u4feb\u4fec\u4fed\u4fee\u4fef\u4ff0\u4ff1\u4ff2\u4ff3\u4ff4\u4ff5\u4ff6\u4ff7\u4ff8\u4ff9\u4ffa\u4ffb\u4ffc\u4ffd\u4ffe\u4fff\u5000\u5001\u5002\u5003\u5004\u5005\u5006\u5007\u5008\u5009\u500a\u500b\u500c\u500d\u500e\u500f\u5010\u5011\u5012\u5013\u5014\u5015\u5016\u5017\u5018\u5019\u501a\u501b\u501c\u501d\u501e\u501f\u5020\u5021\u5022\u5023\u5024\u5025\u5026\u5027\u5028\u5029\u502a\u502b\u502c\u502d\u502e\u502f\u5030\u5031\u5032\u5033\u5034\u5035\u5036\u5037\u5038\u5039\u503a\u503b\u503c\u503d\u503e\u503f\u5040\u5041\u5042\u5043\u5044\u5045\u5046\u5047\u5048\u5049\u504a\u504b\u504c\u504d\u504e\u504f\u5050\u5051\u5052\u5053\u5054\u5055\u5056\u5057\u5058\u5059\u505a\u505b\u505c\u505d\u505e\u505f\u5060\u5061\u5062\u5063\u5064\u5065\u5066\u5067\u5068\u5069\u506a\u506b\u506c\u506d\u506e\u506f\u5070\u5071\u5072\u5073\u5074\u5075\u5076\u5077\u5078\u5079\u507a\u507b\u507c\u507d\u507e\u507f\u5080\u5081\u5082\u5083\u5084\u5085\u5086\u5087\u5088\u5089\u508a\u508b\u508c\u508d\u508e\u508f\u5090\u5091\u5092\u5093\u5094\u5095\u5096\u5097\u5098\u5099\u509a\u509b\u509c\u509d\u509e\u509f\u50a0\u50a1\u50a2\u50a3\u50a4\u50a5\u50a6\u50a7\u50a8\u50a9\u50aa\u50ab\u50ac\u50ad\u50ae\u50af\u50b0\u50b1\u50b2\u50b3\u50b4\u50b5\u50b6\u50b7\u50b8\u50b9\u50ba\u50bb\u50bc\u50bd\u50be\u50bf\u50c0\u50c1\u50c2\u50c3\u50c4\u50c5\u50c6\u50c7\u50c8\u50c9\u50ca\u50cb\u50cc\u50cd\u50ce\u50cf\u50d0\u50d1\u50d2\u50d3\u50d4\u50d5\u50d6\u50d7\u50d8\u50d9\u50da\u50db\u50dc\u50dd\u50de\u50df\u50e0\u50e1\u50e2\u50e3\u50e4\u50e5\u50e6\u50e7\u50e8\u50e9\u50ea\u50eb\u50ec\u50ed\u50ee\u50ef\u50f0\u50f1\u50f2\u50f3\u50f4\u50f5\u50f6\u50f7\u50f8\u50f9\u50fa\u50fb\u50fc\u50fd\u50fe\u50ff\u5100\u5101\u5102\u5103\u5104\u5105\u5106\u5107\u5108\u5109\u510a\u510b\u510c\u510d\u510e\u510f\u5110\u5111\u5112\u5113\u5114\u5115\u5116\u5117\u5118\u5119\u511a\u511b\u511c\u511d\u511e\u511f\u5120\u5121\u5122\u5123\u5124\u5125\u5126\u5127\u5128\u5129\u512a\u512b\u512c\u512d\u512e\u512f\u5130\u5131\u5132\u5133\u5134\u5135\u5136\u5137\u5138\u5139\u513a\u513b\u513c\u513d\u513e\u513f\u5140\u5141\u5142\u5143\u5144\u5145\u5146\u5147\u5148\u5149\u514a\u514b\u514c\u514d\u514e\u514f\u5150\u5151\u5152\u5153\u5154\u5155\u5156\u5157\u5158\u5159\u515a\u515b\u515c\u515d\u515e\u515f\u5160\u5161\u5162\u5163\u5164\u5165\u5166\u5167\u5168\u5169\u516a\u516b\u516c\u516d\u516e\u516f\u5170\u5171\u5172\u5173\u5174\u5175\u5176\u5177\u5178\u5179\u517a\u517b\u517c\u517d\u517e\u517f\u5180\u5181\u5182\u5183\u5184\u5185\u5186\u5187\u5188\u5189\u518a\u518b\u518c\u518d\u518e\u518f\u5190\u5191\u5192\u5193\u5194\u5195\u5196\u5197\u5198\u5199\u519a\u519b\u519c\u519d\u519e\u519f\u51a0\u51a1\u51a2\u51a3\u51a4\u51a5\u51a6\u51a7\u51a8\u51a9\u51aa\u51ab\u51ac\u51ad\u51ae\u51af\u51b0\u51b1\u51b2\u51b3\u51b4\u51b5\u51b6\u51b7\u51b8\u51b9\u51ba\u51bb\u51bc\u51bd\u51be\u51bf\u51c0\u51c1\u51c2\u51c3\u51c4\u51c5\u51c6\u51c7\u51c8\u51c9\u51ca\u51cb\u51cc\u51cd\u51ce\u51cf\u51d0\u51d1\u51d2\u51d3\u51d4\u51d5\u51d6\u51d7\u51d8\u51d9\u51da\u51db\u51dc\u51dd\u51de\u51df\u51e0\u51e1\u51e2\u51e3\u51e4\u51e5\u51e6\u51e7\u51e8\u51e9\u51ea\u51eb\u51ec\u51ed\u51ee\u51ef\u51f0\u51f1\u51f2\u51f3\u51f4\u51f5\u51f6\u51f7\u51f8\u51f9\u51fa\u51fb\u51fc\u51fd\u51fe\u51ff\u5200\u5201\u5202\u5203\u5204\u5205\u5206\u5207\u5208\u5209\u520a\u520b\u520c\u520d\u520e\u520f\u5210\u5211\u5212\u5213\u5214\u5215\u5216\u5217\u5218\u5219\u521a\u521b\u521c\u521d\u521e\u521f\u5220\u5221\u5222\u5223\u5224\u5225\u5226\u5227\u5228\u5229\u522a\u522b\u522c\u522d\u522e\u522f\u5230\u5231\u5232\u5233\u5234\u5235\u5236\u5237\u5238\u5239\u523a\u523b\u523c\u523d\u523e\u523f\u5240\u5241\u5242\u5243\u5244\u5245\u5246\u5247\u5248\u5249\u524a\u524b\u524c\u524d\u524e\u524f\u5250\u5251\u5252\u5253\u5254\u5255\u5256\u5257\u5258\u5259\u525a\u525b\u525c\u525d\u525e\u525f\u5260\u5261\u5262\u5263\u5264\u5265\u5266\u5267\u5268\u5269\u526a\u526b\u526c\u526d\u526e\u526f\u5270\u5271\u5272\u5273\u5274\u5275\u5276\u5277\u5278\u5279\u527a\u527b\u527c\u527d\u527e\u527f\u5280\u5281\u5282\u5283\u5284\u5285\u5286\u5287\u5288\u5289\u528a\u528b\u528c\u528d\u528e\u528f\u5290\u5291\u5292\u5293\u5294\u5295\u5296\u5297\u5298\u5299\u529a\u529b\u529c\u529d\u529e\u529f\u52a0\u52a1\u52a2\u52a3\u52a4\u52a5\u52a6\u52a7\u52a8\u52a9\u52aa\u52ab\u52ac\u52ad\u52ae\u52af\u52b0\u52b1\u52b2\u52b3\u52b4\u52b5\u52b6\u52b7\u52b8\u52b9\u52ba\u52bb\u52bc\u52bd\u52be\u52bf\u52c0\u52c1\u52c2\u52c3\u52c4\u52c5\u52c6\u52c7\u52c8\u52c9\u52ca\u52cb\u52cc\u52cd\u52ce\u52cf\u52d0\u52d1\u52d2\u52d3\u52d4\u52d5\u52d6\u52d7\u52d8\u52d9\u52da\u52db\u52dc\u52dd\u52de\u52df\u52e0\u52e1\u52e2\u52e3\u52e4\u52e5\u52e6\u52e7\u52e8\u52e9\u52ea\u52eb\u52ec\u52ed\u52ee\u52ef\u52f0\u52f1\u52f2\u52f3\u52f4\u52f5\u52f6\u52f7\u52f8\u52f9\u52fa\u52fb\u52fc\u52fd\u52fe\u52ff\u5300\u5301\u5302\u5303\u5304\u5305\u5306\u5307\u5308\u5309\u530a\u530b\u530c\u530d\u530e\u530f\u5310\u5311\u5312\u5313\u5314\u5315\u5316\u5317\u5318\u5319\u531a\u531b\u531c\u531d\u531e\u531f\u5320\u5321\u5322\u5323\u5324\u5325\u5326\u5327\u5328\u5329\u532a\u532b\u532c\u532d\u532e\u532f\u5330\u5331\u5332\u5333\u5334\u5335\u5336\u5337\u5338\u5339\u533a\u533b\u533c\u533d\u533e\u533f\u5340\u5341\u5342\u5343\u5344\u5345\u5346\u5347\u5348\u5349\u534a\u534b\u534c\u534d\u534e\u534f\u5350\u5351\u5352\u5353\u5354\u5355\u5356\u5357\u5358\u5359\u535a\u535b\u535c\u535d\u535e\u535f\u5360\u5361\u5362\u5363\u5364\u5365\u5366\u5367\u5368\u5369\u536a\u536b\u536c\u536d\u536e\u536f\u5370\u5371\u5372\u5373\u5374\u5375\u5376\u5377\u5378\u5379\u537a\u537b\u537c\u537d\u537e\u537f\u5380\u5381\u5382\u5383\u5384\u5385\u5386\u5387\u5388\u5389\u538a\u538b\u538c\u538d\u538e\u538f\u5390\u5391\u5392\u5393\u5394\u5395\u5396\u5397\u5398\u5399\u539a\u539b\u539c\u539d\u539e\u539f\u53a0\u53a1\u53a2\u53a3\u53a4\u53a5\u53a6\u53a7\u53a8\u53a9\u53aa\u53ab\u53ac\u53ad\u53ae\u53af\u53b0\u53b1\u53b2\u53b3\u53b4\u53b5\u53b6\u53b7\u53b8\u53b9\u53ba\u53bb\u53bc\u53bd\u53be\u53bf\u53c0\u53c1\u53c2\u53c3\u53c4\u53c5\u53c6\u53c7\u53c8\u53c9\u53ca\u53cb\u53cc\u53cd\u53ce\u53cf\u53d0\u53d1\u53d2\u53d3\u53d4\u53d5\u53d6\u53d7\u53d8\u53d9\u53da\u53db\u53dc\u53dd\u53de\u53df\u53e0\u53e1\u53e2\u53e3\u53e4\u53e5\u53e6\u53e7\u53e8\u53e9\u53ea\u53eb\u53ec\u53ed\u53ee\u53ef\u53f0\u53f1\u53f2\u53f3\u53f4\u53f5\u53f6\u53f7\u53f8\u53f9\u53fa\u53fb\u53fc\u53fd\u53fe\u53ff\u5400\u5401\u5402\u5403\u5404\u5405\u5406\u5407\u5408\u5409\u540a\u540b\u540c\u540d\u540e\u540f\u5410\u5411\u5412\u5413\u5414\u5415\u5416\u5417\u5418\u5419\u541a\u541b\u541c\u541d\u541e\u541f\u5420\u5421\u5422\u5423\u5424\u5425\u5426\u5427\u5428\u5429\u542a\u542b\u542c\u542d\u542e\u542f\u5430\u5431\u5432\u5433\u5434\u5435\u5436\u5437\u5438\u5439\u543a\u543b\u543c\u543d\u543e\u543f\u5440\u5441\u5442\u5443\u5444\u5445\u5446\u5447\u5448\u5449\u544a\u544b\u544c\u544d\u544e\u544f\u5450\u5451\u5452\u5453\u5454\u5455\u5456\u5457\u5458\u5459\u545a\u545b\u545c\u545d\u545e\u545f\u5460\u5461\u5462\u5463\u5464\u5465\u5466\u5467\u5468\u5469\u546a\u546b\u546c\u546d\u546e\u546f\u5470\u5471\u5472\u5473\u5474\u5475\u5476\u5477\u5478\u5479\u547a\u547b\u547c\u547d\u547e\u547f\u5480\u5481\u5482\u5483\u5484\u5485\u5486\u5487\u5488\u5489\u548a\u548b\u548c\u548d\u548e\u548f\u5490\u5491\u5492\u5493\u5494\u5495\u5496\u5497\u5498\u5499\u549a\u549b\u549c\u549d\u549e\u549f\u54a0\u54a1\u54a2\u54a3\u54a4\u54a5\u54a6\u54a7\u54a8\u54a9\u54aa\u54ab\u54ac\u54ad\u54ae\u54af\u54b0\u54b1\u54b2\u54b3\u54b4\u54b5\u54b6\u54b7\u54b8\u54b9\u54ba\u54bb\u54bc\u54bd\u54be\u54bf\u54c0\u54c1\u54c2\u54c3\u54c4\u54c5\u54c6\u54c7\u54c8\u54c9\u54ca\u54cb\u54cc\u54cd\u54ce\u54cf\u54d0\u54d1\u54d2\u54d3\u54d4\u54d5\u54d6\u54d7\u54d8\u54d9\u54da\u54db\u54dc\u54dd\u54de\u54df\u54e0\u54e1\u54e2\u54e3\u54e4\u54e5\u54e6\u54e7\u54e8\u54e9\u54ea\u54eb\u54ec\u54ed\u54ee\u54ef\u54f0\u54f1\u54f2\u54f3\u54f4\u54f5\u54f6\u54f7\u54f8\u54f9\u54fa\u54fb\u54fc\u54fd\u54fe\u54ff\u5500\u5501\u5502\u5503\u5504\u5505\u5506\u5507\u5508\u5509\u550a\u550b\u550c\u550d\u550e\u550f\u5510\u5511\u5512\u5513\u5514\u5515\u5516\u5517\u5518\u5519\u551a\u551b\u551c\u551d\u551e\u551f\u5520\u5521\u5522\u5523\u5524\u5525\u5526\u5527\u5528\u5529\u552a\u552b\u552c\u552d\u552e\u552f\u5530\u5531\u5532\u5533\u5534\u5535\u5536\u5537\u5538\u5539\u553a\u553b\u553c\u553d\u553e\u553f\u5540\u5541\u5542\u5543\u5544\u5545\u5546\u5547\u5548\u5549\u554a\u554b\u554c\u554d\u554e\u554f\u5550\u5551\u5552\u5553\u5554\u5555\u5556\u5557\u5558\u5559\u555a\u555b\u555c\u555d\u555e\u555f\u5560\u5561\u5562\u5563\u5564\u5565\u5566\u5567\u5568\u5569\u556a\u556b\u556c\u556d\u556e\u556f\u5570\u5571\u5572\u5573\u5574\u5575\u5576\u5577\u5578\u5579\u557a\u557b\u557c\u557d\u557e\u557f\u5580\u5581\u5582\u5583\u5584\u5585\u5586\u5587\u5588\u5589\u558a\u558b\u558c\u558d\u558e\u558f\u5590\u5591\u5592\u5593\u5594\u5595\u5596\u5597\u5598\u5599\u559a\u559b\u559c\u559d\u559e\u559f\u55a0\u55a1\u55a2\u55a3\u55a4\u55a5\u55a6\u55a7\u55a8\u55a9\u55aa\u55ab\u55ac\u55ad\u55ae\u55af\u55b0\u55b1\u55b2\u55b3\u55b4\u55b5\u55b6\u55b7\u55b8\u55b9\u55ba\u55bb\u55bc\u55bd\u55be\u55bf\u55c0\u55c1\u55c2\u55c3\u55c4\u55c5\u55c6\u55c7\u55c8\u55c9\u55ca\u55cb\u55cc\u55cd\u55ce\u55cf\u55d0\u55d1\u55d2\u55d3\u55d4\u55d5\u55d6\u55d7\u55d8\u55d9\u55da\u55db\u55dc\u55dd\u55de\u55df\u55e0\u55e1\u55e2\u55e3\u55e4\u55e5\u55e6\u55e7\u55e8\u55e9\u55ea\u55eb\u55ec\u55ed\u55ee\u55ef\u55f0\u55f1\u55f2\u55f3\u55f4\u55f5\u55f6\u55f7\u55f8\u55f9\u55fa\u55fb\u55fc\u55fd\u55fe\u55ff\u5600\u5601\u5602\u5603\u5604\u5605\u5606\u5607\u5608\u5609\u560a\u560b\u560c\u560d\u560e\u560f\u5610\u5611\u5612\u5613\u5614\u5615\u5616\u5617\u5618\u5619\u561a\u561b\u561c\u561d\u561e\u561f\u5620\u5621\u5622\u5623\u5624\u5625\u5626\u5627\u5628\u5629\u562a\u562b\u562c\u562d\u562e\u562f\u5630\u5631\u5632\u5633\u5634\u5635\u5636\u5637\u5638\u5639\u563a\u563b\u563c\u563d\u563e\u563f\u5640\u5641\u5642\u5643\u5644\u5645\u5646\u5647\u5648\u5649\u564a\u564b\u564c\u564d\u564e\u564f\u5650\u5651\u5652\u5653\u5654\u5655\u5656\u5657\u5658\u5659\u565a\u565b\u565c\u565d\u565e\u565f\u5660\u5661\u5662\u5663\u5664\u5665\u5666\u5667\u5668\u5669\u566a\u566b\u566c\u566d\u566e\u566f\u5670\u5671\u5672\u5673\u5674\u5675\u5676\u5677\u5678\u5679\u567a\u567b\u567c\u567d\u567e\u567f\u5680\u5681\u5682\u5683\u5684\u5685\u5686\u5687\u5688\u5689\u568a\u568b\u568c\u568d\u568e\u568f\u5690\u5691\u5692\u5693\u5694\u5695\u5696\u5697\u5698\u5699\u569a\u569b\u569c\u569d\u569e\u569f\u56a0\u56a1\u56a2\u56a3\u56a4\u56a5\u56a6\u56a7\u56a8\u56a9\u56aa\u56ab\u56ac\u56ad\u56ae\u56af\u56b0\u56b1\u56b2\u56b3\u56b4\u56b5\u56b6\u56b7\u56b8\u56b9\u56ba\u56bb\u56bc\u56bd\u56be\u56bf\u56c0\u56c1\u56c2\u56c3\u56c4\u56c5\u56c6\u56c7\u56c8\u56c9\u56ca\u56cb\u56cc\u56cd\u56ce\u56cf\u56d0\u56d1\u56d2\u56d3\u56d4\u56d5\u56d6\u56d7\u56d8\u56d9\u56da\u56db\u56dc\u56dd\u56de\u56df\u56e0\u56e1\u56e2\u56e3\u56e4\u56e5\u56e6\u56e7\u56e8\u56e9\u56ea\u56eb\u56ec\u56ed\u56ee\u56ef\u56f0\u56f1\u56f2\u56f3\u56f4\u56f5\u56f6\u56f7\u56f8\u56f9\u56fa\u56fb\u56fc\u56fd\u56fe\u56ff\u5700\u5701\u5702\u5703\u5704\u5705\u5706\u5707\u5708\u5709\u570a\u570b\u570c\u570d\u570e\u570f\u5710\u5711\u5712\u5713\u5714\u5715\u5716\u5717\u5718\u5719\u571a\u571b\u571c\u571d\u571e\u571f\u5720\u5721\u5722\u5723\u5724\u5725\u5726\u5727\u5728\u5729\u572a\u572b\u572c\u572d\u572e\u572f\u5730\u5731\u5732\u5733\u5734\u5735\u5736\u5737\u5738\u5739\u573a\u573b\u573c\u573d\u573e\u573f\u5740\u5741\u5742\u5743\u5744\u5745\u5746\u5747\u5748\u5749\u574a\u574b\u574c\u574d\u574e\u574f\u5750\u5751\u5752\u5753\u5754\u5755\u5756\u5757\u5758\u5759\u575a\u575b\u575c\u575d\u575e\u575f\u5760\u5761\u5762\u5763\u5764\u5765\u5766\u5767\u5768\u5769\u576a\u576b\u576c\u576d\u576e\u576f\u5770\u5771\u5772\u5773\u5774\u5775\u5776\u5777\u5778\u5779\u577a\u577b\u577c\u577d\u577e\u577f\u5780\u5781\u5782\u5783\u5784\u5785\u5786\u5787\u5788\u5789\u578a\u578b\u578c\u578d\u578e\u578f\u5790\u5791\u5792\u5793\u5794\u5795\u5796\u5797\u5798\u5799\u579a\u579b\u579c\u579d\u579e\u579f\u57a0\u57a1\u57a2\u57a3\u57a4\u57a5\u57a6\u57a7\u57a8\u57a9\u57aa\u57ab\u57ac\u57ad\u57ae\u57af\u57b0\u57b1\u57b2\u57b3\u57b4\u57b5\u57b6\u57b7\u57b8\u57b9\u57ba\u57bb\u57bc\u57bd\u57be\u57bf\u57c0\u57c1\u57c2\u57c3\u57c4\u57c5\u57c6\u57c7\u57c8\u57c9\u57ca\u57cb\u57cc\u57cd\u57ce\u57cf\u57d0\u57d1\u57d2\u57d3\u57d4\u57d5\u57d6\u57d7\u57d8\u57d9\u57da\u57db\u57dc\u57dd\u57de\u57df\u57e0\u57e1\u57e2\u57e3\u57e4\u57e5\u57e6\u57e7\u57e8\u57e9\u57ea\u57eb\u57ec\u57ed\u57ee\u57ef\u57f0\u57f1\u57f2\u57f3\u57f4\u57f5\u57f6\u57f7\u57f8\u57f9\u57fa\u57fb\u57fc\u57fd\u57fe\u57ff\u5800\u5801\u5802\u5803\u5804\u5805\u5806\u5807\u5808\u5809\u580a\u580b\u580c\u580d\u580e\u580f\u5810\u5811\u5812\u5813\u5814\u5815\u5816\u5817\u5818\u5819\u581a\u581b\u581c\u581d\u581e\u581f\u5820\u5821\u5822\u5823\u5824\u5825\u5826\u5827\u5828\u5829\u582a\u582b\u582c\u582d\u582e\u582f\u5830\u5831\u5832\u5833\u5834\u5835\u5836\u5837\u5838\u5839\u583a\u583b\u583c\u583d\u583e\u583f\u5840\u5841\u5842\u5843\u5844\u5845\u5846\u5847\u5848\u5849\u584a\u584b\u584c\u584d\u584e\u584f\u5850\u5851\u5852\u5853\u5854\u5855\u5856\u5857\u5858\u5859\u585a\u585b\u585c\u585d\u585e\u585f\u5860\u5861\u5862\u5863\u5864\u5865\u5866\u5867\u5868\u5869\u586a\u586b\u586c\u586d\u586e\u586f\u5870\u5871\u5872\u5873\u5874\u5875\u5876\u5877\u5878\u5879\u587a\u587b\u587c\u587d\u587e\u587f\u5880\u5881\u5882\u5883\u5884\u5885\u5886\u5887\u5888\u5889\u588a\u588b\u588c\u588d\u588e\u588f\u5890\u5891\u5892\u5893\u5894\u5895\u5896\u5897\u5898\u5899\u589a\u589b\u589c\u589d\u589e\u589f\u58a0\u58a1\u58a2\u58a3\u58a4\u58a5\u58a6\u58a7\u58a8\u58a9\u58aa\u58ab\u58ac\u58ad\u58ae\u58af\u58b0\u58b1\u58b2\u58b3\u58b4\u58b5\u58b6\u58b7\u58b8\u58b9\u58ba\u58bb\u58bc\u58bd\u58be\u58bf\u58c0\u58c1\u58c2\u58c3\u58c4\u58c5\u58c6\u58c7\u58c8\u58c9\u58ca\u58cb\u58cc\u58cd\u58ce\u58cf\u58d0\u58d1\u58d2\u58d3\u58d4\u58d5\u58d6\u58d7\u58d8\u58d9\u58da\u58db\u58dc\u58dd\u58de\u58df\u58e0\u58e1\u58e2\u58e3\u58e4\u58e5\u58e6\u58e7\u58e8\u58e9\u58ea\u58eb\u58ec\u58ed\u58ee\u58ef\u58f0\u58f1\u58f2\u58f3\u58f4\u58f5\u58f6\u58f7\u58f8\u58f9\u58fa\u58fb\u58fc\u58fd\u58fe\u58ff\u5900\u5901\u5902\u5903\u5904\u5905\u5906\u5907\u5908\u5909\u590a\u590b\u590c\u590d\u590e\u590f\u5910\u5911\u5912\u5913\u5914\u5915\u5916\u5917\u5918\u5919\u591a\u591b\u591c\u591d\u591e\u591f\u5920\u5921\u5922\u5923\u5924\u5925\u5926\u5927\u5928\u5929\u592a\u592b\u592c\u592d\u592e\u592f\u5930\u5931\u5932\u5933\u5934\u5935\u5936\u5937\u5938\u5939\u593a\u593b\u593c\u593d\u593e\u593f\u5940\u5941\u5942\u5943\u5944\u5945\u5946\u5947\u5948\u5949\u594a\u594b\u594c\u594d\u594e\u594f\u5950\u5951\u5952\u5953\u5954\u5955\u5956\u5957\u5958\u5959\u595a\u595b\u595c\u595d\u595e\u595f\u5960\u5961\u5962\u5963\u5964\u5965\u5966\u5967\u5968\u5969\u596a\u596b\u596c\u596d\u596e\u596f\u5970\u5971\u5972\u5973\u5974\u5975\u5976\u5977\u5978\u5979\u597a\u597b\u597c\u597d\u597e\u597f\u5980\u5981\u5982\u5983\u5984\u5985\u5986\u5987\u5988\u5989\u598a\u598b\u598c\u598d\u598e\u598f\u5990\u5991\u5992\u5993\u5994\u5995\u5996\u5997\u5998\u5999\u599a\u599b\u599c\u599d\u599e\u599f\u59a0\u59a1\u59a2\u59a3\u59a4\u59a5\u59a6\u59a7\u59a8\u59a9\u59aa\u59ab\u59ac\u59ad\u59ae\u59af\u59b0\u59b1\u59b2\u59b3\u59b4\u59b5\u59b6\u59b7\u59b8\u59b9\u59ba\u59bb\u59bc\u59bd\u59be\u59bf\u59c0\u59c1\u59c2\u59c3\u59c4\u59c5\u59c6\u59c7\u59c8\u59c9\u59ca\u59cb\u59cc\u59cd\u59ce\u59cf\u59d0\u59d1\u59d2\u59d3\u59d4\u59d5\u59d6\u59d7\u59d8\u59d9\u59da\u59db\u59dc\u59dd\u59de\u59df\u59e0\u59e1\u59e2\u59e3\u59e4\u59e5\u59e6\u59e7\u59e8\u59e9\u59ea\u59eb\u59ec\u59ed\u59ee\u59ef\u59f0\u59f1\u59f2\u59f3\u59f4\u59f5\u59f6\u59f7\u59f8\u59f9\u59fa\u59fb\u59fc\u59fd\u59fe\u59ff\u5a00\u5a01\u5a02\u5a03\u5a04\u5a05\u5a06\u5a07\u5a08\u5a09\u5a0a\u5a0b\u5a0c\u5a0d\u5a0e\u5a0f\u5a10\u5a11\u5a12\u5a13\u5a14\u5a15\u5a16\u5a17\u5a18\u5a19\u5a1a\u5a1b\u5a1c\u5a1d\u5a1e\u5a1f\u5a20\u5a21\u5a22\u5a23\u5a24\u5a25\u5a26\u5a27\u5a28\u5a29\u5a2a\u5a2b\u5a2c\u5a2d\u5a2e\u5a2f\u5a30\u5a31\u5a32\u5a33\u5a34\u5a35\u5a36\u5a37\u5a38\u5a39\u5a3a\u5a3b\u5a3c\u5a3d\u5a3e\u5a3f\u5a40\u5a41\u5a42\u5a43\u5a44\u5a45\u5a46\u5a47\u5a48\u5a49\u5a4a\u5a4b\u5a4c\u5a4d\u5a4e\u5a4f\u5a50\u5a51\u5a52\u5a53\u5a54\u5a55\u5a56\u5a57\u5a58\u5a59\u5a5a\u5a5b\u5a5c\u5a5d\u5a5e\u5a5f\u5a60\u5a61\u5a62\u5a63\u5a64\u5a65\u5a66\u5a67\u5a68\u5a69\u5a6a\u5a6b\u5a6c\u5a6d\u5a6e\u5a6f\u5a70\u5a71\u5a72\u5a73\u5a74\u5a75\u5a76\u5a77\u5a78\u5a79\u5a7a\u5a7b\u5a7c\u5a7d\u5a7e\u5a7f\u5a80\u5a81\u5a82\u5a83\u5a84\u5a85\u5a86\u5a87\u5a88\u5a89\u5a8a\u5a8b\u5a8c\u5a8d\u5a8e\u5a8f\u5a90\u5a91\u5a92\u5a93\u5a94\u5a95\u5a96\u5a97\u5a98\u5a99\u5a9a\u5a9b\u5a9c\u5a9d\u5a9e\u5a9f\u5aa0\u5aa1\u5aa2\u5aa3\u5aa4\u5aa5\u5aa6\u5aa7\u5aa8\u5aa9\u5aaa\u5aab\u5aac\u5aad\u5aae\u5aaf\u5ab0\u5ab1\u5ab2\u5ab3\u5ab4\u5ab5\u5ab6\u5ab7\u5ab8\u5ab9\u5aba\u5abb\u5abc\u5abd\u5abe\u5abf\u5ac0\u5ac1\u5ac2\u5ac3\u5ac4\u5ac5\u5ac6\u5ac7\u5ac8\u5ac9\u5aca\u5acb\u5acc\u5acd\u5ace\u5acf\u5ad0\u5ad1\u5ad2\u5ad3\u5ad4\u5ad5\u5ad6\u5ad7\u5ad8\u5ad9\u5ada\u5adb\u5adc\u5add\u5ade\u5adf\u5ae0\u5ae1\u5ae2\u5ae3\u5ae4\u5ae5\u5ae6\u5ae7\u5ae8\u5ae9\u5aea\u5aeb\u5aec\u5aed\u5aee\u5aef\u5af0\u5af1\u5af2\u5af3\u5af4\u5af5\u5af6\u5af7\u5af8\u5af9\u5afa\u5afb\u5afc\u5afd\u5afe\u5aff\u5b00\u5b01\u5b02\u5b03\u5b04\u5b05\u5b06\u5b07\u5b08\u5b09\u5b0a\u5b0b\u5b0c\u5b0d\u5b0e\u5b0f\u5b10\u5b11\u5b12\u5b13\u5b14\u5b15\u5b16\u5b17\u5b18\u5b19\u5b1a\u5b1b\u5b1c\u5b1d\u5b1e\u5b1f\u5b20\u5b21\u5b22\u5b23\u5b24\u5b25\u5b26\u5b27\u5b28\u5b29\u5b2a\u5b2b\u5b2c\u5b2d\u5b2e\u5b2f\u5b30\u5b31\u5b32\u5b33\u5b34\u5b35\u5b36\u5b37\u5b38\u5b39\u5b3a\u5b3b\u5b3c\u5b3d\u5b3e\u5b3f\u5b40\u5b41\u5b42\u5b43\u5b44\u5b45\u5b46\u5b47\u5b48\u5b49\u5b4a\u5b4b\u5b4c\u5b4d\u5b4e\u5b4f\u5b50\u5b51\u5b52\u5b53\u5b54\u5b55\u5b56\u5b57\u5b58\u5b59\u5b5a\u5b5b\u5b5c\u5b5d\u5b5e\u5b5f\u5b60\u5b61\u5b62\u5b63\u5b64\u5b65\u5b66\u5b67\u5b68\u5b69\u5b6a\u5b6b\u5b6c\u5b6d\u5b6e\u5b6f\u5b70\u5b71\u5b72\u5b73\u5b74\u5b75\u5b76\u5b77\u5b78\u5b79\u5b7a\u5b7b\u5b7c\u5b7d\u5b7e\u5b7f\u5b80\u5b81\u5b82\u5b83\u5b84\u5b85\u5b86\u5b87\u5b88\u5b89\u5b8a\u5b8b\u5b8c\u5b8d\u5b8e\u5b8f\u5b90\u5b91\u5b92\u5b93\u5b94\u5b95\u5b96\u5b97\u5b98\u5b99\u5b9a\u5b9b\u5b9c\u5b9d\u5b9e\u5b9f\u5ba0\u5ba1\u5ba2\u5ba3\u5ba4\u5ba5\u5ba6\u5ba7\u5ba8\u5ba9\u5baa\u5bab\u5bac\u5bad\u5bae\u5baf\u5bb0\u5bb1\u5bb2\u5bb3\u5bb4\u5bb5\u5bb6\u5bb7\u5bb8\u5bb9\u5bba\u5bbb\u5bbc\u5bbd\u5bbe\u5bbf\u5bc0\u5bc1\u5bc2\u5bc3\u5bc4\u5bc5\u5bc6\u5bc7\u5bc8\u5bc9\u5bca\u5bcb\u5bcc\u5bcd\u5bce\u5bcf\u5bd0\u5bd1\u5bd2\u5bd3\u5bd4\u5bd5\u5bd6\u5bd7\u5bd8\u5bd9\u5bda\u5bdb\u5bdc\u5bdd\u5bde\u5bdf\u5be0\u5be1\u5be2\u5be3\u5be4\u5be5\u5be6\u5be7\u5be8\u5be9\u5bea\u5beb\u5bec\u5bed\u5bee\u5bef\u5bf0\u5bf1\u5bf2\u5bf3\u5bf4\u5bf5\u5bf6\u5bf7\u5bf8\u5bf9\u5bfa\u5bfb\u5bfc\u5bfd\u5bfe\u5bff\u5c00\u5c01\u5c02\u5c03\u5c04\u5c05\u5c06\u5c07\u5c08\u5c09\u5c0a\u5c0b\u5c0c\u5c0d\u5c0e\u5c0f\u5c10\u5c11\u5c12\u5c13\u5c14\u5c15\u5c16\u5c17\u5c18\u5c19\u5c1a\u5c1b\u5c1c\u5c1d\u5c1e\u5c1f\u5c20\u5c21\u5c22\u5c23\u5c24\u5c25\u5c26\u5c27\u5c28\u5c29\u5c2a\u5c2b\u5c2c\u5c2d\u5c2e\u5c2f\u5c30\u5c31\u5c32\u5c33\u5c34\u5c35\u5c36\u5c37\u5c38\u5c39\u5c3a\u5c3b\u5c3c\u5c3d\u5c3e\u5c3f\u5c40\u5c41\u5c42\u5c43\u5c44\u5c45\u5c46\u5c47\u5c48\u5c49\u5c4a\u5c4b\u5c4c\u5c4d\u5c4e\u5c4f\u5c50\u5c51\u5c52\u5c53\u5c54\u5c55\u5c56\u5c57\u5c58\u5c59\u5c5a\u5c5b\u5c5c\u5c5d\u5c5e\u5c5f\u5c60\u5c61\u5c62\u5c63\u5c64\u5c65\u5c66\u5c67\u5c68\u5c69\u5c6a\u5c6b\u5c6c\u5c6d\u5c6e\u5c6f\u5c70\u5c71\u5c72\u5c73\u5c74\u5c75\u5c76\u5c77\u5c78\u5c79\u5c7a\u5c7b\u5c7c\u5c7d\u5c7e\u5c7f\u5c80\u5c81\u5c82\u5c83\u5c84\u5c85\u5c86\u5c87\u5c88\u5c89\u5c8a\u5c8b\u5c8c\u5c8d\u5c8e\u5c8f\u5c90\u5c91\u5c92\u5c93\u5c94\u5c95\u5c96\u5c97\u5c98\u5c99\u5c9a\u5c9b\u5c9c\u5c9d\u5c9e\u5c9f\u5ca0\u5ca1\u5ca2\u5ca3\u5ca4\u5ca5\u5ca6\u5ca7\u5ca8\u5ca9\u5caa\u5cab\u5cac\u5cad\u5cae\u5caf\u5cb0\u5cb1\u5cb2\u5cb3\u5cb4\u5cb5\u5cb6\u5cb7\u5cb8\u5cb9\u5cba\u5cbb\u5cbc\u5cbd\u5cbe\u5cbf\u5cc0\u5cc1\u5cc2\u5cc3\u5cc4\u5cc5\u5cc6\u5cc7\u5cc8\u5cc9\u5cca\u5ccb\u5ccc\u5ccd\u5cce\u5ccf\u5cd0\u5cd1\u5cd2\u5cd3\u5cd4\u5cd5\u5cd6\u5cd7\u5cd8\u5cd9\u5cda\u5cdb\u5cdc\u5cdd\u5cde\u5cdf\u5ce0\u5ce1\u5ce2\u5ce3\u5ce4\u5ce5\u5ce6\u5ce7\u5ce8\u5ce9\u5cea\u5ceb\u5cec\u5ced\u5cee\u5cef\u5cf0\u5cf1\u5cf2\u5cf3\u5cf4\u5cf5\u5cf6\u5cf7\u5cf8\u5cf9\u5cfa\u5cfb\u5cfc\u5cfd\u5cfe\u5cff\u5d00\u5d01\u5d02\u5d03\u5d04\u5d05\u5d06\u5d07\u5d08\u5d09\u5d0a\u5d0b\u5d0c\u5d0d\u5d0e\u5d0f\u5d10\u5d11\u5d12\u5d13\u5d14\u5d15\u5d16\u5d17\u5d18\u5d19\u5d1a\u5d1b\u5d1c\u5d1d\u5d1e\u5d1f\u5d20\u5d21\u5d22\u5d23\u5d24\u5d25\u5d26\u5d27\u5d28\u5d29\u5d2a\u5d2b\u5d2c\u5d2d\u5d2e\u5d2f\u5d30\u5d31\u5d32\u5d33\u5d34\u5d35\u5d36\u5d37\u5d38\u5d39\u5d3a\u5d3b\u5d3c\u5d3d\u5d3e\u5d3f\u5d40\u5d41\u5d42\u5d43\u5d44\u5d45\u5d46\u5d47\u5d48\u5d49\u5d4a\u5d4b\u5d4c\u5d4d\u5d4e\u5d4f\u5d50\u5d51\u5d52\u5d53\u5d54\u5d55\u5d56\u5d57\u5d58\u5d59\u5d5a\u5d5b\u5d5c\u5d5d\u5d5e\u5d5f\u5d60\u5d61\u5d62\u5d63\u5d64\u5d65\u5d66\u5d67\u5d68\u5d69\u5d6a\u5d6b\u5d6c\u5d6d\u5d6e\u5d6f\u5d70\u5d71\u5d72\u5d73\u5d74\u5d75\u5d76\u5d77\u5d78\u5d79\u5d7a\u5d7b\u5d7c\u5d7d\u5d7e\u5d7f\u5d80\u5d81\u5d82\u5d83\u5d84\u5d85\u5d86\u5d87\u5d88\u5d89\u5d8a\u5d8b\u5d8c\u5d8d\u5d8e\u5d8f\u5d90\u5d91\u5d92\u5d93\u5d94\u5d95\u5d96\u5d97\u5d98\u5d99\u5d9a\u5d9b\u5d9c\u5d9d\u5d9e\u5d9f\u5da0\u5da1\u5da2\u5da3\u5da4\u5da5\u5da6\u5da7\u5da8\u5da9\u5daa\u5dab\u5dac\u5dad\u5dae\u5daf\u5db0\u5db1\u5db2\u5db3\u5db4\u5db5\u5db6\u5db7\u5db8\u5db9\u5dba\u5dbb\u5dbc\u5dbd\u5dbe\u5dbf\u5dc0\u5dc1\u5dc2\u5dc3\u5dc4\u5dc5\u5dc6\u5dc7\u5dc8\u5dc9\u5dca\u5dcb\u5dcc\u5dcd\u5dce\u5dcf\u5dd0\u5dd1\u5dd2\u5dd3\u5dd4\u5dd5\u5dd6\u5dd7\u5dd8\u5dd9\u5dda\u5ddb\u5ddc\u5ddd\u5dde\u5ddf\u5de0\u5de1\u5de2\u5de3\u5de4\u5de5\u5de6\u5de7\u5de8\u5de9\u5dea\u5deb\u5dec\u5ded\u5dee\u5def\u5df0\u5df1\u5df2\u5df3\u5df4\u5df5\u5df6\u5df7\u5df8\u5df9\u5dfa\u5dfb\u5dfc\u5dfd\u5dfe\u5dff\u5e00\u5e01\u5e02\u5e03\u5e04\u5e05\u5e06\u5e07\u5e08\u5e09\u5e0a\u5e0b\u5e0c\u5e0d\u5e0e\u5e0f\u5e10\u5e11\u5e12\u5e13\u5e14\u5e15\u5e16\u5e17\u5e18\u5e19\u5e1a\u5e1b\u5e1c\u5e1d\u5e1e\u5e1f\u5e20\u5e21\u5e22\u5e23\u5e24\u5e25\u5e26\u5e27\u5e28\u5e29\u5e2a\u5e2b\u5e2c\u5e2d\u5e2e\u5e2f\u5e30\u5e31\u5e32\u5e33\u5e34\u5e35\u5e36\u5e37\u5e38\u5e39\u5e3a\u5e3b\u5e3c\u5e3d\u5e3e\u5e3f\u5e40\u5e41\u5e42\u5e43\u5e44\u5e45\u5e46\u5e47\u5e48\u5e49\u5e4a\u5e4b\u5e4c\u5e4d\u5e4e\u5e4f\u5e50\u5e51\u5e52\u5e53\u5e54\u5e55\u5e56\u5e57\u5e58\u5e59\u5e5a\u5e5b\u5e5c\u5e5d\u5e5e\u5e5f\u5e60\u5e61\u5e62\u5e63\u5e64\u5e65\u5e66\u5e67\u5e68\u5e69\u5e6a\u5e6b\u5e6c\u5e6d\u5e6e\u5e6f\u5e70\u5e71\u5e72\u5e73\u5e74\u5e75\u5e76\u5e77\u5e78\u5e79\u5e7a\u5e7b\u5e7c\u5e7d\u5e7e\u5e7f\u5e80\u5e81\u5e82\u5e83\u5e84\u5e85\u5e86\u5e87\u5e88\u5e89\u5e8a\u5e8b\u5e8c\u5e8d\u5e8e\u5e8f\u5e90\u5e91\u5e92\u5e93\u5e94\u5e95\u5e96\u5e97\u5e98\u5e99\u5e9a\u5e9b\u5e9c\u5e9d\u5e9e\u5e9f\u5ea0\u5ea1\u5ea2\u5ea3\u5ea4\u5ea5\u5ea6\u5ea7\u5ea8\u5ea9\u5eaa\u5eab\u5eac\u5ead\u5eae\u5eaf\u5eb0\u5eb1\u5eb2\u5eb3\u5eb4\u5eb5\u5eb6\u5eb7\u5eb8\u5eb9\u5eba\u5ebb\u5ebc\u5ebd\u5ebe\u5ebf\u5ec0\u5ec1\u5ec2\u5ec3\u5ec4\u5ec5\u5ec6\u5ec7\u5ec8\u5ec9\u5eca\u5ecb\u5ecc\u5ecd\u5ece\u5ecf\u5ed0\u5ed1\u5ed2\u5ed3\u5ed4\u5ed5\u5ed6\u5ed7\u5ed8\u5ed9\u5eda\u5edb\u5edc\u5edd\u5ede\u5edf\u5ee0\u5ee1\u5ee2\u5ee3\u5ee4\u5ee5\u5ee6\u5ee7\u5ee8\u5ee9\u5eea\u5eeb\u5eec\u5eed\u5eee\u5eef\u5ef0\u5ef1\u5ef2\u5ef3\u5ef4\u5ef5\u5ef6\u5ef7\u5ef8\u5ef9\u5efa\u5efb\u5efc\u5efd\u5efe\u5eff\u5f00\u5f01\u5f02\u5f03\u5f04\u5f05\u5f06\u5f07\u5f08\u5f09\u5f0a\u5f0b\u5f0c\u5f0d\u5f0e\u5f0f\u5f10\u5f11\u5f12\u5f13\u5f14\u5f15\u5f16\u5f17\u5f18\u5f19\u5f1a\u5f1b\u5f1c\u5f1d\u5f1e\u5f1f\u5f20\u5f21\u5f22\u5f23\u5f24\u5f25\u5f26\u5f27\u5f28\u5f29\u5f2a\u5f2b\u5f2c\u5f2d\u5f2e\u5f2f\u5f30\u5f31\u5f32\u5f33\u5f34\u5f35\u5f36\u5f37\u5f38\u5f39\u5f3a\u5f3b\u5f3c\u5f3d\u5f3e\u5f3f\u5f40\u5f41\u5f42\u5f43\u5f44\u5f45\u5f46\u5f47\u5f48\u5f49\u5f4a\u5f4b\u5f4c\u5f4d\u5f4e\u5f4f\u5f50\u5f51\u5f52\u5f53\u5f54\u5f55\u5f56\u5f57\u5f58\u5f59\u5f5a\u5f5b\u5f5c\u5f5d\u5f5e\u5f5f\u5f60\u5f61\u5f62\u5f63\u5f64\u5f65\u5f66\u5f67\u5f68\u5f69\u5f6a\u5f6b\u5f6c\u5f6d\u5f6e\u5f6f\u5f70\u5f71\u5f72\u5f73\u5f74\u5f75\u5f76\u5f77\u5f78\u5f79\u5f7a\u5f7b\u5f7c\u5f7d\u5f7e\u5f7f\u5f80\u5f81\u5f82\u5f83\u5f84\u5f85\u5f86\u5f87\u5f88\u5f89\u5f8a\u5f8b\u5f8c\u5f8d\u5f8e\u5f8f\u5f90\u5f91\u5f92\u5f93\u5f94\u5f95\u5f96\u5f97\u5f98\u5f99\u5f9a\u5f9b\u5f9c\u5f9d\u5f9e\u5f9f\u5fa0\u5fa1\u5fa2\u5fa3\u5fa4\u5fa5\u5fa6\u5fa7\u5fa8\u5fa9\u5faa\u5fab\u5fac\u5fad\u5fae\u5faf\u5fb0\u5fb1\u5fb2\u5fb3\u5fb4\u5fb5\u5fb6\u5fb7\u5fb8\u5fb9\u5fba\u5fbb\u5fbc\u5fbd\u5fbe\u5fbf\u5fc0\u5fc1\u5fc2\u5fc3\u5fc4\u5fc5\u5fc6\u5fc7\u5fc8\u5fc9\u5fca\u5fcb\u5fcc\u5fcd\u5fce\u5fcf\u5fd0\u5fd1\u5fd2\u5fd3\u5fd4\u5fd5\u5fd6\u5fd7\u5fd8\u5fd9\u5fda\u5fdb\u5fdc\u5fdd\u5fde\u5fdf\u5fe0\u5fe1\u5fe2\u5fe3\u5fe4\u5fe5\u5fe6\u5fe7\u5fe8\u5fe9\u5fea\u5feb\u5fec\u5fed\u5fee\u5fef\u5ff0\u5ff1\u5ff2\u5ff3\u5ff4\u5ff5\u5ff6\u5ff7\u5ff8\u5ff9\u5ffa\u5ffb\u5ffc\u5ffd\u5ffe\u5fff\u6000\u6001\u6002\u6003\u6004\u6005\u6006\u6007\u6008\u6009\u600a\u600b\u600c\u600d\u600e\u600f\u6010\u6011\u6012\u6013\u6014\u6015\u6016\u6017\u6018\u6019\u601a\u601b\u601c\u601d\u601e\u601f\u6020\u6021\u6022\u6023\u6024\u6025\u6026\u6027\u6028\u6029\u602a\u602b\u602c\u602d\u602e\u602f\u6030\u6031\u6032\u6033\u6034\u6035\u6036\u6037\u6038\u6039\u603a\u603b\u603c\u603d\u603e\u603f\u6040\u6041\u6042\u6043\u6044\u6045\u6046\u6047\u6048\u6049\u604a\u604b\u604c\u604d\u604e\u604f\u6050\u6051\u6052\u6053\u6054\u6055\u6056\u6057\u6058\u6059\u605a\u605b\u605c\u605d\u605e\u605f\u6060\u6061\u6062\u6063\u6064\u6065\u6066\u6067\u6068\u6069\u606a\u606b\u606c\u606d\u606e\u606f\u6070\u6071\u6072\u6073\u6074\u6075\u6076\u6077\u6078\u6079\u607a\u607b\u607c\u607d\u607e\u607f\u6080\u6081\u6082\u6083\u6084\u6085\u6086\u6087\u6088\u6089\u608a\u608b\u608c\u608d\u608e\u608f\u6090\u6091\u6092\u6093\u6094\u6095\u6096\u6097\u6098\u6099\u609a\u609b\u609c\u609d\u609e\u609f\u60a0\u60a1\u60a2\u60a3\u60a4\u60a5\u60a6\u60a7\u60a8\u60a9\u60aa\u60ab\u60ac\u60ad\u60ae\u60af\u60b0\u60b1\u60b2\u60b3\u60b4\u60b5\u60b6\u60b7\u60b8\u60b9\u60ba\u60bb\u60bc\u60bd\u60be\u60bf\u60c0\u60c1\u60c2\u60c3\u60c4\u60c5\u60c6\u60c7\u60c8\u60c9\u60ca\u60cb\u60cc\u60cd\u60ce\u60cf\u60d0\u60d1\u60d2\u60d3\u60d4\u60d5\u60d6\u60d7\u60d8\u60d9\u60da\u60db\u60dc\u60dd\u60de\u60df\u60e0\u60e1\u60e2\u60e3\u60e4\u60e5\u60e6\u60e7\u60e8\u60e9\u60ea\u60eb\u60ec\u60ed\u60ee\u60ef\u60f0\u60f1\u60f2\u60f3\u60f4\u60f5\u60f6\u60f7\u60f8\u60f9\u60fa\u60fb\u60fc\u60fd\u60fe\u60ff\u6100\u6101\u6102\u6103\u6104\u6105\u6106\u6107\u6108\u6109\u610a\u610b\u610c\u610d\u610e\u610f\u6110\u6111\u6112\u6113\u6114\u6115\u6116\u6117\u6118\u6119\u611a\u611b\u611c\u611d\u611e\u611f\u6120\u6121\u6122\u6123\u6124\u6125\u6126\u6127\u6128\u6129\u612a\u612b\u612c\u612d\u612e\u612f\u6130\u6131\u6132\u6133\u6134\u6135\u6136\u6137\u6138\u6139\u613a\u613b\u613c\u613d\u613e\u613f\u6140\u6141\u6142\u6143\u6144\u6145\u6146\u6147\u6148\u6149\u614a\u614b\u614c\u614d\u614e\u614f\u6150\u6151\u6152\u6153\u6154\u6155\u6156\u6157\u6158\u6159\u615a\u615b\u615c\u615d\u615e\u615f\u6160\u6161\u6162\u6163\u6164\u6165\u6166\u6167\u6168\u6169\u616a\u616b\u616c\u616d\u616e\u616f\u6170\u6171\u6172\u6173\u6174\u6175\u6176\u6177\u6178\u6179\u617a\u617b\u617c\u617d\u617e\u617f\u6180\u6181\u6182\u6183\u6184\u6185\u6186\u6187\u6188\u6189\u618a\u618b\u618c\u618d\u618e\u618f\u6190\u6191\u6192\u6193\u6194\u6195\u6196\u6197\u6198\u6199\u619a\u619b\u619c\u619d\u619e\u619f\u61a0\u61a1\u61a2\u61a3\u61a4\u61a5\u61a6\u61a7\u61a8\u61a9\u61aa\u61ab\u61ac\u61ad\u61ae\u61af\u61b0\u61b1\u61b2\u61b3\u61b4\u61b5\u61b6\u61b7\u61b8\u61b9\u61ba\u61bb\u61bc\u61bd\u61be\u61bf\u61c0\u61c1\u61c2\u61c3\u61c4\u61c5\u61c6\u61c7\u61c8\u61c9\u61ca\u61cb\u61cc\u61cd\u61ce\u61cf\u61d0\u61d1\u61d2\u61d3\u61d4\u61d5\u61d6\u61d7\u61d8\u61d9\u61da\u61db\u61dc\u61dd\u61de\u61df\u61e0\u61e1\u61e2\u61e3\u61e4\u61e5\u61e6\u61e7\u61e8\u61e9\u61ea\u61eb\u61ec\u61ed\u61ee\u61ef\u61f0\u61f1\u61f2\u61f3\u61f4\u61f5\u61f6\u61f7\u61f8\u61f9\u61fa\u61fb\u61fc\u61fd\u61fe\u61ff\u6200\u6201\u6202\u6203\u6204\u6205\u6206\u6207\u6208\u6209\u620a\u620b\u620c\u620d\u620e\u620f\u6210\u6211\u6212\u6213\u6214\u6215\u6216\u6217\u6218\u6219\u621a\u621b\u621c\u621d\u621e\u621f\u6220\u6221\u6222\u6223\u6224\u6225\u6226\u6227\u6228\u6229\u622a\u622b\u622c\u622d\u622e\u622f\u6230\u6231\u6232\u6233\u6234\u6235\u6236\u6237\u6238\u6239\u623a\u623b\u623c\u623d\u623e\u623f\u6240\u6241\u6242\u6243\u6244\u6245\u6246\u6247\u6248\u6249\u624a\u624b\u624c\u624d\u624e\u624f\u6250\u6251\u6252\u6253\u6254\u6255\u6256\u6257\u6258\u6259\u625a\u625b\u625c\u625d\u625e\u625f\u6260\u6261\u6262\u6263\u6264\u6265\u6266\u6267\u6268\u6269\u626a\u626b\u626c\u626d\u626e\u626f\u6270\u6271\u6272\u6273\u6274\u6275\u6276\u6277\u6278\u6279\u627a\u627b\u627c\u627d\u627e\u627f\u6280\u6281\u6282\u6283\u6284\u6285\u6286\u6287\u6288\u6289\u628a\u628b\u628c\u628d\u628e\u628f\u6290\u6291\u6292\u6293\u6294\u6295\u6296\u6297\u6298\u6299\u629a\u629b\u629c\u629d\u629e\u629f\u62a0\u62a1\u62a2\u62a3\u62a4\u62a5\u62a6\u62a7\u62a8\u62a9\u62aa\u62ab\u62ac\u62ad\u62ae\u62af\u62b0\u62b1\u62b2\u62b3\u62b4\u62b5\u62b6\u62b7\u62b8\u62b9\u62ba\u62bb\u62bc\u62bd\u62be\u62bf\u62c0\u62c1\u62c2\u62c3\u62c4\u62c5\u62c6\u62c7\u62c8\u62c9\u62ca\u62cb\u62cc\u62cd\u62ce\u62cf\u62d0\u62d1\u62d2\u62d3\u62d4\u62d5\u62d6\u62d7\u62d8\u62d9\u62da\u62db\u62dc\u62dd\u62de\u62df\u62e0\u62e1\u62e2\u62e3\u62e4\u62e5\u62e6\u62e7\u62e8\u62e9\u62ea\u62eb\u62ec\u62ed\u62ee\u62ef\u62f0\u62f1\u62f2\u62f3\u62f4\u62f5\u62f6\u62f7\u62f8\u62f9\u62fa\u62fb\u62fc\u62fd\u62fe\u62ff\u6300\u6301\u6302\u6303\u6304\u6305\u6306\u6307\u6308\u6309\u630a\u630b\u630c\u630d\u630e\u630f\u6310\u6311\u6312\u6313\u6314\u6315\u6316\u6317\u6318\u6319\u631a\u631b\u631c\u631d\u631e\u631f\u6320\u6321\u6322\u6323\u6324\u6325\u6326\u6327\u6328\u6329\u632a\u632b\u632c\u632d\u632e\u632f\u6330\u6331\u6332\u6333\u6334\u6335\u6336\u6337\u6338\u6339\u633a\u633b\u633c\u633d\u633e\u633f\u6340\u6341\u6342\u6343\u6344\u6345\u6346\u6347\u6348\u6349\u634a\u634b\u634c\u634d\u634e\u634f\u6350\u6351\u6352\u6353\u6354\u6355\u6356\u6357\u6358\u6359\u635a\u635b\u635c\u635d\u635e\u635f\u6360\u6361\u6362\u6363\u6364\u6365\u6366\u6367\u6368\u6369\u636a\u636b\u636c\u636d\u636e\u636f\u6370\u6371\u6372\u6373\u6374\u6375\u6376\u6377\u6378\u6379\u637a\u637b\u637c\u637d\u637e\u637f\u6380\u6381\u6382\u6383\u6384\u6385\u6386\u6387\u6388\u6389\u638a\u638b\u638c\u638d\u638e\u638f\u6390\u6391\u6392\u6393\u6394\u6395\u6396\u6397\u6398\u6399\u639a\u639b\u639c\u639d\u639e\u639f\u63a0\u63a1\u63a2\u63a3\u63a4\u63a5\u63a6\u63a7\u63a8\u63a9\u63aa\u63ab\u63ac\u63ad\u63ae\u63af\u63b0\u63b1\u63b2\u63b3\u63b4\u63b5\u63b6\u63b7\u63b8\u63b9\u63ba\u63bb\u63bc\u63bd\u63be\u63bf\u63c0\u63c1\u63c2\u63c3\u63c4\u63c5\u63c6\u63c7\u63c8\u63c9\u63ca\u63cb\u63cc\u63cd\u63ce\u63cf\u63d0\u63d1\u63d2\u63d3\u63d4\u63d5\u63d6\u63d7\u63d8\u63d9\u63da\u63db\u63dc\u63dd\u63de\u63df\u63e0\u63e1\u63e2\u63e3\u63e4\u63e5\u63e6\u63e7\u63e8\u63e9\u63ea\u63eb\u63ec\u63ed\u63ee\u63ef\u63f0\u63f1\u63f2\u63f3\u63f4\u63f5\u63f6\u63f7\u63f8\u63f9\u63fa\u63fb\u63fc\u63fd\u63fe\u63ff\u6400\u6401\u6402\u6403\u6404\u6405\u6406\u6407\u6408\u6409\u640a\u640b\u640c\u640d\u640e\u640f\u6410\u6411\u6412\u6413\u6414\u6415\u6416\u6417\u6418\u6419\u641a\u641b\u641c\u641d\u641e\u641f\u6420\u6421\u6422\u6423\u6424\u6425\u6426\u6427\u6428\u6429\u642a\u642b\u642c\u642d\u642e\u642f\u6430\u6431\u6432\u6433\u6434\u6435\u6436\u6437\u6438\u6439\u643a\u643b\u643c\u643d\u643e\u643f\u6440\u6441\u6442\u6443\u6444\u6445\u6446\u6447\u6448\u6449\u644a\u644b\u644c\u644d\u644e\u644f\u6450\u6451\u6452\u6453\u6454\u6455\u6456\u6457\u6458\u6459\u645a\u645b\u645c\u645d\u645e\u645f\u6460\u6461\u6462\u6463\u6464\u6465\u6466\u6467\u6468\u6469\u646a\u646b\u646c\u646d\u646e\u646f\u6470\u6471\u6472\u6473\u6474\u6475\u6476\u6477\u6478\u6479\u647a\u647b\u647c\u647d\u647e\u647f\u6480\u6481\u6482\u6483\u6484\u6485\u6486\u6487\u6488\u6489\u648a\u648b\u648c\u648d\u648e\u648f\u6490\u6491\u6492\u6493\u6494\u6495\u6496\u6497\u6498\u6499\u649a\u649b\u649c\u649d\u649e\u649f\u64a0\u64a1\u64a2\u64a3\u64a4\u64a5\u64a6\u64a7\u64a8\u64a9\u64aa\u64ab\u64ac\u64ad\u64ae\u64af\u64b0\u64b1\u64b2\u64b3\u64b4\u64b5\u64b6\u64b7\u64b8\u64b9\u64ba\u64bb\u64bc\u64bd\u64be\u64bf\u64c0\u64c1\u64c2\u64c3\u64c4\u64c5\u64c6\u64c7\u64c8\u64c9\u64ca\u64cb\u64cc\u64cd\u64ce\u64cf\u64d0\u64d1\u64d2\u64d3\u64d4\u64d5\u64d6\u64d7\u64d8\u64d9\u64da\u64db\u64dc\u64dd\u64de\u64df\u64e0\u64e1\u64e2\u64e3\u64e4\u64e5\u64e6\u64e7\u64e8\u64e9\u64ea\u64eb\u64ec\u64ed\u64ee\u64ef\u64f0\u64f1\u64f2\u64f3\u64f4\u64f5\u64f6\u64f7\u64f8\u64f9\u64fa\u64fb\u64fc\u64fd\u64fe\u64ff\u6500\u6501\u6502\u6503\u6504\u6505\u6506\u6507\u6508\u6509\u650a\u650b\u650c\u650d\u650e\u650f\u6510\u6511\u6512\u6513\u6514\u6515\u6516\u6517\u6518\u6519\u651a\u651b\u651c\u651d\u651e\u651f\u6520\u6521\u6522\u6523\u6524\u6525\u6526\u6527\u6528\u6529\u652a\u652b\u652c\u652d\u652e\u652f\u6530\u6531\u6532\u6533\u6534\u6535\u6536\u6537\u6538\u6539\u653a\u653b\u653c\u653d\u653e\u653f\u6540\u6541\u6542\u6543\u6544\u6545\u6546\u6547\u6548\u6549\u654a\u654b\u654c\u654d\u654e\u654f\u6550\u6551\u6552\u6553\u6554\u6555\u6556\u6557\u6558\u6559\u655a\u655b\u655c\u655d\u655e\u655f\u6560\u6561\u6562\u6563\u6564\u6565\u6566\u6567\u6568\u6569\u656a\u656b\u656c\u656d\u656e\u656f\u6570\u6571\u6572\u6573\u6574\u6575\u6576\u6577\u6578\u6579\u657a\u657b\u657c\u657d\u657e\u657f\u6580\u6581\u6582\u6583\u6584\u6585\u6586\u6587\u6588\u6589\u658a\u658b\u658c\u658d\u658e\u658f\u6590\u6591\u6592\u6593\u6594\u6595\u6596\u6597\u6598\u6599\u659a\u659b\u659c\u659d\u659e\u659f\u65a0\u65a1\u65a2\u65a3\u65a4\u65a5\u65a6\u65a7\u65a8\u65a9\u65aa\u65ab\u65ac\u65ad\u65ae\u65af\u65b0\u65b1\u65b2\u65b3\u65b4\u65b5\u65b6\u65b7\u65b8\u65b9\u65ba\u65bb\u65bc\u65bd\u65be\u65bf\u65c0\u65c1\u65c2\u65c3\u65c4\u65c5\u65c6\u65c7\u65c8\u65c9\u65ca\u65cb\u65cc\u65cd\u65ce\u65cf\u65d0\u65d1\u65d2\u65d3\u65d4\u65d5\u65d6\u65d7\u65d8\u65d9\u65da\u65db\u65dc\u65dd\u65de\u65df\u65e0\u65e1\u65e2\u65e3\u65e4\u65e5\u65e6\u65e7\u65e8\u65e9\u65ea\u65eb\u65ec\u65ed\u65ee\u65ef\u65f0\u65f1\u65f2\u65f3\u65f4\u65f5\u65f6\u65f7\u65f8\u65f9\u65fa\u65fb\u65fc\u65fd\u65fe\u65ff\u6600\u6601\u6602\u6603\u6604\u6605\u6606\u6607\u6608\u6609\u660a\u660b\u660c\u660d\u660e\u660f\u6610\u6611\u6612\u6613\u6614\u6615\u6616\u6617\u6618\u6619\u661a\u661b\u661c\u661d\u661e\u661f\u6620\u6621\u6622\u6623\u6624\u6625\u6626\u6627\u6628\u6629\u662a\u662b\u662c\u662d\u662e\u662f\u6630\u6631\u6632\u6633\u6634\u6635\u6636\u6637\u6638\u6639\u663a\u663b\u663c\u663d\u663e\u663f\u6640\u6641\u6642\u6643\u6644\u6645\u6646\u6647\u6648\u6649\u664a\u664b\u664c\u664d\u664e\u664f\u6650\u6651\u6652\u6653\u6654\u6655\u6656\u6657\u6658\u6659\u665a\u665b\u665c\u665d\u665e\u665f\u6660\u6661\u6662\u6663\u6664\u6665\u6666\u6667\u6668\u6669\u666a\u666b\u666c\u666d\u666e\u666f\u6670\u6671\u6672\u6673\u6674\u6675\u6676\u6677\u6678\u6679\u667a\u667b\u667c\u667d\u667e\u667f\u6680\u6681\u6682\u6683\u6684\u6685\u6686\u6687\u6688\u6689\u668a\u668b\u668c\u668d\u668e\u668f\u6690\u6691\u6692\u6693\u6694\u6695\u6696\u6697\u6698\u6699\u669a\u669b\u669c\u669d\u669e\u669f\u66a0\u66a1\u66a2\u66a3\u66a4\u66a5\u66a6\u66a7\u66a8\u66a9\u66aa\u66ab\u66ac\u66ad\u66ae\u66af\u66b0\u66b1\u66b2\u66b3\u66b4\u66b5\u66b6\u66b7\u66b8\u66b9\u66ba\u66bb\u66bc\u66bd\u66be\u66bf\u66c0\u66c1\u66c2\u66c3\u66c4\u66c5\u66c6\u66c7\u66c8\u66c9\u66ca\u66cb\u66cc\u66cd\u66ce\u66cf\u66d0\u66d1\u66d2\u66d3\u66d4\u66d5\u66d6\u66d7\u66d8\u66d9\u66da\u66db\u66dc\u66dd\u66de\u66df\u66e0\u66e1\u66e2\u66e3\u66e4\u66e5\u66e6\u66e7\u66e8\u66e9\u66ea\u66eb\u66ec\u66ed\u66ee\u66ef\u66f0\u66f1\u66f2\u66f3\u66f4\u66f5\u66f6\u66f7\u66f8\u66f9\u66fa\u66fb\u66fc\u66fd\u66fe\u66ff\u6700\u6701\u6702\u6703\u6704\u6705\u6706\u6707\u6708\u6709\u670a\u670b\u670c\u670d\u670e\u670f\u6710\u6711\u6712\u6713\u6714\u6715\u6716\u6717\u6718\u6719\u671a\u671b\u671c\u671d\u671e\u671f\u6720\u6721\u6722\u6723\u6724\u6725\u6726\u6727\u6728\u6729\u672a\u672b\u672c\u672d\u672e\u672f\u6730\u6731\u6732\u6733\u6734\u6735\u6736\u6737\u6738\u6739\u673a\u673b\u673c\u673d\u673e\u673f\u6740\u6741\u6742\u6743\u6744\u6745\u6746\u6747\u6748\u6749\u674a\u674b\u674c\u674d\u674e\u674f\u6750\u6751\u6752\u6753\u6754\u6755\u6756\u6757\u6758\u6759\u675a\u675b\u675c\u675d\u675e\u675f\u6760\u6761\u6762\u6763\u6764\u6765\u6766\u6767\u6768\u6769\u676a\u676b\u676c\u676d\u676e\u676f\u6770\u6771\u6772\u6773\u6774\u6775\u6776\u6777\u6778\u6779\u677a\u677b\u677c\u677d\u677e\u677f\u6780\u6781\u6782\u6783\u6784\u6785\u6786\u6787\u6788\u6789\u678a\u678b\u678c\u678d\u678e\u678f\u6790\u6791\u6792\u6793\u6794\u6795\u6796\u6797\u6798\u6799\u679a\u679b\u679c\u679d\u679e\u679f\u67a0\u67a1\u67a2\u67a3\u67a4\u67a5\u67a6\u67a7\u67a8\u67a9\u67aa\u67ab\u67ac\u67ad\u67ae\u67af\u67b0\u67b1\u67b2\u67b3\u67b4\u67b5\u67b6\u67b7\u67b8\u67b9\u67ba\u67bb\u67bc\u67bd\u67be\u67bf\u67c0\u67c1\u67c2\u67c3\u67c4\u67c5\u67c6\u67c7\u67c8\u67c9\u67ca\u67cb\u67cc\u67cd\u67ce\u67cf\u67d0\u67d1\u67d2\u67d3\u67d4\u67d5\u67d6\u67d7\u67d8\u67d9\u67da\u67db\u67dc\u67dd\u67de\u67df\u67e0\u67e1\u67e2\u67e3\u67e4\u67e5\u67e6\u67e7\u67e8\u67e9\u67ea\u67eb\u67ec\u67ed\u67ee\u67ef\u67f0\u67f1\u67f2\u67f3\u67f4\u67f5\u67f6\u67f7\u67f8\u67f9\u67fa\u67fb\u67fc\u67fd\u67fe\u67ff\u6800\u6801\u6802\u6803\u6804\u6805\u6806\u6807\u6808\u6809\u680a\u680b\u680c\u680d\u680e\u680f\u6810\u6811\u6812\u6813\u6814\u6815\u6816\u6817\u6818\u6819\u681a\u681b\u681c\u681d\u681e\u681f\u6820\u6821\u6822\u6823\u6824\u6825\u6826\u6827\u6828\u6829\u682a\u682b\u682c\u682d\u682e\u682f\u6830\u6831\u6832\u6833\u6834\u6835\u6836\u6837\u6838\u6839\u683a\u683b\u683c\u683d\u683e\u683f\u6840\u6841\u6842\u6843\u6844\u6845\u6846\u6847\u6848\u6849\u684a\u684b\u684c\u684d\u684e\u684f\u6850\u6851\u6852\u6853\u6854\u6855\u6856\u6857\u6858\u6859\u685a\u685b\u685c\u685d\u685e\u685f\u6860\u6861\u6862\u6863\u6864\u6865\u6866\u6867\u6868\u6869\u686a\u686b\u686c\u686d\u686e\u686f\u6870\u6871\u6872\u6873\u6874\u6875\u6876\u6877\u6878\u6879\u687a\u687b\u687c\u687d\u687e\u687f\u6880\u6881\u6882\u6883\u6884\u6885\u6886\u6887\u6888\u6889\u688a\u688b\u688c\u688d\u688e\u688f\u6890\u6891\u6892\u6893\u6894\u6895\u6896\u6897\u6898\u6899\u689a\u689b\u689c\u689d\u689e\u689f\u68a0\u68a1\u68a2\u68a3\u68a4\u68a5\u68a6\u68a7\u68a8\u68a9\u68aa\u68ab\u68ac\u68ad\u68ae\u68af\u68b0\u68b1\u68b2\u68b3\u68b4\u68b5\u68b6\u68b7\u68b8\u68b9\u68ba\u68bb\u68bc\u68bd\u68be\u68bf\u68c0\u68c1\u68c2\u68c3\u68c4\u68c5\u68c6\u68c7\u68c8\u68c9\u68ca\u68cb\u68cc\u68cd\u68ce\u68cf\u68d0\u68d1\u68d2\u68d3\u68d4\u68d5\u68d6\u68d7\u68d8\u68d9\u68da\u68db\u68dc\u68dd\u68de\u68df\u68e0\u68e1\u68e2\u68e3\u68e4\u68e5\u68e6\u68e7\u68e8\u68e9\u68ea\u68eb\u68ec\u68ed\u68ee\u68ef\u68f0\u68f1\u68f2\u68f3\u68f4\u68f5\u68f6\u68f7\u68f8\u68f9\u68fa\u68fb\u68fc\u68fd\u68fe\u68ff\u6900\u6901\u6902\u6903\u6904\u6905\u6906\u6907\u6908\u6909\u690a\u690b\u690c\u690d\u690e\u690f\u6910\u6911\u6912\u6913\u6914\u6915\u6916\u6917\u6918\u6919\u691a\u691b\u691c\u691d\u691e\u691f\u6920\u6921\u6922\u6923\u6924\u6925\u6926\u6927\u6928\u6929\u692a\u692b\u692c\u692d\u692e\u692f\u6930\u6931\u6932\u6933\u6934\u6935\u6936\u6937\u6938\u6939\u693a\u693b\u693c\u693d\u693e\u693f\u6940\u6941\u6942\u6943\u6944\u6945\u6946\u6947\u6948\u6949\u694a\u694b\u694c\u694d\u694e\u694f\u6950\u6951\u6952\u6953\u6954\u6955\u6956\u6957\u6958\u6959\u695a\u695b\u695c\u695d\u695e\u695f\u6960\u6961\u6962\u6963\u6964\u6965\u6966\u6967\u6968\u6969\u696a\u696b\u696c\u696d\u696e\u696f\u6970\u6971\u6972\u6973\u6974\u6975\u6976\u6977\u6978\u6979\u697a\u697b\u697c\u697d\u697e\u697f\u6980\u6981\u6982\u6983\u6984\u6985\u6986\u6987\u6988\u6989\u698a\u698b\u698c\u698d\u698e\u698f\u6990\u6991\u6992\u6993\u6994\u6995\u6996\u6997\u6998\u6999\u699a\u699b\u699c\u699d\u699e\u699f\u69a0\u69a1\u69a2\u69a3\u69a4\u69a5\u69a6\u69a7\u69a8\u69a9\u69aa\u69ab\u69ac\u69ad\u69ae\u69af\u69b0\u69b1\u69b2\u69b3\u69b4\u69b5\u69b6\u69b7\u69b8\u69b9\u69ba\u69bb\u69bc\u69bd\u69be\u69bf\u69c0\u69c1\u69c2\u69c3\u69c4\u69c5\u69c6\u69c7\u69c8\u69c9\u69ca\u69cb\u69cc\u69cd\u69ce\u69cf\u69d0\u69d1\u69d2\u69d3\u69d4\u69d5\u69d6\u69d7\u69d8\u69d9\u69da\u69db\u69dc\u69dd\u69de\u69df\u69e0\u69e1\u69e2\u69e3\u69e4\u69e5\u69e6\u69e7\u69e8\u69e9\u69ea\u69eb\u69ec\u69ed\u69ee\u69ef\u69f0\u69f1\u69f2\u69f3\u69f4\u69f5\u69f6\u69f7\u69f8\u69f9\u69fa\u69fb\u69fc\u69fd\u69fe\u69ff\u6a00\u6a01\u6a02\u6a03\u6a04\u6a05\u6a06\u6a07\u6a08\u6a09\u6a0a\u6a0b\u6a0c\u6a0d\u6a0e\u6a0f\u6a10\u6a11\u6a12\u6a13\u6a14\u6a15\u6a16\u6a17\u6a18\u6a19\u6a1a\u6a1b\u6a1c\u6a1d\u6a1e\u6a1f\u6a20\u6a21\u6a22\u6a23\u6a24\u6a25\u6a26\u6a27\u6a28\u6a29\u6a2a\u6a2b\u6a2c\u6a2d\u6a2e\u6a2f\u6a30\u6a31\u6a32\u6a33\u6a34\u6a35\u6a36\u6a37\u6a38\u6a39\u6a3a\u6a3b\u6a3c\u6a3d\u6a3e\u6a3f\u6a40\u6a41\u6a42\u6a43\u6a44\u6a45\u6a46\u6a47\u6a48\u6a49\u6a4a\u6a4b\u6a4c\u6a4d\u6a4e\u6a4f\u6a50\u6a51\u6a52\u6a53\u6a54\u6a55\u6a56\u6a57\u6a58\u6a59\u6a5a\u6a5b\u6a5c\u6a5d\u6a5e\u6a5f\u6a60\u6a61\u6a62\u6a63\u6a64\u6a65\u6a66\u6a67\u6a68\u6a69\u6a6a\u6a6b\u6a6c\u6a6d\u6a6e\u6a6f\u6a70\u6a71\u6a72\u6a73\u6a74\u6a75\u6a76\u6a77\u6a78\u6a79\u6a7a\u6a7b\u6a7c\u6a7d\u6a7e\u6a7f\u6a80\u6a81\u6a82\u6a83\u6a84\u6a85\u6a86\u6a87\u6a88\u6a89\u6a8a\u6a8b\u6a8c\u6a8d\u6a8e\u6a8f\u6a90\u6a91\u6a92\u6a93\u6a94\u6a95\u6a96\u6a97\u6a98\u6a99\u6a9a\u6a9b\u6a9c\u6a9d\u6a9e\u6a9f\u6aa0\u6aa1\u6aa2\u6aa3\u6aa4\u6aa5\u6aa6\u6aa7\u6aa8\u6aa9\u6aaa\u6aab\u6aac\u6aad\u6aae\u6aaf\u6ab0\u6ab1\u6ab2\u6ab3\u6ab4\u6ab5\u6ab6\u6ab7\u6ab8\u6ab9\u6aba\u6abb\u6abc\u6abd\u6abe\u6abf\u6ac0\u6ac1\u6ac2\u6ac3\u6ac4\u6ac5\u6ac6\u6ac7\u6ac8\u6ac9\u6aca\u6acb\u6acc\u6acd\u6ace\u6acf\u6ad0\u6ad1\u6ad2\u6ad3\u6ad4\u6ad5\u6ad6\u6ad7\u6ad8\u6ad9\u6ada\u6adb\u6adc\u6add\u6ade\u6adf\u6ae0\u6ae1\u6ae2\u6ae3\u6ae4\u6ae5\u6ae6\u6ae7\u6ae8\u6ae9\u6aea\u6aeb\u6aec\u6aed\u6aee\u6aef\u6af0\u6af1\u6af2\u6af3\u6af4\u6af5\u6af6\u6af7\u6af8\u6af9\u6afa\u6afb\u6afc\u6afd\u6afe\u6aff\u6b00\u6b01\u6b02\u6b03\u6b04\u6b05\u6b06\u6b07\u6b08\u6b09\u6b0a\u6b0b\u6b0c\u6b0d\u6b0e\u6b0f\u6b10\u6b11\u6b12\u6b13\u6b14\u6b15\u6b16\u6b17\u6b18\u6b19\u6b1a\u6b1b\u6b1c\u6b1d\u6b1e\u6b1f\u6b20\u6b21\u6b22\u6b23\u6b24\u6b25\u6b26\u6b27\u6b28\u6b29\u6b2a\u6b2b\u6b2c\u6b2d\u6b2e\u6b2f\u6b30\u6b31\u6b32\u6b33\u6b34\u6b35\u6b36\u6b37\u6b38\u6b39\u6b3a\u6b3b\u6b3c\u6b3d\u6b3e\u6b3f\u6b40\u6b41\u6b42\u6b43\u6b44\u6b45\u6b46\u6b47\u6b48\u6b49\u6b4a\u6b4b\u6b4c\u6b4d\u6b4e\u6b4f\u6b50\u6b51\u6b52\u6b53\u6b54\u6b55\u6b56\u6b57\u6b58\u6b59\u6b5a\u6b5b\u6b5c\u6b5d\u6b5e\u6b5f\u6b60\u6b61\u6b62\u6b63\u6b64\u6b65\u6b66\u6b67\u6b68\u6b69\u6b6a\u6b6b\u6b6c\u6b6d\u6b6e\u6b6f\u6b70\u6b71\u6b72\u6b73\u6b74\u6b75\u6b76\u6b77\u6b78\u6b79\u6b7a\u6b7b\u6b7c\u6b7d\u6b7e\u6b7f\u6b80\u6b81\u6b82\u6b83\u6b84\u6b85\u6b86\u6b87\u6b88\u6b89\u6b8a\u6b8b\u6b8c\u6b8d\u6b8e\u6b8f\u6b90\u6b91\u6b92\u6b93\u6b94\u6b95\u6b96\u6b97\u6b98\u6b99\u6b9a\u6b9b\u6b9c\u6b9d\u6b9e\u6b9f\u6ba0\u6ba1\u6ba2\u6ba3\u6ba4\u6ba5\u6ba6\u6ba7\u6ba8\u6ba9\u6baa\u6bab\u6bac\u6bad\u6bae\u6baf\u6bb0\u6bb1\u6bb2\u6bb3\u6bb4\u6bb5\u6bb6\u6bb7\u6bb8\u6bb9\u6bba\u6bbb\u6bbc\u6bbd\u6bbe\u6bbf\u6bc0\u6bc1\u6bc2\u6bc3\u6bc4\u6bc5\u6bc6\u6bc7\u6bc8\u6bc9\u6bca\u6bcb\u6bcc\u6bcd\u6bce\u6bcf\u6bd0\u6bd1\u6bd2\u6bd3\u6bd4\u6bd5\u6bd6\u6bd7\u6bd8\u6bd9\u6bda\u6bdb\u6bdc\u6bdd\u6bde\u6bdf\u6be0\u6be1\u6be2\u6be3\u6be4\u6be5\u6be6\u6be7\u6be8\u6be9\u6bea\u6beb\u6bec\u6bed\u6bee\u6bef\u6bf0\u6bf1\u6bf2\u6bf3\u6bf4\u6bf5\u6bf6\u6bf7\u6bf8\u6bf9\u6bfa\u6bfb\u6bfc\u6bfd\u6bfe\u6bff\u6c00\u6c01\u6c02\u6c03\u6c04\u6c05\u6c06\u6c07\u6c08\u6c09\u6c0a\u6c0b\u6c0c\u6c0d\u6c0e\u6c0f\u6c10\u6c11\u6c12\u6c13\u6c14\u6c15\u6c16\u6c17\u6c18\u6c19\u6c1a\u6c1b\u6c1c\u6c1d\u6c1e\u6c1f\u6c20\u6c21\u6c22\u6c23\u6c24\u6c25\u6c26\u6c27\u6c28\u6c29\u6c2a\u6c2b\u6c2c\u6c2d\u6c2e\u6c2f\u6c30\u6c31\u6c32\u6c33\u6c34\u6c35\u6c36\u6c37\u6c38\u6c39\u6c3a\u6c3b\u6c3c\u6c3d\u6c3e\u6c3f\u6c40\u6c41\u6c42\u6c43\u6c44\u6c45\u6c46\u6c47\u6c48\u6c49\u6c4a\u6c4b\u6c4c\u6c4d\u6c4e\u6c4f\u6c50\u6c51\u6c52\u6c53\u6c54\u6c55\u6c56\u6c57\u6c58\u6c59\u6c5a\u6c5b\u6c5c\u6c5d\u6c5e\u6c5f\u6c60\u6c61\u6c62\u6c63\u6c64\u6c65\u6c66\u6c67\u6c68\u6c69\u6c6a\u6c6b\u6c6c\u6c6d\u6c6e\u6c6f\u6c70\u6c71\u6c72\u6c73\u6c74\u6c75\u6c76\u6c77\u6c78\u6c79\u6c7a\u6c7b\u6c7c\u6c7d\u6c7e\u6c7f\u6c80\u6c81\u6c82\u6c83\u6c84\u6c85\u6c86\u6c87\u6c88\u6c89\u6c8a\u6c8b\u6c8c\u6c8d\u6c8e\u6c8f\u6c90\u6c91\u6c92\u6c93\u6c94\u6c95\u6c96\u6c97\u6c98\u6c99\u6c9a\u6c9b\u6c9c\u6c9d\u6c9e\u6c9f\u6ca0\u6ca1\u6ca2\u6ca3\u6ca4\u6ca5\u6ca6\u6ca7\u6ca8\u6ca9\u6caa\u6cab\u6cac\u6cad\u6cae\u6caf\u6cb0\u6cb1\u6cb2\u6cb3\u6cb4\u6cb5\u6cb6\u6cb7\u6cb8\u6cb9\u6cba\u6cbb\u6cbc\u6cbd\u6cbe\u6cbf\u6cc0\u6cc1\u6cc2\u6cc3\u6cc4\u6cc5\u6cc6\u6cc7\u6cc8\u6cc9\u6cca\u6ccb\u6ccc\u6ccd\u6cce\u6ccf\u6cd0\u6cd1\u6cd2\u6cd3\u6cd4\u6cd5\u6cd6\u6cd7\u6cd8\u6cd9\u6cda\u6cdb\u6cdc\u6cdd\u6cde\u6cdf\u6ce0\u6ce1\u6ce2\u6ce3\u6ce4\u6ce5\u6ce6\u6ce7\u6ce8\u6ce9\u6cea\u6ceb\u6cec\u6ced\u6cee\u6cef\u6cf0\u6cf1\u6cf2\u6cf3\u6cf4\u6cf5\u6cf6\u6cf7\u6cf8\u6cf9\u6cfa\u6cfb\u6cfc\u6cfd\u6cfe\u6cff\u6d00\u6d01\u6d02\u6d03\u6d04\u6d05\u6d06\u6d07\u6d08\u6d09\u6d0a\u6d0b\u6d0c\u6d0d\u6d0e\u6d0f\u6d10\u6d11\u6d12\u6d13\u6d14\u6d15\u6d16\u6d17\u6d18\u6d19\u6d1a\u6d1b\u6d1c\u6d1d\u6d1e\u6d1f\u6d20\u6d21\u6d22\u6d23\u6d24\u6d25\u6d26\u6d27\u6d28\u6d29\u6d2a\u6d2b\u6d2c\u6d2d\u6d2e\u6d2f\u6d30\u6d31\u6d32\u6d33\u6d34\u6d35\u6d36\u6d37\u6d38\u6d39\u6d3a\u6d3b\u6d3c\u6d3d\u6d3e\u6d3f\u6d40\u6d41\u6d42\u6d43\u6d44\u6d45\u6d46\u6d47\u6d48\u6d49\u6d4a\u6d4b\u6d4c\u6d4d\u6d4e\u6d4f\u6d50\u6d51\u6d52\u6d53\u6d54\u6d55\u6d56\u6d57\u6d58\u6d59\u6d5a\u6d5b\u6d5c\u6d5d\u6d5e\u6d5f\u6d60\u6d61\u6d62\u6d63\u6d64\u6d65\u6d66\u6d67\u6d68\u6d69\u6d6a\u6d6b\u6d6c\u6d6d\u6d6e\u6d6f\u6d70\u6d71\u6d72\u6d73\u6d74\u6d75\u6d76\u6d77\u6d78\u6d79\u6d7a\u6d7b\u6d7c\u6d7d\u6d7e\u6d7f\u6d80\u6d81\u6d82\u6d83\u6d84\u6d85\u6d86\u6d87\u6d88\u6d89\u6d8a\u6d8b\u6d8c\u6d8d\u6d8e\u6d8f\u6d90\u6d91\u6d92\u6d93\u6d94\u6d95\u6d96\u6d97\u6d98\u6d99\u6d9a\u6d9b\u6d9c\u6d9d\u6d9e\u6d9f\u6da0\u6da1\u6da2\u6da3\u6da4\u6da5\u6da6\u6da7\u6da8\u6da9\u6daa\u6dab\u6dac\u6dad\u6dae\u6daf\u6db0\u6db1\u6db2\u6db3\u6db4\u6db5\u6db6\u6db7\u6db8\u6db9\u6dba\u6dbb\u6dbc\u6dbd\u6dbe\u6dbf\u6dc0\u6dc1\u6dc2\u6dc3\u6dc4\u6dc5\u6dc6\u6dc7\u6dc8\u6dc9\u6dca\u6dcb\u6dcc\u6dcd\u6dce\u6dcf\u6dd0\u6dd1\u6dd2\u6dd3\u6dd4\u6dd5\u6dd6\u6dd7\u6dd8\u6dd9\u6dda\u6ddb\u6ddc\u6ddd\u6dde\u6ddf\u6de0\u6de1\u6de2\u6de3\u6de4\u6de5\u6de6\u6de7\u6de8\u6de9\u6dea\u6deb\u6dec\u6ded\u6dee\u6def\u6df0\u6df1\u6df2\u6df3\u6df4\u6df5\u6df6\u6df7\u6df8\u6df9\u6dfa\u6dfb\u6dfc\u6dfd\u6dfe\u6dff\u6e00\u6e01\u6e02\u6e03\u6e04\u6e05\u6e06\u6e07\u6e08\u6e09\u6e0a\u6e0b\u6e0c\u6e0d\u6e0e\u6e0f\u6e10\u6e11\u6e12\u6e13\u6e14\u6e15\u6e16\u6e17\u6e18\u6e19\u6e1a\u6e1b\u6e1c\u6e1d\u6e1e\u6e1f\u6e20\u6e21\u6e22\u6e23\u6e24\u6e25\u6e26\u6e27\u6e28\u6e29\u6e2a\u6e2b\u6e2c\u6e2d\u6e2e\u6e2f\u6e30\u6e31\u6e32\u6e33\u6e34\u6e35\u6e36\u6e37\u6e38\u6e39\u6e3a\u6e3b\u6e3c\u6e3d\u6e3e\u6e3f\u6e40\u6e41\u6e42\u6e43\u6e44\u6e45\u6e46\u6e47\u6e48\u6e49\u6e4a\u6e4b\u6e4c\u6e4d\u6e4e\u6e4f\u6e50\u6e51\u6e52\u6e53\u6e54\u6e55\u6e56\u6e57\u6e58\u6e59\u6e5a\u6e5b\u6e5c\u6e5d\u6e5e\u6e5f\u6e60\u6e61\u6e62\u6e63\u6e64\u6e65\u6e66\u6e67\u6e68\u6e69\u6e6a\u6e6b\u6e6c\u6e6d\u6e6e\u6e6f\u6e70\u6e71\u6e72\u6e73\u6e74\u6e75\u6e76\u6e77\u6e78\u6e79\u6e7a\u6e7b\u6e7c\u6e7d\u6e7e\u6e7f\u6e80\u6e81\u6e82\u6e83\u6e84\u6e85\u6e86\u6e87\u6e88\u6e89\u6e8a\u6e8b\u6e8c\u6e8d\u6e8e\u6e8f\u6e90\u6e91\u6e92\u6e93\u6e94\u6e95\u6e96\u6e97\u6e98\u6e99\u6e9a\u6e9b\u6e9c\u6e9d\u6e9e\u6e9f\u6ea0\u6ea1\u6ea2\u6ea3\u6ea4\u6ea5\u6ea6\u6ea7\u6ea8\u6ea9\u6eaa\u6eab\u6eac\u6ead\u6eae\u6eaf\u6eb0\u6eb1\u6eb2\u6eb3\u6eb4\u6eb5\u6eb6\u6eb7\u6eb8\u6eb9\u6eba\u6ebb\u6ebc\u6ebd\u6ebe\u6ebf\u6ec0\u6ec1\u6ec2\u6ec3\u6ec4\u6ec5\u6ec6\u6ec7\u6ec8\u6ec9\u6eca\u6ecb\u6ecc\u6ecd\u6ece\u6ecf\u6ed0\u6ed1\u6ed2\u6ed3\u6ed4\u6ed5\u6ed6\u6ed7\u6ed8\u6ed9\u6eda\u6edb\u6edc\u6edd\u6ede\u6edf\u6ee0\u6ee1\u6ee2\u6ee3\u6ee4\u6ee5\u6ee6\u6ee7\u6ee8\u6ee9\u6eea\u6eeb\u6eec\u6eed\u6eee\u6eef\u6ef0\u6ef1\u6ef2\u6ef3\u6ef4\u6ef5\u6ef6\u6ef7\u6ef8\u6ef9\u6efa\u6efb\u6efc\u6efd\u6efe\u6eff\u6f00\u6f01\u6f02\u6f03\u6f04\u6f05\u6f06\u6f07\u6f08\u6f09\u6f0a\u6f0b\u6f0c\u6f0d\u6f0e\u6f0f\u6f10\u6f11\u6f12\u6f13\u6f14\u6f15\u6f16\u6f17\u6f18\u6f19\u6f1a\u6f1b\u6f1c\u6f1d\u6f1e\u6f1f\u6f20\u6f21\u6f22\u6f23\u6f24\u6f25\u6f26\u6f27\u6f28\u6f29\u6f2a\u6f2b\u6f2c\u6f2d\u6f2e\u6f2f\u6f30\u6f31\u6f32\u6f33\u6f34\u6f35\u6f36\u6f37\u6f38\u6f39\u6f3a\u6f3b\u6f3c\u6f3d\u6f3e\u6f3f\u6f40\u6f41\u6f42\u6f43\u6f44\u6f45\u6f46\u6f47\u6f48\u6f49\u6f4a\u6f4b\u6f4c\u6f4d\u6f4e\u6f4f\u6f50\u6f51\u6f52\u6f53\u6f54\u6f55\u6f56\u6f57\u6f58\u6f59\u6f5a\u6f5b\u6f5c\u6f5d\u6f5e\u6f5f\u6f60\u6f61\u6f62\u6f63\u6f64\u6f65\u6f66\u6f67\u6f68\u6f69\u6f6a\u6f6b\u6f6c\u6f6d\u6f6e\u6f6f\u6f70\u6f71\u6f72\u6f73\u6f74\u6f75\u6f76\u6f77\u6f78\u6f79\u6f7a\u6f7b\u6f7c\u6f7d\u6f7e\u6f7f\u6f80\u6f81\u6f82\u6f83\u6f84\u6f85\u6f86\u6f87\u6f88\u6f89\u6f8a\u6f8b\u6f8c\u6f8d\u6f8e\u6f8f\u6f90\u6f91\u6f92\u6f93\u6f94\u6f95\u6f96\u6f97\u6f98\u6f99\u6f9a\u6f9b\u6f9c\u6f9d\u6f9e\u6f9f\u6fa0\u6fa1\u6fa2\u6fa3\u6fa4\u6fa5\u6fa6\u6fa7\u6fa8\u6fa9\u6faa\u6fab\u6fac\u6fad\u6fae\u6faf\u6fb0\u6fb1\u6fb2\u6fb3\u6fb4\u6fb5\u6fb6\u6fb7\u6fb8\u6fb9\u6fba\u6fbb\u6fbc\u6fbd\u6fbe\u6fbf\u6fc0\u6fc1\u6fc2\u6fc3\u6fc4\u6fc5\u6fc6\u6fc7\u6fc8\u6fc9\u6fca\u6fcb\u6fcc\u6fcd\u6fce\u6fcf\u6fd0\u6fd1\u6fd2\u6fd3\u6fd4\u6fd5\u6fd6\u6fd7\u6fd8\u6fd9\u6fda\u6fdb\u6fdc\u6fdd\u6fde\u6fdf\u6fe0\u6fe1\u6fe2\u6fe3\u6fe4\u6fe5\u6fe6\u6fe7\u6fe8\u6fe9\u6fea\u6feb\u6fec\u6fed\u6fee\u6fef\u6ff0\u6ff1\u6ff2\u6ff3\u6ff4\u6ff5\u6ff6\u6ff7\u6ff8\u6ff9\u6ffa\u6ffb\u6ffc\u6ffd\u6ffe\u6fff\u7000\u7001\u7002\u7003\u7004\u7005\u7006\u7007\u7008\u7009\u700a\u700b\u700c\u700d\u700e\u700f\u7010\u7011\u7012\u7013\u7014\u7015\u7016\u7017\u7018\u7019\u701a\u701b\u701c\u701d\u701e\u701f\u7020\u7021\u7022\u7023\u7024\u7025\u7026\u7027\u7028\u7029\u702a\u702b\u702c\u702d\u702e\u702f\u7030\u7031\u7032\u7033\u7034\u7035\u7036\u7037\u7038\u7039\u703a\u703b\u703c\u703d\u703e\u703f\u7040\u7041\u7042\u7043\u7044\u7045\u7046\u7047\u7048\u7049\u704a\u704b\u704c\u704d\u704e\u704f\u7050\u7051\u7052\u7053\u7054\u7055\u7056\u7057\u7058\u7059\u705a\u705b\u705c\u705d\u705e\u705f\u7060\u7061\u7062\u7063\u7064\u7065\u7066\u7067\u7068\u7069\u706a\u706b\u706c\u706d\u706e\u706f\u7070\u7071\u7072\u7073\u7074\u7075\u7076\u7077\u7078\u7079\u707a\u707b\u707c\u707d\u707e\u707f\u7080\u7081\u7082\u7083\u7084\u7085\u7086\u7087\u7088\u7089\u708a\u708b\u708c\u708d\u708e\u708f\u7090\u7091\u7092\u7093\u7094\u7095\u7096\u7097\u7098\u7099\u709a\u709b\u709c\u709d\u709e\u709f\u70a0\u70a1\u70a2\u70a3\u70a4\u70a5\u70a6\u70a7\u70a8\u70a9\u70aa\u70ab\u70ac\u70ad\u70ae\u70af\u70b0\u70b1\u70b2\u70b3\u70b4\u70b5\u70b6\u70b7\u70b8\u70b9\u70ba\u70bb\u70bc\u70bd\u70be\u70bf\u70c0\u70c1\u70c2\u70c3\u70c4\u70c5\u70c6\u70c7\u70c8\u70c9\u70ca\u70cb\u70cc\u70cd\u70ce\u70cf\u70d0\u70d1\u70d2\u70d3\u70d4\u70d5\u70d6\u70d7\u70d8\u70d9\u70da\u70db\u70dc\u70dd\u70de\u70df\u70e0\u70e1\u70e2\u70e3\u70e4\u70e5\u70e6\u70e7\u70e8\u70e9\u70ea\u70eb\u70ec\u70ed\u70ee\u70ef\u70f0\u70f1\u70f2\u70f3\u70f4\u70f5\u70f6\u70f7\u70f8\u70f9\u70fa\u70fb\u70fc\u70fd\u70fe\u70ff\u7100\u7101\u7102\u7103\u7104\u7105\u7106\u7107\u7108\u7109\u710a\u710b\u710c\u710d\u710e\u710f\u7110\u7111\u7112\u7113\u7114\u7115\u7116\u7117\u7118\u7119\u711a\u711b\u711c\u711d\u711e\u711f\u7120\u7121\u7122\u7123\u7124\u7125\u7126\u7127\u7128\u7129\u712a\u712b\u712c\u712d\u712e\u712f\u7130\u7131\u7132\u7133\u7134\u7135\u7136\u7137\u7138\u7139\u713a\u713b\u713c\u713d\u713e\u713f\u7140\u7141\u7142\u7143\u7144\u7145\u7146\u7147\u7148\u7149\u714a\u714b\u714c\u714d\u714e\u714f\u7150\u7151\u7152\u7153\u7154\u7155\u7156\u7157\u7158\u7159\u715a\u715b\u715c\u715d\u715e\u715f\u7160\u7161\u7162\u7163\u7164\u7165\u7166\u7167\u7168\u7169\u716a\u716b\u716c\u716d\u716e\u716f\u7170\u7171\u7172\u7173\u7174\u7175\u7176\u7177\u7178\u7179\u717a\u717b\u717c\u717d\u717e\u717f\u7180\u7181\u7182\u7183\u7184\u7185\u7186\u7187\u7188\u7189\u718a\u718b\u718c\u718d\u718e\u718f\u7190\u7191\u7192\u7193\u7194\u7195\u7196\u7197\u7198\u7199\u719a\u719b\u719c\u719d\u719e\u719f\u71a0\u71a1\u71a2\u71a3\u71a4\u71a5\u71a6\u71a7\u71a8\u71a9\u71aa\u71ab\u71ac\u71ad\u71ae\u71af\u71b0\u71b1\u71b2\u71b3\u71b4\u71b5\u71b6\u71b7\u71b8\u71b9\u71ba\u71bb\u71bc\u71bd\u71be\u71bf\u71c0\u71c1\u71c2\u71c3\u71c4\u71c5\u71c6\u71c7\u71c8\u71c9\u71ca\u71cb\u71cc\u71cd\u71ce\u71cf\u71d0\u71d1\u71d2\u71d3\u71d4\u71d5\u71d6\u71d7\u71d8\u71d9\u71da\u71db\u71dc\u71dd\u71de\u71df\u71e0\u71e1\u71e2\u71e3\u71e4\u71e5\u71e6\u71e7\u71e8\u71e9\u71ea\u71eb\u71ec\u71ed\u71ee\u71ef\u71f0\u71f1\u71f2\u71f3\u71f4\u71f5\u71f6\u71f7\u71f8\u71f9\u71fa\u71fb\u71fc\u71fd\u71fe\u71ff\u7200\u7201\u7202\u7203\u7204\u7205\u7206\u7207\u7208\u7209\u720a\u720b\u720c\u720d\u720e\u720f\u7210\u7211\u7212\u7213\u7214\u7215\u7216\u7217\u7218\u7219\u721a\u721b\u721c\u721d\u721e\u721f\u7220\u7221\u7222\u7223\u7224\u7225\u7226\u7227\u7228\u7229\u722a\u722b\u722c\u722d\u722e\u722f\u7230\u7231\u7232\u7233\u7234\u7235\u7236\u7237\u7238\u7239\u723a\u723b\u723c\u723d\u723e\u723f\u7240\u7241\u7242\u7243\u7244\u7245\u7246\u7247\u7248\u7249\u724a\u724b\u724c\u724d\u724e\u724f\u7250\u7251\u7252\u7253\u7254\u7255\u7256\u7257\u7258\u7259\u725a\u725b\u725c\u725d\u725e\u725f\u7260\u7261\u7262\u7263\u7264\u7265\u7266\u7267\u7268\u7269\u726a\u726b\u726c\u726d\u726e\u726f\u7270\u7271\u7272\u7273\u7274\u7275\u7276\u7277\u7278\u7279\u727a\u727b\u727c\u727d\u727e\u727f\u7280\u7281\u7282\u7283\u7284\u7285\u7286\u7287\u7288\u7289\u728a\u728b\u728c\u728d\u728e\u728f\u7290\u7291\u7292\u7293\u7294\u7295\u7296\u7297\u7298\u7299\u729a\u729b\u729c\u729d\u729e\u729f\u72a0\u72a1\u72a2\u72a3\u72a4\u72a5\u72a6\u72a7\u72a8\u72a9\u72aa\u72ab\u72ac\u72ad\u72ae\u72af\u72b0\u72b1\u72b2\u72b3\u72b4\u72b5\u72b6\u72b7\u72b8\u72b9\u72ba\u72bb\u72bc\u72bd\u72be\u72bf\u72c0\u72c1\u72c2\u72c3\u72c4\u72c5\u72c6\u72c7\u72c8\u72c9\u72ca\u72cb\u72cc\u72cd\u72ce\u72cf\u72d0\u72d1\u72d2\u72d3\u72d4\u72d5\u72d6\u72d7\u72d8\u72d9\u72da\u72db\u72dc\u72dd\u72de\u72df\u72e0\u72e1\u72e2\u72e3\u72e4\u72e5\u72e6\u72e7\u72e8\u72e9\u72ea\u72eb\u72ec\u72ed\u72ee\u72ef\u72f0\u72f1\u72f2\u72f3\u72f4\u72f5\u72f6\u72f7\u72f8\u72f9\u72fa\u72fb\u72fc\u72fd\u72fe\u72ff\u7300\u7301\u7302\u7303\u7304\u7305\u7306\u7307\u7308\u7309\u730a\u730b\u730c\u730d\u730e\u730f\u7310\u7311\u7312\u7313\u7314\u7315\u7316\u7317\u7318\u7319\u731a\u731b\u731c\u731d\u731e\u731f\u7320\u7321\u7322\u7323\u7324\u7325\u7326\u7327\u7328\u7329\u732a\u732b\u732c\u732d\u732e\u732f\u7330\u7331\u7332\u7333\u7334\u7335\u7336\u7337\u7338\u7339\u733a\u733b\u733c\u733d\u733e\u733f\u7340\u7341\u7342\u7343\u7344\u7345\u7346\u7347\u7348\u7349\u734a\u734b\u734c\u734d\u734e\u734f\u7350\u7351\u7352\u7353\u7354\u7355\u7356\u7357\u7358\u7359\u735a\u735b\u735c\u735d\u735e\u735f\u7360\u7361\u7362\u7363\u7364\u7365\u7366\u7367\u7368\u7369\u736a\u736b\u736c\u736d\u736e\u736f\u7370\u7371\u7372\u7373\u7374\u7375\u7376\u7377\u7378\u7379\u737a\u737b\u737c\u737d\u737e\u737f\u7380\u7381\u7382\u7383\u7384\u7385\u7386\u7387\u7388\u7389\u738a\u738b\u738c\u738d\u738e\u738f\u7390\u7391\u7392\u7393\u7394\u7395\u7396\u7397\u7398\u7399\u739a\u739b\u739c\u739d\u739e\u739f\u73a0\u73a1\u73a2\u73a3\u73a4\u73a5\u73a6\u73a7\u73a8\u73a9\u73aa\u73ab\u73ac\u73ad\u73ae\u73af\u73b0\u73b1\u73b2\u73b3\u73b4\u73b5\u73b6\u73b7\u73b8\u73b9\u73ba\u73bb\u73bc\u73bd\u73be\u73bf\u73c0\u73c1\u73c2\u73c3\u73c4\u73c5\u73c6\u73c7\u73c8\u73c9\u73ca\u73cb\u73cc\u73cd\u73ce\u73cf\u73d0\u73d1\u73d2\u73d3\u73d4\u73d5\u73d6\u73d7\u73d8\u73d9\u73da\u73db\u73dc\u73dd\u73de\u73df\u73e0\u73e1\u73e2\u73e3\u73e4\u73e5\u73e6\u73e7\u73e8\u73e9\u73ea\u73eb\u73ec\u73ed\u73ee\u73ef\u73f0\u73f1\u73f2\u73f3\u73f4\u73f5\u73f6\u73f7\u73f8\u73f9\u73fa\u73fb\u73fc\u73fd\u73fe\u73ff\u7400\u7401\u7402\u7403\u7404\u7405\u7406\u7407\u7408\u7409\u740a\u740b\u740c\u740d\u740e\u740f\u7410\u7411\u7412\u7413\u7414\u7415\u7416\u7417\u7418\u7419\u741a\u741b\u741c\u741d\u741e\u741f\u7420\u7421\u7422\u7423\u7424\u7425\u7426\u7427\u7428\u7429\u742a\u742b\u742c\u742d\u742e\u742f\u7430\u7431\u7432\u7433\u7434\u7435\u7436\u7437\u7438\u7439\u743a\u743b\u743c\u743d\u743e\u743f\u7440\u7441\u7442\u7443\u7444\u7445\u7446\u7447\u7448\u7449\u744a\u744b\u744c\u744d\u744e\u744f\u7450\u7451\u7452\u7453\u7454\u7455\u7456\u7457\u7458\u7459\u745a\u745b\u745c\u745d\u745e\u745f\u7460\u7461\u7462\u7463\u7464\u7465\u7466\u7467\u7468\u7469\u746a\u746b\u746c\u746d\u746e\u746f\u7470\u7471\u7472\u7473\u7474\u7475\u7476\u7477\u7478\u7479\u747a\u747b\u747c\u747d\u747e\u747f\u7480\u7481\u7482\u7483\u7484\u7485\u7486\u7487\u7488\u7489\u748a\u748b\u748c\u748d\u748e\u748f\u7490\u7491\u7492\u7493\u7494\u7495\u7496\u7497\u7498\u7499\u749a\u749b\u749c\u749d\u749e\u749f\u74a0\u74a1\u74a2\u74a3\u74a4\u74a5\u74a6\u74a7\u74a8\u74a9\u74aa\u74ab\u74ac\u74ad\u74ae\u74af\u74b0\u74b1\u74b2\u74b3\u74b4\u74b5\u74b6\u74b7\u74b8\u74b9\u74ba\u74bb\u74bc\u74bd\u74be\u74bf\u74c0\u74c1\u74c2\u74c3\u74c4\u74c5\u74c6\u74c7\u74c8\u74c9\u74ca\u74cb\u74cc\u74cd\u74ce\u74cf\u74d0\u74d1\u74d2\u74d3\u74d4\u74d5\u74d6\u74d7\u74d8\u74d9\u74da\u74db\u74dc\u74dd\u74de\u74df\u74e0\u74e1\u74e2\u74e3\u74e4\u74e5\u74e6\u74e7\u74e8\u74e9\u74ea\u74eb\u74ec\u74ed\u74ee\u74ef\u74f0\u74f1\u74f2\u74f3\u74f4\u74f5\u74f6\u74f7\u74f8\u74f9\u74fa\u74fb\u74fc\u74fd\u74fe\u74ff\u7500\u7501\u7502\u7503\u7504\u7505\u7506\u7507\u7508\u7509\u750a\u750b\u750c\u750d\u750e\u750f\u7510\u7511\u7512\u7513\u7514\u7515\u7516\u7517\u7518\u7519\u751a\u751b\u751c\u751d\u751e\u751f\u7520\u7521\u7522\u7523\u7524\u7525\u7526\u7527\u7528\u7529\u752a\u752b\u752c\u752d\u752e\u752f\u7530\u7531\u7532\u7533\u7534\u7535\u7536\u7537\u7538\u7539\u753a\u753b\u753c\u753d\u753e\u753f\u7540\u7541\u7542\u7543\u7544\u7545\u7546\u7547\u7548\u7549\u754a\u754b\u754c\u754d\u754e\u754f\u7550\u7551\u7552\u7553\u7554\u7555\u7556\u7557\u7558\u7559\u755a\u755b\u755c\u755d\u755e\u755f\u7560\u7561\u7562\u7563\u7564\u7565\u7566\u7567\u7568\u7569\u756a\u756b\u756c\u756d\u756e\u756f\u7570\u7571\u7572\u7573\u7574\u7575\u7576\u7577\u7578\u7579\u757a\u757b\u757c\u757d\u757e\u757f\u7580\u7581\u7582\u7583\u7584\u7585\u7586\u7587\u7588\u7589\u758a\u758b\u758c\u758d\u758e\u758f\u7590\u7591\u7592\u7593\u7594\u7595\u7596\u7597\u7598\u7599\u759a\u759b\u759c\u759d\u759e\u759f\u75a0\u75a1\u75a2\u75a3\u75a4\u75a5\u75a6\u75a7\u75a8\u75a9\u75aa\u75ab\u75ac\u75ad\u75ae\u75af\u75b0\u75b1\u75b2\u75b3\u75b4\u75b5\u75b6\u75b7\u75b8\u75b9\u75ba\u75bb\u75bc\u75bd\u75be\u75bf\u75c0\u75c1\u75c2\u75c3\u75c4\u75c5\u75c6\u75c7\u75c8\u75c9\u75ca\u75cb\u75cc\u75cd\u75ce\u75cf\u75d0\u75d1\u75d2\u75d3\u75d4\u75d5\u75d6\u75d7\u75d8\u75d9\u75da\u75db\u75dc\u75dd\u75de\u75df\u75e0\u75e1\u75e2\u75e3\u75e4\u75e5\u75e6\u75e7\u75e8\u75e9\u75ea\u75eb\u75ec\u75ed\u75ee\u75ef\u75f0\u75f1\u75f2\u75f3\u75f4\u75f5\u75f6\u75f7\u75f8\u75f9\u75fa\u75fb\u75fc\u75fd\u75fe\u75ff\u7600\u7601\u7602\u7603\u7604\u7605\u7606\u7607\u7608\u7609\u760a\u760b\u760c\u760d\u760e\u760f\u7610\u7611\u7612\u7613\u7614\u7615\u7616\u7617\u7618\u7619\u761a\u761b\u761c\u761d\u761e\u761f\u7620\u7621\u7622\u7623\u7624\u7625\u7626\u7627\u7628\u7629\u762a\u762b\u762c\u762d\u762e\u762f\u7630\u7631\u7632\u7633\u7634\u7635\u7636\u7637\u7638\u7639\u763a\u763b\u763c\u763d\u763e\u763f\u7640\u7641\u7642\u7643\u7644\u7645\u7646\u7647\u7648\u7649\u764a\u764b\u764c\u764d\u764e\u764f\u7650\u7651\u7652\u7653\u7654\u7655\u7656\u7657\u7658\u7659\u765a\u765b\u765c\u765d\u765e\u765f\u7660\u7661\u7662\u7663\u7664\u7665\u7666\u7667\u7668\u7669\u766a\u766b\u766c\u766d\u766e\u766f\u7670\u7671\u7672\u7673\u7674\u7675\u7676\u7677\u7678\u7679\u767a\u767b\u767c\u767d\u767e\u767f\u7680\u7681\u7682\u7683\u7684\u7685\u7686\u7687\u7688\u7689\u768a\u768b\u768c\u768d\u768e\u768f\u7690\u7691\u7692\u7693\u7694\u7695\u7696\u7697\u7698\u7699\u769a\u769b\u769c\u769d\u769e\u769f\u76a0\u76a1\u76a2\u76a3\u76a4\u76a5\u76a6\u76a7\u76a8\u76a9\u76aa\u76ab\u76ac\u76ad\u76ae\u76af\u76b0\u76b1\u76b2\u76b3\u76b4\u76b5\u76b6\u76b7\u76b8\u76b9\u76ba\u76bb\u76bc\u76bd\u76be\u76bf\u76c0\u76c1\u76c2\u76c3\u76c4\u76c5\u76c6\u76c7\u76c8\u76c9\u76ca\u76cb\u76cc\u76cd\u76ce\u76cf\u76d0\u76d1\u76d2\u76d3\u76d4\u76d5\u76d6\u76d7\u76d8\u76d9\u76da\u76db\u76dc\u76dd\u76de\u76df\u76e0\u76e1\u76e2\u76e3\u76e4\u76e5\u76e6\u76e7\u76e8\u76e9\u76ea\u76eb\u76ec\u76ed\u76ee\u76ef\u76f0\u76f1\u76f2\u76f3\u76f4\u76f5\u76f6\u76f7\u76f8\u76f9\u76fa\u76fb\u76fc\u76fd\u76fe\u76ff\u7700\u7701\u7702\u7703\u7704\u7705\u7706\u7707\u7708\u7709\u770a\u770b\u770c\u770d\u770e\u770f\u7710\u7711\u7712\u7713\u7714\u7715\u7716\u7717\u7718\u7719\u771a\u771b\u771c\u771d\u771e\u771f\u7720\u7721\u7722\u7723\u7724\u7725\u7726\u7727\u7728\u7729\u772a\u772b\u772c\u772d\u772e\u772f\u7730\u7731\u7732\u7733\u7734\u7735\u7736\u7737\u7738\u7739\u773a\u773b\u773c\u773d\u773e\u773f\u7740\u7741\u7742\u7743\u7744\u7745\u7746\u7747\u7748\u7749\u774a\u774b\u774c\u774d\u774e\u774f\u7750\u7751\u7752\u7753\u7754\u7755\u7756\u7757\u7758\u7759\u775a\u775b\u775c\u775d\u775e\u775f\u7760\u7761\u7762\u7763\u7764\u7765\u7766\u7767\u7768\u7769\u776a\u776b\u776c\u776d\u776e\u776f\u7770\u7771\u7772\u7773\u7774\u7775\u7776\u7777\u7778\u7779\u777a\u777b\u777c\u777d\u777e\u777f\u7780\u7781\u7782\u7783\u7784\u7785\u7786\u7787\u7788\u7789\u778a\u778b\u778c\u778d\u778e\u778f\u7790\u7791\u7792\u7793\u7794\u7795\u7796\u7797\u7798\u7799\u779a\u779b\u779c\u779d\u779e\u779f\u77a0\u77a1\u77a2\u77a3\u77a4\u77a5\u77a6\u77a7\u77a8\u77a9\u77aa\u77ab\u77ac\u77ad\u77ae\u77af\u77b0\u77b1\u77b2\u77b3\u77b4\u77b5\u77b6\u77b7\u77b8\u77b9\u77ba\u77bb\u77bc\u77bd\u77be\u77bf\u77c0\u77c1\u77c2\u77c3\u77c4\u77c5\u77c6\u77c7\u77c8\u77c9\u77ca\u77cb\u77cc\u77cd\u77ce\u77cf\u77d0\u77d1\u77d2\u77d3\u77d4\u77d5\u77d6\u77d7\u77d8\u77d9\u77da\u77db\u77dc\u77dd\u77de\u77df\u77e0\u77e1\u77e2\u77e3\u77e4\u77e5\u77e6\u77e7\u77e8\u77e9\u77ea\u77eb\u77ec\u77ed\u77ee\u77ef\u77f0\u77f1\u77f2\u77f3\u77f4\u77f5\u77f6\u77f7\u77f8\u77f9\u77fa\u77fb\u77fc\u77fd\u77fe\u77ff\u7800\u7801\u7802\u7803\u7804\u7805\u7806\u7807\u7808\u7809\u780a\u780b\u780c\u780d\u780e\u780f\u7810\u7811\u7812\u7813\u7814\u7815\u7816\u7817\u7818\u7819\u781a\u781b\u781c\u781d\u781e\u781f\u7820\u7821\u7822\u7823\u7824\u7825\u7826\u7827\u7828\u7829\u782a\u782b\u782c\u782d\u782e\u782f\u7830\u7831\u7832\u7833\u7834\u7835\u7836\u7837\u7838\u7839\u783a\u783b\u783c\u783d\u783e\u783f\u7840\u7841\u7842\u7843\u7844\u7845\u7846\u7847\u7848\u7849\u784a\u784b\u784c\u784d\u784e\u784f\u7850\u7851\u7852\u7853\u7854\u7855\u7856\u7857\u7858\u7859\u785a\u785b\u785c\u785d\u785e\u785f\u7860\u7861\u7862\u7863\u7864\u7865\u7866\u7867\u7868\u7869\u786a\u786b\u786c\u786d\u786e\u786f\u7870\u7871\u7872\u7873\u7874\u7875\u7876\u7877\u7878\u7879\u787a\u787b\u787c\u787d\u787e\u787f\u7880\u7881\u7882\u7883\u7884\u7885\u7886\u7887\u7888\u7889\u788a\u788b\u788c\u788d\u788e\u788f\u7890\u7891\u7892\u7893\u7894\u7895\u7896\u7897\u7898\u7899\u789a\u789b\u789c\u789d\u789e\u789f\u78a0\u78a1\u78a2\u78a3\u78a4\u78a5\u78a6\u78a7\u78a8\u78a9\u78aa\u78ab\u78ac\u78ad\u78ae\u78af\u78b0\u78b1\u78b2\u78b3\u78b4\u78b5\u78b6\u78b7\u78b8\u78b9\u78ba\u78bb\u78bc\u78bd\u78be\u78bf\u78c0\u78c1\u78c2\u78c3\u78c4\u78c5\u78c6\u78c7\u78c8\u78c9\u78ca\u78cb\u78cc\u78cd\u78ce\u78cf\u78d0\u78d1\u78d2\u78d3\u78d4\u78d5\u78d6\u78d7\u78d8\u78d9\u78da\u78db\u78dc\u78dd\u78de\u78df\u78e0\u78e1\u78e2\u78e3\u78e4\u78e5\u78e6\u78e7\u78e8\u78e9\u78ea\u78eb\u78ec\u78ed\u78ee\u78ef\u78f0\u78f1\u78f2\u78f3\u78f4\u78f5\u78f6\u78f7\u78f8\u78f9\u78fa\u78fb\u78fc\u78fd\u78fe\u78ff\u7900\u7901\u7902\u7903\u7904\u7905\u7906\u7907\u7908\u7909\u790a\u790b\u790c\u790d\u790e\u790f\u7910\u7911\u7912\u7913\u7914\u7915\u7916\u7917\u7918\u7919\u791a\u791b\u791c\u791d\u791e\u791f\u7920\u7921\u7922\u7923\u7924\u7925\u7926\u7927\u7928\u7929\u792a\u792b\u792c\u792d\u792e\u792f\u7930\u7931\u7932\u7933\u7934\u7935\u7936\u7937\u7938\u7939\u793a\u793b\u793c\u793d\u793e\u793f\u7940\u7941\u7942\u7943\u7944\u7945\u7946\u7947\u7948\u7949\u794a\u794b\u794c\u794d\u794e\u794f\u7950\u7951\u7952\u7953\u7954\u7955\u7956\u7957\u7958\u7959\u795a\u795b\u795c\u795d\u795e\u795f\u7960\u7961\u7962\u7963\u7964\u7965\u7966\u7967\u7968\u7969\u796a\u796b\u796c\u796d\u796e\u796f\u7970\u7971\u7972\u7973\u7974\u7975\u7976\u7977\u7978\u7979\u797a\u797b\u797c\u797d\u797e\u797f\u7980\u7981\u7982\u7983\u7984\u7985\u7986\u7987\u7988\u7989\u798a\u798b\u798c\u798d\u798e\u798f\u7990\u7991\u7992\u7993\u7994\u7995\u7996\u7997\u7998\u7999\u799a\u799b\u799c\u799d\u799e\u799f\u79a0\u79a1\u79a2\u79a3\u79a4\u79a5\u79a6\u79a7\u79a8\u79a9\u79aa\u79ab\u79ac\u79ad\u79ae\u79af\u79b0\u79b1\u79b2\u79b3\u79b4\u79b5\u79b6\u79b7\u79b8\u79b9\u79ba\u79bb\u79bc\u79bd\u79be\u79bf\u79c0\u79c1\u79c2\u79c3\u79c4\u79c5\u79c6\u79c7\u79c8\u79c9\u79ca\u79cb\u79cc\u79cd\u79ce\u79cf\u79d0\u79d1\u79d2\u79d3\u79d4\u79d5\u79d6\u79d7\u79d8\u79d9\u79da\u79db\u79dc\u79dd\u79de\u79df\u79e0\u79e1\u79e2\u79e3\u79e4\u79e5\u79e6\u79e7\u79e8\u79e9\u79ea\u79eb\u79ec\u79ed\u79ee\u79ef\u79f0\u79f1\u79f2\u79f3\u79f4\u79f5\u79f6\u79f7\u79f8\u79f9\u79fa\u79fb\u79fc\u79fd\u79fe\u79ff\u7a00\u7a01\u7a02\u7a03\u7a04\u7a05\u7a06\u7a07\u7a08\u7a09\u7a0a\u7a0b\u7a0c\u7a0d\u7a0e\u7a0f\u7a10\u7a11\u7a12\u7a13\u7a14\u7a15\u7a16\u7a17\u7a18\u7a19\u7a1a\u7a1b\u7a1c\u7a1d\u7a1e\u7a1f\u7a20\u7a21\u7a22\u7a23\u7a24\u7a25\u7a26\u7a27\u7a28\u7a29\u7a2a\u7a2b\u7a2c\u7a2d\u7a2e\u7a2f\u7a30\u7a31\u7a32\u7a33\u7a34\u7a35\u7a36\u7a37\u7a38\u7a39\u7a3a\u7a3b\u7a3c\u7a3d\u7a3e\u7a3f\u7a40\u7a41\u7a42\u7a43\u7a44\u7a45\u7a46\u7a47\u7a48\u7a49\u7a4a\u7a4b\u7a4c\u7a4d\u7a4e\u7a4f\u7a50\u7a51\u7a52\u7a53\u7a54\u7a55\u7a56\u7a57\u7a58\u7a59\u7a5a\u7a5b\u7a5c\u7a5d\u7a5e\u7a5f\u7a60\u7a61\u7a62\u7a63\u7a64\u7a65\u7a66\u7a67\u7a68\u7a69\u7a6a\u7a6b\u7a6c\u7a6d\u7a6e\u7a6f\u7a70\u7a71\u7a72\u7a73\u7a74\u7a75\u7a76\u7a77\u7a78\u7a79\u7a7a\u7a7b\u7a7c\u7a7d\u7a7e\u7a7f\u7a80\u7a81\u7a82\u7a83\u7a84\u7a85\u7a86\u7a87\u7a88\u7a89\u7a8a\u7a8b\u7a8c\u7a8d\u7a8e\u7a8f\u7a90\u7a91\u7a92\u7a93\u7a94\u7a95\u7a96\u7a97\u7a98\u7a99\u7a9a\u7a9b\u7a9c\u7a9d\u7a9e\u7a9f\u7aa0\u7aa1\u7aa2\u7aa3\u7aa4\u7aa5\u7aa6\u7aa7\u7aa8\u7aa9\u7aaa\u7aab\u7aac\u7aad\u7aae\u7aaf\u7ab0\u7ab1\u7ab2\u7ab3\u7ab4\u7ab5\u7ab6\u7ab7\u7ab8\u7ab9\u7aba\u7abb\u7abc\u7abd\u7abe\u7abf\u7ac0\u7ac1\u7ac2\u7ac3\u7ac4\u7ac5\u7ac6\u7ac7\u7ac8\u7ac9\u7aca\u7acb\u7acc\u7acd\u7ace\u7acf\u7ad0\u7ad1\u7ad2\u7ad3\u7ad4\u7ad5\u7ad6\u7ad7\u7ad8\u7ad9\u7ada\u7adb\u7adc\u7add\u7ade\u7adf\u7ae0\u7ae1\u7ae2\u7ae3\u7ae4\u7ae5\u7ae6\u7ae7\u7ae8\u7ae9\u7aea\u7aeb\u7aec\u7aed\u7aee\u7aef\u7af0\u7af1\u7af2\u7af3\u7af4\u7af5\u7af6\u7af7\u7af8\u7af9\u7afa\u7afb\u7afc\u7afd\u7afe\u7aff\u7b00\u7b01\u7b02\u7b03\u7b04\u7b05\u7b06\u7b07\u7b08\u7b09\u7b0a\u7b0b\u7b0c\u7b0d\u7b0e\u7b0f\u7b10\u7b11\u7b12\u7b13\u7b14\u7b15\u7b16\u7b17\u7b18\u7b19\u7b1a\u7b1b\u7b1c\u7b1d\u7b1e\u7b1f\u7b20\u7b21\u7b22\u7b23\u7b24\u7b25\u7b26\u7b27\u7b28\u7b29\u7b2a\u7b2b\u7b2c\u7b2d\u7b2e\u7b2f\u7b30\u7b31\u7b32\u7b33\u7b34\u7b35\u7b36\u7b37\u7b38\u7b39\u7b3a\u7b3b\u7b3c\u7b3d\u7b3e\u7b3f\u7b40\u7b41\u7b42\u7b43\u7b44\u7b45\u7b46\u7b47\u7b48\u7b49\u7b4a\u7b4b\u7b4c\u7b4d\u7b4e\u7b4f\u7b50\u7b51\u7b52\u7b53\u7b54\u7b55\u7b56\u7b57\u7b58\u7b59\u7b5a\u7b5b\u7b5c\u7b5d\u7b5e\u7b5f\u7b60\u7b61\u7b62\u7b63\u7b64\u7b65\u7b66\u7b67\u7b68\u7b69\u7b6a\u7b6b\u7b6c\u7b6d\u7b6e\u7b6f\u7b70\u7b71\u7b72\u7b73\u7b74\u7b75\u7b76\u7b77\u7b78\u7b79\u7b7a\u7b7b\u7b7c\u7b7d\u7b7e\u7b7f\u7b80\u7b81\u7b82\u7b83\u7b84\u7b85\u7b86\u7b87\u7b88\u7b89\u7b8a\u7b8b\u7b8c\u7b8d\u7b8e\u7b8f\u7b90\u7b91\u7b92\u7b93\u7b94\u7b95\u7b96\u7b97\u7b98\u7b99\u7b9a\u7b9b\u7b9c\u7b9d\u7b9e\u7b9f\u7ba0\u7ba1\u7ba2\u7ba3\u7ba4\u7ba5\u7ba6\u7ba7\u7ba8\u7ba9\u7baa\u7bab\u7bac\u7bad\u7bae\u7baf\u7bb0\u7bb1\u7bb2\u7bb3\u7bb4\u7bb5\u7bb6\u7bb7\u7bb8\u7bb9\u7bba\u7bbb\u7bbc\u7bbd\u7bbe\u7bbf\u7bc0\u7bc1\u7bc2\u7bc3\u7bc4\u7bc5\u7bc6\u7bc7\u7bc8\u7bc9\u7bca\u7bcb\u7bcc\u7bcd\u7bce\u7bcf\u7bd0\u7bd1\u7bd2\u7bd3\u7bd4\u7bd5\u7bd6\u7bd7\u7bd8\u7bd9\u7bda\u7bdb\u7bdc\u7bdd\u7bde\u7bdf\u7be0\u7be1\u7be2\u7be3\u7be4\u7be5\u7be6\u7be7\u7be8\u7be9\u7bea\u7beb\u7bec\u7bed\u7bee\u7bef\u7bf0\u7bf1\u7bf2\u7bf3\u7bf4\u7bf5\u7bf6\u7bf7\u7bf8\u7bf9\u7bfa\u7bfb\u7bfc\u7bfd\u7bfe\u7bff\u7c00\u7c01\u7c02\u7c03\u7c04\u7c05\u7c06\u7c07\u7c08\u7c09\u7c0a\u7c0b\u7c0c\u7c0d\u7c0e\u7c0f\u7c10\u7c11\u7c12\u7c13\u7c14\u7c15\u7c16\u7c17\u7c18\u7c19\u7c1a\u7c1b\u7c1c\u7c1d\u7c1e\u7c1f\u7c20\u7c21\u7c22\u7c23\u7c24\u7c25\u7c26\u7c27\u7c28\u7c29\u7c2a\u7c2b\u7c2c\u7c2d\u7c2e\u7c2f\u7c30\u7c31\u7c32\u7c33\u7c34\u7c35\u7c36\u7c37\u7c38\u7c39\u7c3a\u7c3b\u7c3c\u7c3d\u7c3e\u7c3f\u7c40\u7c41\u7c42\u7c43\u7c44\u7c45\u7c46\u7c47\u7c48\u7c49\u7c4a\u7c4b\u7c4c\u7c4d\u7c4e\u7c4f\u7c50\u7c51\u7c52\u7c53\u7c54\u7c55\u7c56\u7c57\u7c58\u7c59\u7c5a\u7c5b\u7c5c\u7c5d\u7c5e\u7c5f\u7c60\u7c61\u7c62\u7c63\u7c64\u7c65\u7c66\u7c67\u7c68\u7c69\u7c6a\u7c6b\u7c6c\u7c6d\u7c6e\u7c6f\u7c70\u7c71\u7c72\u7c73\u7c74\u7c75\u7c76\u7c77\u7c78\u7c79\u7c7a\u7c7b\u7c7c\u7c7d\u7c7e\u7c7f\u7c80\u7c81\u7c82\u7c83\u7c84\u7c85\u7c86\u7c87\u7c88\u7c89\u7c8a\u7c8b\u7c8c\u7c8d\u7c8e\u7c8f\u7c90\u7c91\u7c92\u7c93\u7c94\u7c95\u7c96\u7c97\u7c98\u7c99\u7c9a\u7c9b\u7c9c\u7c9d\u7c9e\u7c9f\u7ca0\u7ca1\u7ca2\u7ca3\u7ca4\u7ca5\u7ca6\u7ca7\u7ca8\u7ca9\u7caa\u7cab\u7cac\u7cad\u7cae\u7caf\u7cb0\u7cb1\u7cb2\u7cb3\u7cb4\u7cb5\u7cb6\u7cb7\u7cb8\u7cb9\u7cba\u7cbb\u7cbc\u7cbd\u7cbe\u7cbf\u7cc0\u7cc1\u7cc2\u7cc3\u7cc4\u7cc5\u7cc6\u7cc7\u7cc8\u7cc9\u7cca\u7ccb\u7ccc\u7ccd\u7cce\u7ccf\u7cd0\u7cd1\u7cd2\u7cd3\u7cd4\u7cd5\u7cd6\u7cd7\u7cd8\u7cd9\u7cda\u7cdb\u7cdc\u7cdd\u7cde\u7cdf\u7ce0\u7ce1\u7ce2\u7ce3\u7ce4\u7ce5\u7ce6\u7ce7\u7ce8\u7ce9\u7cea\u7ceb\u7cec\u7ced\u7cee\u7cef\u7cf0\u7cf1\u7cf2\u7cf3\u7cf4\u7cf5\u7cf6\u7cf7\u7cf8\u7cf9\u7cfa\u7cfb\u7cfc\u7cfd\u7cfe\u7cff\u7d00\u7d01\u7d02\u7d03\u7d04\u7d05\u7d06\u7d07\u7d08\u7d09\u7d0a\u7d0b\u7d0c\u7d0d\u7d0e\u7d0f\u7d10\u7d11\u7d12\u7d13\u7d14\u7d15\u7d16\u7d17\u7d18\u7d19\u7d1a\u7d1b\u7d1c\u7d1d\u7d1e\u7d1f\u7d20\u7d21\u7d22\u7d23\u7d24\u7d25\u7d26\u7d27\u7d28\u7d29\u7d2a\u7d2b\u7d2c\u7d2d\u7d2e\u7d2f\u7d30\u7d31\u7d32\u7d33\u7d34\u7d35\u7d36\u7d37\u7d38\u7d39\u7d3a\u7d3b\u7d3c\u7d3d\u7d3e\u7d3f\u7d40\u7d41\u7d42\u7d43\u7d44\u7d45\u7d46\u7d47\u7d48\u7d49\u7d4a\u7d4b\u7d4c\u7d4d\u7d4e\u7d4f\u7d50\u7d51\u7d52\u7d53\u7d54\u7d55\u7d56\u7d57\u7d58\u7d59\u7d5a\u7d5b\u7d5c\u7d5d\u7d5e\u7d5f\u7d60\u7d61\u7d62\u7d63\u7d64\u7d65\u7d66\u7d67\u7d68\u7d69\u7d6a\u7d6b\u7d6c\u7d6d\u7d6e\u7d6f\u7d70\u7d71\u7d72\u7d73\u7d74\u7d75\u7d76\u7d77\u7d78\u7d79\u7d7a\u7d7b\u7d7c\u7d7d\u7d7e\u7d7f\u7d80\u7d81\u7d82\u7d83\u7d84\u7d85\u7d86\u7d87\u7d88\u7d89\u7d8a\u7d8b\u7d8c\u7d8d\u7d8e\u7d8f\u7d90\u7d91\u7d92\u7d93\u7d94\u7d95\u7d96\u7d97\u7d98\u7d99\u7d9a\u7d9b\u7d9c\u7d9d\u7d9e\u7d9f\u7da0\u7da1\u7da2\u7da3\u7da4\u7da5\u7da6\u7da7\u7da8\u7da9\u7daa\u7dab\u7dac\u7dad\u7dae\u7daf\u7db0\u7db1\u7db2\u7db3\u7db4\u7db5\u7db6\u7db7\u7db8\u7db9\u7dba\u7dbb\u7dbc\u7dbd\u7dbe\u7dbf\u7dc0\u7dc1\u7dc2\u7dc3\u7dc4\u7dc5\u7dc6\u7dc7\u7dc8\u7dc9\u7dca\u7dcb\u7dcc\u7dcd\u7dce\u7dcf\u7dd0\u7dd1\u7dd2\u7dd3\u7dd4\u7dd5\u7dd6\u7dd7\u7dd8\u7dd9\u7dda\u7ddb\u7ddc\u7ddd\u7dde\u7ddf\u7de0\u7de1\u7de2\u7de3\u7de4\u7de5\u7de6\u7de7\u7de8\u7de9\u7dea\u7deb\u7dec\u7ded\u7dee\u7def\u7df0\u7df1\u7df2\u7df3\u7df4\u7df5\u7df6\u7df7\u7df8\u7df9\u7dfa\u7dfb\u7dfc\u7dfd\u7dfe\u7dff\u7e00\u7e01\u7e02\u7e03\u7e04\u7e05\u7e06\u7e07\u7e08\u7e09\u7e0a\u7e0b\u7e0c\u7e0d\u7e0e\u7e0f\u7e10\u7e11\u7e12\u7e13\u7e14\u7e15\u7e16\u7e17\u7e18\u7e19\u7e1a\u7e1b\u7e1c\u7e1d\u7e1e\u7e1f\u7e20\u7e21\u7e22\u7e23\u7e24\u7e25\u7e26\u7e27\u7e28\u7e29\u7e2a\u7e2b\u7e2c\u7e2d\u7e2e\u7e2f\u7e30\u7e31\u7e32\u7e33\u7e34\u7e35\u7e36\u7e37\u7e38\u7e39\u7e3a\u7e3b\u7e3c\u7e3d\u7e3e\u7e3f\u7e40\u7e41\u7e42\u7e43\u7e44\u7e45\u7e46\u7e47\u7e48\u7e49\u7e4a\u7e4b\u7e4c\u7e4d\u7e4e\u7e4f\u7e50\u7e51\u7e52\u7e53\u7e54\u7e55\u7e56\u7e57\u7e58\u7e59\u7e5a\u7e5b\u7e5c\u7e5d\u7e5e\u7e5f\u7e60\u7e61\u7e62\u7e63\u7e64\u7e65\u7e66\u7e67\u7e68\u7e69\u7e6a\u7e6b\u7e6c\u7e6d\u7e6e\u7e6f\u7e70\u7e71\u7e72\u7e73\u7e74\u7e75\u7e76\u7e77\u7e78\u7e79\u7e7a\u7e7b\u7e7c\u7e7d\u7e7e\u7e7f\u7e80\u7e81\u7e82\u7e83\u7e84\u7e85\u7e86\u7e87\u7e88\u7e89\u7e8a\u7e8b\u7e8c\u7e8d\u7e8e\u7e8f\u7e90\u7e91\u7e92\u7e93\u7e94\u7e95\u7e96\u7e97\u7e98\u7e99\u7e9a\u7e9b\u7e9c\u7e9d\u7e9e\u7e9f\u7ea0\u7ea1\u7ea2\u7ea3\u7ea4\u7ea5\u7ea6\u7ea7\u7ea8\u7ea9\u7eaa\u7eab\u7eac\u7ead\u7eae\u7eaf\u7eb0\u7eb1\u7eb2\u7eb3\u7eb4\u7eb5\u7eb6\u7eb7\u7eb8\u7eb9\u7eba\u7ebb\u7ebc\u7ebd\u7ebe\u7ebf\u7ec0\u7ec1\u7ec2\u7ec3\u7ec4\u7ec5\u7ec6\u7ec7\u7ec8\u7ec9\u7eca\u7ecb\u7ecc\u7ecd\u7ece\u7ecf\u7ed0\u7ed1\u7ed2\u7ed3\u7ed4\u7ed5\u7ed6\u7ed7\u7ed8\u7ed9\u7eda\u7edb\u7edc\u7edd\u7ede\u7edf\u7ee0\u7ee1\u7ee2\u7ee3\u7ee4\u7ee5\u7ee6\u7ee7\u7ee8\u7ee9\u7eea\u7eeb\u7eec\u7eed\u7eee\u7eef\u7ef0\u7ef1\u7ef2\u7ef3\u7ef4\u7ef5\u7ef6\u7ef7\u7ef8\u7ef9\u7efa\u7efb\u7efc\u7efd\u7efe\u7eff\u7f00\u7f01\u7f02\u7f03\u7f04\u7f05\u7f06\u7f07\u7f08\u7f09\u7f0a\u7f0b\u7f0c\u7f0d\u7f0e\u7f0f\u7f10\u7f11\u7f12\u7f13\u7f14\u7f15\u7f16\u7f17\u7f18\u7f19\u7f1a\u7f1b\u7f1c\u7f1d\u7f1e\u7f1f\u7f20\u7f21\u7f22\u7f23\u7f24\u7f25\u7f26\u7f27\u7f28\u7f29\u7f2a\u7f2b\u7f2c\u7f2d\u7f2e\u7f2f\u7f30\u7f31\u7f32\u7f33\u7f34\u7f35\u7f36\u7f37\u7f38\u7f39\u7f3a\u7f3b\u7f3c\u7f3d\u7f3e\u7f3f\u7f40\u7f41\u7f42\u7f43\u7f44\u7f45\u7f46\u7f47\u7f48\u7f49\u7f4a\u7f4b\u7f4c\u7f4d\u7f4e\u7f4f\u7f50\u7f51\u7f52\u7f53\u7f54\u7f55\u7f56\u7f57\u7f58\u7f59\u7f5a\u7f5b\u7f5c\u7f5d\u7f5e\u7f5f\u7f60\u7f61\u7f62\u7f63\u7f64\u7f65\u7f66\u7f67\u7f68\u7f69\u7f6a\u7f6b\u7f6c\u7f6d\u7f6e\u7f6f\u7f70\u7f71\u7f72\u7f73\u7f74\u7f75\u7f76\u7f77\u7f78\u7f79\u7f7a\u7f7b\u7f7c\u7f7d\u7f7e\u7f7f\u7f80\u7f81\u7f82\u7f83\u7f84\u7f85\u7f86\u7f87\u7f88\u7f89\u7f8a\u7f8b\u7f8c\u7f8d\u7f8e\u7f8f\u7f90\u7f91\u7f92\u7f93\u7f94\u7f95\u7f96\u7f97\u7f98\u7f99\u7f9a\u7f9b\u7f9c\u7f9d\u7f9e\u7f9f\u7fa0\u7fa1\u7fa2\u7fa3\u7fa4\u7fa5\u7fa6\u7fa7\u7fa8\u7fa9\u7faa\u7fab\u7fac\u7fad\u7fae\u7faf\u7fb0\u7fb1\u7fb2\u7fb3\u7fb4\u7fb5\u7fb6\u7fb7\u7fb8\u7fb9\u7fba\u7fbb\u7fbc\u7fbd\u7fbe\u7fbf\u7fc0\u7fc1\u7fc2\u7fc3\u7fc4\u7fc5\u7fc6\u7fc7\u7fc8\u7fc9\u7fca\u7fcb\u7fcc\u7fcd\u7fce\u7fcf\u7fd0\u7fd1\u7fd2\u7fd3\u7fd4\u7fd5\u7fd6\u7fd7\u7fd8\u7fd9\u7fda\u7fdb\u7fdc\u7fdd\u7fde\u7fdf\u7fe0\u7fe1\u7fe2\u7fe3\u7fe4\u7fe5\u7fe6\u7fe7\u7fe8\u7fe9\u7fea\u7feb\u7fec\u7fed\u7fee\u7fef\u7ff0\u7ff1\u7ff2\u7ff3\u7ff4\u7ff5\u7ff6\u7ff7\u7ff8\u7ff9\u7ffa\u7ffb\u7ffc\u7ffd\u7ffe\u7fff\u8000\u8001\u8002\u8003\u8004\u8005\u8006\u8007\u8008\u8009\u800a\u800b\u800c\u800d\u800e\u800f\u8010\u8011\u8012\u8013\u8014\u8015\u8016\u8017\u8018\u8019\u801a\u801b\u801c\u801d\u801e\u801f\u8020\u8021\u8022\u8023\u8024\u8025\u8026\u8027\u8028\u8029\u802a\u802b\u802c\u802d\u802e\u802f\u8030\u8031\u8032\u8033\u8034\u8035\u8036\u8037\u8038\u8039\u803a\u803b\u803c\u803d\u803e\u803f\u8040\u8041\u8042\u8043\u8044\u8045\u8046\u8047\u8048\u8049\u804a\u804b\u804c\u804d\u804e\u804f\u8050\u8051\u8052\u8053\u8054\u8055\u8056\u8057\u8058\u8059\u805a\u805b\u805c\u805d\u805e\u805f\u8060\u8061\u8062\u8063\u8064\u8065\u8066\u8067\u8068\u8069\u806a\u806b\u806c\u806d\u806e\u806f\u8070\u8071\u8072\u8073\u8074\u8075\u8076\u8077\u8078\u8079\u807a\u807b\u807c\u807d\u807e\u807f\u8080\u8081\u8082\u8083\u8084\u8085\u8086\u8087\u8088\u8089\u808a\u808b\u808c\u808d\u808e\u808f\u8090\u8091\u8092\u8093\u8094\u8095\u8096\u8097\u8098\u8099\u809a\u809b\u809c\u809d\u809e\u809f\u80a0\u80a1\u80a2\u80a3\u80a4\u80a5\u80a6\u80a7\u80a8\u80a9\u80aa\u80ab\u80ac\u80ad\u80ae\u80af\u80b0\u80b1\u80b2\u80b3\u80b4\u80b5\u80b6\u80b7\u80b8\u80b9\u80ba\u80bb\u80bc\u80bd\u80be\u80bf\u80c0\u80c1\u80c2\u80c3\u80c4\u80c5\u80c6\u80c7\u80c8\u80c9\u80ca\u80cb\u80cc\u80cd\u80ce\u80cf\u80d0\u80d1\u80d2\u80d3\u80d4\u80d5\u80d6\u80d7\u80d8\u80d9\u80da\u80db\u80dc\u80dd\u80de\u80df\u80e0\u80e1\u80e2\u80e3\u80e4\u80e5\u80e6\u80e7\u80e8\u80e9\u80ea\u80eb\u80ec\u80ed\u80ee\u80ef\u80f0\u80f1\u80f2\u80f3\u80f4\u80f5\u80f6\u80f7\u80f8\u80f9\u80fa\u80fb\u80fc\u80fd\u80fe\u80ff\u8100\u8101\u8102\u8103\u8104\u8105\u8106\u8107\u8108\u8109\u810a\u810b\u810c\u810d\u810e\u810f\u8110\u8111\u8112\u8113\u8114\u8115\u8116\u8117\u8118\u8119\u811a\u811b\u811c\u811d\u811e\u811f\u8120\u8121\u8122\u8123\u8124\u8125\u8126\u8127\u8128\u8129\u812a\u812b\u812c\u812d\u812e\u812f\u8130\u8131\u8132\u8133\u8134\u8135\u8136\u8137\u8138\u8139\u813a\u813b\u813c\u813d\u813e\u813f\u8140\u8141\u8142\u8143\u8144\u8145\u8146\u8147\u8148\u8149\u814a\u814b\u814c\u814d\u814e\u814f\u8150\u8151\u8152\u8153\u8154\u8155\u8156\u8157\u8158\u8159\u815a\u815b\u815c\u815d\u815e\u815f\u8160\u8161\u8162\u8163\u8164\u8165\u8166\u8167\u8168\u8169\u816a\u816b\u816c\u816d\u816e\u816f\u8170\u8171\u8172\u8173\u8174\u8175\u8176\u8177\u8178\u8179\u817a\u817b\u817c\u817d\u817e\u817f\u8180\u8181\u8182\u8183\u8184\u8185\u8186\u8187\u8188\u8189\u818a\u818b\u818c\u818d\u818e\u818f\u8190\u8191\u8192\u8193\u8194\u8195\u8196\u8197\u8198\u8199\u819a\u819b\u819c\u819d\u819e\u819f\u81a0\u81a1\u81a2\u81a3\u81a4\u81a5\u81a6\u81a7\u81a8\u81a9\u81aa\u81ab\u81ac\u81ad\u81ae\u81af\u81b0\u81b1\u81b2\u81b3\u81b4\u81b5\u81b6\u81b7\u81b8\u81b9\u81ba\u81bb\u81bc\u81bd\u81be\u81bf\u81c0\u81c1\u81c2\u81c3\u81c4\u81c5\u81c6\u81c7\u81c8\u81c9\u81ca\u81cb\u81cc\u81cd\u81ce\u81cf\u81d0\u81d1\u81d2\u81d3\u81d4\u81d5\u81d6\u81d7\u81d8\u81d9\u81da\u81db\u81dc\u81dd\u81de\u81df\u81e0\u81e1\u81e2\u81e3\u81e4\u81e5\u81e6\u81e7\u81e8\u81e9\u81ea\u81eb\u81ec\u81ed\u81ee\u81ef\u81f0\u81f1\u81f2\u81f3\u81f4\u81f5\u81f6\u81f7\u81f8\u81f9\u81fa\u81fb\u81fc\u81fd\u81fe\u81ff\u8200\u8201\u8202\u8203\u8204\u8205\u8206\u8207\u8208\u8209\u820a\u820b\u820c\u820d\u820e\u820f\u8210\u8211\u8212\u8213\u8214\u8215\u8216\u8217\u8218\u8219\u821a\u821b\u821c\u821d\u821e\u821f\u8220\u8221\u8222\u8223\u8224\u8225\u8226\u8227\u8228\u8229\u822a\u822b\u822c\u822d\u822e\u822f\u8230\u8231\u8232\u8233\u8234\u8235\u8236\u8237\u8238\u8239\u823a\u823b\u823c\u823d\u823e\u823f\u8240\u8241\u8242\u8243\u8244\u8245\u8246\u8247\u8248\u8249\u824a\u824b\u824c\u824d\u824e\u824f\u8250\u8251\u8252\u8253\u8254\u8255\u8256\u8257\u8258\u8259\u825a\u825b\u825c\u825d\u825e\u825f\u8260\u8261\u8262\u8263\u8264\u8265\u8266\u8267\u8268\u8269\u826a\u826b\u826c\u826d\u826e\u826f\u8270\u8271\u8272\u8273\u8274\u8275\u8276\u8277\u8278\u8279\u827a\u827b\u827c\u827d\u827e\u827f\u8280\u8281\u8282\u8283\u8284\u8285\u8286\u8287\u8288\u8289\u828a\u828b\u828c\u828d\u828e\u828f\u8290\u8291\u8292\u8293\u8294\u8295\u8296\u8297\u8298\u8299\u829a\u829b\u829c\u829d\u829e\u829f\u82a0\u82a1\u82a2\u82a3\u82a4\u82a5\u82a6\u82a7\u82a8\u82a9\u82aa\u82ab\u82ac\u82ad\u82ae\u82af\u82b0\u82b1\u82b2\u82b3\u82b4\u82b5\u82b6\u82b7\u82b8\u82b9\u82ba\u82bb\u82bc\u82bd\u82be\u82bf\u82c0\u82c1\u82c2\u82c3\u82c4\u82c5\u82c6\u82c7\u82c8\u82c9\u82ca\u82cb\u82cc\u82cd\u82ce\u82cf\u82d0\u82d1\u82d2\u82d3\u82d4\u82d5\u82d6\u82d7\u82d8\u82d9\u82da\u82db\u82dc\u82dd\u82de\u82df\u82e0\u82e1\u82e2\u82e3\u82e4\u82e5\u82e6\u82e7\u82e8\u82e9\u82ea\u82eb\u82ec\u82ed\u82ee\u82ef\u82f0\u82f1\u82f2\u82f3\u82f4\u82f5\u82f6\u82f7\u82f8\u82f9\u82fa\u82fb\u82fc\u82fd\u82fe\u82ff\u8300\u8301\u8302\u8303\u8304\u8305\u8306\u8307\u8308\u8309\u830a\u830b\u830c\u830d\u830e\u830f\u8310\u8311\u8312\u8313\u8314\u8315\u8316\u8317\u8318\u8319\u831a\u831b\u831c\u831d\u831e\u831f\u8320\u8321\u8322\u8323\u8324\u8325\u8326\u8327\u8328\u8329\u832a\u832b\u832c\u832d\u832e\u832f\u8330\u8331\u8332\u8333\u8334\u8335\u8336\u8337\u8338\u8339\u833a\u833b\u833c\u833d\u833e\u833f\u8340\u8341\u8342\u8343\u8344\u8345\u8346\u8347\u8348\u8349\u834a\u834b\u834c\u834d\u834e\u834f\u8350\u8351\u8352\u8353\u8354\u8355\u8356\u8357\u8358\u8359\u835a\u835b\u835c\u835d\u835e\u835f\u8360\u8361\u8362\u8363\u8364\u8365\u8366\u8367\u8368\u8369\u836a\u836b\u836c\u836d\u836e\u836f\u8370\u8371\u8372\u8373\u8374\u8375\u8376\u8377\u8378\u8379\u837a\u837b\u837c\u837d\u837e\u837f\u8380\u8381\u8382\u8383\u8384\u8385\u8386\u8387\u8388\u8389\u838a\u838b\u838c\u838d\u838e\u838f\u8390\u8391\u8392\u8393\u8394\u8395\u8396\u8397\u8398\u8399\u839a\u839b\u839c\u839d\u839e\u839f\u83a0\u83a1\u83a2\u83a3\u83a4\u83a5\u83a6\u83a7\u83a8\u83a9\u83aa\u83ab\u83ac\u83ad\u83ae\u83af\u83b0\u83b1\u83b2\u83b3\u83b4\u83b5\u83b6\u83b7\u83b8\u83b9\u83ba\u83bb\u83bc\u83bd\u83be\u83bf\u83c0\u83c1\u83c2\u83c3\u83c4\u83c5\u83c6\u83c7\u83c8\u83c9\u83ca\u83cb\u83cc\u83cd\u83ce\u83cf\u83d0\u83d1\u83d2\u83d3\u83d4\u83d5\u83d6\u83d7\u83d8\u83d9\u83da\u83db\u83dc\u83dd\u83de\u83df\u83e0\u83e1\u83e2\u83e3\u83e4\u83e5\u83e6\u83e7\u83e8\u83e9\u83ea\u83eb\u83ec\u83ed\u83ee\u83ef\u83f0\u83f1\u83f2\u83f3\u83f4\u83f5\u83f6\u83f7\u83f8\u83f9\u83fa\u83fb\u83fc\u83fd\u83fe\u83ff\u8400\u8401\u8402\u8403\u8404\u8405\u8406\u8407\u8408\u8409\u840a\u840b\u840c\u840d\u840e\u840f\u8410\u8411\u8412\u8413\u8414\u8415\u8416\u8417\u8418\u8419\u841a\u841b\u841c\u841d\u841e\u841f\u8420\u8421\u8422\u8423\u8424\u8425\u8426\u8427\u8428\u8429\u842a\u842b\u842c\u842d\u842e\u842f\u8430\u8431\u8432\u8433\u8434\u8435\u8436\u8437\u8438\u8439\u843a\u843b\u843c\u843d\u843e\u843f\u8440\u8441\u8442\u8443\u8444\u8445\u8446\u8447\u8448\u8449\u844a\u844b\u844c\u844d\u844e\u844f\u8450\u8451\u8452\u8453\u8454\u8455\u8456\u8457\u8458\u8459\u845a\u845b\u845c\u845d\u845e\u845f\u8460\u8461\u8462\u8463\u8464\u8465\u8466\u8467\u8468\u8469\u846a\u846b\u846c\u846d\u846e\u846f\u8470\u8471\u8472\u8473\u8474\u8475\u8476\u8477\u8478\u8479\u847a\u847b\u847c\u847d\u847e\u847f\u8480\u8481\u8482\u8483\u8484\u8485\u8486\u8487\u8488\u8489\u848a\u848b\u848c\u848d\u848e\u848f\u8490\u8491\u8492\u8493\u8494\u8495\u8496\u8497\u8498\u8499\u849a\u849b\u849c\u849d\u849e\u849f\u84a0\u84a1\u84a2\u84a3\u84a4\u84a5\u84a6\u84a7\u84a8\u84a9\u84aa\u84ab\u84ac\u84ad\u84ae\u84af\u84b0\u84b1\u84b2\u84b3\u84b4\u84b5\u84b6\u84b7\u84b8\u84b9\u84ba\u84bb\u84bc\u84bd\u84be\u84bf\u84c0\u84c1\u84c2\u84c3\u84c4\u84c5\u84c6\u84c7\u84c8\u84c9\u84ca\u84cb\u84cc\u84cd\u84ce\u84cf\u84d0\u84d1\u84d2\u84d3\u84d4\u84d5\u84d6\u84d7\u84d8\u84d9\u84da\u84db\u84dc\u84dd\u84de\u84df\u84e0\u84e1\u84e2\u84e3\u84e4\u84e5\u84e6\u84e7\u84e8\u84e9\u84ea\u84eb\u84ec\u84ed\u84ee\u84ef\u84f0\u84f1\u84f2\u84f3\u84f4\u84f5\u84f6\u84f7\u84f8\u84f9\u84fa\u84fb\u84fc\u84fd\u84fe\u84ff\u8500\u8501\u8502\u8503\u8504\u8505\u8506\u8507\u8508\u8509\u850a\u850b\u850c\u850d\u850e\u850f\u8510\u8511\u8512\u8513\u8514\u8515\u8516\u8517\u8518\u8519\u851a\u851b\u851c\u851d\u851e\u851f\u8520\u8521\u8522\u8523\u8524\u8525\u8526\u8527\u8528\u8529\u852a\u852b\u852c\u852d\u852e\u852f\u8530\u8531\u8532\u8533\u8534\u8535\u8536\u8537\u8538\u8539\u853a\u853b\u853c\u853d\u853e\u853f\u8540\u8541\u8542\u8543\u8544\u8545\u8546\u8547\u8548\u8549\u854a\u854b\u854c\u854d\u854e\u854f\u8550\u8551\u8552\u8553\u8554\u8555\u8556\u8557\u8558\u8559\u855a\u855b\u855c\u855d\u855e\u855f\u8560\u8561\u8562\u8563\u8564\u8565\u8566\u8567\u8568\u8569\u856a\u856b\u856c\u856d\u856e\u856f\u8570\u8571\u8572\u8573\u8574\u8575\u8576\u8577\u8578\u8579\u857a\u857b\u857c\u857d\u857e\u857f\u8580\u8581\u8582\u8583\u8584\u8585\u8586\u8587\u8588\u8589\u858a\u858b\u858c\u858d\u858e\u858f\u8590\u8591\u8592\u8593\u8594\u8595\u8596\u8597\u8598\u8599\u859a\u859b\u859c\u859d\u859e\u859f\u85a0\u85a1\u85a2\u85a3\u85a4\u85a5\u85a6\u85a7\u85a8\u85a9\u85aa\u85ab\u85ac\u85ad\u85ae\u85af\u85b0\u85b1\u85b2\u85b3\u85b4\u85b5\u85b6\u85b7\u85b8\u85b9\u85ba\u85bb\u85bc\u85bd\u85be\u85bf\u85c0\u85c1\u85c2\u85c3\u85c4\u85c5\u85c6\u85c7\u85c8\u85c9\u85ca\u85cb\u85cc\u85cd\u85ce\u85cf\u85d0\u85d1\u85d2\u85d3\u85d4\u85d5\u85d6\u85d7\u85d8\u85d9\u85da\u85db\u85dc\u85dd\u85de\u85df\u85e0\u85e1\u85e2\u85e3\u85e4\u85e5\u85e6\u85e7\u85e8\u85e9\u85ea\u85eb\u85ec\u85ed\u85ee\u85ef\u85f0\u85f1\u85f2\u85f3\u85f4\u85f5\u85f6\u85f7\u85f8\u85f9\u85fa\u85fb\u85fc\u85fd\u85fe\u85ff\u8600\u8601\u8602\u8603\u8604\u8605\u8606\u8607\u8608\u8609\u860a\u860b\u860c\u860d\u860e\u860f\u8610\u8611\u8612\u8613\u8614\u8615\u8616\u8617\u8618\u8619\u861a\u861b\u861c\u861d\u861e\u861f\u8620\u8621\u8622\u8623\u8624\u8625\u8626\u8627\u8628\u8629\u862a\u862b\u862c\u862d\u862e\u862f\u8630\u8631\u8632\u8633\u8634\u8635\u8636\u8637\u8638\u8639\u863a\u863b\u863c\u863d\u863e\u863f\u8640\u8641\u8642\u8643\u8644\u8645\u8646\u8647\u8648\u8649\u864a\u864b\u864c\u864d\u864e\u864f\u8650\u8651\u8652\u8653\u8654\u8655\u8656\u8657\u8658\u8659\u865a\u865b\u865c\u865d\u865e\u865f\u8660\u8661\u8662\u8663\u8664\u8665\u8666\u8667\u8668\u8669\u866a\u866b\u866c\u866d\u866e\u866f\u8670\u8671\u8672\u8673\u8674\u8675\u8676\u8677\u8678\u8679\u867a\u867b\u867c\u867d\u867e\u867f\u8680\u8681\u8682\u8683\u8684\u8685\u8686\u8687\u8688\u8689\u868a\u868b\u868c\u868d\u868e\u868f\u8690\u8691\u8692\u8693\u8694\u8695\u8696\u8697\u8698\u8699\u869a\u869b\u869c\u869d\u869e\u869f\u86a0\u86a1\u86a2\u86a3\u86a4\u86a5\u86a6\u86a7\u86a8\u86a9\u86aa\u86ab\u86ac\u86ad\u86ae\u86af\u86b0\u86b1\u86b2\u86b3\u86b4\u86b5\u86b6\u86b7\u86b8\u86b9\u86ba\u86bb\u86bc\u86bd\u86be\u86bf\u86c0\u86c1\u86c2\u86c3\u86c4\u86c5\u86c6\u86c7\u86c8\u86c9\u86ca\u86cb\u86cc\u86cd\u86ce\u86cf\u86d0\u86d1\u86d2\u86d3\u86d4\u86d5\u86d6\u86d7\u86d8\u86d9\u86da\u86db\u86dc\u86dd\u86de\u86df\u86e0\u86e1\u86e2\u86e3\u86e4\u86e5\u86e6\u86e7\u86e8\u86e9\u86ea\u86eb\u86ec\u86ed\u86ee\u86ef\u86f0\u86f1\u86f2\u86f3\u86f4\u86f5\u86f6\u86f7\u86f8\u86f9\u86fa\u86fb\u86fc\u86fd\u86fe\u86ff\u8700\u8701\u8702\u8703\u8704\u8705\u8706\u8707\u8708\u8709\u870a\u870b\u870c\u870d\u870e\u870f\u8710\u8711\u8712\u8713\u8714\u8715\u8716\u8717\u8718\u8719\u871a\u871b\u871c\u871d\u871e\u871f\u8720\u8721\u8722\u8723\u8724\u8725\u8726\u8727\u8728\u8729\u872a\u872b\u872c\u872d\u872e\u872f\u8730\u8731\u8732\u8733\u8734\u8735\u8736\u8737\u8738\u8739\u873a\u873b\u873c\u873d\u873e\u873f\u8740\u8741\u8742\u8743\u8744\u8745\u8746\u8747\u8748\u8749\u874a\u874b\u874c\u874d\u874e\u874f\u8750\u8751\u8752\u8753\u8754\u8755\u8756\u8757\u8758\u8759\u875a\u875b\u875c\u875d\u875e\u875f\u8760\u8761\u8762\u8763\u8764\u8765\u8766\u8767\u8768\u8769\u876a\u876b\u876c\u876d\u876e\u876f\u8770\u8771\u8772\u8773\u8774\u8775\u8776\u8777\u8778\u8779\u877a\u877b\u877c\u877d\u877e\u877f\u8780\u8781\u8782\u8783\u8784\u8785\u8786\u8787\u8788\u8789\u878a\u878b\u878c\u878d\u878e\u878f\u8790\u8791\u8792\u8793\u8794\u8795\u8796\u8797\u8798\u8799\u879a\u879b\u879c\u879d\u879e\u879f\u87a0\u87a1\u87a2\u87a3\u87a4\u87a5\u87a6\u87a7\u87a8\u87a9\u87aa\u87ab\u87ac\u87ad\u87ae\u87af\u87b0\u87b1\u87b2\u87b3\u87b4\u87b5\u87b6\u87b7\u87b8\u87b9\u87ba\u87bb\u87bc\u87bd\u87be\u87bf\u87c0\u87c1\u87c2\u87c3\u87c4\u87c5\u87c6\u87c7\u87c8\u87c9\u87ca\u87cb\u87cc\u87cd\u87ce\u87cf\u87d0\u87d1\u87d2\u87d3\u87d4\u87d5\u87d6\u87d7\u87d8\u87d9\u87da\u87db\u87dc\u87dd\u87de\u87df\u87e0\u87e1\u87e2\u87e3\u87e4\u87e5\u87e6\u87e7\u87e8\u87e9\u87ea\u87eb\u87ec\u87ed\u87ee\u87ef\u87f0\u87f1\u87f2\u87f3\u87f4\u87f5\u87f6\u87f7\u87f8\u87f9\u87fa\u87fb\u87fc\u87fd\u87fe\u87ff\u8800\u8801\u8802\u8803\u8804\u8805\u8806\u8807\u8808\u8809\u880a\u880b\u880c\u880d\u880e\u880f\u8810\u8811\u8812\u8813\u8814\u8815\u8816\u8817\u8818\u8819\u881a\u881b\u881c\u881d\u881e\u881f\u8820\u8821\u8822\u8823\u8824\u8825\u8826\u8827\u8828\u8829\u882a\u882b\u882c\u882d\u882e\u882f\u8830\u8831\u8832\u8833\u8834\u8835\u8836\u8837\u8838\u8839\u883a\u883b\u883c\u883d\u883e\u883f\u8840\u8841\u8842\u8843\u8844\u8845\u8846\u8847\u8848\u8849\u884a\u884b\u884c\u884d\u884e\u884f\u8850\u8851\u8852\u8853\u8854\u8855\u8856\u8857\u8858\u8859\u885a\u885b\u885c\u885d\u885e\u885f\u8860\u8861\u8862\u8863\u8864\u8865\u8866\u8867\u8868\u8869\u886a\u886b\u886c\u886d\u886e\u886f\u8870\u8871\u8872\u8873\u8874\u8875\u8876\u8877\u8878\u8879\u887a\u887b\u887c\u887d\u887e\u887f\u8880\u8881\u8882\u8883\u8884\u8885\u8886\u8887\u8888\u8889\u888a\u888b\u888c\u888d\u888e\u888f\u8890\u8891\u8892\u8893\u8894\u8895\u8896\u8897\u8898\u8899\u889a\u889b\u889c\u889d\u889e\u889f\u88a0\u88a1\u88a2\u88a3\u88a4\u88a5\u88a6\u88a7\u88a8\u88a9\u88aa\u88ab\u88ac\u88ad\u88ae\u88af\u88b0\u88b1\u88b2\u88b3\u88b4\u88b5\u88b6\u88b7\u88b8\u88b9\u88ba\u88bb\u88bc\u88bd\u88be\u88bf\u88c0\u88c1\u88c2\u88c3\u88c4\u88c5\u88c6\u88c7\u88c8\u88c9\u88ca\u88cb\u88cc\u88cd\u88ce\u88cf\u88d0\u88d1\u88d2\u88d3\u88d4\u88d5\u88d6\u88d7\u88d8\u88d9\u88da\u88db\u88dc\u88dd\u88de\u88df\u88e0\u88e1\u88e2\u88e3\u88e4\u88e5\u88e6\u88e7\u88e8\u88e9\u88ea\u88eb\u88ec\u88ed\u88ee\u88ef\u88f0\u88f1\u88f2\u88f3\u88f4\u88f5\u88f6\u88f7\u88f8\u88f9\u88fa\u88fb\u88fc\u88fd\u88fe\u88ff\u8900\u8901\u8902\u8903\u8904\u8905\u8906\u8907\u8908\u8909\u890a\u890b\u890c\u890d\u890e\u890f\u8910\u8911\u8912\u8913\u8914\u8915\u8916\u8917\u8918\u8919\u891a\u891b\u891c\u891d\u891e\u891f\u8920\u8921\u8922\u8923\u8924\u8925\u8926\u8927\u8928\u8929\u892a\u892b\u892c\u892d\u892e\u892f\u8930\u8931\u8932\u8933\u8934\u8935\u8936\u8937\u8938\u8939\u893a\u893b\u893c\u893d\u893e\u893f\u8940\u8941\u8942\u8943\u8944\u8945\u8946\u8947\u8948\u8949\u894a\u894b\u894c\u894d\u894e\u894f\u8950\u8951\u8952\u8953\u8954\u8955\u8956\u8957\u8958\u8959\u895a\u895b\u895c\u895d\u895e\u895f\u8960\u8961\u8962\u8963\u8964\u8965\u8966\u8967\u8968\u8969\u896a\u896b\u896c\u896d\u896e\u896f\u8970\u8971\u8972\u8973\u8974\u8975\u8976\u8977\u8978\u8979\u897a\u897b\u897c\u897d\u897e\u897f\u8980\u8981\u8982\u8983\u8984\u8985\u8986\u8987\u8988\u8989\u898a\u898b\u898c\u898d\u898e\u898f\u8990\u8991\u8992\u8993\u8994\u8995\u8996\u8997\u8998\u8999\u899a\u899b\u899c\u899d\u899e\u899f\u89a0\u89a1\u89a2\u89a3\u89a4\u89a5\u89a6\u89a7\u89a8\u89a9\u89aa\u89ab\u89ac\u89ad\u89ae\u89af\u89b0\u89b1\u89b2\u89b3\u89b4\u89b5\u89b6\u89b7\u89b8\u89b9\u89ba\u89bb\u89bc\u89bd\u89be\u89bf\u89c0\u89c1\u89c2\u89c3\u89c4\u89c5\u89c6\u89c7\u89c8\u89c9\u89ca\u89cb\u89cc\u89cd\u89ce\u89cf\u89d0\u89d1\u89d2\u89d3\u89d4\u89d5\u89d6\u89d7\u89d8\u89d9\u89da\u89db\u89dc\u89dd\u89de\u89df\u89e0\u89e1\u89e2\u89e3\u89e4\u89e5\u89e6\u89e7\u89e8\u89e9\u89ea\u89eb\u89ec\u89ed\u89ee\u89ef\u89f0\u89f1\u89f2\u89f3\u89f4\u89f5\u89f6\u89f7\u89f8\u89f9\u89fa\u89fb\u89fc\u89fd\u89fe\u89ff\u8a00\u8a01\u8a02\u8a03\u8a04\u8a05\u8a06\u8a07\u8a08\u8a09\u8a0a\u8a0b\u8a0c\u8a0d\u8a0e\u8a0f\u8a10\u8a11\u8a12\u8a13\u8a14\u8a15\u8a16\u8a17\u8a18\u8a19\u8a1a\u8a1b\u8a1c\u8a1d\u8a1e\u8a1f\u8a20\u8a21\u8a22\u8a23\u8a24\u8a25\u8a26\u8a27\u8a28\u8a29\u8a2a\u8a2b\u8a2c\u8a2d\u8a2e\u8a2f\u8a30\u8a31\u8a32\u8a33\u8a34\u8a35\u8a36\u8a37\u8a38\u8a39\u8a3a\u8a3b\u8a3c\u8a3d\u8a3e\u8a3f\u8a40\u8a41\u8a42\u8a43\u8a44\u8a45\u8a46\u8a47\u8a48\u8a49\u8a4a\u8a4b\u8a4c\u8a4d\u8a4e\u8a4f\u8a50\u8a51\u8a52\u8a53\u8a54\u8a55\u8a56\u8a57\u8a58\u8a59\u8a5a\u8a5b\u8a5c\u8a5d\u8a5e\u8a5f\u8a60\u8a61\u8a62\u8a63\u8a64\u8a65\u8a66\u8a67\u8a68\u8a69\u8a6a\u8a6b\u8a6c\u8a6d\u8a6e\u8a6f\u8a70\u8a71\u8a72\u8a73\u8a74\u8a75\u8a76\u8a77\u8a78\u8a79\u8a7a\u8a7b\u8a7c\u8a7d\u8a7e\u8a7f\u8a80\u8a81\u8a82\u8a83\u8a84\u8a85\u8a86\u8a87\u8a88\u8a89\u8a8a\u8a8b\u8a8c\u8a8d\u8a8e\u8a8f\u8a90\u8a91\u8a92\u8a93\u8a94\u8a95\u8a96\u8a97\u8a98\u8a99\u8a9a\u8a9b\u8a9c\u8a9d\u8a9e\u8a9f\u8aa0\u8aa1\u8aa2\u8aa3\u8aa4\u8aa5\u8aa6\u8aa7\u8aa8\u8aa9\u8aaa\u8aab\u8aac\u8aad\u8aae\u8aaf\u8ab0\u8ab1\u8ab2\u8ab3\u8ab4\u8ab5\u8ab6\u8ab7\u8ab8\u8ab9\u8aba\u8abb\u8abc\u8abd\u8abe\u8abf\u8ac0\u8ac1\u8ac2\u8ac3\u8ac4\u8ac5\u8ac6\u8ac7\u8ac8\u8ac9\u8aca\u8acb\u8acc\u8acd\u8ace\u8acf\u8ad0\u8ad1\u8ad2\u8ad3\u8ad4\u8ad5\u8ad6\u8ad7\u8ad8\u8ad9\u8ada\u8adb\u8adc\u8add\u8ade\u8adf\u8ae0\u8ae1\u8ae2\u8ae3\u8ae4\u8ae5\u8ae6\u8ae7\u8ae8\u8ae9\u8aea\u8aeb\u8aec\u8aed\u8aee\u8aef\u8af0\u8af1\u8af2\u8af3\u8af4\u8af5\u8af6\u8af7\u8af8\u8af9\u8afa\u8afb\u8afc\u8afd\u8afe\u8aff\u8b00\u8b01\u8b02\u8b03\u8b04\u8b05\u8b06\u8b07\u8b08\u8b09\u8b0a\u8b0b\u8b0c\u8b0d\u8b0e\u8b0f\u8b10\u8b11\u8b12\u8b13\u8b14\u8b15\u8b16\u8b17\u8b18\u8b19\u8b1a\u8b1b\u8b1c\u8b1d\u8b1e\u8b1f\u8b20\u8b21\u8b22\u8b23\u8b24\u8b25\u8b26\u8b27\u8b28\u8b29\u8b2a\u8b2b\u8b2c\u8b2d\u8b2e\u8b2f\u8b30\u8b31\u8b32\u8b33\u8b34\u8b35\u8b36\u8b37\u8b38\u8b39\u8b3a\u8b3b\u8b3c\u8b3d\u8b3e\u8b3f\u8b40\u8b41\u8b42\u8b43\u8b44\u8b45\u8b46\u8b47\u8b48\u8b49\u8b4a\u8b4b\u8b4c\u8b4d\u8b4e\u8b4f\u8b50\u8b51\u8b52\u8b53\u8b54\u8b55\u8b56\u8b57\u8b58\u8b59\u8b5a\u8b5b\u8b5c\u8b5d\u8b5e\u8b5f\u8b60\u8b61\u8b62\u8b63\u8b64\u8b65\u8b66\u8b67\u8b68\u8b69\u8b6a\u8b6b\u8b6c\u8b6d\u8b6e\u8b6f\u8b70\u8b71\u8b72\u8b73\u8b74\u8b75\u8b76\u8b77\u8b78\u8b79\u8b7a\u8b7b\u8b7c\u8b7d\u8b7e\u8b7f\u8b80\u8b81\u8b82\u8b83\u8b84\u8b85\u8b86\u8b87\u8b88\u8b89\u8b8a\u8b8b\u8b8c\u8b8d\u8b8e\u8b8f\u8b90\u8b91\u8b92\u8b93\u8b94\u8b95\u8b96\u8b97\u8b98\u8b99\u8b9a\u8b9b\u8b9c\u8b9d\u8b9e\u8b9f\u8ba0\u8ba1\u8ba2\u8ba3\u8ba4\u8ba5\u8ba6\u8ba7\u8ba8\u8ba9\u8baa\u8bab\u8bac\u8bad\u8bae\u8baf\u8bb0\u8bb1\u8bb2\u8bb3\u8bb4\u8bb5\u8bb6\u8bb7\u8bb8\u8bb9\u8bba\u8bbb\u8bbc\u8bbd\u8bbe\u8bbf\u8bc0\u8bc1\u8bc2\u8bc3\u8bc4\u8bc5\u8bc6\u8bc7\u8bc8\u8bc9\u8bca\u8bcb\u8bcc\u8bcd\u8bce\u8bcf\u8bd0\u8bd1\u8bd2\u8bd3\u8bd4\u8bd5\u8bd6\u8bd7\u8bd8\u8bd9\u8bda\u8bdb\u8bdc\u8bdd\u8bde\u8bdf\u8be0\u8be1\u8be2\u8be3\u8be4\u8be5\u8be6\u8be7\u8be8\u8be9\u8bea\u8beb\u8bec\u8bed\u8bee\u8bef\u8bf0\u8bf1\u8bf2\u8bf3\u8bf4\u8bf5\u8bf6\u8bf7\u8bf8\u8bf9\u8bfa\u8bfb\u8bfc\u8bfd\u8bfe\u8bff\u8c00\u8c01\u8c02\u8c03\u8c04\u8c05\u8c06\u8c07\u8c08\u8c09\u8c0a\u8c0b\u8c0c\u8c0d\u8c0e\u8c0f\u8c10\u8c11\u8c12\u8c13\u8c14\u8c15\u8c16\u8c17\u8c18\u8c19\u8c1a\u8c1b\u8c1c\u8c1d\u8c1e\u8c1f\u8c20\u8c21\u8c22\u8c23\u8c24\u8c25\u8c26\u8c27\u8c28\u8c29\u8c2a\u8c2b\u8c2c\u8c2d\u8c2e\u8c2f\u8c30\u8c31\u8c32\u8c33\u8c34\u8c35\u8c36\u8c37\u8c38\u8c39\u8c3a\u8c3b\u8c3c\u8c3d\u8c3e\u8c3f\u8c40\u8c41\u8c42\u8c43\u8c44\u8c45\u8c46\u8c47\u8c48\u8c49\u8c4a\u8c4b\u8c4c\u8c4d\u8c4e\u8c4f\u8c50\u8c51\u8c52\u8c53\u8c54\u8c55\u8c56\u8c57\u8c58\u8c59\u8c5a\u8c5b\u8c5c\u8c5d\u8c5e\u8c5f\u8c60\u8c61\u8c62\u8c63\u8c64\u8c65\u8c66\u8c67\u8c68\u8c69\u8c6a\u8c6b\u8c6c\u8c6d\u8c6e\u8c6f\u8c70\u8c71\u8c72\u8c73\u8c74\u8c75\u8c76\u8c77\u8c78\u8c79\u8c7a\u8c7b\u8c7c\u8c7d\u8c7e\u8c7f\u8c80\u8c81\u8c82\u8c83\u8c84\u8c85\u8c86\u8c87\u8c88\u8c89\u8c8a\u8c8b\u8c8c\u8c8d\u8c8e\u8c8f\u8c90\u8c91\u8c92\u8c93\u8c94\u8c95\u8c96\u8c97\u8c98\u8c99\u8c9a\u8c9b\u8c9c\u8c9d\u8c9e\u8c9f\u8ca0\u8ca1\u8ca2\u8ca3\u8ca4\u8ca5\u8ca6\u8ca7\u8ca8\u8ca9\u8caa\u8cab\u8cac\u8cad\u8cae\u8caf\u8cb0\u8cb1\u8cb2\u8cb3\u8cb4\u8cb5\u8cb6\u8cb7\u8cb8\u8cb9\u8cba\u8cbb\u8cbc\u8cbd\u8cbe\u8cbf\u8cc0\u8cc1\u8cc2\u8cc3\u8cc4\u8cc5\u8cc6\u8cc7\u8cc8\u8cc9\u8cca\u8ccb\u8ccc\u8ccd\u8cce\u8ccf\u8cd0\u8cd1\u8cd2\u8cd3\u8cd4\u8cd5\u8cd6\u8cd7\u8cd8\u8cd9\u8cda\u8cdb\u8cdc\u8cdd\u8cde\u8cdf\u8ce0\u8ce1\u8ce2\u8ce3\u8ce4\u8ce5\u8ce6\u8ce7\u8ce8\u8ce9\u8cea\u8ceb\u8cec\u8ced\u8cee\u8cef\u8cf0\u8cf1\u8cf2\u8cf3\u8cf4\u8cf5\u8cf6\u8cf7\u8cf8\u8cf9\u8cfa\u8cfb\u8cfc\u8cfd\u8cfe\u8cff\u8d00\u8d01\u8d02\u8d03\u8d04\u8d05\u8d06\u8d07\u8d08\u8d09\u8d0a\u8d0b\u8d0c\u8d0d\u8d0e\u8d0f\u8d10\u8d11\u8d12\u8d13\u8d14\u8d15\u8d16\u8d17\u8d18\u8d19\u8d1a\u8d1b\u8d1c\u8d1d\u8d1e\u8d1f\u8d20\u8d21\u8d22\u8d23\u8d24\u8d25\u8d26\u8d27\u8d28\u8d29\u8d2a\u8d2b\u8d2c\u8d2d\u8d2e\u8d2f\u8d30\u8d31\u8d32\u8d33\u8d34\u8d35\u8d36\u8d37\u8d38\u8d39\u8d3a\u8d3b\u8d3c\u8d3d\u8d3e\u8d3f\u8d40\u8d41\u8d42\u8d43\u8d44\u8d45\u8d46\u8d47\u8d48\u8d49\u8d4a\u8d4b\u8d4c\u8d4d\u8d4e\u8d4f\u8d50\u8d51\u8d52\u8d53\u8d54\u8d55\u8d56\u8d57\u8d58\u8d59\u8d5a\u8d5b\u8d5c\u8d5d\u8d5e\u8d5f\u8d60\u8d61\u8d62\u8d63\u8d64\u8d65\u8d66\u8d67\u8d68\u8d69\u8d6a\u8d6b\u8d6c\u8d6d\u8d6e\u8d6f\u8d70\u8d71\u8d72\u8d73\u8d74\u8d75\u8d76\u8d77\u8d78\u8d79\u8d7a\u8d7b\u8d7c\u8d7d\u8d7e\u8d7f\u8d80\u8d81\u8d82\u8d83\u8d84\u8d85\u8d86\u8d87\u8d88\u8d89\u8d8a\u8d8b\u8d8c\u8d8d\u8d8e\u8d8f\u8d90\u8d91\u8d92\u8d93\u8d94\u8d95\u8d96\u8d97\u8d98\u8d99\u8d9a\u8d9b\u8d9c\u8d9d\u8d9e\u8d9f\u8da0\u8da1\u8da2\u8da3\u8da4\u8da5\u8da6\u8da7\u8da8\u8da9\u8daa\u8dab\u8dac\u8dad\u8dae\u8daf\u8db0\u8db1\u8db2\u8db3\u8db4\u8db5\u8db6\u8db7\u8db8\u8db9\u8dba\u8dbb\u8dbc\u8dbd\u8dbe\u8dbf\u8dc0\u8dc1\u8dc2\u8dc3\u8dc4\u8dc5\u8dc6\u8dc7\u8dc8\u8dc9\u8dca\u8dcb\u8dcc\u8dcd\u8dce\u8dcf\u8dd0\u8dd1\u8dd2\u8dd3\u8dd4\u8dd5\u8dd6\u8dd7\u8dd8\u8dd9\u8dda\u8ddb\u8ddc\u8ddd\u8dde\u8ddf\u8de0\u8de1\u8de2\u8de3\u8de4\u8de5\u8de6\u8de7\u8de8\u8de9\u8dea\u8deb\u8dec\u8ded\u8dee\u8def\u8df0\u8df1\u8df2\u8df3\u8df4\u8df5\u8df6\u8df7\u8df8\u8df9\u8dfa\u8dfb\u8dfc\u8dfd\u8dfe\u8dff\u8e00\u8e01\u8e02\u8e03\u8e04\u8e05\u8e06\u8e07\u8e08\u8e09\u8e0a\u8e0b\u8e0c\u8e0d\u8e0e\u8e0f\u8e10\u8e11\u8e12\u8e13\u8e14\u8e15\u8e16\u8e17\u8e18\u8e19\u8e1a\u8e1b\u8e1c\u8e1d\u8e1e\u8e1f\u8e20\u8e21\u8e22\u8e23\u8e24\u8e25\u8e26\u8e27\u8e28\u8e29\u8e2a\u8e2b\u8e2c\u8e2d\u8e2e\u8e2f\u8e30\u8e31\u8e32\u8e33\u8e34\u8e35\u8e36\u8e37\u8e38\u8e39\u8e3a\u8e3b\u8e3c\u8e3d\u8e3e\u8e3f\u8e40\u8e41\u8e42\u8e43\u8e44\u8e45\u8e46\u8e47\u8e48\u8e49\u8e4a\u8e4b\u8e4c\u8e4d\u8e4e\u8e4f\u8e50\u8e51\u8e52\u8e53\u8e54\u8e55\u8e56\u8e57\u8e58\u8e59\u8e5a\u8e5b\u8e5c\u8e5d\u8e5e\u8e5f\u8e60\u8e61\u8e62\u8e63\u8e64\u8e65\u8e66\u8e67\u8e68\u8e69\u8e6a\u8e6b\u8e6c\u8e6d\u8e6e\u8e6f\u8e70\u8e71\u8e72\u8e73\u8e74\u8e75\u8e76\u8e77\u8e78\u8e79\u8e7a\u8e7b\u8e7c\u8e7d\u8e7e\u8e7f\u8e80\u8e81\u8e82\u8e83\u8e84\u8e85\u8e86\u8e87\u8e88\u8e89\u8e8a\u8e8b\u8e8c\u8e8d\u8e8e\u8e8f\u8e90\u8e91\u8e92\u8e93\u8e94\u8e95\u8e96\u8e97\u8e98\u8e99\u8e9a\u8e9b\u8e9c\u8e9d\u8e9e\u8e9f\u8ea0\u8ea1\u8ea2\u8ea3\u8ea4\u8ea5\u8ea6\u8ea7\u8ea8\u8ea9\u8eaa\u8eab\u8eac\u8ead\u8eae\u8eaf\u8eb0\u8eb1\u8eb2\u8eb3\u8eb4\u8eb5\u8eb6\u8eb7\u8eb8\u8eb9\u8eba\u8ebb\u8ebc\u8ebd\u8ebe\u8ebf\u8ec0\u8ec1\u8ec2\u8ec3\u8ec4\u8ec5\u8ec6\u8ec7\u8ec8\u8ec9\u8eca\u8ecb\u8ecc\u8ecd\u8ece\u8ecf\u8ed0\u8ed1\u8ed2\u8ed3\u8ed4\u8ed5\u8ed6\u8ed7\u8ed8\u8ed9\u8eda\u8edb\u8edc\u8edd\u8ede\u8edf\u8ee0\u8ee1\u8ee2\u8ee3\u8ee4\u8ee5\u8ee6\u8ee7\u8ee8\u8ee9\u8eea\u8eeb\u8eec\u8eed\u8eee\u8eef\u8ef0\u8ef1\u8ef2\u8ef3\u8ef4\u8ef5\u8ef6\u8ef7\u8ef8\u8ef9\u8efa\u8efb\u8efc\u8efd\u8efe\u8eff\u8f00\u8f01\u8f02\u8f03\u8f04\u8f05\u8f06\u8f07\u8f08\u8f09\u8f0a\u8f0b\u8f0c\u8f0d\u8f0e\u8f0f\u8f10\u8f11\u8f12\u8f13\u8f14\u8f15\u8f16\u8f17\u8f18\u8f19\u8f1a\u8f1b\u8f1c\u8f1d\u8f1e\u8f1f\u8f20\u8f21\u8f22\u8f23\u8f24\u8f25\u8f26\u8f27\u8f28\u8f29\u8f2a\u8f2b\u8f2c\u8f2d\u8f2e\u8f2f\u8f30\u8f31\u8f32\u8f33\u8f34\u8f35\u8f36\u8f37\u8f38\u8f39\u8f3a\u8f3b\u8f3c\u8f3d\u8f3e\u8f3f\u8f40\u8f41\u8f42\u8f43\u8f44\u8f45\u8f46\u8f47\u8f48\u8f49\u8f4a\u8f4b\u8f4c\u8f4d\u8f4e\u8f4f\u8f50\u8f51\u8f52\u8f53\u8f54\u8f55\u8f56\u8f57\u8f58\u8f59\u8f5a\u8f5b\u8f5c\u8f5d\u8f5e\u8f5f\u8f60\u8f61\u8f62\u8f63\u8f64\u8f65\u8f66\u8f67\u8f68\u8f69\u8f6a\u8f6b\u8f6c\u8f6d\u8f6e\u8f6f\u8f70\u8f71\u8f72\u8f73\u8f74\u8f75\u8f76\u8f77\u8f78\u8f79\u8f7a\u8f7b\u8f7c\u8f7d\u8f7e\u8f7f\u8f80\u8f81\u8f82\u8f83\u8f84\u8f85\u8f86\u8f87\u8f88\u8f89\u8f8a\u8f8b\u8f8c\u8f8d\u8f8e\u8f8f\u8f90\u8f91\u8f92\u8f93\u8f94\u8f95\u8f96\u8f97\u8f98\u8f99\u8f9a\u8f9b\u8f9c\u8f9d\u8f9e\u8f9f\u8fa0\u8fa1\u8fa2\u8fa3\u8fa4\u8fa5\u8fa6\u8fa7\u8fa8\u8fa9\u8faa\u8fab\u8fac\u8fad\u8fae\u8faf\u8fb0\u8fb1\u8fb2\u8fb3\u8fb4\u8fb5\u8fb6\u8fb7\u8fb8\u8fb9\u8fba\u8fbb\u8fbc\u8fbd\u8fbe\u8fbf\u8fc0\u8fc1\u8fc2\u8fc3\u8fc4\u8fc5\u8fc6\u8fc7\u8fc8\u8fc9\u8fca\u8fcb\u8fcc\u8fcd\u8fce\u8fcf\u8fd0\u8fd1\u8fd2\u8fd3\u8fd4\u8fd5\u8fd6\u8fd7\u8fd8\u8fd9\u8fda\u8fdb\u8fdc\u8fdd\u8fde\u8fdf\u8fe0\u8fe1\u8fe2\u8fe3\u8fe4\u8fe5\u8fe6\u8fe7\u8fe8\u8fe9\u8fea\u8feb\u8fec\u8fed\u8fee\u8fef\u8ff0\u8ff1\u8ff2\u8ff3\u8ff4\u8ff5\u8ff6\u8ff7\u8ff8\u8ff9\u8ffa\u8ffb\u8ffc\u8ffd\u8ffe\u8fff\u9000\u9001\u9002\u9003\u9004\u9005\u9006\u9007\u9008\u9009\u900a\u900b\u900c\u900d\u900e\u900f\u9010\u9011\u9012\u9013\u9014\u9015\u9016\u9017\u9018\u9019\u901a\u901b\u901c\u901d\u901e\u901f\u9020\u9021\u9022\u9023\u9024\u9025\u9026\u9027\u9028\u9029\u902a\u902b\u902c\u902d\u902e\u902f\u9030\u9031\u9032\u9033\u9034\u9035\u9036\u9037\u9038\u9039\u903a\u903b\u903c\u903d\u903e\u903f\u9040\u9041\u9042\u9043\u9044\u9045\u9046\u9047\u9048\u9049\u904a\u904b\u904c\u904d\u904e\u904f\u9050\u9051\u9052\u9053\u9054\u9055\u9056\u9057\u9058\u9059\u905a\u905b\u905c\u905d\u905e\u905f\u9060\u9061\u9062\u9063\u9064\u9065\u9066\u9067\u9068\u9069\u906a\u906b\u906c\u906d\u906e\u906f\u9070\u9071\u9072\u9073\u9074\u9075\u9076\u9077\u9078\u9079\u907a\u907b\u907c\u907d\u907e\u907f\u9080\u9081\u9082\u9083\u9084\u9085\u9086\u9087\u9088\u9089\u908a\u908b\u908c\u908d\u908e\u908f\u9090\u9091\u9092\u9093\u9094\u9095\u9096\u9097\u9098\u9099\u909a\u909b\u909c\u909d\u909e\u909f\u90a0\u90a1\u90a2\u90a3\u90a4\u90a5\u90a6\u90a7\u90a8\u90a9\u90aa\u90ab\u90ac\u90ad\u90ae\u90af\u90b0\u90b1\u90b2\u90b3\u90b4\u90b5\u90b6\u90b7\u90b8\u90b9\u90ba\u90bb\u90bc\u90bd\u90be\u90bf\u90c0\u90c1\u90c2\u90c3\u90c4\u90c5\u90c6\u90c7\u90c8\u90c9\u90ca\u90cb\u90cc\u90cd\u90ce\u90cf\u90d0\u90d1\u90d2\u90d3\u90d4\u90d5\u90d6\u90d7\u90d8\u90d9\u90da\u90db\u90dc\u90dd\u90de\u90df\u90e0\u90e1\u90e2\u90e3\u90e4\u90e5\u90e6\u90e7\u90e8\u90e9\u90ea\u90eb\u90ec\u90ed\u90ee\u90ef\u90f0\u90f1\u90f2\u90f3\u90f4\u90f5\u90f6\u90f7\u90f8\u90f9\u90fa\u90fb\u90fc\u90fd\u90fe\u90ff\u9100\u9101\u9102\u9103\u9104\u9105\u9106\u9107\u9108\u9109\u910a\u910b\u910c\u910d\u910e\u910f\u9110\u9111\u9112\u9113\u9114\u9115\u9116\u9117\u9118\u9119\u911a\u911b\u911c\u911d\u911e\u911f\u9120\u9121\u9122\u9123\u9124\u9125\u9126\u9127\u9128\u9129\u912a\u912b\u912c\u912d\u912e\u912f\u9130\u9131\u9132\u9133\u9134\u9135\u9136\u9137\u9138\u9139\u913a\u913b\u913c\u913d\u913e\u913f\u9140\u9141\u9142\u9143\u9144\u9145\u9146\u9147\u9148\u9149\u914a\u914b\u914c\u914d\u914e\u914f\u9150\u9151\u9152\u9153\u9154\u9155\u9156\u9157\u9158\u9159\u915a\u915b\u915c\u915d\u915e\u915f\u9160\u9161\u9162\u9163\u9164\u9165\u9166\u9167\u9168\u9169\u916a\u916b\u916c\u916d\u916e\u916f\u9170\u9171\u9172\u9173\u9174\u9175\u9176\u9177\u9178\u9179\u917a\u917b\u917c\u917d\u917e\u917f\u9180\u9181\u9182\u9183\u9184\u9185\u9186\u9187\u9188\u9189\u918a\u918b\u918c\u918d\u918e\u918f\u9190\u9191\u9192\u9193\u9194\u9195\u9196\u9197\u9198\u9199\u919a\u919b\u919c\u919d\u919e\u919f\u91a0\u91a1\u91a2\u91a3\u91a4\u91a5\u91a6\u91a7\u91a8\u91a9\u91aa\u91ab\u91ac\u91ad\u91ae\u91af\u91b0\u91b1\u91b2\u91b3\u91b4\u91b5\u91b6\u91b7\u91b8\u91b9\u91ba\u91bb\u91bc\u91bd\u91be\u91bf\u91c0\u91c1\u91c2\u91c3\u91c4\u91c5\u91c6\u91c7\u91c8\u91c9\u91ca\u91cb\u91cc\u91cd\u91ce\u91cf\u91d0\u91d1\u91d2\u91d3\u91d4\u91d5\u91d6\u91d7\u91d8\u91d9\u91da\u91db\u91dc\u91dd\u91de\u91df\u91e0\u91e1\u91e2\u91e3\u91e4\u91e5\u91e6\u91e7\u91e8\u91e9\u91ea\u91eb\u91ec\u91ed\u91ee\u91ef\u91f0\u91f1\u91f2\u91f3\u91f4\u91f5\u91f6\u91f7\u91f8\u91f9\u91fa\u91fb\u91fc\u91fd\u91fe\u91ff\u9200\u9201\u9202\u9203\u9204\u9205\u9206\u9207\u9208\u9209\u920a\u920b\u920c\u920d\u920e\u920f\u9210\u9211\u9212\u9213\u9214\u9215\u9216\u9217\u9218\u9219\u921a\u921b\u921c\u921d\u921e\u921f\u9220\u9221\u9222\u9223\u9224\u9225\u9226\u9227\u9228\u9229\u922a\u922b\u922c\u922d\u922e\u922f\u9230\u9231\u9232\u9233\u9234\u9235\u9236\u9237\u9238\u9239\u923a\u923b\u923c\u923d\u923e\u923f\u9240\u9241\u9242\u9243\u9244\u9245\u9246\u9247\u9248\u9249\u924a\u924b\u924c\u924d\u924e\u924f\u9250\u9251\u9252\u9253\u9254\u9255\u9256\u9257\u9258\u9259\u925a\u925b\u925c\u925d\u925e\u925f\u9260\u9261\u9262\u9263\u9264\u9265\u9266\u9267\u9268\u9269\u926a\u926b\u926c\u926d\u926e\u926f\u9270\u9271\u9272\u9273\u9274\u9275\u9276\u9277\u9278\u9279\u927a\u927b\u927c\u927d\u927e\u927f\u9280\u9281\u9282\u9283\u9284\u9285\u9286\u9287\u9288\u9289\u928a\u928b\u928c\u928d\u928e\u928f\u9290\u9291\u9292\u9293\u9294\u9295\u9296\u9297\u9298\u9299\u929a\u929b\u929c\u929d\u929e\u929f\u92a0\u92a1\u92a2\u92a3\u92a4\u92a5\u92a6\u92a7\u92a8\u92a9\u92aa\u92ab\u92ac\u92ad\u92ae\u92af\u92b0\u92b1\u92b2\u92b3\u92b4\u92b5\u92b6\u92b7\u92b8\u92b9\u92ba\u92bb\u92bc\u92bd\u92be\u92bf\u92c0\u92c1\u92c2\u92c3\u92c4\u92c5\u92c6\u92c7\u92c8\u92c9\u92ca\u92cb\u92cc\u92cd\u92ce\u92cf\u92d0\u92d1\u92d2\u92d3\u92d4\u92d5\u92d6\u92d7\u92d8\u92d9\u92da\u92db\u92dc\u92dd\u92de\u92df\u92e0\u92e1\u92e2\u92e3\u92e4\u92e5\u92e6\u92e7\u92e8\u92e9\u92ea\u92eb\u92ec\u92ed\u92ee\u92ef\u92f0\u92f1\u92f2\u92f3\u92f4\u92f5\u92f6\u92f7\u92f8\u92f9\u92fa\u92fb\u92fc\u92fd\u92fe\u92ff\u9300\u9301\u9302\u9303\u9304\u9305\u9306\u9307\u9308\u9309\u930a\u930b\u930c\u930d\u930e\u930f\u9310\u9311\u9312\u9313\u9314\u9315\u9316\u9317\u9318\u9319\u931a\u931b\u931c\u931d\u931e\u931f\u9320\u9321\u9322\u9323\u9324\u9325\u9326\u9327\u9328\u9329\u932a\u932b\u932c\u932d\u932e\u932f\u9330\u9331\u9332\u9333\u9334\u9335\u9336\u9337\u9338\u9339\u933a\u933b\u933c\u933d\u933e\u933f\u9340\u9341\u9342\u9343\u9344\u9345\u9346\u9347\u9348\u9349\u934a\u934b\u934c\u934d\u934e\u934f\u9350\u9351\u9352\u9353\u9354\u9355\u9356\u9357\u9358\u9359\u935a\u935b\u935c\u935d\u935e\u935f\u9360\u9361\u9362\u9363\u9364\u9365\u9366\u9367\u9368\u9369\u936a\u936b\u936c\u936d\u936e\u936f\u9370\u9371\u9372\u9373\u9374\u9375\u9376\u9377\u9378\u9379\u937a\u937b\u937c\u937d\u937e\u937f\u9380\u9381\u9382\u9383\u9384\u9385\u9386\u9387\u9388\u9389\u938a\u938b\u938c\u938d\u938e\u938f\u9390\u9391\u9392\u9393\u9394\u9395\u9396\u9397\u9398\u9399\u939a\u939b\u939c\u939d\u939e\u939f\u93a0\u93a1\u93a2\u93a3\u93a4\u93a5\u93a6\u93a7\u93a8\u93a9\u93aa\u93ab\u93ac\u93ad\u93ae\u93af\u93b0\u93b1\u93b2\u93b3\u93b4\u93b5\u93b6\u93b7\u93b8\u93b9\u93ba\u93bb\u93bc\u93bd\u93be\u93bf\u93c0\u93c1\u93c2\u93c3\u93c4\u93c5\u93c6\u93c7\u93c8\u93c9\u93ca\u93cb\u93cc\u93cd\u93ce\u93cf\u93d0\u93d1\u93d2\u93d3\u93d4\u93d5\u93d6\u93d7\u93d8\u93d9\u93da\u93db\u93dc\u93dd\u93de\u93df\u93e0\u93e1\u93e2\u93e3\u93e4\u93e5\u93e6\u93e7\u93e8\u93e9\u93ea\u93eb\u93ec\u93ed\u93ee\u93ef\u93f0\u93f1\u93f2\u93f3\u93f4\u93f5\u93f6\u93f7\u93f8\u93f9\u93fa\u93fb\u93fc\u93fd\u93fe\u93ff\u9400\u9401\u9402\u9403\u9404\u9405\u9406\u9407\u9408\u9409\u940a\u940b\u940c\u940d\u940e\u940f\u9410\u9411\u9412\u9413\u9414\u9415\u9416\u9417\u9418\u9419\u941a\u941b\u941c\u941d\u941e\u941f\u9420\u9421\u9422\u9423\u9424\u9425\u9426\u9427\u9428\u9429\u942a\u942b\u942c\u942d\u942e\u942f\u9430\u9431\u9432\u9433\u9434\u9435\u9436\u9437\u9438\u9439\u943a\u943b\u943c\u943d\u943e\u943f\u9440\u9441\u9442\u9443\u9444\u9445\u9446\u9447\u9448\u9449\u944a\u944b\u944c\u944d\u944e\u944f\u9450\u9451\u9452\u9453\u9454\u9455\u9456\u9457\u9458\u9459\u945a\u945b\u945c\u945d\u945e\u945f\u9460\u9461\u9462\u9463\u9464\u9465\u9466\u9467\u9468\u9469\u946a\u946b\u946c\u946d\u946e\u946f\u9470\u9471\u9472\u9473\u9474\u9475\u9476\u9477\u9478\u9479\u947a\u947b\u947c\u947d\u947e\u947f\u9480\u9481\u9482\u9483\u9484\u9485\u9486\u9487\u9488\u9489\u948a\u948b\u948c\u948d\u948e\u948f\u9490\u9491\u9492\u9493\u9494\u9495\u9496\u9497\u9498\u9499\u949a\u949b\u949c\u949d\u949e\u949f\u94a0\u94a1\u94a2\u94a3\u94a4\u94a5\u94a6\u94a7\u94a8\u94a9\u94aa\u94ab\u94ac\u94ad\u94ae\u94af\u94b0\u94b1\u94b2\u94b3\u94b4\u94b5\u94b6\u94b7\u94b8\u94b9\u94ba\u94bb\u94bc\u94bd\u94be\u94bf\u94c0\u94c1\u94c2\u94c3\u94c4\u94c5\u94c6\u94c7\u94c8\u94c9\u94ca\u94cb\u94cc\u94cd\u94ce\u94cf\u94d0\u94d1\u94d2\u94d3\u94d4\u94d5\u94d6\u94d7\u94d8\u94d9\u94da\u94db\u94dc\u94dd\u94de\u94df\u94e0\u94e1\u94e2\u94e3\u94e4\u94e5\u94e6\u94e7\u94e8\u94e9\u94ea\u94eb\u94ec\u94ed\u94ee\u94ef\u94f0\u94f1\u94f2\u94f3\u94f4\u94f5\u94f6\u94f7\u94f8\u94f9\u94fa\u94fb\u94fc\u94fd\u94fe\u94ff\u9500\u9501\u9502\u9503\u9504\u9505\u9506\u9507\u9508\u9509\u950a\u950b\u950c\u950d\u950e\u950f\u9510\u9511\u9512\u9513\u9514\u9515\u9516\u9517\u9518\u9519\u951a\u951b\u951c\u951d\u951e\u951f\u9520\u9521\u9522\u9523\u9524\u9525\u9526\u9527\u9528\u9529\u952a\u952b\u952c\u952d\u952e\u952f\u9530\u9531\u9532\u9533\u9534\u9535\u9536\u9537\u9538\u9539\u953a\u953b\u953c\u953d\u953e\u953f\u9540\u9541\u9542\u9543\u9544\u9545\u9546\u9547\u9548\u9549\u954a\u954b\u954c\u954d\u954e\u954f\u9550\u9551\u9552\u9553\u9554\u9555\u9556\u9557\u9558\u9559\u955a\u955b\u955c\u955d\u955e\u955f\u9560\u9561\u9562\u9563\u9564\u9565\u9566\u9567\u9568\u9569\u956a\u956b\u956c\u956d\u956e\u956f\u9570\u9571\u9572\u9573\u9574\u9575\u9576\u9577\u9578\u9579\u957a\u957b\u957c\u957d\u957e\u957f\u9580\u9581\u9582\u9583\u9584\u9585\u9586\u9587\u9588\u9589\u958a\u958b\u958c\u958d\u958e\u958f\u9590\u9591\u9592\u9593\u9594\u9595\u9596\u9597\u9598\u9599\u959a\u959b\u959c\u959d\u959e\u959f\u95a0\u95a1\u95a2\u95a3\u95a4\u95a5\u95a6\u95a7\u95a8\u95a9\u95aa\u95ab\u95ac\u95ad\u95ae\u95af\u95b0\u95b1\u95b2\u95b3\u95b4\u95b5\u95b6\u95b7\u95b8\u95b9\u95ba\u95bb\u95bc\u95bd\u95be\u95bf\u95c0\u95c1\u95c2\u95c3\u95c4\u95c5\u95c6\u95c7\u95c8\u95c9\u95ca\u95cb\u95cc\u95cd\u95ce\u95cf\u95d0\u95d1\u95d2\u95d3\u95d4\u95d5\u95d6\u95d7\u95d8\u95d9\u95da\u95db\u95dc\u95dd\u95de\u95df\u95e0\u95e1\u95e2\u95e3\u95e4\u95e5\u95e6\u95e7\u95e8\u95e9\u95ea\u95eb\u95ec\u95ed\u95ee\u95ef\u95f0\u95f1\u95f2\u95f3\u95f4\u95f5\u95f6\u95f7\u95f8\u95f9\u95fa\u95fb\u95fc\u95fd\u95fe\u95ff\u9600\u9601\u9602\u9603\u9604\u9605\u9606\u9607\u9608\u9609\u960a\u960b\u960c\u960d\u960e\u960f\u9610\u9611\u9612\u9613\u9614\u9615\u9616\u9617\u9618\u9619\u961a\u961b\u961c\u961d\u961e\u961f\u9620\u9621\u9622\u9623\u9624\u9625\u9626\u9627\u9628\u9629\u962a\u962b\u962c\u962d\u962e\u962f\u9630\u9631\u9632\u9633\u9634\u9635\u9636\u9637\u9638\u9639\u963a\u963b\u963c\u963d\u963e\u963f\u9640\u9641\u9642\u9643\u9644\u9645\u9646\u9647\u9648\u9649\u964a\u964b\u964c\u964d\u964e\u964f\u9650\u9651\u9652\u9653\u9654\u9655\u9656\u9657\u9658\u9659\u965a\u965b\u965c\u965d\u965e\u965f\u9660\u9661\u9662\u9663\u9664\u9665\u9666\u9667\u9668\u9669\u966a\u966b\u966c\u966d\u966e\u966f\u9670\u9671\u9672\u9673\u9674\u9675\u9676\u9677\u9678\u9679\u967a\u967b\u967c\u967d\u967e\u967f\u9680\u9681\u9682\u9683\u9684\u9685\u9686\u9687\u9688\u9689\u968a\u968b\u968c\u968d\u968e\u968f\u9690\u9691\u9692\u9693\u9694\u9695\u9696\u9697\u9698\u9699\u969a\u969b\u969c\u969d\u969e\u969f\u96a0\u96a1\u96a2\u96a3\u96a4\u96a5\u96a6\u96a7\u96a8\u96a9\u96aa\u96ab\u96ac\u96ad\u96ae\u96af\u96b0\u96b1\u96b2\u96b3\u96b4\u96b5\u96b6\u96b7\u96b8\u96b9\u96ba\u96bb\u96bc\u96bd\u96be\u96bf\u96c0\u96c1\u96c2\u96c3\u96c4\u96c5\u96c6\u96c7\u96c8\u96c9\u96ca\u96cb\u96cc\u96cd\u96ce\u96cf\u96d0\u96d1\u96d2\u96d3\u96d4\u96d5\u96d6\u96d7\u96d8\u96d9\u96da\u96db\u96dc\u96dd\u96de\u96df\u96e0\u96e1\u96e2\u96e3\u96e4\u96e5\u96e6\u96e7\u96e8\u96e9\u96ea\u96eb\u96ec\u96ed\u96ee\u96ef\u96f0\u96f1\u96f2\u96f3\u96f4\u96f5\u96f6\u96f7\u96f8\u96f9\u96fa\u96fb\u96fc\u96fd\u96fe\u96ff\u9700\u9701\u9702\u9703\u9704\u9705\u9706\u9707\u9708\u9709\u970a\u970b\u970c\u970d\u970e\u970f\u9710\u9711\u9712\u9713\u9714\u9715\u9716\u9717\u9718\u9719\u971a\u971b\u971c\u971d\u971e\u971f\u9720\u9721\u9722\u9723\u9724\u9725\u9726\u9727\u9728\u9729\u972a\u972b\u972c\u972d\u972e\u972f\u9730\u9731\u9732\u9733\u9734\u9735\u9736\u9737\u9738\u9739\u973a\u973b\u973c\u973d\u973e\u973f\u9740\u9741\u9742\u9743\u9744\u9745\u9746\u9747\u9748\u9749\u974a\u974b\u974c\u974d\u974e\u974f\u9750\u9751\u9752\u9753\u9754\u9755\u9756\u9757\u9758\u9759\u975a\u975b\u975c\u975d\u975e\u975f\u9760\u9761\u9762\u9763\u9764\u9765\u9766\u9767\u9768\u9769\u976a\u976b\u976c\u976d\u976e\u976f\u9770\u9771\u9772\u9773\u9774\u9775\u9776\u9777\u9778\u9779\u977a\u977b\u977c\u977d\u977e\u977f\u9780\u9781\u9782\u9783\u9784\u9785\u9786\u9787\u9788\u9789\u978a\u978b\u978c\u978d\u978e\u978f\u9790\u9791\u9792\u9793\u9794\u9795\u9796\u9797\u9798\u9799\u979a\u979b\u979c\u979d\u979e\u979f\u97a0\u97a1\u97a2\u97a3\u97a4\u97a5\u97a6\u97a7\u97a8\u97a9\u97aa\u97ab\u97ac\u97ad\u97ae\u97af\u97b0\u97b1\u97b2\u97b3\u97b4\u97b5\u97b6\u97b7\u97b8\u97b9\u97ba\u97bb\u97bc\u97bd\u97be\u97bf\u97c0\u97c1\u97c2\u97c3\u97c4\u97c5\u97c6\u97c7\u97c8\u97c9\u97ca\u97cb\u97cc\u97cd\u97ce\u97cf\u97d0\u97d1\u97d2\u97d3\u97d4\u97d5\u97d6\u97d7\u97d8\u97d9\u97da\u97db\u97dc\u97dd\u97de\u97df\u97e0\u97e1\u97e2\u97e3\u97e4\u97e5\u97e6\u97e7\u97e8\u97e9\u97ea\u97eb\u97ec\u97ed\u97ee\u97ef\u97f0\u97f1\u97f2\u97f3\u97f4\u97f5\u97f6\u97f7\u97f8\u97f9\u97fa\u97fb\u97fc\u97fd\u97fe\u97ff\u9800\u9801\u9802\u9803\u9804\u9805\u9806\u9807\u9808\u9809\u980a\u980b\u980c\u980d\u980e\u980f\u9810\u9811\u9812\u9813\u9814\u9815\u9816\u9817\u9818\u9819\u981a\u981b\u981c\u981d\u981e\u981f\u9820\u9821\u9822\u9823\u9824\u9825\u9826\u9827\u9828\u9829\u982a\u982b\u982c\u982d\u982e\u982f\u9830\u9831\u9832\u9833\u9834\u9835\u9836\u9837\u9838\u9839\u983a\u983b\u983c\u983d\u983e\u983f\u9840\u9841\u9842\u9843\u9844\u9845\u9846\u9847\u9848\u9849\u984a\u984b\u984c\u984d\u984e\u984f\u9850\u9851\u9852\u9853\u9854\u9855\u9856\u9857\u9858\u9859\u985a\u985b\u985c\u985d\u985e\u985f\u9860\u9861\u9862\u9863\u9864\u9865\u9866\u9867\u9868\u9869\u986a\u986b\u986c\u986d\u986e\u986f\u9870\u9871\u9872\u9873\u9874\u9875\u9876\u9877\u9878\u9879\u987a\u987b\u987c\u987d\u987e\u987f\u9880\u9881\u9882\u9883\u9884\u9885\u9886\u9887\u9888\u9889\u988a\u988b\u988c\u988d\u988e\u988f\u9890\u9891\u9892\u9893\u9894\u9895\u9896\u9897\u9898\u9899\u989a\u989b\u989c\u989d\u989e\u989f\u98a0\u98a1\u98a2\u98a3\u98a4\u98a5\u98a6\u98a7\u98a8\u98a9\u98aa\u98ab\u98ac\u98ad\u98ae\u98af\u98b0\u98b1\u98b2\u98b3\u98b4\u98b5\u98b6\u98b7\u98b8\u98b9\u98ba\u98bb\u98bc\u98bd\u98be\u98bf\u98c0\u98c1\u98c2\u98c3\u98c4\u98c5\u98c6\u98c7\u98c8\u98c9\u98ca\u98cb\u98cc\u98cd\u98ce\u98cf\u98d0\u98d1\u98d2\u98d3\u98d4\u98d5\u98d6\u98d7\u98d8\u98d9\u98da\u98db\u98dc\u98dd\u98de\u98df\u98e0\u98e1\u98e2\u98e3\u98e4\u98e5\u98e6\u98e7\u98e8\u98e9\u98ea\u98eb\u98ec\u98ed\u98ee\u98ef\u98f0\u98f1\u98f2\u98f3\u98f4\u98f5\u98f6\u98f7\u98f8\u98f9\u98fa\u98fb\u98fc\u98fd\u98fe\u98ff\u9900\u9901\u9902\u9903\u9904\u9905\u9906\u9907\u9908\u9909\u990a\u990b\u990c\u990d\u990e\u990f\u9910\u9911\u9912\u9913\u9914\u9915\u9916\u9917\u9918\u9919\u991a\u991b\u991c\u991d\u991e\u991f\u9920\u9921\u9922\u9923\u9924\u9925\u9926\u9927\u9928\u9929\u992a\u992b\u992c\u992d\u992e\u992f\u9930\u9931\u9932\u9933\u9934\u9935\u9936\u9937\u9938\u9939\u993a\u993b\u993c\u993d\u993e\u993f\u9940\u9941\u9942\u9943\u9944\u9945\u9946\u9947\u9948\u9949\u994a\u994b\u994c\u994d\u994e\u994f\u9950\u9951\u9952\u9953\u9954\u9955\u9956\u9957\u9958\u9959\u995a\u995b\u995c\u995d\u995e\u995f\u9960\u9961\u9962\u9963\u9964\u9965\u9966\u9967\u9968\u9969\u996a\u996b\u996c\u996d\u996e\u996f\u9970\u9971\u9972\u9973\u9974\u9975\u9976\u9977\u9978\u9979\u997a\u997b\u997c\u997d\u997e\u997f\u9980\u9981\u9982\u9983\u9984\u9985\u9986\u9987\u9988\u9989\u998a\u998b\u998c\u998d\u998e\u998f\u9990\u9991\u9992\u9993\u9994\u9995\u9996\u9997\u9998\u9999\u999a\u999b\u999c\u999d\u999e\u999f\u99a0\u99a1\u99a2\u99a3\u99a4\u99a5\u99a6\u99a7\u99a8\u99a9\u99aa\u99ab\u99ac\u99ad\u99ae\u99af\u99b0\u99b1\u99b2\u99b3\u99b4\u99b5\u99b6\u99b7\u99b8\u99b9\u99ba\u99bb\u99bc\u99bd\u99be\u99bf\u99c0\u99c1\u99c2\u99c3\u99c4\u99c5\u99c6\u99c7\u99c8\u99c9\u99ca\u99cb\u99cc\u99cd\u99ce\u99cf\u99d0\u99d1\u99d2\u99d3\u99d4\u99d5\u99d6\u99d7\u99d8\u99d9\u99da\u99db\u99dc\u99dd\u99de\u99df\u99e0\u99e1\u99e2\u99e3\u99e4\u99e5\u99e6\u99e7\u99e8\u99e9\u99ea\u99eb\u99ec\u99ed\u99ee\u99ef\u99f0\u99f1\u99f2\u99f3\u99f4\u99f5\u99f6\u99f7\u99f8\u99f9\u99fa\u99fb\u99fc\u99fd\u99fe\u99ff\u9a00\u9a01\u9a02\u9a03\u9a04\u9a05\u9a06\u9a07\u9a08\u9a09\u9a0a\u9a0b\u9a0c\u9a0d\u9a0e\u9a0f\u9a10\u9a11\u9a12\u9a13\u9a14\u9a15\u9a16\u9a17\u9a18\u9a19\u9a1a\u9a1b\u9a1c\u9a1d\u9a1e\u9a1f\u9a20\u9a21\u9a22\u9a23\u9a24\u9a25\u9a26\u9a27\u9a28\u9a29\u9a2a\u9a2b\u9a2c\u9a2d\u9a2e\u9a2f\u9a30\u9a31\u9a32\u9a33\u9a34\u9a35\u9a36\u9a37\u9a38\u9a39\u9a3a\u9a3b\u9a3c\u9a3d\u9a3e\u9a3f\u9a40\u9a41\u9a42\u9a43\u9a44\u9a45\u9a46\u9a47\u9a48\u9a49\u9a4a\u9a4b\u9a4c\u9a4d\u9a4e\u9a4f\u9a50\u9a51\u9a52\u9a53\u9a54\u9a55\u9a56\u9a57\u9a58\u9a59\u9a5a\u9a5b\u9a5c\u9a5d\u9a5e\u9a5f\u9a60\u9a61\u9a62\u9a63\u9a64\u9a65\u9a66\u9a67\u9a68\u9a69\u9a6a\u9a6b\u9a6c\u9a6d\u9a6e\u9a6f\u9a70\u9a71\u9a72\u9a73\u9a74\u9a75\u9a76\u9a77\u9a78\u9a79\u9a7a\u9a7b\u9a7c\u9a7d\u9a7e\u9a7f\u9a80\u9a81\u9a82\u9a83\u9a84\u9a85\u9a86\u9a87\u9a88\u9a89\u9a8a\u9a8b\u9a8c\u9a8d\u9a8e\u9a8f\u9a90\u9a91\u9a92\u9a93\u9a94\u9a95\u9a96\u9a97\u9a98\u9a99\u9a9a\u9a9b\u9a9c\u9a9d\u9a9e\u9a9f\u9aa0\u9aa1\u9aa2\u9aa3\u9aa4\u9aa5\u9aa6\u9aa7\u9aa8\u9aa9\u9aaa\u9aab\u9aac\u9aad\u9aae\u9aaf\u9ab0\u9ab1\u9ab2\u9ab3\u9ab4\u9ab5\u9ab6\u9ab7\u9ab8\u9ab9\u9aba\u9abb\u9abc\u9abd\u9abe\u9abf\u9ac0\u9ac1\u9ac2\u9ac3\u9ac4\u9ac5\u9ac6\u9ac7\u9ac8\u9ac9\u9aca\u9acb\u9acc\u9acd\u9ace\u9acf\u9ad0\u9ad1\u9ad2\u9ad3\u9ad4\u9ad5\u9ad6\u9ad7\u9ad8\u9ad9\u9ada\u9adb\u9adc\u9add\u9ade\u9adf\u9ae0\u9ae1\u9ae2\u9ae3\u9ae4\u9ae5\u9ae6\u9ae7\u9ae8\u9ae9\u9aea\u9aeb\u9aec\u9aed\u9aee\u9aef\u9af0\u9af1\u9af2\u9af3\u9af4\u9af5\u9af6\u9af7\u9af8\u9af9\u9afa\u9afb\u9afc\u9afd\u9afe\u9aff\u9b00\u9b01\u9b02\u9b03\u9b04\u9b05\u9b06\u9b07\u9b08\u9b09\u9b0a\u9b0b\u9b0c\u9b0d\u9b0e\u9b0f\u9b10\u9b11\u9b12\u9b13\u9b14\u9b15\u9b16\u9b17\u9b18\u9b19\u9b1a\u9b1b\u9b1c\u9b1d\u9b1e\u9b1f\u9b20\u9b21\u9b22\u9b23\u9b24\u9b25\u9b26\u9b27\u9b28\u9b29\u9b2a\u9b2b\u9b2c\u9b2d\u9b2e\u9b2f\u9b30\u9b31\u9b32\u9b33\u9b34\u9b35\u9b36\u9b37\u9b38\u9b39\u9b3a\u9b3b\u9b3c\u9b3d\u9b3e\u9b3f\u9b40\u9b41\u9b42\u9b43\u9b44\u9b45\u9b46\u9b47\u9b48\u9b49\u9b4a\u9b4b\u9b4c\u9b4d\u9b4e\u9b4f\u9b50\u9b51\u9b52\u9b53\u9b54\u9b55\u9b56\u9b57\u9b58\u9b59\u9b5a\u9b5b\u9b5c\u9b5d\u9b5e\u9b5f\u9b60\u9b61\u9b62\u9b63\u9b64\u9b65\u9b66\u9b67\u9b68\u9b69\u9b6a\u9b6b\u9b6c\u9b6d\u9b6e\u9b6f\u9b70\u9b71\u9b72\u9b73\u9b74\u9b75\u9b76\u9b77\u9b78\u9b79\u9b7a\u9b7b\u9b7c\u9b7d\u9b7e\u9b7f\u9b80\u9b81\u9b82\u9b83\u9b84\u9b85\u9b86\u9b87\u9b88\u9b89\u9b8a\u9b8b\u9b8c\u9b8d\u9b8e\u9b8f\u9b90\u9b91\u9b92\u9b93\u9b94\u9b95\u9b96\u9b97\u9b98\u9b99\u9b9a\u9b9b\u9b9c\u9b9d\u9b9e\u9b9f\u9ba0\u9ba1\u9ba2\u9ba3\u9ba4\u9ba5\u9ba6\u9ba7\u9ba8\u9ba9\u9baa\u9bab\u9bac\u9bad\u9bae\u9baf\u9bb0\u9bb1\u9bb2\u9bb3\u9bb4\u9bb5\u9bb6\u9bb7\u9bb8\u9bb9\u9bba\u9bbb\u9bbc\u9bbd\u9bbe\u9bbf\u9bc0\u9bc1\u9bc2\u9bc3\u9bc4\u9bc5\u9bc6\u9bc7\u9bc8\u9bc9\u9bca\u9bcb\u9bcc\u9bcd\u9bce\u9bcf\u9bd0\u9bd1\u9bd2\u9bd3\u9bd4\u9bd5\u9bd6\u9bd7\u9bd8\u9bd9\u9bda\u9bdb\u9bdc\u9bdd\u9bde\u9bdf\u9be0\u9be1\u9be2\u9be3\u9be4\u9be5\u9be6\u9be7\u9be8\u9be9\u9bea\u9beb\u9bec\u9bed\u9bee\u9bef\u9bf0\u9bf1\u9bf2\u9bf3\u9bf4\u9bf5\u9bf6\u9bf7\u9bf8\u9bf9\u9bfa\u9bfb\u9bfc\u9bfd\u9bfe\u9bff\u9c00\u9c01\u9c02\u9c03\u9c04\u9c05\u9c06\u9c07\u9c08\u9c09\u9c0a\u9c0b\u9c0c\u9c0d\u9c0e\u9c0f\u9c10\u9c11\u9c12\u9c13\u9c14\u9c15\u9c16\u9c17\u9c18\u9c19\u9c1a\u9c1b\u9c1c\u9c1d\u9c1e\u9c1f\u9c20\u9c21\u9c22\u9c23\u9c24\u9c25\u9c26\u9c27\u9c28\u9c29\u9c2a\u9c2b\u9c2c\u9c2d\u9c2e\u9c2f\u9c30\u9c31\u9c32\u9c33\u9c34\u9c35\u9c36\u9c37\u9c38\u9c39\u9c3a\u9c3b\u9c3c\u9c3d\u9c3e\u9c3f\u9c40\u9c41\u9c42\u9c43\u9c44\u9c45\u9c46\u9c47\u9c48\u9c49\u9c4a\u9c4b\u9c4c\u9c4d\u9c4e\u9c4f\u9c50\u9c51\u9c52\u9c53\u9c54\u9c55\u9c56\u9c57\u9c58\u9c59\u9c5a\u9c5b\u9c5c\u9c5d\u9c5e\u9c5f\u9c60\u9c61\u9c62\u9c63\u9c64\u9c65\u9c66\u9c67\u9c68\u9c69\u9c6a\u9c6b\u9c6c\u9c6d\u9c6e\u9c6f\u9c70\u9c71\u9c72\u9c73\u9c74\u9c75\u9c76\u9c77\u9c78\u9c79\u9c7a\u9c7b\u9c7c\u9c7d\u9c7e\u9c7f\u9c80\u9c81\u9c82\u9c83\u9c84\u9c85\u9c86\u9c87\u9c88\u9c89\u9c8a\u9c8b\u9c8c\u9c8d\u9c8e\u9c8f\u9c90\u9c91\u9c92\u9c93\u9c94\u9c95\u9c96\u9c97\u9c98\u9c99\u9c9a\u9c9b\u9c9c\u9c9d\u9c9e\u9c9f\u9ca0\u9ca1\u9ca2\u9ca3\u9ca4\u9ca5\u9ca6\u9ca7\u9ca8\u9ca9\u9caa\u9cab\u9cac\u9cad\u9cae\u9caf\u9cb0\u9cb1\u9cb2\u9cb3\u9cb4\u9cb5\u9cb6\u9cb7\u9cb8\u9cb9\u9cba\u9cbb\u9cbc\u9cbd\u9cbe\u9cbf\u9cc0\u9cc1\u9cc2\u9cc3\u9cc4\u9cc5\u9cc6\u9cc7\u9cc8\u9cc9\u9cca\u9ccb\u9ccc\u9ccd\u9cce\u9ccf\u9cd0\u9cd1\u9cd2\u9cd3\u9cd4\u9cd5\u9cd6\u9cd7\u9cd8\u9cd9\u9cda\u9cdb\u9cdc\u9cdd\u9cde\u9cdf\u9ce0\u9ce1\u9ce2\u9ce3\u9ce4\u9ce5\u9ce6\u9ce7\u9ce8\u9ce9\u9cea\u9ceb\u9cec\u9ced\u9cee\u9cef\u9cf0\u9cf1\u9cf2\u9cf3\u9cf4\u9cf5\u9cf6\u9cf7\u9cf8\u9cf9\u9cfa\u9cfb\u9cfc\u9cfd\u9cfe\u9cff\u9d00\u9d01\u9d02\u9d03\u9d04\u9d05\u9d06\u9d07\u9d08\u9d09\u9d0a\u9d0b\u9d0c\u9d0d\u9d0e\u9d0f\u9d10\u9d11\u9d12\u9d13\u9d14\u9d15\u9d16\u9d17\u9d18\u9d19\u9d1a\u9d1b\u9d1c\u9d1d\u9d1e\u9d1f\u9d20\u9d21\u9d22\u9d23\u9d24\u9d25\u9d26\u9d27\u9d28\u9d29\u9d2a\u9d2b\u9d2c\u9d2d\u9d2e\u9d2f\u9d30\u9d31\u9d32\u9d33\u9d34\u9d35\u9d36\u9d37\u9d38\u9d39\u9d3a\u9d3b\u9d3c\u9d3d\u9d3e\u9d3f\u9d40\u9d41\u9d42\u9d43\u9d44\u9d45\u9d46\u9d47\u9d48\u9d49\u9d4a\u9d4b\u9d4c\u9d4d\u9d4e\u9d4f\u9d50\u9d51\u9d52\u9d53\u9d54\u9d55\u9d56\u9d57\u9d58\u9d59\u9d5a\u9d5b\u9d5c\u9d5d\u9d5e\u9d5f\u9d60\u9d61\u9d62\u9d63\u9d64\u9d65\u9d66\u9d67\u9d68\u9d69\u9d6a\u9d6b\u9d6c\u9d6d\u9d6e\u9d6f\u9d70\u9d71\u9d72\u9d73\u9d74\u9d75\u9d76\u9d77\u9d78\u9d79\u9d7a\u9d7b\u9d7c\u9d7d\u9d7e\u9d7f\u9d80\u9d81\u9d82\u9d83\u9d84\u9d85\u9d86\u9d87\u9d88\u9d89\u9d8a\u9d8b\u9d8c\u9d8d\u9d8e\u9d8f\u9d90\u9d91\u9d92\u9d93\u9d94\u9d95\u9d96\u9d97\u9d98\u9d99\u9d9a\u9d9b\u9d9c\u9d9d\u9d9e\u9d9f\u9da0\u9da1\u9da2\u9da3\u9da4\u9da5\u9da6\u9da7\u9da8\u9da9\u9daa\u9dab\u9dac\u9dad\u9dae\u9daf\u9db0\u9db1\u9db2\u9db3\u9db4\u9db5\u9db6\u9db7\u9db8\u9db9\u9dba\u9dbb\u9dbc\u9dbd\u9dbe\u9dbf\u9dc0\u9dc1\u9dc2\u9dc3\u9dc4\u9dc5\u9dc6\u9dc7\u9dc8\u9dc9\u9dca\u9dcb\u9dcc\u9dcd\u9dce\u9dcf\u9dd0\u9dd1\u9dd2\u9dd3\u9dd4\u9dd5\u9dd6\u9dd7\u9dd8\u9dd9\u9dda\u9ddb\u9ddc\u9ddd\u9dde\u9ddf\u9de0\u9de1\u9de2\u9de3\u9de4\u9de5\u9de6\u9de7\u9de8\u9de9\u9dea\u9deb\u9dec\u9ded\u9dee\u9def\u9df0\u9df1\u9df2\u9df3\u9df4\u9df5\u9df6\u9df7\u9df8\u9df9\u9dfa\u9dfb\u9dfc\u9dfd\u9dfe\u9dff\u9e00\u9e01\u9e02\u9e03\u9e04\u9e05\u9e06\u9e07\u9e08\u9e09\u9e0a\u9e0b\u9e0c\u9e0d\u9e0e\u9e0f\u9e10\u9e11\u9e12\u9e13\u9e14\u9e15\u9e16\u9e17\u9e18\u9e19\u9e1a\u9e1b\u9e1c\u9e1d\u9e1e\u9e1f\u9e20\u9e21\u9e22\u9e23\u9e24\u9e25\u9e26\u9e27\u9e28\u9e29\u9e2a\u9e2b\u9e2c\u9e2d\u9e2e\u9e2f\u9e30\u9e31\u9e32\u9e33\u9e34\u9e35\u9e36\u9e37\u9e38\u9e39\u9e3a\u9e3b\u9e3c\u9e3d\u9e3e\u9e3f\u9e40\u9e41\u9e42\u9e43\u9e44\u9e45\u9e46\u9e47\u9e48\u9e49\u9e4a\u9e4b\u9e4c\u9e4d\u9e4e\u9e4f\u9e50\u9e51\u9e52\u9e53\u9e54\u9e55\u9e56\u9e57\u9e58\u9e59\u9e5a\u9e5b\u9e5c\u9e5d\u9e5e\u9e5f\u9e60\u9e61\u9e62\u9e63\u9e64\u9e65\u9e66\u9e67\u9e68\u9e69\u9e6a\u9e6b\u9e6c\u9e6d\u9e6e\u9e6f\u9e70\u9e71\u9e72\u9e73\u9e74\u9e75\u9e76\u9e77\u9e78\u9e79\u9e7a\u9e7b\u9e7c\u9e7d\u9e7e\u9e7f\u9e80\u9e81\u9e82\u9e83\u9e84\u9e85\u9e86\u9e87\u9e88\u9e89\u9e8a\u9e8b\u9e8c\u9e8d\u9e8e\u9e8f\u9e90\u9e91\u9e92\u9e93\u9e94\u9e95\u9e96\u9e97\u9e98\u9e99\u9e9a\u9e9b\u9e9c\u9e9d\u9e9e\u9e9f\u9ea0\u9ea1\u9ea2\u9ea3\u9ea4\u9ea5\u9ea6\u9ea7\u9ea8\u9ea9\u9eaa\u9eab\u9eac\u9ead\u9eae\u9eaf\u9eb0\u9eb1\u9eb2\u9eb3\u9eb4\u9eb5\u9eb6\u9eb7\u9eb8\u9eb9\u9eba\u9ebb\u9ebc\u9ebd\u9ebe\u9ebf\u9ec0\u9ec1\u9ec2\u9ec3\u9ec4\u9ec5\u9ec6\u9ec7\u9ec8\u9ec9\u9eca\u9ecb\u9ecc\u9ecd\u9ece\u9ecf\u9ed0\u9ed1\u9ed2\u9ed3\u9ed4\u9ed5\u9ed6\u9ed7\u9ed8\u9ed9\u9eda\u9edb\u9edc\u9edd\u9ede\u9edf\u9ee0\u9ee1\u9ee2\u9ee3\u9ee4\u9ee5\u9ee6\u9ee7\u9ee8\u9ee9\u9eea\u9eeb\u9eec\u9eed\u9eee\u9eef\u9ef0\u9ef1\u9ef2\u9ef3\u9ef4\u9ef5\u9ef6\u9ef7\u9ef8\u9ef9\u9efa\u9efb\u9efc\u9efd\u9efe\u9eff\u9f00\u9f01\u9f02\u9f03\u9f04\u9f05\u9f06\u9f07\u9f08\u9f09\u9f0a\u9f0b\u9f0c\u9f0d\u9f0e\u9f0f\u9f10\u9f11\u9f12\u9f13\u9f14\u9f15\u9f16\u9f17\u9f18\u9f19\u9f1a\u9f1b\u9f1c\u9f1d\u9f1e\u9f1f\u9f20\u9f21\u9f22\u9f23\u9f24\u9f25\u9f26\u9f27\u9f28\u9f29\u9f2a\u9f2b\u9f2c\u9f2d\u9f2e\u9f2f\u9f30\u9f31\u9f32\u9f33\u9f34\u9f35\u9f36\u9f37\u9f38\u9f39\u9f3a\u9f3b\u9f3c\u9f3d\u9f3e\u9f3f\u9f40\u9f41\u9f42\u9f43\u9f44\u9f45\u9f46\u9f47\u9f48\u9f49\u9f4a\u9f4b\u9f4c\u9f4d\u9f4e\u9f4f\u9f50\u9f51\u9f52\u9f53\u9f54\u9f55\u9f56\u9f57\u9f58\u9f59\u9f5a\u9f5b\u9f5c\u9f5d\u9f5e\u9f5f\u9f60\u9f61\u9f62\u9f63\u9f64\u9f65\u9f66\u9f67\u9f68\u9f69\u9f6a\u9f6b\u9f6c\u9f6d\u9f6e\u9f6f\u9f70\u9f71\u9f72\u9f73\u9f74\u9f75\u9f76\u9f77\u9f78\u9f79\u9f7a\u9f7b\u9f7c\u9f7d\u9f7e\u9f7f\u9f80\u9f81\u9f82\u9f83\u9f84\u9f85\u9f86\u9f87\u9f88\u9f89\u9f8a\u9f8b\u9f8c\u9f8d\u9f8e\u9f8f\u9f90\u9f91\u9f92\u9f93\u9f94\u9f95\u9f96\u9f97\u9f98\u9f99\u9f9a\u9f9b\u9f9c\u9f9d\u9f9e\u9f9f\u9fa0\u9fa1\u9fa2\u9fa3\u9fa4\u9fa5\u9fa6\u9fa7\u9fa8\u9fa9\u9faa\u9fab\u9fac\u9fad\u9fae\u9faf\u9fb0\u9fb1\u9fb2\u9fb3\u9fb4\u9fb5\u9fb6\u9fb7\u9fb8\u9fb9\u9fba\u9fbb\ua000\ua001\ua002\ua003\ua004\ua005\ua006\ua007\ua008\ua009\ua00a\ua00b\ua00c\ua00d\ua00e\ua00f\ua010\ua011\ua012\ua013\ua014\ua016\ua017\ua018\ua019\ua01a\ua01b\ua01c\ua01d\ua01e\ua01f\ua020\ua021\ua022\ua023\ua024\ua025\ua026\ua027\ua028\ua029\ua02a\ua02b\ua02c\ua02d\ua02e\ua02f\ua030\ua031\ua032\ua033\ua034\ua035\ua036\ua037\ua038\ua039\ua03a\ua03b\ua03c\ua03d\ua03e\ua03f\ua040\ua041\ua042\ua043\ua044\ua045\ua046\ua047\ua048\ua049\ua04a\ua04b\ua04c\ua04d\ua04e\ua04f\ua050\ua051\ua052\ua053\ua054\ua055\ua056\ua057\ua058\ua059\ua05a\ua05b\ua05c\ua05d\ua05e\ua05f\ua060\ua061\ua062\ua063\ua064\ua065\ua066\ua067\ua068\ua069\ua06a\ua06b\ua06c\ua06d\ua06e\ua06f\ua070\ua071\ua072\ua073\ua074\ua075\ua076\ua077\ua078\ua079\ua07a\ua07b\ua07c\ua07d\ua07e\ua07f\ua080\ua081\ua082\ua083\ua084\ua085\ua086\ua087\ua088\ua089\ua08a\ua08b\ua08c\ua08d\ua08e\ua08f\ua090\ua091\ua092\ua093\ua094\ua095\ua096\ua097\ua098\ua099\ua09a\ua09b\ua09c\ua09d\ua09e\ua09f\ua0a0\ua0a1\ua0a2\ua0a3\ua0a4\ua0a5\ua0a6\ua0a7\ua0a8\ua0a9\ua0aa\ua0ab\ua0ac\ua0ad\ua0ae\ua0af\ua0b0\ua0b1\ua0b2\ua0b3\ua0b4\ua0b5\ua0b6\ua0b7\ua0b8\ua0b9\ua0ba\ua0bb\ua0bc\ua0bd\ua0be\ua0bf\ua0c0\ua0c1\ua0c2\ua0c3\ua0c4\ua0c5\ua0c6\ua0c7\ua0c8\ua0c9\ua0ca\ua0cb\ua0cc\ua0cd\ua0ce\ua0cf\ua0d0\ua0d1\ua0d2\ua0d3\ua0d4\ua0d5\ua0d6\ua0d7\ua0d8\ua0d9\ua0da\ua0db\ua0dc\ua0dd\ua0de\ua0df\ua0e0\ua0e1\ua0e2\ua0e3\ua0e4\ua0e5\ua0e6\ua0e7\ua0e8\ua0e9\ua0ea\ua0eb\ua0ec\ua0ed\ua0ee\ua0ef\ua0f0\ua0f1\ua0f2\ua0f3\ua0f4\ua0f5\ua0f6\ua0f7\ua0f8\ua0f9\ua0fa\ua0fb\ua0fc\ua0fd\ua0fe\ua0ff\ua100\ua101\ua102\ua103\ua104\ua105\ua106\ua107\ua108\ua109\ua10a\ua10b\ua10c\ua10d\ua10e\ua10f\ua110\ua111\ua112\ua113\ua114\ua115\ua116\ua117\ua118\ua119\ua11a\ua11b\ua11c\ua11d\ua11e\ua11f\ua120\ua121\ua122\ua123\ua124\ua125\ua126\ua127\ua128\ua129\ua12a\ua12b\ua12c\ua12d\ua12e\ua12f\ua130\ua131\ua132\ua133\ua134\ua135\ua136\ua137\ua138\ua139\ua13a\ua13b\ua13c\ua13d\ua13e\ua13f\ua140\ua141\ua142\ua143\ua144\ua145\ua146\ua147\ua148\ua149\ua14a\ua14b\ua14c\ua14d\ua14e\ua14f\ua150\ua151\ua152\ua153\ua154\ua155\ua156\ua157\ua158\ua159\ua15a\ua15b\ua15c\ua15d\ua15e\ua15f\ua160\ua161\ua162\ua163\ua164\ua165\ua166\ua167\ua168\ua169\ua16a\ua16b\ua16c\ua16d\ua16e\ua16f\ua170\ua171\ua172\ua173\ua174\ua175\ua176\ua177\ua178\ua179\ua17a\ua17b\ua17c\ua17d\ua17e\ua17f\ua180\ua181\ua182\ua183\ua184\ua185\ua186\ua187\ua188\ua189\ua18a\ua18b\ua18c\ua18d\ua18e\ua18f\ua190\ua191\ua192\ua193\ua194\ua195\ua196\ua197\ua198\ua199\ua19a\ua19b\ua19c\ua19d\ua19e\ua19f\ua1a0\ua1a1\ua1a2\ua1a3\ua1a4\ua1a5\ua1a6\ua1a7\ua1a8\ua1a9\ua1aa\ua1ab\ua1ac\ua1ad\ua1ae\ua1af\ua1b0\ua1b1\ua1b2\ua1b3\ua1b4\ua1b5\ua1b6\ua1b7\ua1b8\ua1b9\ua1ba\ua1bb\ua1bc\ua1bd\ua1be\ua1bf\ua1c0\ua1c1\ua1c2\ua1c3\ua1c4\ua1c5\ua1c6\ua1c7\ua1c8\ua1c9\ua1ca\ua1cb\ua1cc\ua1cd\ua1ce\ua1cf\ua1d0\ua1d1\ua1d2\ua1d3\ua1d4\ua1d5\ua1d6\ua1d7\ua1d8\ua1d9\ua1da\ua1db\ua1dc\ua1dd\ua1de\ua1df\ua1e0\ua1e1\ua1e2\ua1e3\ua1e4\ua1e5\ua1e6\ua1e7\ua1e8\ua1e9\ua1ea\ua1eb\ua1ec\ua1ed\ua1ee\ua1ef\ua1f0\ua1f1\ua1f2\ua1f3\ua1f4\ua1f5\ua1f6\ua1f7\ua1f8\ua1f9\ua1fa\ua1fb\ua1fc\ua1fd\ua1fe\ua1ff\ua200\ua201\ua202\ua203\ua204\ua205\ua206\ua207\ua208\ua209\ua20a\ua20b\ua20c\ua20d\ua20e\ua20f\ua210\ua211\ua212\ua213\ua214\ua215\ua216\ua217\ua218\ua219\ua21a\ua21b\ua21c\ua21d\ua21e\ua21f\ua220\ua221\ua222\ua223\ua224\ua225\ua226\ua227\ua228\ua229\ua22a\ua22b\ua22c\ua22d\ua22e\ua22f\ua230\ua231\ua232\ua233\ua234\ua235\ua236\ua237\ua238\ua239\ua23a\ua23b\ua23c\ua23d\ua23e\ua23f\ua240\ua241\ua242\ua243\ua244\ua245\ua246\ua247\ua248\ua249\ua24a\ua24b\ua24c\ua24d\ua24e\ua24f\ua250\ua251\ua252\ua253\ua254\ua255\ua256\ua257\ua258\ua259\ua25a\ua25b\ua25c\ua25d\ua25e\ua25f\ua260\ua261\ua262\ua263\ua264\ua265\ua266\ua267\ua268\ua269\ua26a\ua26b\ua26c\ua26d\ua26e\ua26f\ua270\ua271\ua272\ua273\ua274\ua275\ua276\ua277\ua278\ua279\ua27a\ua27b\ua27c\ua27d\ua27e\ua27f\ua280\ua281\ua282\ua283\ua284\ua285\ua286\ua287\ua288\ua289\ua28a\ua28b\ua28c\ua28d\ua28e\ua28f\ua290\ua291\ua292\ua293\ua294\ua295\ua296\ua297\ua298\ua299\ua29a\ua29b\ua29c\ua29d\ua29e\ua29f\ua2a0\ua2a1\ua2a2\ua2a3\ua2a4\ua2a5\ua2a6\ua2a7\ua2a8\ua2a9\ua2aa\ua2ab\ua2ac\ua2ad\ua2ae\ua2af\ua2b0\ua2b1\ua2b2\ua2b3\ua2b4\ua2b5\ua2b6\ua2b7\ua2b8\ua2b9\ua2ba\ua2bb\ua2bc\ua2bd\ua2be\ua2bf\ua2c0\ua2c1\ua2c2\ua2c3\ua2c4\ua2c5\ua2c6\ua2c7\ua2c8\ua2c9\ua2ca\ua2cb\ua2cc\ua2cd\ua2ce\ua2cf\ua2d0\ua2d1\ua2d2\ua2d3\ua2d4\ua2d5\ua2d6\ua2d7\ua2d8\ua2d9\ua2da\ua2db\ua2dc\ua2dd\ua2de\ua2df\ua2e0\ua2e1\ua2e2\ua2e3\ua2e4\ua2e5\ua2e6\ua2e7\ua2e8\ua2e9\ua2ea\ua2eb\ua2ec\ua2ed\ua2ee\ua2ef\ua2f0\ua2f1\ua2f2\ua2f3\ua2f4\ua2f5\ua2f6\ua2f7\ua2f8\ua2f9\ua2fa\ua2fb\ua2fc\ua2fd\ua2fe\ua2ff\ua300\ua301\ua302\ua303\ua304\ua305\ua306\ua307\ua308\ua309\ua30a\ua30b\ua30c\ua30d\ua30e\ua30f\ua310\ua311\ua312\ua313\ua314\ua315\ua316\ua317\ua318\ua319\ua31a\ua31b\ua31c\ua31d\ua31e\ua31f\ua320\ua321\ua322\ua323\ua324\ua325\ua326\ua327\ua328\ua329\ua32a\ua32b\ua32c\ua32d\ua32e\ua32f\ua330\ua331\ua332\ua333\ua334\ua335\ua336\ua337\ua338\ua339\ua33a\ua33b\ua33c\ua33d\ua33e\ua33f\ua340\ua341\ua342\ua343\ua344\ua345\ua346\ua347\ua348\ua349\ua34a\ua34b\ua34c\ua34d\ua34e\ua34f\ua350\ua351\ua352\ua353\ua354\ua355\ua356\ua357\ua358\ua359\ua35a\ua35b\ua35c\ua35d\ua35e\ua35f\ua360\ua361\ua362\ua363\ua364\ua365\ua366\ua367\ua368\ua369\ua36a\ua36b\ua36c\ua36d\ua36e\ua36f\ua370\ua371\ua372\ua373\ua374\ua375\ua376\ua377\ua378\ua379\ua37a\ua37b\ua37c\ua37d\ua37e\ua37f\ua380\ua381\ua382\ua383\ua384\ua385\ua386\ua387\ua388\ua389\ua38a\ua38b\ua38c\ua38d\ua38e\ua38f\ua390\ua391\ua392\ua393\ua394\ua395\ua396\ua397\ua398\ua399\ua39a\ua39b\ua39c\ua39d\ua39e\ua39f\ua3a0\ua3a1\ua3a2\ua3a3\ua3a4\ua3a5\ua3a6\ua3a7\ua3a8\ua3a9\ua3aa\ua3ab\ua3ac\ua3ad\ua3ae\ua3af\ua3b0\ua3b1\ua3b2\ua3b3\ua3b4\ua3b5\ua3b6\ua3b7\ua3b8\ua3b9\ua3ba\ua3bb\ua3bc\ua3bd\ua3be\ua3bf\ua3c0\ua3c1\ua3c2\ua3c3\ua3c4\ua3c5\ua3c6\ua3c7\ua3c8\ua3c9\ua3ca\ua3cb\ua3cc\ua3cd\ua3ce\ua3cf\ua3d0\ua3d1\ua3d2\ua3d3\ua3d4\ua3d5\ua3d6\ua3d7\ua3d8\ua3d9\ua3da\ua3db\ua3dc\ua3dd\ua3de\ua3df\ua3e0\ua3e1\ua3e2\ua3e3\ua3e4\ua3e5\ua3e6\ua3e7\ua3e8\ua3e9\ua3ea\ua3eb\ua3ec\ua3ed\ua3ee\ua3ef\ua3f0\ua3f1\ua3f2\ua3f3\ua3f4\ua3f5\ua3f6\ua3f7\ua3f8\ua3f9\ua3fa\ua3fb\ua3fc\ua3fd\ua3fe\ua3ff\ua400\ua401\ua402\ua403\ua404\ua405\ua406\ua407\ua408\ua409\ua40a\ua40b\ua40c\ua40d\ua40e\ua40f\ua410\ua411\ua412\ua413\ua414\ua415\ua416\ua417\ua418\ua419\ua41a\ua41b\ua41c\ua41d\ua41e\ua41f\ua420\ua421\ua422\ua423\ua424\ua425\ua426\ua427\ua428\ua429\ua42a\ua42b\ua42c\ua42d\ua42e\ua42f\ua430\ua431\ua432\ua433\ua434\ua435\ua436\ua437\ua438\ua439\ua43a\ua43b\ua43c\ua43d\ua43e\ua43f\ua440\ua441\ua442\ua443\ua444\ua445\ua446\ua447\ua448\ua449\ua44a\ua44b\ua44c\ua44d\ua44e\ua44f\ua450\ua451\ua452\ua453\ua454\ua455\ua456\ua457\ua458\ua459\ua45a\ua45b\ua45c\ua45d\ua45e\ua45f\ua460\ua461\ua462\ua463\ua464\ua465\ua466\ua467\ua468\ua469\ua46a\ua46b\ua46c\ua46d\ua46e\ua46f\ua470\ua471\ua472\ua473\ua474\ua475\ua476\ua477\ua478\ua479\ua47a\ua47b\ua47c\ua47d\ua47e\ua47f\ua480\ua481\ua482\ua483\ua484\ua485\ua486\ua487\ua488\ua489\ua48a\ua48b\ua48c\ua800\ua801\ua803\ua804\ua805\ua807\ua808\ua809\ua80a\ua80c\ua80d\ua80e\ua80f\ua810\ua811\ua812\ua813\ua814\ua815\ua816\ua817\ua818\ua819\ua81a\ua81b\ua81c\ua81d\ua81e\ua81f\ua820\ua821\ua822\uac00\uac01\uac02\uac03\uac04\uac05\uac06\uac07\uac08\uac09\uac0a\uac0b\uac0c\uac0d\uac0e\uac0f\uac10\uac11\uac12\uac13\uac14\uac15\uac16\uac17\uac18\uac19\uac1a\uac1b\uac1c\uac1d\uac1e\uac1f\uac20\uac21\uac22\uac23\uac24\uac25\uac26\uac27\uac28\uac29\uac2a\uac2b\uac2c\uac2d\uac2e\uac2f\uac30\uac31\uac32\uac33\uac34\uac35\uac36\uac37\uac38\uac39\uac3a\uac3b\uac3c\uac3d\uac3e\uac3f\uac40\uac41\uac42\uac43\uac44\uac45\uac46\uac47\uac48\uac49\uac4a\uac4b\uac4c\uac4d\uac4e\uac4f\uac50\uac51\uac52\uac53\uac54\uac55\uac56\uac57\uac58\uac59\uac5a\uac5b\uac5c\uac5d\uac5e\uac5f\uac60\uac61\uac62\uac63\uac64\uac65\uac66\uac67\uac68\uac69\uac6a\uac6b\uac6c\uac6d\uac6e\uac6f\uac70\uac71\uac72\uac73\uac74\uac75\uac76\uac77\uac78\uac79\uac7a\uac7b\uac7c\uac7d\uac7e\uac7f\uac80\uac81\uac82\uac83\uac84\uac85\uac86\uac87\uac88\uac89\uac8a\uac8b\uac8c\uac8d\uac8e\uac8f\uac90\uac91\uac92\uac93\uac94\uac95\uac96\uac97\uac98\uac99\uac9a\uac9b\uac9c\uac9d\uac9e\uac9f\uaca0\uaca1\uaca2\uaca3\uaca4\uaca5\uaca6\uaca7\uaca8\uaca9\uacaa\uacab\uacac\uacad\uacae\uacaf\uacb0\uacb1\uacb2\uacb3\uacb4\uacb5\uacb6\uacb7\uacb8\uacb9\uacba\uacbb\uacbc\uacbd\uacbe\uacbf\uacc0\uacc1\uacc2\uacc3\uacc4\uacc5\uacc6\uacc7\uacc8\uacc9\uacca\uaccb\uaccc\uaccd\uacce\uaccf\uacd0\uacd1\uacd2\uacd3\uacd4\uacd5\uacd6\uacd7\uacd8\uacd9\uacda\uacdb\uacdc\uacdd\uacde\uacdf\uace0\uace1\uace2\uace3\uace4\uace5\uace6\uace7\uace8\uace9\uacea\uaceb\uacec\uaced\uacee\uacef\uacf0\uacf1\uacf2\uacf3\uacf4\uacf5\uacf6\uacf7\uacf8\uacf9\uacfa\uacfb\uacfc\uacfd\uacfe\uacff\uad00\uad01\uad02\uad03\uad04\uad05\uad06\uad07\uad08\uad09\uad0a\uad0b\uad0c\uad0d\uad0e\uad0f\uad10\uad11\uad12\uad13\uad14\uad15\uad16\uad17\uad18\uad19\uad1a\uad1b\uad1c\uad1d\uad1e\uad1f\uad20\uad21\uad22\uad23\uad24\uad25\uad26\uad27\uad28\uad29\uad2a\uad2b\uad2c\uad2d\uad2e\uad2f\uad30\uad31\uad32\uad33\uad34\uad35\uad36\uad37\uad38\uad39\uad3a\uad3b\uad3c\uad3d\uad3e\uad3f\uad40\uad41\uad42\uad43\uad44\uad45\uad46\uad47\uad48\uad49\uad4a\uad4b\uad4c\uad4d\uad4e\uad4f\uad50\uad51\uad52\uad53\uad54\uad55\uad56\uad57\uad58\uad59\uad5a\uad5b\uad5c\uad5d\uad5e\uad5f\uad60\uad61\uad62\uad63\uad64\uad65\uad66\uad67\uad68\uad69\uad6a\uad6b\uad6c\uad6d\uad6e\uad6f\uad70\uad71\uad72\uad73\uad74\uad75\uad76\uad77\uad78\uad79\uad7a\uad7b\uad7c\uad7d\uad7e\uad7f\uad80\uad81\uad82\uad83\uad84\uad85\uad86\uad87\uad88\uad89\uad8a\uad8b\uad8c\uad8d\uad8e\uad8f\uad90\uad91\uad92\uad93\uad94\uad95\uad96\uad97\uad98\uad99\uad9a\uad9b\uad9c\uad9d\uad9e\uad9f\uada0\uada1\uada2\uada3\uada4\uada5\uada6\uada7\uada8\uada9\uadaa\uadab\uadac\uadad\uadae\uadaf\uadb0\uadb1\uadb2\uadb3\uadb4\uadb5\uadb6\uadb7\uadb8\uadb9\uadba\uadbb\uadbc\uadbd\uadbe\uadbf\uadc0\uadc1\uadc2\uadc3\uadc4\uadc5\uadc6\uadc7\uadc8\uadc9\uadca\uadcb\uadcc\uadcd\uadce\uadcf\uadd0\uadd1\uadd2\uadd3\uadd4\uadd5\uadd6\uadd7\uadd8\uadd9\uadda\uaddb\uaddc\uaddd\uadde\uaddf\uade0\uade1\uade2\uade3\uade4\uade5\uade6\uade7\uade8\uade9\uadea\uadeb\uadec\uaded\uadee\uadef\uadf0\uadf1\uadf2\uadf3\uadf4\uadf5\uadf6\uadf7\uadf8\uadf9\uadfa\uadfb\uadfc\uadfd\uadfe\uadff\uae00\uae01\uae02\uae03\uae04\uae05\uae06\uae07\uae08\uae09\uae0a\uae0b\uae0c\uae0d\uae0e\uae0f\uae10\uae11\uae12\uae13\uae14\uae15\uae16\uae17\uae18\uae19\uae1a\uae1b\uae1c\uae1d\uae1e\uae1f\uae20\uae21\uae22\uae23\uae24\uae25\uae26\uae27\uae28\uae29\uae2a\uae2b\uae2c\uae2d\uae2e\uae2f\uae30\uae31\uae32\uae33\uae34\uae35\uae36\uae37\uae38\uae39\uae3a\uae3b\uae3c\uae3d\uae3e\uae3f\uae40\uae41\uae42\uae43\uae44\uae45\uae46\uae47\uae48\uae49\uae4a\uae4b\uae4c\uae4d\uae4e\uae4f\uae50\uae51\uae52\uae53\uae54\uae55\uae56\uae57\uae58\uae59\uae5a\uae5b\uae5c\uae5d\uae5e\uae5f\uae60\uae61\uae62\uae63\uae64\uae65\uae66\uae67\uae68\uae69\uae6a\uae6b\uae6c\uae6d\uae6e\uae6f\uae70\uae71\uae72\uae73\uae74\uae75\uae76\uae77\uae78\uae79\uae7a\uae7b\uae7c\uae7d\uae7e\uae7f\uae80\uae81\uae82\uae83\uae84\uae85\uae86\uae87\uae88\uae89\uae8a\uae8b\uae8c\uae8d\uae8e\uae8f\uae90\uae91\uae92\uae93\uae94\uae95\uae96\uae97\uae98\uae99\uae9a\uae9b\uae9c\uae9d\uae9e\uae9f\uaea0\uaea1\uaea2\uaea3\uaea4\uaea5\uaea6\uaea7\uaea8\uaea9\uaeaa\uaeab\uaeac\uaead\uaeae\uaeaf\uaeb0\uaeb1\uaeb2\uaeb3\uaeb4\uaeb5\uaeb6\uaeb7\uaeb8\uaeb9\uaeba\uaebb\uaebc\uaebd\uaebe\uaebf\uaec0\uaec1\uaec2\uaec3\uaec4\uaec5\uaec6\uaec7\uaec8\uaec9\uaeca\uaecb\uaecc\uaecd\uaece\uaecf\uaed0\uaed1\uaed2\uaed3\uaed4\uaed5\uaed6\uaed7\uaed8\uaed9\uaeda\uaedb\uaedc\uaedd\uaede\uaedf\uaee0\uaee1\uaee2\uaee3\uaee4\uaee5\uaee6\uaee7\uaee8\uaee9\uaeea\uaeeb\uaeec\uaeed\uaeee\uaeef\uaef0\uaef1\uaef2\uaef3\uaef4\uaef5\uaef6\uaef7\uaef8\uaef9\uaefa\uaefb\uaefc\uaefd\uaefe\uaeff\uaf00\uaf01\uaf02\uaf03\uaf04\uaf05\uaf06\uaf07\uaf08\uaf09\uaf0a\uaf0b\uaf0c\uaf0d\uaf0e\uaf0f\uaf10\uaf11\uaf12\uaf13\uaf14\uaf15\uaf16\uaf17\uaf18\uaf19\uaf1a\uaf1b\uaf1c\uaf1d\uaf1e\uaf1f\uaf20\uaf21\uaf22\uaf23\uaf24\uaf25\uaf26\uaf27\uaf28\uaf29\uaf2a\uaf2b\uaf2c\uaf2d\uaf2e\uaf2f\uaf30\uaf31\uaf32\uaf33\uaf34\uaf35\uaf36\uaf37\uaf38\uaf39\uaf3a\uaf3b\uaf3c\uaf3d\uaf3e\uaf3f\uaf40\uaf41\uaf42\uaf43\uaf44\uaf45\uaf46\uaf47\uaf48\uaf49\uaf4a\uaf4b\uaf4c\uaf4d\uaf4e\uaf4f\uaf50\uaf51\uaf52\uaf53\uaf54\uaf55\uaf56\uaf57\uaf58\uaf59\uaf5a\uaf5b\uaf5c\uaf5d\uaf5e\uaf5f\uaf60\uaf61\uaf62\uaf63\uaf64\uaf65\uaf66\uaf67\uaf68\uaf69\uaf6a\uaf6b\uaf6c\uaf6d\uaf6e\uaf6f\uaf70\uaf71\uaf72\uaf73\uaf74\uaf75\uaf76\uaf77\uaf78\uaf79\uaf7a\uaf7b\uaf7c\uaf7d\uaf7e\uaf7f\uaf80\uaf81\uaf82\uaf83\uaf84\uaf85\uaf86\uaf87\uaf88\uaf89\uaf8a\uaf8b\uaf8c\uaf8d\uaf8e\uaf8f\uaf90\uaf91\uaf92\uaf93\uaf94\uaf95\uaf96\uaf97\uaf98\uaf99\uaf9a\uaf9b\uaf9c\uaf9d\uaf9e\uaf9f\uafa0\uafa1\uafa2\uafa3\uafa4\uafa5\uafa6\uafa7\uafa8\uafa9\uafaa\uafab\uafac\uafad\uafae\uafaf\uafb0\uafb1\uafb2\uafb3\uafb4\uafb5\uafb6\uafb7\uafb8\uafb9\uafba\uafbb\uafbc\uafbd\uafbe\uafbf\uafc0\uafc1\uafc2\uafc3\uafc4\uafc5\uafc6\uafc7\uafc8\uafc9\uafca\uafcb\uafcc\uafcd\uafce\uafcf\uafd0\uafd1\uafd2\uafd3\uafd4\uafd5\uafd6\uafd7\uafd8\uafd9\uafda\uafdb\uafdc\uafdd\uafde\uafdf\uafe0\uafe1\uafe2\uafe3\uafe4\uafe5\uafe6\uafe7\uafe8\uafe9\uafea\uafeb\uafec\uafed\uafee\uafef\uaff0\uaff1\uaff2\uaff3\uaff4\uaff5\uaff6\uaff7\uaff8\uaff9\uaffa\uaffb\uaffc\uaffd\uaffe\uafff\ub000\ub001\ub002\ub003\ub004\ub005\ub006\ub007\ub008\ub009\ub00a\ub00b\ub00c\ub00d\ub00e\ub00f\ub010\ub011\ub012\ub013\ub014\ub015\ub016\ub017\ub018\ub019\ub01a\ub01b\ub01c\ub01d\ub01e\ub01f\ub020\ub021\ub022\ub023\ub024\ub025\ub026\ub027\ub028\ub029\ub02a\ub02b\ub02c\ub02d\ub02e\ub02f\ub030\ub031\ub032\ub033\ub034\ub035\ub036\ub037\ub038\ub039\ub03a\ub03b\ub03c\ub03d\ub03e\ub03f\ub040\ub041\ub042\ub043\ub044\ub045\ub046\ub047\ub048\ub049\ub04a\ub04b\ub04c\ub04d\ub04e\ub04f\ub050\ub051\ub052\ub053\ub054\ub055\ub056\ub057\ub058\ub059\ub05a\ub05b\ub05c\ub05d\ub05e\ub05f\ub060\ub061\ub062\ub063\ub064\ub065\ub066\ub067\ub068\ub069\ub06a\ub06b\ub06c\ub06d\ub06e\ub06f\ub070\ub071\ub072\ub073\ub074\ub075\ub076\ub077\ub078\ub079\ub07a\ub07b\ub07c\ub07d\ub07e\ub07f\ub080\ub081\ub082\ub083\ub084\ub085\ub086\ub087\ub088\ub089\ub08a\ub08b\ub08c\ub08d\ub08e\ub08f\ub090\ub091\ub092\ub093\ub094\ub095\ub096\ub097\ub098\ub099\ub09a\ub09b\ub09c\ub09d\ub09e\ub09f\ub0a0\ub0a1\ub0a2\ub0a3\ub0a4\ub0a5\ub0a6\ub0a7\ub0a8\ub0a9\ub0aa\ub0ab\ub0ac\ub0ad\ub0ae\ub0af\ub0b0\ub0b1\ub0b2\ub0b3\ub0b4\ub0b5\ub0b6\ub0b7\ub0b8\ub0b9\ub0ba\ub0bb\ub0bc\ub0bd\ub0be\ub0bf\ub0c0\ub0c1\ub0c2\ub0c3\ub0c4\ub0c5\ub0c6\ub0c7\ub0c8\ub0c9\ub0ca\ub0cb\ub0cc\ub0cd\ub0ce\ub0cf\ub0d0\ub0d1\ub0d2\ub0d3\ub0d4\ub0d5\ub0d6\ub0d7\ub0d8\ub0d9\ub0da\ub0db\ub0dc\ub0dd\ub0de\ub0df\ub0e0\ub0e1\ub0e2\ub0e3\ub0e4\ub0e5\ub0e6\ub0e7\ub0e8\ub0e9\ub0ea\ub0eb\ub0ec\ub0ed\ub0ee\ub0ef\ub0f0\ub0f1\ub0f2\ub0f3\ub0f4\ub0f5\ub0f6\ub0f7\ub0f8\ub0f9\ub0fa\ub0fb\ub0fc\ub0fd\ub0fe\ub0ff\ub100\ub101\ub102\ub103\ub104\ub105\ub106\ub107\ub108\ub109\ub10a\ub10b\ub10c\ub10d\ub10e\ub10f\ub110\ub111\ub112\ub113\ub114\ub115\ub116\ub117\ub118\ub119\ub11a\ub11b\ub11c\ub11d\ub11e\ub11f\ub120\ub121\ub122\ub123\ub124\ub125\ub126\ub127\ub128\ub129\ub12a\ub12b\ub12c\ub12d\ub12e\ub12f\ub130\ub131\ub132\ub133\ub134\ub135\ub136\ub137\ub138\ub139\ub13a\ub13b\ub13c\ub13d\ub13e\ub13f\ub140\ub141\ub142\ub143\ub144\ub145\ub146\ub147\ub148\ub149\ub14a\ub14b\ub14c\ub14d\ub14e\ub14f\ub150\ub151\ub152\ub153\ub154\ub155\ub156\ub157\ub158\ub159\ub15a\ub15b\ub15c\ub15d\ub15e\ub15f\ub160\ub161\ub162\ub163\ub164\ub165\ub166\ub167\ub168\ub169\ub16a\ub16b\ub16c\ub16d\ub16e\ub16f\ub170\ub171\ub172\ub173\ub174\ub175\ub176\ub177\ub178\ub179\ub17a\ub17b\ub17c\ub17d\ub17e\ub17f\ub180\ub181\ub182\ub183\ub184\ub185\ub186\ub187\ub188\ub189\ub18a\ub18b\ub18c\ub18d\ub18e\ub18f\ub190\ub191\ub192\ub193\ub194\ub195\ub196\ub197\ub198\ub199\ub19a\ub19b\ub19c\ub19d\ub19e\ub19f\ub1a0\ub1a1\ub1a2\ub1a3\ub1a4\ub1a5\ub1a6\ub1a7\ub1a8\ub1a9\ub1aa\ub1ab\ub1ac\ub1ad\ub1ae\ub1af\ub1b0\ub1b1\ub1b2\ub1b3\ub1b4\ub1b5\ub1b6\ub1b7\ub1b8\ub1b9\ub1ba\ub1bb\ub1bc\ub1bd\ub1be\ub1bf\ub1c0\ub1c1\ub1c2\ub1c3\ub1c4\ub1c5\ub1c6\ub1c7\ub1c8\ub1c9\ub1ca\ub1cb\ub1cc\ub1cd\ub1ce\ub1cf\ub1d0\ub1d1\ub1d2\ub1d3\ub1d4\ub1d5\ub1d6\ub1d7\ub1d8\ub1d9\ub1da\ub1db\ub1dc\ub1dd\ub1de\ub1df\ub1e0\ub1e1\ub1e2\ub1e3\ub1e4\ub1e5\ub1e6\ub1e7\ub1e8\ub1e9\ub1ea\ub1eb\ub1ec\ub1ed\ub1ee\ub1ef\ub1f0\ub1f1\ub1f2\ub1f3\ub1f4\ub1f5\ub1f6\ub1f7\ub1f8\ub1f9\ub1fa\ub1fb\ub1fc\ub1fd\ub1fe\ub1ff\ub200\ub201\ub202\ub203\ub204\ub205\ub206\ub207\ub208\ub209\ub20a\ub20b\ub20c\ub20d\ub20e\ub20f\ub210\ub211\ub212\ub213\ub214\ub215\ub216\ub217\ub218\ub219\ub21a\ub21b\ub21c\ub21d\ub21e\ub21f\ub220\ub221\ub222\ub223\ub224\ub225\ub226\ub227\ub228\ub229\ub22a\ub22b\ub22c\ub22d\ub22e\ub22f\ub230\ub231\ub232\ub233\ub234\ub235\ub236\ub237\ub238\ub239\ub23a\ub23b\ub23c\ub23d\ub23e\ub23f\ub240\ub241\ub242\ub243\ub244\ub245\ub246\ub247\ub248\ub249\ub24a\ub24b\ub24c\ub24d\ub24e\ub24f\ub250\ub251\ub252\ub253\ub254\ub255\ub256\ub257\ub258\ub259\ub25a\ub25b\ub25c\ub25d\ub25e\ub25f\ub260\ub261\ub262\ub263\ub264\ub265\ub266\ub267\ub268\ub269\ub26a\ub26b\ub26c\ub26d\ub26e\ub26f\ub270\ub271\ub272\ub273\ub274\ub275\ub276\ub277\ub278\ub279\ub27a\ub27b\ub27c\ub27d\ub27e\ub27f\ub280\ub281\ub282\ub283\ub284\ub285\ub286\ub287\ub288\ub289\ub28a\ub28b\ub28c\ub28d\ub28e\ub28f\ub290\ub291\ub292\ub293\ub294\ub295\ub296\ub297\ub298\ub299\ub29a\ub29b\ub29c\ub29d\ub29e\ub29f\ub2a0\ub2a1\ub2a2\ub2a3\ub2a4\ub2a5\ub2a6\ub2a7\ub2a8\ub2a9\ub2aa\ub2ab\ub2ac\ub2ad\ub2ae\ub2af\ub2b0\ub2b1\ub2b2\ub2b3\ub2b4\ub2b5\ub2b6\ub2b7\ub2b8\ub2b9\ub2ba\ub2bb\ub2bc\ub2bd\ub2be\ub2bf\ub2c0\ub2c1\ub2c2\ub2c3\ub2c4\ub2c5\ub2c6\ub2c7\ub2c8\ub2c9\ub2ca\ub2cb\ub2cc\ub2cd\ub2ce\ub2cf\ub2d0\ub2d1\ub2d2\ub2d3\ub2d4\ub2d5\ub2d6\ub2d7\ub2d8\ub2d9\ub2da\ub2db\ub2dc\ub2dd\ub2de\ub2df\ub2e0\ub2e1\ub2e2\ub2e3\ub2e4\ub2e5\ub2e6\ub2e7\ub2e8\ub2e9\ub2ea\ub2eb\ub2ec\ub2ed\ub2ee\ub2ef\ub2f0\ub2f1\ub2f2\ub2f3\ub2f4\ub2f5\ub2f6\ub2f7\ub2f8\ub2f9\ub2fa\ub2fb\ub2fc\ub2fd\ub2fe\ub2ff\ub300\ub301\ub302\ub303\ub304\ub305\ub306\ub307\ub308\ub309\ub30a\ub30b\ub30c\ub30d\ub30e\ub30f\ub310\ub311\ub312\ub313\ub314\ub315\ub316\ub317\ub318\ub319\ub31a\ub31b\ub31c\ub31d\ub31e\ub31f\ub320\ub321\ub322\ub323\ub324\ub325\ub326\ub327\ub328\ub329\ub32a\ub32b\ub32c\ub32d\ub32e\ub32f\ub330\ub331\ub332\ub333\ub334\ub335\ub336\ub337\ub338\ub339\ub33a\ub33b\ub33c\ub33d\ub33e\ub33f\ub340\ub341\ub342\ub343\ub344\ub345\ub346\ub347\ub348\ub349\ub34a\ub34b\ub34c\ub34d\ub34e\ub34f\ub350\ub351\ub352\ub353\ub354\ub355\ub356\ub357\ub358\ub359\ub35a\ub35b\ub35c\ub35d\ub35e\ub35f\ub360\ub361\ub362\ub363\ub364\ub365\ub366\ub367\ub368\ub369\ub36a\ub36b\ub36c\ub36d\ub36e\ub36f\ub370\ub371\ub372\ub373\ub374\ub375\ub376\ub377\ub378\ub379\ub37a\ub37b\ub37c\ub37d\ub37e\ub37f\ub380\ub381\ub382\ub383\ub384\ub385\ub386\ub387\ub388\ub389\ub38a\ub38b\ub38c\ub38d\ub38e\ub38f\ub390\ub391\ub392\ub393\ub394\ub395\ub396\ub397\ub398\ub399\ub39a\ub39b\ub39c\ub39d\ub39e\ub39f\ub3a0\ub3a1\ub3a2\ub3a3\ub3a4\ub3a5\ub3a6\ub3a7\ub3a8\ub3a9\ub3aa\ub3ab\ub3ac\ub3ad\ub3ae\ub3af\ub3b0\ub3b1\ub3b2\ub3b3\ub3b4\ub3b5\ub3b6\ub3b7\ub3b8\ub3b9\ub3ba\ub3bb\ub3bc\ub3bd\ub3be\ub3bf\ub3c0\ub3c1\ub3c2\ub3c3\ub3c4\ub3c5\ub3c6\ub3c7\ub3c8\ub3c9\ub3ca\ub3cb\ub3cc\ub3cd\ub3ce\ub3cf\ub3d0\ub3d1\ub3d2\ub3d3\ub3d4\ub3d5\ub3d6\ub3d7\ub3d8\ub3d9\ub3da\ub3db\ub3dc\ub3dd\ub3de\ub3df\ub3e0\ub3e1\ub3e2\ub3e3\ub3e4\ub3e5\ub3e6\ub3e7\ub3e8\ub3e9\ub3ea\ub3eb\ub3ec\ub3ed\ub3ee\ub3ef\ub3f0\ub3f1\ub3f2\ub3f3\ub3f4\ub3f5\ub3f6\ub3f7\ub3f8\ub3f9\ub3fa\ub3fb\ub3fc\ub3fd\ub3fe\ub3ff\ub400\ub401\ub402\ub403\ub404\ub405\ub406\ub407\ub408\ub409\ub40a\ub40b\ub40c\ub40d\ub40e\ub40f\ub410\ub411\ub412\ub413\ub414\ub415\ub416\ub417\ub418\ub419\ub41a\ub41b\ub41c\ub41d\ub41e\ub41f\ub420\ub421\ub422\ub423\ub424\ub425\ub426\ub427\ub428\ub429\ub42a\ub42b\ub42c\ub42d\ub42e\ub42f\ub430\ub431\ub432\ub433\ub434\ub435\ub436\ub437\ub438\ub439\ub43a\ub43b\ub43c\ub43d\ub43e\ub43f\ub440\ub441\ub442\ub443\ub444\ub445\ub446\ub447\ub448\ub449\ub44a\ub44b\ub44c\ub44d\ub44e\ub44f\ub450\ub451\ub452\ub453\ub454\ub455\ub456\ub457\ub458\ub459\ub45a\ub45b\ub45c\ub45d\ub45e\ub45f\ub460\ub461\ub462\ub463\ub464\ub465\ub466\ub467\ub468\ub469\ub46a\ub46b\ub46c\ub46d\ub46e\ub46f\ub470\ub471\ub472\ub473\ub474\ub475\ub476\ub477\ub478\ub479\ub47a\ub47b\ub47c\ub47d\ub47e\ub47f\ub480\ub481\ub482\ub483\ub484\ub485\ub486\ub487\ub488\ub489\ub48a\ub48b\ub48c\ub48d\ub48e\ub48f\ub490\ub491\ub492\ub493\ub494\ub495\ub496\ub497\ub498\ub499\ub49a\ub49b\ub49c\ub49d\ub49e\ub49f\ub4a0\ub4a1\ub4a2\ub4a3\ub4a4\ub4a5\ub4a6\ub4a7\ub4a8\ub4a9\ub4aa\ub4ab\ub4ac\ub4ad\ub4ae\ub4af\ub4b0\ub4b1\ub4b2\ub4b3\ub4b4\ub4b5\ub4b6\ub4b7\ub4b8\ub4b9\ub4ba\ub4bb\ub4bc\ub4bd\ub4be\ub4bf\ub4c0\ub4c1\ub4c2\ub4c3\ub4c4\ub4c5\ub4c6\ub4c7\ub4c8\ub4c9\ub4ca\ub4cb\ub4cc\ub4cd\ub4ce\ub4cf\ub4d0\ub4d1\ub4d2\ub4d3\ub4d4\ub4d5\ub4d6\ub4d7\ub4d8\ub4d9\ub4da\ub4db\ub4dc\ub4dd\ub4de\ub4df\ub4e0\ub4e1\ub4e2\ub4e3\ub4e4\ub4e5\ub4e6\ub4e7\ub4e8\ub4e9\ub4ea\ub4eb\ub4ec\ub4ed\ub4ee\ub4ef\ub4f0\ub4f1\ub4f2\ub4f3\ub4f4\ub4f5\ub4f6\ub4f7\ub4f8\ub4f9\ub4fa\ub4fb\ub4fc\ub4fd\ub4fe\ub4ff\ub500\ub501\ub502\ub503\ub504\ub505\ub506\ub507\ub508\ub509\ub50a\ub50b\ub50c\ub50d\ub50e\ub50f\ub510\ub511\ub512\ub513\ub514\ub515\ub516\ub517\ub518\ub519\ub51a\ub51b\ub51c\ub51d\ub51e\ub51f\ub520\ub521\ub522\ub523\ub524\ub525\ub526\ub527\ub528\ub529\ub52a\ub52b\ub52c\ub52d\ub52e\ub52f\ub530\ub531\ub532\ub533\ub534\ub535\ub536\ub537\ub538\ub539\ub53a\ub53b\ub53c\ub53d\ub53e\ub53f\ub540\ub541\ub542\ub543\ub544\ub545\ub546\ub547\ub548\ub549\ub54a\ub54b\ub54c\ub54d\ub54e\ub54f\ub550\ub551\ub552\ub553\ub554\ub555\ub556\ub557\ub558\ub559\ub55a\ub55b\ub55c\ub55d\ub55e\ub55f\ub560\ub561\ub562\ub563\ub564\ub565\ub566\ub567\ub568\ub569\ub56a\ub56b\ub56c\ub56d\ub56e\ub56f\ub570\ub571\ub572\ub573\ub574\ub575\ub576\ub577\ub578\ub579\ub57a\ub57b\ub57c\ub57d\ub57e\ub57f\ub580\ub581\ub582\ub583\ub584\ub585\ub586\ub587\ub588\ub589\ub58a\ub58b\ub58c\ub58d\ub58e\ub58f\ub590\ub591\ub592\ub593\ub594\ub595\ub596\ub597\ub598\ub599\ub59a\ub59b\ub59c\ub59d\ub59e\ub59f\ub5a0\ub5a1\ub5a2\ub5a3\ub5a4\ub5a5\ub5a6\ub5a7\ub5a8\ub5a9\ub5aa\ub5ab\ub5ac\ub5ad\ub5ae\ub5af\ub5b0\ub5b1\ub5b2\ub5b3\ub5b4\ub5b5\ub5b6\ub5b7\ub5b8\ub5b9\ub5ba\ub5bb\ub5bc\ub5bd\ub5be\ub5bf\ub5c0\ub5c1\ub5c2\ub5c3\ub5c4\ub5c5\ub5c6\ub5c7\ub5c8\ub5c9\ub5ca\ub5cb\ub5cc\ub5cd\ub5ce\ub5cf\ub5d0\ub5d1\ub5d2\ub5d3\ub5d4\ub5d5\ub5d6\ub5d7\ub5d8\ub5d9\ub5da\ub5db\ub5dc\ub5dd\ub5de\ub5df\ub5e0\ub5e1\ub5e2\ub5e3\ub5e4\ub5e5\ub5e6\ub5e7\ub5e8\ub5e9\ub5ea\ub5eb\ub5ec\ub5ed\ub5ee\ub5ef\ub5f0\ub5f1\ub5f2\ub5f3\ub5f4\ub5f5\ub5f6\ub5f7\ub5f8\ub5f9\ub5fa\ub5fb\ub5fc\ub5fd\ub5fe\ub5ff\ub600\ub601\ub602\ub603\ub604\ub605\ub606\ub607\ub608\ub609\ub60a\ub60b\ub60c\ub60d\ub60e\ub60f\ub610\ub611\ub612\ub613\ub614\ub615\ub616\ub617\ub618\ub619\ub61a\ub61b\ub61c\ub61d\ub61e\ub61f\ub620\ub621\ub622\ub623\ub624\ub625\ub626\ub627\ub628\ub629\ub62a\ub62b\ub62c\ub62d\ub62e\ub62f\ub630\ub631\ub632\ub633\ub634\ub635\ub636\ub637\ub638\ub639\ub63a\ub63b\ub63c\ub63d\ub63e\ub63f\ub640\ub641\ub642\ub643\ub644\ub645\ub646\ub647\ub648\ub649\ub64a\ub64b\ub64c\ub64d\ub64e\ub64f\ub650\ub651\ub652\ub653\ub654\ub655\ub656\ub657\ub658\ub659\ub65a\ub65b\ub65c\ub65d\ub65e\ub65f\ub660\ub661\ub662\ub663\ub664\ub665\ub666\ub667\ub668\ub669\ub66a\ub66b\ub66c\ub66d\ub66e\ub66f\ub670\ub671\ub672\ub673\ub674\ub675\ub676\ub677\ub678\ub679\ub67a\ub67b\ub67c\ub67d\ub67e\ub67f\ub680\ub681\ub682\ub683\ub684\ub685\ub686\ub687\ub688\ub689\ub68a\ub68b\ub68c\ub68d\ub68e\ub68f\ub690\ub691\ub692\ub693\ub694\ub695\ub696\ub697\ub698\ub699\ub69a\ub69b\ub69c\ub69d\ub69e\ub69f\ub6a0\ub6a1\ub6a2\ub6a3\ub6a4\ub6a5\ub6a6\ub6a7\ub6a8\ub6a9\ub6aa\ub6ab\ub6ac\ub6ad\ub6ae\ub6af\ub6b0\ub6b1\ub6b2\ub6b3\ub6b4\ub6b5\ub6b6\ub6b7\ub6b8\ub6b9\ub6ba\ub6bb\ub6bc\ub6bd\ub6be\ub6bf\ub6c0\ub6c1\ub6c2\ub6c3\ub6c4\ub6c5\ub6c6\ub6c7\ub6c8\ub6c9\ub6ca\ub6cb\ub6cc\ub6cd\ub6ce\ub6cf\ub6d0\ub6d1\ub6d2\ub6d3\ub6d4\ub6d5\ub6d6\ub6d7\ub6d8\ub6d9\ub6da\ub6db\ub6dc\ub6dd\ub6de\ub6df\ub6e0\ub6e1\ub6e2\ub6e3\ub6e4\ub6e5\ub6e6\ub6e7\ub6e8\ub6e9\ub6ea\ub6eb\ub6ec\ub6ed\ub6ee\ub6ef\ub6f0\ub6f1\ub6f2\ub6f3\ub6f4\ub6f5\ub6f6\ub6f7\ub6f8\ub6f9\ub6fa\ub6fb\ub6fc\ub6fd\ub6fe\ub6ff\ub700\ub701\ub702\ub703\ub704\ub705\ub706\ub707\ub708\ub709\ub70a\ub70b\ub70c\ub70d\ub70e\ub70f\ub710\ub711\ub712\ub713\ub714\ub715\ub716\ub717\ub718\ub719\ub71a\ub71b\ub71c\ub71d\ub71e\ub71f\ub720\ub721\ub722\ub723\ub724\ub725\ub726\ub727\ub728\ub729\ub72a\ub72b\ub72c\ub72d\ub72e\ub72f\ub730\ub731\ub732\ub733\ub734\ub735\ub736\ub737\ub738\ub739\ub73a\ub73b\ub73c\ub73d\ub73e\ub73f\ub740\ub741\ub742\ub743\ub744\ub745\ub746\ub747\ub748\ub749\ub74a\ub74b\ub74c\ub74d\ub74e\ub74f\ub750\ub751\ub752\ub753\ub754\ub755\ub756\ub757\ub758\ub759\ub75a\ub75b\ub75c\ub75d\ub75e\ub75f\ub760\ub761\ub762\ub763\ub764\ub765\ub766\ub767\ub768\ub769\ub76a\ub76b\ub76c\ub76d\ub76e\ub76f\ub770\ub771\ub772\ub773\ub774\ub775\ub776\ub777\ub778\ub779\ub77a\ub77b\ub77c\ub77d\ub77e\ub77f\ub780\ub781\ub782\ub783\ub784\ub785\ub786\ub787\ub788\ub789\ub78a\ub78b\ub78c\ub78d\ub78e\ub78f\ub790\ub791\ub792\ub793\ub794\ub795\ub796\ub797\ub798\ub799\ub79a\ub79b\ub79c\ub79d\ub79e\ub79f\ub7a0\ub7a1\ub7a2\ub7a3\ub7a4\ub7a5\ub7a6\ub7a7\ub7a8\ub7a9\ub7aa\ub7ab\ub7ac\ub7ad\ub7ae\ub7af\ub7b0\ub7b1\ub7b2\ub7b3\ub7b4\ub7b5\ub7b6\ub7b7\ub7b8\ub7b9\ub7ba\ub7bb\ub7bc\ub7bd\ub7be\ub7bf\ub7c0\ub7c1\ub7c2\ub7c3\ub7c4\ub7c5\ub7c6\ub7c7\ub7c8\ub7c9\ub7ca\ub7cb\ub7cc\ub7cd\ub7ce\ub7cf\ub7d0\ub7d1\ub7d2\ub7d3\ub7d4\ub7d5\ub7d6\ub7d7\ub7d8\ub7d9\ub7da\ub7db\ub7dc\ub7dd\ub7de\ub7df\ub7e0\ub7e1\ub7e2\ub7e3\ub7e4\ub7e5\ub7e6\ub7e7\ub7e8\ub7e9\ub7ea\ub7eb\ub7ec\ub7ed\ub7ee\ub7ef\ub7f0\ub7f1\ub7f2\ub7f3\ub7f4\ub7f5\ub7f6\ub7f7\ub7f8\ub7f9\ub7fa\ub7fb\ub7fc\ub7fd\ub7fe\ub7ff\ub800\ub801\ub802\ub803\ub804\ub805\ub806\ub807\ub808\ub809\ub80a\ub80b\ub80c\ub80d\ub80e\ub80f\ub810\ub811\ub812\ub813\ub814\ub815\ub816\ub817\ub818\ub819\ub81a\ub81b\ub81c\ub81d\ub81e\ub81f\ub820\ub821\ub822\ub823\ub824\ub825\ub826\ub827\ub828\ub829\ub82a\ub82b\ub82c\ub82d\ub82e\ub82f\ub830\ub831\ub832\ub833\ub834\ub835\ub836\ub837\ub838\ub839\ub83a\ub83b\ub83c\ub83d\ub83e\ub83f\ub840\ub841\ub842\ub843\ub844\ub845\ub846\ub847\ub848\ub849\ub84a\ub84b\ub84c\ub84d\ub84e\ub84f\ub850\ub851\ub852\ub853\ub854\ub855\ub856\ub857\ub858\ub859\ub85a\ub85b\ub85c\ub85d\ub85e\ub85f\ub860\ub861\ub862\ub863\ub864\ub865\ub866\ub867\ub868\ub869\ub86a\ub86b\ub86c\ub86d\ub86e\ub86f\ub870\ub871\ub872\ub873\ub874\ub875\ub876\ub877\ub878\ub879\ub87a\ub87b\ub87c\ub87d\ub87e\ub87f\ub880\ub881\ub882\ub883\ub884\ub885\ub886\ub887\ub888\ub889\ub88a\ub88b\ub88c\ub88d\ub88e\ub88f\ub890\ub891\ub892\ub893\ub894\ub895\ub896\ub897\ub898\ub899\ub89a\ub89b\ub89c\ub89d\ub89e\ub89f\ub8a0\ub8a1\ub8a2\ub8a3\ub8a4\ub8a5\ub8a6\ub8a7\ub8a8\ub8a9\ub8aa\ub8ab\ub8ac\ub8ad\ub8ae\ub8af\ub8b0\ub8b1\ub8b2\ub8b3\ub8b4\ub8b5\ub8b6\ub8b7\ub8b8\ub8b9\ub8ba\ub8bb\ub8bc\ub8bd\ub8be\ub8bf\ub8c0\ub8c1\ub8c2\ub8c3\ub8c4\ub8c5\ub8c6\ub8c7\ub8c8\ub8c9\ub8ca\ub8cb\ub8cc\ub8cd\ub8ce\ub8cf\ub8d0\ub8d1\ub8d2\ub8d3\ub8d4\ub8d5\ub8d6\ub8d7\ub8d8\ub8d9\ub8da\ub8db\ub8dc\ub8dd\ub8de\ub8df\ub8e0\ub8e1\ub8e2\ub8e3\ub8e4\ub8e5\ub8e6\ub8e7\ub8e8\ub8e9\ub8ea\ub8eb\ub8ec\ub8ed\ub8ee\ub8ef\ub8f0\ub8f1\ub8f2\ub8f3\ub8f4\ub8f5\ub8f6\ub8f7\ub8f8\ub8f9\ub8fa\ub8fb\ub8fc\ub8fd\ub8fe\ub8ff\ub900\ub901\ub902\ub903\ub904\ub905\ub906\ub907\ub908\ub909\ub90a\ub90b\ub90c\ub90d\ub90e\ub90f\ub910\ub911\ub912\ub913\ub914\ub915\ub916\ub917\ub918\ub919\ub91a\ub91b\ub91c\ub91d\ub91e\ub91f\ub920\ub921\ub922\ub923\ub924\ub925\ub926\ub927\ub928\ub929\ub92a\ub92b\ub92c\ub92d\ub92e\ub92f\ub930\ub931\ub932\ub933\ub934\ub935\ub936\ub937\ub938\ub939\ub93a\ub93b\ub93c\ub93d\ub93e\ub93f\ub940\ub941\ub942\ub943\ub944\ub945\ub946\ub947\ub948\ub949\ub94a\ub94b\ub94c\ub94d\ub94e\ub94f\ub950\ub951\ub952\ub953\ub954\ub955\ub956\ub957\ub958\ub959\ub95a\ub95b\ub95c\ub95d\ub95e\ub95f\ub960\ub961\ub962\ub963\ub964\ub965\ub966\ub967\ub968\ub969\ub96a\ub96b\ub96c\ub96d\ub96e\ub96f\ub970\ub971\ub972\ub973\ub974\ub975\ub976\ub977\ub978\ub979\ub97a\ub97b\ub97c\ub97d\ub97e\ub97f\ub980\ub981\ub982\ub983\ub984\ub985\ub986\ub987\ub988\ub989\ub98a\ub98b\ub98c\ub98d\ub98e\ub98f\ub990\ub991\ub992\ub993\ub994\ub995\ub996\ub997\ub998\ub999\ub99a\ub99b\ub99c\ub99d\ub99e\ub99f\ub9a0\ub9a1\ub9a2\ub9a3\ub9a4\ub9a5\ub9a6\ub9a7\ub9a8\ub9a9\ub9aa\ub9ab\ub9ac\ub9ad\ub9ae\ub9af\ub9b0\ub9b1\ub9b2\ub9b3\ub9b4\ub9b5\ub9b6\ub9b7\ub9b8\ub9b9\ub9ba\ub9bb\ub9bc\ub9bd\ub9be\ub9bf\ub9c0\ub9c1\ub9c2\ub9c3\ub9c4\ub9c5\ub9c6\ub9c7\ub9c8\ub9c9\ub9ca\ub9cb\ub9cc\ub9cd\ub9ce\ub9cf\ub9d0\ub9d1\ub9d2\ub9d3\ub9d4\ub9d5\ub9d6\ub9d7\ub9d8\ub9d9\ub9da\ub9db\ub9dc\ub9dd\ub9de\ub9df\ub9e0\ub9e1\ub9e2\ub9e3\ub9e4\ub9e5\ub9e6\ub9e7\ub9e8\ub9e9\ub9ea\ub9eb\ub9ec\ub9ed\ub9ee\ub9ef\ub9f0\ub9f1\ub9f2\ub9f3\ub9f4\ub9f5\ub9f6\ub9f7\ub9f8\ub9f9\ub9fa\ub9fb\ub9fc\ub9fd\ub9fe\ub9ff\uba00\uba01\uba02\uba03\uba04\uba05\uba06\uba07\uba08\uba09\uba0a\uba0b\uba0c\uba0d\uba0e\uba0f\uba10\uba11\uba12\uba13\uba14\uba15\uba16\uba17\uba18\uba19\uba1a\uba1b\uba1c\uba1d\uba1e\uba1f\uba20\uba21\uba22\uba23\uba24\uba25\uba26\uba27\uba28\uba29\uba2a\uba2b\uba2c\uba2d\uba2e\uba2f\uba30\uba31\uba32\uba33\uba34\uba35\uba36\uba37\uba38\uba39\uba3a\uba3b\uba3c\uba3d\uba3e\uba3f\uba40\uba41\uba42\uba43\uba44\uba45\uba46\uba47\uba48\uba49\uba4a\uba4b\uba4c\uba4d\uba4e\uba4f\uba50\uba51\uba52\uba53\uba54\uba55\uba56\uba57\uba58\uba59\uba5a\uba5b\uba5c\uba5d\uba5e\uba5f\uba60\uba61\uba62\uba63\uba64\uba65\uba66\uba67\uba68\uba69\uba6a\uba6b\uba6c\uba6d\uba6e\uba6f\uba70\uba71\uba72\uba73\uba74\uba75\uba76\uba77\uba78\uba79\uba7a\uba7b\uba7c\uba7d\uba7e\uba7f\uba80\uba81\uba82\uba83\uba84\uba85\uba86\uba87\uba88\uba89\uba8a\uba8b\uba8c\uba8d\uba8e\uba8f\uba90\uba91\uba92\uba93\uba94\uba95\uba96\uba97\uba98\uba99\uba9a\uba9b\uba9c\uba9d\uba9e\uba9f\ubaa0\ubaa1\ubaa2\ubaa3\ubaa4\ubaa5\ubaa6\ubaa7\ubaa8\ubaa9\ubaaa\ubaab\ubaac\ubaad\ubaae\ubaaf\ubab0\ubab1\ubab2\ubab3\ubab4\ubab5\ubab6\ubab7\ubab8\ubab9\ubaba\ubabb\ubabc\ubabd\ubabe\ubabf\ubac0\ubac1\ubac2\ubac3\ubac4\ubac5\ubac6\ubac7\ubac8\ubac9\ubaca\ubacb\ubacc\ubacd\ubace\ubacf\ubad0\ubad1\ubad2\ubad3\ubad4\ubad5\ubad6\ubad7\ubad8\ubad9\ubada\ubadb\ubadc\ubadd\ubade\ubadf\ubae0\ubae1\ubae2\ubae3\ubae4\ubae5\ubae6\ubae7\ubae8\ubae9\ubaea\ubaeb\ubaec\ubaed\ubaee\ubaef\ubaf0\ubaf1\ubaf2\ubaf3\ubaf4\ubaf5\ubaf6\ubaf7\ubaf8\ubaf9\ubafa\ubafb\ubafc\ubafd\ubafe\ubaff\ubb00\ubb01\ubb02\ubb03\ubb04\ubb05\ubb06\ubb07\ubb08\ubb09\ubb0a\ubb0b\ubb0c\ubb0d\ubb0e\ubb0f\ubb10\ubb11\ubb12\ubb13\ubb14\ubb15\ubb16\ubb17\ubb18\ubb19\ubb1a\ubb1b\ubb1c\ubb1d\ubb1e\ubb1f\ubb20\ubb21\ubb22\ubb23\ubb24\ubb25\ubb26\ubb27\ubb28\ubb29\ubb2a\ubb2b\ubb2c\ubb2d\ubb2e\ubb2f\ubb30\ubb31\ubb32\ubb33\ubb34\ubb35\ubb36\ubb37\ubb38\ubb39\ubb3a\ubb3b\ubb3c\ubb3d\ubb3e\ubb3f\ubb40\ubb41\ubb42\ubb43\ubb44\ubb45\ubb46\ubb47\ubb48\ubb49\ubb4a\ubb4b\ubb4c\ubb4d\ubb4e\ubb4f\ubb50\ubb51\ubb52\ubb53\ubb54\ubb55\ubb56\ubb57\ubb58\ubb59\ubb5a\ubb5b\ubb5c\ubb5d\ubb5e\ubb5f\ubb60\ubb61\ubb62\ubb63\ubb64\ubb65\ubb66\ubb67\ubb68\ubb69\ubb6a\ubb6b\ubb6c\ubb6d\ubb6e\ubb6f\ubb70\ubb71\ubb72\ubb73\ubb74\ubb75\ubb76\ubb77\ubb78\ubb79\ubb7a\ubb7b\ubb7c\ubb7d\ubb7e\ubb7f\ubb80\ubb81\ubb82\ubb83\ubb84\ubb85\ubb86\ubb87\ubb88\ubb89\ubb8a\ubb8b\ubb8c\ubb8d\ubb8e\ubb8f\ubb90\ubb91\ubb92\ubb93\ubb94\ubb95\ubb96\ubb97\ubb98\ubb99\ubb9a\ubb9b\ubb9c\ubb9d\ubb9e\ubb9f\ubba0\ubba1\ubba2\ubba3\ubba4\ubba5\ubba6\ubba7\ubba8\ubba9\ubbaa\ubbab\ubbac\ubbad\ubbae\ubbaf\ubbb0\ubbb1\ubbb2\ubbb3\ubbb4\ubbb5\ubbb6\ubbb7\ubbb8\ubbb9\ubbba\ubbbb\ubbbc\ubbbd\ubbbe\ubbbf\ubbc0\ubbc1\ubbc2\ubbc3\ubbc4\ubbc5\ubbc6\ubbc7\ubbc8\ubbc9\ubbca\ubbcb\ubbcc\ubbcd\ubbce\ubbcf\ubbd0\ubbd1\ubbd2\ubbd3\ubbd4\ubbd5\ubbd6\ubbd7\ubbd8\ubbd9\ubbda\ubbdb\ubbdc\ubbdd\ubbde\ubbdf\ubbe0\ubbe1\ubbe2\ubbe3\ubbe4\ubbe5\ubbe6\ubbe7\ubbe8\ubbe9\ubbea\ubbeb\ubbec\ubbed\ubbee\ubbef\ubbf0\ubbf1\ubbf2\ubbf3\ubbf4\ubbf5\ubbf6\ubbf7\ubbf8\ubbf9\ubbfa\ubbfb\ubbfc\ubbfd\ubbfe\ubbff\ubc00\ubc01\ubc02\ubc03\ubc04\ubc05\ubc06\ubc07\ubc08\ubc09\ubc0a\ubc0b\ubc0c\ubc0d\ubc0e\ubc0f\ubc10\ubc11\ubc12\ubc13\ubc14\ubc15\ubc16\ubc17\ubc18\ubc19\ubc1a\ubc1b\ubc1c\ubc1d\ubc1e\ubc1f\ubc20\ubc21\ubc22\ubc23\ubc24\ubc25\ubc26\ubc27\ubc28\ubc29\ubc2a\ubc2b\ubc2c\ubc2d\ubc2e\ubc2f\ubc30\ubc31\ubc32\ubc33\ubc34\ubc35\ubc36\ubc37\ubc38\ubc39\ubc3a\ubc3b\ubc3c\ubc3d\ubc3e\ubc3f\ubc40\ubc41\ubc42\ubc43\ubc44\ubc45\ubc46\ubc47\ubc48\ubc49\ubc4a\ubc4b\ubc4c\ubc4d\ubc4e\ubc4f\ubc50\ubc51\ubc52\ubc53\ubc54\ubc55\ubc56\ubc57\ubc58\ubc59\ubc5a\ubc5b\ubc5c\ubc5d\ubc5e\ubc5f\ubc60\ubc61\ubc62\ubc63\ubc64\ubc65\ubc66\ubc67\ubc68\ubc69\ubc6a\ubc6b\ubc6c\ubc6d\ubc6e\ubc6f\ubc70\ubc71\ubc72\ubc73\ubc74\ubc75\ubc76\ubc77\ubc78\ubc79\ubc7a\ubc7b\ubc7c\ubc7d\ubc7e\ubc7f\ubc80\ubc81\ubc82\ubc83\ubc84\ubc85\ubc86\ubc87\ubc88\ubc89\ubc8a\ubc8b\ubc8c\ubc8d\ubc8e\ubc8f\ubc90\ubc91\ubc92\ubc93\ubc94\ubc95\ubc96\ubc97\ubc98\ubc99\ubc9a\ubc9b\ubc9c\ubc9d\ubc9e\ubc9f\ubca0\ubca1\ubca2\ubca3\ubca4\ubca5\ubca6\ubca7\ubca8\ubca9\ubcaa\ubcab\ubcac\ubcad\ubcae\ubcaf\ubcb0\ubcb1\ubcb2\ubcb3\ubcb4\ubcb5\ubcb6\ubcb7\ubcb8\ubcb9\ubcba\ubcbb\ubcbc\ubcbd\ubcbe\ubcbf\ubcc0\ubcc1\ubcc2\ubcc3\ubcc4\ubcc5\ubcc6\ubcc7\ubcc8\ubcc9\ubcca\ubccb\ubccc\ubccd\ubcce\ubccf\ubcd0\ubcd1\ubcd2\ubcd3\ubcd4\ubcd5\ubcd6\ubcd7\ubcd8\ubcd9\ubcda\ubcdb\ubcdc\ubcdd\ubcde\ubcdf\ubce0\ubce1\ubce2\ubce3\ubce4\ubce5\ubce6\ubce7\ubce8\ubce9\ubcea\ubceb\ubcec\ubced\ubcee\ubcef\ubcf0\ubcf1\ubcf2\ubcf3\ubcf4\ubcf5\ubcf6\ubcf7\ubcf8\ubcf9\ubcfa\ubcfb\ubcfc\ubcfd\ubcfe\ubcff\ubd00\ubd01\ubd02\ubd03\ubd04\ubd05\ubd06\ubd07\ubd08\ubd09\ubd0a\ubd0b\ubd0c\ubd0d\ubd0e\ubd0f\ubd10\ubd11\ubd12\ubd13\ubd14\ubd15\ubd16\ubd17\ubd18\ubd19\ubd1a\ubd1b\ubd1c\ubd1d\ubd1e\ubd1f\ubd20\ubd21\ubd22\ubd23\ubd24\ubd25\ubd26\ubd27\ubd28\ubd29\ubd2a\ubd2b\ubd2c\ubd2d\ubd2e\ubd2f\ubd30\ubd31\ubd32\ubd33\ubd34\ubd35\ubd36\ubd37\ubd38\ubd39\ubd3a\ubd3b\ubd3c\ubd3d\ubd3e\ubd3f\ubd40\ubd41\ubd42\ubd43\ubd44\ubd45\ubd46\ubd47\ubd48\ubd49\ubd4a\ubd4b\ubd4c\ubd4d\ubd4e\ubd4f\ubd50\ubd51\ubd52\ubd53\ubd54\ubd55\ubd56\ubd57\ubd58\ubd59\ubd5a\ubd5b\ubd5c\ubd5d\ubd5e\ubd5f\ubd60\ubd61\ubd62\ubd63\ubd64\ubd65\ubd66\ubd67\ubd68\ubd69\ubd6a\ubd6b\ubd6c\ubd6d\ubd6e\ubd6f\ubd70\ubd71\ubd72\ubd73\ubd74\ubd75\ubd76\ubd77\ubd78\ubd79\ubd7a\ubd7b\ubd7c\ubd7d\ubd7e\ubd7f\ubd80\ubd81\ubd82\ubd83\ubd84\ubd85\ubd86\ubd87\ubd88\ubd89\ubd8a\ubd8b\ubd8c\ubd8d\ubd8e\ubd8f\ubd90\ubd91\ubd92\ubd93\ubd94\ubd95\ubd96\ubd97\ubd98\ubd99\ubd9a\ubd9b\ubd9c\ubd9d\ubd9e\ubd9f\ubda0\ubda1\ubda2\ubda3\ubda4\ubda5\ubda6\ubda7\ubda8\ubda9\ubdaa\ubdab\ubdac\ubdad\ubdae\ubdaf\ubdb0\ubdb1\ubdb2\ubdb3\ubdb4\ubdb5\ubdb6\ubdb7\ubdb8\ubdb9\ubdba\ubdbb\ubdbc\ubdbd\ubdbe\ubdbf\ubdc0\ubdc1\ubdc2\ubdc3\ubdc4\ubdc5\ubdc6\ubdc7\ubdc8\ubdc9\ubdca\ubdcb\ubdcc\ubdcd\ubdce\ubdcf\ubdd0\ubdd1\ubdd2\ubdd3\ubdd4\ubdd5\ubdd6\ubdd7\ubdd8\ubdd9\ubdda\ubddb\ubddc\ubddd\ubdde\ubddf\ubde0\ubde1\ubde2\ubde3\ubde4\ubde5\ubde6\ubde7\ubde8\ubde9\ubdea\ubdeb\ubdec\ubded\ubdee\ubdef\ubdf0\ubdf1\ubdf2\ubdf3\ubdf4\ubdf5\ubdf6\ubdf7\ubdf8\ubdf9\ubdfa\ubdfb\ubdfc\ubdfd\ubdfe\ubdff\ube00\ube01\ube02\ube03\ube04\ube05\ube06\ube07\ube08\ube09\ube0a\ube0b\ube0c\ube0d\ube0e\ube0f\ube10\ube11\ube12\ube13\ube14\ube15\ube16\ube17\ube18\ube19\ube1a\ube1b\ube1c\ube1d\ube1e\ube1f\ube20\ube21\ube22\ube23\ube24\ube25\ube26\ube27\ube28\ube29\ube2a\ube2b\ube2c\ube2d\ube2e\ube2f\ube30\ube31\ube32\ube33\ube34\ube35\ube36\ube37\ube38\ube39\ube3a\ube3b\ube3c\ube3d\ube3e\ube3f\ube40\ube41\ube42\ube43\ube44\ube45\ube46\ube47\ube48\ube49\ube4a\ube4b\ube4c\ube4d\ube4e\ube4f\ube50\ube51\ube52\ube53\ube54\ube55\ube56\ube57\ube58\ube59\ube5a\ube5b\ube5c\ube5d\ube5e\ube5f\ube60\ube61\ube62\ube63\ube64\ube65\ube66\ube67\ube68\ube69\ube6a\ube6b\ube6c\ube6d\ube6e\ube6f\ube70\ube71\ube72\ube73\ube74\ube75\ube76\ube77\ube78\ube79\ube7a\ube7b\ube7c\ube7d\ube7e\ube7f\ube80\ube81\ube82\ube83\ube84\ube85\ube86\ube87\ube88\ube89\ube8a\ube8b\ube8c\ube8d\ube8e\ube8f\ube90\ube91\ube92\ube93\ube94\ube95\ube96\ube97\ube98\ube99\ube9a\ube9b\ube9c\ube9d\ube9e\ube9f\ubea0\ubea1\ubea2\ubea3\ubea4\ubea5\ubea6\ubea7\ubea8\ubea9\ubeaa\ubeab\ubeac\ubead\ubeae\ubeaf\ubeb0\ubeb1\ubeb2\ubeb3\ubeb4\ubeb5\ubeb6\ubeb7\ubeb8\ubeb9\ubeba\ubebb\ubebc\ubebd\ubebe\ubebf\ubec0\ubec1\ubec2\ubec3\ubec4\ubec5\ubec6\ubec7\ubec8\ubec9\ubeca\ubecb\ubecc\ubecd\ubece\ubecf\ubed0\ubed1\ubed2\ubed3\ubed4\ubed5\ubed6\ubed7\ubed8\ubed9\ubeda\ubedb\ubedc\ubedd\ubede\ubedf\ubee0\ubee1\ubee2\ubee3\ubee4\ubee5\ubee6\ubee7\ubee8\ubee9\ubeea\ubeeb\ubeec\ubeed\ubeee\ubeef\ubef0\ubef1\ubef2\ubef3\ubef4\ubef5\ubef6\ubef7\ubef8\ubef9\ubefa\ubefb\ubefc\ubefd\ubefe\ubeff\ubf00\ubf01\ubf02\ubf03\ubf04\ubf05\ubf06\ubf07\ubf08\ubf09\ubf0a\ubf0b\ubf0c\ubf0d\ubf0e\ubf0f\ubf10\ubf11\ubf12\ubf13\ubf14\ubf15\ubf16\ubf17\ubf18\ubf19\ubf1a\ubf1b\ubf1c\ubf1d\ubf1e\ubf1f\ubf20\ubf21\ubf22\ubf23\ubf24\ubf25\ubf26\ubf27\ubf28\ubf29\ubf2a\ubf2b\ubf2c\ubf2d\ubf2e\ubf2f\ubf30\ubf31\ubf32\ubf33\ubf34\ubf35\ubf36\ubf37\ubf38\ubf39\ubf3a\ubf3b\ubf3c\ubf3d\ubf3e\ubf3f\ubf40\ubf41\ubf42\ubf43\ubf44\ubf45\ubf46\ubf47\ubf48\ubf49\ubf4a\ubf4b\ubf4c\ubf4d\ubf4e\ubf4f\ubf50\ubf51\ubf52\ubf53\ubf54\ubf55\ubf56\ubf57\ubf58\ubf59\ubf5a\ubf5b\ubf5c\ubf5d\ubf5e\ubf5f\ubf60\ubf61\ubf62\ubf63\ubf64\ubf65\ubf66\ubf67\ubf68\ubf69\ubf6a\ubf6b\ubf6c\ubf6d\ubf6e\ubf6f\ubf70\ubf71\ubf72\ubf73\ubf74\ubf75\ubf76\ubf77\ubf78\ubf79\ubf7a\ubf7b\ubf7c\ubf7d\ubf7e\ubf7f\ubf80\ubf81\ubf82\ubf83\ubf84\ubf85\ubf86\ubf87\ubf88\ubf89\ubf8a\ubf8b\ubf8c\ubf8d\ubf8e\ubf8f\ubf90\ubf91\ubf92\ubf93\ubf94\ubf95\ubf96\ubf97\ubf98\ubf99\ubf9a\ubf9b\ubf9c\ubf9d\ubf9e\ubf9f\ubfa0\ubfa1\ubfa2\ubfa3\ubfa4\ubfa5\ubfa6\ubfa7\ubfa8\ubfa9\ubfaa\ubfab\ubfac\ubfad\ubfae\ubfaf\ubfb0\ubfb1\ubfb2\ubfb3\ubfb4\ubfb5\ubfb6\ubfb7\ubfb8\ubfb9\ubfba\ubfbb\ubfbc\ubfbd\ubfbe\ubfbf\ubfc0\ubfc1\ubfc2\ubfc3\ubfc4\ubfc5\ubfc6\ubfc7\ubfc8\ubfc9\ubfca\ubfcb\ubfcc\ubfcd\ubfce\ubfcf\ubfd0\ubfd1\ubfd2\ubfd3\ubfd4\ubfd5\ubfd6\ubfd7\ubfd8\ubfd9\ubfda\ubfdb\ubfdc\ubfdd\ubfde\ubfdf\ubfe0\ubfe1\ubfe2\ubfe3\ubfe4\ubfe5\ubfe6\ubfe7\ubfe8\ubfe9\ubfea\ubfeb\ubfec\ubfed\ubfee\ubfef\ubff0\ubff1\ubff2\ubff3\ubff4\ubff5\ubff6\ubff7\ubff8\ubff9\ubffa\ubffb\ubffc\ubffd\ubffe\ubfff\uc000\uc001\uc002\uc003\uc004\uc005\uc006\uc007\uc008\uc009\uc00a\uc00b\uc00c\uc00d\uc00e\uc00f\uc010\uc011\uc012\uc013\uc014\uc015\uc016\uc017\uc018\uc019\uc01a\uc01b\uc01c\uc01d\uc01e\uc01f\uc020\uc021\uc022\uc023\uc024\uc025\uc026\uc027\uc028\uc029\uc02a\uc02b\uc02c\uc02d\uc02e\uc02f\uc030\uc031\uc032\uc033\uc034\uc035\uc036\uc037\uc038\uc039\uc03a\uc03b\uc03c\uc03d\uc03e\uc03f\uc040\uc041\uc042\uc043\uc044\uc045\uc046\uc047\uc048\uc049\uc04a\uc04b\uc04c\uc04d\uc04e\uc04f\uc050\uc051\uc052\uc053\uc054\uc055\uc056\uc057\uc058\uc059\uc05a\uc05b\uc05c\uc05d\uc05e\uc05f\uc060\uc061\uc062\uc063\uc064\uc065\uc066\uc067\uc068\uc069\uc06a\uc06b\uc06c\uc06d\uc06e\uc06f\uc070\uc071\uc072\uc073\uc074\uc075\uc076\uc077\uc078\uc079\uc07a\uc07b\uc07c\uc07d\uc07e\uc07f\uc080\uc081\uc082\uc083\uc084\uc085\uc086\uc087\uc088\uc089\uc08a\uc08b\uc08c\uc08d\uc08e\uc08f\uc090\uc091\uc092\uc093\uc094\uc095\uc096\uc097\uc098\uc099\uc09a\uc09b\uc09c\uc09d\uc09e\uc09f\uc0a0\uc0a1\uc0a2\uc0a3\uc0a4\uc0a5\uc0a6\uc0a7\uc0a8\uc0a9\uc0aa\uc0ab\uc0ac\uc0ad\uc0ae\uc0af\uc0b0\uc0b1\uc0b2\uc0b3\uc0b4\uc0b5\uc0b6\uc0b7\uc0b8\uc0b9\uc0ba\uc0bb\uc0bc\uc0bd\uc0be\uc0bf\uc0c0\uc0c1\uc0c2\uc0c3\uc0c4\uc0c5\uc0c6\uc0c7\uc0c8\uc0c9\uc0ca\uc0cb\uc0cc\uc0cd\uc0ce\uc0cf\uc0d0\uc0d1\uc0d2\uc0d3\uc0d4\uc0d5\uc0d6\uc0d7\uc0d8\uc0d9\uc0da\uc0db\uc0dc\uc0dd\uc0de\uc0df\uc0e0\uc0e1\uc0e2\uc0e3\uc0e4\uc0e5\uc0e6\uc0e7\uc0e8\uc0e9\uc0ea\uc0eb\uc0ec\uc0ed\uc0ee\uc0ef\uc0f0\uc0f1\uc0f2\uc0f3\uc0f4\uc0f5\uc0f6\uc0f7\uc0f8\uc0f9\uc0fa\uc0fb\uc0fc\uc0fd\uc0fe\uc0ff\uc100\uc101\uc102\uc103\uc104\uc105\uc106\uc107\uc108\uc109\uc10a\uc10b\uc10c\uc10d\uc10e\uc10f\uc110\uc111\uc112\uc113\uc114\uc115\uc116\uc117\uc118\uc119\uc11a\uc11b\uc11c\uc11d\uc11e\uc11f\uc120\uc121\uc122\uc123\uc124\uc125\uc126\uc127\uc128\uc129\uc12a\uc12b\uc12c\uc12d\uc12e\uc12f\uc130\uc131\uc132\uc133\uc134\uc135\uc136\uc137\uc138\uc139\uc13a\uc13b\uc13c\uc13d\uc13e\uc13f\uc140\uc141\uc142\uc143\uc144\uc145\uc146\uc147\uc148\uc149\uc14a\uc14b\uc14c\uc14d\uc14e\uc14f\uc150\uc151\uc152\uc153\uc154\uc155\uc156\uc157\uc158\uc159\uc15a\uc15b\uc15c\uc15d\uc15e\uc15f\uc160\uc161\uc162\uc163\uc164\uc165\uc166\uc167\uc168\uc169\uc16a\uc16b\uc16c\uc16d\uc16e\uc16f\uc170\uc171\uc172\uc173\uc174\uc175\uc176\uc177\uc178\uc179\uc17a\uc17b\uc17c\uc17d\uc17e\uc17f\uc180\uc181\uc182\uc183\uc184\uc185\uc186\uc187\uc188\uc189\uc18a\uc18b\uc18c\uc18d\uc18e\uc18f\uc190\uc191\uc192\uc193\uc194\uc195\uc196\uc197\uc198\uc199\uc19a\uc19b\uc19c\uc19d\uc19e\uc19f\uc1a0\uc1a1\uc1a2\uc1a3\uc1a4\uc1a5\uc1a6\uc1a7\uc1a8\uc1a9\uc1aa\uc1ab\uc1ac\uc1ad\uc1ae\uc1af\uc1b0\uc1b1\uc1b2\uc1b3\uc1b4\uc1b5\uc1b6\uc1b7\uc1b8\uc1b9\uc1ba\uc1bb\uc1bc\uc1bd\uc1be\uc1bf\uc1c0\uc1c1\uc1c2\uc1c3\uc1c4\uc1c5\uc1c6\uc1c7\uc1c8\uc1c9\uc1ca\uc1cb\uc1cc\uc1cd\uc1ce\uc1cf\uc1d0\uc1d1\uc1d2\uc1d3\uc1d4\uc1d5\uc1d6\uc1d7\uc1d8\uc1d9\uc1da\uc1db\uc1dc\uc1dd\uc1de\uc1df\uc1e0\uc1e1\uc1e2\uc1e3\uc1e4\uc1e5\uc1e6\uc1e7\uc1e8\uc1e9\uc1ea\uc1eb\uc1ec\uc1ed\uc1ee\uc1ef\uc1f0\uc1f1\uc1f2\uc1f3\uc1f4\uc1f5\uc1f6\uc1f7\uc1f8\uc1f9\uc1fa\uc1fb\uc1fc\uc1fd\uc1fe\uc1ff\uc200\uc201\uc202\uc203\uc204\uc205\uc206\uc207\uc208\uc209\uc20a\uc20b\uc20c\uc20d\uc20e\uc20f\uc210\uc211\uc212\uc213\uc214\uc215\uc216\uc217\uc218\uc219\uc21a\uc21b\uc21c\uc21d\uc21e\uc21f\uc220\uc221\uc222\uc223\uc224\uc225\uc226\uc227\uc228\uc229\uc22a\uc22b\uc22c\uc22d\uc22e\uc22f\uc230\uc231\uc232\uc233\uc234\uc235\uc236\uc237\uc238\uc239\uc23a\uc23b\uc23c\uc23d\uc23e\uc23f\uc240\uc241\uc242\uc243\uc244\uc245\uc246\uc247\uc248\uc249\uc24a\uc24b\uc24c\uc24d\uc24e\uc24f\uc250\uc251\uc252\uc253\uc254\uc255\uc256\uc257\uc258\uc259\uc25a\uc25b\uc25c\uc25d\uc25e\uc25f\uc260\uc261\uc262\uc263\uc264\uc265\uc266\uc267\uc268\uc269\uc26a\uc26b\uc26c\uc26d\uc26e\uc26f\uc270\uc271\uc272\uc273\uc274\uc275\uc276\uc277\uc278\uc279\uc27a\uc27b\uc27c\uc27d\uc27e\uc27f\uc280\uc281\uc282\uc283\uc284\uc285\uc286\uc287\uc288\uc289\uc28a\uc28b\uc28c\uc28d\uc28e\uc28f\uc290\uc291\uc292\uc293\uc294\uc295\uc296\uc297\uc298\uc299\uc29a\uc29b\uc29c\uc29d\uc29e\uc29f\uc2a0\uc2a1\uc2a2\uc2a3\uc2a4\uc2a5\uc2a6\uc2a7\uc2a8\uc2a9\uc2aa\uc2ab\uc2ac\uc2ad\uc2ae\uc2af\uc2b0\uc2b1\uc2b2\uc2b3\uc2b4\uc2b5\uc2b6\uc2b7\uc2b8\uc2b9\uc2ba\uc2bb\uc2bc\uc2bd\uc2be\uc2bf\uc2c0\uc2c1\uc2c2\uc2c3\uc2c4\uc2c5\uc2c6\uc2c7\uc2c8\uc2c9\uc2ca\uc2cb\uc2cc\uc2cd\uc2ce\uc2cf\uc2d0\uc2d1\uc2d2\uc2d3\uc2d4\uc2d5\uc2d6\uc2d7\uc2d8\uc2d9\uc2da\uc2db\uc2dc\uc2dd\uc2de\uc2df\uc2e0\uc2e1\uc2e2\uc2e3\uc2e4\uc2e5\uc2e6\uc2e7\uc2e8\uc2e9\uc2ea\uc2eb\uc2ec\uc2ed\uc2ee\uc2ef\uc2f0\uc2f1\uc2f2\uc2f3\uc2f4\uc2f5\uc2f6\uc2f7\uc2f8\uc2f9\uc2fa\uc2fb\uc2fc\uc2fd\uc2fe\uc2ff\uc300\uc301\uc302\uc303\uc304\uc305\uc306\uc307\uc308\uc309\uc30a\uc30b\uc30c\uc30d\uc30e\uc30f\uc310\uc311\uc312\uc313\uc314\uc315\uc316\uc317\uc318\uc319\uc31a\uc31b\uc31c\uc31d\uc31e\uc31f\uc320\uc321\uc322\uc323\uc324\uc325\uc326\uc327\uc328\uc329\uc32a\uc32b\uc32c\uc32d\uc32e\uc32f\uc330\uc331\uc332\uc333\uc334\uc335\uc336\uc337\uc338\uc339\uc33a\uc33b\uc33c\uc33d\uc33e\uc33f\uc340\uc341\uc342\uc343\uc344\uc345\uc346\uc347\uc348\uc349\uc34a\uc34b\uc34c\uc34d\uc34e\uc34f\uc350\uc351\uc352\uc353\uc354\uc355\uc356\uc357\uc358\uc359\uc35a\uc35b\uc35c\uc35d\uc35e\uc35f\uc360\uc361\uc362\uc363\uc364\uc365\uc366\uc367\uc368\uc369\uc36a\uc36b\uc36c\uc36d\uc36e\uc36f\uc370\uc371\uc372\uc373\uc374\uc375\uc376\uc377\uc378\uc379\uc37a\uc37b\uc37c\uc37d\uc37e\uc37f\uc380\uc381\uc382\uc383\uc384\uc385\uc386\uc387\uc388\uc389\uc38a\uc38b\uc38c\uc38d\uc38e\uc38f\uc390\uc391\uc392\uc393\uc394\uc395\uc396\uc397\uc398\uc399\uc39a\uc39b\uc39c\uc39d\uc39e\uc39f\uc3a0\uc3a1\uc3a2\uc3a3\uc3a4\uc3a5\uc3a6\uc3a7\uc3a8\uc3a9\uc3aa\uc3ab\uc3ac\uc3ad\uc3ae\uc3af\uc3b0\uc3b1\uc3b2\uc3b3\uc3b4\uc3b5\uc3b6\uc3b7\uc3b8\uc3b9\uc3ba\uc3bb\uc3bc\uc3bd\uc3be\uc3bf\uc3c0\uc3c1\uc3c2\uc3c3\uc3c4\uc3c5\uc3c6\uc3c7\uc3c8\uc3c9\uc3ca\uc3cb\uc3cc\uc3cd\uc3ce\uc3cf\uc3d0\uc3d1\uc3d2\uc3d3\uc3d4\uc3d5\uc3d6\uc3d7\uc3d8\uc3d9\uc3da\uc3db\uc3dc\uc3dd\uc3de\uc3df\uc3e0\uc3e1\uc3e2\uc3e3\uc3e4\uc3e5\uc3e6\uc3e7\uc3e8\uc3e9\uc3ea\uc3eb\uc3ec\uc3ed\uc3ee\uc3ef\uc3f0\uc3f1\uc3f2\uc3f3\uc3f4\uc3f5\uc3f6\uc3f7\uc3f8\uc3f9\uc3fa\uc3fb\uc3fc\uc3fd\uc3fe\uc3ff\uc400\uc401\uc402\uc403\uc404\uc405\uc406\uc407\uc408\uc409\uc40a\uc40b\uc40c\uc40d\uc40e\uc40f\uc410\uc411\uc412\uc413\uc414\uc415\uc416\uc417\uc418\uc419\uc41a\uc41b\uc41c\uc41d\uc41e\uc41f\uc420\uc421\uc422\uc423\uc424\uc425\uc426\uc427\uc428\uc429\uc42a\uc42b\uc42c\uc42d\uc42e\uc42f\uc430\uc431\uc432\uc433\uc434\uc435\uc436\uc437\uc438\uc439\uc43a\uc43b\uc43c\uc43d\uc43e\uc43f\uc440\uc441\uc442\uc443\uc444\uc445\uc446\uc447\uc448\uc449\uc44a\uc44b\uc44c\uc44d\uc44e\uc44f\uc450\uc451\uc452\uc453\uc454\uc455\uc456\uc457\uc458\uc459\uc45a\uc45b\uc45c\uc45d\uc45e\uc45f\uc460\uc461\uc462\uc463\uc464\uc465\uc466\uc467\uc468\uc469\uc46a\uc46b\uc46c\uc46d\uc46e\uc46f\uc470\uc471\uc472\uc473\uc474\uc475\uc476\uc477\uc478\uc479\uc47a\uc47b\uc47c\uc47d\uc47e\uc47f\uc480\uc481\uc482\uc483\uc484\uc485\uc486\uc487\uc488\uc489\uc48a\uc48b\uc48c\uc48d\uc48e\uc48f\uc490\uc491\uc492\uc493\uc494\uc495\uc496\uc497\uc498\uc499\uc49a\uc49b\uc49c\uc49d\uc49e\uc49f\uc4a0\uc4a1\uc4a2\uc4a3\uc4a4\uc4a5\uc4a6\uc4a7\uc4a8\uc4a9\uc4aa\uc4ab\uc4ac\uc4ad\uc4ae\uc4af\uc4b0\uc4b1\uc4b2\uc4b3\uc4b4\uc4b5\uc4b6\uc4b7\uc4b8\uc4b9\uc4ba\uc4bb\uc4bc\uc4bd\uc4be\uc4bf\uc4c0\uc4c1\uc4c2\uc4c3\uc4c4\uc4c5\uc4c6\uc4c7\uc4c8\uc4c9\uc4ca\uc4cb\uc4cc\uc4cd\uc4ce\uc4cf\uc4d0\uc4d1\uc4d2\uc4d3\uc4d4\uc4d5\uc4d6\uc4d7\uc4d8\uc4d9\uc4da\uc4db\uc4dc\uc4dd\uc4de\uc4df\uc4e0\uc4e1\uc4e2\uc4e3\uc4e4\uc4e5\uc4e6\uc4e7\uc4e8\uc4e9\uc4ea\uc4eb\uc4ec\uc4ed\uc4ee\uc4ef\uc4f0\uc4f1\uc4f2\uc4f3\uc4f4\uc4f5\uc4f6\uc4f7\uc4f8\uc4f9\uc4fa\uc4fb\uc4fc\uc4fd\uc4fe\uc4ff\uc500\uc501\uc502\uc503\uc504\uc505\uc506\uc507\uc508\uc509\uc50a\uc50b\uc50c\uc50d\uc50e\uc50f\uc510\uc511\uc512\uc513\uc514\uc515\uc516\uc517\uc518\uc519\uc51a\uc51b\uc51c\uc51d\uc51e\uc51f\uc520\uc521\uc522\uc523\uc524\uc525\uc526\uc527\uc528\uc529\uc52a\uc52b\uc52c\uc52d\uc52e\uc52f\uc530\uc531\uc532\uc533\uc534\uc535\uc536\uc537\uc538\uc539\uc53a\uc53b\uc53c\uc53d\uc53e\uc53f\uc540\uc541\uc542\uc543\uc544\uc545\uc546\uc547\uc548\uc549\uc54a\uc54b\uc54c\uc54d\uc54e\uc54f\uc550\uc551\uc552\uc553\uc554\uc555\uc556\uc557\uc558\uc559\uc55a\uc55b\uc55c\uc55d\uc55e\uc55f\uc560\uc561\uc562\uc563\uc564\uc565\uc566\uc567\uc568\uc569\uc56a\uc56b\uc56c\uc56d\uc56e\uc56f\uc570\uc571\uc572\uc573\uc574\uc575\uc576\uc577\uc578\uc579\uc57a\uc57b\uc57c\uc57d\uc57e\uc57f\uc580\uc581\uc582\uc583\uc584\uc585\uc586\uc587\uc588\uc589\uc58a\uc58b\uc58c\uc58d\uc58e\uc58f\uc590\uc591\uc592\uc593\uc594\uc595\uc596\uc597\uc598\uc599\uc59a\uc59b\uc59c\uc59d\uc59e\uc59f\uc5a0\uc5a1\uc5a2\uc5a3\uc5a4\uc5a5\uc5a6\uc5a7\uc5a8\uc5a9\uc5aa\uc5ab\uc5ac\uc5ad\uc5ae\uc5af\uc5b0\uc5b1\uc5b2\uc5b3\uc5b4\uc5b5\uc5b6\uc5b7\uc5b8\uc5b9\uc5ba\uc5bb\uc5bc\uc5bd\uc5be\uc5bf\uc5c0\uc5c1\uc5c2\uc5c3\uc5c4\uc5c5\uc5c6\uc5c7\uc5c8\uc5c9\uc5ca\uc5cb\uc5cc\uc5cd\uc5ce\uc5cf\uc5d0\uc5d1\uc5d2\uc5d3\uc5d4\uc5d5\uc5d6\uc5d7\uc5d8\uc5d9\uc5da\uc5db\uc5dc\uc5dd\uc5de\uc5df\uc5e0\uc5e1\uc5e2\uc5e3\uc5e4\uc5e5\uc5e6\uc5e7\uc5e8\uc5e9\uc5ea\uc5eb\uc5ec\uc5ed\uc5ee\uc5ef\uc5f0\uc5f1\uc5f2\uc5f3\uc5f4\uc5f5\uc5f6\uc5f7\uc5f8\uc5f9\uc5fa\uc5fb\uc5fc\uc5fd\uc5fe\uc5ff\uc600\uc601\uc602\uc603\uc604\uc605\uc606\uc607\uc608\uc609\uc60a\uc60b\uc60c\uc60d\uc60e\uc60f\uc610\uc611\uc612\uc613\uc614\uc615\uc616\uc617\uc618\uc619\uc61a\uc61b\uc61c\uc61d\uc61e\uc61f\uc620\uc621\uc622\uc623\uc624\uc625\uc626\uc627\uc628\uc629\uc62a\uc62b\uc62c\uc62d\uc62e\uc62f\uc630\uc631\uc632\uc633\uc634\uc635\uc636\uc637\uc638\uc639\uc63a\uc63b\uc63c\uc63d\uc63e\uc63f\uc640\uc641\uc642\uc643\uc644\uc645\uc646\uc647\uc648\uc649\uc64a\uc64b\uc64c\uc64d\uc64e\uc64f\uc650\uc651\uc652\uc653\uc654\uc655\uc656\uc657\uc658\uc659\uc65a\uc65b\uc65c\uc65d\uc65e\uc65f\uc660\uc661\uc662\uc663\uc664\uc665\uc666\uc667\uc668\uc669\uc66a\uc66b\uc66c\uc66d\uc66e\uc66f\uc670\uc671\uc672\uc673\uc674\uc675\uc676\uc677\uc678\uc679\uc67a\uc67b\uc67c\uc67d\uc67e\uc67f\uc680\uc681\uc682\uc683\uc684\uc685\uc686\uc687\uc688\uc689\uc68a\uc68b\uc68c\uc68d\uc68e\uc68f\uc690\uc691\uc692\uc693\uc694\uc695\uc696\uc697\uc698\uc699\uc69a\uc69b\uc69c\uc69d\uc69e\uc69f\uc6a0\uc6a1\uc6a2\uc6a3\uc6a4\uc6a5\uc6a6\uc6a7\uc6a8\uc6a9\uc6aa\uc6ab\uc6ac\uc6ad\uc6ae\uc6af\uc6b0\uc6b1\uc6b2\uc6b3\uc6b4\uc6b5\uc6b6\uc6b7\uc6b8\uc6b9\uc6ba\uc6bb\uc6bc\uc6bd\uc6be\uc6bf\uc6c0\uc6c1\uc6c2\uc6c3\uc6c4\uc6c5\uc6c6\uc6c7\uc6c8\uc6c9\uc6ca\uc6cb\uc6cc\uc6cd\uc6ce\uc6cf\uc6d0\uc6d1\uc6d2\uc6d3\uc6d4\uc6d5\uc6d6\uc6d7\uc6d8\uc6d9\uc6da\uc6db\uc6dc\uc6dd\uc6de\uc6df\uc6e0\uc6e1\uc6e2\uc6e3\uc6e4\uc6e5\uc6e6\uc6e7\uc6e8\uc6e9\uc6ea\uc6eb\uc6ec\uc6ed\uc6ee\uc6ef\uc6f0\uc6f1\uc6f2\uc6f3\uc6f4\uc6f5\uc6f6\uc6f7\uc6f8\uc6f9\uc6fa\uc6fb\uc6fc\uc6fd\uc6fe\uc6ff\uc700\uc701\uc702\uc703\uc704\uc705\uc706\uc707\uc708\uc709\uc70a\uc70b\uc70c\uc70d\uc70e\uc70f\uc710\uc711\uc712\uc713\uc714\uc715\uc716\uc717\uc718\uc719\uc71a\uc71b\uc71c\uc71d\uc71e\uc71f\uc720\uc721\uc722\uc723\uc724\uc725\uc726\uc727\uc728\uc729\uc72a\uc72b\uc72c\uc72d\uc72e\uc72f\uc730\uc731\uc732\uc733\uc734\uc735\uc736\uc737\uc738\uc739\uc73a\uc73b\uc73c\uc73d\uc73e\uc73f\uc740\uc741\uc742\uc743\uc744\uc745\uc746\uc747\uc748\uc749\uc74a\uc74b\uc74c\uc74d\uc74e\uc74f\uc750\uc751\uc752\uc753\uc754\uc755\uc756\uc757\uc758\uc759\uc75a\uc75b\uc75c\uc75d\uc75e\uc75f\uc760\uc761\uc762\uc763\uc764\uc765\uc766\uc767\uc768\uc769\uc76a\uc76b\uc76c\uc76d\uc76e\uc76f\uc770\uc771\uc772\uc773\uc774\uc775\uc776\uc777\uc778\uc779\uc77a\uc77b\uc77c\uc77d\uc77e\uc77f\uc780\uc781\uc782\uc783\uc784\uc785\uc786\uc787\uc788\uc789\uc78a\uc78b\uc78c\uc78d\uc78e\uc78f\uc790\uc791\uc792\uc793\uc794\uc795\uc796\uc797\uc798\uc799\uc79a\uc79b\uc79c\uc79d\uc79e\uc79f\uc7a0\uc7a1\uc7a2\uc7a3\uc7a4\uc7a5\uc7a6\uc7a7\uc7a8\uc7a9\uc7aa\uc7ab\uc7ac\uc7ad\uc7ae\uc7af\uc7b0\uc7b1\uc7b2\uc7b3\uc7b4\uc7b5\uc7b6\uc7b7\uc7b8\uc7b9\uc7ba\uc7bb\uc7bc\uc7bd\uc7be\uc7bf\uc7c0\uc7c1\uc7c2\uc7c3\uc7c4\uc7c5\uc7c6\uc7c7\uc7c8\uc7c9\uc7ca\uc7cb\uc7cc\uc7cd\uc7ce\uc7cf\uc7d0\uc7d1\uc7d2\uc7d3\uc7d4\uc7d5\uc7d6\uc7d7\uc7d8\uc7d9\uc7da\uc7db\uc7dc\uc7dd\uc7de\uc7df\uc7e0\uc7e1\uc7e2\uc7e3\uc7e4\uc7e5\uc7e6\uc7e7\uc7e8\uc7e9\uc7ea\uc7eb\uc7ec\uc7ed\uc7ee\uc7ef\uc7f0\uc7f1\uc7f2\uc7f3\uc7f4\uc7f5\uc7f6\uc7f7\uc7f8\uc7f9\uc7fa\uc7fb\uc7fc\uc7fd\uc7fe\uc7ff\uc800\uc801\uc802\uc803\uc804\uc805\uc806\uc807\uc808\uc809\uc80a\uc80b\uc80c\uc80d\uc80e\uc80f\uc810\uc811\uc812\uc813\uc814\uc815\uc816\uc817\uc818\uc819\uc81a\uc81b\uc81c\uc81d\uc81e\uc81f\uc820\uc821\uc822\uc823\uc824\uc825\uc826\uc827\uc828\uc829\uc82a\uc82b\uc82c\uc82d\uc82e\uc82f\uc830\uc831\uc832\uc833\uc834\uc835\uc836\uc837\uc838\uc839\uc83a\uc83b\uc83c\uc83d\uc83e\uc83f\uc840\uc841\uc842\uc843\uc844\uc845\uc846\uc847\uc848\uc849\uc84a\uc84b\uc84c\uc84d\uc84e\uc84f\uc850\uc851\uc852\uc853\uc854\uc855\uc856\uc857\uc858\uc859\uc85a\uc85b\uc85c\uc85d\uc85e\uc85f\uc860\uc861\uc862\uc863\uc864\uc865\uc866\uc867\uc868\uc869\uc86a\uc86b\uc86c\uc86d\uc86e\uc86f\uc870\uc871\uc872\uc873\uc874\uc875\uc876\uc877\uc878\uc879\uc87a\uc87b\uc87c\uc87d\uc87e\uc87f\uc880\uc881\uc882\uc883\uc884\uc885\uc886\uc887\uc888\uc889\uc88a\uc88b\uc88c\uc88d\uc88e\uc88f\uc890\uc891\uc892\uc893\uc894\uc895\uc896\uc897\uc898\uc899\uc89a\uc89b\uc89c\uc89d\uc89e\uc89f\uc8a0\uc8a1\uc8a2\uc8a3\uc8a4\uc8a5\uc8a6\uc8a7\uc8a8\uc8a9\uc8aa\uc8ab\uc8ac\uc8ad\uc8ae\uc8af\uc8b0\uc8b1\uc8b2\uc8b3\uc8b4\uc8b5\uc8b6\uc8b7\uc8b8\uc8b9\uc8ba\uc8bb\uc8bc\uc8bd\uc8be\uc8bf\uc8c0\uc8c1\uc8c2\uc8c3\uc8c4\uc8c5\uc8c6\uc8c7\uc8c8\uc8c9\uc8ca\uc8cb\uc8cc\uc8cd\uc8ce\uc8cf\uc8d0\uc8d1\uc8d2\uc8d3\uc8d4\uc8d5\uc8d6\uc8d7\uc8d8\uc8d9\uc8da\uc8db\uc8dc\uc8dd\uc8de\uc8df\uc8e0\uc8e1\uc8e2\uc8e3\uc8e4\uc8e5\uc8e6\uc8e7\uc8e8\uc8e9\uc8ea\uc8eb\uc8ec\uc8ed\uc8ee\uc8ef\uc8f0\uc8f1\uc8f2\uc8f3\uc8f4\uc8f5\uc8f6\uc8f7\uc8f8\uc8f9\uc8fa\uc8fb\uc8fc\uc8fd\uc8fe\uc8ff\uc900\uc901\uc902\uc903\uc904\uc905\uc906\uc907\uc908\uc909\uc90a\uc90b\uc90c\uc90d\uc90e\uc90f\uc910\uc911\uc912\uc913\uc914\uc915\uc916\uc917\uc918\uc919\uc91a\uc91b\uc91c\uc91d\uc91e\uc91f\uc920\uc921\uc922\uc923\uc924\uc925\uc926\uc927\uc928\uc929\uc92a\uc92b\uc92c\uc92d\uc92e\uc92f\uc930\uc931\uc932\uc933\uc934\uc935\uc936\uc937\uc938\uc939\uc93a\uc93b\uc93c\uc93d\uc93e\uc93f\uc940\uc941\uc942\uc943\uc944\uc945\uc946\uc947\uc948\uc949\uc94a\uc94b\uc94c\uc94d\uc94e\uc94f\uc950\uc951\uc952\uc953\uc954\uc955\uc956\uc957\uc958\uc959\uc95a\uc95b\uc95c\uc95d\uc95e\uc95f\uc960\uc961\uc962\uc963\uc964\uc965\uc966\uc967\uc968\uc969\uc96a\uc96b\uc96c\uc96d\uc96e\uc96f\uc970\uc971\uc972\uc973\uc974\uc975\uc976\uc977\uc978\uc979\uc97a\uc97b\uc97c\uc97d\uc97e\uc97f\uc980\uc981\uc982\uc983\uc984\uc985\uc986\uc987\uc988\uc989\uc98a\uc98b\uc98c\uc98d\uc98e\uc98f\uc990\uc991\uc992\uc993\uc994\uc995\uc996\uc997\uc998\uc999\uc99a\uc99b\uc99c\uc99d\uc99e\uc99f\uc9a0\uc9a1\uc9a2\uc9a3\uc9a4\uc9a5\uc9a6\uc9a7\uc9a8\uc9a9\uc9aa\uc9ab\uc9ac\uc9ad\uc9ae\uc9af\uc9b0\uc9b1\uc9b2\uc9b3\uc9b4\uc9b5\uc9b6\uc9b7\uc9b8\uc9b9\uc9ba\uc9bb\uc9bc\uc9bd\uc9be\uc9bf\uc9c0\uc9c1\uc9c2\uc9c3\uc9c4\uc9c5\uc9c6\uc9c7\uc9c8\uc9c9\uc9ca\uc9cb\uc9cc\uc9cd\uc9ce\uc9cf\uc9d0\uc9d1\uc9d2\uc9d3\uc9d4\uc9d5\uc9d6\uc9d7\uc9d8\uc9d9\uc9da\uc9db\uc9dc\uc9dd\uc9de\uc9df\uc9e0\uc9e1\uc9e2\uc9e3\uc9e4\uc9e5\uc9e6\uc9e7\uc9e8\uc9e9\uc9ea\uc9eb\uc9ec\uc9ed\uc9ee\uc9ef\uc9f0\uc9f1\uc9f2\uc9f3\uc9f4\uc9f5\uc9f6\uc9f7\uc9f8\uc9f9\uc9fa\uc9fb\uc9fc\uc9fd\uc9fe\uc9ff\uca00\uca01\uca02\uca03\uca04\uca05\uca06\uca07\uca08\uca09\uca0a\uca0b\uca0c\uca0d\uca0e\uca0f\uca10\uca11\uca12\uca13\uca14\uca15\uca16\uca17\uca18\uca19\uca1a\uca1b\uca1c\uca1d\uca1e\uca1f\uca20\uca21\uca22\uca23\uca24\uca25\uca26\uca27\uca28\uca29\uca2a\uca2b\uca2c\uca2d\uca2e\uca2f\uca30\uca31\uca32\uca33\uca34\uca35\uca36\uca37\uca38\uca39\uca3a\uca3b\uca3c\uca3d\uca3e\uca3f\uca40\uca41\uca42\uca43\uca44\uca45\uca46\uca47\uca48\uca49\uca4a\uca4b\uca4c\uca4d\uca4e\uca4f\uca50\uca51\uca52\uca53\uca54\uca55\uca56\uca57\uca58\uca59\uca5a\uca5b\uca5c\uca5d\uca5e\uca5f\uca60\uca61\uca62\uca63\uca64\uca65\uca66\uca67\uca68\uca69\uca6a\uca6b\uca6c\uca6d\uca6e\uca6f\uca70\uca71\uca72\uca73\uca74\uca75\uca76\uca77\uca78\uca79\uca7a\uca7b\uca7c\uca7d\uca7e\uca7f\uca80\uca81\uca82\uca83\uca84\uca85\uca86\uca87\uca88\uca89\uca8a\uca8b\uca8c\uca8d\uca8e\uca8f\uca90\uca91\uca92\uca93\uca94\uca95\uca96\uca97\uca98\uca99\uca9a\uca9b\uca9c\uca9d\uca9e\uca9f\ucaa0\ucaa1\ucaa2\ucaa3\ucaa4\ucaa5\ucaa6\ucaa7\ucaa8\ucaa9\ucaaa\ucaab\ucaac\ucaad\ucaae\ucaaf\ucab0\ucab1\ucab2\ucab3\ucab4\ucab5\ucab6\ucab7\ucab8\ucab9\ucaba\ucabb\ucabc\ucabd\ucabe\ucabf\ucac0\ucac1\ucac2\ucac3\ucac4\ucac5\ucac6\ucac7\ucac8\ucac9\ucaca\ucacb\ucacc\ucacd\ucace\ucacf\ucad0\ucad1\ucad2\ucad3\ucad4\ucad5\ucad6\ucad7\ucad8\ucad9\ucada\ucadb\ucadc\ucadd\ucade\ucadf\ucae0\ucae1\ucae2\ucae3\ucae4\ucae5\ucae6\ucae7\ucae8\ucae9\ucaea\ucaeb\ucaec\ucaed\ucaee\ucaef\ucaf0\ucaf1\ucaf2\ucaf3\ucaf4\ucaf5\ucaf6\ucaf7\ucaf8\ucaf9\ucafa\ucafb\ucafc\ucafd\ucafe\ucaff\ucb00\ucb01\ucb02\ucb03\ucb04\ucb05\ucb06\ucb07\ucb08\ucb09\ucb0a\ucb0b\ucb0c\ucb0d\ucb0e\ucb0f\ucb10\ucb11\ucb12\ucb13\ucb14\ucb15\ucb16\ucb17\ucb18\ucb19\ucb1a\ucb1b\ucb1c\ucb1d\ucb1e\ucb1f\ucb20\ucb21\ucb22\ucb23\ucb24\ucb25\ucb26\ucb27\ucb28\ucb29\ucb2a\ucb2b\ucb2c\ucb2d\ucb2e\ucb2f\ucb30\ucb31\ucb32\ucb33\ucb34\ucb35\ucb36\ucb37\ucb38\ucb39\ucb3a\ucb3b\ucb3c\ucb3d\ucb3e\ucb3f\ucb40\ucb41\ucb42\ucb43\ucb44\ucb45\ucb46\ucb47\ucb48\ucb49\ucb4a\ucb4b\ucb4c\ucb4d\ucb4e\ucb4f\ucb50\ucb51\ucb52\ucb53\ucb54\ucb55\ucb56\ucb57\ucb58\ucb59\ucb5a\ucb5b\ucb5c\ucb5d\ucb5e\ucb5f\ucb60\ucb61\ucb62\ucb63\ucb64\ucb65\ucb66\ucb67\ucb68\ucb69\ucb6a\ucb6b\ucb6c\ucb6d\ucb6e\ucb6f\ucb70\ucb71\ucb72\ucb73\ucb74\ucb75\ucb76\ucb77\ucb78\ucb79\ucb7a\ucb7b\ucb7c\ucb7d\ucb7e\ucb7f\ucb80\ucb81\ucb82\ucb83\ucb84\ucb85\ucb86\ucb87\ucb88\ucb89\ucb8a\ucb8b\ucb8c\ucb8d\ucb8e\ucb8f\ucb90\ucb91\ucb92\ucb93\ucb94\ucb95\ucb96\ucb97\ucb98\ucb99\ucb9a\ucb9b\ucb9c\ucb9d\ucb9e\ucb9f\ucba0\ucba1\ucba2\ucba3\ucba4\ucba5\ucba6\ucba7\ucba8\ucba9\ucbaa\ucbab\ucbac\ucbad\ucbae\ucbaf\ucbb0\ucbb1\ucbb2\ucbb3\ucbb4\ucbb5\ucbb6\ucbb7\ucbb8\ucbb9\ucbba\ucbbb\ucbbc\ucbbd\ucbbe\ucbbf\ucbc0\ucbc1\ucbc2\ucbc3\ucbc4\ucbc5\ucbc6\ucbc7\ucbc8\ucbc9\ucbca\ucbcb\ucbcc\ucbcd\ucbce\ucbcf\ucbd0\ucbd1\ucbd2\ucbd3\ucbd4\ucbd5\ucbd6\ucbd7\ucbd8\ucbd9\ucbda\ucbdb\ucbdc\ucbdd\ucbde\ucbdf\ucbe0\ucbe1\ucbe2\ucbe3\ucbe4\ucbe5\ucbe6\ucbe7\ucbe8\ucbe9\ucbea\ucbeb\ucbec\ucbed\ucbee\ucbef\ucbf0\ucbf1\ucbf2\ucbf3\ucbf4\ucbf5\ucbf6\ucbf7\ucbf8\ucbf9\ucbfa\ucbfb\ucbfc\ucbfd\ucbfe\ucbff\ucc00\ucc01\ucc02\ucc03\ucc04\ucc05\ucc06\ucc07\ucc08\ucc09\ucc0a\ucc0b\ucc0c\ucc0d\ucc0e\ucc0f\ucc10\ucc11\ucc12\ucc13\ucc14\ucc15\ucc16\ucc17\ucc18\ucc19\ucc1a\ucc1b\ucc1c\ucc1d\ucc1e\ucc1f\ucc20\ucc21\ucc22\ucc23\ucc24\ucc25\ucc26\ucc27\ucc28\ucc29\ucc2a\ucc2b\ucc2c\ucc2d\ucc2e\ucc2f\ucc30\ucc31\ucc32\ucc33\ucc34\ucc35\ucc36\ucc37\ucc38\ucc39\ucc3a\ucc3b\ucc3c\ucc3d\ucc3e\ucc3f\ucc40\ucc41\ucc42\ucc43\ucc44\ucc45\ucc46\ucc47\ucc48\ucc49\ucc4a\ucc4b\ucc4c\ucc4d\ucc4e\ucc4f\ucc50\ucc51\ucc52\ucc53\ucc54\ucc55\ucc56\ucc57\ucc58\ucc59\ucc5a\ucc5b\ucc5c\ucc5d\ucc5e\ucc5f\ucc60\ucc61\ucc62\ucc63\ucc64\ucc65\ucc66\ucc67\ucc68\ucc69\ucc6a\ucc6b\ucc6c\ucc6d\ucc6e\ucc6f\ucc70\ucc71\ucc72\ucc73\ucc74\ucc75\ucc76\ucc77\ucc78\ucc79\ucc7a\ucc7b\ucc7c\ucc7d\ucc7e\ucc7f\ucc80\ucc81\ucc82\ucc83\ucc84\ucc85\ucc86\ucc87\ucc88\ucc89\ucc8a\ucc8b\ucc8c\ucc8d\ucc8e\ucc8f\ucc90\ucc91\ucc92\ucc93\ucc94\ucc95\ucc96\ucc97\ucc98\ucc99\ucc9a\ucc9b\ucc9c\ucc9d\ucc9e\ucc9f\ucca0\ucca1\ucca2\ucca3\ucca4\ucca5\ucca6\ucca7\ucca8\ucca9\uccaa\uccab\uccac\uccad\uccae\uccaf\uccb0\uccb1\uccb2\uccb3\uccb4\uccb5\uccb6\uccb7\uccb8\uccb9\uccba\uccbb\uccbc\uccbd\uccbe\uccbf\uccc0\uccc1\uccc2\uccc3\uccc4\uccc5\uccc6\uccc7\uccc8\uccc9\uccca\ucccb\ucccc\ucccd\uccce\ucccf\uccd0\uccd1\uccd2\uccd3\uccd4\uccd5\uccd6\uccd7\uccd8\uccd9\uccda\uccdb\uccdc\uccdd\uccde\uccdf\ucce0\ucce1\ucce2\ucce3\ucce4\ucce5\ucce6\ucce7\ucce8\ucce9\uccea\ucceb\uccec\ucced\uccee\uccef\uccf0\uccf1\uccf2\uccf3\uccf4\uccf5\uccf6\uccf7\uccf8\uccf9\uccfa\uccfb\uccfc\uccfd\uccfe\uccff\ucd00\ucd01\ucd02\ucd03\ucd04\ucd05\ucd06\ucd07\ucd08\ucd09\ucd0a\ucd0b\ucd0c\ucd0d\ucd0e\ucd0f\ucd10\ucd11\ucd12\ucd13\ucd14\ucd15\ucd16\ucd17\ucd18\ucd19\ucd1a\ucd1b\ucd1c\ucd1d\ucd1e\ucd1f\ucd20\ucd21\ucd22\ucd23\ucd24\ucd25\ucd26\ucd27\ucd28\ucd29\ucd2a\ucd2b\ucd2c\ucd2d\ucd2e\ucd2f\ucd30\ucd31\ucd32\ucd33\ucd34\ucd35\ucd36\ucd37\ucd38\ucd39\ucd3a\ucd3b\ucd3c\ucd3d\ucd3e\ucd3f\ucd40\ucd41\ucd42\ucd43\ucd44\ucd45\ucd46\ucd47\ucd48\ucd49\ucd4a\ucd4b\ucd4c\ucd4d\ucd4e\ucd4f\ucd50\ucd51\ucd52\ucd53\ucd54\ucd55\ucd56\ucd57\ucd58\ucd59\ucd5a\ucd5b\ucd5c\ucd5d\ucd5e\ucd5f\ucd60\ucd61\ucd62\ucd63\ucd64\ucd65\ucd66\ucd67\ucd68\ucd69\ucd6a\ucd6b\ucd6c\ucd6d\ucd6e\ucd6f\ucd70\ucd71\ucd72\ucd73\ucd74\ucd75\ucd76\ucd77\ucd78\ucd79\ucd7a\ucd7b\ucd7c\ucd7d\ucd7e\ucd7f\ucd80\ucd81\ucd82\ucd83\ucd84\ucd85\ucd86\ucd87\ucd88\ucd89\ucd8a\ucd8b\ucd8c\ucd8d\ucd8e\ucd8f\ucd90\ucd91\ucd92\ucd93\ucd94\ucd95\ucd96\ucd97\ucd98\ucd99\ucd9a\ucd9b\ucd9c\ucd9d\ucd9e\ucd9f\ucda0\ucda1\ucda2\ucda3\ucda4\ucda5\ucda6\ucda7\ucda8\ucda9\ucdaa\ucdab\ucdac\ucdad\ucdae\ucdaf\ucdb0\ucdb1\ucdb2\ucdb3\ucdb4\ucdb5\ucdb6\ucdb7\ucdb8\ucdb9\ucdba\ucdbb\ucdbc\ucdbd\ucdbe\ucdbf\ucdc0\ucdc1\ucdc2\ucdc3\ucdc4\ucdc5\ucdc6\ucdc7\ucdc8\ucdc9\ucdca\ucdcb\ucdcc\ucdcd\ucdce\ucdcf\ucdd0\ucdd1\ucdd2\ucdd3\ucdd4\ucdd5\ucdd6\ucdd7\ucdd8\ucdd9\ucdda\ucddb\ucddc\ucddd\ucdde\ucddf\ucde0\ucde1\ucde2\ucde3\ucde4\ucde5\ucde6\ucde7\ucde8\ucde9\ucdea\ucdeb\ucdec\ucded\ucdee\ucdef\ucdf0\ucdf1\ucdf2\ucdf3\ucdf4\ucdf5\ucdf6\ucdf7\ucdf8\ucdf9\ucdfa\ucdfb\ucdfc\ucdfd\ucdfe\ucdff\uce00\uce01\uce02\uce03\uce04\uce05\uce06\uce07\uce08\uce09\uce0a\uce0b\uce0c\uce0d\uce0e\uce0f\uce10\uce11\uce12\uce13\uce14\uce15\uce16\uce17\uce18\uce19\uce1a\uce1b\uce1c\uce1d\uce1e\uce1f\uce20\uce21\uce22\uce23\uce24\uce25\uce26\uce27\uce28\uce29\uce2a\uce2b\uce2c\uce2d\uce2e\uce2f\uce30\uce31\uce32\uce33\uce34\uce35\uce36\uce37\uce38\uce39\uce3a\uce3b\uce3c\uce3d\uce3e\uce3f\uce40\uce41\uce42\uce43\uce44\uce45\uce46\uce47\uce48\uce49\uce4a\uce4b\uce4c\uce4d\uce4e\uce4f\uce50\uce51\uce52\uce53\uce54\uce55\uce56\uce57\uce58\uce59\uce5a\uce5b\uce5c\uce5d\uce5e\uce5f\uce60\uce61\uce62\uce63\uce64\uce65\uce66\uce67\uce68\uce69\uce6a\uce6b\uce6c\uce6d\uce6e\uce6f\uce70\uce71\uce72\uce73\uce74\uce75\uce76\uce77\uce78\uce79\uce7a\uce7b\uce7c\uce7d\uce7e\uce7f\uce80\uce81\uce82\uce83\uce84\uce85\uce86\uce87\uce88\uce89\uce8a\uce8b\uce8c\uce8d\uce8e\uce8f\uce90\uce91\uce92\uce93\uce94\uce95\uce96\uce97\uce98\uce99\uce9a\uce9b\uce9c\uce9d\uce9e\uce9f\ucea0\ucea1\ucea2\ucea3\ucea4\ucea5\ucea6\ucea7\ucea8\ucea9\uceaa\uceab\uceac\ucead\uceae\uceaf\uceb0\uceb1\uceb2\uceb3\uceb4\uceb5\uceb6\uceb7\uceb8\uceb9\uceba\ucebb\ucebc\ucebd\ucebe\ucebf\ucec0\ucec1\ucec2\ucec3\ucec4\ucec5\ucec6\ucec7\ucec8\ucec9\uceca\ucecb\ucecc\ucecd\ucece\ucecf\uced0\uced1\uced2\uced3\uced4\uced5\uced6\uced7\uced8\uced9\uceda\ucedb\ucedc\ucedd\ucede\ucedf\ucee0\ucee1\ucee2\ucee3\ucee4\ucee5\ucee6\ucee7\ucee8\ucee9\uceea\uceeb\uceec\uceed\uceee\uceef\ucef0\ucef1\ucef2\ucef3\ucef4\ucef5\ucef6\ucef7\ucef8\ucef9\ucefa\ucefb\ucefc\ucefd\ucefe\uceff\ucf00\ucf01\ucf02\ucf03\ucf04\ucf05\ucf06\ucf07\ucf08\ucf09\ucf0a\ucf0b\ucf0c\ucf0d\ucf0e\ucf0f\ucf10\ucf11\ucf12\ucf13\ucf14\ucf15\ucf16\ucf17\ucf18\ucf19\ucf1a\ucf1b\ucf1c\ucf1d\ucf1e\ucf1f\ucf20\ucf21\ucf22\ucf23\ucf24\ucf25\ucf26\ucf27\ucf28\ucf29\ucf2a\ucf2b\ucf2c\ucf2d\ucf2e\ucf2f\ucf30\ucf31\ucf32\ucf33\ucf34\ucf35\ucf36\ucf37\ucf38\ucf39\ucf3a\ucf3b\ucf3c\ucf3d\ucf3e\ucf3f\ucf40\ucf41\ucf42\ucf43\ucf44\ucf45\ucf46\ucf47\ucf48\ucf49\ucf4a\ucf4b\ucf4c\ucf4d\ucf4e\ucf4f\ucf50\ucf51\ucf52\ucf53\ucf54\ucf55\ucf56\ucf57\ucf58\ucf59\ucf5a\ucf5b\ucf5c\ucf5d\ucf5e\ucf5f\ucf60\ucf61\ucf62\ucf63\ucf64\ucf65\ucf66\ucf67\ucf68\ucf69\ucf6a\ucf6b\ucf6c\ucf6d\ucf6e\ucf6f\ucf70\ucf71\ucf72\ucf73\ucf74\ucf75\ucf76\ucf77\ucf78\ucf79\ucf7a\ucf7b\ucf7c\ucf7d\ucf7e\ucf7f\ucf80\ucf81\ucf82\ucf83\ucf84\ucf85\ucf86\ucf87\ucf88\ucf89\ucf8a\ucf8b\ucf8c\ucf8d\ucf8e\ucf8f\ucf90\ucf91\ucf92\ucf93\ucf94\ucf95\ucf96\ucf97\ucf98\ucf99\ucf9a\ucf9b\ucf9c\ucf9d\ucf9e\ucf9f\ucfa0\ucfa1\ucfa2\ucfa3\ucfa4\ucfa5\ucfa6\ucfa7\ucfa8\ucfa9\ucfaa\ucfab\ucfac\ucfad\ucfae\ucfaf\ucfb0\ucfb1\ucfb2\ucfb3\ucfb4\ucfb5\ucfb6\ucfb7\ucfb8\ucfb9\ucfba\ucfbb\ucfbc\ucfbd\ucfbe\ucfbf\ucfc0\ucfc1\ucfc2\ucfc3\ucfc4\ucfc5\ucfc6\ucfc7\ucfc8\ucfc9\ucfca\ucfcb\ucfcc\ucfcd\ucfce\ucfcf\ucfd0\ucfd1\ucfd2\ucfd3\ucfd4\ucfd5\ucfd6\ucfd7\ucfd8\ucfd9\ucfda\ucfdb\ucfdc\ucfdd\ucfde\ucfdf\ucfe0\ucfe1\ucfe2\ucfe3\ucfe4\ucfe5\ucfe6\ucfe7\ucfe8\ucfe9\ucfea\ucfeb\ucfec\ucfed\ucfee\ucfef\ucff0\ucff1\ucff2\ucff3\ucff4\ucff5\ucff6\ucff7\ucff8\ucff9\ucffa\ucffb\ucffc\ucffd\ucffe\ucfff\ud000\ud001\ud002\ud003\ud004\ud005\ud006\ud007\ud008\ud009\ud00a\ud00b\ud00c\ud00d\ud00e\ud00f\ud010\ud011\ud012\ud013\ud014\ud015\ud016\ud017\ud018\ud019\ud01a\ud01b\ud01c\ud01d\ud01e\ud01f\ud020\ud021\ud022\ud023\ud024\ud025\ud026\ud027\ud028\ud029\ud02a\ud02b\ud02c\ud02d\ud02e\ud02f\ud030\ud031\ud032\ud033\ud034\ud035\ud036\ud037\ud038\ud039\ud03a\ud03b\ud03c\ud03d\ud03e\ud03f\ud040\ud041\ud042\ud043\ud044\ud045\ud046\ud047\ud048\ud049\ud04a\ud04b\ud04c\ud04d\ud04e\ud04f\ud050\ud051\ud052\ud053\ud054\ud055\ud056\ud057\ud058\ud059\ud05a\ud05b\ud05c\ud05d\ud05e\ud05f\ud060\ud061\ud062\ud063\ud064\ud065\ud066\ud067\ud068\ud069\ud06a\ud06b\ud06c\ud06d\ud06e\ud06f\ud070\ud071\ud072\ud073\ud074\ud075\ud076\ud077\ud078\ud079\ud07a\ud07b\ud07c\ud07d\ud07e\ud07f\ud080\ud081\ud082\ud083\ud084\ud085\ud086\ud087\ud088\ud089\ud08a\ud08b\ud08c\ud08d\ud08e\ud08f\ud090\ud091\ud092\ud093\ud094\ud095\ud096\ud097\ud098\ud099\ud09a\ud09b\ud09c\ud09d\ud09e\ud09f\ud0a0\ud0a1\ud0a2\ud0a3\ud0a4\ud0a5\ud0a6\ud0a7\ud0a8\ud0a9\ud0aa\ud0ab\ud0ac\ud0ad\ud0ae\ud0af\ud0b0\ud0b1\ud0b2\ud0b3\ud0b4\ud0b5\ud0b6\ud0b7\ud0b8\ud0b9\ud0ba\ud0bb\ud0bc\ud0bd\ud0be\ud0bf\ud0c0\ud0c1\ud0c2\ud0c3\ud0c4\ud0c5\ud0c6\ud0c7\ud0c8\ud0c9\ud0ca\ud0cb\ud0cc\ud0cd\ud0ce\ud0cf\ud0d0\ud0d1\ud0d2\ud0d3\ud0d4\ud0d5\ud0d6\ud0d7\ud0d8\ud0d9\ud0da\ud0db\ud0dc\ud0dd\ud0de\ud0df\ud0e0\ud0e1\ud0e2\ud0e3\ud0e4\ud0e5\ud0e6\ud0e7\ud0e8\ud0e9\ud0ea\ud0eb\ud0ec\ud0ed\ud0ee\ud0ef\ud0f0\ud0f1\ud0f2\ud0f3\ud0f4\ud0f5\ud0f6\ud0f7\ud0f8\ud0f9\ud0fa\ud0fb\ud0fc\ud0fd\ud0fe\ud0ff\ud100\ud101\ud102\ud103\ud104\ud105\ud106\ud107\ud108\ud109\ud10a\ud10b\ud10c\ud10d\ud10e\ud10f\ud110\ud111\ud112\ud113\ud114\ud115\ud116\ud117\ud118\ud119\ud11a\ud11b\ud11c\ud11d\ud11e\ud11f\ud120\ud121\ud122\ud123\ud124\ud125\ud126\ud127\ud128\ud129\ud12a\ud12b\ud12c\ud12d\ud12e\ud12f\ud130\ud131\ud132\ud133\ud134\ud135\ud136\ud137\ud138\ud139\ud13a\ud13b\ud13c\ud13d\ud13e\ud13f\ud140\ud141\ud142\ud143\ud144\ud145\ud146\ud147\ud148\ud149\ud14a\ud14b\ud14c\ud14d\ud14e\ud14f\ud150\ud151\ud152\ud153\ud154\ud155\ud156\ud157\ud158\ud159\ud15a\ud15b\ud15c\ud15d\ud15e\ud15f\ud160\ud161\ud162\ud163\ud164\ud165\ud166\ud167\ud168\ud169\ud16a\ud16b\ud16c\ud16d\ud16e\ud16f\ud170\ud171\ud172\ud173\ud174\ud175\ud176\ud177\ud178\ud179\ud17a\ud17b\ud17c\ud17d\ud17e\ud17f\ud180\ud181\ud182\ud183\ud184\ud185\ud186\ud187\ud188\ud189\ud18a\ud18b\ud18c\ud18d\ud18e\ud18f\ud190\ud191\ud192\ud193\ud194\ud195\ud196\ud197\ud198\ud199\ud19a\ud19b\ud19c\ud19d\ud19e\ud19f\ud1a0\ud1a1\ud1a2\ud1a3\ud1a4\ud1a5\ud1a6\ud1a7\ud1a8\ud1a9\ud1aa\ud1ab\ud1ac\ud1ad\ud1ae\ud1af\ud1b0\ud1b1\ud1b2\ud1b3\ud1b4\ud1b5\ud1b6\ud1b7\ud1b8\ud1b9\ud1ba\ud1bb\ud1bc\ud1bd\ud1be\ud1bf\ud1c0\ud1c1\ud1c2\ud1c3\ud1c4\ud1c5\ud1c6\ud1c7\ud1c8\ud1c9\ud1ca\ud1cb\ud1cc\ud1cd\ud1ce\ud1cf\ud1d0\ud1d1\ud1d2\ud1d3\ud1d4\ud1d5\ud1d6\ud1d7\ud1d8\ud1d9\ud1da\ud1db\ud1dc\ud1dd\ud1de\ud1df\ud1e0\ud1e1\ud1e2\ud1e3\ud1e4\ud1e5\ud1e6\ud1e7\ud1e8\ud1e9\ud1ea\ud1eb\ud1ec\ud1ed\ud1ee\ud1ef\ud1f0\ud1f1\ud1f2\ud1f3\ud1f4\ud1f5\ud1f6\ud1f7\ud1f8\ud1f9\ud1fa\ud1fb\ud1fc\ud1fd\ud1fe\ud1ff\ud200\ud201\ud202\ud203\ud204\ud205\ud206\ud207\ud208\ud209\ud20a\ud20b\ud20c\ud20d\ud20e\ud20f\ud210\ud211\ud212\ud213\ud214\ud215\ud216\ud217\ud218\ud219\ud21a\ud21b\ud21c\ud21d\ud21e\ud21f\ud220\ud221\ud222\ud223\ud224\ud225\ud226\ud227\ud228\ud229\ud22a\ud22b\ud22c\ud22d\ud22e\ud22f\ud230\ud231\ud232\ud233\ud234\ud235\ud236\ud237\ud238\ud239\ud23a\ud23b\ud23c\ud23d\ud23e\ud23f\ud240\ud241\ud242\ud243\ud244\ud245\ud246\ud247\ud248\ud249\ud24a\ud24b\ud24c\ud24d\ud24e\ud24f\ud250\ud251\ud252\ud253\ud254\ud255\ud256\ud257\ud258\ud259\ud25a\ud25b\ud25c\ud25d\ud25e\ud25f\ud260\ud261\ud262\ud263\ud264\ud265\ud266\ud267\ud268\ud269\ud26a\ud26b\ud26c\ud26d\ud26e\ud26f\ud270\ud271\ud272\ud273\ud274\ud275\ud276\ud277\ud278\ud279\ud27a\ud27b\ud27c\ud27d\ud27e\ud27f\ud280\ud281\ud282\ud283\ud284\ud285\ud286\ud287\ud288\ud289\ud28a\ud28b\ud28c\ud28d\ud28e\ud28f\ud290\ud291\ud292\ud293\ud294\ud295\ud296\ud297\ud298\ud299\ud29a\ud29b\ud29c\ud29d\ud29e\ud29f\ud2a0\ud2a1\ud2a2\ud2a3\ud2a4\ud2a5\ud2a6\ud2a7\ud2a8\ud2a9\ud2aa\ud2ab\ud2ac\ud2ad\ud2ae\ud2af\ud2b0\ud2b1\ud2b2\ud2b3\ud2b4\ud2b5\ud2b6\ud2b7\ud2b8\ud2b9\ud2ba\ud2bb\ud2bc\ud2bd\ud2be\ud2bf\ud2c0\ud2c1\ud2c2\ud2c3\ud2c4\ud2c5\ud2c6\ud2c7\ud2c8\ud2c9\ud2ca\ud2cb\ud2cc\ud2cd\ud2ce\ud2cf\ud2d0\ud2d1\ud2d2\ud2d3\ud2d4\ud2d5\ud2d6\ud2d7\ud2d8\ud2d9\ud2da\ud2db\ud2dc\ud2dd\ud2de\ud2df\ud2e0\ud2e1\ud2e2\ud2e3\ud2e4\ud2e5\ud2e6\ud2e7\ud2e8\ud2e9\ud2ea\ud2eb\ud2ec\ud2ed\ud2ee\ud2ef\ud2f0\ud2f1\ud2f2\ud2f3\ud2f4\ud2f5\ud2f6\ud2f7\ud2f8\ud2f9\ud2fa\ud2fb\ud2fc\ud2fd\ud2fe\ud2ff\ud300\ud301\ud302\ud303\ud304\ud305\ud306\ud307\ud308\ud309\ud30a\ud30b\ud30c\ud30d\ud30e\ud30f\ud310\ud311\ud312\ud313\ud314\ud315\ud316\ud317\ud318\ud319\ud31a\ud31b\ud31c\ud31d\ud31e\ud31f\ud320\ud321\ud322\ud323\ud324\ud325\ud326\ud327\ud328\ud329\ud32a\ud32b\ud32c\ud32d\ud32e\ud32f\ud330\ud331\ud332\ud333\ud334\ud335\ud336\ud337\ud338\ud339\ud33a\ud33b\ud33c\ud33d\ud33e\ud33f\ud340\ud341\ud342\ud343\ud344\ud345\ud346\ud347\ud348\ud349\ud34a\ud34b\ud34c\ud34d\ud34e\ud34f\ud350\ud351\ud352\ud353\ud354\ud355\ud356\ud357\ud358\ud359\ud35a\ud35b\ud35c\ud35d\ud35e\ud35f\ud360\ud361\ud362\ud363\ud364\ud365\ud366\ud367\ud368\ud369\ud36a\ud36b\ud36c\ud36d\ud36e\ud36f\ud370\ud371\ud372\ud373\ud374\ud375\ud376\ud377\ud378\ud379\ud37a\ud37b\ud37c\ud37d\ud37e\ud37f\ud380\ud381\ud382\ud383\ud384\ud385\ud386\ud387\ud388\ud389\ud38a\ud38b\ud38c\ud38d\ud38e\ud38f\ud390\ud391\ud392\ud393\ud394\ud395\ud396\ud397\ud398\ud399\ud39a\ud39b\ud39c\ud39d\ud39e\ud39f\ud3a0\ud3a1\ud3a2\ud3a3\ud3a4\ud3a5\ud3a6\ud3a7\ud3a8\ud3a9\ud3aa\ud3ab\ud3ac\ud3ad\ud3ae\ud3af\ud3b0\ud3b1\ud3b2\ud3b3\ud3b4\ud3b5\ud3b6\ud3b7\ud3b8\ud3b9\ud3ba\ud3bb\ud3bc\ud3bd\ud3be\ud3bf\ud3c0\ud3c1\ud3c2\ud3c3\ud3c4\ud3c5\ud3c6\ud3c7\ud3c8\ud3c9\ud3ca\ud3cb\ud3cc\ud3cd\ud3ce\ud3cf\ud3d0\ud3d1\ud3d2\ud3d3\ud3d4\ud3d5\ud3d6\ud3d7\ud3d8\ud3d9\ud3da\ud3db\ud3dc\ud3dd\ud3de\ud3df\ud3e0\ud3e1\ud3e2\ud3e3\ud3e4\ud3e5\ud3e6\ud3e7\ud3e8\ud3e9\ud3ea\ud3eb\ud3ec\ud3ed\ud3ee\ud3ef\ud3f0\ud3f1\ud3f2\ud3f3\ud3f4\ud3f5\ud3f6\ud3f7\ud3f8\ud3f9\ud3fa\ud3fb\ud3fc\ud3fd\ud3fe\ud3ff\ud400\ud401\ud402\ud403\ud404\ud405\ud406\ud407\ud408\ud409\ud40a\ud40b\ud40c\ud40d\ud40e\ud40f\ud410\ud411\ud412\ud413\ud414\ud415\ud416\ud417\ud418\ud419\ud41a\ud41b\ud41c\ud41d\ud41e\ud41f\ud420\ud421\ud422\ud423\ud424\ud425\ud426\ud427\ud428\ud429\ud42a\ud42b\ud42c\ud42d\ud42e\ud42f\ud430\ud431\ud432\ud433\ud434\ud435\ud436\ud437\ud438\ud439\ud43a\ud43b\ud43c\ud43d\ud43e\ud43f\ud440\ud441\ud442\ud443\ud444\ud445\ud446\ud447\ud448\ud449\ud44a\ud44b\ud44c\ud44d\ud44e\ud44f\ud450\ud451\ud452\ud453\ud454\ud455\ud456\ud457\ud458\ud459\ud45a\ud45b\ud45c\ud45d\ud45e\ud45f\ud460\ud461\ud462\ud463\ud464\ud465\ud466\ud467\ud468\ud469\ud46a\ud46b\ud46c\ud46d\ud46e\ud46f\ud470\ud471\ud472\ud473\ud474\ud475\ud476\ud477\ud478\ud479\ud47a\ud47b\ud47c\ud47d\ud47e\ud47f\ud480\ud481\ud482\ud483\ud484\ud485\ud486\ud487\ud488\ud489\ud48a\ud48b\ud48c\ud48d\ud48e\ud48f\ud490\ud491\ud492\ud493\ud494\ud495\ud496\ud497\ud498\ud499\ud49a\ud49b\ud49c\ud49d\ud49e\ud49f\ud4a0\ud4a1\ud4a2\ud4a3\ud4a4\ud4a5\ud4a6\ud4a7\ud4a8\ud4a9\ud4aa\ud4ab\ud4ac\ud4ad\ud4ae\ud4af\ud4b0\ud4b1\ud4b2\ud4b3\ud4b4\ud4b5\ud4b6\ud4b7\ud4b8\ud4b9\ud4ba\ud4bb\ud4bc\ud4bd\ud4be\ud4bf\ud4c0\ud4c1\ud4c2\ud4c3\ud4c4\ud4c5\ud4c6\ud4c7\ud4c8\ud4c9\ud4ca\ud4cb\ud4cc\ud4cd\ud4ce\ud4cf\ud4d0\ud4d1\ud4d2\ud4d3\ud4d4\ud4d5\ud4d6\ud4d7\ud4d8\ud4d9\ud4da\ud4db\ud4dc\ud4dd\ud4de\ud4df\ud4e0\ud4e1\ud4e2\ud4e3\ud4e4\ud4e5\ud4e6\ud4e7\ud4e8\ud4e9\ud4ea\ud4eb\ud4ec\ud4ed\ud4ee\ud4ef\ud4f0\ud4f1\ud4f2\ud4f3\ud4f4\ud4f5\ud4f6\ud4f7\ud4f8\ud4f9\ud4fa\ud4fb\ud4fc\ud4fd\ud4fe\ud4ff\ud500\ud501\ud502\ud503\ud504\ud505\ud506\ud507\ud508\ud509\ud50a\ud50b\ud50c\ud50d\ud50e\ud50f\ud510\ud511\ud512\ud513\ud514\ud515\ud516\ud517\ud518\ud519\ud51a\ud51b\ud51c\ud51d\ud51e\ud51f\ud520\ud521\ud522\ud523\ud524\ud525\ud526\ud527\ud528\ud529\ud52a\ud52b\ud52c\ud52d\ud52e\ud52f\ud530\ud531\ud532\ud533\ud534\ud535\ud536\ud537\ud538\ud539\ud53a\ud53b\ud53c\ud53d\ud53e\ud53f\ud540\ud541\ud542\ud543\ud544\ud545\ud546\ud547\ud548\ud549\ud54a\ud54b\ud54c\ud54d\ud54e\ud54f\ud550\ud551\ud552\ud553\ud554\ud555\ud556\ud557\ud558\ud559\ud55a\ud55b\ud55c\ud55d\ud55e\ud55f\ud560\ud561\ud562\ud563\ud564\ud565\ud566\ud567\ud568\ud569\ud56a\ud56b\ud56c\ud56d\ud56e\ud56f\ud570\ud571\ud572\ud573\ud574\ud575\ud576\ud577\ud578\ud579\ud57a\ud57b\ud57c\ud57d\ud57e\ud57f\ud580\ud581\ud582\ud583\ud584\ud585\ud586\ud587\ud588\ud589\ud58a\ud58b\ud58c\ud58d\ud58e\ud58f\ud590\ud591\ud592\ud593\ud594\ud595\ud596\ud597\ud598\ud599\ud59a\ud59b\ud59c\ud59d\ud59e\ud59f\ud5a0\ud5a1\ud5a2\ud5a3\ud5a4\ud5a5\ud5a6\ud5a7\ud5a8\ud5a9\ud5aa\ud5ab\ud5ac\ud5ad\ud5ae\ud5af\ud5b0\ud5b1\ud5b2\ud5b3\ud5b4\ud5b5\ud5b6\ud5b7\ud5b8\ud5b9\ud5ba\ud5bb\ud5bc\ud5bd\ud5be\ud5bf\ud5c0\ud5c1\ud5c2\ud5c3\ud5c4\ud5c5\ud5c6\ud5c7\ud5c8\ud5c9\ud5ca\ud5cb\ud5cc\ud5cd\ud5ce\ud5cf\ud5d0\ud5d1\ud5d2\ud5d3\ud5d4\ud5d5\ud5d6\ud5d7\ud5d8\ud5d9\ud5da\ud5db\ud5dc\ud5dd\ud5de\ud5df\ud5e0\ud5e1\ud5e2\ud5e3\ud5e4\ud5e5\ud5e6\ud5e7\ud5e8\ud5e9\ud5ea\ud5eb\ud5ec\ud5ed\ud5ee\ud5ef\ud5f0\ud5f1\ud5f2\ud5f3\ud5f4\ud5f5\ud5f6\ud5f7\ud5f8\ud5f9\ud5fa\ud5fb\ud5fc\ud5fd\ud5fe\ud5ff\ud600\ud601\ud602\ud603\ud604\ud605\ud606\ud607\ud608\ud609\ud60a\ud60b\ud60c\ud60d\ud60e\ud60f\ud610\ud611\ud612\ud613\ud614\ud615\ud616\ud617\ud618\ud619\ud61a\ud61b\ud61c\ud61d\ud61e\ud61f\ud620\ud621\ud622\ud623\ud624\ud625\ud626\ud627\ud628\ud629\ud62a\ud62b\ud62c\ud62d\ud62e\ud62f\ud630\ud631\ud632\ud633\ud634\ud635\ud636\ud637\ud638\ud639\ud63a\ud63b\ud63c\ud63d\ud63e\ud63f\ud640\ud641\ud642\ud643\ud644\ud645\ud646\ud647\ud648\ud649\ud64a\ud64b\ud64c\ud64d\ud64e\ud64f\ud650\ud651\ud652\ud653\ud654\ud655\ud656\ud657\ud658\ud659\ud65a\ud65b\ud65c\ud65d\ud65e\ud65f\ud660\ud661\ud662\ud663\ud664\ud665\ud666\ud667\ud668\ud669\ud66a\ud66b\ud66c\ud66d\ud66e\ud66f\ud670\ud671\ud672\ud673\ud674\ud675\ud676\ud677\ud678\ud679\ud67a\ud67b\ud67c\ud67d\ud67e\ud67f\ud680\ud681\ud682\ud683\ud684\ud685\ud686\ud687\ud688\ud689\ud68a\ud68b\ud68c\ud68d\ud68e\ud68f\ud690\ud691\ud692\ud693\ud694\ud695\ud696\ud697\ud698\ud699\ud69a\ud69b\ud69c\ud69d\ud69e\ud69f\ud6a0\ud6a1\ud6a2\ud6a3\ud6a4\ud6a5\ud6a6\ud6a7\ud6a8\ud6a9\ud6aa\ud6ab\ud6ac\ud6ad\ud6ae\ud6af\ud6b0\ud6b1\ud6b2\ud6b3\ud6b4\ud6b5\ud6b6\ud6b7\ud6b8\ud6b9\ud6ba\ud6bb\ud6bc\ud6bd\ud6be\ud6bf\ud6c0\ud6c1\ud6c2\ud6c3\ud6c4\ud6c5\ud6c6\ud6c7\ud6c8\ud6c9\ud6ca\ud6cb\ud6cc\ud6cd\ud6ce\ud6cf\ud6d0\ud6d1\ud6d2\ud6d3\ud6d4\ud6d5\ud6d6\ud6d7\ud6d8\ud6d9\ud6da\ud6db\ud6dc\ud6dd\ud6de\ud6df\ud6e0\ud6e1\ud6e2\ud6e3\ud6e4\ud6e5\ud6e6\ud6e7\ud6e8\ud6e9\ud6ea\ud6eb\ud6ec\ud6ed\ud6ee\ud6ef\ud6f0\ud6f1\ud6f2\ud6f3\ud6f4\ud6f5\ud6f6\ud6f7\ud6f8\ud6f9\ud6fa\ud6fb\ud6fc\ud6fd\ud6fe\ud6ff\ud700\ud701\ud702\ud703\ud704\ud705\ud706\ud707\ud708\ud709\ud70a\ud70b\ud70c\ud70d\ud70e\ud70f\ud710\ud711\ud712\ud713\ud714\ud715\ud716\ud717\ud718\ud719\ud71a\ud71b\ud71c\ud71d\ud71e\ud71f\ud720\ud721\ud722\ud723\ud724\ud725\ud726\ud727\ud728\ud729\ud72a\ud72b\ud72c\ud72d\ud72e\ud72f\ud730\ud731\ud732\ud733\ud734\ud735\ud736\ud737\ud738\ud739\ud73a\ud73b\ud73c\ud73d\ud73e\ud73f\ud740\ud741\ud742\ud743\ud744\ud745\ud746\ud747\ud748\ud749\ud74a\ud74b\ud74c\ud74d\ud74e\ud74f\ud750\ud751\ud752\ud753\ud754\ud755\ud756\ud757\ud758\ud759\ud75a\ud75b\ud75c\ud75d\ud75e\ud75f\ud760\ud761\ud762\ud763\ud764\ud765\ud766\ud767\ud768\ud769\ud76a\ud76b\ud76c\ud76d\ud76e\ud76f\ud770\ud771\ud772\ud773\ud774\ud775\ud776\ud777\ud778\ud779\ud77a\ud77b\ud77c\ud77d\ud77e\ud77f\ud780\ud781\ud782\ud783\ud784\ud785\ud786\ud787\ud788\ud789\ud78a\ud78b\ud78c\ud78d\ud78e\ud78f\ud790\ud791\ud792\ud793\ud794\ud795\ud796\ud797\ud798\ud799\ud79a\ud79b\ud79c\ud79d\ud79e\ud79f\ud7a0\ud7a1\ud7a2\ud7a3\uf900\uf901\uf902\uf903\uf904\uf905\uf906\uf907\uf908\uf909\uf90a\uf90b\uf90c\uf90d\uf90e\uf90f\uf910\uf911\uf912\uf913\uf914\uf915\uf916\uf917\uf918\uf919\uf91a\uf91b\uf91c\uf91d\uf91e\uf91f\uf920\uf921\uf922\uf923\uf924\uf925\uf926\uf927\uf928\uf929\uf92a\uf92b\uf92c\uf92d\uf92e\uf92f\uf930\uf931\uf932\uf933\uf934\uf935\uf936\uf937\uf938\uf939\uf93a\uf93b\uf93c\uf93d\uf93e\uf93f\uf940\uf941\uf942\uf943\uf944\uf945\uf946\uf947\uf948\uf949\uf94a\uf94b\uf94c\uf94d\uf94e\uf94f\uf950\uf951\uf952\uf953\uf954\uf955\uf956\uf957\uf958\uf959\uf95a\uf95b\uf95c\uf95d\uf95e\uf95f\uf960\uf961\uf962\uf963\uf964\uf965\uf966\uf967\uf968\uf969\uf96a\uf96b\uf96c\uf96d\uf96e\uf96f\uf970\uf971\uf972\uf973\uf974\uf975\uf976\uf977\uf978\uf979\uf97a\uf97b\uf97c\uf97d\uf97e\uf97f\uf980\uf981\uf982\uf983\uf984\uf985\uf986\uf987\uf988\uf989\uf98a\uf98b\uf98c\uf98d\uf98e\uf98f\uf990\uf991\uf992\uf993\uf994\uf995\uf996\uf997\uf998\uf999\uf99a\uf99b\uf99c\uf99d\uf99e\uf99f\uf9a0\uf9a1\uf9a2\uf9a3\uf9a4\uf9a5\uf9a6\uf9a7\uf9a8\uf9a9\uf9aa\uf9ab\uf9ac\uf9ad\uf9ae\uf9af\uf9b0\uf9b1\uf9b2\uf9b3\uf9b4\uf9b5\uf9b6\uf9b7\uf9b8\uf9b9\uf9ba\uf9bb\uf9bc\uf9bd\uf9be\uf9bf\uf9c0\uf9c1\uf9c2\uf9c3\uf9c4\uf9c5\uf9c6\uf9c7\uf9c8\uf9c9\uf9ca\uf9cb\uf9cc\uf9cd\uf9ce\uf9cf\uf9d0\uf9d1\uf9d2\uf9d3\uf9d4\uf9d5\uf9d6\uf9d7\uf9d8\uf9d9\uf9da\uf9db\uf9dc\uf9dd\uf9de\uf9df\uf9e0\uf9e1\uf9e2\uf9e3\uf9e4\uf9e5\uf9e6\uf9e7\uf9e8\uf9e9\uf9ea\uf9eb\uf9ec\uf9ed\uf9ee\uf9ef\uf9f0\uf9f1\uf9f2\uf9f3\uf9f4\uf9f5\uf9f6\uf9f7\uf9f8\uf9f9\uf9fa\uf9fb\uf9fc\uf9fd\uf9fe\uf9ff\ufa00\ufa01\ufa02\ufa03\ufa04\ufa05\ufa06\ufa07\ufa08\ufa09\ufa0a\ufa0b\ufa0c\ufa0d\ufa0e\ufa0f\ufa10\ufa11\ufa12\ufa13\ufa14\ufa15\ufa16\ufa17\ufa18\ufa19\ufa1a\ufa1b\ufa1c\ufa1d\ufa1e\ufa1f\ufa20\ufa21\ufa22\ufa23\ufa24\ufa25\ufa26\ufa27\ufa28\ufa29\ufa2a\ufa2b\ufa2c\ufa2d\ufa30\ufa31\ufa32\ufa33\ufa34\ufa35\ufa36\ufa37\ufa38\ufa39\ufa3a\ufa3b\ufa3c\ufa3d\ufa3e\ufa3f\ufa40\ufa41\ufa42\ufa43\ufa44\ufa45\ufa46\ufa47\ufa48\ufa49\ufa4a\ufa4b\ufa4c\ufa4d\ufa4e\ufa4f\ufa50\ufa51\ufa52\ufa53\ufa54\ufa55\ufa56\ufa57\ufa58\ufa59\ufa5a\ufa5b\ufa5c\ufa5d\ufa5e\ufa5f\ufa60\ufa61\ufa62\ufa63\ufa64\ufa65\ufa66\ufa67\ufa68\ufa69\ufa6a\ufa70\ufa71\ufa72\ufa73\ufa74\ufa75\ufa76\ufa77\ufa78\ufa79\ufa7a\ufa7b\ufa7c\ufa7d\ufa7e\ufa7f\ufa80\ufa81\ufa82\ufa83\ufa84\ufa85\ufa86\ufa87\ufa88\ufa89\ufa8a\ufa8b\ufa8c\ufa8d\ufa8e\ufa8f\ufa90\ufa91\ufa92\ufa93\ufa94\ufa95\ufa96\ufa97\ufa98\ufa99\ufa9a\ufa9b\ufa9c\ufa9d\ufa9e\ufa9f\ufaa0\ufaa1\ufaa2\ufaa3\ufaa4\ufaa5\ufaa6\ufaa7\ufaa8\ufaa9\ufaaa\ufaab\ufaac\ufaad\ufaae\ufaaf\ufab0\ufab1\ufab2\ufab3\ufab4\ufab5\ufab6\ufab7\ufab8\ufab9\ufaba\ufabb\ufabc\ufabd\ufabe\ufabf\ufac0\ufac1\ufac2\ufac3\ufac4\ufac5\ufac6\ufac7\ufac8\ufac9\ufaca\ufacb\ufacc\ufacd\uface\ufacf\ufad0\ufad1\ufad2\ufad3\ufad4\ufad5\ufad6\ufad7\ufad8\ufad9\ufb1d\ufb1f\ufb20\ufb21\ufb22\ufb23\ufb24\ufb25\ufb26\ufb27\ufb28\ufb2a\ufb2b\ufb2c\ufb2d\ufb2e\ufb2f\ufb30\ufb31\ufb32\ufb33\ufb34\ufb35\ufb36\ufb38\ufb39\ufb3a\ufb3b\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46\ufb47\ufb48\ufb49\ufb4a\ufb4b\ufb4c\ufb4d\ufb4e\ufb4f\ufb50\ufb51\ufb52\ufb53\ufb54\ufb55\ufb56\ufb57\ufb58\ufb59\ufb5a\ufb5b\ufb5c\ufb5d\ufb5e\ufb5f\ufb60\ufb61\ufb62\ufb63\ufb64\ufb65\ufb66\ufb67\ufb68\ufb69\ufb6a\ufb6b\ufb6c\ufb6d\ufb6e\ufb6f\ufb70\ufb71\ufb72\ufb73\ufb74\ufb75\ufb76\ufb77\ufb78\ufb79\ufb7a\ufb7b\ufb7c\ufb7d\ufb7e\ufb7f\ufb80\ufb81\ufb82\ufb83\ufb84\ufb85\ufb86\ufb87\ufb88\ufb89\ufb8a\ufb8b\ufb8c\ufb8d\ufb8e\ufb8f\ufb90\ufb91\ufb92\ufb93\ufb94\ufb95\ufb96\ufb97\ufb98\ufb99\ufb9a\ufb9b\ufb9c\ufb9d\ufb9e\ufb9f\ufba0\ufba1\ufba2\ufba3\ufba4\ufba5\ufba6\ufba7\ufba8\ufba9\ufbaa\ufbab\ufbac\ufbad\ufbae\ufbaf\ufbb0\ufbb1\ufbd3\ufbd4\ufbd5\ufbd6\ufbd7\ufbd8\ufbd9\ufbda\ufbdb\ufbdc\ufbdd\ufbde\ufbdf\ufbe0\ufbe1\ufbe2\ufbe3\ufbe4\ufbe5\ufbe6\ufbe7\ufbe8\ufbe9\ufbea\ufbeb\ufbec\ufbed\ufbee\ufbef\ufbf0\ufbf1\ufbf2\ufbf3\ufbf4\ufbf5\ufbf6\ufbf7\ufbf8\ufbf9\ufbfa\ufbfb\ufbfc\ufbfd\ufbfe\ufbff\ufc00\ufc01\ufc02\ufc03\ufc04\ufc05\ufc06\ufc07\ufc08\ufc09\ufc0a\ufc0b\ufc0c\ufc0d\ufc0e\ufc0f\ufc10\ufc11\ufc12\ufc13\ufc14\ufc15\ufc16\ufc17\ufc18\ufc19\ufc1a\ufc1b\ufc1c\ufc1d\ufc1e\ufc1f\ufc20\ufc21\ufc22\ufc23\ufc24\ufc25\ufc26\ufc27\ufc28\ufc29\ufc2a\ufc2b\ufc2c\ufc2d\ufc2e\ufc2f\ufc30\ufc31\ufc32\ufc33\ufc34\ufc35\ufc36\ufc37\ufc38\ufc39\ufc3a\ufc3b\ufc3c\ufc3d\ufc3e\ufc3f\ufc40\ufc41\ufc42\ufc43\ufc44\ufc45\ufc46\ufc47\ufc48\ufc49\ufc4a\ufc4b\ufc4c\ufc4d\ufc4e\ufc4f\ufc50\ufc51\ufc52\ufc53\ufc54\ufc55\ufc56\ufc57\ufc58\ufc59\ufc5a\ufc5b\ufc5c\ufc5d\ufc5e\ufc5f\ufc60\ufc61\ufc62\ufc63\ufc64\ufc65\ufc66\ufc67\ufc68\ufc69\ufc6a\ufc6b\ufc6c\ufc6d\ufc6e\ufc6f\ufc70\ufc71\ufc72\ufc73\ufc74\ufc75\ufc76\ufc77\ufc78\ufc79\ufc7a\ufc7b\ufc7c\ufc7d\ufc7e\ufc7f\ufc80\ufc81\ufc82\ufc83\ufc84\ufc85\ufc86\ufc87\ufc88\ufc89\ufc8a\ufc8b\ufc8c\ufc8d\ufc8e\ufc8f\ufc90\ufc91\ufc92\ufc93\ufc94\ufc95\ufc96\ufc97\ufc98\ufc99\ufc9a\ufc9b\ufc9c\ufc9d\ufc9e\ufc9f\ufca0\ufca1\ufca2\ufca3\ufca4\ufca5\ufca6\ufca7\ufca8\ufca9\ufcaa\ufcab\ufcac\ufcad\ufcae\ufcaf\ufcb0\ufcb1\ufcb2\ufcb3\ufcb4\ufcb5\ufcb6\ufcb7\ufcb8\ufcb9\ufcba\ufcbb\ufcbc\ufcbd\ufcbe\ufcbf\ufcc0\ufcc1\ufcc2\ufcc3\ufcc4\ufcc5\ufcc6\ufcc7\ufcc8\ufcc9\ufcca\ufccb\ufccc\ufccd\ufcce\ufccf\ufcd0\ufcd1\ufcd2\ufcd3\ufcd4\ufcd5\ufcd6\ufcd7\ufcd8\ufcd9\ufcda\ufcdb\ufcdc\ufcdd\ufcde\ufcdf\ufce0\ufce1\ufce2\ufce3\ufce4\ufce5\ufce6\ufce7\ufce8\ufce9\ufcea\ufceb\ufcec\ufced\ufcee\ufcef\ufcf0\ufcf1\ufcf2\ufcf3\ufcf4\ufcf5\ufcf6\ufcf7\ufcf8\ufcf9\ufcfa\ufcfb\ufcfc\ufcfd\ufcfe\ufcff\ufd00\ufd01\ufd02\ufd03\ufd04\ufd05\ufd06\ufd07\ufd08\ufd09\ufd0a\ufd0b\ufd0c\ufd0d\ufd0e\ufd0f\ufd10\ufd11\ufd12\ufd13\ufd14\ufd15\ufd16\ufd17\ufd18\ufd19\ufd1a\ufd1b\ufd1c\ufd1d\ufd1e\ufd1f\ufd20\ufd21\ufd22\ufd23\ufd24\ufd25\ufd26\ufd27\ufd28\ufd29\ufd2a\ufd2b\ufd2c\ufd2d\ufd2e\ufd2f\ufd30\ufd31\ufd32\ufd33\ufd34\ufd35\ufd36\ufd37\ufd38\ufd39\ufd3a\ufd3b\ufd3c\ufd3d\ufd50\ufd51\ufd52\ufd53\ufd54\ufd55\ufd56\ufd57\ufd58\ufd59\ufd5a\ufd5b\ufd5c\ufd5d\ufd5e\ufd5f\ufd60\ufd61\ufd62\ufd63\ufd64\ufd65\ufd66\ufd67\ufd68\ufd69\ufd6a\ufd6b\ufd6c\ufd6d\ufd6e\ufd6f\ufd70\ufd71\ufd72\ufd73\ufd74\ufd75\ufd76\ufd77\ufd78\ufd79\ufd7a\ufd7b\ufd7c\ufd7d\ufd7e\ufd7f\ufd80\ufd81\ufd82\ufd83\ufd84\ufd85\ufd86\ufd87\ufd88\ufd89\ufd8a\ufd8b\ufd8c\ufd8d\ufd8e\ufd8f\ufd92\ufd93\ufd94\ufd95\ufd96\ufd97\ufd98\ufd99\ufd9a\ufd9b\ufd9c\ufd9d\ufd9e\ufd9f\ufda0\ufda1\ufda2\ufda3\ufda4\ufda5\ufda6\ufda7\ufda8\ufda9\ufdaa\ufdab\ufdac\ufdad\ufdae\ufdaf\ufdb0\ufdb1\ufdb2\ufdb3\ufdb4\ufdb5\ufdb6\ufdb7\ufdb8\ufdb9\ufdba\ufdbb\ufdbc\ufdbd\ufdbe\ufdbf\ufdc0\ufdc1\ufdc2\ufdc3\ufdc4\ufdc5\ufdc6\ufdc7\ufdf0\ufdf1\ufdf2\ufdf3\ufdf4\ufdf5\ufdf6\ufdf7\ufdf8\ufdf9\ufdfa\ufdfb\ufe70\ufe71\ufe72\ufe73\ufe74\ufe76\ufe77\ufe78\ufe79\ufe7a\ufe7b\ufe7c\ufe7d\ufe7e\ufe7f\ufe80\ufe81\ufe82\ufe83\ufe84\ufe85\ufe86\ufe87\ufe88\ufe89\ufe8a\ufe8b\ufe8c\ufe8d\ufe8e\ufe8f\ufe90\ufe91\ufe92\ufe93\ufe94\ufe95\ufe96\ufe97\ufe98\ufe99\ufe9a\ufe9b\ufe9c\ufe9d\ufe9e\ufe9f\ufea0\ufea1\ufea2\ufea3\ufea4\ufea5\ufea6\ufea7\ufea8\ufea9\ufeaa\ufeab\ufeac\ufead\ufeae\ufeaf\ufeb0\ufeb1\ufeb2\ufeb3\ufeb4\ufeb5\ufeb6\ufeb7\ufeb8\ufeb9\ufeba\ufebb\ufebc\ufebd\ufebe\ufebf\ufec0\ufec1\ufec2\ufec3\ufec4\ufec5\ufec6\ufec7\ufec8\ufec9\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1\ufed2\ufed3\ufed4\ufed5\ufed6\ufed7\ufed8\ufed9\ufeda\ufedb\ufedc\ufedd\ufede\ufedf\ufee0\ufee1\ufee2\ufee3\ufee4\ufee5\ufee6\ufee7\ufee8\ufee9\ufeea\ufeeb\ufeec\ufeed\ufeee\ufeef\ufef0\ufef1\ufef2\ufef3\ufef4\ufef5\ufef6\ufef7\ufef8\ufef9\ufefa\ufefb\ufefc\uff66\uff67\uff68\uff69\uff6a\uff6b\uff6c\uff6d\uff6e\uff6f\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78\uff79\uff7a\uff7b\uff7c\uff7d\uff7e\uff7f\uff80\uff81\uff82\uff83\uff84\uff85\uff86\uff87\uff88\uff89\uff8a\uff8b\uff8c\uff8d\uff8e\uff8f\uff90\uff91\uff92\uff93\uff94\uff95\uff96\uff97\uff98\uff99\uff9a\uff9b\uff9c\uff9d\uffa0\uffa1\uffa2\uffa3\uffa4\uffa5\uffa6\uffa7\uffa8\uffa9\uffaa\uffab\uffac\uffad\uffae\uffaf\uffb0\uffb1\uffb2\uffb3\uffb4\uffb5\uffb6\uffb7\uffb8\uffb9\uffba\uffbb\uffbc\uffbd\uffbe\uffc2\uffc3\uffc4\uffc5\uffc6\uffc7\uffca\uffcb\uffcc\uffcd\uffce\uffcf\uffd2\uffd3\uffd4\uffd5\uffd6\uffd7\uffda\uffdb\uffdc'
-
-Lt = u'\u01c5\u01c8\u01cb\u01f2\u1f88\u1f89\u1f8a\u1f8b\u1f8c\u1f8d\u1f8e\u1f8f\u1f98\u1f99\u1f9a\u1f9b\u1f9c\u1f9d\u1f9e\u1f9f\u1fa8\u1fa9\u1faa\u1fab\u1fac\u1fad\u1fae\u1faf\u1fbc\u1fcc\u1ffc'
-
-Lu = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xde\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178\u0179\u017b\u017d\u0181\u0182\u0184\u0186\u0187\u0189\u018a\u018b\u018e\u018f\u0190\u0191\u0193\u0194\u0196\u0197\u0198\u019c\u019d\u019f\u01a0\u01a2\u01a4\u01a6\u01a7\u01a9\u01ac\u01ae\u01af\u01b1\u01b2\u01b3\u01b5\u01b7\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6\u01f7\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a\u023b\u023d\u023e\u0241\u0386\u0388\u0389\u038a\u038c\u038e\u038f\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03d2\u03d3\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9\u03fa\u03fd\u03fe\u03ff\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b\u040c\u040d\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0531\u0532\u0533\u0534\u0535\u0536\u0537\u0538\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054a\u054b\u054c\u054d\u054e\u054f\u0550\u0551\u0552\u0553\u0554\u0555\u0556\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1f08\u1f09\u1f0a\u1f0b\u1f0c\u1f0d\u1f0e\u1f0f\u1f18\u1f19\u1f1a\u1f1b\u1f1c\u1f1d\u1f28\u1f29\u1f2a\u1f2b\u1f2c\u1f2d\u1f2e\u1f2f\u1f38\u1f39\u1f3a\u1f3b\u1f3c\u1f3d\u1f3e\u1f3f\u1f48\u1f49\u1f4a\u1f4b\u1f4c\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68\u1f69\u1f6a\u1f6b\u1f6c\u1f6d\u1f6e\u1f6f\u1fb8\u1fb9\u1fba\u1fbb\u1fc8\u1fc9\u1fca\u1fcb\u1fd8\u1fd9\u1fda\u1fdb\u1fe8\u1fe9\u1fea\u1feb\u1fec\u1ff8\u1ff9\u1ffa\u1ffb\u2102\u2107\u210b\u210c\u210d\u2110\u2111\u2112\u2115\u2119\u211a\u211b\u211c\u211d\u2124\u2126\u2128\u212a\u212b\u212c\u212d\u2130\u2131\u2133\u213e\u213f\u2145\u2c00\u2c01\u2c02\u2c03\u2c04\u2c05\u2c06\u2c07\u2c08\u2c09\u2c0a\u2c0b\u2c0c\u2c0d\u2c0e\u2c0f\u2c10\u2c11\u2c12\u2c13\u2c14\u2c15\u2c16\u2c17\u2c18\u2c19\u2c1a\u2c1b\u2c1c\u2c1d\u2c1e\u2c1f\u2c20\u2c21\u2c22\u2c23\u2c24\u2c25\u2c26\u2c27\u2c28\u2c29\u2c2a\u2c2b\u2c2c\u2c2d\u2c2e\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\uff21\uff22\uff23\uff24\uff25\uff26\uff27\uff28\uff29\uff2a\uff2b\uff2c\uff2d\uff2e\uff2f\uff30\uff31\uff32\uff33\uff34\uff35\uff36\uff37\uff38\uff39\uff3a'
-
-Mc = u'\u0903\u093e\u093f\u0940\u0949\u094a\u094b\u094c\u0982\u0983\u09be\u09bf\u09c0\u09c7\u09c8\u09cb\u09cc\u09d7\u0a03\u0a3e\u0a3f\u0a40\u0a83\u0abe\u0abf\u0ac0\u0ac9\u0acb\u0acc\u0b02\u0b03\u0b3e\u0b40\u0b47\u0b48\u0b4b\u0b4c\u0b57\u0bbe\u0bbf\u0bc1\u0bc2\u0bc6\u0bc7\u0bc8\u0bca\u0bcb\u0bcc\u0bd7\u0c01\u0c02\u0c03\u0c41\u0c42\u0c43\u0c44\u0c82\u0c83\u0cbe\u0cc0\u0cc1\u0cc2\u0cc3\u0cc4\u0cc7\u0cc8\u0cca\u0ccb\u0cd5\u0cd6\u0d02\u0d03\u0d3e\u0d3f\u0d40\u0d46\u0d47\u0d48\u0d4a\u0d4b\u0d4c\u0d57\u0d82\u0d83\u0dcf\u0dd0\u0dd1\u0dd8\u0dd9\u0dda\u0ddb\u0ddc\u0ddd\u0dde\u0ddf\u0df2\u0df3\u0f3e\u0f3f\u0f7f\u102c\u1031\u1038\u1056\u1057\u17b6\u17be\u17bf\u17c0\u17c1\u17c2\u17c3\u17c4\u17c5\u17c7\u17c8\u1923\u1924\u1925\u1926\u1929\u192a\u192b\u1930\u1931\u1933\u1934\u1935\u1936\u1937\u1938\u19b0\u19b1\u19b2\u19b3\u19b4\u19b5\u19b6\u19b7\u19b8\u19b9\u19ba\u19bb\u19bc\u19bd\u19be\u19bf\u19c0\u19c8\u19c9\u1a19\u1a1a\u1a1b\ua802\ua823\ua824\ua827'
-
-Me = u'\u0488\u0489\u06de\u20dd\u20de\u20df\u20e0\u20e2\u20e3\u20e4'
-
-Mn = u'\u0300\u0301\u0302\u0303\u0304\u0305\u0306\u0307\u0308\u0309\u030a\u030b\u030c\u030d\u030e\u030f\u0310\u0311\u0312\u0313\u0314\u0315\u0316\u0317\u0318\u0319\u031a\u031b\u031c\u031d\u031e\u031f\u0320\u0321\u0322\u0323\u0324\u0325\u0326\u0327\u0328\u0329\u032a\u032b\u032c\u032d\u032e\u032f\u0330\u0331\u0332\u0333\u0334\u0335\u0336\u0337\u0338\u0339\u033a\u033b\u033c\u033d\u033e\u033f\u0340\u0341\u0342\u0343\u0344\u0345\u0346\u0347\u0348\u0349\u034a\u034b\u034c\u034d\u034e\u034f\u0350\u0351\u0352\u0353\u0354\u0355\u0356\u0357\u0358\u0359\u035a\u035b\u035c\u035d\u035e\u035f\u0360\u0361\u0362\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036a\u036b\u036c\u036d\u036e\u036f\u0483\u0484\u0485\u0486\u0591\u0592\u0593\u0594\u0595\u0596\u0597\u0598\u0599\u059a\u059b\u059c\u059d\u059e\u059f\u05a0\u05a1\u05a2\u05a3\u05a4\u05a5\u05a6\u05a7\u05a8\u05a9\u05aa\u05ab\u05ac\u05ad\u05ae\u05af\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9\u05bb\u05bc\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610\u0611\u0612\u0613\u0614\u0615\u064b\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u0653\u0654\u0655\u0656\u0657\u0658\u0659\u065a\u065b\u065c\u065d\u065e\u0670\u06d6\u06d7\u06d8\u06d9\u06da\u06db\u06dc\u06df\u06e0\u06e1\u06e2\u06e3\u06e4\u06e7\u06e8\u06ea\u06eb\u06ec\u06ed\u0711\u0730\u0731\u0732\u0733\u0734\u0735\u0736\u0737\u0738\u0739\u073a\u073b\u073c\u073d\u073e\u073f\u0740\u0741\u0742\u0743\u0744\u0745\u0746\u0747\u0748\u0749\u074a\u07a6\u07a7\u07a8\u07a9\u07aa\u07ab\u07ac\u07ad\u07ae\u07af\u07b0\u0901\u0902\u093c\u0941\u0942\u0943\u0944\u0945\u0946\u0947\u0948\u094d\u0951\u0952\u0953\u0954\u0962\u0963\u0981\u09bc\u09c1\u09c2\u09c3\u09c4\u09cd\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b\u0a4c\u0a4d\u0a70\u0a71\u0a81\u0a82\u0abc\u0ac1\u0ac2\u0ac3\u0ac4\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3f\u0b41\u0b42\u0b43\u0b4d\u0b56\u0b82\u0bc0\u0bcd\u0c3e\u0c3f\u0c40\u0c46\u0c47\u0c48\u0c4a\u0c4b\u0c4c\u0c4d\u0c55\u0c56\u0cbc\u0cbf\u0cc6\u0ccc\u0ccd\u0d41\u0d42\u0d43\u0d4d\u0dca\u0dd2\u0dd3\u0dd4\u0dd6\u0e31\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0e3a\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0eb1\u0eb4\u0eb5\u0eb6\u0eb7\u0eb8\u0eb9\u0ebb\u0ebc\u0ec8\u0ec9\u0eca\u0ecb\u0ecc\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71\u0f72\u0f73\u0f74\u0f75\u0f76\u0f77\u0f78\u0f79\u0f7a\u0f7b\u0f7c\u0f7d\u0f7e\u0f80\u0f81\u0f82\u0f83\u0f84\u0f86\u0f87\u0f90\u0f91\u0f92\u0f93\u0f94\u0f95\u0f96\u0f97\u0f99\u0f9a\u0f9b\u0f9c\u0f9d\u0f9e\u0f9f\u0fa0\u0fa1\u0fa2\u0fa3\u0fa4\u0fa5\u0fa6\u0fa7\u0fa8\u0fa9\u0faa\u0fab\u0fac\u0fad\u0fae\u0faf\u0fb0\u0fb1\u0fb2\u0fb3\u0fb4\u0fb5\u0fb6\u0fb7\u0fb8\u0fb9\u0fba\u0fbb\u0fbc\u0fc6\u102d\u102e\u102f\u1030\u1032\u1036\u1037\u1039\u1058\u1059\u135f\u1712\u1713\u1714\u1732\u1733\u1734\u1752\u1753\u1772\u1773\u17b7\u17b8\u17b9\u17ba\u17bb\u17bc\u17bd\u17c6\u17c9\u17ca\u17cb\u17cc\u17cd\u17ce\u17cf\u17d0\u17d1\u17d2\u17d3\u17dd\u180b\u180c\u180d\u18a9\u1920\u1921\u1922\u1927\u1928\u1932\u1939\u193a\u193b\u1a17\u1a18\u1dc0\u1dc1\u1dc2\u1dc3\u20d0\u20d1\u20d2\u20d3\u20d4\u20d5\u20d6\u20d7\u20d8\u20d9\u20da\u20db\u20dc\u20e1\u20e5\u20e6\u20e7\u20e8\u20e9\u20ea\u20eb\u302a\u302b\u302c\u302d\u302e\u302f\u3099\u309a\ua806\ua80b\ua825\ua826\ufb1e\ufe00\ufe01\ufe02\ufe03\ufe04\ufe05\ufe06\ufe07\ufe08\ufe09\ufe0a\ufe0b\ufe0c\ufe0d\ufe0e\ufe0f\ufe20\ufe21\ufe22\ufe23'
-
-Nd = u'0123456789\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u0966\u0967\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u09e6\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u0a66\u0a67\u0a68\u0a69\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u0b66\u0b67\u0b68\u0b69\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0be6\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0c66\u0c67\u0c68\u0c69\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0d66\u0d67\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0ed8\u0ed9\u0f20\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\u0f29\u1040\u1041\u1042\u1043\u1044\u1045\u1046\u1047\u1048\u1049\u17e0\u17e1\u17e2\u17e3\u17e4\u17e5\u17e6\u17e7\u17e8\u17e9\u1810\u1811\u1812\u1813\u1814\u1815\u1816\u1817\u1818\u1819\u1946\u1947\u1948\u1949\u194a\u194b\u194c\u194d\u194e\u194f\u19d0\u19d1\u19d2\u19d3\u19d4\u19d5\u19d6\u19d7\u19d8\u19d9\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19'
-
-Nl = u'\u16ee\u16ef\u16f0\u2160\u2161\u2162\u2163\u2164\u2165\u2166\u2167\u2168\u2169\u216a\u216b\u216c\u216d\u216e\u216f\u2170\u2171\u2172\u2173\u2174\u2175\u2176\u2177\u2178\u2179\u217a\u217b\u217c\u217d\u217e\u217f\u2180\u2181\u2182\u2183\u3007\u3021\u3022\u3023\u3024\u3025\u3026\u3027\u3028\u3029\u3038\u3039\u303a'
-
-No = u'\xb2\xb3\xb9\xbc\xbd\xbe\u09f4\u09f5\u09f6\u09f7\u09f8\u09f9\u0bf0\u0bf1\u0bf2\u0f2a\u0f2b\u0f2c\u0f2d\u0f2e\u0f2f\u0f30\u0f31\u0f32\u0f33\u1369\u136a\u136b\u136c\u136d\u136e\u136f\u1370\u1371\u1372\u1373\u1374\u1375\u1376\u1377\u1378\u1379\u137a\u137b\u137c\u17f0\u17f1\u17f2\u17f3\u17f4\u17f5\u17f6\u17f7\u17f8\u17f9\u2070\u2074\u2075\u2076\u2077\u2078\u2079\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2153\u2154\u2155\u2156\u2157\u2158\u2159\u215a\u215b\u215c\u215d\u215e\u215f\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2469\u246a\u246b\u246c\u246d\u246e\u246f\u2470\u2471\u2472\u2473\u2474\u2475\u2476\u2477\u2478\u2479\u247a\u247b\u247c\u247d\u247e\u247f\u2480\u2481\u2482\u2483\u2484\u2485\u2486\u2487\u2488\u2489\u248a\u248b\u248c\u248d\u248e\u248f\u2490\u2491\u2492\u2493\u2494\u2495\u2496\u2497\u2498\u2499\u249a\u249b\u24ea\u24eb\u24ec\u24ed\u24ee\u24ef\u24f0\u24f1\u24f2\u24f3\u24f4\u24f5\u24f6\u24f7\u24f8\u24f9\u24fa\u24fb\u24fc\u24fd\u24fe\u24ff\u2776\u2777\u2778\u2779\u277a\u277b\u277c\u277d\u277e\u277f\u2780\u2781\u2782\u2783\u2784\u2785\u2786\u2787\u2788\u2789\u278a\u278b\u278c\u278d\u278e\u278f\u2790\u2791\u2792\u2793\u2cfd\u3192\u3193\u3194\u3195\u3220\u3221\u3222\u3223\u3224\u3225\u3226\u3227\u3228\u3229\u3251\u3252\u3253\u3254\u3255\u3256\u3257\u3258\u3259\u325a\u325b\u325c\u325d\u325e\u325f\u3280\u3281\u3282\u3283\u3284\u3285\u3286\u3287\u3288\u3289\u32b1\u32b2\u32b3\u32b4\u32b5\u32b6\u32b7\u32b8\u32b9\u32ba\u32bb\u32bc\u32bd\u32be\u32bf'
-
-Pc = u'_\u203f\u2040\u2054\ufe33\ufe34\ufe4d\ufe4e\ufe4f\uff3f'
-
-Pd = u'-\u058a\u1806\u2010\u2011\u2012\u2013\u2014\u2015\u2e17\u301c\u3030\u30a0\ufe31\ufe32\ufe58\ufe63\uff0d'
-
-Pe = u')]}\u0f3b\u0f3d\u169c\u2046\u207e\u208e\u232a\u23b5\u2769\u276b\u276d\u276f\u2771\u2773\u2775\u27c6\u27e7\u27e9\u27eb\u2984\u2986\u2988\u298a\u298c\u298e\u2990\u2992\u2994\u2996\u2998\u29d9\u29db\u29fd\u3009\u300b\u300d\u300f\u3011\u3015\u3017\u3019\u301b\u301e\u301f\ufd3f\ufe18\ufe36\ufe38\ufe3a\ufe3c\ufe3e\ufe40\ufe42\ufe44\ufe48\ufe5a\ufe5c\ufe5e\uff09\uff3d\uff5d\uff60\uff63'
-
-Pf = u'\xbb\u2019\u201d\u203a\u2e03\u2e05\u2e0a\u2e0d\u2e1d'
-
-Pi = u'\xab\u2018\u201b\u201c\u201f\u2039\u2e02\u2e04\u2e09\u2e0c\u2e1c'
-
-Po = u'!"#%&\'*,./:;?@\\\xa1\xb7\xbf\u037e\u0387\u055a\u055b\u055c\u055d\u055e\u055f\u0589\u05be\u05c0\u05c3\u05c6\u05f3\u05f4\u060c\u060d\u061b\u061e\u061f\u066a\u066b\u066c\u066d\u06d4\u0700\u0701\u0702\u0703\u0704\u0705\u0706\u0707\u0708\u0709\u070a\u070b\u070c\u070d\u0964\u0965\u0970\u0df4\u0e4f\u0e5a\u0e5b\u0f04\u0f05\u0f06\u0f07\u0f08\u0f09\u0f0a\u0f0b\u0f0c\u0f0d\u0f0e\u0f0f\u0f10\u0f11\u0f12\u0f85\u0fd0\u0fd1\u104a\u104b\u104c\u104d\u104e\u104f\u10fb\u1361\u1362\u1363\u1364\u1365\u1366\u1367\u1368\u166d\u166e\u16eb\u16ec\u16ed\u1735\u1736\u17d4\u17d5\u17d6\u17d8\u17d9\u17da\u1800\u1801\u1802\u1803\u1804\u1805\u1807\u1808\u1809\u180a\u1944\u1945\u19de\u19df\u1a1e\u1a1f\u2016\u2017\u2020\u2021\u2022\u2023\u2024\u2025\u2026\u2027\u2030\u2031\u2032\u2033\u2034\u2035\u2036\u2037\u2038\u203b\u203c\u203d\u203e\u2041\u2042\u2043\u2047\u2048\u2049\u204a\u204b\u204c\u204d\u204e\u204f\u2050\u2051\u2053\u2055\u2056\u2057\u2058\u2059\u205a\u205b\u205c\u205d\u205e\u23b6\u2cf9\u2cfa\u2cfb\u2cfc\u2cfe\u2cff\u2e00\u2e01\u2e06\u2e07\u2e08\u2e0b\u2e0e\u2e0f\u2e10\u2e11\u2e12\u2e13\u2e14\u2e15\u2e16\u3001\u3002\u3003\u303d\u30fb\ufe10\ufe11\ufe12\ufe13\ufe14\ufe15\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49\ufe4a\ufe4b\ufe4c\ufe50\ufe51\ufe52\ufe54\ufe55\ufe56\ufe57\ufe5f\ufe60\ufe61\ufe68\ufe6a\ufe6b\uff01\uff02\uff03\uff05\uff06\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65'
-
-Ps = u'([{\u0f3a\u0f3c\u169b\u201a\u201e\u2045\u207d\u208d\u2329\u23b4\u2768\u276a\u276c\u276e\u2770\u2772\u2774\u27c5\u27e6\u27e8\u27ea\u2983\u2985\u2987\u2989\u298b\u298d\u298f\u2991\u2993\u2995\u2997\u29d8\u29da\u29fc\u3008\u300a\u300c\u300e\u3010\u3014\u3016\u3018\u301a\u301d\ufd3e\ufe17\ufe35\ufe37\ufe39\ufe3b\ufe3d\ufe3f\ufe41\ufe43\ufe47\ufe59\ufe5b\ufe5d\uff08\uff3b\uff5b\uff5f\uff62'
-
-Sc = u'$\xa2\xa3\xa4\xa5\u060b\u09f2\u09f3\u0af1\u0bf9\u0e3f\u17db\u20a0\u20a1\u20a2\u20a3\u20a4\u20a5\u20a6\u20a7\u20a8\u20a9\u20aa\u20ab\u20ac\u20ad\u20ae\u20af\u20b0\u20b1\u20b2\u20b3\u20b4\u20b5\ufdfc\ufe69\uff04\uffe0\uffe1\uffe5\uffe6'
-
-Sk = u'^`\xa8\xaf\xb4\xb8\u02c2\u02c3\u02c4\u02c5\u02d2\u02d3\u02d4\u02d5\u02d6\u02d7\u02d8\u02d9\u02da\u02db\u02dc\u02dd\u02de\u02df\u02e5\u02e6\u02e7\u02e8\u02e9\u02ea\u02eb\u02ec\u02ed\u02ef\u02f0\u02f1\u02f2\u02f3\u02f4\u02f5\u02f6\u02f7\u02f8\u02f9\u02fa\u02fb\u02fc\u02fd\u02fe\u02ff\u0374\u0375\u0384\u0385\u1fbd\u1fbf\u1fc0\u1fc1\u1fcd\u1fce\u1fcf\u1fdd\u1fde\u1fdf\u1fed\u1fee\u1fef\u1ffd\u1ffe\u309b\u309c\ua700\ua701\ua702\ua703\ua704\ua705\ua706\ua707\ua708\ua709\ua70a\ua70b\ua70c\ua70d\ua70e\ua70f\ua710\ua711\ua712\ua713\ua714\ua715\ua716\uff3e\uff40\uffe3'
-
-Sm = u'+<=>|~\xac\xb1\xd7\xf7\u03f6\u2044\u2052\u207a\u207b\u207c\u208a\u208b\u208c\u2140\u2141\u2142\u2143\u2144\u214b\u2190\u2191\u2192\u2193\u2194\u219a\u219b\u21a0\u21a3\u21a6\u21ae\u21ce\u21cf\u21d2\u21d4\u21f4\u21f5\u21f6\u21f7\u21f8\u21f9\u21fa\u21fb\u21fc\u21fd\u21fe\u21ff\u2200\u2201\u2202\u2203\u2204\u2205\u2206\u2207\u2208\u2209\u220a\u220b\u220c\u220d\u220e\u220f\u2210\u2211\u2212\u2213\u2214\u2215\u2216\u2217\u2218\u2219\u221a\u221b\u221c\u221d\u221e\u221f\u2220\u2221\u2222\u2223\u2224\u2225\u2226\u2227\u2228\u2229\u222a\u222b\u222c\u222d\u222e\u222f\u2230\u2231\u2232\u2233\u2234\u2235\u2236\u2237\u2238\u2239\u223a\u223b\u223c\u223d\u223e\u223f\u2240\u2241\u2242\u2243\u2244\u2245\u2246\u2247\u2248\u2249\u224a\u224b\u224c\u224d\u224e\u224f\u2250\u2251\u2252\u2253\u2254\u2255\u2256\u2257\u2258\u2259\u225a\u225b\u225c\u225d\u225e\u225f\u2260\u2261\u2262\u2263\u2264\u2265\u2266\u2267\u2268\u2269\u226a\u226b\u226c\u226d\u226e\u226f\u2270\u2271\u2272\u2273\u2274\u2275\u2276\u2277\u2278\u2279\u227a\u227b\u227c\u227d\u227e\u227f\u2280\u2281\u2282\u2283\u2284\u2285\u2286\u2287\u2288\u2289\u228a\u228b\u228c\u228d\u228e\u228f\u2290\u2291\u2292\u2293\u2294\u2295\u2296\u2297\u2298\u2299\u229a\u229b\u229c\u229d\u229e\u229f\u22a0\u22a1\u22a2\u22a3\u22a4\u22a5\u22a6\u22a7\u22a8\u22a9\u22aa\u22ab\u22ac\u22ad\u22ae\u22af\u22b0\u22b1\u22b2\u22b3\u22b4\u22b5\u22b6\u22b7\u22b8\u22b9\u22ba\u22bb\u22bc\u22bd\u22be\u22bf\u22c0\u22c1\u22c2\u22c3\u22c4\u22c5\u22c6\u22c7\u22c8\u22c9\u22ca\u22cb\u22cc\u22cd\u22ce\u22cf\u22d0\u22d1\u22d2\u22d3\u22d4\u22d5\u22d6\u22d7\u22d8\u22d9\u22da\u22db\u22dc\u22dd\u22de\u22df\u22e0\u22e1\u22e2\u22e3\u22e4\u22e5\u22e6\u22e7\u22e8\u22e9\u22ea\u22eb\u22ec\u22ed\u22ee\u22ef\u22f0\u22f1\u22f2\u22f3\u22f4\u22f5\u22f6\u22f7\u22f8\u22f9\u22fa\u22fb\u22fc\u22fd\u22fe\u22ff\u2308\u2309\u230a\u230b\u2320\u2321\u237c\u239b\u239c\u239d\u239e\u239f\u23a0\u23a1\u23a2\u23a3\u23a4\u23a5\u23a6\u23a7\u23a8\u23a9\u23aa\u23ab\u23ac\u23ad\u23ae\u23af\u23b0\u23b1\u23b2\u23b3\u25b7\u25c1\u25f8\u25f9\u25fa\u25fb\u25fc\u25fd\u25fe\u25ff\u266f\u27c0\u27c1\u27c2\u27c3\u27c4\u27d0\u27d1\u27d2\u27d3\u27d4\u27d5\u27d6\u27d7\u27d8\u27d9\u27da\u27db\u27dc\u27dd\u27de\u27df\u27e0\u27e1\u27e2\u27e3\u27e4\u27e5\u27f0\u27f1\u27f2\u27f3\u27f4\u27f5\u27f6\u27f7\u27f8\u27f9\u27fa\u27fb\u27fc\u27fd\u27fe\u27ff\u2900\u2901\u2902\u2903\u2904\u2905\u2906\u2907\u2908\u2909\u290a\u290b\u290c\u290d\u290e\u290f\u2910\u2911\u2912\u2913\u2914\u2915\u2916\u2917\u2918\u2919\u291a\u291b\u291c\u291d\u291e\u291f\u2920\u2921\u2922\u2923\u2924\u2925\u2926\u2927\u2928\u2929\u292a\u292b\u292c\u292d\u292e\u292f\u2930\u2931\u2932\u2933\u2934\u2935\u2936\u2937\u2938\u2939\u293a\u293b\u293c\u293d\u293e\u293f\u2940\u2941\u2942\u2943\u2944\u2945\u2946\u2947\u2948\u2949\u294a\u294b\u294c\u294d\u294e\u294f\u2950\u2951\u2952\u2953\u2954\u2955\u2956\u2957\u2958\u2959\u295a\u295b\u295c\u295d\u295e\u295f\u2960\u2961\u2962\u2963\u2964\u2965\u2966\u2967\u2968\u2969\u296a\u296b\u296c\u296d\u296e\u296f\u2970\u2971\u2972\u2973\u2974\u2975\u2976\u2977\u2978\u2979\u297a\u297b\u297c\u297d\u297e\u297f\u2980\u2981\u2982\u2999\u299a\u299b\u299c\u299d\u299e\u299f\u29a0\u29a1\u29a2\u29a3\u29a4\u29a5\u29a6\u29a7\u29a8\u29a9\u29aa\u29ab\u29ac\u29ad\u29ae\u29af\u29b0\u29b1\u29b2\u29b3\u29b4\u29b5\u29b6\u29b7\u29b8\u29b9\u29ba\u29bb\u29bc\u29bd\u29be\u29bf\u29c0\u29c1\u29c2\u29c3\u29c4\u29c5\u29c6\u29c7\u29c8\u29c9\u29ca\u29cb\u29cc\u29cd\u29ce\u29cf\u29d0\u29d1\u29d2\u29d3\u29d4\u29d5\u29d6\u29d7\u29dc\u29dd\u29de\u29df\u29e0\u29e1\u29e2\u29e3\u29e4\u29e5\u29e6\u29e7\u29e8\u29e9\u29ea\u29eb\u29ec\u29ed\u29ee\u29ef\u29f0\u29f1\u29f2\u29f3\u29f4\u29f5\u29f6\u29f7\u29f8\u29f9\u29fa\u29fb\u29fe\u29ff\u2a00\u2a01\u2a02\u2a03\u2a04\u2a05\u2a06\u2a07\u2a08\u2a09\u2a0a\u2a0b\u2a0c\u2a0d\u2a0e\u2a0f\u2a10\u2a11\u2a12\u2a13\u2a14\u2a15\u2a16\u2a17\u2a18\u2a19\u2a1a\u2a1b\u2a1c\u2a1d\u2a1e\u2a1f\u2a20\u2a21\u2a22\u2a23\u2a24\u2a25\u2a26\u2a27\u2a28\u2a29\u2a2a\u2a2b\u2a2c\u2a2d\u2a2e\u2a2f\u2a30\u2a31\u2a32\u2a33\u2a34\u2a35\u2a36\u2a37\u2a38\u2a39\u2a3a\u2a3b\u2a3c\u2a3d\u2a3e\u2a3f\u2a40\u2a41\u2a42\u2a43\u2a44\u2a45\u2a46\u2a47\u2a48\u2a49\u2a4a\u2a4b\u2a4c\u2a4d\u2a4e\u2a4f\u2a50\u2a51\u2a52\u2a53\u2a54\u2a55\u2a56\u2a57\u2a58\u2a59\u2a5a\u2a5b\u2a5c\u2a5d\u2a5e\u2a5f\u2a60\u2a61\u2a62\u2a63\u2a64\u2a65\u2a66\u2a67\u2a68\u2a69\u2a6a\u2a6b\u2a6c\u2a6d\u2a6e\u2a6f\u2a70\u2a71\u2a72\u2a73\u2a74\u2a75\u2a76\u2a77\u2a78\u2a79\u2a7a\u2a7b\u2a7c\u2a7d\u2a7e\u2a7f\u2a80\u2a81\u2a82\u2a83\u2a84\u2a85\u2a86\u2a87\u2a88\u2a89\u2a8a\u2a8b\u2a8c\u2a8d\u2a8e\u2a8f\u2a90\u2a91\u2a92\u2a93\u2a94\u2a95\u2a96\u2a97\u2a98\u2a99\u2a9a\u2a9b\u2a9c\u2a9d\u2a9e\u2a9f\u2aa0\u2aa1\u2aa2\u2aa3\u2aa4\u2aa5\u2aa6\u2aa7\u2aa8\u2aa9\u2aaa\u2aab\u2aac\u2aad\u2aae\u2aaf\u2ab0\u2ab1\u2ab2\u2ab3\u2ab4\u2ab5\u2ab6\u2ab7\u2ab8\u2ab9\u2aba\u2abb\u2abc\u2abd\u2abe\u2abf\u2ac0\u2ac1\u2ac2\u2ac3\u2ac4\u2ac5\u2ac6\u2ac7\u2ac8\u2ac9\u2aca\u2acb\u2acc\u2acd\u2ace\u2acf\u2ad0\u2ad1\u2ad2\u2ad3\u2ad4\u2ad5\u2ad6\u2ad7\u2ad8\u2ad9\u2ada\u2adb\u2adc\u2add\u2ade\u2adf\u2ae0\u2ae1\u2ae2\u2ae3\u2ae4\u2ae5\u2ae6\u2ae7\u2ae8\u2ae9\u2aea\u2aeb\u2aec\u2aed\u2aee\u2aef\u2af0\u2af1\u2af2\u2af3\u2af4\u2af5\u2af6\u2af7\u2af8\u2af9\u2afa\u2afb\u2afc\u2afd\u2afe\u2aff\ufb29\ufe62\ufe64\ufe65\ufe66\uff0b\uff1c\uff1d\uff1e\uff5c\uff5e\uffe2\uffe9\uffea\uffeb\uffec'
-
-So = u'\xa6\xa7\xa9\xae\xb0\xb6\u0482\u060e\u060f\u06e9\u06fd\u06fe\u09fa\u0b70\u0bf3\u0bf4\u0bf5\u0bf6\u0bf7\u0bf8\u0bfa\u0f01\u0f02\u0f03\u0f13\u0f14\u0f15\u0f16\u0f17\u0f1a\u0f1b\u0f1c\u0f1d\u0f1e\u0f1f\u0f34\u0f36\u0f38\u0fbe\u0fbf\u0fc0\u0fc1\u0fc2\u0fc3\u0fc4\u0fc5\u0fc7\u0fc8\u0fc9\u0fca\u0fcb\u0fcc\u0fcf\u1360\u1390\u1391\u1392\u1393\u1394\u1395\u1396\u1397\u1398\u1399\u1940\u19e0\u19e1\u19e2\u19e3\u19e4\u19e5\u19e6\u19e7\u19e8\u19e9\u19ea\u19eb\u19ec\u19ed\u19ee\u19ef\u19f0\u19f1\u19f2\u19f3\u19f4\u19f5\u19f6\u19f7\u19f8\u19f9\u19fa\u19fb\u19fc\u19fd\u19fe\u19ff\u2100\u2101\u2103\u2104\u2105\u2106\u2108\u2109\u2114\u2116\u2117\u2118\u211e\u211f\u2120\u2121\u2122\u2123\u2125\u2127\u2129\u212e\u2132\u213a\u213b\u214a\u214c\u2195\u2196\u2197\u2198\u2199\u219c\u219d\u219e\u219f\u21a1\u21a2\u21a4\u21a5\u21a7\u21a8\u21a9\u21aa\u21ab\u21ac\u21ad\u21af\u21b0\u21b1\u21b2\u21b3\u21b4\u21b5\u21b6\u21b7\u21b8\u21b9\u21ba\u21bb\u21bc\u21bd\u21be\u21bf\u21c0\u21c1\u21c2\u21c3\u21c4\u21c5\u21c6\u21c7\u21c8\u21c9\u21ca\u21cb\u21cc\u21cd\u21d0\u21d1\u21d3\u21d5\u21d6\u21d7\u21d8\u21d9\u21da\u21db\u21dc\u21dd\u21de\u21df\u21e0\u21e1\u21e2\u21e3\u21e4\u21e5\u21e6\u21e7\u21e8\u21e9\u21ea\u21eb\u21ec\u21ed\u21ee\u21ef\u21f0\u21f1\u21f2\u21f3\u2300\u2301\u2302\u2303\u2304\u2305\u2306\u2307\u230c\u230d\u230e\u230f\u2310\u2311\u2312\u2313\u2314\u2315\u2316\u2317\u2318\u2319\u231a\u231b\u231c\u231d\u231e\u231f\u2322\u2323\u2324\u2325\u2326\u2327\u2328\u232b\u232c\u232d\u232e\u232f\u2330\u2331\u2332\u2333\u2334\u2335\u2336\u2337\u2338\u2339\u233a\u233b\u233c\u233d\u233e\u233f\u2340\u2341\u2342\u2343\u2344\u2345\u2346\u2347\u2348\u2349\u234a\u234b\u234c\u234d\u234e\u234f\u2350\u2351\u2352\u2353\u2354\u2355\u2356\u2357\u2358\u2359\u235a\u235b\u235c\u235d\u235e\u235f\u2360\u2361\u2362\u2363\u2364\u2365\u2366\u2367\u2368\u2369\u236a\u236b\u236c\u236d\u236e\u236f\u2370\u2371\u2372\u2373\u2374\u2375\u2376\u2377\u2378\u2379\u237a\u237b\u237d\u237e\u237f\u2380\u2381\u2382\u2383\u2384\u2385\u2386\u2387\u2388\u2389\u238a\u238b\u238c\u238d\u238e\u238f\u2390\u2391\u2392\u2393\u2394\u2395\u2396\u2397\u2398\u2399\u239a\u23b7\u23b8\u23b9\u23ba\u23bb\u23bc\u23bd\u23be\u23bf\u23c0\u23c1\u23c2\u23c3\u23c4\u23c5\u23c6\u23c7\u23c8\u23c9\u23ca\u23cb\u23cc\u23cd\u23ce\u23cf\u23d0\u23d1\u23d2\u23d3\u23d4\u23d5\u23d6\u23d7\u23d8\u23d9\u23da\u23db\u2400\u2401\u2402\u2403\u2404\u2405\u2406\u2407\u2408\u2409\u240a\u240b\u240c\u240d\u240e\u240f\u2410\u2411\u2412\u2413\u2414\u2415\u2416\u2417\u2418\u2419\u241a\u241b\u241c\u241d\u241e\u241f\u2420\u2421\u2422\u2423\u2424\u2425\u2426\u2440\u2441\u2442\u2443\u2444\u2445\u2446\u2447\u2448\u2449\u244a\u249c\u249d\u249e\u249f\u24a0\u24a1\u24a2\u24a3\u24a4\u24a5\u24a6\u24a7\u24a8\u24a9\u24aa\u24ab\u24ac\u24ad\u24ae\u24af\u24b0\u24b1\u24b2\u24b3\u24b4\u24b5\u24b6\u24b7\u24b8\u24b9\u24ba\u24bb\u24bc\u24bd\u24be\u24bf\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca\u24cb\u24cc\u24cd\u24ce\u24cf\u24d0\u24d1\u24d2\u24d3\u24d4\u24d5\u24d6\u24d7\u24d8\u24d9\u24da\u24db\u24dc\u24dd\u24de\u24df\u24e0\u24e1\u24e2\u24e3\u24e4\u24e5\u24e6\u24e7\u24e8\u24e9\u2500\u2501\u2502\u2503\u2504\u2505\u2506\u2507\u2508\u2509\u250a\u250b\u250c\u250d\u250e\u250f\u2510\u2511\u2512\u2513\u2514\u2515\u2516\u2517\u2518\u2519\u251a\u251b\u251c\u251d\u251e\u251f\u2520\u2521\u2522\u2523\u2524\u2525\u2526\u2527\u2528\u2529\u252a\u252b\u252c\u252d\u252e\u252f\u2530\u2531\u2532\u2533\u2534\u2535\u2536\u2537\u2538\u2539\u253a\u253b\u253c\u253d\u253e\u253f\u2540\u2541\u2542\u2543\u2544\u2545\u2546\u2547\u2548\u2549\u254a\u254b\u254c\u254d\u254e\u254f\u2550\u2551\u2552\u2553\u2554\u2555\u2556\u2557\u2558\u2559\u255a\u255b\u255c\u255d\u255e\u255f\u2560\u2561\u2562\u2563\u2564\u2565\u2566\u2567\u2568\u2569\u256a\u256b\u256c\u256d\u256e\u256f\u2570\u2571\u2572\u2573\u2574\u2575\u2576\u2577\u2578\u2579\u257a\u257b\u257c\u257d\u257e\u257f\u2580\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2589\u258a\u258b\u258c\u258d\u258e\u258f\u2590\u2591\u2592\u2593\u2594\u2595\u2596\u2597\u2598\u2599\u259a\u259b\u259c\u259d\u259e\u259f\u25a0\u25a1\u25a2\u25a3\u25a4\u25a5\u25a6\u25a7\u25a8\u25a9\u25aa\u25ab\u25ac\u25ad\u25ae\u25af\u25b0\u25b1\u25b2\u25b3\u25b4\u25b5\u25b6\u25b8\u25b9\u25ba\u25bb\u25bc\u25bd\u25be\u25bf\u25c0\u25c2\u25c3\u25c4\u25c5\u25c6\u25c7\u25c8\u25c9\u25ca\u25cb\u25cc\u25cd\u25ce\u25cf\u25d0\u25d1\u25d2\u25d3\u25d4\u25d5\u25d6\u25d7\u25d8\u25d9\u25da\u25db\u25dc\u25dd\u25de\u25df\u25e0\u25e1\u25e2\u25e3\u25e4\u25e5\u25e6\u25e7\u25e8\u25e9\u25ea\u25eb\u25ec\u25ed\u25ee\u25ef\u25f0\u25f1\u25f2\u25f3\u25f4\u25f5\u25f6\u25f7\u2600\u2601\u2602\u2603\u2604\u2605\u2606\u2607\u2608\u2609\u260a\u260b\u260c\u260d\u260e\u260f\u2610\u2611\u2612\u2613\u2614\u2615\u2616\u2617\u2618\u2619\u261a\u261b\u261c\u261d\u261e\u261f\u2620\u2621\u2622\u2623\u2624\u2625\u2626\u2627\u2628\u2629\u262a\u262b\u262c\u262d\u262e\u262f\u2630\u2631\u2632\u2633\u2634\u2635\u2636\u2637\u2638\u2639\u263a\u263b\u263c\u263d\u263e\u263f\u2640\u2641\u2642\u2643\u2644\u2645\u2646\u2647\u2648\u2649\u264a\u264b\u264c\u264d\u264e\u264f\u2650\u2651\u2652\u2653\u2654\u2655\u2656\u2657\u2658\u2659\u265a\u265b\u265c\u265d\u265e\u265f\u2660\u2661\u2662\u2663\u2664\u2665\u2666\u2667\u2668\u2669\u266a\u266b\u266c\u266d\u266e\u2670\u2671\u2672\u2673\u2674\u2675\u2676\u2677\u2678\u2679\u267a\u267b\u267c\u267d\u267e\u267f\u2680\u2681\u2682\u2683\u2684\u2685\u2686\u2687\u2688\u2689\u268a\u268b\u268c\u268d\u268e\u268f\u2690\u2691\u2692\u2693\u2694\u2695\u2696\u2697\u2698\u2699\u269a\u269b\u269c\u26a0\u26a1\u26a2\u26a3\u26a4\u26a5\u26a6\u26a7\u26a8\u26a9\u26aa\u26ab\u26ac\u26ad\u26ae\u26af\u26b0\u26b1\u2701\u2702\u2703\u2704\u2706\u2707\u2708\u2709\u270c\u270d\u270e\u270f\u2710\u2711\u2712\u2713\u2714\u2715\u2716\u2717\u2718\u2719\u271a\u271b\u271c\u271d\u271e\u271f\u2720\u2721\u2722\u2723\u2724\u2725\u2726\u2727\u2729\u272a\u272b\u272c\u272d\u272e\u272f\u2730\u2731\u2732\u2733\u2734\u2735\u2736\u2737\u2738\u2739\u273a\u273b\u273c\u273d\u273e\u273f\u2740\u2741\u2742\u2743\u2744\u2745\u2746\u2747\u2748\u2749\u274a\u274b\u274d\u274f\u2750\u2751\u2752\u2756\u2758\u2759\u275a\u275b\u275c\u275d\u275e\u2761\u2762\u2763\u2764\u2765\u2766\u2767\u2794\u2798\u2799\u279a\u279b\u279c\u279d\u279e\u279f\u27a0\u27a1\u27a2\u27a3\u27a4\u27a5\u27a6\u27a7\u27a8\u27a9\u27aa\u27ab\u27ac\u27ad\u27ae\u27af\u27b1\u27b2\u27b3\u27b4\u27b5\u27b6\u27b7\u27b8\u27b9\u27ba\u27bb\u27bc\u27bd\u27be\u2800\u2801\u2802\u2803\u2804\u2805\u2806\u2807\u2808\u2809\u280a\u280b\u280c\u280d\u280e\u280f\u2810\u2811\u2812\u2813\u2814\u2815\u2816\u2817\u2818\u2819\u281a\u281b\u281c\u281d\u281e\u281f\u2820\u2821\u2822\u2823\u2824\u2825\u2826\u2827\u2828\u2829\u282a\u282b\u282c\u282d\u282e\u282f\u2830\u2831\u2832\u2833\u2834\u2835\u2836\u2837\u2838\u2839\u283a\u283b\u283c\u283d\u283e\u283f\u2840\u2841\u2842\u2843\u2844\u2845\u2846\u2847\u2848\u2849\u284a\u284b\u284c\u284d\u284e\u284f\u2850\u2851\u2852\u2853\u2854\u2855\u2856\u2857\u2858\u2859\u285a\u285b\u285c\u285d\u285e\u285f\u2860\u2861\u2862\u2863\u2864\u2865\u2866\u2867\u2868\u2869\u286a\u286b\u286c\u286d\u286e\u286f\u2870\u2871\u2872\u2873\u2874\u2875\u2876\u2877\u2878\u2879\u287a\u287b\u287c\u287d\u287e\u287f\u2880\u2881\u2882\u2883\u2884\u2885\u2886\u2887\u2888\u2889\u288a\u288b\u288c\u288d\u288e\u288f\u2890\u2891\u2892\u2893\u2894\u2895\u2896\u2897\u2898\u2899\u289a\u289b\u289c\u289d\u289e\u289f\u28a0\u28a1\u28a2\u28a3\u28a4\u28a5\u28a6\u28a7\u28a8\u28a9\u28aa\u28ab\u28ac\u28ad\u28ae\u28af\u28b0\u28b1\u28b2\u28b3\u28b4\u28b5\u28b6\u28b7\u28b8\u28b9\u28ba\u28bb\u28bc\u28bd\u28be\u28bf\u28c0\u28c1\u28c2\u28c3\u28c4\u28c5\u28c6\u28c7\u28c8\u28c9\u28ca\u28cb\u28cc\u28cd\u28ce\u28cf\u28d0\u28d1\u28d2\u28d3\u28d4\u28d5\u28d6\u28d7\u28d8\u28d9\u28da\u28db\u28dc\u28dd\u28de\u28df\u28e0\u28e1\u28e2\u28e3\u28e4\u28e5\u28e6\u28e7\u28e8\u28e9\u28ea\u28eb\u28ec\u28ed\u28ee\u28ef\u28f0\u28f1\u28f2\u28f3\u28f4\u28f5\u28f6\u28f7\u28f8\u28f9\u28fa\u28fb\u28fc\u28fd\u28fe\u28ff\u2b00\u2b01\u2b02\u2b03\u2b04\u2b05\u2b06\u2b07\u2b08\u2b09\u2b0a\u2b0b\u2b0c\u2b0d\u2b0e\u2b0f\u2b10\u2b11\u2b12\u2b13\u2ce5\u2ce6\u2ce7\u2ce8\u2ce9\u2cea\u2e80\u2e81\u2e82\u2e83\u2e84\u2e85\u2e86\u2e87\u2e88\u2e89\u2e8a\u2e8b\u2e8c\u2e8d\u2e8e\u2e8f\u2e90\u2e91\u2e92\u2e93\u2e94\u2e95\u2e96\u2e97\u2e98\u2e99\u2e9b\u2e9c\u2e9d\u2e9e\u2e9f\u2ea0\u2ea1\u2ea2\u2ea3\u2ea4\u2ea5\u2ea6\u2ea7\u2ea8\u2ea9\u2eaa\u2eab\u2eac\u2ead\u2eae\u2eaf\u2eb0\u2eb1\u2eb2\u2eb3\u2eb4\u2eb5\u2eb6\u2eb7\u2eb8\u2eb9\u2eba\u2ebb\u2ebc\u2ebd\u2ebe\u2ebf\u2ec0\u2ec1\u2ec2\u2ec3\u2ec4\u2ec5\u2ec6\u2ec7\u2ec8\u2ec9\u2eca\u2ecb\u2ecc\u2ecd\u2ece\u2ecf\u2ed0\u2ed1\u2ed2\u2ed3\u2ed4\u2ed5\u2ed6\u2ed7\u2ed8\u2ed9\u2eda\u2edb\u2edc\u2edd\u2ede\u2edf\u2ee0\u2ee1\u2ee2\u2ee3\u2ee4\u2ee5\u2ee6\u2ee7\u2ee8\u2ee9\u2eea\u2eeb\u2eec\u2eed\u2eee\u2eef\u2ef0\u2ef1\u2ef2\u2ef3\u2f00\u2f01\u2f02\u2f03\u2f04\u2f05\u2f06\u2f07\u2f08\u2f09\u2f0a\u2f0b\u2f0c\u2f0d\u2f0e\u2f0f\u2f10\u2f11\u2f12\u2f13\u2f14\u2f15\u2f16\u2f17\u2f18\u2f19\u2f1a\u2f1b\u2f1c\u2f1d\u2f1e\u2f1f\u2f20\u2f21\u2f22\u2f23\u2f24\u2f25\u2f26\u2f27\u2f28\u2f29\u2f2a\u2f2b\u2f2c\u2f2d\u2f2e\u2f2f\u2f30\u2f31\u2f32\u2f33\u2f34\u2f35\u2f36\u2f37\u2f38\u2f39\u2f3a\u2f3b\u2f3c\u2f3d\u2f3e\u2f3f\u2f40\u2f41\u2f42\u2f43\u2f44\u2f45\u2f46\u2f47\u2f48\u2f49\u2f4a\u2f4b\u2f4c\u2f4d\u2f4e\u2f4f\u2f50\u2f51\u2f52\u2f53\u2f54\u2f55\u2f56\u2f57\u2f58\u2f59\u2f5a\u2f5b\u2f5c\u2f5d\u2f5e\u2f5f\u2f60\u2f61\u2f62\u2f63\u2f64\u2f65\u2f66\u2f67\u2f68\u2f69\u2f6a\u2f6b\u2f6c\u2f6d\u2f6e\u2f6f\u2f70\u2f71\u2f72\u2f73\u2f74\u2f75\u2f76\u2f77\u2f78\u2f79\u2f7a\u2f7b\u2f7c\u2f7d\u2f7e\u2f7f\u2f80\u2f81\u2f82\u2f83\u2f84\u2f85\u2f86\u2f87\u2f88\u2f89\u2f8a\u2f8b\u2f8c\u2f8d\u2f8e\u2f8f\u2f90\u2f91\u2f92\u2f93\u2f94\u2f95\u2f96\u2f97\u2f98\u2f99\u2f9a\u2f9b\u2f9c\u2f9d\u2f9e\u2f9f\u2fa0\u2fa1\u2fa2\u2fa3\u2fa4\u2fa5\u2fa6\u2fa7\u2fa8\u2fa9\u2faa\u2fab\u2fac\u2fad\u2fae\u2faf\u2fb0\u2fb1\u2fb2\u2fb3\u2fb4\u2fb5\u2fb6\u2fb7\u2fb8\u2fb9\u2fba\u2fbb\u2fbc\u2fbd\u2fbe\u2fbf\u2fc0\u2fc1\u2fc2\u2fc3\u2fc4\u2fc5\u2fc6\u2fc7\u2fc8\u2fc9\u2fca\u2fcb\u2fcc\u2fcd\u2fce\u2fcf\u2fd0\u2fd1\u2fd2\u2fd3\u2fd4\u2fd5\u2ff0\u2ff1\u2ff2\u2ff3\u2ff4\u2ff5\u2ff6\u2ff7\u2ff8\u2ff9\u2ffa\u2ffb\u3004\u3012\u3013\u3020\u3036\u3037\u303e\u303f\u3190\u3191\u3196\u3197\u3198\u3199\u319a\u319b\u319c\u319d\u319e\u319f\u31c0\u31c1\u31c2\u31c3\u31c4\u31c5\u31c6\u31c7\u31c8\u31c9\u31ca\u31cb\u31cc\u31cd\u31ce\u31cf\u3200\u3201\u3202\u3203\u3204\u3205\u3206\u3207\u3208\u3209\u320a\u320b\u320c\u320d\u320e\u320f\u3210\u3211\u3212\u3213\u3214\u3215\u3216\u3217\u3218\u3219\u321a\u321b\u321c\u321d\u321e\u322a\u322b\u322c\u322d\u322e\u322f\u3230\u3231\u3232\u3233\u3234\u3235\u3236\u3237\u3238\u3239\u323a\u323b\u323c\u323d\u323e\u323f\u3240\u3241\u3242\u3243\u3250\u3260\u3261\u3262\u3263\u3264\u3265\u3266\u3267\u3268\u3269\u326a\u326b\u326c\u326d\u326e\u326f\u3270\u3271\u3272\u3273\u3274\u3275\u3276\u3277\u3278\u3279\u327a\u327b\u327c\u327d\u327e\u327f\u328a\u328b\u328c\u328d\u328e\u328f\u3290\u3291\u3292\u3293\u3294\u3295\u3296\u3297\u3298\u3299\u329a\u329b\u329c\u329d\u329e\u329f\u32a0\u32a1\u32a2\u32a3\u32a4\u32a5\u32a6\u32a7\u32a8\u32a9\u32aa\u32ab\u32ac\u32ad\u32ae\u32af\u32b0\u32c0\u32c1\u32c2\u32c3\u32c4\u32c5\u32c6\u32c7\u32c8\u32c9\u32ca\u32cb\u32cc\u32cd\u32ce\u32cf\u32d0\u32d1\u32d2\u32d3\u32d4\u32d5\u32d6\u32d7\u32d8\u32d9\u32da\u32db\u32dc\u32dd\u32de\u32df\u32e0\u32e1\u32e2\u32e3\u32e4\u32e5\u32e6\u32e7\u32e8\u32e9\u32ea\u32eb\u32ec\u32ed\u32ee\u32ef\u32f0\u32f1\u32f2\u32f3\u32f4\u32f5\u32f6\u32f7\u32f8\u32f9\u32fa\u32fb\u32fc\u32fd\u32fe\u3300\u3301\u3302\u3303\u3304\u3305\u3306\u3307\u3308\u3309\u330a\u330b\u330c\u330d\u330e\u330f\u3310\u3311\u3312\u3313\u3314\u3315\u3316\u3317\u3318\u3319\u331a\u331b\u331c\u331d\u331e\u331f\u3320\u3321\u3322\u3323\u3324\u3325\u3326\u3327\u3328\u3329\u332a\u332b\u332c\u332d\u332e\u332f\u3330\u3331\u3332\u3333\u3334\u3335\u3336\u3337\u3338\u3339\u333a\u333b\u333c\u333d\u333e\u333f\u3340\u3341\u3342\u3343\u3344\u3345\u3346\u3347\u3348\u3349\u334a\u334b\u334c\u334d\u334e\u334f\u3350\u3351\u3352\u3353\u3354\u3355\u3356\u3357\u3358\u3359\u335a\u335b\u335c\u335d\u335e\u335f\u3360\u3361\u3362\u3363\u3364\u3365\u3366\u3367\u3368\u3369\u336a\u336b\u336c\u336d\u336e\u336f\u3370\u3371\u3372\u3373\u3374\u3375\u3376\u3377\u3378\u3379\u337a\u337b\u337c\u337d\u337e\u337f\u3380\u3381\u3382\u3383\u3384\u3385\u3386\u3387\u3388\u3389\u338a\u338b\u338c\u338d\u338e\u338f\u3390\u3391\u3392\u3393\u3394\u3395\u3396\u3397\u3398\u3399\u339a\u339b\u339c\u339d\u339e\u339f\u33a0\u33a1\u33a2\u33a3\u33a4\u33a5\u33a6\u33a7\u33a8\u33a9\u33aa\u33ab\u33ac\u33ad\u33ae\u33af\u33b0\u33b1\u33b2\u33b3\u33b4\u33b5\u33b6\u33b7\u33b8\u33b9\u33ba\u33bb\u33bc\u33bd\u33be\u33bf\u33c0\u33c1\u33c2\u33c3\u33c4\u33c5\u33c6\u33c7\u33c8\u33c9\u33ca\u33cb\u33cc\u33cd\u33ce\u33cf\u33d0\u33d1\u33d2\u33d3\u33d4\u33d5\u33d6\u33d7\u33d8\u33d9\u33da\u33db\u33dc\u33dd\u33de\u33df\u33e0\u33e1\u33e2\u33e3\u33e4\u33e5\u33e6\u33e7\u33e8\u33e9\u33ea\u33eb\u33ec\u33ed\u33ee\u33ef\u33f0\u33f1\u33f2\u33f3\u33f4\u33f5\u33f6\u33f7\u33f8\u33f9\u33fa\u33fb\u33fc\u33fd\u33fe\u33ff\u4dc0\u4dc1\u4dc2\u4dc3\u4dc4\u4dc5\u4dc6\u4dc7\u4dc8\u4dc9\u4dca\u4dcb\u4dcc\u4dcd\u4dce\u4dcf\u4dd0\u4dd1\u4dd2\u4dd3\u4dd4\u4dd5\u4dd6\u4dd7\u4dd8\u4dd9\u4dda\u4ddb\u4ddc\u4ddd\u4dde\u4ddf\u4de0\u4de1\u4de2\u4de3\u4de4\u4de5\u4de6\u4de7\u4de8\u4de9\u4dea\u4deb\u4dec\u4ded\u4dee\u4def\u4df0\u4df1\u4df2\u4df3\u4df4\u4df5\u4df6\u4df7\u4df8\u4df9\u4dfa\u4dfb\u4dfc\u4dfd\u4dfe\u4dff\ua490\ua491\ua492\ua493\ua494\ua495\ua496\ua497\ua498\ua499\ua49a\ua49b\ua49c\ua49d\ua49e\ua49f\ua4a0\ua4a1\ua4a2\ua4a3\ua4a4\ua4a5\ua4a6\ua4a7\ua4a8\ua4a9\ua4aa\ua4ab\ua4ac\ua4ad\ua4ae\ua4af\ua4b0\ua4b1\ua4b2\ua4b3\ua4b4\ua4b5\ua4b6\ua4b7\ua4b8\ua4b9\ua4ba\ua4bb\ua4bc\ua4bd\ua4be\ua4bf\ua4c0\ua4c1\ua4c2\ua4c3\ua4c4\ua4c5\ua4c6\ua828\ua829\ua82a\ua82b\ufdfd\uffe4\uffe8\uffed\uffee\ufffc\ufffd'
-
-Zl = u'\u2028'
-
-Zp = u'\u2029'
-
-Zs = u' \xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
-
-cats = ['Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc', 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po', 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs']
-
-def combine(*args):
- return u''.join([globals()[cat] for cat in args])
-
-xid_start = u'\u0041-\u005A\u005F\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u01BA\u01BB\u01BC-\u01BF\u01C0-\u01C3\u01C4-\u0241\u0250-\u02AF\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EE\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03F5\u03F7-\u0481\u048A-\u04CE\u04D0-\u04F9\u0500-\u050F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0640\u0641-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u076D\u0780-\u07A5\u07B1\u0904-\u0939\u093D\u0950\u0958-\u0961\u097D\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E40-\u0E45\u0E46\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDD\u0F00\u0F40-\u0F47\u0F49-\u0F6A\u0F88-\u0F8B\u1000-\u1021\u1023-\u1027\u1029-\u102A\u1050-\u1055\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1159\u115F-\u11A2\u11A8-\u11F9\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u1676\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1842\u1843\u1844-\u1877\u1880-\u18A8\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19A9\u19C1-\u19C7\u1A00-\u1A16\u1D00-\u1D2B\u1D2C-\u1D61\u1D62-\u1D77\u1D78\u1D79-\u1D9A\u1D9B-\u1DBF\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u2094\u2102\u2107\u210A-\u2113\u2115\u2118\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212E\u212F-\u2131\u2133-\u2134\u2135-\u2138\u2139\u213C-\u213F\u2145-\u2149\u2160-\u2183\u2C00-\u2C2E\u2C30-\u2C5E\u2C80-\u2CE4\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005\u3006\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303A\u303B\u303C\u3041-\u3096\u309D-\u309E\u309F\u30A1-\u30FA\u30FC-\u30FE\u30FF\u3105-\u312C\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FBB\uA000-\uA014\uA015\uA016-\uA48C\uA800-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uAC00-\uD7A3\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFC5D\uFC64-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDF9\uFE71\uFE73\uFE77\uFE79\uFE7B\uFE7D\uFE7F-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFF6F\uFF70\uFF71-\uFF9D\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC'
-
-xid_continue = u'\u0030-\u0039\u0041-\u005A\u005F\u0061-\u007A\u00AA\u00B5\u00B7\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u01BA\u01BB\u01BC-\u01BF\u01C0-\u01C3\u01C4-\u0241\u0250-\u02AF\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EE\u0300-\u036F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03F5\u03F7-\u0481\u0483-\u0486\u048A-\u04CE\u04D0-\u04F9\u0500-\u050F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05B9\u05BB-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u0615\u0621-\u063A\u0640\u0641-\u064A\u064B-\u065E\u0660-\u0669\u066E-\u066F\u0670\u0671-\u06D3\u06D5\u06D6-\u06DC\u06DF-\u06E4\u06E5-\u06E6\u06E7-\u06E8\u06EA-\u06ED\u06EE-\u06EF\u06F0-\u06F9\u06FA-\u06FC\u06FF\u0710\u0711\u0712-\u072F\u0730-\u074A\u074D-\u076D\u0780-\u07A5\u07A6-\u07B0\u07B1\u0901-\u0902\u0903\u0904-\u0939\u093C\u093D\u093E-\u0940\u0941-\u0948\u0949-\u094C\u094D\u0950\u0951-\u0954\u0958-\u0961\u0962-\u0963\u0966-\u096F\u097D\u0981\u0982-\u0983\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC\u09BD\u09BE-\u09C0\u09C1-\u09C4\u09C7-\u09C8\u09CB-\u09CC\u09CD\u09CE\u09D7\u09DC-\u09DD\u09DF-\u09E1\u09E2-\u09E3\u09E6-\u09EF\u09F0-\u09F1\u0A01-\u0A02\u0A03\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A3C\u0A3E-\u0A40\u0A41-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A70-\u0A71\u0A72-\u0A74\u0A81-\u0A82\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABC\u0ABD\u0ABE-\u0AC0\u0AC1-\u0AC5\u0AC7-\u0AC8\u0AC9\u0ACB-\u0ACC\u0ACD\u0AD0\u0AE0-\u0AE1\u0AE2-\u0AE3\u0AE6-\u0AEF\u0B01\u0B02-\u0B03\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3C\u0B3D\u0B3E\u0B3F\u0B40\u0B41-\u0B43\u0B47-\u0B48\u0B4B-\u0B4C\u0B4D\u0B56\u0B57\u0B5C-\u0B5D\u0B5F-\u0B61\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BBF\u0BC0\u0BC1-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BCD\u0BD7\u0BE6-\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3E-\u0C40\u0C41-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56\u0C60-\u0C61\u0C66-\u0C6F\u0C82-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC\u0CBD\u0CBE\u0CBF\u0CC0-\u0CC4\u0CC6\u0CC7-\u0CC8\u0CCA-\u0CCB\u0CCC-\u0CCD\u0CD5-\u0CD6\u0CDE\u0CE0-\u0CE1\u0CE6-\u0CEF\u0D02-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D3E-\u0D40\u0D41-\u0D43\u0D46-\u0D48\u0D4A-\u0D4C\u0D4D\u0D57\u0D60-\u0D61\u0D66-\u0D6F\u0D82-\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD1\u0DD2-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2-\u0DF3\u0E01-\u0E30\u0E31\u0E32-\u0E33\u0E34-\u0E3A\u0E40-\u0E45\u0E46\u0E47-\u0E4E\u0E50-\u0E59\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB1\u0EB2-\u0EB3\u0EB4-\u0EB9\u0EBB-\u0EBC\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDD\u0F00\u0F18-\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F3F\u0F40-\u0F47\u0F49-\u0F6A\u0F71-\u0F7E\u0F7F\u0F80-\u0F84\u0F86-\u0F87\u0F88-\u0F8B\u0F90-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1021\u1023-\u1027\u1029-\u102A\u102C\u102D-\u1030\u1031\u1032\u1036-\u1037\u1038\u1039\u1040-\u1049\u1050-\u1055\u1056-\u1057\u1058-\u1059\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1159\u115F-\u11A2\u11A8-\u11F9\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u1676\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1712-\u1714\u1720-\u1731\u1732-\u1734\u1740-\u1751\u1752-\u1753\u1760-\u176C\u176E-\u1770\u1772-\u1773\u1780-\u17B3\u17B6\u17B7-\u17BD\u17BE-\u17C5\u17C6\u17C7-\u17C8\u17C9-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1842\u1843\u1844-\u1877\u1880-\u18A8\u18A9\u1900-\u191C\u1920-\u1922\u1923-\u1926\u1927-\u1928\u1929-\u192B\u1930-\u1931\u1932\u1933-\u1938\u1939-\u193B\u1946-\u194F\u1950-\u196D\u1970-\u1974\u1980-\u19A9\u19B0-\u19C0\u19C1-\u19C7\u19C8-\u19C9\u19D0-\u19D9\u1A00-\u1A16\u1A17-\u1A18\u1A19-\u1A1B\u1D00-\u1D2B\u1D2C-\u1D61\u1D62-\u1D77\u1D78\u1D79-\u1D9A\u1D9B-\u1DBF\u1DC0-\u1DC3\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F-\u2040\u2054\u2071\u207F\u2090-\u2094\u20D0-\u20DC\u20E1\u20E5-\u20EB\u2102\u2107\u210A-\u2113\u2115\u2118\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212E\u212F-\u2131\u2133-\u2134\u2135-\u2138\u2139\u213C-\u213F\u2145-\u2149\u2160-\u2183\u2C00-\u2C2E\u2C30-\u2C5E\u2C80-\u2CE4\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005\u3006\u3007\u3021-\u3029\u302A-\u302F\u3031-\u3035\u3038-\u303A\u303B\u303C\u3041-\u3096\u3099-\u309A\u309D-\u309E\u309F\u30A1-\u30FA\u30FC-\u30FE\u30FF\u3105-\u312C\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FBB\uA000-\uA014\uA015\uA016-\uA48C\uA800-\uA801\uA802\uA803-\uA805\uA806\uA807-\uA80A\uA80B\uA80C-\uA822\uA823-\uA824\uA825-\uA826\uA827\uAC00-\uD7A3\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1E\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFC5D\uFC64-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDF9\uFE00-\uFE0F\uFE20-\uFE23\uFE33-\uFE34\uFE4D-\uFE4F\uFE71\uFE73\uFE77\uFE79\uFE7B\uFE7D\uFE7F-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFF6F\uFF70\uFF71-\uFF9D\uFF9E-\uFF9F\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC'
-
-def allexcept(*args):
- newcats = cats[:]
- for arg in args:
- newcats.remove(arg)
- return u''.join([globals()[cat] for cat in newcats])
-
-if __name__ == '__main__':
- import unicodedata
-
- categories = {}
-
- f = open(__file__.rstrip('co'))
- try:
- content = f.read()
- finally:
- f.close()
-
- header = content[:content.find('Cc =')]
- footer = content[content.find("def combine("):]
-
- for code in range(65535):
- c = unichr(code)
- cat = unicodedata.category(c)
- categories.setdefault(cat, []).append(c)
-
- f = open(__file__, 'w')
- f.write(header)
-
- for cat in sorted(categories):
- val = u''.join(categories[cat])
- if cat == 'Cs':
- # Jython can't handle isolated surrogates
- f.write("""\
-try:
- Cs = eval(r"%r")
-except UnicodeDecodeError:
- Cs = '' # Jython can't handle isolated surrogates\n\n""" % val)
- else:
- f.write('%s = %r\n\n' % (cat, val))
- f.write('cats = %r\n\n' % sorted(categories.keys()))
-
- f.write(footer)
- f.close()
diff --git a/module/lib/jinja2/bccache.py b/module/lib/jinja2/bccache.py
deleted file mode 100644
index 1e2236c3a..000000000
--- a/module/lib/jinja2/bccache.py
+++ /dev/null
@@ -1,280 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.bccache
- ~~~~~~~~~~~~~~
-
- This module implements the bytecode cache system Jinja is optionally
- using. This is useful if you have very complex template situations and
- the compiliation of all those templates slow down your application too
- much.
-
- Situations where this is useful are often forking web applications that
- are initialized on the first request.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-from os import path, listdir
-import marshal
-import tempfile
-import cPickle as pickle
-import fnmatch
-from cStringIO import StringIO
-try:
- from hashlib import sha1
-except ImportError:
- from sha import new as sha1
-from jinja2.utils import open_if_exists
-
-
-bc_version = 1
-bc_magic = 'j2'.encode('ascii') + pickle.dumps(bc_version, 2)
-
-
-class Bucket(object):
- """Buckets are used to store the bytecode for one template. It's created
- and initialized by the bytecode cache and passed to the loading functions.
-
- The buckets get an internal checksum from the cache assigned and use this
- to automatically reject outdated cache material. Individual bytecode
- cache subclasses don't have to care about cache invalidation.
- """
-
- def __init__(self, environment, key, checksum):
- self.environment = environment
- self.key = key
- self.checksum = checksum
- self.reset()
-
- def reset(self):
- """Resets the bucket (unloads the bytecode)."""
- self.code = None
-
- def load_bytecode(self, f):
- """Loads bytecode from a file or file like object."""
- # make sure the magic header is correct
- magic = f.read(len(bc_magic))
- if magic != bc_magic:
- self.reset()
- return
- # the source code of the file changed, we need to reload
- checksum = pickle.load(f)
- if self.checksum != checksum:
- self.reset()
- return
- # now load the code. Because marshal is not able to load
- # from arbitrary streams we have to work around that
- if isinstance(f, file):
- self.code = marshal.load(f)
- else:
- self.code = marshal.loads(f.read())
-
- def write_bytecode(self, f):
- """Dump the bytecode into the file or file like object passed."""
- if self.code is None:
- raise TypeError('can\'t write empty bucket')
- f.write(bc_magic)
- pickle.dump(self.checksum, f, 2)
- if isinstance(f, file):
- marshal.dump(self.code, f)
- else:
- f.write(marshal.dumps(self.code))
-
- def bytecode_from_string(self, string):
- """Load bytecode from a string."""
- self.load_bytecode(StringIO(string))
-
- def bytecode_to_string(self):
- """Return the bytecode as string."""
- out = StringIO()
- self.write_bytecode(out)
- return out.getvalue()
-
-
-class BytecodeCache(object):
- """To implement your own bytecode cache you have to subclass this class
- and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of
- these methods are passed a :class:`~jinja2.bccache.Bucket`.
-
- A very basic bytecode cache that saves the bytecode on the file system::
-
- from os import path
-
- class MyCache(BytecodeCache):
-
- def __init__(self, directory):
- self.directory = directory
-
- def load_bytecode(self, bucket):
- filename = path.join(self.directory, bucket.key)
- if path.exists(filename):
- with open(filename, 'rb') as f:
- bucket.load_bytecode(f)
-
- def dump_bytecode(self, bucket):
- filename = path.join(self.directory, bucket.key)
- with open(filename, 'wb') as f:
- bucket.write_bytecode(f)
-
- A more advanced version of a filesystem based bytecode cache is part of
- Jinja2.
- """
-
- def load_bytecode(self, bucket):
- """Subclasses have to override this method to load bytecode into a
- bucket. If they are not able to find code in the cache for the
- bucket, it must not do anything.
- """
- raise NotImplementedError()
-
- def dump_bytecode(self, bucket):
- """Subclasses have to override this method to write the bytecode
- from a bucket back to the cache. If it unable to do so it must not
- fail silently but raise an exception.
- """
- raise NotImplementedError()
-
- def clear(self):
- """Clears the cache. This method is not used by Jinja2 but should be
- implemented to allow applications to clear the bytecode cache used
- by a particular environment.
- """
-
- def get_cache_key(self, name, filename=None):
- """Returns the unique hash key for this template name."""
- hash = sha1(name.encode('utf-8'))
- if filename is not None:
- if isinstance(filename, unicode):
- filename = filename.encode('utf-8')
- hash.update('|' + filename)
- return hash.hexdigest()
-
- def get_source_checksum(self, source):
- """Returns a checksum for the source."""
- return sha1(source.encode('utf-8')).hexdigest()
-
- def get_bucket(self, environment, name, filename, source):
- """Return a cache bucket for the given template. All arguments are
- mandatory but filename may be `None`.
- """
- key = self.get_cache_key(name, filename)
- checksum = self.get_source_checksum(source)
- bucket = Bucket(environment, key, checksum)
- self.load_bytecode(bucket)
- return bucket
-
- def set_bucket(self, bucket):
- """Put the bucket into the cache."""
- self.dump_bytecode(bucket)
-
-
-class FileSystemBytecodeCache(BytecodeCache):
- """A bytecode cache that stores bytecode on the filesystem. It accepts
- two arguments: The directory where the cache items are stored and a
- pattern string that is used to build the filename.
-
- If no directory is specified the system temporary items folder is used.
-
- The pattern can be used to have multiple separate caches operate on the
- same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s``
- is replaced with the cache key.
-
- >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
-
- This bytecode cache supports clearing of the cache using the clear method.
- """
-
- def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
- if directory is None:
- directory = tempfile.gettempdir()
- self.directory = directory
- self.pattern = pattern
-
- def _get_cache_filename(self, bucket):
- return path.join(self.directory, self.pattern % bucket.key)
-
- def load_bytecode(self, bucket):
- f = open_if_exists(self._get_cache_filename(bucket), 'rb')
- if f is not None:
- try:
- bucket.load_bytecode(f)
- finally:
- f.close()
-
- def dump_bytecode(self, bucket):
- f = open(self._get_cache_filename(bucket), 'wb')
- try:
- bucket.write_bytecode(f)
- finally:
- f.close()
-
- def clear(self):
- # imported lazily here because google app-engine doesn't support
- # write access on the file system and the function does not exist
- # normally.
- from os import remove
- files = fnmatch.filter(listdir(self.directory), self.pattern % '*')
- for filename in files:
- try:
- remove(path.join(self.directory, filename))
- except OSError:
- pass
-
-
-class MemcachedBytecodeCache(BytecodeCache):
- """This class implements a bytecode cache that uses a memcache cache for
- storing the information. It does not enforce a specific memcache library
- (tummy's memcache or cmemcache) but will accept any class that provides
- the minimal interface required.
-
- Libraries compatible with this class:
-
- - `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
- - `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_
- - `cmemcache <http://gijsbert.org/cmemcache/>`_
-
- (Unfortunately the django cache interface is not compatible because it
- does not support storing binary data, only unicode. You can however pass
- the underlying cache client to the bytecode cache which is available
- as `django.core.cache.cache._client`.)
-
- The minimal interface for the client passed to the constructor is this:
-
- .. class:: MinimalClientInterface
-
- .. method:: set(key, value[, timeout])
-
- Stores the bytecode in the cache. `value` is a string and
- `timeout` the timeout of the key. If timeout is not provided
- a default timeout or no timeout should be assumed, if it's
- provided it's an integer with the number of seconds the cache
- item should exist.
-
- .. method:: get(key)
-
- Returns the value for the cache key. If the item does not
- exist in the cache the return value must be `None`.
-
- The other arguments to the constructor are the prefix for all keys that
- is added before the actual cache key and the timeout for the bytecode in
- the cache system. We recommend a high (or no) timeout.
-
- This bytecode cache does not support clearing of used items in the cache.
- The clear method is a no-operation function.
- """
-
- def __init__(self, client, prefix='jinja2/bytecode/', timeout=None):
- self.client = client
- self.prefix = prefix
- self.timeout = timeout
-
- def load_bytecode(self, bucket):
- code = self.client.get(self.prefix + bucket.key)
- if code is not None:
- bucket.bytecode_from_string(code)
-
- def dump_bytecode(self, bucket):
- args = (self.prefix + bucket.key, bucket.bytecode_to_string())
- if self.timeout is not None:
- args += (self.timeout,)
- self.client.set(*args)
diff --git a/module/lib/jinja2/compiler.py b/module/lib/jinja2/compiler.py
deleted file mode 100644
index 57641596a..000000000
--- a/module/lib/jinja2/compiler.py
+++ /dev/null
@@ -1,1640 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.compiler
- ~~~~~~~~~~~~~~~
-
- Compiles nodes into python code.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-from cStringIO import StringIO
-from itertools import chain
-from copy import deepcopy
-from jinja2 import nodes
-from jinja2.nodes import EvalContext
-from jinja2.visitor import NodeVisitor, NodeTransformer
-from jinja2.exceptions import TemplateAssertionError
-from jinja2.utils import Markup, concat, escape, is_python_keyword, next
-
-
-operators = {
- 'eq': '==',
- 'ne': '!=',
- 'gt': '>',
- 'gteq': '>=',
- 'lt': '<',
- 'lteq': '<=',
- 'in': 'in',
- 'notin': 'not in'
-}
-
-try:
- exec '(0 if 0 else 0)'
-except SyntaxError:
- have_condexpr = False
-else:
- have_condexpr = True
-
-
-# what method to iterate over items do we want to use for dict iteration
-# in generated code? on 2.x let's go with iteritems, on 3.x with items
-if hasattr(dict, 'iteritems'):
- dict_item_iter = 'iteritems'
-else:
- dict_item_iter = 'items'
-
-
-# does if 0: dummy(x) get us x into the scope?
-def unoptimize_before_dead_code():
- x = 42
- def f():
- if 0: dummy(x)
- return f
-unoptimize_before_dead_code = bool(unoptimize_before_dead_code().func_closure)
-
-
-def generate(node, environment, name, filename, stream=None,
- defer_init=False):
- """Generate the python source for a node tree."""
- if not isinstance(node, nodes.Template):
- raise TypeError('Can\'t compile non template nodes')
- generator = CodeGenerator(environment, name, filename, stream, defer_init)
- generator.visit(node)
- if stream is None:
- return generator.stream.getvalue()
-
-
-def has_safe_repr(value):
- """Does the node have a safe representation?"""
- if value is None or value is NotImplemented or value is Ellipsis:
- return True
- if isinstance(value, (bool, int, long, float, complex, basestring,
- xrange, Markup)):
- return True
- if isinstance(value, (tuple, list, set, frozenset)):
- for item in value:
- if not has_safe_repr(item):
- return False
- return True
- elif isinstance(value, dict):
- for key, value in value.iteritems():
- if not has_safe_repr(key):
- return False
- if not has_safe_repr(value):
- return False
- return True
- return False
-
-
-def find_undeclared(nodes, names):
- """Check if the names passed are accessed undeclared. The return value
- is a set of all the undeclared names from the sequence of names found.
- """
- visitor = UndeclaredNameVisitor(names)
- try:
- for node in nodes:
- visitor.visit(node)
- except VisitorExit:
- pass
- return visitor.undeclared
-
-
-class Identifiers(object):
- """Tracks the status of identifiers in frames."""
-
- def __init__(self):
- # variables that are known to be declared (probably from outer
- # frames or because they are special for the frame)
- self.declared = set()
-
- # undeclared variables from outer scopes
- self.outer_undeclared = set()
-
- # names that are accessed without being explicitly declared by
- # this one or any of the outer scopes. Names can appear both in
- # declared and undeclared.
- self.undeclared = set()
-
- # names that are declared locally
- self.declared_locally = set()
-
- # names that are declared by parameters
- self.declared_parameter = set()
-
- def add_special(self, name):
- """Register a special name like `loop`."""
- self.undeclared.discard(name)
- self.declared.add(name)
-
- def is_declared(self, name, local_only=False):
- """Check if a name is declared in this or an outer scope."""
- if name in self.declared_locally or name in self.declared_parameter:
- return True
- if local_only:
- return False
- return name in self.declared
-
- def copy(self):
- return deepcopy(self)
-
-
-class Frame(object):
- """Holds compile time information for us."""
-
- def __init__(self, eval_ctx, parent=None):
- self.eval_ctx = eval_ctx
- self.identifiers = Identifiers()
-
- # a toplevel frame is the root + soft frames such as if conditions.
- self.toplevel = False
-
- # the root frame is basically just the outermost frame, so no if
- # conditions. This information is used to optimize inheritance
- # situations.
- self.rootlevel = False
-
- # in some dynamic inheritance situations the compiler needs to add
- # write tests around output statements.
- self.require_output_check = parent and parent.require_output_check
-
- # inside some tags we are using a buffer rather than yield statements.
- # this for example affects {% filter %} or {% macro %}. If a frame
- # is buffered this variable points to the name of the list used as
- # buffer.
- self.buffer = None
-
- # the name of the block we're in, otherwise None.
- self.block = parent and parent.block or None
-
- # a set of actually assigned names
- self.assigned_names = set()
-
- # the parent of this frame
- self.parent = parent
-
- if parent is not None:
- self.identifiers.declared.update(
- parent.identifiers.declared |
- parent.identifiers.declared_parameter |
- parent.assigned_names
- )
- self.identifiers.outer_undeclared.update(
- parent.identifiers.undeclared -
- self.identifiers.declared
- )
- self.buffer = parent.buffer
-
- def copy(self):
- """Create a copy of the current one."""
- rv = object.__new__(self.__class__)
- rv.__dict__.update(self.__dict__)
- rv.identifiers = object.__new__(self.identifiers.__class__)
- rv.identifiers.__dict__.update(self.identifiers.__dict__)
- return rv
-
- def inspect(self, nodes, hard_scope=False):
- """Walk the node and check for identifiers. If the scope is hard (eg:
- enforce on a python level) overrides from outer scopes are tracked
- differently.
- """
- visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
- for node in nodes:
- visitor.visit(node)
-
- def find_shadowed(self, extra=()):
- """Find all the shadowed names. extra is an iterable of variables
- that may be defined with `add_special` which may occour scoped.
- """
- i = self.identifiers
- return (i.declared | i.outer_undeclared) & \
- (i.declared_locally | i.declared_parameter) | \
- set(x for x in extra if i.is_declared(x))
-
- def inner(self):
- """Return an inner frame."""
- return Frame(self.eval_ctx, self)
-
- def soft(self):
- """Return a soft frame. A soft frame may not be modified as
- standalone thing as it shares the resources with the frame it
- was created of, but it's not a rootlevel frame any longer.
- """
- rv = self.copy()
- rv.rootlevel = False
- return rv
-
- __copy__ = copy
-
-
-class VisitorExit(RuntimeError):
- """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
-
-
-class DependencyFinderVisitor(NodeVisitor):
- """A visitor that collects filter and test calls."""
-
- def __init__(self):
- self.filters = set()
- self.tests = set()
-
- def visit_Filter(self, node):
- self.generic_visit(node)
- self.filters.add(node.name)
-
- def visit_Test(self, node):
- self.generic_visit(node)
- self.tests.add(node.name)
-
- def visit_Block(self, node):
- """Stop visiting at blocks."""
-
-
-class UndeclaredNameVisitor(NodeVisitor):
- """A visitor that checks if a name is accessed without being
- declared. This is different from the frame visitor as it will
- not stop at closure frames.
- """
-
- def __init__(self, names):
- self.names = set(names)
- self.undeclared = set()
-
- def visit_Name(self, node):
- if node.ctx == 'load' and node.name in self.names:
- self.undeclared.add(node.name)
- if self.undeclared == self.names:
- raise VisitorExit()
- else:
- self.names.discard(node.name)
-
- def visit_Block(self, node):
- """Stop visiting a blocks."""
-
-
-class FrameIdentifierVisitor(NodeVisitor):
- """A visitor for `Frame.inspect`."""
-
- def __init__(self, identifiers, hard_scope):
- self.identifiers = identifiers
- self.hard_scope = hard_scope
-
- def visit_Name(self, node):
- """All assignments to names go through this function."""
- if node.ctx == 'store':
- self.identifiers.declared_locally.add(node.name)
- elif node.ctx == 'param':
- self.identifiers.declared_parameter.add(node.name)
- elif node.ctx == 'load' and not \
- self.identifiers.is_declared(node.name, self.hard_scope):
- self.identifiers.undeclared.add(node.name)
-
- def visit_If(self, node):
- self.visit(node.test)
- real_identifiers = self.identifiers
-
- old_names = real_identifiers.declared_locally | \
- real_identifiers.declared_parameter
-
- def inner_visit(nodes):
- if not nodes:
- return set()
- self.identifiers = real_identifiers.copy()
- for subnode in nodes:
- self.visit(subnode)
- rv = self.identifiers.declared_locally - old_names
- # we have to remember the undeclared variables of this branch
- # because we will have to pull them.
- real_identifiers.undeclared.update(self.identifiers.undeclared)
- self.identifiers = real_identifiers
- return rv
-
- body = inner_visit(node.body)
- else_ = inner_visit(node.else_ or ())
-
- # the differences between the two branches are also pulled as
- # undeclared variables
- real_identifiers.undeclared.update(body.symmetric_difference(else_) -
- real_identifiers.declared)
-
- # remember those that are declared.
- real_identifiers.declared_locally.update(body | else_)
-
- def visit_Macro(self, node):
- self.identifiers.declared_locally.add(node.name)
-
- def visit_Import(self, node):
- self.generic_visit(node)
- self.identifiers.declared_locally.add(node.target)
-
- def visit_FromImport(self, node):
- self.generic_visit(node)
- for name in node.names:
- if isinstance(name, tuple):
- self.identifiers.declared_locally.add(name[1])
- else:
- self.identifiers.declared_locally.add(name)
-
- def visit_Assign(self, node):
- """Visit assignments in the correct order."""
- self.visit(node.node)
- self.visit(node.target)
-
- def visit_For(self, node):
- """Visiting stops at for blocks. However the block sequence
- is visited as part of the outer scope.
- """
- self.visit(node.iter)
-
- def visit_CallBlock(self, node):
- self.visit(node.call)
-
- def visit_FilterBlock(self, node):
- self.visit(node.filter)
-
- def visit_Scope(self, node):
- """Stop visiting at scopes."""
-
- def visit_Block(self, node):
- """Stop visiting at blocks."""
-
-
-class CompilerExit(Exception):
- """Raised if the compiler encountered a situation where it just
- doesn't make sense to further process the code. Any block that
- raises such an exception is not further processed.
- """
-
-
-class CodeGenerator(NodeVisitor):
-
- def __init__(self, environment, name, filename, stream=None,
- defer_init=False):
- if stream is None:
- stream = StringIO()
- self.environment = environment
- self.name = name
- self.filename = filename
- self.stream = stream
- self.created_block_context = False
- self.defer_init = defer_init
-
- # aliases for imports
- self.import_aliases = {}
-
- # a registry for all blocks. Because blocks are moved out
- # into the global python scope they are registered here
- self.blocks = {}
-
- # the number of extends statements so far
- self.extends_so_far = 0
-
- # some templates have a rootlevel extends. In this case we
- # can safely assume that we're a child template and do some
- # more optimizations.
- self.has_known_extends = False
-
- # the current line number
- self.code_lineno = 1
-
- # registry of all filters and tests (global, not block local)
- self.tests = {}
- self.filters = {}
-
- # the debug information
- self.debug_info = []
- self._write_debug_info = None
-
- # the number of new lines before the next write()
- self._new_lines = 0
-
- # the line number of the last written statement
- self._last_line = 0
-
- # true if nothing was written so far.
- self._first_write = True
-
- # used by the `temporary_identifier` method to get new
- # unique, temporary identifier
- self._last_identifier = 0
-
- # the current indentation
- self._indentation = 0
-
- # -- Various compilation helpers
-
- def fail(self, msg, lineno):
- """Fail with a :exc:`TemplateAssertionError`."""
- raise TemplateAssertionError(msg, lineno, self.name, self.filename)
-
- def temporary_identifier(self):
- """Get a new unique identifier."""
- self._last_identifier += 1
- return 't_%d' % self._last_identifier
-
- def buffer(self, frame):
- """Enable buffering for the frame from that point onwards."""
- frame.buffer = self.temporary_identifier()
- self.writeline('%s = []' % frame.buffer)
-
- def return_buffer_contents(self, frame):
- """Return the buffer contents of the frame."""
- if frame.eval_ctx.volatile:
- self.writeline('if context.eval_ctx.autoescape:')
- self.indent()
- self.writeline('return Markup(concat(%s))' % frame.buffer)
- self.outdent()
- self.writeline('else:')
- self.indent()
- self.writeline('return concat(%s)' % frame.buffer)
- self.outdent()
- elif frame.eval_ctx.autoescape:
- self.writeline('return Markup(concat(%s))' % frame.buffer)
- else:
- self.writeline('return concat(%s)' % frame.buffer)
-
- def indent(self):
- """Indent by one."""
- self._indentation += 1
-
- def outdent(self, step=1):
- """Outdent by step."""
- self._indentation -= step
-
- def start_write(self, frame, node=None):
- """Yield or write into the frame buffer."""
- if frame.buffer is None:
- self.writeline('yield ', node)
- else:
- self.writeline('%s.append(' % frame.buffer, node)
-
- def end_write(self, frame):
- """End the writing process started by `start_write`."""
- if frame.buffer is not None:
- self.write(')')
-
- def simple_write(self, s, frame, node=None):
- """Simple shortcut for start_write + write + end_write."""
- self.start_write(frame, node)
- self.write(s)
- self.end_write(frame)
-
- def blockvisit(self, nodes, frame):
- """Visit a list of nodes as block in a frame. If the current frame
- is no buffer a dummy ``if 0: yield None`` is written automatically
- unless the force_generator parameter is set to False.
- """
- if frame.buffer is None:
- self.writeline('if 0: yield None')
- else:
- self.writeline('pass')
- try:
- for node in nodes:
- self.visit(node, frame)
- except CompilerExit:
- pass
-
- def write(self, x):
- """Write a string into the output stream."""
- if self._new_lines:
- if not self._first_write:
- self.stream.write('\n' * self._new_lines)
- self.code_lineno += self._new_lines
- if self._write_debug_info is not None:
- self.debug_info.append((self._write_debug_info,
- self.code_lineno))
- self._write_debug_info = None
- self._first_write = False
- self.stream.write(' ' * self._indentation)
- self._new_lines = 0
- self.stream.write(x)
-
- def writeline(self, x, node=None, extra=0):
- """Combination of newline and write."""
- self.newline(node, extra)
- self.write(x)
-
- def newline(self, node=None, extra=0):
- """Add one or more newlines before the next write."""
- self._new_lines = max(self._new_lines, 1 + extra)
- if node is not None and node.lineno != self._last_line:
- self._write_debug_info = node.lineno
- self._last_line = node.lineno
-
- def signature(self, node, frame, extra_kwargs=None):
- """Writes a function call to the stream for the current node.
- A leading comma is added automatically. The extra keyword
- arguments may not include python keywords otherwise a syntax
- error could occour. The extra keyword arguments should be given
- as python dict.
- """
- # if any of the given keyword arguments is a python keyword
- # we have to make sure that no invalid call is created.
- kwarg_workaround = False
- for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()):
- if is_python_keyword(kwarg):
- kwarg_workaround = True
- break
-
- for arg in node.args:
- self.write(', ')
- self.visit(arg, frame)
-
- if not kwarg_workaround:
- for kwarg in node.kwargs:
- self.write(', ')
- self.visit(kwarg, frame)
- if extra_kwargs is not None:
- for key, value in extra_kwargs.iteritems():
- self.write(', %s=%s' % (key, value))
- if node.dyn_args:
- self.write(', *')
- self.visit(node.dyn_args, frame)
-
- if kwarg_workaround:
- if node.dyn_kwargs is not None:
- self.write(', **dict({')
- else:
- self.write(', **{')
- for kwarg in node.kwargs:
- self.write('%r: ' % kwarg.key)
- self.visit(kwarg.value, frame)
- self.write(', ')
- if extra_kwargs is not None:
- for key, value in extra_kwargs.iteritems():
- self.write('%r: %s, ' % (key, value))
- if node.dyn_kwargs is not None:
- self.write('}, **')
- self.visit(node.dyn_kwargs, frame)
- self.write(')')
- else:
- self.write('}')
-
- elif node.dyn_kwargs is not None:
- self.write(', **')
- self.visit(node.dyn_kwargs, frame)
-
- def pull_locals(self, frame):
- """Pull all the references identifiers into the local scope."""
- for name in frame.identifiers.undeclared:
- self.writeline('l_%s = context.resolve(%r)' % (name, name))
-
- def pull_dependencies(self, nodes):
- """Pull all the dependencies."""
- visitor = DependencyFinderVisitor()
- for node in nodes:
- visitor.visit(node)
- for dependency in 'filters', 'tests':
- mapping = getattr(self, dependency)
- for name in getattr(visitor, dependency):
- if name not in mapping:
- mapping[name] = self.temporary_identifier()
- self.writeline('%s = environment.%s[%r]' %
- (mapping[name], dependency, name))
-
- def unoptimize_scope(self, frame):
- """Disable Python optimizations for the frame."""
- # XXX: this is not that nice but it has no real overhead. It
- # mainly works because python finds the locals before dead code
- # is removed. If that breaks we have to add a dummy function
- # that just accepts the arguments and does nothing.
- if frame.identifiers.declared:
- self.writeline('%sdummy(%s)' % (
- unoptimize_before_dead_code and 'if 0: ' or '',
- ', '.join('l_' + name for name in frame.identifiers.declared)
- ))
-
- def push_scope(self, frame, extra_vars=()):
- """This function returns all the shadowed variables in a dict
- in the form name: alias and will write the required assignments
- into the current scope. No indentation takes place.
-
- This also predefines locally declared variables from the loop
- body because under some circumstances it may be the case that
-
- `extra_vars` is passed to `Frame.find_shadowed`.
- """
- aliases = {}
- for name in frame.find_shadowed(extra_vars):
- aliases[name] = ident = self.temporary_identifier()
- self.writeline('%s = l_%s' % (ident, name))
- to_declare = set()
- for name in frame.identifiers.declared_locally:
- if name not in aliases:
- to_declare.add('l_' + name)
- if to_declare:
- self.writeline(' = '.join(to_declare) + ' = missing')
- return aliases
-
- def pop_scope(self, aliases, frame):
- """Restore all aliases and delete unused variables."""
- for name, alias in aliases.iteritems():
- self.writeline('l_%s = %s' % (name, alias))
- to_delete = set()
- for name in frame.identifiers.declared_locally:
- if name not in aliases:
- to_delete.add('l_' + name)
- if to_delete:
- # we cannot use the del statement here because enclosed
- # scopes can trigger a SyntaxError:
- # a = 42; b = lambda: a; del a
- self.writeline(' = '.join(to_delete) + ' = missing')
-
- def function_scoping(self, node, frame, children=None,
- find_special=True):
- """In Jinja a few statements require the help of anonymous
- functions. Those are currently macros and call blocks and in
- the future also recursive loops. As there is currently
- technical limitation that doesn't allow reading and writing a
- variable in a scope where the initial value is coming from an
- outer scope, this function tries to fall back with a common
- error message. Additionally the frame passed is modified so
- that the argumetns are collected and callers are looked up.
-
- This will return the modified frame.
- """
- # we have to iterate twice over it, make sure that works
- if children is None:
- children = node.iter_child_nodes()
- children = list(children)
- func_frame = frame.inner()
- func_frame.inspect(children, hard_scope=True)
-
- # variables that are undeclared (accessed before declaration) and
- # declared locally *and* part of an outside scope raise a template
- # assertion error. Reason: we can't generate reasonable code from
- # it without aliasing all the variables.
- # this could be fixed in Python 3 where we have the nonlocal
- # keyword or if we switch to bytecode generation
- overriden_closure_vars = (
- func_frame.identifiers.undeclared &
- func_frame.identifiers.declared &
- (func_frame.identifiers.declared_locally |
- func_frame.identifiers.declared_parameter)
- )
- if overriden_closure_vars:
- self.fail('It\'s not possible to set and access variables '
- 'derived from an outer scope! (affects: %s)' %
- ', '.join(sorted(overriden_closure_vars)), node.lineno)
-
- # remove variables from a closure from the frame's undeclared
- # identifiers.
- func_frame.identifiers.undeclared -= (
- func_frame.identifiers.undeclared &
- func_frame.identifiers.declared
- )
-
- # no special variables for this scope, abort early
- if not find_special:
- return func_frame
-
- func_frame.accesses_kwargs = False
- func_frame.accesses_varargs = False
- func_frame.accesses_caller = False
- func_frame.arguments = args = ['l_' + x.name for x in node.args]
-
- undeclared = find_undeclared(children, ('caller', 'kwargs', 'varargs'))
-
- if 'caller' in undeclared:
- func_frame.accesses_caller = True
- func_frame.identifiers.add_special('caller')
- args.append('l_caller')
- if 'kwargs' in undeclared:
- func_frame.accesses_kwargs = True
- func_frame.identifiers.add_special('kwargs')
- args.append('l_kwargs')
- if 'varargs' in undeclared:
- func_frame.accesses_varargs = True
- func_frame.identifiers.add_special('varargs')
- args.append('l_varargs')
- return func_frame
-
- def macro_body(self, node, frame, children=None):
- """Dump the function def of a macro or call block."""
- frame = self.function_scoping(node, frame, children)
- # macros are delayed, they never require output checks
- frame.require_output_check = False
- args = frame.arguments
- # XXX: this is an ugly fix for the loop nesting bug
- # (tests.test_old_bugs.test_loop_call_bug). This works around
- # a identifier nesting problem we have in general. It's just more
- # likely to happen in loops which is why we work around it. The
- # real solution would be "nonlocal" all the identifiers that are
- # leaking into a new python frame and might be used both unassigned
- # and assigned.
- if 'loop' in frame.identifiers.declared:
- args = args + ['l_loop=l_loop']
- self.writeline('def macro(%s):' % ', '.join(args), node)
- self.indent()
- self.buffer(frame)
- self.pull_locals(frame)
- self.blockvisit(node.body, frame)
- self.return_buffer_contents(frame)
- self.outdent()
- return frame
-
- def macro_def(self, node, frame):
- """Dump the macro definition for the def created by macro_body."""
- arg_tuple = ', '.join(repr(x.name) for x in node.args)
- name = getattr(node, 'name', None)
- if len(node.args) == 1:
- arg_tuple += ','
- self.write('Macro(environment, macro, %r, (%s), (' %
- (name, arg_tuple))
- for arg in node.defaults:
- self.visit(arg, frame)
- self.write(', ')
- self.write('), %r, %r, %r)' % (
- bool(frame.accesses_kwargs),
- bool(frame.accesses_varargs),
- bool(frame.accesses_caller)
- ))
-
- def position(self, node):
- """Return a human readable position for the node."""
- rv = 'line %d' % node.lineno
- if self.name is not None:
- rv += ' in ' + repr(self.name)
- return rv
-
- # -- Statement Visitors
-
- def visit_Template(self, node, frame=None):
- assert frame is None, 'no root frame allowed'
- eval_ctx = EvalContext(self.environment, self.name)
-
- from jinja2.runtime import __all__ as exported
- self.writeline('from __future__ import division')
- self.writeline('from jinja2.runtime import ' + ', '.join(exported))
- if not unoptimize_before_dead_code:
- self.writeline('dummy = lambda *x: None')
-
- # if we want a deferred initialization we cannot move the
- # environment into a local name
- envenv = not self.defer_init and ', environment=environment' or ''
-
- # do we have an extends tag at all? If not, we can save some
- # overhead by just not processing any inheritance code.
- have_extends = node.find(nodes.Extends) is not None
-
- # find all blocks
- for block in node.find_all(nodes.Block):
- if block.name in self.blocks:
- self.fail('block %r defined twice' % block.name, block.lineno)
- self.blocks[block.name] = block
-
- # find all imports and import them
- for import_ in node.find_all(nodes.ImportedName):
- if import_.importname not in self.import_aliases:
- imp = import_.importname
- self.import_aliases[imp] = alias = self.temporary_identifier()
- if '.' in imp:
- module, obj = imp.rsplit('.', 1)
- self.writeline('from %s import %s as %s' %
- (module, obj, alias))
- else:
- self.writeline('import %s as %s' % (imp, alias))
-
- # add the load name
- self.writeline('name = %r' % self.name)
-
- # generate the root render function.
- self.writeline('def root(context%s):' % envenv, extra=1)
-
- # process the root
- frame = Frame(eval_ctx)
- frame.inspect(node.body)
- frame.toplevel = frame.rootlevel = True
- frame.require_output_check = have_extends and not self.has_known_extends
- self.indent()
- if have_extends:
- self.writeline('parent_template = None')
- if 'self' in find_undeclared(node.body, ('self',)):
- frame.identifiers.add_special('self')
- self.writeline('l_self = TemplateReference(context)')
- self.pull_locals(frame)
- self.pull_dependencies(node.body)
- self.blockvisit(node.body, frame)
- self.outdent()
-
- # make sure that the parent root is called.
- if have_extends:
- if not self.has_known_extends:
- self.indent()
- self.writeline('if parent_template is not None:')
- self.indent()
- self.writeline('for event in parent_template.'
- 'root_render_func(context):')
- self.indent()
- self.writeline('yield event')
- self.outdent(2 + (not self.has_known_extends))
-
- # at this point we now have the blocks collected and can visit them too.
- for name, block in self.blocks.iteritems():
- block_frame = Frame(eval_ctx)
- block_frame.inspect(block.body)
- block_frame.block = name
- self.writeline('def block_%s(context%s):' % (name, envenv),
- block, 1)
- self.indent()
- undeclared = find_undeclared(block.body, ('self', 'super'))
- if 'self' in undeclared:
- block_frame.identifiers.add_special('self')
- self.writeline('l_self = TemplateReference(context)')
- if 'super' in undeclared:
- block_frame.identifiers.add_special('super')
- self.writeline('l_super = context.super(%r, '
- 'block_%s)' % (name, name))
- self.pull_locals(block_frame)
- self.pull_dependencies(block.body)
- self.blockvisit(block.body, block_frame)
- self.outdent()
-
- self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
- for x in self.blocks),
- extra=1)
-
- # add a function that returns the debug info
- self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x
- in self.debug_info))
-
- def visit_Block(self, node, frame):
- """Call a block and register it for the template."""
- level = 1
- if frame.toplevel:
- # if we know that we are a child template, there is no need to
- # check if we are one
- if self.has_known_extends:
- return
- if self.extends_so_far > 0:
- self.writeline('if parent_template is None:')
- self.indent()
- level += 1
- context = node.scoped and 'context.derived(locals())' or 'context'
- self.writeline('for event in context.blocks[%r][0](%s):' % (
- node.name, context), node)
- self.indent()
- self.simple_write('event', frame)
- self.outdent(level)
-
- def visit_Extends(self, node, frame):
- """Calls the extender."""
- if not frame.toplevel:
- self.fail('cannot use extend from a non top-level scope',
- node.lineno)
-
- # if the number of extends statements in general is zero so
- # far, we don't have to add a check if something extended
- # the template before this one.
- if self.extends_so_far > 0:
-
- # if we have a known extends we just add a template runtime
- # error into the generated code. We could catch that at compile
- # time too, but i welcome it not to confuse users by throwing the
- # same error at different times just "because we can".
- if not self.has_known_extends:
- self.writeline('if parent_template is not None:')
- self.indent()
- self.writeline('raise TemplateRuntimeError(%r)' %
- 'extended multiple times')
- self.outdent()
-
- # if we have a known extends already we don't need that code here
- # as we know that the template execution will end here.
- if self.has_known_extends:
- raise CompilerExit()
-
- self.writeline('parent_template = environment.get_template(', node)
- self.visit(node.template, frame)
- self.write(', %r)' % self.name)
- self.writeline('for name, parent_block in parent_template.'
- 'blocks.%s():' % dict_item_iter)
- self.indent()
- self.writeline('context.blocks.setdefault(name, []).'
- 'append(parent_block)')
- self.outdent()
-
- # if this extends statement was in the root level we can take
- # advantage of that information and simplify the generated code
- # in the top level from this point onwards
- if frame.rootlevel:
- self.has_known_extends = True
-
- # and now we have one more
- self.extends_so_far += 1
-
- def visit_Include(self, node, frame):
- """Handles includes."""
- if node.with_context:
- self.unoptimize_scope(frame)
- if node.ignore_missing:
- self.writeline('try:')
- self.indent()
-
- func_name = 'get_or_select_template'
- if isinstance(node.template, nodes.Const):
- if isinstance(node.template.value, basestring):
- func_name = 'get_template'
- elif isinstance(node.template.value, (tuple, list)):
- func_name = 'select_template'
- elif isinstance(node.template, (nodes.Tuple, nodes.List)):
- func_name = 'select_template'
-
- self.writeline('template = environment.%s(' % func_name, node)
- self.visit(node.template, frame)
- self.write(', %r)' % self.name)
- if node.ignore_missing:
- self.outdent()
- self.writeline('except TemplateNotFound:')
- self.indent()
- self.writeline('pass')
- self.outdent()
- self.writeline('else:')
- self.indent()
-
- if node.with_context:
- self.writeline('for event in template.root_render_func('
- 'template.new_context(context.parent, True, '
- 'locals())):')
- else:
- self.writeline('for event in template.module._body_stream:')
-
- self.indent()
- self.simple_write('event', frame)
- self.outdent()
-
- if node.ignore_missing:
- self.outdent()
-
- def visit_Import(self, node, frame):
- """Visit regular imports."""
- if node.with_context:
- self.unoptimize_scope(frame)
- self.writeline('l_%s = ' % node.target, node)
- if frame.toplevel:
- self.write('context.vars[%r] = ' % node.target)
- self.write('environment.get_template(')
- self.visit(node.template, frame)
- self.write(', %r).' % self.name)
- if node.with_context:
- self.write('make_module(context.parent, True, locals())')
- else:
- self.write('module')
- if frame.toplevel and not node.target.startswith('_'):
- self.writeline('context.exported_vars.discard(%r)' % node.target)
- frame.assigned_names.add(node.target)
-
- def visit_FromImport(self, node, frame):
- """Visit named imports."""
- self.newline(node)
- self.write('included_template = environment.get_template(')
- self.visit(node.template, frame)
- self.write(', %r).' % self.name)
- if node.with_context:
- self.write('make_module(context.parent, True)')
- else:
- self.write('module')
-
- var_names = []
- discarded_names = []
- for name in node.names:
- if isinstance(name, tuple):
- name, alias = name
- else:
- alias = name
- self.writeline('l_%s = getattr(included_template, '
- '%r, missing)' % (alias, name))
- self.writeline('if l_%s is missing:' % alias)
- self.indent()
- self.writeline('l_%s = environment.undefined(%r %% '
- 'included_template.__name__, '
- 'name=%r)' %
- (alias, 'the template %%r (imported on %s) does '
- 'not export the requested name %s' % (
- self.position(node),
- repr(name)
- ), name))
- self.outdent()
- if frame.toplevel:
- var_names.append(alias)
- if not alias.startswith('_'):
- discarded_names.append(alias)
- frame.assigned_names.add(alias)
-
- if var_names:
- if len(var_names) == 1:
- name = var_names[0]
- self.writeline('context.vars[%r] = l_%s' % (name, name))
- else:
- self.writeline('context.vars.update({%s})' % ', '.join(
- '%r: l_%s' % (name, name) for name in var_names
- ))
- if discarded_names:
- if len(discarded_names) == 1:
- self.writeline('context.exported_vars.discard(%r)' %
- discarded_names[0])
- else:
- self.writeline('context.exported_vars.difference_'
- 'update((%s))' % ', '.join(map(repr, discarded_names)))
-
- def visit_For(self, node, frame):
- # when calculating the nodes for the inner frame we have to exclude
- # the iterator contents from it
- children = node.iter_child_nodes(exclude=('iter',))
- if node.recursive:
- loop_frame = self.function_scoping(node, frame, children,
- find_special=False)
- else:
- loop_frame = frame.inner()
- loop_frame.inspect(children)
-
- # try to figure out if we have an extended loop. An extended loop
- # is necessary if the loop is in recursive mode if the special loop
- # variable is accessed in the body.
- extended_loop = node.recursive or 'loop' in \
- find_undeclared(node.iter_child_nodes(
- only=('body',)), ('loop',))
-
- # if we don't have an recursive loop we have to find the shadowed
- # variables at that point. Because loops can be nested but the loop
- # variable is a special one we have to enforce aliasing for it.
- if not node.recursive:
- aliases = self.push_scope(loop_frame, ('loop',))
-
- # otherwise we set up a buffer and add a function def
- else:
- self.writeline('def loop(reciter, loop_render_func):', node)
- self.indent()
- self.buffer(loop_frame)
- aliases = {}
-
- # make sure the loop variable is a special one and raise a template
- # assertion error if a loop tries to write to loop
- if extended_loop:
- loop_frame.identifiers.add_special('loop')
- for name in node.find_all(nodes.Name):
- if name.ctx == 'store' and name.name == 'loop':
- self.fail('Can\'t assign to special loop variable '
- 'in for-loop target', name.lineno)
-
- self.pull_locals(loop_frame)
- if node.else_:
- iteration_indicator = self.temporary_identifier()
- self.writeline('%s = 1' % iteration_indicator)
-
- # Create a fake parent loop if the else or test section of a
- # loop is accessing the special loop variable and no parent loop
- # exists.
- if 'loop' not in aliases and 'loop' in find_undeclared(
- node.iter_child_nodes(only=('else_', 'test')), ('loop',)):
- self.writeline("l_loop = environment.undefined(%r, name='loop')" %
- ("'loop' is undefined. the filter section of a loop as well "
- "as the else block doesn't have access to the special 'loop'"
- " variable of the current loop. Because there is no parent "
- "loop it's undefined. Happened in loop on %s" %
- self.position(node)))
-
- self.writeline('for ', node)
- self.visit(node.target, loop_frame)
- self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
-
- # if we have an extened loop and a node test, we filter in the
- # "outer frame".
- if extended_loop and node.test is not None:
- self.write('(')
- self.visit(node.target, loop_frame)
- self.write(' for ')
- self.visit(node.target, loop_frame)
- self.write(' in ')
- if node.recursive:
- self.write('reciter')
- else:
- self.visit(node.iter, loop_frame)
- self.write(' if (')
- test_frame = loop_frame.copy()
- self.visit(node.test, test_frame)
- self.write('))')
-
- elif node.recursive:
- self.write('reciter')
- else:
- self.visit(node.iter, loop_frame)
-
- if node.recursive:
- self.write(', recurse=loop_render_func):')
- else:
- self.write(extended_loop and '):' or ':')
-
- # tests in not extended loops become a continue
- if not extended_loop and node.test is not None:
- self.indent()
- self.writeline('if not ')
- self.visit(node.test, loop_frame)
- self.write(':')
- self.indent()
- self.writeline('continue')
- self.outdent(2)
-
- self.indent()
- self.blockvisit(node.body, loop_frame)
- if node.else_:
- self.writeline('%s = 0' % iteration_indicator)
- self.outdent()
-
- if node.else_:
- self.writeline('if %s:' % iteration_indicator)
- self.indent()
- self.blockvisit(node.else_, loop_frame)
- self.outdent()
-
- # reset the aliases if there are any.
- if not node.recursive:
- self.pop_scope(aliases, loop_frame)
-
- # if the node was recursive we have to return the buffer contents
- # and start the iteration code
- if node.recursive:
- self.return_buffer_contents(loop_frame)
- self.outdent()
- self.start_write(frame, node)
- self.write('loop(')
- self.visit(node.iter, frame)
- self.write(', loop)')
- self.end_write(frame)
-
- def visit_If(self, node, frame):
- if_frame = frame.soft()
- self.writeline('if ', node)
- self.visit(node.test, if_frame)
- self.write(':')
- self.indent()
- self.blockvisit(node.body, if_frame)
- self.outdent()
- if node.else_:
- self.writeline('else:')
- self.indent()
- self.blockvisit(node.else_, if_frame)
- self.outdent()
-
- def visit_Macro(self, node, frame):
- macro_frame = self.macro_body(node, frame)
- self.newline()
- if frame.toplevel:
- if not node.name.startswith('_'):
- self.write('context.exported_vars.add(%r)' % node.name)
- self.writeline('context.vars[%r] = ' % node.name)
- self.write('l_%s = ' % node.name)
- self.macro_def(node, macro_frame)
- frame.assigned_names.add(node.name)
-
- def visit_CallBlock(self, node, frame):
- children = node.iter_child_nodes(exclude=('call',))
- call_frame = self.macro_body(node, frame, children)
- self.writeline('caller = ')
- self.macro_def(node, call_frame)
- self.start_write(frame, node)
- self.visit_Call(node.call, call_frame, forward_caller=True)
- self.end_write(frame)
-
- def visit_FilterBlock(self, node, frame):
- filter_frame = frame.inner()
- filter_frame.inspect(node.iter_child_nodes())
- aliases = self.push_scope(filter_frame)
- self.pull_locals(filter_frame)
- self.buffer(filter_frame)
- self.blockvisit(node.body, filter_frame)
- self.start_write(frame, node)
- self.visit_Filter(node.filter, filter_frame)
- self.end_write(frame)
- self.pop_scope(aliases, filter_frame)
-
- def visit_ExprStmt(self, node, frame):
- self.newline(node)
- self.visit(node.node, frame)
-
- def visit_Output(self, node, frame):
- # if we have a known extends statement, we don't output anything
- # if we are in a require_output_check section
- if self.has_known_extends and frame.require_output_check:
- return
-
- if self.environment.finalize:
- finalize = lambda x: unicode(self.environment.finalize(x))
- else:
- finalize = unicode
-
- # if we are inside a frame that requires output checking, we do so
- outdent_later = False
- if frame.require_output_check:
- self.writeline('if parent_template is None:')
- self.indent()
- outdent_later = True
-
- # try to evaluate as many chunks as possible into a static
- # string at compile time.
- body = []
- for child in node.nodes:
- try:
- const = child.as_const(frame.eval_ctx)
- except nodes.Impossible:
- body.append(child)
- continue
- # the frame can't be volatile here, becaus otherwise the
- # as_const() function would raise an Impossible exception
- # at that point.
- try:
- if frame.eval_ctx.autoescape:
- if hasattr(const, '__html__'):
- const = const.__html__()
- else:
- const = escape(const)
- const = finalize(const)
- except:
- # if something goes wrong here we evaluate the node
- # at runtime for easier debugging
- body.append(child)
- continue
- if body and isinstance(body[-1], list):
- body[-1].append(const)
- else:
- body.append([const])
-
- # if we have less than 3 nodes or a buffer we yield or extend/append
- if len(body) < 3 or frame.buffer is not None:
- if frame.buffer is not None:
- # for one item we append, for more we extend
- if len(body) == 1:
- self.writeline('%s.append(' % frame.buffer)
- else:
- self.writeline('%s.extend((' % frame.buffer)
- self.indent()
- for item in body:
- if isinstance(item, list):
- val = repr(concat(item))
- if frame.buffer is None:
- self.writeline('yield ' + val)
- else:
- self.writeline(val + ', ')
- else:
- if frame.buffer is None:
- self.writeline('yield ', item)
- else:
- self.newline(item)
- close = 1
- if frame.eval_ctx.volatile:
- self.write('(context.eval_ctx.autoescape and'
- ' escape or to_string)(')
- elif frame.eval_ctx.autoescape:
- self.write('escape(')
- else:
- self.write('to_string(')
- if self.environment.finalize is not None:
- self.write('environment.finalize(')
- close += 1
- self.visit(item, frame)
- self.write(')' * close)
- if frame.buffer is not None:
- self.write(', ')
- if frame.buffer is not None:
- # close the open parentheses
- self.outdent()
- self.writeline(len(body) == 1 and ')' or '))')
-
- # otherwise we create a format string as this is faster in that case
- else:
- format = []
- arguments = []
- for item in body:
- if isinstance(item, list):
- format.append(concat(item).replace('%', '%%'))
- else:
- format.append('%s')
- arguments.append(item)
- self.writeline('yield ')
- self.write(repr(concat(format)) + ' % (')
- idx = -1
- self.indent()
- for argument in arguments:
- self.newline(argument)
- close = 0
- if frame.eval_ctx.volatile:
- self.write('(context.eval_ctx.autoescape and'
- ' escape or to_string)(')
- close += 1
- elif frame.eval_ctx.autoescape:
- self.write('escape(')
- close += 1
- if self.environment.finalize is not None:
- self.write('environment.finalize(')
- close += 1
- self.visit(argument, frame)
- self.write(')' * close + ', ')
- self.outdent()
- self.writeline(')')
-
- if outdent_later:
- self.outdent()
-
- def visit_Assign(self, node, frame):
- self.newline(node)
- # toplevel assignments however go into the local namespace and
- # the current template's context. We create a copy of the frame
- # here and add a set so that the Name visitor can add the assigned
- # names here.
- if frame.toplevel:
- assignment_frame = frame.copy()
- assignment_frame.toplevel_assignments = set()
- else:
- assignment_frame = frame
- self.visit(node.target, assignment_frame)
- self.write(' = ')
- self.visit(node.node, frame)
-
- # make sure toplevel assignments are added to the context.
- if frame.toplevel:
- public_names = [x for x in assignment_frame.toplevel_assignments
- if not x.startswith('_')]
- if len(assignment_frame.toplevel_assignments) == 1:
- name = next(iter(assignment_frame.toplevel_assignments))
- self.writeline('context.vars[%r] = l_%s' % (name, name))
- else:
- self.writeline('context.vars.update({')
- for idx, name in enumerate(assignment_frame.toplevel_assignments):
- if idx:
- self.write(', ')
- self.write('%r: l_%s' % (name, name))
- self.write('})')
- if public_names:
- if len(public_names) == 1:
- self.writeline('context.exported_vars.add(%r)' %
- public_names[0])
- else:
- self.writeline('context.exported_vars.update((%s))' %
- ', '.join(map(repr, public_names)))
-
- # -- Expression Visitors
-
- def visit_Name(self, node, frame):
- if node.ctx == 'store' and frame.toplevel:
- frame.toplevel_assignments.add(node.name)
- self.write('l_' + node.name)
- frame.assigned_names.add(node.name)
-
- def visit_Const(self, node, frame):
- val = node.value
- if isinstance(val, float):
- self.write(str(val))
- else:
- self.write(repr(val))
-
- def visit_TemplateData(self, node, frame):
- try:
- self.write(repr(node.as_const(frame.eval_ctx)))
- except nodes.Impossible:
- self.write('(context.eval_ctx.autoescape and Markup or identity)(%r)'
- % node.data)
-
- def visit_Tuple(self, node, frame):
- self.write('(')
- idx = -1
- for idx, item in enumerate(node.items):
- if idx:
- self.write(', ')
- self.visit(item, frame)
- self.write(idx == 0 and ',)' or ')')
-
- def visit_List(self, node, frame):
- self.write('[')
- for idx, item in enumerate(node.items):
- if idx:
- self.write(', ')
- self.visit(item, frame)
- self.write(']')
-
- def visit_Dict(self, node, frame):
- self.write('{')
- for idx, item in enumerate(node.items):
- if idx:
- self.write(', ')
- self.visit(item.key, frame)
- self.write(': ')
- self.visit(item.value, frame)
- self.write('}')
-
- def binop(operator):
- def visitor(self, node, frame):
- self.write('(')
- self.visit(node.left, frame)
- self.write(' %s ' % operator)
- self.visit(node.right, frame)
- self.write(')')
- return visitor
-
- def uaop(operator):
- def visitor(self, node, frame):
- self.write('(' + operator)
- self.visit(node.node, frame)
- self.write(')')
- return visitor
-
- visit_Add = binop('+')
- visit_Sub = binop('-')
- visit_Mul = binop('*')
- visit_Div = binop('/')
- visit_FloorDiv = binop('//')
- visit_Pow = binop('**')
- visit_Mod = binop('%')
- visit_And = binop('and')
- visit_Or = binop('or')
- visit_Pos = uaop('+')
- visit_Neg = uaop('-')
- visit_Not = uaop('not ')
- del binop, uaop
-
- def visit_Concat(self, node, frame):
- if frame.eval_ctx.volatile:
- func_name = '(context.eval_ctx.volatile and' \
- ' markup_join or unicode_join)'
- elif frame.eval_ctx.autoescape:
- func_name = 'markup_join'
- else:
- func_name = 'unicode_join'
- self.write('%s((' % func_name)
- for arg in node.nodes:
- self.visit(arg, frame)
- self.write(', ')
- self.write('))')
-
- def visit_Compare(self, node, frame):
- self.visit(node.expr, frame)
- for op in node.ops:
- self.visit(op, frame)
-
- def visit_Operand(self, node, frame):
- self.write(' %s ' % operators[node.op])
- self.visit(node.expr, frame)
-
- def visit_Getattr(self, node, frame):
- self.write('environment.getattr(')
- self.visit(node.node, frame)
- self.write(', %r)' % node.attr)
-
- def visit_Getitem(self, node, frame):
- # slices bypass the environment getitem method.
- if isinstance(node.arg, nodes.Slice):
- self.visit(node.node, frame)
- self.write('[')
- self.visit(node.arg, frame)
- self.write(']')
- else:
- self.write('environment.getitem(')
- self.visit(node.node, frame)
- self.write(', ')
- self.visit(node.arg, frame)
- self.write(')')
-
- def visit_Slice(self, node, frame):
- if node.start is not None:
- self.visit(node.start, frame)
- self.write(':')
- if node.stop is not None:
- self.visit(node.stop, frame)
- if node.step is not None:
- self.write(':')
- self.visit(node.step, frame)
-
- def visit_Filter(self, node, frame):
- self.write(self.filters[node.name] + '(')
- func = self.environment.filters.get(node.name)
- if func is None:
- self.fail('no filter named %r' % node.name, node.lineno)
- if getattr(func, 'contextfilter', False):
- self.write('context, ')
- elif getattr(func, 'evalcontextfilter', False):
- self.write('context.eval_ctx, ')
- elif getattr(func, 'environmentfilter', False):
- self.write('environment, ')
-
- # if the filter node is None we are inside a filter block
- # and want to write to the current buffer
- if node.node is not None:
- self.visit(node.node, frame)
- elif frame.eval_ctx.volatile:
- self.write('(context.eval_ctx.autoescape and'
- ' Markup(concat(%s)) or concat(%s))' %
- (frame.buffer, frame.buffer))
- elif frame.eval_ctx.autoescape:
- self.write('Markup(concat(%s))' % frame.buffer)
- else:
- self.write('concat(%s)' % frame.buffer)
- self.signature(node, frame)
- self.write(')')
-
- def visit_Test(self, node, frame):
- self.write(self.tests[node.name] + '(')
- if node.name not in self.environment.tests:
- self.fail('no test named %r' % node.name, node.lineno)
- self.visit(node.node, frame)
- self.signature(node, frame)
- self.write(')')
-
- def visit_CondExpr(self, node, frame):
- def write_expr2():
- if node.expr2 is not None:
- return self.visit(node.expr2, frame)
- self.write('environment.undefined(%r)' % ('the inline if-'
- 'expression on %s evaluated to false and '
- 'no else section was defined.' % self.position(node)))
-
- if not have_condexpr:
- self.write('((')
- self.visit(node.test, frame)
- self.write(') and (')
- self.visit(node.expr1, frame)
- self.write(',) or (')
- write_expr2()
- self.write(',))[0]')
- else:
- self.write('(')
- self.visit(node.expr1, frame)
- self.write(' if ')
- self.visit(node.test, frame)
- self.write(' else ')
- write_expr2()
- self.write(')')
-
- def visit_Call(self, node, frame, forward_caller=False):
- if self.environment.sandboxed:
- self.write('environment.call(context, ')
- else:
- self.write('context.call(')
- self.visit(node.node, frame)
- extra_kwargs = forward_caller and {'caller': 'caller'} or None
- self.signature(node, frame, extra_kwargs)
- self.write(')')
-
- def visit_Keyword(self, node, frame):
- self.write(node.key + '=')
- self.visit(node.value, frame)
-
- # -- Unused nodes for extensions
-
- def visit_MarkSafe(self, node, frame):
- self.write('Markup(')
- self.visit(node.expr, frame)
- self.write(')')
-
- def visit_MarkSafeIfAutoescape(self, node, frame):
- self.write('(context.eval_ctx.autoescape and Markup or identity)(')
- self.visit(node.expr, frame)
- self.write(')')
-
- def visit_EnvironmentAttribute(self, node, frame):
- self.write('environment.' + node.name)
-
- def visit_ExtensionAttribute(self, node, frame):
- self.write('environment.extensions[%r].%s' % (node.identifier, node.name))
-
- def visit_ImportedName(self, node, frame):
- self.write(self.import_aliases[node.importname])
-
- def visit_InternalName(self, node, frame):
- self.write(node.name)
-
- def visit_ContextReference(self, node, frame):
- self.write('context')
-
- def visit_Continue(self, node, frame):
- self.writeline('continue', node)
-
- def visit_Break(self, node, frame):
- self.writeline('break', node)
-
- def visit_Scope(self, node, frame):
- scope_frame = frame.inner()
- scope_frame.inspect(node.iter_child_nodes())
- aliases = self.push_scope(scope_frame)
- self.pull_locals(scope_frame)
- self.blockvisit(node.body, scope_frame)
- self.pop_scope(aliases, scope_frame)
-
- def visit_EvalContextModifier(self, node, frame):
- for keyword in node.options:
- self.writeline('context.eval_ctx.%s = ' % keyword.key)
- self.visit(keyword.value, frame)
- try:
- val = keyword.value.as_const(frame.eval_ctx)
- except nodes.Impossible:
- frame.eval_ctx.volatile = True
- else:
- setattr(frame.eval_ctx, keyword.key, val)
-
- def visit_ScopedEvalContextModifier(self, node, frame):
- old_ctx_name = self.temporary_identifier()
- safed_ctx = frame.eval_ctx.save()
- self.writeline('%s = context.eval_ctx.save()' % old_ctx_name)
- self.visit_EvalContextModifier(node, frame)
- for child in node.body:
- self.visit(child, frame)
- frame.eval_ctx.revert(safed_ctx)
- self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name)
diff --git a/module/lib/jinja2/constants.py b/module/lib/jinja2/constants.py
deleted file mode 100644
index cab203cc7..000000000
--- a/module/lib/jinja2/constants.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja.constants
- ~~~~~~~~~~~~~~~
-
- Various constants.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-
-
-#: list of lorem ipsum words used by the lipsum() helper function
-LOREM_IPSUM_WORDS = u'''\
-a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at
-auctor augue bibendum blandit class commodo condimentum congue consectetuer
-consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus
-diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend
-elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames
-faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac
-hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum
-justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem
-luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie
-mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non
-nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque
-penatibus per pharetra phasellus placerat platea porta porttitor posuere
-potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus
-ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit
-sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor
-tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices
-ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus
-viverra volutpat vulputate'''
diff --git a/module/lib/jinja2/debug.py b/module/lib/jinja2/debug.py
deleted file mode 100644
index eb15456d1..000000000
--- a/module/lib/jinja2/debug.py
+++ /dev/null
@@ -1,308 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.debug
- ~~~~~~~~~~~~
-
- Implements the debug interface for Jinja. This module does some pretty
- ugly stuff with the Python traceback system in order to achieve tracebacks
- with correct line numbers, locals and contents.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import sys
-import traceback
-from jinja2.utils import CodeType, missing, internal_code
-from jinja2.exceptions import TemplateSyntaxError
-
-
-# how does the raise helper look like?
-try:
- exec "raise TypeError, 'foo'"
-except SyntaxError:
- raise_helper = 'raise __jinja_exception__[1]'
-except TypeError:
- raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]'
-
-
-class TracebackFrameProxy(object):
- """Proxies a traceback frame."""
-
- def __init__(self, tb):
- self.tb = tb
-
- def _set_tb_next(self, next):
- if tb_set_next is not None:
- tb_set_next(self.tb, next and next.tb or None)
- self._tb_next = next
-
- def _get_tb_next(self):
- return self._tb_next
-
- tb_next = property(_get_tb_next, _set_tb_next)
- del _get_tb_next, _set_tb_next
-
- @property
- def is_jinja_frame(self):
- return '__jinja_template__' in self.tb.tb_frame.f_globals
-
- def __getattr__(self, name):
- return getattr(self.tb, name)
-
-
-class ProcessedTraceback(object):
- """Holds a Jinja preprocessed traceback for priting or reraising."""
-
- def __init__(self, exc_type, exc_value, frames):
- assert frames, 'no frames for this traceback?'
- self.exc_type = exc_type
- self.exc_value = exc_value
- self.frames = frames
-
- def chain_frames(self):
- """Chains the frames. Requires ctypes or the debugsupport extension."""
- prev_tb = None
- for tb in self.frames:
- if prev_tb is not None:
- prev_tb.tb_next = tb
- prev_tb = tb
- prev_tb.tb_next = None
-
- def render_as_text(self, limit=None):
- """Return a string with the traceback."""
- lines = traceback.format_exception(self.exc_type, self.exc_value,
- self.frames[0], limit=limit)
- return ''.join(lines).rstrip()
-
- def render_as_html(self, full=False):
- """Return a unicode string with the traceback as rendered HTML."""
- from jinja2.debugrenderer import render_traceback
- return u'%s\n\n<!--\n%s\n-->' % (
- render_traceback(self, full=full),
- self.render_as_text().decode('utf-8', 'replace')
- )
-
- @property
- def is_template_syntax_error(self):
- """`True` if this is a template syntax error."""
- return isinstance(self.exc_value, TemplateSyntaxError)
-
- @property
- def exc_info(self):
- """Exception info tuple with a proxy around the frame objects."""
- return self.exc_type, self.exc_value, self.frames[0]
-
- @property
- def standard_exc_info(self):
- """Standard python exc_info for re-raising"""
- return self.exc_type, self.exc_value, self.frames[0].tb
-
-
-def make_traceback(exc_info, source_hint=None):
- """Creates a processed traceback object from the exc_info."""
- exc_type, exc_value, tb = exc_info
- if isinstance(exc_value, TemplateSyntaxError):
- exc_info = translate_syntax_error(exc_value, source_hint)
- initial_skip = 0
- else:
- initial_skip = 1
- return translate_exception(exc_info, initial_skip)
-
-
-def translate_syntax_error(error, source=None):
- """Rewrites a syntax error to please traceback systems."""
- error.source = source
- error.translated = True
- exc_info = (error.__class__, error, None)
- filename = error.filename
- if filename is None:
- filename = '<unknown>'
- return fake_exc_info(exc_info, filename, error.lineno)
-
-
-def translate_exception(exc_info, initial_skip=0):
- """If passed an exc_info it will automatically rewrite the exceptions
- all the way down to the correct line numbers and frames.
- """
- tb = exc_info[2]
- frames = []
-
- # skip some internal frames if wanted
- for x in xrange(initial_skip):
- if tb is not None:
- tb = tb.tb_next
- initial_tb = tb
-
- while tb is not None:
- # skip frames decorated with @internalcode. These are internal
- # calls we can't avoid and that are useless in template debugging
- # output.
- if tb.tb_frame.f_code in internal_code:
- tb = tb.tb_next
- continue
-
- # save a reference to the next frame if we override the current
- # one with a faked one.
- next = tb.tb_next
-
- # fake template exceptions
- template = tb.tb_frame.f_globals.get('__jinja_template__')
- if template is not None:
- lineno = template.get_corresponding_lineno(tb.tb_lineno)
- tb = fake_exc_info(exc_info[:2] + (tb,), template.filename,
- lineno)[2]
-
- frames.append(TracebackFrameProxy(tb))
- tb = next
-
- # if we don't have any exceptions in the frames left, we have to
- # reraise it unchanged.
- # XXX: can we backup here? when could this happen?
- if not frames:
- raise exc_info[0], exc_info[1], exc_info[2]
-
- traceback = ProcessedTraceback(exc_info[0], exc_info[1], frames)
- if tb_set_next is not None:
- traceback.chain_frames()
- return traceback
-
-
-def fake_exc_info(exc_info, filename, lineno):
- """Helper for `translate_exception`."""
- exc_type, exc_value, tb = exc_info
-
- # figure the real context out
- if tb is not None:
- real_locals = tb.tb_frame.f_locals.copy()
- ctx = real_locals.get('context')
- if ctx:
- locals = ctx.get_all()
- else:
- locals = {}
- for name, value in real_locals.iteritems():
- if name.startswith('l_') and value is not missing:
- locals[name[2:]] = value
-
- # if there is a local called __jinja_exception__, we get
- # rid of it to not break the debug functionality.
- locals.pop('__jinja_exception__', None)
- else:
- locals = {}
-
- # assamble fake globals we need
- globals = {
- '__name__': filename,
- '__file__': filename,
- '__jinja_exception__': exc_info[:2],
-
- # we don't want to keep the reference to the template around
- # to not cause circular dependencies, but we mark it as Jinja
- # frame for the ProcessedTraceback
- '__jinja_template__': None
- }
-
- # and fake the exception
- code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec')
-
- # if it's possible, change the name of the code. This won't work
- # on some python environments such as google appengine
- try:
- if tb is None:
- location = 'template'
- else:
- function = tb.tb_frame.f_code.co_name
- if function == 'root':
- location = 'top-level template code'
- elif function.startswith('block_'):
- location = 'block "%s"' % function[6:]
- else:
- location = 'template'
- code = CodeType(0, code.co_nlocals, code.co_stacksize,
- code.co_flags, code.co_code, code.co_consts,
- code.co_names, code.co_varnames, filename,
- location, code.co_firstlineno,
- code.co_lnotab, (), ())
- except:
- pass
-
- # execute the code and catch the new traceback
- try:
- exec code in globals, locals
- except:
- exc_info = sys.exc_info()
- new_tb = exc_info[2].tb_next
-
- # return without this frame
- return exc_info[:2] + (new_tb,)
-
-
-def _init_ugly_crap():
- """This function implements a few ugly things so that we can patch the
- traceback objects. The function returned allows resetting `tb_next` on
- any python traceback object.
- """
- import ctypes
- from types import TracebackType
-
- # figure out side of _Py_ssize_t
- if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'):
- _Py_ssize_t = ctypes.c_int64
- else:
- _Py_ssize_t = ctypes.c_int
-
- # regular python
- class _PyObject(ctypes.Structure):
- pass
- _PyObject._fields_ = [
- ('ob_refcnt', _Py_ssize_t),
- ('ob_type', ctypes.POINTER(_PyObject))
- ]
-
- # python with trace
- if hasattr(sys, 'getobjects'):
- class _PyObject(ctypes.Structure):
- pass
- _PyObject._fields_ = [
- ('_ob_next', ctypes.POINTER(_PyObject)),
- ('_ob_prev', ctypes.POINTER(_PyObject)),
- ('ob_refcnt', _Py_ssize_t),
- ('ob_type', ctypes.POINTER(_PyObject))
- ]
-
- class _Traceback(_PyObject):
- pass
- _Traceback._fields_ = [
- ('tb_next', ctypes.POINTER(_Traceback)),
- ('tb_frame', ctypes.POINTER(_PyObject)),
- ('tb_lasti', ctypes.c_int),
- ('tb_lineno', ctypes.c_int)
- ]
-
- def tb_set_next(tb, next):
- """Set the tb_next attribute of a traceback object."""
- if not (isinstance(tb, TracebackType) and
- (next is None or isinstance(next, TracebackType))):
- raise TypeError('tb_set_next arguments must be traceback objects')
- obj = _Traceback.from_address(id(tb))
- if tb.tb_next is not None:
- old = _Traceback.from_address(id(tb.tb_next))
- old.ob_refcnt -= 1
- if next is None:
- obj.tb_next = ctypes.POINTER(_Traceback)()
- else:
- next = _Traceback.from_address(id(next))
- next.ob_refcnt += 1
- obj.tb_next = ctypes.pointer(next)
-
- return tb_set_next
-
-
-# try to get a tb_set_next implementation
-try:
- from jinja2._debugsupport import tb_set_next
-except ImportError:
- try:
- tb_set_next = _init_ugly_crap()
- except:
- tb_set_next = None
-del _init_ugly_crap
diff --git a/module/lib/jinja2/defaults.py b/module/lib/jinja2/defaults.py
deleted file mode 100644
index d2d45443a..000000000
--- a/module/lib/jinja2/defaults.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.defaults
- ~~~~~~~~~~~~~~~
-
- Jinja default filters and tags.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner
-
-
-# defaults for the parser / lexer
-BLOCK_START_STRING = '{%'
-BLOCK_END_STRING = '%}'
-VARIABLE_START_STRING = '{{'
-VARIABLE_END_STRING = '}}'
-COMMENT_START_STRING = '{#'
-COMMENT_END_STRING = '#}'
-LINE_STATEMENT_PREFIX = None
-LINE_COMMENT_PREFIX = None
-TRIM_BLOCKS = False
-NEWLINE_SEQUENCE = '\n'
-
-
-# default filters, tests and namespace
-from jinja2.filters import FILTERS as DEFAULT_FILTERS
-from jinja2.tests import TESTS as DEFAULT_TESTS
-DEFAULT_NAMESPACE = {
- 'range': xrange,
- 'dict': lambda **kw: kw,
- 'lipsum': generate_lorem_ipsum,
- 'cycler': Cycler,
- 'joiner': Joiner
-}
-
-
-# export all constants
-__all__ = tuple(x for x in locals().keys() if x.isupper())
diff --git a/module/lib/jinja2/environment.py b/module/lib/jinja2/environment.py
deleted file mode 100644
index ac74a5c68..000000000
--- a/module/lib/jinja2/environment.py
+++ /dev/null
@@ -1,1118 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.environment
- ~~~~~~~~~~~~~~~~~~
-
- Provides a class that holds runtime and parsing time options.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import os
-import sys
-from jinja2 import nodes
-from jinja2.defaults import *
-from jinja2.lexer import get_lexer, TokenStream
-from jinja2.parser import Parser
-from jinja2.optimizer import optimize
-from jinja2.compiler import generate
-from jinja2.runtime import Undefined, new_context
-from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \
- TemplatesNotFound
-from jinja2.utils import import_string, LRUCache, Markup, missing, \
- concat, consume, internalcode, _encode_filename
-
-
-# for direct template usage we have up to ten living environments
-_spontaneous_environments = LRUCache(10)
-
-# the function to create jinja traceback objects. This is dynamically
-# imported on the first exception in the exception handler.
-_make_traceback = None
-
-
-def get_spontaneous_environment(*args):
- """Return a new spontaneous environment. A spontaneous environment is an
- unnamed and unaccessible (in theory) environment that is used for
- templates generated from a string and not from the file system.
- """
- try:
- env = _spontaneous_environments.get(args)
- except TypeError:
- return Environment(*args)
- if env is not None:
- return env
- _spontaneous_environments[args] = env = Environment(*args)
- env.shared = True
- return env
-
-
-def create_cache(size):
- """Return the cache class for the given size."""
- if size == 0:
- return None
- if size < 0:
- return {}
- return LRUCache(size)
-
-
-def copy_cache(cache):
- """Create an empty copy of the given cache."""
- if cache is None:
- return None
- elif type(cache) is dict:
- return {}
- return LRUCache(cache.capacity)
-
-
-def load_extensions(environment, extensions):
- """Load the extensions from the list and bind it to the environment.
- Returns a dict of instanciated environments.
- """
- result = {}
- for extension in extensions:
- if isinstance(extension, basestring):
- extension = import_string(extension)
- result[extension.identifier] = extension(environment)
- return result
-
-
-def _environment_sanity_check(environment):
- """Perform a sanity check on the environment."""
- assert issubclass(environment.undefined, Undefined), 'undefined must ' \
- 'be a subclass of undefined because filters depend on it.'
- assert environment.block_start_string != \
- environment.variable_start_string != \
- environment.comment_start_string, 'block, variable and comment ' \
- 'start strings must be different'
- assert environment.newline_sequence in ('\r', '\r\n', '\n'), \
- 'newline_sequence set to unknown line ending string.'
- return environment
-
-
-class Environment(object):
- r"""The core component of Jinja is the `Environment`. It contains
- important shared variables like configuration, filters, tests,
- globals and others. Instances of this class may be modified if
- they are not shared and if no template was loaded so far.
- Modifications on environments after the first template was loaded
- will lead to surprising effects and undefined behavior.
-
- Here the possible initialization parameters:
-
- `block_start_string`
- The string marking the begin of a block. Defaults to ``'{%'``.
-
- `block_end_string`
- The string marking the end of a block. Defaults to ``'%}'``.
-
- `variable_start_string`
- The string marking the begin of a print statement.
- Defaults to ``'{{'``.
-
- `variable_end_string`
- The string marking the end of a print statement. Defaults to
- ``'}}'``.
-
- `comment_start_string`
- The string marking the begin of a comment. Defaults to ``'{#'``.
-
- `comment_end_string`
- The string marking the end of a comment. Defaults to ``'#}'``.
-
- `line_statement_prefix`
- If given and a string, this will be used as prefix for line based
- statements. See also :ref:`line-statements`.
-
- `line_comment_prefix`
- If given and a string, this will be used as prefix for line based
- based comments. See also :ref:`line-statements`.
-
- .. versionadded:: 2.2
-
- `trim_blocks`
- If this is set to ``True`` the first newline after a block is
- removed (block, not variable tag!). Defaults to `False`.
-
- `newline_sequence`
- The sequence that starts a newline. Must be one of ``'\r'``,
- ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a
- useful default for Linux and OS X systems as well as web
- applications.
-
- `extensions`
- List of Jinja extensions to use. This can either be import paths
- as strings or extension classes. For more information have a
- look at :ref:`the extensions documentation <jinja-extensions>`.
-
- `optimized`
- should the optimizer be enabled? Default is `True`.
-
- `undefined`
- :class:`Undefined` or a subclass of it that is used to represent
- undefined values in the template.
-
- `finalize`
- A callable that can be used to process the result of a variable
- expression before it is output. For example one can convert
- `None` implicitly into an empty string here.
-
- `autoescape`
- If set to true the XML/HTML autoescaping feature is enabled by
- default. For more details about auto escaping see
- :class:`~jinja2.utils.Markup`. As of Jinja 2.4 this can also
- be a callable that is passed the template name and has to
- return `True` or `False` depending on autoescape should be
- enabled by default.
-
- .. versionchanged:: 2.4
- `autoescape` can now be a function
-
- `loader`
- The template loader for this environment.
-
- `cache_size`
- The size of the cache. Per default this is ``50`` which means
- that if more than 50 templates are loaded the loader will clean
- out the least recently used template. If the cache size is set to
- ``0`` templates are recompiled all the time, if the cache size is
- ``-1`` the cache will not be cleaned.
-
- `auto_reload`
- Some loaders load templates from locations where the template
- sources may change (ie: file system or database). If
- `auto_reload` is set to `True` (default) every time a template is
- requested the loader checks if the source changed and if yes, it
- will reload the template. For higher performance it's possible to
- disable that.
-
- `bytecode_cache`
- If set to a bytecode cache object, this object will provide a
- cache for the internal Jinja bytecode so that templates don't
- have to be parsed if they were not changed.
-
- See :ref:`bytecode-cache` for more information.
- """
-
- #: if this environment is sandboxed. Modifying this variable won't make
- #: the environment sandboxed though. For a real sandboxed environment
- #: have a look at jinja2.sandbox
- sandboxed = False
-
- #: True if the environment is just an overlay
- overlayed = False
-
- #: the environment this environment is linked to if it is an overlay
- linked_to = None
-
- #: shared environments have this set to `True`. A shared environment
- #: must not be modified
- shared = False
-
- #: these are currently EXPERIMENTAL undocumented features.
- exception_handler = None
- exception_formatter = None
-
- def __init__(self,
- block_start_string=BLOCK_START_STRING,
- block_end_string=BLOCK_END_STRING,
- variable_start_string=VARIABLE_START_STRING,
- variable_end_string=VARIABLE_END_STRING,
- comment_start_string=COMMENT_START_STRING,
- comment_end_string=COMMENT_END_STRING,
- line_statement_prefix=LINE_STATEMENT_PREFIX,
- line_comment_prefix=LINE_COMMENT_PREFIX,
- trim_blocks=TRIM_BLOCKS,
- newline_sequence=NEWLINE_SEQUENCE,
- extensions=(),
- optimized=True,
- undefined=Undefined,
- finalize=None,
- autoescape=False,
- loader=None,
- cache_size=50,
- auto_reload=True,
- bytecode_cache=None):
- # !!Important notice!!
- # The constructor accepts quite a few arguments that should be
- # passed by keyword rather than position. However it's important to
- # not change the order of arguments because it's used at least
- # internally in those cases:
- # - spontaneus environments (i18n extension and Template)
- # - unittests
- # If parameter changes are required only add parameters at the end
- # and don't change the arguments (or the defaults!) of the arguments
- # existing already.
-
- # lexer / parser information
- self.block_start_string = block_start_string
- self.block_end_string = block_end_string
- self.variable_start_string = variable_start_string
- self.variable_end_string = variable_end_string
- self.comment_start_string = comment_start_string
- self.comment_end_string = comment_end_string
- self.line_statement_prefix = line_statement_prefix
- self.line_comment_prefix = line_comment_prefix
- self.trim_blocks = trim_blocks
- self.newline_sequence = newline_sequence
-
- # runtime information
- self.undefined = undefined
- self.optimized = optimized
- self.finalize = finalize
- self.autoescape = autoescape
-
- # defaults
- self.filters = DEFAULT_FILTERS.copy()
- self.tests = DEFAULT_TESTS.copy()
- self.globals = DEFAULT_NAMESPACE.copy()
-
- # set the loader provided
- self.loader = loader
- self.bytecode_cache = None
- self.cache = create_cache(cache_size)
- self.bytecode_cache = bytecode_cache
- self.auto_reload = auto_reload
-
- # load extensions
- self.extensions = load_extensions(self, extensions)
-
- _environment_sanity_check(self)
-
- def add_extension(self, extension):
- """Adds an extension after the environment was created.
-
- .. versionadded:: 2.5
- """
- self.extensions.update(load_extensions(self, [extension]))
-
- def extend(self, **attributes):
- """Add the items to the instance of the environment if they do not exist
- yet. This is used by :ref:`extensions <writing-extensions>` to register
- callbacks and configuration values without breaking inheritance.
- """
- for key, value in attributes.iteritems():
- if not hasattr(self, key):
- setattr(self, key, value)
-
- def overlay(self, block_start_string=missing, block_end_string=missing,
- variable_start_string=missing, variable_end_string=missing,
- comment_start_string=missing, comment_end_string=missing,
- line_statement_prefix=missing, line_comment_prefix=missing,
- trim_blocks=missing, extensions=missing, optimized=missing,
- undefined=missing, finalize=missing, autoescape=missing,
- loader=missing, cache_size=missing, auto_reload=missing,
- bytecode_cache=missing):
- """Create a new overlay environment that shares all the data with the
- current environment except of cache and the overridden attributes.
- Extensions cannot be removed for an overlayed environment. An overlayed
- environment automatically gets all the extensions of the environment it
- is linked to plus optional extra extensions.
-
- Creating overlays should happen after the initial environment was set
- up completely. Not all attributes are truly linked, some are just
- copied over so modifications on the original environment may not shine
- through.
- """
- args = dict(locals())
- del args['self'], args['cache_size'], args['extensions']
-
- rv = object.__new__(self.__class__)
- rv.__dict__.update(self.__dict__)
- rv.overlayed = True
- rv.linked_to = self
-
- for key, value in args.iteritems():
- if value is not missing:
- setattr(rv, key, value)
-
- if cache_size is not missing:
- rv.cache = create_cache(cache_size)
- else:
- rv.cache = copy_cache(self.cache)
-
- rv.extensions = {}
- for key, value in self.extensions.iteritems():
- rv.extensions[key] = value.bind(rv)
- if extensions is not missing:
- rv.extensions.update(load_extensions(rv, extensions))
-
- return _environment_sanity_check(rv)
-
- lexer = property(get_lexer, doc="The lexer for this environment.")
-
- def iter_extensions(self):
- """Iterates over the extensions by priority."""
- return iter(sorted(self.extensions.values(),
- key=lambda x: x.priority))
-
- def getitem(self, obj, argument):
- """Get an item or attribute of an object but prefer the item."""
- try:
- return obj[argument]
- except (TypeError, LookupError):
- if isinstance(argument, basestring):
- try:
- attr = str(argument)
- except:
- pass
- else:
- try:
- return getattr(obj, attr)
- except AttributeError:
- pass
- return self.undefined(obj=obj, name=argument)
-
- def getattr(self, obj, attribute):
- """Get an item or attribute of an object but prefer the attribute.
- Unlike :meth:`getitem` the attribute *must* be a bytestring.
- """
- try:
- return getattr(obj, attribute)
- except AttributeError:
- pass
- try:
- return obj[attribute]
- except (TypeError, LookupError, AttributeError):
- return self.undefined(obj=obj, name=attribute)
-
- @internalcode
- def parse(self, source, name=None, filename=None):
- """Parse the sourcecode and return the abstract syntax tree. This
- tree of nodes is used by the compiler to convert the template into
- executable source- or bytecode. This is useful for debugging or to
- extract information from templates.
-
- If you are :ref:`developing Jinja2 extensions <writing-extensions>`
- this gives you a good overview of the node tree generated.
- """
- try:
- return self._parse(source, name, filename)
- except TemplateSyntaxError:
- exc_info = sys.exc_info()
- self.handle_exception(exc_info, source_hint=source)
-
- def _parse(self, source, name, filename):
- """Internal parsing function used by `parse` and `compile`."""
- return Parser(self, source, name, _encode_filename(filename)).parse()
-
- def lex(self, source, name=None, filename=None):
- """Lex the given sourcecode and return a generator that yields
- tokens as tuples in the form ``(lineno, token_type, value)``.
- This can be useful for :ref:`extension development <writing-extensions>`
- and debugging templates.
-
- This does not perform preprocessing. If you want the preprocessing
- of the extensions to be applied you have to filter source through
- the :meth:`preprocess` method.
- """
- source = unicode(source)
- try:
- return self.lexer.tokeniter(source, name, filename)
- except TemplateSyntaxError:
- exc_info = sys.exc_info()
- self.handle_exception(exc_info, source_hint=source)
-
- def preprocess(self, source, name=None, filename=None):
- """Preprocesses the source with all extensions. This is automatically
- called for all parsing and compiling methods but *not* for :meth:`lex`
- because there you usually only want the actual source tokenized.
- """
- return reduce(lambda s, e: e.preprocess(s, name, filename),
- self.iter_extensions(), unicode(source))
-
- def _tokenize(self, source, name, filename=None, state=None):
- """Called by the parser to do the preprocessing and filtering
- for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`.
- """
- source = self.preprocess(source, name, filename)
- stream = self.lexer.tokenize(source, name, filename, state)
- for ext in self.iter_extensions():
- stream = ext.filter_stream(stream)
- if not isinstance(stream, TokenStream):
- stream = TokenStream(stream, name, filename)
- return stream
-
- def _generate(self, source, name, filename, defer_init=False):
- """Internal hook that can be overriden to hook a different generate
- method in.
-
- .. versionadded:: 2.5
- """
- return generate(source, self, name, filename, defer_init=defer_init)
-
- def _compile(self, source, filename):
- """Internal hook that can be overriden to hook a different compile
- method in.
-
- .. versionadded:: 2.5
- """
- return compile(source, filename, 'exec')
-
- @internalcode
- def compile(self, source, name=None, filename=None, raw=False,
- defer_init=False):
- """Compile a node or template source code. The `name` parameter is
- the load name of the template after it was joined using
- :meth:`join_path` if necessary, not the filename on the file system.
- the `filename` parameter is the estimated filename of the template on
- the file system. If the template came from a database or memory this
- can be omitted.
-
- The return value of this method is a python code object. If the `raw`
- parameter is `True` the return value will be a string with python
- code equivalent to the bytecode returned otherwise. This method is
- mainly used internally.
-
- `defer_init` is use internally to aid the module code generator. This
- causes the generated code to be able to import without the global
- environment variable to be set.
-
- .. versionadded:: 2.4
- `defer_init` parameter added.
- """
- source_hint = None
- try:
- if isinstance(source, basestring):
- source_hint = source
- source = self._parse(source, name, filename)
- if self.optimized:
- source = optimize(source, self)
- source = self._generate(source, name, filename,
- defer_init=defer_init)
- if raw:
- return source
- if filename is None:
- filename = '<template>'
- else:
- filename = _encode_filename(filename)
- return self._compile(source, filename)
- except TemplateSyntaxError:
- exc_info = sys.exc_info()
- self.handle_exception(exc_info, source_hint=source)
-
- def compile_expression(self, source, undefined_to_none=True):
- """A handy helper method that returns a callable that accepts keyword
- arguments that appear as variables in the expression. If called it
- returns the result of the expression.
-
- This is useful if applications want to use the same rules as Jinja
- in template "configuration files" or similar situations.
-
- Example usage:
-
- >>> env = Environment()
- >>> expr = env.compile_expression('foo == 42')
- >>> expr(foo=23)
- False
- >>> expr(foo=42)
- True
-
- Per default the return value is converted to `None` if the
- expression returns an undefined value. This can be changed
- by setting `undefined_to_none` to `False`.
-
- >>> env.compile_expression('var')() is None
- True
- >>> env.compile_expression('var', undefined_to_none=False)()
- Undefined
-
- .. versionadded:: 2.1
- """
- parser = Parser(self, source, state='variable')
- exc_info = None
- try:
- expr = parser.parse_expression()
- if not parser.stream.eos:
- raise TemplateSyntaxError('chunk after expression',
- parser.stream.current.lineno,
- None, None)
- expr.set_environment(self)
- except TemplateSyntaxError:
- exc_info = sys.exc_info()
- if exc_info is not None:
- self.handle_exception(exc_info, source_hint=source)
- body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)]
- template = self.from_string(nodes.Template(body, lineno=1))
- return TemplateExpression(template, undefined_to_none)
-
- def compile_templates(self, target, extensions=None, filter_func=None,
- zip='deflated', log_function=None,
- ignore_errors=True, py_compile=False):
- """Compiles all the templates the loader can find, compiles them
- and stores them in `target`. If `zip` is `None`, instead of in a
- zipfile, the templates will be will be stored in a directory.
- By default a deflate zip algorithm is used, to switch to
- the stored algorithm, `zip` can be set to ``'stored'``.
-
- `extensions` and `filter_func` are passed to :meth:`list_templates`.
- Each template returned will be compiled to the target folder or
- zipfile.
-
- By default template compilation errors are ignored. In case a
- log function is provided, errors are logged. If you want template
- syntax errors to abort the compilation you can set `ignore_errors`
- to `False` and you will get an exception on syntax errors.
-
- If `py_compile` is set to `True` .pyc files will be written to the
- target instead of standard .py files.
-
- .. versionadded:: 2.4
- """
- from jinja2.loaders import ModuleLoader
-
- if log_function is None:
- log_function = lambda x: None
-
- if py_compile:
- import imp, struct, marshal
- py_header = imp.get_magic() + \
- u'\xff\xff\xff\xff'.encode('iso-8859-15')
-
- def write_file(filename, data, mode):
- if zip:
- info = ZipInfo(filename)
- info.external_attr = 0755 << 16L
- zip_file.writestr(info, data)
- else:
- f = open(os.path.join(target, filename), mode)
- try:
- f.write(data)
- finally:
- f.close()
-
- if zip is not None:
- from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED
- zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED,
- stored=ZIP_STORED)[zip])
- log_function('Compiling into Zip archive "%s"' % target)
- else:
- if not os.path.isdir(target):
- os.makedirs(target)
- log_function('Compiling into folder "%s"' % target)
-
- try:
- for name in self.list_templates(extensions, filter_func):
- source, filename, _ = self.loader.get_source(self, name)
- try:
- code = self.compile(source, name, filename, True, True)
- except TemplateSyntaxError, e:
- if not ignore_errors:
- raise
- log_function('Could not compile "%s": %s' % (name, e))
- continue
-
- filename = ModuleLoader.get_module_filename(name)
-
- if py_compile:
- c = self._compile(code, _encode_filename(filename))
- write_file(filename + 'c', py_header +
- marshal.dumps(c), 'wb')
- log_function('Byte-compiled "%s" as %s' %
- (name, filename + 'c'))
- else:
- write_file(filename, code, 'w')
- log_function('Compiled "%s" as %s' % (name, filename))
- finally:
- if zip:
- zip_file.close()
-
- log_function('Finished compiling templates')
-
- def list_templates(self, extensions=None, filter_func=None):
- """Returns a list of templates for this environment. This requires
- that the loader supports the loader's
- :meth:`~BaseLoader.list_templates` method.
-
- If there are other files in the template folder besides the
- actual templates, the returned list can be filtered. There are two
- ways: either `extensions` is set to a list of file extensions for
- templates, or a `filter_func` can be provided which is a callable that
- is passed a template name and should return `True` if it should end up
- in the result list.
-
- If the loader does not support that, a :exc:`TypeError` is raised.
- """
- x = self.loader.list_templates()
- if extensions is not None:
- if filter_func is not None:
- raise TypeError('either extensions or filter_func '
- 'can be passed, but not both')
- filter_func = lambda x: '.' in x and \
- x.rsplit('.', 1)[1] in extensions
- if filter_func is not None:
- x = filter(filter_func, x)
- return x
-
- def handle_exception(self, exc_info=None, rendered=False, source_hint=None):
- """Exception handling helper. This is used internally to either raise
- rewritten exceptions or return a rendered traceback for the template.
- """
- global _make_traceback
- if exc_info is None:
- exc_info = sys.exc_info()
-
- # the debugging module is imported when it's used for the first time.
- # we're doing a lot of stuff there and for applications that do not
- # get any exceptions in template rendering there is no need to load
- # all of that.
- if _make_traceback is None:
- from jinja2.debug import make_traceback as _make_traceback
- traceback = _make_traceback(exc_info, source_hint)
- if rendered and self.exception_formatter is not None:
- return self.exception_formatter(traceback)
- if self.exception_handler is not None:
- self.exception_handler(traceback)
- exc_type, exc_value, tb = traceback.standard_exc_info
- raise exc_type, exc_value, tb
-
- def join_path(self, template, parent):
- """Join a template with the parent. By default all the lookups are
- relative to the loader root so this method returns the `template`
- parameter unchanged, but if the paths should be relative to the
- parent template, this function can be used to calculate the real
- template name.
-
- Subclasses may override this method and implement template path
- joining here.
- """
- return template
-
- @internalcode
- def _load_template(self, name, globals):
- if self.loader is None:
- raise TypeError('no loader for this environment specified')
- if self.cache is not None:
- template = self.cache.get(name)
- if template is not None and (not self.auto_reload or \
- template.is_up_to_date):
- return template
- template = self.loader.load(self, name, globals)
- if self.cache is not None:
- self.cache[name] = template
- return template
-
- @internalcode
- def get_template(self, name, parent=None, globals=None):
- """Load a template from the loader. If a loader is configured this
- method ask the loader for the template and returns a :class:`Template`.
- If the `parent` parameter is not `None`, :meth:`join_path` is called
- to get the real template name before loading.
-
- The `globals` parameter can be used to provide template wide globals.
- These variables are available in the context at render time.
-
- If the template does not exist a :exc:`TemplateNotFound` exception is
- raised.
-
- .. versionchanged:: 2.4
- If `name` is a :class:`Template` object it is returned from the
- function unchanged.
- """
- if isinstance(name, Template):
- return name
- if parent is not None:
- name = self.join_path(name, parent)
- return self._load_template(name, self.make_globals(globals))
-
- @internalcode
- def select_template(self, names, parent=None, globals=None):
- """Works like :meth:`get_template` but tries a number of templates
- before it fails. If it cannot find any of the templates, it will
- raise a :exc:`TemplatesNotFound` exception.
-
- .. versionadded:: 2.3
-
- .. versionchanged:: 2.4
- If `names` contains a :class:`Template` object it is returned
- from the function unchanged.
- """
- if not names:
- raise TemplatesNotFound(message=u'Tried to select from an empty list '
- u'of templates.')
- globals = self.make_globals(globals)
- for name in names:
- if isinstance(name, Template):
- return name
- if parent is not None:
- name = self.join_path(name, parent)
- try:
- return self._load_template(name, globals)
- except TemplateNotFound:
- pass
- raise TemplatesNotFound(names)
-
- @internalcode
- def get_or_select_template(self, template_name_or_list,
- parent=None, globals=None):
- """Does a typecheck and dispatches to :meth:`select_template`
- if an iterable of template names is given, otherwise to
- :meth:`get_template`.
-
- .. versionadded:: 2.3
- """
- if isinstance(template_name_or_list, basestring):
- return self.get_template(template_name_or_list, parent, globals)
- elif isinstance(template_name_or_list, Template):
- return template_name_or_list
- return self.select_template(template_name_or_list, parent, globals)
-
- def from_string(self, source, globals=None, template_class=None):
- """Load a template from a string. This parses the source given and
- returns a :class:`Template` object.
- """
- globals = self.make_globals(globals)
- cls = template_class or self.template_class
- return cls.from_code(self, self.compile(source), globals, None)
-
- def make_globals(self, d):
- """Return a dict for the globals."""
- if not d:
- return self.globals
- return dict(self.globals, **d)
-
-
-class Template(object):
- """The central template object. This class represents a compiled template
- and is used to evaluate it.
-
- Normally the template object is generated from an :class:`Environment` but
- it also has a constructor that makes it possible to create a template
- instance directly using the constructor. It takes the same arguments as
- the environment constructor but it's not possible to specify a loader.
-
- Every template object has a few methods and members that are guaranteed
- to exist. However it's important that a template object should be
- considered immutable. Modifications on the object are not supported.
-
- Template objects created from the constructor rather than an environment
- do have an `environment` attribute that points to a temporary environment
- that is probably shared with other templates created with the constructor
- and compatible settings.
-
- >>> template = Template('Hello {{ name }}!')
- >>> template.render(name='John Doe')
- u'Hello John Doe!'
-
- >>> stream = template.stream(name='John Doe')
- >>> stream.next()
- u'Hello John Doe!'
- >>> stream.next()
- Traceback (most recent call last):
- ...
- StopIteration
- """
-
- def __new__(cls, source,
- block_start_string=BLOCK_START_STRING,
- block_end_string=BLOCK_END_STRING,
- variable_start_string=VARIABLE_START_STRING,
- variable_end_string=VARIABLE_END_STRING,
- comment_start_string=COMMENT_START_STRING,
- comment_end_string=COMMENT_END_STRING,
- line_statement_prefix=LINE_STATEMENT_PREFIX,
- line_comment_prefix=LINE_COMMENT_PREFIX,
- trim_blocks=TRIM_BLOCKS,
- newline_sequence=NEWLINE_SEQUENCE,
- extensions=(),
- optimized=True,
- undefined=Undefined,
- finalize=None,
- autoescape=False):
- env = get_spontaneous_environment(
- block_start_string, block_end_string, variable_start_string,
- variable_end_string, comment_start_string, comment_end_string,
- line_statement_prefix, line_comment_prefix, trim_blocks,
- newline_sequence, frozenset(extensions), optimized, undefined,
- finalize, autoescape, None, 0, False, None)
- return env.from_string(source, template_class=cls)
-
- @classmethod
- def from_code(cls, environment, code, globals, uptodate=None):
- """Creates a template object from compiled code and the globals. This
- is used by the loaders and environment to create a template object.
- """
- namespace = {
- 'environment': environment,
- '__file__': code.co_filename
- }
- exec code in namespace
- rv = cls._from_namespace(environment, namespace, globals)
- rv._uptodate = uptodate
- return rv
-
- @classmethod
- def from_module_dict(cls, environment, module_dict, globals):
- """Creates a template object from a module. This is used by the
- module loader to create a template object.
-
- .. versionadded:: 2.4
- """
- return cls._from_namespace(environment, module_dict, globals)
-
- @classmethod
- def _from_namespace(cls, environment, namespace, globals):
- t = object.__new__(cls)
- t.environment = environment
- t.globals = globals
- t.name = namespace['name']
- t.filename = namespace['__file__']
- t.blocks = namespace['blocks']
-
- # render function and module
- t.root_render_func = namespace['root']
- t._module = None
-
- # debug and loader helpers
- t._debug_info = namespace['debug_info']
- t._uptodate = None
-
- # store the reference
- namespace['environment'] = environment
- namespace['__jinja_template__'] = t
-
- return t
-
- def render(self, *args, **kwargs):
- """This method accepts the same arguments as the `dict` constructor:
- A dict, a dict subclass or some keyword arguments. If no arguments
- are given the context will be empty. These two calls do the same::
-
- template.render(knights='that say nih')
- template.render({'knights': 'that say nih'})
-
- This will return the rendered template as unicode string.
- """
- vars = dict(*args, **kwargs)
- try:
- return concat(self.root_render_func(self.new_context(vars)))
- except:
- exc_info = sys.exc_info()
- return self.environment.handle_exception(exc_info, True)
-
- def stream(self, *args, **kwargs):
- """Works exactly like :meth:`generate` but returns a
- :class:`TemplateStream`.
- """
- return TemplateStream(self.generate(*args, **kwargs))
-
- def generate(self, *args, **kwargs):
- """For very large templates it can be useful to not render the whole
- template at once but evaluate each statement after another and yield
- piece for piece. This method basically does exactly that and returns
- a generator that yields one item after another as unicode strings.
-
- It accepts the same arguments as :meth:`render`.
- """
- vars = dict(*args, **kwargs)
- try:
- for event in self.root_render_func(self.new_context(vars)):
- yield event
- except:
- exc_info = sys.exc_info()
- else:
- return
- yield self.environment.handle_exception(exc_info, True)
-
- def new_context(self, vars=None, shared=False, locals=None):
- """Create a new :class:`Context` for this template. The vars
- provided will be passed to the template. Per default the globals
- are added to the context. If shared is set to `True` the data
- is passed as it to the context without adding the globals.
-
- `locals` can be a dict of local variables for internal usage.
- """
- return new_context(self.environment, self.name, self.blocks,
- vars, shared, self.globals, locals)
-
- def make_module(self, vars=None, shared=False, locals=None):
- """This method works like the :attr:`module` attribute when called
- without arguments but it will evaluate the template on every call
- rather than caching it. It's also possible to provide
- a dict which is then used as context. The arguments are the same
- as for the :meth:`new_context` method.
- """
- return TemplateModule(self, self.new_context(vars, shared, locals))
-
- @property
- def module(self):
- """The template as module. This is used for imports in the
- template runtime but is also useful if one wants to access
- exported template variables from the Python layer:
-
- >>> t = Template('{% macro foo() %}42{% endmacro %}23')
- >>> unicode(t.module)
- u'23'
- >>> t.module.foo()
- u'42'
- """
- if self._module is not None:
- return self._module
- self._module = rv = self.make_module()
- return rv
-
- def get_corresponding_lineno(self, lineno):
- """Return the source line number of a line number in the
- generated bytecode as they are not in sync.
- """
- for template_line, code_line in reversed(self.debug_info):
- if code_line <= lineno:
- return template_line
- return 1
-
- @property
- def is_up_to_date(self):
- """If this variable is `False` there is a newer version available."""
- if self._uptodate is None:
- return True
- return self._uptodate()
-
- @property
- def debug_info(self):
- """The debug info mapping."""
- return [tuple(map(int, x.split('='))) for x in
- self._debug_info.split('&')]
-
- def __repr__(self):
- if self.name is None:
- name = 'memory:%x' % id(self)
- else:
- name = repr(self.name)
- return '<%s %s>' % (self.__class__.__name__, name)
-
-
-class TemplateModule(object):
- """Represents an imported template. All the exported names of the
- template are available as attributes on this object. Additionally
- converting it into an unicode- or bytestrings renders the contents.
- """
-
- def __init__(self, template, context):
- self._body_stream = list(template.root_render_func(context))
- self.__dict__.update(context.get_exported())
- self.__name__ = template.name
-
- def __html__(self):
- return Markup(concat(self._body_stream))
-
- def __str__(self):
- return unicode(self).encode('utf-8')
-
- # unicode goes after __str__ because we configured 2to3 to rename
- # __unicode__ to __str__. because the 2to3 tree is not designed to
- # remove nodes from it, we leave the above __str__ around and let
- # it override at runtime.
- def __unicode__(self):
- return concat(self._body_stream)
-
- def __repr__(self):
- if self.__name__ is None:
- name = 'memory:%x' % id(self)
- else:
- name = repr(self.__name__)
- return '<%s %s>' % (self.__class__.__name__, name)
-
-
-class TemplateExpression(object):
- """The :meth:`jinja2.Environment.compile_expression` method returns an
- instance of this object. It encapsulates the expression-like access
- to the template with an expression it wraps.
- """
-
- def __init__(self, template, undefined_to_none):
- self._template = template
- self._undefined_to_none = undefined_to_none
-
- def __call__(self, *args, **kwargs):
- context = self._template.new_context(dict(*args, **kwargs))
- consume(self._template.root_render_func(context))
- rv = context.vars['result']
- if self._undefined_to_none and isinstance(rv, Undefined):
- rv = None
- return rv
-
-
-class TemplateStream(object):
- """A template stream works pretty much like an ordinary python generator
- but it can buffer multiple items to reduce the number of total iterations.
- Per default the output is unbuffered which means that for every unbuffered
- instruction in the template one unicode string is yielded.
-
- If buffering is enabled with a buffer size of 5, five items are combined
- into a new unicode string. This is mainly useful if you are streaming
- big templates to a client via WSGI which flushes after each iteration.
- """
-
- def __init__(self, gen):
- self._gen = gen
- self.disable_buffering()
-
- def dump(self, fp, encoding=None, errors='strict'):
- """Dump the complete stream into a file or file-like object.
- Per default unicode strings are written, if you want to encode
- before writing specifiy an `encoding`.
-
- Example usage::
-
- Template('Hello {{ name }}!').stream(name='foo').dump('hello.html')
- """
- close = False
- if isinstance(fp, basestring):
- fp = file(fp, 'w')
- close = True
- try:
- if encoding is not None:
- iterable = (x.encode(encoding, errors) for x in self)
- else:
- iterable = self
- if hasattr(fp, 'writelines'):
- fp.writelines(iterable)
- else:
- for item in iterable:
- fp.write(item)
- finally:
- if close:
- fp.close()
-
- def disable_buffering(self):
- """Disable the output buffering."""
- self._next = self._gen.next
- self.buffered = False
-
- def enable_buffering(self, size=5):
- """Enable buffering. Buffer `size` items before yielding them."""
- if size <= 1:
- raise ValueError('buffer size too small')
-
- def generator(next):
- buf = []
- c_size = 0
- push = buf.append
-
- while 1:
- try:
- while c_size < size:
- c = next()
- push(c)
- if c:
- c_size += 1
- except StopIteration:
- if not c_size:
- return
- yield concat(buf)
- del buf[:]
- c_size = 0
-
- self.buffered = True
- self._next = generator(self._gen.next).next
-
- def __iter__(self):
- return self
-
- def next(self):
- return self._next()
-
-
-# hook in default template class. if anyone reads this comment: ignore that
-# it's possible to use custom templates ;-)
-Environment.template_class = Template
diff --git a/module/lib/jinja2/exceptions.py b/module/lib/jinja2/exceptions.py
deleted file mode 100644
index 771f6a8d7..000000000
--- a/module/lib/jinja2/exceptions.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.exceptions
- ~~~~~~~~~~~~~~~~~
-
- Jinja exceptions.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-
-
-class TemplateError(Exception):
- """Baseclass for all template errors."""
-
- def __init__(self, message=None):
- if message is not None:
- message = unicode(message).encode('utf-8')
- Exception.__init__(self, message)
-
- @property
- def message(self):
- if self.args:
- message = self.args[0]
- if message is not None:
- return message.decode('utf-8', 'replace')
-
-
-class TemplateNotFound(IOError, LookupError, TemplateError):
- """Raised if a template does not exist."""
-
- # looks weird, but removes the warning descriptor that just
- # bogusly warns us about message being deprecated
- message = None
-
- def __init__(self, name, message=None):
- IOError.__init__(self)
- if message is None:
- message = name
- self.message = message
- self.name = name
- self.templates = [name]
-
- def __str__(self):
- return self.message.encode('utf-8')
-
- # unicode goes after __str__ because we configured 2to3 to rename
- # __unicode__ to __str__. because the 2to3 tree is not designed to
- # remove nodes from it, we leave the above __str__ around and let
- # it override at runtime.
- def __unicode__(self):
- return self.message
-
-
-class TemplatesNotFound(TemplateNotFound):
- """Like :class:`TemplateNotFound` but raised if multiple templates
- are selected. This is a subclass of :class:`TemplateNotFound`
- exception, so just catching the base exception will catch both.
-
- .. versionadded:: 2.2
- """
-
- def __init__(self, names=(), message=None):
- if message is None:
- message = u'non of the templates given were found: ' + \
- u', '.join(map(unicode, names))
- TemplateNotFound.__init__(self, names and names[-1] or None, message)
- self.templates = list(names)
-
-
-class TemplateSyntaxError(TemplateError):
- """Raised to tell the user that there is a problem with the template."""
-
- def __init__(self, message, lineno, name=None, filename=None):
- TemplateError.__init__(self, message)
- self.lineno = lineno
- self.name = name
- self.filename = filename
- self.source = None
-
- # this is set to True if the debug.translate_syntax_error
- # function translated the syntax error into a new traceback
- self.translated = False
-
- def __str__(self):
- return unicode(self).encode('utf-8')
-
- # unicode goes after __str__ because we configured 2to3 to rename
- # __unicode__ to __str__. because the 2to3 tree is not designed to
- # remove nodes from it, we leave the above __str__ around and let
- # it override at runtime.
- def __unicode__(self):
- # for translated errors we only return the message
- if self.translated:
- return self.message
-
- # otherwise attach some stuff
- location = 'line %d' % self.lineno
- name = self.filename or self.name
- if name:
- location = 'File "%s", %s' % (name, location)
- lines = [self.message, ' ' + location]
-
- # if the source is set, add the line to the output
- if self.source is not None:
- try:
- line = self.source.splitlines()[self.lineno - 1]
- except IndexError:
- line = None
- if line:
- lines.append(' ' + line.strip())
-
- return u'\n'.join(lines)
-
-
-class TemplateAssertionError(TemplateSyntaxError):
- """Like a template syntax error, but covers cases where something in the
- template caused an error at compile time that wasn't necessarily caused
- by a syntax error. However it's a direct subclass of
- :exc:`TemplateSyntaxError` and has the same attributes.
- """
-
-
-class TemplateRuntimeError(TemplateError):
- """A generic runtime error in the template engine. Under some situations
- Jinja may raise this exception.
- """
-
-
-class UndefinedError(TemplateRuntimeError):
- """Raised if a template tries to operate on :class:`Undefined`."""
-
-
-class SecurityError(TemplateRuntimeError):
- """Raised if a template tries to do something insecure if the
- sandbox is enabled.
- """
-
-
-class FilterArgumentError(TemplateRuntimeError):
- """This error is raised if a filter was called with inappropriate
- arguments
- """
diff --git a/module/lib/jinja2/ext.py b/module/lib/jinja2/ext.py
deleted file mode 100644
index ceb38953a..000000000
--- a/module/lib/jinja2/ext.py
+++ /dev/null
@@ -1,610 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.ext
- ~~~~~~~~~~
-
- Jinja extensions allow to add custom tags similar to the way django custom
- tags work. By default two example extensions exist: an i18n and a cache
- extension.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-from collections import deque
-from jinja2 import nodes
-from jinja2.defaults import *
-from jinja2.environment import Environment
-from jinja2.runtime import Undefined, concat
-from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
-from jinja2.utils import contextfunction, import_string, Markup, next
-
-
-# the only real useful gettext functions for a Jinja template. Note
-# that ugettext must be assigned to gettext as Jinja doesn't support
-# non unicode strings.
-GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext')
-
-
-class ExtensionRegistry(type):
- """Gives the extension an unique identifier."""
-
- def __new__(cls, name, bases, d):
- rv = type.__new__(cls, name, bases, d)
- rv.identifier = rv.__module__ + '.' + rv.__name__
- return rv
-
-
-class Extension(object):
- """Extensions can be used to add extra functionality to the Jinja template
- system at the parser level. Custom extensions are bound to an environment
- but may not store environment specific data on `self`. The reason for
- this is that an extension can be bound to another environment (for
- overlays) by creating a copy and reassigning the `environment` attribute.
-
- As extensions are created by the environment they cannot accept any
- arguments for configuration. One may want to work around that by using
- a factory function, but that is not possible as extensions are identified
- by their import name. The correct way to configure the extension is
- storing the configuration values on the environment. Because this way the
- environment ends up acting as central configuration storage the
- attributes may clash which is why extensions have to ensure that the names
- they choose for configuration are not too generic. ``prefix`` for example
- is a terrible name, ``fragment_cache_prefix`` on the other hand is a good
- name as includes the name of the extension (fragment cache).
- """
- __metaclass__ = ExtensionRegistry
-
- #: if this extension parses this is the list of tags it's listening to.
- tags = set()
-
- #: the priority of that extension. This is especially useful for
- #: extensions that preprocess values. A lower value means higher
- #: priority.
- #:
- #: .. versionadded:: 2.4
- priority = 100
-
- def __init__(self, environment):
- self.environment = environment
-
- def bind(self, environment):
- """Create a copy of this extension bound to another environment."""
- rv = object.__new__(self.__class__)
- rv.__dict__.update(self.__dict__)
- rv.environment = environment
- return rv
-
- def preprocess(self, source, name, filename=None):
- """This method is called before the actual lexing and can be used to
- preprocess the source. The `filename` is optional. The return value
- must be the preprocessed source.
- """
- return source
-
- def filter_stream(self, stream):
- """It's passed a :class:`~jinja2.lexer.TokenStream` that can be used
- to filter tokens returned. This method has to return an iterable of
- :class:`~jinja2.lexer.Token`\s, but it doesn't have to return a
- :class:`~jinja2.lexer.TokenStream`.
-
- In the `ext` folder of the Jinja2 source distribution there is a file
- called `inlinegettext.py` which implements a filter that utilizes this
- method.
- """
- return stream
-
- def parse(self, parser):
- """If any of the :attr:`tags` matched this method is called with the
- parser as first argument. The token the parser stream is pointing at
- is the name token that matched. This method has to return one or a
- list of multiple nodes.
- """
- raise NotImplementedError()
-
- def attr(self, name, lineno=None):
- """Return an attribute node for the current extension. This is useful
- to pass constants on extensions to generated template code::
-
- self.attr('_my_attribute', lineno=lineno)
- """
- return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
-
- def call_method(self, name, args=None, kwargs=None, dyn_args=None,
- dyn_kwargs=None, lineno=None):
- """Call a method of the extension. This is a shortcut for
- :meth:`attr` + :class:`jinja2.nodes.Call`.
- """
- if args is None:
- args = []
- if kwargs is None:
- kwargs = []
- return nodes.Call(self.attr(name, lineno=lineno), args, kwargs,
- dyn_args, dyn_kwargs, lineno=lineno)
-
-
-@contextfunction
-def _gettext_alias(__context, *args, **kwargs):
- return __context.call(__context.resolve('gettext'), *args, **kwargs)
-
-
-def _make_new_gettext(func):
- @contextfunction
- def gettext(__context, __string, **variables):
- rv = __context.call(func, __string)
- if __context.eval_ctx.autoescape:
- rv = Markup(rv)
- return rv % variables
- return gettext
-
-
-def _make_new_ngettext(func):
- @contextfunction
- def ngettext(__context, __singular, __plural, __num, **variables):
- variables.setdefault('num', __num)
- rv = __context.call(func, __singular, __plural, __num)
- if __context.eval_ctx.autoescape:
- rv = Markup(rv)
- return rv % variables
- return ngettext
-
-
-class InternationalizationExtension(Extension):
- """This extension adds gettext support to Jinja2."""
- tags = set(['trans'])
-
- # TODO: the i18n extension is currently reevaluating values in a few
- # situations. Take this example:
- # {% trans count=something() %}{{ count }} foo{% pluralize
- # %}{{ count }} fooss{% endtrans %}
- # something is called twice here. One time for the gettext value and
- # the other time for the n-parameter of the ngettext function.
-
- def __init__(self, environment):
- Extension.__init__(self, environment)
- environment.globals['_'] = _gettext_alias
- environment.extend(
- install_gettext_translations=self._install,
- install_null_translations=self._install_null,
- install_gettext_callables=self._install_callables,
- uninstall_gettext_translations=self._uninstall,
- extract_translations=self._extract,
- newstyle_gettext=False
- )
-
- def _install(self, translations, newstyle=None):
- gettext = getattr(translations, 'ugettext', None)
- if gettext is None:
- gettext = translations.gettext
- ngettext = getattr(translations, 'ungettext', None)
- if ngettext is None:
- ngettext = translations.ngettext
- self._install_callables(gettext, ngettext, newstyle)
-
- def _install_null(self, newstyle=None):
- self._install_callables(
- lambda x: x,
- lambda s, p, n: (n != 1 and (p,) or (s,))[0],
- newstyle
- )
-
- def _install_callables(self, gettext, ngettext, newstyle=None):
- if newstyle is not None:
- self.environment.newstyle_gettext = newstyle
- if self.environment.newstyle_gettext:
- gettext = _make_new_gettext(gettext)
- ngettext = _make_new_ngettext(ngettext)
- self.environment.globals.update(
- gettext=gettext,
- ngettext=ngettext
- )
-
- def _uninstall(self, translations):
- for key in 'gettext', 'ngettext':
- self.environment.globals.pop(key, None)
-
- def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
- if isinstance(source, basestring):
- source = self.environment.parse(source)
- return extract_from_ast(source, gettext_functions)
-
- def parse(self, parser):
- """Parse a translatable tag."""
- lineno = next(parser.stream).lineno
- num_called_num = False
-
- # find all the variables referenced. Additionally a variable can be
- # defined in the body of the trans block too, but this is checked at
- # a later state.
- plural_expr = None
- variables = {}
- while parser.stream.current.type != 'block_end':
- if variables:
- parser.stream.expect('comma')
-
- # skip colon for python compatibility
- if parser.stream.skip_if('colon'):
- break
-
- name = parser.stream.expect('name')
- if name.value in variables:
- parser.fail('translatable variable %r defined twice.' %
- name.value, name.lineno,
- exc=TemplateAssertionError)
-
- # expressions
- if parser.stream.current.type == 'assign':
- next(parser.stream)
- variables[name.value] = var = parser.parse_expression()
- else:
- variables[name.value] = var = nodes.Name(name.value, 'load')
-
- if plural_expr is None:
- plural_expr = var
- num_called_num = name.value == 'num'
-
- parser.stream.expect('block_end')
-
- plural = plural_names = None
- have_plural = False
- referenced = set()
-
- # now parse until endtrans or pluralize
- singular_names, singular = self._parse_block(parser, True)
- if singular_names:
- referenced.update(singular_names)
- if plural_expr is None:
- plural_expr = nodes.Name(singular_names[0], 'load')
- num_called_num = singular_names[0] == 'num'
-
- # if we have a pluralize block, we parse that too
- if parser.stream.current.test('name:pluralize'):
- have_plural = True
- next(parser.stream)
- if parser.stream.current.type != 'block_end':
- name = parser.stream.expect('name')
- if name.value not in variables:
- parser.fail('unknown variable %r for pluralization' %
- name.value, name.lineno,
- exc=TemplateAssertionError)
- plural_expr = variables[name.value]
- num_called_num = name.value == 'num'
- parser.stream.expect('block_end')
- plural_names, plural = self._parse_block(parser, False)
- next(parser.stream)
- referenced.update(plural_names)
- else:
- next(parser.stream)
-
- # register free names as simple name expressions
- for var in referenced:
- if var not in variables:
- variables[var] = nodes.Name(var, 'load')
-
- if not have_plural:
- plural_expr = None
- elif plural_expr is None:
- parser.fail('pluralize without variables', lineno)
-
- node = self._make_node(singular, plural, variables, plural_expr,
- bool(referenced),
- num_called_num and have_plural)
- node.set_lineno(lineno)
- return node
-
- def _parse_block(self, parser, allow_pluralize):
- """Parse until the next block tag with a given name."""
- referenced = []
- buf = []
- while 1:
- if parser.stream.current.type == 'data':
- buf.append(parser.stream.current.value.replace('%', '%%'))
- next(parser.stream)
- elif parser.stream.current.type == 'variable_begin':
- next(parser.stream)
- name = parser.stream.expect('name').value
- referenced.append(name)
- buf.append('%%(%s)s' % name)
- parser.stream.expect('variable_end')
- elif parser.stream.current.type == 'block_begin':
- next(parser.stream)
- if parser.stream.current.test('name:endtrans'):
- break
- elif parser.stream.current.test('name:pluralize'):
- if allow_pluralize:
- break
- parser.fail('a translatable section can have only one '
- 'pluralize section')
- parser.fail('control structures in translatable sections are '
- 'not allowed')
- elif parser.stream.eos:
- parser.fail('unclosed translation block')
- else:
- assert False, 'internal parser error'
-
- return referenced, concat(buf)
-
- def _make_node(self, singular, plural, variables, plural_expr,
- vars_referenced, num_called_num):
- """Generates a useful node from the data provided."""
- # no variables referenced? no need to escape for old style
- # gettext invocations only if there are vars.
- if not vars_referenced and not self.environment.newstyle_gettext:
- singular = singular.replace('%%', '%')
- if plural:
- plural = plural.replace('%%', '%')
-
- # singular only:
- if plural_expr is None:
- gettext = nodes.Name('gettext', 'load')
- node = nodes.Call(gettext, [nodes.Const(singular)],
- [], None, None)
-
- # singular and plural
- else:
- ngettext = nodes.Name('ngettext', 'load')
- node = nodes.Call(ngettext, [
- nodes.Const(singular),
- nodes.Const(plural),
- plural_expr
- ], [], None, None)
-
- # in case newstyle gettext is used, the method is powerful
- # enough to handle the variable expansion and autoescape
- # handling itself
- if self.environment.newstyle_gettext:
- for key, value in variables.iteritems():
- # the function adds that later anyways in case num was
- # called num, so just skip it.
- if num_called_num and key == 'num':
- continue
- node.kwargs.append(nodes.Keyword(key, value))
-
- # otherwise do that here
- else:
- # mark the return value as safe if we are in an
- # environment with autoescaping turned on
- node = nodes.MarkSafeIfAutoescape(node)
- if variables:
- node = nodes.Mod(node, nodes.Dict([
- nodes.Pair(nodes.Const(key), value)
- for key, value in variables.items()
- ]))
- return nodes.Output([node])
-
-
-class ExprStmtExtension(Extension):
- """Adds a `do` tag to Jinja2 that works like the print statement just
- that it doesn't print the return value.
- """
- tags = set(['do'])
-
- def parse(self, parser):
- node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
- node.node = parser.parse_tuple()
- return node
-
-
-class LoopControlExtension(Extension):
- """Adds break and continue to the template engine."""
- tags = set(['break', 'continue'])
-
- def parse(self, parser):
- token = next(parser.stream)
- if token.value == 'break':
- return nodes.Break(lineno=token.lineno)
- return nodes.Continue(lineno=token.lineno)
-
-
-class WithExtension(Extension):
- """Adds support for a django-like with block."""
- tags = set(['with'])
-
- def parse(self, parser):
- node = nodes.Scope(lineno=next(parser.stream).lineno)
- assignments = []
- while parser.stream.current.type != 'block_end':
- lineno = parser.stream.current.lineno
- if assignments:
- parser.stream.expect('comma')
- target = parser.parse_assign_target()
- parser.stream.expect('assign')
- expr = parser.parse_expression()
- assignments.append(nodes.Assign(target, expr, lineno=lineno))
- node.body = assignments + \
- list(parser.parse_statements(('name:endwith',),
- drop_needle=True))
- return node
-
-
-class AutoEscapeExtension(Extension):
- """Changes auto escape rules for a scope."""
- tags = set(['autoescape'])
-
- def parse(self, parser):
- node = nodes.ScopedEvalContextModifier(lineno=next(parser.stream).lineno)
- node.options = [
- nodes.Keyword('autoescape', parser.parse_expression())
- ]
- node.body = parser.parse_statements(('name:endautoescape',),
- drop_needle=True)
- return nodes.Scope([node])
-
-
-def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
- babel_style=True):
- """Extract localizable strings from the given template node. Per
- default this function returns matches in babel style that means non string
- parameters as well as keyword arguments are returned as `None`. This
- allows Babel to figure out what you really meant if you are using
- gettext functions that allow keyword arguments for placeholder expansion.
- If you don't want that behavior set the `babel_style` parameter to `False`
- which causes only strings to be returned and parameters are always stored
- in tuples. As a consequence invalid gettext calls (calls without a single
- string parameter or string parameters after non-string parameters) are
- skipped.
-
- This example explains the behavior:
-
- >>> from jinja2 import Environment
- >>> env = Environment()
- >>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}')
- >>> list(extract_from_ast(node))
- [(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))]
- >>> list(extract_from_ast(node, babel_style=False))
- [(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))]
-
- For every string found this function yields a ``(lineno, function,
- message)`` tuple, where:
-
- * ``lineno`` is the number of the line on which the string was found,
- * ``function`` is the name of the ``gettext`` function used (if the
- string was extracted from embedded Python code), and
- * ``message`` is the string itself (a ``unicode`` object, or a tuple
- of ``unicode`` objects for functions with multiple string arguments).
-
- This extraction function operates on the AST and is because of that unable
- to extract any comments. For comment support you have to use the babel
- extraction interface or extract comments yourself.
- """
- for node in node.find_all(nodes.Call):
- if not isinstance(node.node, nodes.Name) or \
- node.node.name not in gettext_functions:
- continue
-
- strings = []
- for arg in node.args:
- if isinstance(arg, nodes.Const) and \
- isinstance(arg.value, basestring):
- strings.append(arg.value)
- else:
- strings.append(None)
-
- for arg in node.kwargs:
- strings.append(None)
- if node.dyn_args is not None:
- strings.append(None)
- if node.dyn_kwargs is not None:
- strings.append(None)
-
- if not babel_style:
- strings = tuple(x for x in strings if x is not None)
- if not strings:
- continue
- else:
- if len(strings) == 1:
- strings = strings[0]
- else:
- strings = tuple(strings)
- yield node.lineno, node.node.name, strings
-
-
-class _CommentFinder(object):
- """Helper class to find comments in a token stream. Can only
- find comments for gettext calls forwards. Once the comment
- from line 4 is found, a comment for line 1 will not return a
- usable value.
- """
-
- def __init__(self, tokens, comment_tags):
- self.tokens = tokens
- self.comment_tags = comment_tags
- self.offset = 0
- self.last_lineno = 0
-
- def find_backwards(self, offset):
- try:
- for _, token_type, token_value in \
- reversed(self.tokens[self.offset:offset]):
- if token_type in ('comment', 'linecomment'):
- try:
- prefix, comment = token_value.split(None, 1)
- except ValueError:
- continue
- if prefix in self.comment_tags:
- return [comment.rstrip()]
- return []
- finally:
- self.offset = offset
-
- def find_comments(self, lineno):
- if not self.comment_tags or self.last_lineno > lineno:
- return []
- for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset:]):
- if token_lineno > lineno:
- return self.find_backwards(self.offset + idx)
- return self.find_backwards(len(self.tokens))
-
-
-def babel_extract(fileobj, keywords, comment_tags, options):
- """Babel extraction method for Jinja templates.
-
- .. versionchanged:: 2.3
- Basic support for translation comments was added. If `comment_tags`
- is now set to a list of keywords for extraction, the extractor will
- try to find the best preceeding comment that begins with one of the
- keywords. For best results, make sure to not have more than one
- gettext call in one line of code and the matching comment in the
- same line or the line before.
-
- .. versionchanged:: 2.5.1
- The `newstyle_gettext` flag can be set to `True` to enable newstyle
- gettext calls.
-
- :param fileobj: the file-like object the messages should be extracted from
- :param keywords: a list of keywords (i.e. function names) that should be
- recognized as translation functions
- :param comment_tags: a list of translator tags to search for and include
- in the results.
- :param options: a dictionary of additional options (optional)
- :return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
- (comments will be empty currently)
- """
- extensions = set()
- for extension in options.get('extensions', '').split(','):
- extension = extension.strip()
- if not extension:
- continue
- extensions.add(import_string(extension))
- if InternationalizationExtension not in extensions:
- extensions.add(InternationalizationExtension)
-
- def getbool(options, key, default=False):
- options.get(key, str(default)).lower() in ('1', 'on', 'yes', 'true')
-
- environment = Environment(
- options.get('block_start_string', BLOCK_START_STRING),
- options.get('block_end_string', BLOCK_END_STRING),
- options.get('variable_start_string', VARIABLE_START_STRING),
- options.get('variable_end_string', VARIABLE_END_STRING),
- options.get('comment_start_string', COMMENT_START_STRING),
- options.get('comment_end_string', COMMENT_END_STRING),
- options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX,
- options.get('line_comment_prefix') or LINE_COMMENT_PREFIX,
- getbool(options, 'trim_blocks', TRIM_BLOCKS),
- NEWLINE_SEQUENCE, frozenset(extensions),
- cache_size=0,
- auto_reload=False
- )
-
- if getbool(options, 'newstyle_gettext'):
- environment.newstyle_gettext = True
-
- source = fileobj.read().decode(options.get('encoding', 'utf-8'))
- try:
- node = environment.parse(source)
- tokens = list(environment.lex(environment.preprocess(source)))
- except TemplateSyntaxError, e:
- # skip templates with syntax errors
- return
-
- finder = _CommentFinder(tokens, comment_tags)
- for lineno, func, message in extract_from_ast(node, keywords):
- yield lineno, func, message, finder.find_comments(lineno)
-
-
-#: nicer import names
-i18n = InternationalizationExtension
-do = ExprStmtExtension
-loopcontrols = LoopControlExtension
-with_ = WithExtension
-autoescape = AutoEscapeExtension
diff --git a/module/lib/jinja2/filters.py b/module/lib/jinja2/filters.py
deleted file mode 100644
index d1848e434..000000000
--- a/module/lib/jinja2/filters.py
+++ /dev/null
@@ -1,719 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.filters
- ~~~~~~~~~~~~~~
-
- Bundled jinja filters.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import re
-import math
-from random import choice
-from operator import itemgetter
-from itertools import imap, groupby
-from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode
-from jinja2.runtime import Undefined
-from jinja2.exceptions import FilterArgumentError, SecurityError
-
-
-_word_re = re.compile(r'\w+(?u)')
-
-
-def contextfilter(f):
- """Decorator for marking context dependent filters. The current
- :class:`Context` will be passed as first argument.
- """
- f.contextfilter = True
- return f
-
-
-def evalcontextfilter(f):
- """Decorator for marking eval-context dependent filters. An eval
- context object is passed as first argument. For more information
- about the eval context, see :ref:`eval-context`.
-
- .. versionadded:: 2.4
- """
- f.evalcontextfilter = True
- return f
-
-
-def environmentfilter(f):
- """Decorator for marking evironment dependent filters. The current
- :class:`Environment` is passed to the filter as first argument.
- """
- f.environmentfilter = True
- return f
-
-
-def do_forceescape(value):
- """Enforce HTML escaping. This will probably double escape variables."""
- if hasattr(value, '__html__'):
- value = value.__html__()
- return escape(unicode(value))
-
-
-@evalcontextfilter
-def do_replace(eval_ctx, s, old, new, count=None):
- """Return a copy of the value with all occurrences of a substring
- replaced with a new one. The first argument is the substring
- that should be replaced, the second is the replacement string.
- If the optional third argument ``count`` is given, only the first
- ``count`` occurrences are replaced:
-
- .. sourcecode:: jinja
-
- {{ "Hello World"|replace("Hello", "Goodbye") }}
- -> Goodbye World
-
- {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
- -> d'oh, d'oh, aaargh
- """
- if count is None:
- count = -1
- if not eval_ctx.autoescape:
- return unicode(s).replace(unicode(old), unicode(new), count)
- if hasattr(old, '__html__') or hasattr(new, '__html__') and \
- not hasattr(s, '__html__'):
- s = escape(s)
- else:
- s = soft_unicode(s)
- return s.replace(soft_unicode(old), soft_unicode(new), count)
-
-
-def do_upper(s):
- """Convert a value to uppercase."""
- return soft_unicode(s).upper()
-
-
-def do_lower(s):
- """Convert a value to lowercase."""
- return soft_unicode(s).lower()
-
-
-@evalcontextfilter
-def do_xmlattr(_eval_ctx, d, autospace=True):
- """Create an SGML/XML attribute string based on the items in a dict.
- All values that are neither `none` nor `undefined` are automatically
- escaped:
-
- .. sourcecode:: html+jinja
-
- <ul{{ {'class': 'my_list', 'missing': none,
- 'id': 'list-%d'|format(variable)}|xmlattr }}>
- ...
- </ul>
-
- Results in something like this:
-
- .. sourcecode:: html
-
- <ul class="my_list" id="list-42">
- ...
- </ul>
-
- As you can see it automatically prepends a space in front of the item
- if the filter returned something unless the second parameter is false.
- """
- rv = u' '.join(
- u'%s="%s"' % (escape(key), escape(value))
- for key, value in d.iteritems()
- if value is not None and not isinstance(value, Undefined)
- )
- if autospace and rv:
- rv = u' ' + rv
- if _eval_ctx.autoescape:
- rv = Markup(rv)
- return rv
-
-
-def do_capitalize(s):
- """Capitalize a value. The first character will be uppercase, all others
- lowercase.
- """
- return soft_unicode(s).capitalize()
-
-
-def do_title(s):
- """Return a titlecased version of the value. I.e. words will start with
- uppercase letters, all remaining characters are lowercase.
- """
- return soft_unicode(s).title()
-
-
-def do_dictsort(value, case_sensitive=False, by='key'):
- """Sort a dict and yield (key, value) pairs. Because python dicts are
- unsorted you may want to use this function to order them by either
- key or value:
-
- .. sourcecode:: jinja
-
- {% for item in mydict|dictsort %}
- sort the dict by key, case insensitive
-
- {% for item in mydict|dicsort(true) %}
- sort the dict by key, case sensitive
-
- {% for item in mydict|dictsort(false, 'value') %}
- sort the dict by key, case insensitive, sorted
- normally and ordered by value.
- """
- if by == 'key':
- pos = 0
- elif by == 'value':
- pos = 1
- else:
- raise FilterArgumentError('You can only sort by either '
- '"key" or "value"')
- def sort_func(item):
- value = item[pos]
- if isinstance(value, basestring) and not case_sensitive:
- value = value.lower()
- return value
-
- return sorted(value.items(), key=sort_func)
-
-
-def do_sort(value, reverse=False, case_sensitive=False):
- """Sort an iterable. Per default it sorts ascending, if you pass it
- true as first argument it will reverse the sorting.
-
- If the iterable is made of strings the third parameter can be used to
- control the case sensitiveness of the comparison which is disabled by
- default.
-
- .. sourcecode:: jinja
-
- {% for item in iterable|sort %}
- ...
- {% endfor %}
- """
- if not case_sensitive:
- def sort_func(item):
- if isinstance(item, basestring):
- item = item.lower()
- return item
- else:
- sort_func = None
- return sorted(value, key=sort_func, reverse=reverse)
-
-
-def do_default(value, default_value=u'', boolean=False):
- """If the value is undefined it will return the passed default value,
- otherwise the value of the variable:
-
- .. sourcecode:: jinja
-
- {{ my_variable|default('my_variable is not defined') }}
-
- This will output the value of ``my_variable`` if the variable was
- defined, otherwise ``'my_variable is not defined'``. If you want
- to use default with variables that evaluate to false you have to
- set the second parameter to `true`:
-
- .. sourcecode:: jinja
-
- {{ ''|default('the string was empty', true) }}
- """
- if (boolean and not value) or isinstance(value, Undefined):
- return default_value
- return value
-
-
-@evalcontextfilter
-def do_join(eval_ctx, value, d=u''):
- """Return a string which is the concatenation of the strings in the
- sequence. The separator between elements is an empty string per
- default, you can define it with the optional parameter:
-
- .. sourcecode:: jinja
-
- {{ [1, 2, 3]|join('|') }}
- -> 1|2|3
-
- {{ [1, 2, 3]|join }}
- -> 123
- """
- # no automatic escaping? joining is a lot eaiser then
- if not eval_ctx.autoescape:
- return unicode(d).join(imap(unicode, value))
-
- # if the delimiter doesn't have an html representation we check
- # if any of the items has. If yes we do a coercion to Markup
- if not hasattr(d, '__html__'):
- value = list(value)
- do_escape = False
- for idx, item in enumerate(value):
- if hasattr(item, '__html__'):
- do_escape = True
- else:
- value[idx] = unicode(item)
- if do_escape:
- d = escape(d)
- else:
- d = unicode(d)
- return d.join(value)
-
- # no html involved, to normal joining
- return soft_unicode(d).join(imap(soft_unicode, value))
-
-
-def do_center(value, width=80):
- """Centers the value in a field of a given width."""
- return unicode(value).center(width)
-
-
-@environmentfilter
-def do_first(environment, seq):
- """Return the first item of a sequence."""
- try:
- return iter(seq).next()
- except StopIteration:
- return environment.undefined('No first item, sequence was empty.')
-
-
-@environmentfilter
-def do_last(environment, seq):
- """Return the last item of a sequence."""
- try:
- return iter(reversed(seq)).next()
- except StopIteration:
- return environment.undefined('No last item, sequence was empty.')
-
-
-@environmentfilter
-def do_random(environment, seq):
- """Return a random item from the sequence."""
- try:
- return choice(seq)
- except IndexError:
- return environment.undefined('No random item, sequence was empty.')
-
-
-def do_filesizeformat(value, binary=False):
- """Format the value like a 'human-readable' file size (i.e. 13 KB,
- 4.1 MB, 102 bytes, etc). Per default decimal prefixes are used (mega,
- giga, etc.), if the second parameter is set to `True` the binary
- prefixes are used (mebi, gibi).
- """
- bytes = float(value)
- base = binary and 1024 or 1000
- middle = binary and 'i' or ''
- if bytes < base:
- return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
- elif bytes < base * base:
- return "%.1f K%sB" % (bytes / base, middle)
- elif bytes < base * base * base:
- return "%.1f M%sB" % (bytes / (base * base), middle)
- return "%.1f G%sB" % (bytes / (base * base * base), middle)
-
-
-def do_pprint(value, verbose=False):
- """Pretty print a variable. Useful for debugging.
-
- With Jinja 1.2 onwards you can pass it a parameter. If this parameter
- is truthy the output will be more verbose (this requires `pretty`)
- """
- return pformat(value, verbose=verbose)
-
-
-@evalcontextfilter
-def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False):
- """Converts URLs in plain text into clickable links.
-
- If you pass the filter an additional integer it will shorten the urls
- to that number. Also a third argument exists that makes the urls
- "nofollow":
-
- .. sourcecode:: jinja
-
- {{ mytext|urlize(40, true) }}
- links are shortened to 40 chars and defined with rel="nofollow"
- """
- rv = urlize(value, trim_url_limit, nofollow)
- if eval_ctx.autoescape:
- rv = Markup(rv)
- return rv
-
-
-def do_indent(s, width=4, indentfirst=False):
- """Return a copy of the passed string, each line indented by
- 4 spaces. The first line is not indented. If you want to
- change the number of spaces or indent the first line too
- you can pass additional parameters to the filter:
-
- .. sourcecode:: jinja
-
- {{ mytext|indent(2, true) }}
- indent by two spaces and indent the first line too.
- """
- indention = u' ' * width
- rv = (u'\n' + indention).join(s.splitlines())
- if indentfirst:
- rv = indention + rv
- return rv
-
-
-def do_truncate(s, length=255, killwords=False, end='...'):
- """Return a truncated copy of the string. The length is specified
- with the first parameter which defaults to ``255``. If the second
- parameter is ``true`` the filter will cut the text at length. Otherwise
- it will try to save the last word. If the text was in fact
- truncated it will append an ellipsis sign (``"..."``). If you want a
- different ellipsis sign than ``"..."`` you can specify it using the
- third parameter.
-
- .. sourcecode jinja::
-
- {{ mytext|truncate(300, false, '&raquo;') }}
- truncate mytext to 300 chars, don't split up words, use a
- right pointing double arrow as ellipsis sign.
- """
- if len(s) <= length:
- return s
- elif killwords:
- return s[:length] + end
- words = s.split(' ')
- result = []
- m = 0
- for word in words:
- m += len(word) + 1
- if m > length:
- break
- result.append(word)
- result.append(end)
- return u' '.join(result)
-
-
-def do_wordwrap(s, width=79, break_long_words=True):
- """
- Return a copy of the string passed to the filter wrapped after
- ``79`` characters. You can override this default using the first
- parameter. If you set the second parameter to `false` Jinja will not
- split words apart if they are longer than `width`.
- """
- import textwrap
- return u'\n'.join(textwrap.wrap(s, width=width, expand_tabs=False,
- replace_whitespace=False,
- break_long_words=break_long_words))
-
-
-def do_wordcount(s):
- """Count the words in that string."""
- return len(_word_re.findall(s))
-
-
-def do_int(value, default=0):
- """Convert the value into an integer. If the
- conversion doesn't work it will return ``0``. You can
- override this default using the first parameter.
- """
- try:
- return int(value)
- except (TypeError, ValueError):
- # this quirk is necessary so that "42.23"|int gives 42.
- try:
- return int(float(value))
- except (TypeError, ValueError):
- return default
-
-
-def do_float(value, default=0.0):
- """Convert the value into a floating point number. If the
- conversion doesn't work it will return ``0.0``. You can
- override this default using the first parameter.
- """
- try:
- return float(value)
- except (TypeError, ValueError):
- return default
-
-
-def do_format(value, *args, **kwargs):
- """
- Apply python string formatting on an object:
-
- .. sourcecode:: jinja
-
- {{ "%s - %s"|format("Hello?", "Foo!") }}
- -> Hello? - Foo!
- """
- if args and kwargs:
- raise FilterArgumentError('can\'t handle positional and keyword '
- 'arguments at the same time')
- return soft_unicode(value) % (kwargs or args)
-
-
-def do_trim(value):
- """Strip leading and trailing whitespace."""
- return soft_unicode(value).strip()
-
-
-def do_striptags(value):
- """Strip SGML/XML tags and replace adjacent whitespace by one space.
- """
- if hasattr(value, '__html__'):
- value = value.__html__()
- return Markup(unicode(value)).striptags()
-
-
-def do_slice(value, slices, fill_with=None):
- """Slice an iterator and return a list of lists containing
- those items. Useful if you want to create a div containing
- three ul tags that represent columns:
-
- .. sourcecode:: html+jinja
-
- <div class="columwrapper">
- {%- for column in items|slice(3) %}
- <ul class="column-{{ loop.index }}">
- {%- for item in column %}
- <li>{{ item }}</li>
- {%- endfor %}
- </ul>
- {%- endfor %}
- </div>
-
- If you pass it a second argument it's used to fill missing
- values on the last iteration.
- """
- seq = list(value)
- length = len(seq)
- items_per_slice = length // slices
- slices_with_extra = length % slices
- offset = 0
- for slice_number in xrange(slices):
- start = offset + slice_number * items_per_slice
- if slice_number < slices_with_extra:
- offset += 1
- end = offset + (slice_number + 1) * items_per_slice
- tmp = seq[start:end]
- if fill_with is not None and slice_number >= slices_with_extra:
- tmp.append(fill_with)
- yield tmp
-
-
-def do_batch(value, linecount, fill_with=None):
- """
- A filter that batches items. It works pretty much like `slice`
- just the other way round. It returns a list of lists with the
- given number of items. If you provide a second parameter this
- is used to fill missing items. See this example:
-
- .. sourcecode:: html+jinja
-
- <table>
- {%- for row in items|batch(3, '&nbsp;') %}
- <tr>
- {%- for column in row %}
- <td>{{ column }}</td>
- {%- endfor %}
- </tr>
- {%- endfor %}
- </table>
- """
- result = []
- tmp = []
- for item in value:
- if len(tmp) == linecount:
- yield tmp
- tmp = []
- tmp.append(item)
- if tmp:
- if fill_with is not None and len(tmp) < linecount:
- tmp += [fill_with] * (linecount - len(tmp))
- yield tmp
-
-
-def do_round(value, precision=0, method='common'):
- """Round the number to a given precision. The first
- parameter specifies the precision (default is ``0``), the
- second the rounding method:
-
- - ``'common'`` rounds either up or down
- - ``'ceil'`` always rounds up
- - ``'floor'`` always rounds down
-
- If you don't specify a method ``'common'`` is used.
-
- .. sourcecode:: jinja
-
- {{ 42.55|round }}
- -> 43.0
- {{ 42.55|round(1, 'floor') }}
- -> 42.5
-
- Note that even if rounded to 0 precision, a float is returned. If
- you need a real integer, pipe it through `int`:
-
- .. sourcecode:: jinja
-
- {{ 42.55|round|int }}
- -> 43
- """
- if not method in ('common', 'ceil', 'floor'):
- raise FilterArgumentError('method must be common, ceil or floor')
- if method == 'common':
- return round(value, precision)
- func = getattr(math, method)
- return func(value * (10 ** precision)) / (10 ** precision)
-
-
-@environmentfilter
-def do_groupby(environment, value, attribute):
- """Group a sequence of objects by a common attribute.
-
- If you for example have a list of dicts or objects that represent persons
- with `gender`, `first_name` and `last_name` attributes and you want to
- group all users by genders you can do something like the following
- snippet:
-
- .. sourcecode:: html+jinja
-
- <ul>
- {% for group in persons|groupby('gender') %}
- <li>{{ group.grouper }}<ul>
- {% for person in group.list %}
- <li>{{ person.first_name }} {{ person.last_name }}</li>
- {% endfor %}</ul></li>
- {% endfor %}
- </ul>
-
- Additionally it's possible to use tuple unpacking for the grouper and
- list:
-
- .. sourcecode:: html+jinja
-
- <ul>
- {% for grouper, list in persons|groupby('gender') %}
- ...
- {% endfor %}
- </ul>
-
- As you can see the item we're grouping by is stored in the `grouper`
- attribute and the `list` contains all the objects that have this grouper
- in common.
- """
- expr = lambda x: environment.getitem(x, attribute)
- return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
-
-
-class _GroupTuple(tuple):
- __slots__ = ()
- grouper = property(itemgetter(0))
- list = property(itemgetter(1))
-
- def __new__(cls, (key, value)):
- return tuple.__new__(cls, (key, list(value)))
-
-
-def do_list(value):
- """Convert the value into a list. If it was a string the returned list
- will be a list of characters.
- """
- return list(value)
-
-
-def do_mark_safe(value):
- """Mark the value as safe which means that in an environment with automatic
- escaping enabled this variable will not be escaped.
- """
- return Markup(value)
-
-
-def do_mark_unsafe(value):
- """Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
- return unicode(value)
-
-
-def do_reverse(value):
- """Reverse the object or return an iterator the iterates over it the other
- way round.
- """
- if isinstance(value, basestring):
- return value[::-1]
- try:
- return reversed(value)
- except TypeError:
- try:
- rv = list(value)
- rv.reverse()
- return rv
- except TypeError:
- raise FilterArgumentError('argument must be iterable')
-
-
-@environmentfilter
-def do_attr(environment, obj, name):
- """Get an attribute of an object. ``foo|attr("bar")`` works like
- ``foo["bar"]`` just that always an attribute is returned and items are not
- looked up.
-
- See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
- """
- try:
- name = str(name)
- except UnicodeError:
- pass
- else:
- try:
- value = getattr(obj, name)
- except AttributeError:
- pass
- else:
- if environment.sandboxed and not \
- environment.is_safe_attribute(obj, name, value):
- return environment.unsafe_undefined(obj, name)
- return value
- return environment.undefined(obj=obj, name=name)
-
-
-FILTERS = {
- 'attr': do_attr,
- 'replace': do_replace,
- 'upper': do_upper,
- 'lower': do_lower,
- 'escape': escape,
- 'e': escape,
- 'forceescape': do_forceescape,
- 'capitalize': do_capitalize,
- 'title': do_title,
- 'default': do_default,
- 'd': do_default,
- 'join': do_join,
- 'count': len,
- 'dictsort': do_dictsort,
- 'sort': do_sort,
- 'length': len,
- 'reverse': do_reverse,
- 'center': do_center,
- 'indent': do_indent,
- 'title': do_title,
- 'capitalize': do_capitalize,
- 'first': do_first,
- 'last': do_last,
- 'random': do_random,
- 'filesizeformat': do_filesizeformat,
- 'pprint': do_pprint,
- 'truncate': do_truncate,
- 'wordwrap': do_wordwrap,
- 'wordcount': do_wordcount,
- 'int': do_int,
- 'float': do_float,
- 'string': soft_unicode,
- 'list': do_list,
- 'urlize': do_urlize,
- 'format': do_format,
- 'trim': do_trim,
- 'striptags': do_striptags,
- 'slice': do_slice,
- 'batch': do_batch,
- 'sum': sum,
- 'abs': abs,
- 'round': do_round,
- 'groupby': do_groupby,
- 'safe': do_mark_safe,
- 'xmlattr': do_xmlattr
-}
diff --git a/module/lib/jinja2/lexer.py b/module/lib/jinja2/lexer.py
deleted file mode 100644
index 0d3f69617..000000000
--- a/module/lib/jinja2/lexer.py
+++ /dev/null
@@ -1,681 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.lexer
- ~~~~~~~~~~~~
-
- This module implements a Jinja / Python combination lexer. The
- `Lexer` class provided by this module is used to do some preprocessing
- for Jinja.
-
- On the one hand it filters out invalid operators like the bitshift
- operators we don't allow in templates. On the other hand it separates
- template code and python code in expressions.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import re
-from operator import itemgetter
-from collections import deque
-from jinja2.exceptions import TemplateSyntaxError
-from jinja2.utils import LRUCache, next
-
-
-# cache for the lexers. Exists in order to be able to have multiple
-# environments with the same lexer
-_lexer_cache = LRUCache(50)
-
-# static regular expressions
-whitespace_re = re.compile(r'\s+', re.U)
-string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
- r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
-integer_re = re.compile(r'\d+')
-
-# we use the unicode identifier rule if this python version is able
-# to handle unicode identifiers, otherwise the standard ASCII one.
-try:
- compile('föö', '<unknown>', 'eval')
-except SyntaxError:
- name_re = re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b')
-else:
- from jinja2 import _stringdefs
- name_re = re.compile(r'[%s][%s]*' % (_stringdefs.xid_start,
- _stringdefs.xid_continue))
-
-float_re = re.compile(r'(?<!\.)\d+\.\d+')
-newline_re = re.compile(r'(\r\n|\r|\n)')
-
-# internal the tokens and keep references to them
-TOKEN_ADD = intern('add')
-TOKEN_ASSIGN = intern('assign')
-TOKEN_COLON = intern('colon')
-TOKEN_COMMA = intern('comma')
-TOKEN_DIV = intern('div')
-TOKEN_DOT = intern('dot')
-TOKEN_EQ = intern('eq')
-TOKEN_FLOORDIV = intern('floordiv')
-TOKEN_GT = intern('gt')
-TOKEN_GTEQ = intern('gteq')
-TOKEN_LBRACE = intern('lbrace')
-TOKEN_LBRACKET = intern('lbracket')
-TOKEN_LPAREN = intern('lparen')
-TOKEN_LT = intern('lt')
-TOKEN_LTEQ = intern('lteq')
-TOKEN_MOD = intern('mod')
-TOKEN_MUL = intern('mul')
-TOKEN_NE = intern('ne')
-TOKEN_PIPE = intern('pipe')
-TOKEN_POW = intern('pow')
-TOKEN_RBRACE = intern('rbrace')
-TOKEN_RBRACKET = intern('rbracket')
-TOKEN_RPAREN = intern('rparen')
-TOKEN_SEMICOLON = intern('semicolon')
-TOKEN_SUB = intern('sub')
-TOKEN_TILDE = intern('tilde')
-TOKEN_WHITESPACE = intern('whitespace')
-TOKEN_FLOAT = intern('float')
-TOKEN_INTEGER = intern('integer')
-TOKEN_NAME = intern('name')
-TOKEN_STRING = intern('string')
-TOKEN_OPERATOR = intern('operator')
-TOKEN_BLOCK_BEGIN = intern('block_begin')
-TOKEN_BLOCK_END = intern('block_end')
-TOKEN_VARIABLE_BEGIN = intern('variable_begin')
-TOKEN_VARIABLE_END = intern('variable_end')
-TOKEN_RAW_BEGIN = intern('raw_begin')
-TOKEN_RAW_END = intern('raw_end')
-TOKEN_COMMENT_BEGIN = intern('comment_begin')
-TOKEN_COMMENT_END = intern('comment_end')
-TOKEN_COMMENT = intern('comment')
-TOKEN_LINESTATEMENT_BEGIN = intern('linestatement_begin')
-TOKEN_LINESTATEMENT_END = intern('linestatement_end')
-TOKEN_LINECOMMENT_BEGIN = intern('linecomment_begin')
-TOKEN_LINECOMMENT_END = intern('linecomment_end')
-TOKEN_LINECOMMENT = intern('linecomment')
-TOKEN_DATA = intern('data')
-TOKEN_INITIAL = intern('initial')
-TOKEN_EOF = intern('eof')
-
-# bind operators to token types
-operators = {
- '+': TOKEN_ADD,
- '-': TOKEN_SUB,
- '/': TOKEN_DIV,
- '//': TOKEN_FLOORDIV,
- '*': TOKEN_MUL,
- '%': TOKEN_MOD,
- '**': TOKEN_POW,
- '~': TOKEN_TILDE,
- '[': TOKEN_LBRACKET,
- ']': TOKEN_RBRACKET,
- '(': TOKEN_LPAREN,
- ')': TOKEN_RPAREN,
- '{': TOKEN_LBRACE,
- '}': TOKEN_RBRACE,
- '==': TOKEN_EQ,
- '!=': TOKEN_NE,
- '>': TOKEN_GT,
- '>=': TOKEN_GTEQ,
- '<': TOKEN_LT,
- '<=': TOKEN_LTEQ,
- '=': TOKEN_ASSIGN,
- '.': TOKEN_DOT,
- ':': TOKEN_COLON,
- '|': TOKEN_PIPE,
- ',': TOKEN_COMMA,
- ';': TOKEN_SEMICOLON
-}
-
-reverse_operators = dict([(v, k) for k, v in operators.iteritems()])
-assert len(operators) == len(reverse_operators), 'operators dropped'
-operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in
- sorted(operators, key=lambda x: -len(x))))
-
-ignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT,
- TOKEN_COMMENT_END, TOKEN_WHITESPACE,
- TOKEN_WHITESPACE, TOKEN_LINECOMMENT_BEGIN,
- TOKEN_LINECOMMENT_END, TOKEN_LINECOMMENT])
-ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA,
- TOKEN_COMMENT, TOKEN_LINECOMMENT])
-
-
-def _describe_token_type(token_type):
- if token_type in reverse_operators:
- return reverse_operators[token_type]
- return {
- TOKEN_COMMENT_BEGIN: 'begin of comment',
- TOKEN_COMMENT_END: 'end of comment',
- TOKEN_COMMENT: 'comment',
- TOKEN_LINECOMMENT: 'comment',
- TOKEN_BLOCK_BEGIN: 'begin of statement block',
- TOKEN_BLOCK_END: 'end of statement block',
- TOKEN_VARIABLE_BEGIN: 'begin of print statement',
- TOKEN_VARIABLE_END: 'end of print statement',
- TOKEN_LINESTATEMENT_BEGIN: 'begin of line statement',
- TOKEN_LINESTATEMENT_END: 'end of line statement',
- TOKEN_DATA: 'template data / text',
- TOKEN_EOF: 'end of template'
- }.get(token_type, token_type)
-
-
-def describe_token(token):
- """Returns a description of the token."""
- if token.type == 'name':
- return token.value
- return _describe_token_type(token.type)
-
-
-def describe_token_expr(expr):
- """Like `describe_token` but for token expressions."""
- if ':' in expr:
- type, value = expr.split(':', 1)
- if type == 'name':
- return value
- else:
- type = expr
- return _describe_token_type(type)
-
-
-def count_newlines(value):
- """Count the number of newline characters in the string. This is
- useful for extensions that filter a stream.
- """
- return len(newline_re.findall(value))
-
-
-def compile_rules(environment):
- """Compiles all the rules from the environment into a list of rules."""
- e = re.escape
- rules = [
- (len(environment.comment_start_string), 'comment',
- e(environment.comment_start_string)),
- (len(environment.block_start_string), 'block',
- e(environment.block_start_string)),
- (len(environment.variable_start_string), 'variable',
- e(environment.variable_start_string))
- ]
-
- if environment.line_statement_prefix is not None:
- rules.append((len(environment.line_statement_prefix), 'linestatement',
- r'^\s*' + e(environment.line_statement_prefix)))
- if environment.line_comment_prefix is not None:
- rules.append((len(environment.line_comment_prefix), 'linecomment',
- r'(?:^|(?<=\S))[^\S\r\n]*' +
- e(environment.line_comment_prefix)))
-
- return [x[1:] for x in sorted(rules, reverse=True)]
-
-
-class Failure(object):
- """Class that raises a `TemplateSyntaxError` if called.
- Used by the `Lexer` to specify known errors.
- """
-
- def __init__(self, message, cls=TemplateSyntaxError):
- self.message = message
- self.error_class = cls
-
- def __call__(self, lineno, filename):
- raise self.error_class(self.message, lineno, filename)
-
-
-class Token(tuple):
- """Token class."""
- __slots__ = ()
- lineno, type, value = (property(itemgetter(x)) for x in range(3))
-
- def __new__(cls, lineno, type, value):
- return tuple.__new__(cls, (lineno, intern(str(type)), value))
-
- def __str__(self):
- if self.type in reverse_operators:
- return reverse_operators[self.type]
- elif self.type == 'name':
- return self.value
- return self.type
-
- def test(self, expr):
- """Test a token against a token expression. This can either be a
- token type or ``'token_type:token_value'``. This can only test
- against string values and types.
- """
- # here we do a regular string equality check as test_any is usually
- # passed an iterable of not interned strings.
- if self.type == expr:
- return True
- elif ':' in expr:
- return expr.split(':', 1) == [self.type, self.value]
- return False
-
- def test_any(self, *iterable):
- """Test against multiple token expressions."""
- for expr in iterable:
- if self.test(expr):
- return True
- return False
-
- def __repr__(self):
- return 'Token(%r, %r, %r)' % (
- self.lineno,
- self.type,
- self.value
- )
-
-
-class TokenStreamIterator(object):
- """The iterator for tokenstreams. Iterate over the stream
- until the eof token is reached.
- """
-
- def __init__(self, stream):
- self.stream = stream
-
- def __iter__(self):
- return self
-
- def next(self):
- token = self.stream.current
- if token.type is TOKEN_EOF:
- self.stream.close()
- raise StopIteration()
- next(self.stream)
- return token
-
-
-class TokenStream(object):
- """A token stream is an iterable that yields :class:`Token`\s. The
- parser however does not iterate over it but calls :meth:`next` to go
- one token ahead. The current active token is stored as :attr:`current`.
- """
-
- def __init__(self, generator, name, filename):
- self._next = iter(generator).next
- self._pushed = deque()
- self.name = name
- self.filename = filename
- self.closed = False
- self.current = Token(1, TOKEN_INITIAL, '')
- next(self)
-
- def __iter__(self):
- return TokenStreamIterator(self)
-
- def __nonzero__(self):
- return bool(self._pushed) or self.current.type is not TOKEN_EOF
-
- eos = property(lambda x: not x, doc="Are we at the end of the stream?")
-
- def push(self, token):
- """Push a token back to the stream."""
- self._pushed.append(token)
-
- def look(self):
- """Look at the next token."""
- old_token = next(self)
- result = self.current
- self.push(result)
- self.current = old_token
- return result
-
- def skip(self, n=1):
- """Got n tokens ahead."""
- for x in xrange(n):
- next(self)
-
- def next_if(self, expr):
- """Perform the token test and return the token if it matched.
- Otherwise the return value is `None`.
- """
- if self.current.test(expr):
- return next(self)
-
- def skip_if(self, expr):
- """Like :meth:`next_if` but only returns `True` or `False`."""
- return self.next_if(expr) is not None
-
- def next(self):
- """Go one token ahead and return the old one"""
- rv = self.current
- if self._pushed:
- self.current = self._pushed.popleft()
- elif self.current.type is not TOKEN_EOF:
- try:
- self.current = self._next()
- except StopIteration:
- self.close()
- return rv
-
- def close(self):
- """Close the stream."""
- self.current = Token(self.current.lineno, TOKEN_EOF, '')
- self._next = None
- self.closed = True
-
- def expect(self, expr):
- """Expect a given token type and return it. This accepts the same
- argument as :meth:`jinja2.lexer.Token.test`.
- """
- if not self.current.test(expr):
- expr = describe_token_expr(expr)
- if self.current.type is TOKEN_EOF:
- raise TemplateSyntaxError('unexpected end of template, '
- 'expected %r.' % expr,
- self.current.lineno,
- self.name, self.filename)
- raise TemplateSyntaxError("expected token %r, got %r" %
- (expr, describe_token(self.current)),
- self.current.lineno,
- self.name, self.filename)
- try:
- return self.current
- finally:
- next(self)
-
-
-def get_lexer(environment):
- """Return a lexer which is probably cached."""
- key = (environment.block_start_string,
- environment.block_end_string,
- environment.variable_start_string,
- environment.variable_end_string,
- environment.comment_start_string,
- environment.comment_end_string,
- environment.line_statement_prefix,
- environment.line_comment_prefix,
- environment.trim_blocks,
- environment.newline_sequence)
- lexer = _lexer_cache.get(key)
- if lexer is None:
- lexer = Lexer(environment)
- _lexer_cache[key] = lexer
- return lexer
-
-
-class Lexer(object):
- """Class that implements a lexer for a given environment. Automatically
- created by the environment class, usually you don't have to do that.
-
- Note that the lexer is not automatically bound to an environment.
- Multiple environments can share the same lexer.
- """
-
- def __init__(self, environment):
- # shortcuts
- c = lambda x: re.compile(x, re.M | re.S)
- e = re.escape
-
- # lexing rules for tags
- tag_rules = [
- (whitespace_re, TOKEN_WHITESPACE, None),
- (float_re, TOKEN_FLOAT, None),
- (integer_re, TOKEN_INTEGER, None),
- (name_re, TOKEN_NAME, None),
- (string_re, TOKEN_STRING, None),
- (operator_re, TOKEN_OPERATOR, None)
- ]
-
- # assamble the root lexing rule. because "|" is ungreedy
- # we have to sort by length so that the lexer continues working
- # as expected when we have parsing rules like <% for block and
- # <%= for variables. (if someone wants asp like syntax)
- # variables are just part of the rules if variable processing
- # is required.
- root_tag_rules = compile_rules(environment)
-
- # block suffix if trimming is enabled
- block_suffix_re = environment.trim_blocks and '\\n?' or ''
-
- self.newline_sequence = environment.newline_sequence
-
- # global lexing rules
- self.rules = {
- 'root': [
- # directives
- (c('(.*?)(?:%s)' % '|'.join(
- [r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % (
- e(environment.block_start_string),
- e(environment.block_start_string),
- e(environment.block_end_string),
- e(environment.block_end_string)
- )] + [
- r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, r)
- for n, r in root_tag_rules
- ])), (TOKEN_DATA, '#bygroup'), '#bygroup'),
- # data
- (c('.+'), TOKEN_DATA, None)
- ],
- # comments
- TOKEN_COMMENT_BEGIN: [
- (c(r'(.*?)((?:\-%s\s*|%s)%s)' % (
- e(environment.comment_end_string),
- e(environment.comment_end_string),
- block_suffix_re
- )), (TOKEN_COMMENT, TOKEN_COMMENT_END), '#pop'),
- (c('(.)'), (Failure('Missing end of comment tag'),), None)
- ],
- # blocks
- TOKEN_BLOCK_BEGIN: [
- (c('(?:\-%s\s*|%s)%s' % (
- e(environment.block_end_string),
- e(environment.block_end_string),
- block_suffix_re
- )), TOKEN_BLOCK_END, '#pop'),
- ] + tag_rules,
- # variables
- TOKEN_VARIABLE_BEGIN: [
- (c('\-%s\s*|%s' % (
- e(environment.variable_end_string),
- e(environment.variable_end_string)
- )), TOKEN_VARIABLE_END, '#pop')
- ] + tag_rules,
- # raw block
- TOKEN_RAW_BEGIN: [
- (c('(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % (
- e(environment.block_start_string),
- e(environment.block_start_string),
- e(environment.block_end_string),
- e(environment.block_end_string),
- block_suffix_re
- )), (TOKEN_DATA, TOKEN_RAW_END), '#pop'),
- (c('(.)'), (Failure('Missing end of raw directive'),), None)
- ],
- # line statements
- TOKEN_LINESTATEMENT_BEGIN: [
- (c(r'\s*(\n|$)'), TOKEN_LINESTATEMENT_END, '#pop')
- ] + tag_rules,
- # line comments
- TOKEN_LINECOMMENT_BEGIN: [
- (c(r'(.*?)()(?=\n|$)'), (TOKEN_LINECOMMENT,
- TOKEN_LINECOMMENT_END), '#pop')
- ]
- }
-
- def _normalize_newlines(self, value):
- """Called for strings and template data to normlize it to unicode."""
- return newline_re.sub(self.newline_sequence, value)
-
- def tokenize(self, source, name=None, filename=None, state=None):
- """Calls tokeniter + tokenize and wraps it in a token stream.
- """
- stream = self.tokeniter(source, name, filename, state)
- return TokenStream(self.wrap(stream, name, filename), name, filename)
-
- def wrap(self, stream, name=None, filename=None):
- """This is called with the stream as returned by `tokenize` and wraps
- every token in a :class:`Token` and converts the value.
- """
- for lineno, token, value in stream:
- if token in ignored_tokens:
- continue
- elif token == 'linestatement_begin':
- token = 'block_begin'
- elif token == 'linestatement_end':
- token = 'block_end'
- # we are not interested in those tokens in the parser
- elif token in ('raw_begin', 'raw_end'):
- continue
- elif token == 'data':
- value = self._normalize_newlines(value)
- elif token == 'keyword':
- token = value
- elif token == 'name':
- value = str(value)
- elif token == 'string':
- # try to unescape string
- try:
- value = self._normalize_newlines(value[1:-1]) \
- .encode('ascii', 'backslashreplace') \
- .decode('unicode-escape')
- except Exception, e:
- msg = str(e).split(':')[-1].strip()
- raise TemplateSyntaxError(msg, lineno, name, filename)
- # if we can express it as bytestring (ascii only)
- # we do that for support of semi broken APIs
- # as datetime.datetime.strftime. On python 3 this
- # call becomes a noop thanks to 2to3
- try:
- value = str(value)
- except UnicodeError:
- pass
- elif token == 'integer':
- value = int(value)
- elif token == 'float':
- value = float(value)
- elif token == 'operator':
- token = operators[value]
- yield Token(lineno, token, value)
-
- def tokeniter(self, source, name, filename=None, state=None):
- """This method tokenizes the text and returns the tokens in a
- generator. Use this method if you just want to tokenize a template.
- """
- source = '\n'.join(unicode(source).splitlines())
- pos = 0
- lineno = 1
- stack = ['root']
- if state is not None and state != 'root':
- assert state in ('variable', 'block'), 'invalid state'
- stack.append(state + '_begin')
- else:
- state = 'root'
- statetokens = self.rules[stack[-1]]
- source_length = len(source)
-
- balancing_stack = []
-
- while 1:
- # tokenizer loop
- for regex, tokens, new_state in statetokens:
- m = regex.match(source, pos)
- # if no match we try again with the next rule
- if m is None:
- continue
-
- # we only match blocks and variables if brances / parentheses
- # are balanced. continue parsing with the lower rule which
- # is the operator rule. do this only if the end tags look
- # like operators
- if balancing_stack and \
- tokens in ('variable_end', 'block_end',
- 'linestatement_end'):
- continue
-
- # tuples support more options
- if isinstance(tokens, tuple):
- for idx, token in enumerate(tokens):
- # failure group
- if token.__class__ is Failure:
- raise token(lineno, filename)
- # bygroup is a bit more complex, in that case we
- # yield for the current token the first named
- # group that matched
- elif token == '#bygroup':
- for key, value in m.groupdict().iteritems():
- if value is not None:
- yield lineno, key, value
- lineno += value.count('\n')
- break
- else:
- raise RuntimeError('%r wanted to resolve '
- 'the token dynamically'
- ' but no group matched'
- % regex)
- # normal group
- else:
- data = m.group(idx + 1)
- if data or token not in ignore_if_empty:
- yield lineno, token, data
- lineno += data.count('\n')
-
- # strings as token just are yielded as it.
- else:
- data = m.group()
- # update brace/parentheses balance
- if tokens == 'operator':
- if data == '{':
- balancing_stack.append('}')
- elif data == '(':
- balancing_stack.append(')')
- elif data == '[':
- balancing_stack.append(']')
- elif data in ('}', ')', ']'):
- if not balancing_stack:
- raise TemplateSyntaxError('unexpected \'%s\'' %
- data, lineno, name,
- filename)
- expected_op = balancing_stack.pop()
- if expected_op != data:
- raise TemplateSyntaxError('unexpected \'%s\', '
- 'expected \'%s\'' %
- (data, expected_op),
- lineno, name,
- filename)
- # yield items
- if data or tokens not in ignore_if_empty:
- yield lineno, tokens, data
- lineno += data.count('\n')
-
- # fetch new position into new variable so that we can check
- # if there is a internal parsing error which would result
- # in an infinite loop
- pos2 = m.end()
-
- # handle state changes
- if new_state is not None:
- # remove the uppermost state
- if new_state == '#pop':
- stack.pop()
- # resolve the new state by group checking
- elif new_state == '#bygroup':
- for key, value in m.groupdict().iteritems():
- if value is not None:
- stack.append(key)
- break
- else:
- raise RuntimeError('%r wanted to resolve the '
- 'new state dynamically but'
- ' no group matched' %
- regex)
- # direct state name given
- else:
- stack.append(new_state)
- statetokens = self.rules[stack[-1]]
- # we are still at the same position and no stack change.
- # this means a loop without break condition, avoid that and
- # raise error
- elif pos2 == pos:
- raise RuntimeError('%r yielded empty string without '
- 'stack change' % regex)
- # publish new function and start again
- pos = pos2
- break
- # if loop terminated without break we havn't found a single match
- # either we are at the end of the file or we have a problem
- else:
- # end of text
- if pos >= source_length:
- return
- # something went wrong
- raise TemplateSyntaxError('unexpected char %r at %d' %
- (source[pos], pos), lineno,
- name, filename)
diff --git a/module/lib/jinja2/loaders.py b/module/lib/jinja2/loaders.py
deleted file mode 100644
index bd435e8b0..000000000
--- a/module/lib/jinja2/loaders.py
+++ /dev/null
@@ -1,449 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.loaders
- ~~~~~~~~~~~~~~
-
- Jinja loader classes.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import os
-import sys
-import weakref
-from types import ModuleType
-from os import path
-try:
- from hashlib import sha1
-except ImportError:
- from sha import new as sha1
-from jinja2.exceptions import TemplateNotFound
-from jinja2.utils import LRUCache, open_if_exists, internalcode
-
-
-def split_template_path(template):
- """Split a path into segments and perform a sanity check. If it detects
- '..' in the path it will raise a `TemplateNotFound` error.
- """
- pieces = []
- for piece in template.split('/'):
- if path.sep in piece \
- or (path.altsep and path.altsep in piece) or \
- piece == path.pardir:
- raise TemplateNotFound(template)
- elif piece and piece != '.':
- pieces.append(piece)
- return pieces
-
-
-class BaseLoader(object):
- """Baseclass for all loaders. Subclass this and override `get_source` to
- implement a custom loading mechanism. The environment provides a
- `get_template` method that calls the loader's `load` method to get the
- :class:`Template` object.
-
- A very basic example for a loader that looks up templates on the file
- system could look like this::
-
- from jinja2 import BaseLoader, TemplateNotFound
- from os.path import join, exists, getmtime
-
- class MyLoader(BaseLoader):
-
- def __init__(self, path):
- self.path = path
-
- def get_source(self, environment, template):
- path = join(self.path, template)
- if not exists(path):
- raise TemplateNotFound(template)
- mtime = getmtime(path)
- with file(path) as f:
- source = f.read().decode('utf-8')
- return source, path, lambda: mtime == getmtime(path)
- """
-
- #: if set to `False` it indicates that the loader cannot provide access
- #: to the source of templates.
- #:
- #: .. versionadded:: 2.4
- has_source_access = True
-
- def get_source(self, environment, template):
- """Get the template source, filename and reload helper for a template.
- It's passed the environment and template name and has to return a
- tuple in the form ``(source, filename, uptodate)`` or raise a
- `TemplateNotFound` error if it can't locate the template.
-
- The source part of the returned tuple must be the source of the
- template as unicode string or a ASCII bytestring. The filename should
- be the name of the file on the filesystem if it was loaded from there,
- otherwise `None`. The filename is used by python for the tracebacks
- if no loader extension is used.
-
- The last item in the tuple is the `uptodate` function. If auto
- reloading is enabled it's always called to check if the template
- changed. No arguments are passed so the function must store the
- old state somewhere (for example in a closure). If it returns `False`
- the template will be reloaded.
- """
- if not self.has_source_access:
- raise RuntimeError('%s cannot provide access to the source' %
- self.__class__.__name__)
- raise TemplateNotFound(template)
-
- def list_templates(self):
- """Iterates over all templates. If the loader does not support that
- it should raise a :exc:`TypeError` which is the default behavior.
- """
- raise TypeError('this loader cannot iterate over all templates')
-
- @internalcode
- def load(self, environment, name, globals=None):
- """Loads a template. This method looks up the template in the cache
- or loads one by calling :meth:`get_source`. Subclasses should not
- override this method as loaders working on collections of other
- loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`)
- will not call this method but `get_source` directly.
- """
- code = None
- if globals is None:
- globals = {}
-
- # first we try to get the source for this template together
- # with the filename and the uptodate function.
- source, filename, uptodate = self.get_source(environment, name)
-
- # try to load the code from the bytecode cache if there is a
- # bytecode cache configured.
- bcc = environment.bytecode_cache
- if bcc is not None:
- bucket = bcc.get_bucket(environment, name, filename, source)
- code = bucket.code
-
- # if we don't have code so far (not cached, no longer up to
- # date) etc. we compile the template
- if code is None:
- code = environment.compile(source, name, filename)
-
- # if the bytecode cache is available and the bucket doesn't
- # have a code so far, we give the bucket the new code and put
- # it back to the bytecode cache.
- if bcc is not None and bucket.code is None:
- bucket.code = code
- bcc.set_bucket(bucket)
-
- return environment.template_class.from_code(environment, code,
- globals, uptodate)
-
-
-class FileSystemLoader(BaseLoader):
- """Loads templates from the file system. This loader can find templates
- in folders on the file system and is the preferred way to load them.
-
- The loader takes the path to the templates as string, or if multiple
- locations are wanted a list of them which is then looked up in the
- given order:
-
- >>> loader = FileSystemLoader('/path/to/templates')
- >>> loader = FileSystemLoader(['/path/to/templates', '/other/path'])
-
- Per default the template encoding is ``'utf-8'`` which can be changed
- by setting the `encoding` parameter to something else.
- """
-
- def __init__(self, searchpath, encoding='utf-8'):
- if isinstance(searchpath, basestring):
- searchpath = [searchpath]
- self.searchpath = list(searchpath)
- self.encoding = encoding
-
- def get_source(self, environment, template):
- pieces = split_template_path(template)
- for searchpath in self.searchpath:
- filename = path.join(searchpath, *pieces)
- f = open_if_exists(filename)
- if f is None:
- continue
- try:
- contents = f.read().decode(self.encoding)
- finally:
- f.close()
-
- mtime = path.getmtime(filename)
- def uptodate():
- try:
- return path.getmtime(filename) == mtime
- except OSError:
- return False
- return contents, filename, uptodate
- raise TemplateNotFound(template)
-
- def list_templates(self):
- found = set()
- for searchpath in self.searchpath:
- for dirpath, dirnames, filenames in os.walk(searchpath):
- for filename in filenames:
- template = os.path.join(dirpath, filename) \
- [len(searchpath):].strip(os.path.sep) \
- .replace(os.path.sep, '/')
- if template[:2] == './':
- template = template[2:]
- if template not in found:
- found.add(template)
- return sorted(found)
-
-
-class PackageLoader(BaseLoader):
- """Load templates from python eggs or packages. It is constructed with
- the name of the python package and the path to the templates in that
- package::
-
- loader = PackageLoader('mypackage', 'views')
-
- If the package path is not given, ``'templates'`` is assumed.
-
- Per default the template encoding is ``'utf-8'`` which can be changed
- by setting the `encoding` parameter to something else. Due to the nature
- of eggs it's only possible to reload templates if the package was loaded
- from the file system and not a zip file.
- """
-
- def __init__(self, package_name, package_path='templates',
- encoding='utf-8'):
- from pkg_resources import DefaultProvider, ResourceManager, \
- get_provider
- provider = get_provider(package_name)
- self.encoding = encoding
- self.manager = ResourceManager()
- self.filesystem_bound = isinstance(provider, DefaultProvider)
- self.provider = provider
- self.package_path = package_path
-
- def get_source(self, environment, template):
- pieces = split_template_path(template)
- p = '/'.join((self.package_path,) + tuple(pieces))
- if not self.provider.has_resource(p):
- raise TemplateNotFound(template)
-
- filename = uptodate = None
- if self.filesystem_bound:
- filename = self.provider.get_resource_filename(self.manager, p)
- mtime = path.getmtime(filename)
- def uptodate():
- try:
- return path.getmtime(filename) == mtime
- except OSError:
- return False
-
- source = self.provider.get_resource_string(self.manager, p)
- return source.decode(self.encoding), filename, uptodate
-
- def list_templates(self):
- path = self.package_path
- if path[:2] == './':
- path = path[2:]
- elif path == '.':
- path = ''
- offset = len(path)
- results = []
- def _walk(path):
- for filename in self.provider.resource_listdir(path):
- fullname = path + '/' + filename
- if self.provider.resource_isdir(fullname):
- for item in _walk(fullname):
- results.append(item)
- else:
- results.append(fullname[offset:].lstrip('/'))
- _walk(path)
- results.sort()
- return results
-
-
-class DictLoader(BaseLoader):
- """Loads a template from a python dict. It's passed a dict of unicode
- strings bound to template names. This loader is useful for unittesting:
-
- >>> loader = DictLoader({'index.html': 'source here'})
-
- Because auto reloading is rarely useful this is disabled per default.
- """
-
- def __init__(self, mapping):
- self.mapping = mapping
-
- def get_source(self, environment, template):
- if template in self.mapping:
- source = self.mapping[template]
- return source, None, lambda: source != self.mapping.get(template)
- raise TemplateNotFound(template)
-
- def list_templates(self):
- return sorted(self.mapping)
-
-
-class FunctionLoader(BaseLoader):
- """A loader that is passed a function which does the loading. The
- function becomes the name of the template passed and has to return either
- an unicode string with the template source, a tuple in the form ``(source,
- filename, uptodatefunc)`` or `None` if the template does not exist.
-
- >>> def load_template(name):
- ... if name == 'index.html':
- ... return '...'
- ...
- >>> loader = FunctionLoader(load_template)
-
- The `uptodatefunc` is a function that is called if autoreload is enabled
- and has to return `True` if the template is still up to date. For more
- details have a look at :meth:`BaseLoader.get_source` which has the same
- return value.
- """
-
- def __init__(self, load_func):
- self.load_func = load_func
-
- def get_source(self, environment, template):
- rv = self.load_func(template)
- if rv is None:
- raise TemplateNotFound(template)
- elif isinstance(rv, basestring):
- return rv, None, None
- return rv
-
-
-class PrefixLoader(BaseLoader):
- """A loader that is passed a dict of loaders where each loader is bound
- to a prefix. The prefix is delimited from the template by a slash per
- default, which can be changed by setting the `delimiter` argument to
- something else::
-
- loader = PrefixLoader({
- 'app1': PackageLoader('mypackage.app1'),
- 'app2': PackageLoader('mypackage.app2')
- })
-
- By loading ``'app1/index.html'`` the file from the app1 package is loaded,
- by loading ``'app2/index.html'`` the file from the second.
- """
-
- def __init__(self, mapping, delimiter='/'):
- self.mapping = mapping
- self.delimiter = delimiter
-
- def get_source(self, environment, template):
- try:
- prefix, name = template.split(self.delimiter, 1)
- loader = self.mapping[prefix]
- except (ValueError, KeyError):
- raise TemplateNotFound(template)
- try:
- return loader.get_source(environment, name)
- except TemplateNotFound:
- # re-raise the exception with the correct fileame here.
- # (the one that includes the prefix)
- raise TemplateNotFound(template)
-
- def list_templates(self):
- result = []
- for prefix, loader in self.mapping.iteritems():
- for template in loader.list_templates():
- result.append(prefix + self.delimiter + template)
- return result
-
-
-class ChoiceLoader(BaseLoader):
- """This loader works like the `PrefixLoader` just that no prefix is
- specified. If a template could not be found by one loader the next one
- is tried.
-
- >>> loader = ChoiceLoader([
- ... FileSystemLoader('/path/to/user/templates'),
- ... FileSystemLoader('/path/to/system/templates')
- ... ])
-
- This is useful if you want to allow users to override builtin templates
- from a different location.
- """
-
- def __init__(self, loaders):
- self.loaders = loaders
-
- def get_source(self, environment, template):
- for loader in self.loaders:
- try:
- return loader.get_source(environment, template)
- except TemplateNotFound:
- pass
- raise TemplateNotFound(template)
-
- def list_templates(self):
- found = set()
- for loader in self.loaders:
- found.update(loader.list_templates())
- return sorted(found)
-
-
-class _TemplateModule(ModuleType):
- """Like a normal module but with support for weak references"""
-
-
-class ModuleLoader(BaseLoader):
- """This loader loads templates from precompiled templates.
-
- Example usage:
-
- >>> loader = ChoiceLoader([
- ... ModuleLoader('/path/to/compiled/templates'),
- ... FileSystemLoader('/path/to/templates')
- ... ])
- """
-
- has_source_access = False
-
- def __init__(self, path):
- package_name = '_jinja2_module_templates_%x' % id(self)
-
- # create a fake module that looks for the templates in the
- # path given.
- mod = _TemplateModule(package_name)
- if isinstance(path, basestring):
- path = [path]
- else:
- path = list(path)
- mod.__path__ = path
-
- sys.modules[package_name] = weakref.proxy(mod,
- lambda x: sys.modules.pop(package_name, None))
-
- # the only strong reference, the sys.modules entry is weak
- # so that the garbage collector can remove it once the
- # loader that created it goes out of business.
- self.module = mod
- self.package_name = package_name
-
- @staticmethod
- def get_template_key(name):
- return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest()
-
- @staticmethod
- def get_module_filename(name):
- return ModuleLoader.get_template_key(name) + '.py'
-
- @internalcode
- def load(self, environment, name, globals=None):
- key = self.get_template_key(name)
- module = '%s.%s' % (self.package_name, key)
- mod = getattr(self.module, module, None)
- if mod is None:
- try:
- mod = __import__(module, None, None, ['root'])
- except ImportError:
- raise TemplateNotFound(name)
-
- # remove the entry from sys.modules, we only want the attribute
- # on the module object we have stored on the loader.
- sys.modules.pop(module, None)
-
- return environment.template_class.from_module_dict(
- environment, mod.__dict__, globals)
diff --git a/module/lib/jinja2/meta.py b/module/lib/jinja2/meta.py
deleted file mode 100644
index 3a779a5e9..000000000
--- a/module/lib/jinja2/meta.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.meta
- ~~~~~~~~~~~
-
- This module implements various functions that exposes information about
- templates that might be interesting for various kinds of applications.
-
- :copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details.
- :license: BSD, see LICENSE for more details.
-"""
-from jinja2 import nodes
-from jinja2.compiler import CodeGenerator
-
-
-class TrackingCodeGenerator(CodeGenerator):
- """We abuse the code generator for introspection."""
-
- def __init__(self, environment):
- CodeGenerator.__init__(self, environment, '<introspection>',
- '<introspection>')
- self.undeclared_identifiers = set()
-
- def write(self, x):
- """Don't write."""
-
- def pull_locals(self, frame):
- """Remember all undeclared identifiers."""
- self.undeclared_identifiers.update(frame.identifiers.undeclared)
-
-
-def find_undeclared_variables(ast):
- """Returns a set of all variables in the AST that will be looked up from
- the context at runtime. Because at compile time it's not known which
- variables will be used depending on the path the execution takes at
- runtime, all variables are returned.
-
- >>> from jinja2 import Environment, meta
- >>> env = Environment()
- >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
- >>> meta.find_undeclared_variables(ast)
- set(['bar'])
-
- .. admonition:: Implementation
-
- Internally the code generator is used for finding undeclared variables.
- This is good to know because the code generator might raise a
- :exc:`TemplateAssertionError` during compilation and as a matter of
- fact this function can currently raise that exception as well.
- """
- codegen = TrackingCodeGenerator(ast.environment)
- codegen.visit(ast)
- return codegen.undeclared_identifiers
-
-
-def find_referenced_templates(ast):
- """Finds all the referenced templates from the AST. This will return an
- iterator over all the hardcoded template extensions, inclusions and
- imports. If dynamic inheritance or inclusion is used, `None` will be
- yielded.
-
- >>> from jinja2 import Environment, meta
- >>> env = Environment()
- >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}')
- >>> list(meta.find_referenced_templates(ast))
- ['layout.html', None]
-
- This function is useful for dependency tracking. For example if you want
- to rebuild parts of the website after a layout template has changed.
- """
- for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import,
- nodes.Include)):
- if not isinstance(node.template, nodes.Const):
- # a tuple with some non consts in there
- if isinstance(node.template, (nodes.Tuple, nodes.List)):
- for template_name in node.template.items:
- # something const, only yield the strings and ignore
- # non-string consts that really just make no sense
- if isinstance(template_name, nodes.Const):
- if isinstance(template_name.value, basestring):
- yield template_name.value
- # something dynamic in there
- else:
- yield None
- # something dynamic we don't know about here
- else:
- yield None
- continue
- # constant is a basestring, direct template name
- if isinstance(node.template.value, basestring):
- yield node.template.value
- # a tuple or list (latter *should* not happen) made of consts,
- # yield the consts that are strings. We could warn here for
- # non string values
- elif isinstance(node, nodes.Include) and \
- isinstance(node.template.value, (tuple, list)):
- for template_name in node.template.value:
- if isinstance(template_name, basestring):
- yield template_name
- # something else we don't care about, we could warn here
- else:
- yield None
diff --git a/module/lib/jinja2/nodes.py b/module/lib/jinja2/nodes.py
deleted file mode 100644
index 6446c70ea..000000000
--- a/module/lib/jinja2/nodes.py
+++ /dev/null
@@ -1,901 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.nodes
- ~~~~~~~~~~~~
-
- This module implements additional nodes derived from the ast base node.
-
- It also provides some node tree helper functions like `in_lineno` and
- `get_nodes` used by the parser and translator in order to normalize
- python and jinja nodes.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import operator
-from itertools import chain, izip
-from collections import deque
-from jinja2.utils import Markup, MethodType, FunctionType
-
-
-#: the types we support for context functions
-_context_function_types = (FunctionType, MethodType)
-
-
-_binop_to_func = {
- '*': operator.mul,
- '/': operator.truediv,
- '//': operator.floordiv,
- '**': operator.pow,
- '%': operator.mod,
- '+': operator.add,
- '-': operator.sub
-}
-
-_uaop_to_func = {
- 'not': operator.not_,
- '+': operator.pos,
- '-': operator.neg
-}
-
-_cmpop_to_func = {
- 'eq': operator.eq,
- 'ne': operator.ne,
- 'gt': operator.gt,
- 'gteq': operator.ge,
- 'lt': operator.lt,
- 'lteq': operator.le,
- 'in': lambda a, b: a in b,
- 'notin': lambda a, b: a not in b
-}
-
-
-class Impossible(Exception):
- """Raised if the node could not perform a requested action."""
-
-
-class NodeType(type):
- """A metaclass for nodes that handles the field and attribute
- inheritance. fields and attributes from the parent class are
- automatically forwarded to the child."""
-
- def __new__(cls, name, bases, d):
- for attr in 'fields', 'attributes':
- storage = []
- storage.extend(getattr(bases[0], attr, ()))
- storage.extend(d.get(attr, ()))
- assert len(bases) == 1, 'multiple inheritance not allowed'
- assert len(storage) == len(set(storage)), 'layout conflict'
- d[attr] = tuple(storage)
- d.setdefault('abstract', False)
- return type.__new__(cls, name, bases, d)
-
-
-class EvalContext(object):
- """Holds evaluation time information. Custom attributes can be attached
- to it in extensions.
- """
-
- def __init__(self, environment, template_name=None):
- if callable(environment.autoescape):
- self.autoescape = environment.autoescape(template_name)
- else:
- self.autoescape = environment.autoescape
- self.volatile = False
-
- def save(self):
- return self.__dict__.copy()
-
- def revert(self, old):
- self.__dict__.clear()
- self.__dict__.update(old)
-
-
-def get_eval_context(node, ctx):
- if ctx is None:
- if node.environment is None:
- raise RuntimeError('if no eval context is passed, the '
- 'node must have an attached '
- 'environment.')
- return EvalContext(node.environment)
- return ctx
-
-
-class Node(object):
- """Baseclass for all Jinja2 nodes. There are a number of nodes available
- of different types. There are three major types:
-
- - :class:`Stmt`: statements
- - :class:`Expr`: expressions
- - :class:`Helper`: helper nodes
- - :class:`Template`: the outermost wrapper node
-
- All nodes have fields and attributes. Fields may be other nodes, lists,
- or arbitrary values. Fields are passed to the constructor as regular
- positional arguments, attributes as keyword arguments. Each node has
- two attributes: `lineno` (the line number of the node) and `environment`.
- The `environment` attribute is set at the end of the parsing process for
- all nodes automatically.
- """
- __metaclass__ = NodeType
- fields = ()
- attributes = ('lineno', 'environment')
- abstract = True
-
- def __init__(self, *fields, **attributes):
- if self.abstract:
- raise TypeError('abstract nodes are not instanciable')
- if fields:
- if len(fields) != len(self.fields):
- if not self.fields:
- raise TypeError('%r takes 0 arguments' %
- self.__class__.__name__)
- raise TypeError('%r takes 0 or %d argument%s' % (
- self.__class__.__name__,
- len(self.fields),
- len(self.fields) != 1 and 's' or ''
- ))
- for name, arg in izip(self.fields, fields):
- setattr(self, name, arg)
- for attr in self.attributes:
- setattr(self, attr, attributes.pop(attr, None))
- if attributes:
- raise TypeError('unknown attribute %r' %
- iter(attributes).next())
-
- def iter_fields(self, exclude=None, only=None):
- """This method iterates over all fields that are defined and yields
- ``(key, value)`` tuples. Per default all fields are returned, but
- it's possible to limit that to some fields by providing the `only`
- parameter or to exclude some using the `exclude` parameter. Both
- should be sets or tuples of field names.
- """
- for name in self.fields:
- if (exclude is only is None) or \
- (exclude is not None and name not in exclude) or \
- (only is not None and name in only):
- try:
- yield name, getattr(self, name)
- except AttributeError:
- pass
-
- def iter_child_nodes(self, exclude=None, only=None):
- """Iterates over all direct child nodes of the node. This iterates
- over all fields and yields the values of they are nodes. If the value
- of a field is a list all the nodes in that list are returned.
- """
- for field, item in self.iter_fields(exclude, only):
- if isinstance(item, list):
- for n in item:
- if isinstance(n, Node):
- yield n
- elif isinstance(item, Node):
- yield item
-
- def find(self, node_type):
- """Find the first node of a given type. If no such node exists the
- return value is `None`.
- """
- for result in self.find_all(node_type):
- return result
-
- def find_all(self, node_type):
- """Find all the nodes of a given type. If the type is a tuple,
- the check is performed for any of the tuple items.
- """
- for child in self.iter_child_nodes():
- if isinstance(child, node_type):
- yield child
- for result in child.find_all(node_type):
- yield result
-
- def set_ctx(self, ctx):
- """Reset the context of a node and all child nodes. Per default the
- parser will all generate nodes that have a 'load' context as it's the
- most common one. This method is used in the parser to set assignment
- targets and other nodes to a store context.
- """
- todo = deque([self])
- while todo:
- node = todo.popleft()
- if 'ctx' in node.fields:
- node.ctx = ctx
- todo.extend(node.iter_child_nodes())
- return self
-
- def set_lineno(self, lineno, override=False):
- """Set the line numbers of the node and children."""
- todo = deque([self])
- while todo:
- node = todo.popleft()
- if 'lineno' in node.attributes:
- if node.lineno is None or override:
- node.lineno = lineno
- todo.extend(node.iter_child_nodes())
- return self
-
- def set_environment(self, environment):
- """Set the environment for all nodes."""
- todo = deque([self])
- while todo:
- node = todo.popleft()
- node.environment = environment
- todo.extend(node.iter_child_nodes())
- return self
-
- def __eq__(self, other):
- return type(self) is type(other) and \
- tuple(self.iter_fields()) == tuple(other.iter_fields())
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def __repr__(self):
- return '%s(%s)' % (
- self.__class__.__name__,
- ', '.join('%s=%r' % (arg, getattr(self, arg, None)) for
- arg in self.fields)
- )
-
-
-class Stmt(Node):
- """Base node for all statements."""
- abstract = True
-
-
-class Helper(Node):
- """Nodes that exist in a specific context only."""
- abstract = True
-
-
-class Template(Node):
- """Node that represents a template. This must be the outermost node that
- is passed to the compiler.
- """
- fields = ('body',)
-
-
-class Output(Stmt):
- """A node that holds multiple expressions which are then printed out.
- This is used both for the `print` statement and the regular template data.
- """
- fields = ('nodes',)
-
-
-class Extends(Stmt):
- """Represents an extends statement."""
- fields = ('template',)
-
-
-class For(Stmt):
- """The for loop. `target` is the target for the iteration (usually a
- :class:`Name` or :class:`Tuple`), `iter` the iterable. `body` is a list
- of nodes that are used as loop-body, and `else_` a list of nodes for the
- `else` block. If no else node exists it has to be an empty list.
-
- For filtered nodes an expression can be stored as `test`, otherwise `None`.
- """
- fields = ('target', 'iter', 'body', 'else_', 'test', 'recursive')
-
-
-class If(Stmt):
- """If `test` is true, `body` is rendered, else `else_`."""
- fields = ('test', 'body', 'else_')
-
-
-class Macro(Stmt):
- """A macro definition. `name` is the name of the macro, `args` a list of
- arguments and `defaults` a list of defaults if there are any. `body` is
- a list of nodes for the macro body.
- """
- fields = ('name', 'args', 'defaults', 'body')
-
-
-class CallBlock(Stmt):
- """Like a macro without a name but a call instead. `call` is called with
- the unnamed macro as `caller` argument this node holds.
- """
- fields = ('call', 'args', 'defaults', 'body')
-
-
-class FilterBlock(Stmt):
- """Node for filter sections."""
- fields = ('body', 'filter')
-
-
-class Block(Stmt):
- """A node that represents a block."""
- fields = ('name', 'body', 'scoped')
-
-
-class Include(Stmt):
- """A node that represents the include tag."""
- fields = ('template', 'with_context', 'ignore_missing')
-
-
-class Import(Stmt):
- """A node that represents the import tag."""
- fields = ('template', 'target', 'with_context')
-
-
-class FromImport(Stmt):
- """A node that represents the from import tag. It's important to not
- pass unsafe names to the name attribute. The compiler translates the
- attribute lookups directly into getattr calls and does *not* use the
- subscript callback of the interface. As exported variables may not
- start with double underscores (which the parser asserts) this is not a
- problem for regular Jinja code, but if this node is used in an extension
- extra care must be taken.
-
- The list of names may contain tuples if aliases are wanted.
- """
- fields = ('template', 'names', 'with_context')
-
-
-class ExprStmt(Stmt):
- """A statement that evaluates an expression and discards the result."""
- fields = ('node',)
-
-
-class Assign(Stmt):
- """Assigns an expression to a target."""
- fields = ('target', 'node')
-
-
-class Expr(Node):
- """Baseclass for all expressions."""
- abstract = True
-
- def as_const(self, eval_ctx=None):
- """Return the value of the expression as constant or raise
- :exc:`Impossible` if this was not possible.
-
- An :class:`EvalContext` can be provided, if none is given
- a default context is created which requires the nodes to have
- an attached environment.
-
- .. versionchanged:: 2.4
- the `eval_ctx` parameter was added.
- """
- raise Impossible()
-
- def can_assign(self):
- """Check if it's possible to assign something to this node."""
- return False
-
-
-class BinExpr(Expr):
- """Baseclass for all binary expressions."""
- fields = ('left', 'right')
- operator = None
- abstract = True
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- f = _binop_to_func[self.operator]
- try:
- return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx))
- except:
- raise Impossible()
-
-
-class UnaryExpr(Expr):
- """Baseclass for all unary expressions."""
- fields = ('node',)
- operator = None
- abstract = True
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- f = _uaop_to_func[self.operator]
- try:
- return f(self.node.as_const(eval_ctx))
- except:
- raise Impossible()
-
-
-class Name(Expr):
- """Looks up a name or stores a value in a name.
- The `ctx` of the node can be one of the following values:
-
- - `store`: store a value in the name
- - `load`: load that name
- - `param`: like `store` but if the name was defined as function parameter.
- """
- fields = ('name', 'ctx')
-
- def can_assign(self):
- return self.name not in ('true', 'false', 'none',
- 'True', 'False', 'None')
-
-
-class Literal(Expr):
- """Baseclass for literals."""
- abstract = True
-
-
-class Const(Literal):
- """All constant values. The parser will return this node for simple
- constants such as ``42`` or ``"foo"`` but it can be used to store more
- complex values such as lists too. Only constants with a safe
- representation (objects where ``eval(repr(x)) == x`` is true).
- """
- fields = ('value',)
-
- def as_const(self, eval_ctx=None):
- return self.value
-
- @classmethod
- def from_untrusted(cls, value, lineno=None, environment=None):
- """Return a const object if the value is representable as
- constant value in the generated code, otherwise it will raise
- an `Impossible` exception.
- """
- from compiler import has_safe_repr
- if not has_safe_repr(value):
- raise Impossible()
- return cls(value, lineno=lineno, environment=environment)
-
-
-class TemplateData(Literal):
- """A constant template string."""
- fields = ('data',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if eval_ctx.volatile:
- raise Impossible()
- if eval_ctx.autoescape:
- return Markup(self.data)
- return self.data
-
-
-class Tuple(Literal):
- """For loop unpacking and some other things like multiple arguments
- for subscripts. Like for :class:`Name` `ctx` specifies if the tuple
- is used for loading the names or storing.
- """
- fields = ('items', 'ctx')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return tuple(x.as_const(eval_ctx) for x in self.items)
-
- def can_assign(self):
- for item in self.items:
- if not item.can_assign():
- return False
- return True
-
-
-class List(Literal):
- """Any list literal such as ``[1, 2, 3]``"""
- fields = ('items',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return [x.as_const(eval_ctx) for x in self.items]
-
-
-class Dict(Literal):
- """Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of
- :class:`Pair` nodes.
- """
- fields = ('items',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return dict(x.as_const(eval_ctx) for x in self.items)
-
-
-class Pair(Helper):
- """A key, value pair for dicts."""
- fields = ('key', 'value')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx)
-
-
-class Keyword(Helper):
- """A key, value pair for keyword arguments where key is a string."""
- fields = ('key', 'value')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.key, self.value.as_const(eval_ctx)
-
-
-class CondExpr(Expr):
- """A conditional expression (inline if expression). (``{{
- foo if bar else baz }}``)
- """
- fields = ('test', 'expr1', 'expr2')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if self.test.as_const(eval_ctx):
- return self.expr1.as_const(eval_ctx)
-
- # if we evaluate to an undefined object, we better do that at runtime
- if self.expr2 is None:
- raise Impossible()
-
- return self.expr2.as_const(eval_ctx)
-
-
-class Filter(Expr):
- """This node applies a filter on an expression. `name` is the name of
- the filter, the rest of the fields are the same as for :class:`Call`.
-
- If the `node` of a filter is `None` the contents of the last buffer are
- filtered. Buffers are created by macros and filter blocks.
- """
- fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if eval_ctx.volatile or self.node is None:
- raise Impossible()
- # we have to be careful here because we call filter_ below.
- # if this variable would be called filter, 2to3 would wrap the
- # call in a list beause it is assuming we are talking about the
- # builtin filter function here which no longer returns a list in
- # python 3. because of that, do not rename filter_ to filter!
- filter_ = self.environment.filters.get(self.name)
- if filter_ is None or getattr(filter_, 'contextfilter', False):
- raise Impossible()
- obj = self.node.as_const(eval_ctx)
- args = [x.as_const(eval_ctx) for x in self.args]
- if getattr(filter_, 'evalcontextfilter', False):
- args.insert(0, eval_ctx)
- elif getattr(filter_, 'environmentfilter', False):
- args.insert(0, self.environment)
- kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs)
- if self.dyn_args is not None:
- try:
- args.extend(self.dyn_args.as_const(eval_ctx))
- except:
- raise Impossible()
- if self.dyn_kwargs is not None:
- try:
- kwargs.update(self.dyn_kwargs.as_const(eval_ctx))
- except:
- raise Impossible()
- try:
- return filter_(obj, *args, **kwargs)
- except:
- raise Impossible()
-
-
-class Test(Expr):
- """Applies a test on an expression. `name` is the name of the test, the
- rest of the fields are the same as for :class:`Call`.
- """
- fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
-
-
-class Call(Expr):
- """Calls an expression. `args` is a list of arguments, `kwargs` a list
- of keyword arguments (list of :class:`Keyword` nodes), and `dyn_args`
- and `dyn_kwargs` has to be either `None` or a node that is used as
- node for dynamic positional (``*args``) or keyword (``**kwargs``)
- arguments.
- """
- fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if eval_ctx.volatile:
- raise Impossible()
- obj = self.node.as_const(eval_ctx)
-
- # don't evaluate context functions
- args = [x.as_const(eval_ctx) for x in self.args]
- if isinstance(obj, _context_function_types):
- if getattr(obj, 'contextfunction', False):
- raise Impossible()
- elif getattr(obj, 'evalcontextfunction', False):
- args.insert(0, eval_ctx)
- elif getattr(obj, 'environmentfunction', False):
- args.insert(0, self.environment)
-
- kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs)
- if self.dyn_args is not None:
- try:
- args.extend(self.dyn_args.as_const(eval_ctx))
- except:
- raise Impossible()
- if self.dyn_kwargs is not None:
- try:
- kwargs.update(self.dyn_kwargs.as_const(eval_ctx))
- except:
- raise Impossible()
- try:
- return obj(*args, **kwargs)
- except:
- raise Impossible()
-
-
-class Getitem(Expr):
- """Get an attribute or item from an expression and prefer the item."""
- fields = ('node', 'arg', 'ctx')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if self.ctx != 'load':
- raise Impossible()
- try:
- return self.environment.getitem(self.node.as_const(eval_ctx),
- self.arg.as_const(eval_ctx))
- except:
- raise Impossible()
-
- def can_assign(self):
- return False
-
-
-class Getattr(Expr):
- """Get an attribute or item from an expression that is a ascii-only
- bytestring and prefer the attribute.
- """
- fields = ('node', 'attr', 'ctx')
-
- def as_const(self, eval_ctx=None):
- if self.ctx != 'load':
- raise Impossible()
- try:
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.environment.getattr(self.node.as_const(eval_ctx),
- self.attr)
- except:
- raise Impossible()
-
- def can_assign(self):
- return False
-
-
-class Slice(Expr):
- """Represents a slice object. This must only be used as argument for
- :class:`Subscript`.
- """
- fields = ('start', 'stop', 'step')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- def const(obj):
- if obj is None:
- return None
- return obj.as_const(eval_ctx)
- return slice(const(self.start), const(self.stop), const(self.step))
-
-
-class Concat(Expr):
- """Concatenates the list of expressions provided after converting them to
- unicode.
- """
- fields = ('nodes',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return ''.join(unicode(x.as_const(eval_ctx)) for x in self.nodes)
-
-
-class Compare(Expr):
- """Compares an expression with some other expressions. `ops` must be a
- list of :class:`Operand`\s.
- """
- fields = ('expr', 'ops')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- result = value = self.expr.as_const(eval_ctx)
- try:
- for op in self.ops:
- new_value = op.expr.as_const(eval_ctx)
- result = _cmpop_to_func[op.op](value, new_value)
- value = new_value
- except:
- raise Impossible()
- return result
-
-
-class Operand(Helper):
- """Holds an operator and an expression."""
- fields = ('op', 'expr')
-
-if __debug__:
- Operand.__doc__ += '\nThe following operators are available: ' + \
- ', '.join(sorted('``%s``' % x for x in set(_binop_to_func) |
- set(_uaop_to_func) | set(_cmpop_to_func)))
-
-
-class Mul(BinExpr):
- """Multiplies the left with the right node."""
- operator = '*'
-
-
-class Div(BinExpr):
- """Divides the left by the right node."""
- operator = '/'
-
-
-class FloorDiv(BinExpr):
- """Divides the left by the right node and truncates conver the
- result into an integer by truncating.
- """
- operator = '//'
-
-
-class Add(BinExpr):
- """Add the left to the right node."""
- operator = '+'
-
-
-class Sub(BinExpr):
- """Substract the right from the left node."""
- operator = '-'
-
-
-class Mod(BinExpr):
- """Left modulo right."""
- operator = '%'
-
-
-class Pow(BinExpr):
- """Left to the power of right."""
- operator = '**'
-
-
-class And(BinExpr):
- """Short circuited AND."""
- operator = 'and'
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx)
-
-
-class Or(BinExpr):
- """Short circuited OR."""
- operator = 'or'
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx)
-
-
-class Not(UnaryExpr):
- """Negate the expression."""
- operator = 'not'
-
-
-class Neg(UnaryExpr):
- """Make the expression negative."""
- operator = '-'
-
-
-class Pos(UnaryExpr):
- """Make the expression positive (noop for most expressions)"""
- operator = '+'
-
-
-# Helpers for extensions
-
-
-class EnvironmentAttribute(Expr):
- """Loads an attribute from the environment object. This is useful for
- extensions that want to call a callback stored on the environment.
- """
- fields = ('name',)
-
-
-class ExtensionAttribute(Expr):
- """Returns the attribute of an extension bound to the environment.
- The identifier is the identifier of the :class:`Extension`.
-
- This node is usually constructed by calling the
- :meth:`~jinja2.ext.Extension.attr` method on an extension.
- """
- fields = ('identifier', 'name')
-
-
-class ImportedName(Expr):
- """If created with an import name the import name is returned on node
- access. For example ``ImportedName('cgi.escape')`` returns the `escape`
- function from the cgi module on evaluation. Imports are optimized by the
- compiler so there is no need to assign them to local variables.
- """
- fields = ('importname',)
-
-
-class InternalName(Expr):
- """An internal name in the compiler. You cannot create these nodes
- yourself but the parser provides a
- :meth:`~jinja2.parser.Parser.free_identifier` method that creates
- a new identifier for you. This identifier is not available from the
- template and is not threated specially by the compiler.
- """
- fields = ('name',)
-
- def __init__(self):
- raise TypeError('Can\'t create internal names. Use the '
- '`free_identifier` method on a parser.')
-
-
-class MarkSafe(Expr):
- """Mark the wrapped expression as safe (wrap it as `Markup`)."""
- fields = ('expr',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return Markup(self.expr.as_const(eval_ctx))
-
-
-class MarkSafeIfAutoescape(Expr):
- """Mark the wrapped expression as safe (wrap it as `Markup`) but
- only if autoescaping is active.
-
- .. versionadded:: 2.5
- """
- fields = ('expr',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if eval_ctx.volatile:
- raise Impossible()
- expr = self.expr.as_const(eval_ctx)
- if eval_ctx.autoescape:
- return Markup(expr)
- return expr
-
-
-class ContextReference(Expr):
- """Returns the current template context. It can be used like a
- :class:`Name` node, with a ``'load'`` ctx and will return the
- current :class:`~jinja2.runtime.Context` object.
-
- Here an example that assigns the current template name to a
- variable named `foo`::
-
- Assign(Name('foo', ctx='store'),
- Getattr(ContextReference(), 'name'))
- """
-
-
-class Continue(Stmt):
- """Continue a loop."""
-
-
-class Break(Stmt):
- """Break a loop."""
-
-
-class Scope(Stmt):
- """An artificial scope."""
- fields = ('body',)
-
-
-class EvalContextModifier(Stmt):
- """Modifies the eval context. For each option that should be modified,
- a :class:`Keyword` has to be added to the :attr:`options` list.
-
- Example to change the `autoescape` setting::
-
- EvalContextModifier(options=[Keyword('autoescape', Const(True))])
- """
- fields = ('options',)
-
-
-class ScopedEvalContextModifier(EvalContextModifier):
- """Modifies the eval context and reverts it later. Works exactly like
- :class:`EvalContextModifier` but will only modify the
- :class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`.
- """
- fields = ('body',)
-
-
-# make sure nobody creates custom nodes
-def _failing_new(*args, **kwargs):
- raise TypeError('can\'t create custom node types')
-NodeType.__new__ = staticmethod(_failing_new); del _failing_new
diff --git a/module/lib/jinja2/optimizer.py b/module/lib/jinja2/optimizer.py
deleted file mode 100644
index 00eab115e..000000000
--- a/module/lib/jinja2/optimizer.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.optimizer
- ~~~~~~~~~~~~~~~~
-
- The jinja optimizer is currently trying to constant fold a few expressions
- and modify the AST in place so that it should be easier to evaluate it.
-
- Because the AST does not contain all the scoping information and the
- compiler has to find that out, we cannot do all the optimizations we
- want. For example loop unrolling doesn't work because unrolled loops would
- have a different scoping.
-
- The solution would be a second syntax tree that has the scoping rules stored.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-from jinja2 import nodes
-from jinja2.visitor import NodeTransformer
-
-
-def optimize(node, environment):
- """The context hint can be used to perform an static optimization
- based on the context given."""
- optimizer = Optimizer(environment)
- return optimizer.visit(node)
-
-
-class Optimizer(NodeTransformer):
-
- def __init__(self, environment):
- self.environment = environment
-
- def visit_If(self, node):
- """Eliminate dead code."""
- # do not optimize ifs that have a block inside so that it doesn't
- # break super().
- if node.find(nodes.Block) is not None:
- return self.generic_visit(node)
- try:
- val = self.visit(node.test).as_const()
- except nodes.Impossible:
- return self.generic_visit(node)
- if val:
- body = node.body
- else:
- body = node.else_
- result = []
- for node in body:
- result.extend(self.visit_list(node))
- return result
-
- def fold(self, node):
- """Do constant folding."""
- node = self.generic_visit(node)
- try:
- return nodes.Const.from_untrusted(node.as_const(),
- lineno=node.lineno,
- environment=self.environment)
- except nodes.Impossible:
- return node
-
- visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \
- visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \
- visit_Not = visit_Compare = visit_Getitem = visit_Getattr = visit_Call = \
- visit_Filter = visit_Test = visit_CondExpr = fold
- del fold
diff --git a/module/lib/jinja2/parser.py b/module/lib/jinja2/parser.py
deleted file mode 100644
index d44229ad0..000000000
--- a/module/lib/jinja2/parser.py
+++ /dev/null
@@ -1,896 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.parser
- ~~~~~~~~~~~~~
-
- Implements the template parser.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-from jinja2 import nodes
-from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
-from jinja2.utils import next
-from jinja2.lexer import describe_token, describe_token_expr
-
-
-#: statements that callinto
-_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
- 'macro', 'include', 'from', 'import',
- 'set'])
-_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq'])
-
-
-class Parser(object):
- """This is the central parsing class Jinja2 uses. It's passed to
- extensions and can be used to parse expressions or statements.
- """
-
- def __init__(self, environment, source, name=None, filename=None,
- state=None):
- self.environment = environment
- self.stream = environment._tokenize(source, name, filename, state)
- self.name = name
- self.filename = filename
- self.closed = False
- self.extensions = {}
- for extension in environment.iter_extensions():
- for tag in extension.tags:
- self.extensions[tag] = extension.parse
- self._last_identifier = 0
- self._tag_stack = []
- self._end_token_stack = []
-
- def fail(self, msg, lineno=None, exc=TemplateSyntaxError):
- """Convenience method that raises `exc` with the message, passed
- line number or last line number as well as the current name and
- filename.
- """
- if lineno is None:
- lineno = self.stream.current.lineno
- raise exc(msg, lineno, self.name, self.filename)
-
- def _fail_ut_eof(self, name, end_token_stack, lineno):
- expected = []
- for exprs in end_token_stack:
- expected.extend(map(describe_token_expr, exprs))
- if end_token_stack:
- currently_looking = ' or '.join(
- "'%s'" % describe_token_expr(expr)
- for expr in end_token_stack[-1])
- else:
- currently_looking = None
-
- if name is None:
- message = ['Unexpected end of template.']
- else:
- message = ['Encountered unknown tag \'%s\'.' % name]
-
- if currently_looking:
- if name is not None and name in expected:
- message.append('You probably made a nesting mistake. Jinja '
- 'is expecting this tag, but currently looking '
- 'for %s.' % currently_looking)
- else:
- message.append('Jinja was looking for the following tags: '
- '%s.' % currently_looking)
-
- if self._tag_stack:
- message.append('The innermost block that needs to be '
- 'closed is \'%s\'.' % self._tag_stack[-1])
-
- self.fail(' '.join(message), lineno)
-
- def fail_unknown_tag(self, name, lineno=None):
- """Called if the parser encounters an unknown tag. Tries to fail
- with a human readable error message that could help to identify
- the problem.
- """
- return self._fail_ut_eof(name, self._end_token_stack, lineno)
-
- def fail_eof(self, end_tokens=None, lineno=None):
- """Like fail_unknown_tag but for end of template situations."""
- stack = list(self._end_token_stack)
- if end_tokens is not None:
- stack.append(end_tokens)
- return self._fail_ut_eof(None, stack, lineno)
-
- def is_tuple_end(self, extra_end_rules=None):
- """Are we at the end of a tuple?"""
- if self.stream.current.type in ('variable_end', 'block_end', 'rparen'):
- return True
- elif extra_end_rules is not None:
- return self.stream.current.test_any(extra_end_rules)
- return False
-
- def free_identifier(self, lineno=None):
- """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
- self._last_identifier += 1
- rv = object.__new__(nodes.InternalName)
- nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno)
- return rv
-
- def parse_statement(self):
- """Parse a single statement."""
- token = self.stream.current
- if token.type != 'name':
- self.fail('tag name expected', token.lineno)
- self._tag_stack.append(token.value)
- pop_tag = True
- try:
- if token.value in _statement_keywords:
- return getattr(self, 'parse_' + self.stream.current.value)()
- if token.value == 'call':
- return self.parse_call_block()
- if token.value == 'filter':
- return self.parse_filter_block()
- ext = self.extensions.get(token.value)
- if ext is not None:
- return ext(self)
-
- # did not work out, remove the token we pushed by accident
- # from the stack so that the unknown tag fail function can
- # produce a proper error message.
- self._tag_stack.pop()
- pop_tag = False
- self.fail_unknown_tag(token.value, token.lineno)
- finally:
- if pop_tag:
- self._tag_stack.pop()
-
- def parse_statements(self, end_tokens, drop_needle=False):
- """Parse multiple statements into a list until one of the end tokens
- is reached. This is used to parse the body of statements as it also
- parses template data if appropriate. The parser checks first if the
- current token is a colon and skips it if there is one. Then it checks
- for the block end and parses until if one of the `end_tokens` is
- reached. Per default the active token in the stream at the end of
- the call is the matched end token. If this is not wanted `drop_needle`
- can be set to `True` and the end token is removed.
- """
- # the first token may be a colon for python compatibility
- self.stream.skip_if('colon')
-
- # in the future it would be possible to add whole code sections
- # by adding some sort of end of statement token and parsing those here.
- self.stream.expect('block_end')
- result = self.subparse(end_tokens)
-
- # we reached the end of the template too early, the subparser
- # does not check for this, so we do that now
- if self.stream.current.type == 'eof':
- self.fail_eof(end_tokens)
-
- if drop_needle:
- next(self.stream)
- return result
-
- def parse_set(self):
- """Parse an assign statement."""
- lineno = next(self.stream).lineno
- target = self.parse_assign_target()
- self.stream.expect('assign')
- expr = self.parse_tuple()
- return nodes.Assign(target, expr, lineno=lineno)
-
- def parse_for(self):
- """Parse a for loop."""
- lineno = self.stream.expect('name:for').lineno
- target = self.parse_assign_target(extra_end_rules=('name:in',))
- self.stream.expect('name:in')
- iter = self.parse_tuple(with_condexpr=False,
- extra_end_rules=('name:recursive',))
- test = None
- if self.stream.skip_if('name:if'):
- test = self.parse_expression()
- recursive = self.stream.skip_if('name:recursive')
- body = self.parse_statements(('name:endfor', 'name:else'))
- if next(self.stream).value == 'endfor':
- else_ = []
- else:
- else_ = self.parse_statements(('name:endfor',), drop_needle=True)
- return nodes.For(target, iter, body, else_, test,
- recursive, lineno=lineno)
-
- def parse_if(self):
- """Parse an if construct."""
- node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
- while 1:
- node.test = self.parse_tuple(with_condexpr=False)
- node.body = self.parse_statements(('name:elif', 'name:else',
- 'name:endif'))
- token = next(self.stream)
- if token.test('name:elif'):
- new_node = nodes.If(lineno=self.stream.current.lineno)
- node.else_ = [new_node]
- node = new_node
- continue
- elif token.test('name:else'):
- node.else_ = self.parse_statements(('name:endif',),
- drop_needle=True)
- else:
- node.else_ = []
- break
- return result
-
- def parse_block(self):
- node = nodes.Block(lineno=next(self.stream).lineno)
- node.name = self.stream.expect('name').value
- node.scoped = self.stream.skip_if('name:scoped')
-
- # common problem people encounter when switching from django
- # to jinja. we do not support hyphens in block names, so let's
- # raise a nicer error message in that case.
- if self.stream.current.type == 'sub':
- self.fail('Block names in Jinja have to be valid Python '
- 'identifiers and may not contain hypens, use an '
- 'underscore instead.')
-
- node.body = self.parse_statements(('name:endblock',), drop_needle=True)
- self.stream.skip_if('name:' + node.name)
- return node
-
- def parse_extends(self):
- node = nodes.Extends(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
- return node
-
- def parse_import_context(self, node, default):
- if self.stream.current.test_any('name:with', 'name:without') and \
- self.stream.look().test('name:context'):
- node.with_context = next(self.stream).value == 'with'
- self.stream.skip()
- else:
- node.with_context = default
- return node
-
- def parse_include(self):
- node = nodes.Include(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
- if self.stream.current.test('name:ignore') and \
- self.stream.look().test('name:missing'):
- node.ignore_missing = True
- self.stream.skip(2)
- else:
- node.ignore_missing = False
- return self.parse_import_context(node, True)
-
- def parse_import(self):
- node = nodes.Import(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
- self.stream.expect('name:as')
- node.target = self.parse_assign_target(name_only=True).name
- return self.parse_import_context(node, False)
-
- def parse_from(self):
- node = nodes.FromImport(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
- self.stream.expect('name:import')
- node.names = []
-
- def parse_context():
- if self.stream.current.value in ('with', 'without') and \
- self.stream.look().test('name:context'):
- node.with_context = next(self.stream).value == 'with'
- self.stream.skip()
- return True
- return False
-
- while 1:
- if node.names:
- self.stream.expect('comma')
- if self.stream.current.type == 'name':
- if parse_context():
- break
- target = self.parse_assign_target(name_only=True)
- if target.name.startswith('_'):
- self.fail('names starting with an underline can not '
- 'be imported', target.lineno,
- exc=TemplateAssertionError)
- if self.stream.skip_if('name:as'):
- alias = self.parse_assign_target(name_only=True)
- node.names.append((target.name, alias.name))
- else:
- node.names.append(target.name)
- if parse_context() or self.stream.current.type != 'comma':
- break
- else:
- break
- if not hasattr(node, 'with_context'):
- node.with_context = False
- self.stream.skip_if('comma')
- return node
-
- def parse_signature(self, node):
- node.args = args = []
- node.defaults = defaults = []
- self.stream.expect('lparen')
- while self.stream.current.type != 'rparen':
- if args:
- self.stream.expect('comma')
- arg = self.parse_assign_target(name_only=True)
- arg.set_ctx('param')
- if self.stream.skip_if('assign'):
- defaults.append(self.parse_expression())
- args.append(arg)
- self.stream.expect('rparen')
-
- def parse_call_block(self):
- node = nodes.CallBlock(lineno=next(self.stream).lineno)
- if self.stream.current.type == 'lparen':
- self.parse_signature(node)
- else:
- node.args = []
- node.defaults = []
-
- node.call = self.parse_expression()
- if not isinstance(node.call, nodes.Call):
- self.fail('expected call', node.lineno)
- node.body = self.parse_statements(('name:endcall',), drop_needle=True)
- return node
-
- def parse_filter_block(self):
- node = nodes.FilterBlock(lineno=next(self.stream).lineno)
- node.filter = self.parse_filter(None, start_inline=True)
- node.body = self.parse_statements(('name:endfilter',),
- drop_needle=True)
- return node
-
- def parse_macro(self):
- node = nodes.Macro(lineno=next(self.stream).lineno)
- node.name = self.parse_assign_target(name_only=True).name
- self.parse_signature(node)
- node.body = self.parse_statements(('name:endmacro',),
- drop_needle=True)
- return node
-
- def parse_print(self):
- node = nodes.Output(lineno=next(self.stream).lineno)
- node.nodes = []
- while self.stream.current.type != 'block_end':
- if node.nodes:
- self.stream.expect('comma')
- node.nodes.append(self.parse_expression())
- return node
-
- def parse_assign_target(self, with_tuple=True, name_only=False,
- extra_end_rules=None):
- """Parse an assignment target. As Jinja2 allows assignments to
- tuples, this function can parse all allowed assignment targets. Per
- default assignments to tuples are parsed, that can be disable however
- by setting `with_tuple` to `False`. If only assignments to names are
- wanted `name_only` can be set to `True`. The `extra_end_rules`
- parameter is forwarded to the tuple parsing function.
- """
- if name_only:
- token = self.stream.expect('name')
- target = nodes.Name(token.value, 'store', lineno=token.lineno)
- else:
- if with_tuple:
- target = self.parse_tuple(simplified=True,
- extra_end_rules=extra_end_rules)
- else:
- target = self.parse_primary()
- target.set_ctx('store')
- if not target.can_assign():
- self.fail('can\'t assign to %r' % target.__class__.
- __name__.lower(), target.lineno)
- return target
-
- def parse_expression(self, with_condexpr=True):
- """Parse an expression. Per default all expressions are parsed, if
- the optional `with_condexpr` parameter is set to `False` conditional
- expressions are not parsed.
- """
- if with_condexpr:
- return self.parse_condexpr()
- return self.parse_or()
-
- def parse_condexpr(self):
- lineno = self.stream.current.lineno
- expr1 = self.parse_or()
- while self.stream.skip_if('name:if'):
- expr2 = self.parse_or()
- if self.stream.skip_if('name:else'):
- expr3 = self.parse_condexpr()
- else:
- expr3 = None
- expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
- lineno = self.stream.current.lineno
- return expr1
-
- def parse_or(self):
- lineno = self.stream.current.lineno
- left = self.parse_and()
- while self.stream.skip_if('name:or'):
- right = self.parse_and()
- left = nodes.Or(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_and(self):
- lineno = self.stream.current.lineno
- left = self.parse_not()
- while self.stream.skip_if('name:and'):
- right = self.parse_not()
- left = nodes.And(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_not(self):
- if self.stream.current.test('name:not'):
- lineno = next(self.stream).lineno
- return nodes.Not(self.parse_not(), lineno=lineno)
- return self.parse_compare()
-
- def parse_compare(self):
- lineno = self.stream.current.lineno
- expr = self.parse_add()
- ops = []
- while 1:
- token_type = self.stream.current.type
- if token_type in _compare_operators:
- next(self.stream)
- ops.append(nodes.Operand(token_type, self.parse_add()))
- elif self.stream.skip_if('name:in'):
- ops.append(nodes.Operand('in', self.parse_add()))
- elif self.stream.current.test('name:not') and \
- self.stream.look().test('name:in'):
- self.stream.skip(2)
- ops.append(nodes.Operand('notin', self.parse_add()))
- else:
- break
- lineno = self.stream.current.lineno
- if not ops:
- return expr
- return nodes.Compare(expr, ops, lineno=lineno)
-
- def parse_add(self):
- lineno = self.stream.current.lineno
- left = self.parse_sub()
- while self.stream.current.type == 'add':
- next(self.stream)
- right = self.parse_sub()
- left = nodes.Add(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_sub(self):
- lineno = self.stream.current.lineno
- left = self.parse_concat()
- while self.stream.current.type == 'sub':
- next(self.stream)
- right = self.parse_concat()
- left = nodes.Sub(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_concat(self):
- lineno = self.stream.current.lineno
- args = [self.parse_mul()]
- while self.stream.current.type == 'tilde':
- next(self.stream)
- args.append(self.parse_mul())
- if len(args) == 1:
- return args[0]
- return nodes.Concat(args, lineno=lineno)
-
- def parse_mul(self):
- lineno = self.stream.current.lineno
- left = self.parse_div()
- while self.stream.current.type == 'mul':
- next(self.stream)
- right = self.parse_div()
- left = nodes.Mul(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_div(self):
- lineno = self.stream.current.lineno
- left = self.parse_floordiv()
- while self.stream.current.type == 'div':
- next(self.stream)
- right = self.parse_floordiv()
- left = nodes.Div(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_floordiv(self):
- lineno = self.stream.current.lineno
- left = self.parse_mod()
- while self.stream.current.type == 'floordiv':
- next(self.stream)
- right = self.parse_mod()
- left = nodes.FloorDiv(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_mod(self):
- lineno = self.stream.current.lineno
- left = self.parse_pow()
- while self.stream.current.type == 'mod':
- next(self.stream)
- right = self.parse_pow()
- left = nodes.Mod(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_pow(self):
- lineno = self.stream.current.lineno
- left = self.parse_unary()
- while self.stream.current.type == 'pow':
- next(self.stream)
- right = self.parse_unary()
- left = nodes.Pow(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_unary(self, with_filter=True):
- token_type = self.stream.current.type
- lineno = self.stream.current.lineno
- if token_type == 'sub':
- next(self.stream)
- node = nodes.Neg(self.parse_unary(False), lineno=lineno)
- elif token_type == 'add':
- next(self.stream)
- node = nodes.Pos(self.parse_unary(False), lineno=lineno)
- else:
- node = self.parse_primary()
- node = self.parse_postfix(node)
- if with_filter:
- node = self.parse_filter_expr(node)
- return node
-
- def parse_primary(self):
- token = self.stream.current
- if token.type == 'name':
- if token.value in ('true', 'false', 'True', 'False'):
- node = nodes.Const(token.value in ('true', 'True'),
- lineno=token.lineno)
- elif token.value in ('none', 'None'):
- node = nodes.Const(None, lineno=token.lineno)
- else:
- node = nodes.Name(token.value, 'load', lineno=token.lineno)
- next(self.stream)
- elif token.type == 'string':
- next(self.stream)
- buf = [token.value]
- lineno = token.lineno
- while self.stream.current.type == 'string':
- buf.append(self.stream.current.value)
- next(self.stream)
- node = nodes.Const(''.join(buf), lineno=lineno)
- elif token.type in ('integer', 'float'):
- next(self.stream)
- node = nodes.Const(token.value, lineno=token.lineno)
- elif token.type == 'lparen':
- next(self.stream)
- node = self.parse_tuple(explicit_parentheses=True)
- self.stream.expect('rparen')
- elif token.type == 'lbracket':
- node = self.parse_list()
- elif token.type == 'lbrace':
- node = self.parse_dict()
- else:
- self.fail("unexpected '%s'" % describe_token(token), token.lineno)
- return node
-
- def parse_tuple(self, simplified=False, with_condexpr=True,
- extra_end_rules=None, explicit_parentheses=False):
- """Works like `parse_expression` but if multiple expressions are
- delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
- This method could also return a regular expression instead of a tuple
- if no commas where found.
-
- The default parsing mode is a full tuple. If `simplified` is `True`
- only names and literals are parsed. The `no_condexpr` parameter is
- forwarded to :meth:`parse_expression`.
-
- Because tuples do not require delimiters and may end in a bogus comma
- an extra hint is needed that marks the end of a tuple. For example
- for loops support tuples between `for` and `in`. In that case the
- `extra_end_rules` is set to ``['name:in']``.
-
- `explicit_parentheses` is true if the parsing was triggered by an
- expression in parentheses. This is used to figure out if an empty
- tuple is a valid expression or not.
- """
- lineno = self.stream.current.lineno
- if simplified:
- parse = self.parse_primary
- elif with_condexpr:
- parse = self.parse_expression
- else:
- parse = lambda: self.parse_expression(with_condexpr=False)
- args = []
- is_tuple = False
- while 1:
- if args:
- self.stream.expect('comma')
- if self.is_tuple_end(extra_end_rules):
- break
- args.append(parse())
- if self.stream.current.type == 'comma':
- is_tuple = True
- else:
- break
- lineno = self.stream.current.lineno
-
- if not is_tuple:
- if args:
- return args[0]
-
- # if we don't have explicit parentheses, an empty tuple is
- # not a valid expression. This would mean nothing (literally
- # nothing) in the spot of an expression would be an empty
- # tuple.
- if not explicit_parentheses:
- self.fail('Expected an expression, got \'%s\'' %
- describe_token(self.stream.current))
-
- return nodes.Tuple(args, 'load', lineno=lineno)
-
- def parse_list(self):
- token = self.stream.expect('lbracket')
- items = []
- while self.stream.current.type != 'rbracket':
- if items:
- self.stream.expect('comma')
- if self.stream.current.type == 'rbracket':
- break
- items.append(self.parse_expression())
- self.stream.expect('rbracket')
- return nodes.List(items, lineno=token.lineno)
-
- def parse_dict(self):
- token = self.stream.expect('lbrace')
- items = []
- while self.stream.current.type != 'rbrace':
- if items:
- self.stream.expect('comma')
- if self.stream.current.type == 'rbrace':
- break
- key = self.parse_expression()
- self.stream.expect('colon')
- value = self.parse_expression()
- items.append(nodes.Pair(key, value, lineno=key.lineno))
- self.stream.expect('rbrace')
- return nodes.Dict(items, lineno=token.lineno)
-
- def parse_postfix(self, node):
- while 1:
- token_type = self.stream.current.type
- if token_type == 'dot' or token_type == 'lbracket':
- node = self.parse_subscript(node)
- # calls are valid both after postfix expressions (getattr
- # and getitem) as well as filters and tests
- elif token_type == 'lparen':
- node = self.parse_call(node)
- else:
- break
- return node
-
- def parse_filter_expr(self, node):
- while 1:
- token_type = self.stream.current.type
- if token_type == 'pipe':
- node = self.parse_filter(node)
- elif token_type == 'name' and self.stream.current.value == 'is':
- node = self.parse_test(node)
- # calls are valid both after postfix expressions (getattr
- # and getitem) as well as filters and tests
- elif token_type == 'lparen':
- node = self.parse_call(node)
- else:
- break
- return node
-
- def parse_subscript(self, node):
- token = next(self.stream)
- if token.type == 'dot':
- attr_token = self.stream.current
- next(self.stream)
- if attr_token.type == 'name':
- return nodes.Getattr(node, attr_token.value, 'load',
- lineno=token.lineno)
- elif attr_token.type != 'integer':
- self.fail('expected name or number', attr_token.lineno)
- arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
- return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
- if token.type == 'lbracket':
- priority_on_attribute = False
- args = []
- while self.stream.current.type != 'rbracket':
- if args:
- self.stream.expect('comma')
- args.append(self.parse_subscribed())
- self.stream.expect('rbracket')
- if len(args) == 1:
- arg = args[0]
- else:
- arg = nodes.Tuple(args, 'load', lineno=token.lineno)
- return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
- self.fail('expected subscript expression', self.lineno)
-
- def parse_subscribed(self):
- lineno = self.stream.current.lineno
-
- if self.stream.current.type == 'colon':
- next(self.stream)
- args = [None]
- else:
- node = self.parse_expression()
- if self.stream.current.type != 'colon':
- return node
- next(self.stream)
- args = [node]
-
- if self.stream.current.type == 'colon':
- args.append(None)
- elif self.stream.current.type not in ('rbracket', 'comma'):
- args.append(self.parse_expression())
- else:
- args.append(None)
-
- if self.stream.current.type == 'colon':
- next(self.stream)
- if self.stream.current.type not in ('rbracket', 'comma'):
- args.append(self.parse_expression())
- else:
- args.append(None)
- else:
- args.append(None)
-
- return nodes.Slice(lineno=lineno, *args)
-
- def parse_call(self, node):
- token = self.stream.expect('lparen')
- args = []
- kwargs = []
- dyn_args = dyn_kwargs = None
- require_comma = False
-
- def ensure(expr):
- if not expr:
- self.fail('invalid syntax for function call expression',
- token.lineno)
-
- while self.stream.current.type != 'rparen':
- if require_comma:
- self.stream.expect('comma')
- # support for trailing comma
- if self.stream.current.type == 'rparen':
- break
- if self.stream.current.type == 'mul':
- ensure(dyn_args is None and dyn_kwargs is None)
- next(self.stream)
- dyn_args = self.parse_expression()
- elif self.stream.current.type == 'pow':
- ensure(dyn_kwargs is None)
- next(self.stream)
- dyn_kwargs = self.parse_expression()
- else:
- ensure(dyn_args is None and dyn_kwargs is None)
- if self.stream.current.type == 'name' and \
- self.stream.look().type == 'assign':
- key = self.stream.current.value
- self.stream.skip(2)
- value = self.parse_expression()
- kwargs.append(nodes.Keyword(key, value,
- lineno=value.lineno))
- else:
- ensure(not kwargs)
- args.append(self.parse_expression())
-
- require_comma = True
- self.stream.expect('rparen')
-
- if node is None:
- return args, kwargs, dyn_args, dyn_kwargs
- return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
- lineno=token.lineno)
-
- def parse_filter(self, node, start_inline=False):
- while self.stream.current.type == 'pipe' or start_inline:
- if not start_inline:
- next(self.stream)
- token = self.stream.expect('name')
- name = token.value
- while self.stream.current.type == 'dot':
- next(self.stream)
- name += '.' + self.stream.expect('name').value
- if self.stream.current.type == 'lparen':
- args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
- else:
- args = []
- kwargs = []
- dyn_args = dyn_kwargs = None
- node = nodes.Filter(node, name, args, kwargs, dyn_args,
- dyn_kwargs, lineno=token.lineno)
- start_inline = False
- return node
-
- def parse_test(self, node):
- token = next(self.stream)
- if self.stream.current.test('name:not'):
- next(self.stream)
- negated = True
- else:
- negated = False
- name = self.stream.expect('name').value
- while self.stream.current.type == 'dot':
- next(self.stream)
- name += '.' + self.stream.expect('name').value
- dyn_args = dyn_kwargs = None
- kwargs = []
- if self.stream.current.type == 'lparen':
- args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
- elif self.stream.current.type in ('name', 'string', 'integer',
- 'float', 'lparen', 'lbracket',
- 'lbrace') and not \
- self.stream.current.test_any('name:else', 'name:or',
- 'name:and'):
- if self.stream.current.test('name:is'):
- self.fail('You cannot chain multiple tests with is')
- args = [self.parse_expression()]
- else:
- args = []
- node = nodes.Test(node, name, args, kwargs, dyn_args,
- dyn_kwargs, lineno=token.lineno)
- if negated:
- node = nodes.Not(node, lineno=token.lineno)
- return node
-
- def subparse(self, end_tokens=None):
- body = []
- data_buffer = []
- add_data = data_buffer.append
-
- if end_tokens is not None:
- self._end_token_stack.append(end_tokens)
-
- def flush_data():
- if data_buffer:
- lineno = data_buffer[0].lineno
- body.append(nodes.Output(data_buffer[:], lineno=lineno))
- del data_buffer[:]
-
- try:
- while self.stream:
- token = self.stream.current
- if token.type == 'data':
- if token.value:
- add_data(nodes.TemplateData(token.value,
- lineno=token.lineno))
- next(self.stream)
- elif token.type == 'variable_begin':
- next(self.stream)
- add_data(self.parse_tuple(with_condexpr=True))
- self.stream.expect('variable_end')
- elif token.type == 'block_begin':
- flush_data()
- next(self.stream)
- if end_tokens is not None and \
- self.stream.current.test_any(*end_tokens):
- return body
- rv = self.parse_statement()
- if isinstance(rv, list):
- body.extend(rv)
- else:
- body.append(rv)
- self.stream.expect('block_end')
- else:
- raise AssertionError('internal parsing error')
-
- flush_data()
- finally:
- if end_tokens is not None:
- self._end_token_stack.pop()
-
- return body
-
- def parse(self):
- """Parse the whole template into a `Template` node."""
- result = nodes.Template(self.subparse(), lineno=1)
- result.set_environment(self.environment)
- return result
diff --git a/module/lib/jinja2/runtime.py b/module/lib/jinja2/runtime.py
deleted file mode 100644
index 6fea3aa4f..000000000
--- a/module/lib/jinja2/runtime.py
+++ /dev/null
@@ -1,544 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.runtime
- ~~~~~~~~~~~~~~
-
- Runtime helpers.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-import sys
-from itertools import chain, imap
-from jinja2.nodes import EvalContext, _context_function_types
-from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \
- concat, internalcode, next, object_type_repr
-from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
- TemplateNotFound
-
-
-# these variables are exported to the template runtime
-__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
- 'TemplateRuntimeError', 'missing', 'concat', 'escape',
- 'markup_join', 'unicode_join', 'to_string', 'identity',
- 'TemplateNotFound']
-
-#: the name of the function that is used to convert something into
-#: a string. 2to3 will adopt that automatically and the generated
-#: code can take advantage of it.
-to_string = unicode
-
-#: the identity function. Useful for certain things in the environment
-identity = lambda x: x
-
-
-def markup_join(seq):
- """Concatenation that escapes if necessary and converts to unicode."""
- buf = []
- iterator = imap(soft_unicode, seq)
- for arg in iterator:
- buf.append(arg)
- if hasattr(arg, '__html__'):
- return Markup(u'').join(chain(buf, iterator))
- return concat(buf)
-
-
-def unicode_join(seq):
- """Simple args to unicode conversion and concatenation."""
- return concat(imap(unicode, seq))
-
-
-def new_context(environment, template_name, blocks, vars=None,
- shared=None, globals=None, locals=None):
- """Internal helper to for context creation."""
- if vars is None:
- vars = {}
- if shared:
- parent = vars
- else:
- parent = dict(globals or (), **vars)
- if locals:
- # if the parent is shared a copy should be created because
- # we don't want to modify the dict passed
- if shared:
- parent = dict(parent)
- for key, value in locals.iteritems():
- if key[:2] == 'l_' and value is not missing:
- parent[key[2:]] = value
- return Context(environment, parent, template_name, blocks)
-
-
-class TemplateReference(object):
- """The `self` in templates."""
-
- def __init__(self, context):
- self.__context = context
-
- def __getitem__(self, name):
- blocks = self.__context.blocks[name]
- wrap = self.__context.eval_ctx.autoescape and \
- Markup or (lambda x: x)
- return BlockReference(name, self.__context, blocks, 0)
-
- def __repr__(self):
- return '<%s %r>' % (
- self.__class__.__name__,
- self.__context.name
- )
-
-
-class Context(object):
- """The template context holds the variables of a template. It stores the
- values passed to the template and also the names the template exports.
- Creating instances is neither supported nor useful as it's created
- automatically at various stages of the template evaluation and should not
- be created by hand.
-
- The context is immutable. Modifications on :attr:`parent` **must not**
- happen and modifications on :attr:`vars` are allowed from generated
- template code only. Template filters and global functions marked as
- :func:`contextfunction`\s get the active context passed as first argument
- and are allowed to access the context read-only.
-
- The template context supports read only dict operations (`get`,
- `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
- `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
- method that doesn't fail with a `KeyError` but returns an
- :class:`Undefined` object for missing variables.
- """
- __slots__ = ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars',
- 'name', 'blocks', '__weakref__')
-
- def __init__(self, environment, parent, name, blocks):
- self.parent = parent
- self.vars = {}
- self.environment = environment
- self.eval_ctx = EvalContext(self.environment, name)
- self.exported_vars = set()
- self.name = name
-
- # create the initial mapping of blocks. Whenever template inheritance
- # takes place the runtime will update this mapping with the new blocks
- # from the template.
- self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
-
- def super(self, name, current):
- """Render a parent block."""
- try:
- blocks = self.blocks[name]
- index = blocks.index(current) + 1
- blocks[index]
- except LookupError:
- return self.environment.undefined('there is no parent block '
- 'called %r.' % name,
- name='super')
- return BlockReference(name, self, blocks, index)
-
- def get(self, key, default=None):
- """Returns an item from the template context, if it doesn't exist
- `default` is returned.
- """
- try:
- return self[key]
- except KeyError:
- return default
-
- def resolve(self, key):
- """Looks up a variable like `__getitem__` or `get` but returns an
- :class:`Undefined` object with the name of the name looked up.
- """
- if key in self.vars:
- return self.vars[key]
- if key in self.parent:
- return self.parent[key]
- return self.environment.undefined(name=key)
-
- def get_exported(self):
- """Get a new dict with the exported variables."""
- return dict((k, self.vars[k]) for k in self.exported_vars)
-
- def get_all(self):
- """Return a copy of the complete context as dict including the
- exported variables.
- """
- return dict(self.parent, **self.vars)
-
- @internalcode
- def call(__self, __obj, *args, **kwargs):
- """Call the callable with the arguments and keyword arguments
- provided but inject the active context or environment as first
- argument if the callable is a :func:`contextfunction` or
- :func:`environmentfunction`.
- """
- if __debug__:
- __traceback_hide__ = True
- if isinstance(__obj, _context_function_types):
- if getattr(__obj, 'contextfunction', 0):
- args = (__self,) + args
- elif getattr(__obj, 'evalcontextfunction', 0):
- args = (__self.eval_ctx,) + args
- elif getattr(__obj, 'environmentfunction', 0):
- args = (__self.environment,) + args
- try:
- return __obj(*args, **kwargs)
- except StopIteration:
- return __self.environment.undefined('value was undefined because '
- 'a callable raised a '
- 'StopIteration exception')
-
- def derived(self, locals=None):
- """Internal helper function to create a derived context."""
- context = new_context(self.environment, self.name, {},
- self.parent, True, None, locals)
- context.eval_ctx = self.eval_ctx
- context.blocks.update((k, list(v)) for k, v in self.blocks.iteritems())
- return context
-
- def _all(meth):
- proxy = lambda self: getattr(self.get_all(), meth)()
- proxy.__doc__ = getattr(dict, meth).__doc__
- proxy.__name__ = meth
- return proxy
-
- keys = _all('keys')
- values = _all('values')
- items = _all('items')
-
- # not available on python 3
- if hasattr(dict, 'iterkeys'):
- iterkeys = _all('iterkeys')
- itervalues = _all('itervalues')
- iteritems = _all('iteritems')
- del _all
-
- def __contains__(self, name):
- return name in self.vars or name in self.parent
-
- def __getitem__(self, key):
- """Lookup a variable or raise `KeyError` if the variable is
- undefined.
- """
- item = self.resolve(key)
- if isinstance(item, Undefined):
- raise KeyError(key)
- return item
-
- def __repr__(self):
- return '<%s %s of %r>' % (
- self.__class__.__name__,
- repr(self.get_all()),
- self.name
- )
-
-
-# register the context as mapping if possible
-try:
- from collections import Mapping
- Mapping.register(Context)
-except ImportError:
- pass
-
-
-class BlockReference(object):
- """One block on a template reference."""
-
- def __init__(self, name, context, stack, depth):
- self.name = name
- self._context = context
- self._stack = stack
- self._depth = depth
-
- @property
- def super(self):
- """Super the block."""
- if self._depth + 1 >= len(self._stack):
- return self._context.environment. \
- undefined('there is no parent block called %r.' %
- self.name, name='super')
- return BlockReference(self.name, self._context, self._stack,
- self._depth + 1)
-
- @internalcode
- def __call__(self):
- rv = concat(self._stack[self._depth](self._context))
- if self._context.eval_ctx.autoescape:
- rv = Markup(rv)
- return rv
-
-
-class LoopContext(object):
- """A loop context for dynamic iteration."""
-
- def __init__(self, iterable, recurse=None):
- self._iterator = iter(iterable)
- self._recurse = recurse
- self.index0 = -1
-
- # try to get the length of the iterable early. This must be done
- # here because there are some broken iterators around where there
- # __len__ is the number of iterations left (i'm looking at your
- # listreverseiterator!).
- try:
- self._length = len(iterable)
- except (TypeError, AttributeError):
- self._length = None
-
- def cycle(self, *args):
- """Cycles among the arguments with the current loop index."""
- if not args:
- raise TypeError('no items for cycling given')
- return args[self.index0 % len(args)]
-
- first = property(lambda x: x.index0 == 0)
- last = property(lambda x: x.index0 + 1 == x.length)
- index = property(lambda x: x.index0 + 1)
- revindex = property(lambda x: x.length - x.index0)
- revindex0 = property(lambda x: x.length - x.index)
-
- def __len__(self):
- return self.length
-
- def __iter__(self):
- return LoopContextIterator(self)
-
- @internalcode
- def loop(self, iterable):
- if self._recurse is None:
- raise TypeError('Tried to call non recursive loop. Maybe you '
- "forgot the 'recursive' modifier.")
- return self._recurse(iterable, self._recurse)
-
- # a nifty trick to enhance the error message if someone tried to call
- # the the loop without or with too many arguments.
- __call__ = loop
- del loop
-
- @property
- def length(self):
- if self._length is None:
- # if was not possible to get the length of the iterator when
- # the loop context was created (ie: iterating over a generator)
- # we have to convert the iterable into a sequence and use the
- # length of that.
- iterable = tuple(self._iterator)
- self._iterator = iter(iterable)
- self._length = len(iterable) + self.index0 + 1
- return self._length
-
- def __repr__(self):
- return '<%s %r/%r>' % (
- self.__class__.__name__,
- self.index,
- self.length
- )
-
-
-class LoopContextIterator(object):
- """The iterator for a loop context."""
- __slots__ = ('context',)
-
- def __init__(self, context):
- self.context = context
-
- def __iter__(self):
- return self
-
- def next(self):
- ctx = self.context
- ctx.index0 += 1
- return next(ctx._iterator), ctx
-
-
-class Macro(object):
- """Wraps a macro function."""
-
- def __init__(self, environment, func, name, arguments, defaults,
- catch_kwargs, catch_varargs, caller):
- self._environment = environment
- self._func = func
- self._argument_count = len(arguments)
- self.name = name
- self.arguments = arguments
- self.defaults = defaults
- self.catch_kwargs = catch_kwargs
- self.catch_varargs = catch_varargs
- self.caller = caller
-
- @internalcode
- def __call__(self, *args, **kwargs):
- # try to consume the positional arguments
- arguments = list(args[:self._argument_count])
- off = len(arguments)
-
- # if the number of arguments consumed is not the number of
- # arguments expected we start filling in keyword arguments
- # and defaults.
- if off != self._argument_count:
- for idx, name in enumerate(self.arguments[len(arguments):]):
- try:
- value = kwargs.pop(name)
- except KeyError:
- try:
- value = self.defaults[idx - self._argument_count + off]
- except IndexError:
- value = self._environment.undefined(
- 'parameter %r was not provided' % name, name=name)
- arguments.append(value)
-
- # it's important that the order of these arguments does not change
- # if not also changed in the compiler's `function_scoping` method.
- # the order is caller, keyword arguments, positional arguments!
- if self.caller:
- caller = kwargs.pop('caller', None)
- if caller is None:
- caller = self._environment.undefined('No caller defined',
- name='caller')
- arguments.append(caller)
- if self.catch_kwargs:
- arguments.append(kwargs)
- elif kwargs:
- raise TypeError('macro %r takes no keyword argument %r' %
- (self.name, next(iter(kwargs))))
- if self.catch_varargs:
- arguments.append(args[self._argument_count:])
- elif len(args) > self._argument_count:
- raise TypeError('macro %r takes not more than %d argument(s)' %
- (self.name, len(self.arguments)))
- return self._func(*arguments)
-
- def __repr__(self):
- return '<%s %s>' % (
- self.__class__.__name__,
- self.name is None and 'anonymous' or repr(self.name)
- )
-
-
-class Undefined(object):
- """The default undefined type. This undefined type can be printed and
- iterated over, but every other access will raise an :exc:`UndefinedError`:
-
- >>> foo = Undefined(name='foo')
- >>> str(foo)
- ''
- >>> not foo
- True
- >>> foo + 42
- Traceback (most recent call last):
- ...
- UndefinedError: 'foo' is undefined
- """
- __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
- '_undefined_exception')
-
- def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError):
- self._undefined_hint = hint
- self._undefined_obj = obj
- self._undefined_name = name
- self._undefined_exception = exc
-
- @internalcode
- def _fail_with_undefined_error(self, *args, **kwargs):
- """Regular callback function for undefined objects that raises an
- `UndefinedError` on call.
- """
- if self._undefined_hint is None:
- if self._undefined_obj is missing:
- hint = '%r is undefined' % self._undefined_name
- elif not isinstance(self._undefined_name, basestring):
- hint = '%s has no element %r' % (
- object_type_repr(self._undefined_obj),
- self._undefined_name
- )
- else:
- hint = '%r has no attribute %r' % (
- object_type_repr(self._undefined_obj),
- self._undefined_name
- )
- else:
- hint = self._undefined_hint
- raise self._undefined_exception(hint)
-
- __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
- __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \
- __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
- __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
- __int__ = __float__ = __complex__ = __pow__ = __rpow__ = \
- _fail_with_undefined_error
-
- def __str__(self):
- return unicode(self).encode('utf-8')
-
- # unicode goes after __str__ because we configured 2to3 to rename
- # __unicode__ to __str__. because the 2to3 tree is not designed to
- # remove nodes from it, we leave the above __str__ around and let
- # it override at runtime.
- def __unicode__(self):
- return u''
-
- def __len__(self):
- return 0
-
- def __iter__(self):
- if 0:
- yield None
-
- def __nonzero__(self):
- return False
-
- def __repr__(self):
- return 'Undefined'
-
-
-class DebugUndefined(Undefined):
- """An undefined that returns the debug info when printed.
-
- >>> foo = DebugUndefined(name='foo')
- >>> str(foo)
- '{{ foo }}'
- >>> not foo
- True
- >>> foo + 42
- Traceback (most recent call last):
- ...
- UndefinedError: 'foo' is undefined
- """
- __slots__ = ()
-
- def __unicode__(self):
- if self._undefined_hint is None:
- if self._undefined_obj is missing:
- return u'{{ %s }}' % self._undefined_name
- return '{{ no such element: %s[%r] }}' % (
- object_type_repr(self._undefined_obj),
- self._undefined_name
- )
- return u'{{ undefined value printed: %s }}' % self._undefined_hint
-
-
-class StrictUndefined(Undefined):
- """An undefined that barks on print and iteration as well as boolean
- tests and all kinds of comparisons. In other words: you can do nothing
- with it except checking if it's defined using the `defined` test.
-
- >>> foo = StrictUndefined(name='foo')
- >>> str(foo)
- Traceback (most recent call last):
- ...
- UndefinedError: 'foo' is undefined
- >>> not foo
- Traceback (most recent call last):
- ...
- UndefinedError: 'foo' is undefined
- >>> foo + 42
- Traceback (most recent call last):
- ...
- UndefinedError: 'foo' is undefined
- """
- __slots__ = ()
- __iter__ = __unicode__ = __str__ = __len__ = __nonzero__ = __eq__ = \
- __ne__ = __bool__ = Undefined._fail_with_undefined_error
-
-
-# remove remaining slots attributes, after the metaclass did the magic they
-# are unneeded and irritating as they contain wrong data for the subclasses.
-del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__
diff --git a/module/lib/jinja2/sandbox.py b/module/lib/jinja2/sandbox.py
deleted file mode 100644
index 749719548..000000000
--- a/module/lib/jinja2/sandbox.py
+++ /dev/null
@@ -1,271 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.sandbox
- ~~~~~~~~~~~~~~
-
- Adds a sandbox layer to Jinja as it was the default behavior in the old
- Jinja 1 releases. This sandbox is slightly different from Jinja 1 as the
- default behavior is easier to use.
-
- The behavior can be changed by subclassing the environment.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-import operator
-from jinja2.runtime import Undefined
-from jinja2.environment import Environment
-from jinja2.exceptions import SecurityError
-from jinja2.utils import FunctionType, MethodType, TracebackType, CodeType, \
- FrameType, GeneratorType
-
-
-#: maximum number of items a range may produce
-MAX_RANGE = 100000
-
-#: attributes of function objects that are considered unsafe.
-UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
- 'func_defaults', 'func_globals'])
-
-#: unsafe method attributes. function attributes are unsafe for methods too
-UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
-
-
-import warnings
-
-# make sure we don't warn in python 2.6 about stuff we don't care about
-warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning,
- module='jinja2.sandbox')
-
-from collections import deque
-
-_mutable_set_types = (set,)
-_mutable_mapping_types = (dict,)
-_mutable_sequence_types = (list,)
-
-
-# on python 2.x we can register the user collection types
-try:
- from UserDict import UserDict, DictMixin
- from UserList import UserList
- _mutable_mapping_types += (UserDict, DictMixin)
- _mutable_set_types += (UserList,)
-except ImportError:
- pass
-
-# if sets is still available, register the mutable set from there as well
-try:
- from sets import Set
- _mutable_set_types += (Set,)
-except ImportError:
- pass
-
-#: register Python 2.6 abstract base classes
-try:
- from collections import MutableSet, MutableMapping, MutableSequence
- _mutable_set_types += (MutableSet,)
- _mutable_mapping_types += (MutableMapping,)
- _mutable_sequence_types += (MutableSequence,)
-except ImportError:
- pass
-
-_mutable_spec = (
- (_mutable_set_types, frozenset([
- 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
- 'symmetric_difference_update', 'update'
- ])),
- (_mutable_mapping_types, frozenset([
- 'clear', 'pop', 'popitem', 'setdefault', 'update'
- ])),
- (_mutable_sequence_types, frozenset([
- 'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
- ])),
- (deque, frozenset([
- 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
- 'popleft', 'remove', 'rotate'
- ]))
-)
-
-
-def safe_range(*args):
- """A range that can't generate ranges with a length of more than
- MAX_RANGE items.
- """
- rng = xrange(*args)
- if len(rng) > MAX_RANGE:
- raise OverflowError('range too big, maximum size for range is %d' %
- MAX_RANGE)
- return rng
-
-
-def unsafe(f):
- """
- Mark a function or method as unsafe::
-
- @unsafe
- def delete(self):
- pass
- """
- f.unsafe_callable = True
- return f
-
-
-def is_internal_attribute(obj, attr):
- """Test if the attribute given is an internal python attribute. For
- example this function returns `True` for the `func_code` attribute of
- python objects. This is useful if the environment method
- :meth:`~SandboxedEnvironment.is_safe_attribute` is overriden.
-
- >>> from jinja2.sandbox import is_internal_attribute
- >>> is_internal_attribute(lambda: None, "func_code")
- True
- >>> is_internal_attribute((lambda x:x).func_code, 'co_code')
- True
- >>> is_internal_attribute(str, "upper")
- False
- """
- if isinstance(obj, FunctionType):
- if attr in UNSAFE_FUNCTION_ATTRIBUTES:
- return True
- elif isinstance(obj, MethodType):
- if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
- attr in UNSAFE_METHOD_ATTRIBUTES:
- return True
- elif isinstance(obj, type):
- if attr == 'mro':
- return True
- elif isinstance(obj, (CodeType, TracebackType, FrameType)):
- return True
- elif isinstance(obj, GeneratorType):
- if attr == 'gi_frame':
- return True
- return attr.startswith('__')
-
-
-def modifies_known_mutable(obj, attr):
- """This function checks if an attribute on a builtin mutable object
- (list, dict, set or deque) would modify it if called. It also supports
- the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
- with Python 2.6 onwards the abstract base classes `MutableSet`,
- `MutableMapping`, and `MutableSequence`.
-
- >>> modifies_known_mutable({}, "clear")
- True
- >>> modifies_known_mutable({}, "keys")
- False
- >>> modifies_known_mutable([], "append")
- True
- >>> modifies_known_mutable([], "index")
- False
-
- If called with an unsupported object (such as unicode) `False` is
- returned.
-
- >>> modifies_known_mutable("foo", "upper")
- False
- """
- for typespec, unsafe in _mutable_spec:
- if isinstance(obj, typespec):
- return attr in unsafe
- return False
-
-
-class SandboxedEnvironment(Environment):
- """The sandboxed environment. It works like the regular environment but
- tells the compiler to generate sandboxed code. Additionally subclasses of
- this environment may override the methods that tell the runtime what
- attributes or functions are safe to access.
-
- If the template tries to access insecure code a :exc:`SecurityError` is
- raised. However also other exceptions may occour during the rendering so
- the caller has to ensure that all exceptions are catched.
- """
- sandboxed = True
-
- def __init__(self, *args, **kwargs):
- Environment.__init__(self, *args, **kwargs)
- self.globals['range'] = safe_range
-
- def is_safe_attribute(self, obj, attr, value):
- """The sandboxed environment will call this method to check if the
- attribute of an object is safe to access. Per default all attributes
- starting with an underscore are considered private as well as the
- special attributes of internal python objects as returned by the
- :func:`is_internal_attribute` function.
- """
- return not (attr.startswith('_') or is_internal_attribute(obj, attr))
-
- def is_safe_callable(self, obj):
- """Check if an object is safely callable. Per default a function is
- considered safe unless the `unsafe_callable` attribute exists and is
- True. Override this method to alter the behavior, but this won't
- affect the `unsafe` decorator from this module.
- """
- return not (getattr(obj, 'unsafe_callable', False) or \
- getattr(obj, 'alters_data', False))
-
- def getitem(self, obj, argument):
- """Subscribe an object from sandboxed code."""
- try:
- return obj[argument]
- except (TypeError, LookupError):
- if isinstance(argument, basestring):
- try:
- attr = str(argument)
- except:
- pass
- else:
- try:
- value = getattr(obj, attr)
- except AttributeError:
- pass
- else:
- if self.is_safe_attribute(obj, argument, value):
- return value
- return self.unsafe_undefined(obj, argument)
- return self.undefined(obj=obj, name=argument)
-
- def getattr(self, obj, attribute):
- """Subscribe an object from sandboxed code and prefer the
- attribute. The attribute passed *must* be a bytestring.
- """
- try:
- value = getattr(obj, attribute)
- except AttributeError:
- try:
- return obj[attribute]
- except (TypeError, LookupError):
- pass
- else:
- if self.is_safe_attribute(obj, attribute, value):
- return value
- return self.unsafe_undefined(obj, attribute)
- return self.undefined(obj=obj, name=attribute)
-
- def unsafe_undefined(self, obj, attribute):
- """Return an undefined object for unsafe attributes."""
- return self.undefined('access to attribute %r of %r '
- 'object is unsafe.' % (
- attribute,
- obj.__class__.__name__
- ), name=attribute, obj=obj, exc=SecurityError)
-
- def call(__self, __context, __obj, *args, **kwargs):
- """Call an object from sandboxed code."""
- # the double prefixes are to avoid double keyword argument
- # errors when proxying the call.
- if not __self.is_safe_callable(__obj):
- raise SecurityError('%r is not safely callable' % (__obj,))
- return __context.call(__obj, *args, **kwargs)
-
-
-class ImmutableSandboxedEnvironment(SandboxedEnvironment):
- """Works exactly like the regular `SandboxedEnvironment` but does not
- permit modifications on the builtin mutable objects `list`, `set`, and
- `dict` by using the :func:`modifies_known_mutable` function.
- """
-
- def is_safe_attribute(self, obj, attr, value):
- if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
- return False
- return not modifies_known_mutable(obj, attr)
diff --git a/module/lib/jinja2/tests.py b/module/lib/jinja2/tests.py
deleted file mode 100644
index d257eca0a..000000000
--- a/module/lib/jinja2/tests.py
+++ /dev/null
@@ -1,146 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.tests
- ~~~~~~~~~~~~
-
- Jinja test functions. Used with the "is" operator.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import re
-from jinja2.runtime import Undefined
-
-# nose, nothing here to test
-__test__ = False
-
-
-number_re = re.compile(r'^-?\d+(\.\d+)?$')
-regex_type = type(number_re)
-
-
-try:
- test_callable = callable
-except NameError:
- def test_callable(x):
- return hasattr(x, '__call__')
-
-
-def test_odd(value):
- """Return true if the variable is odd."""
- return value % 2 == 1
-
-
-def test_even(value):
- """Return true if the variable is even."""
- return value % 2 == 0
-
-
-def test_divisibleby(value, num):
- """Check if a variable is divisible by a number."""
- return value % num == 0
-
-
-def test_defined(value):
- """Return true if the variable is defined:
-
- .. sourcecode:: jinja
-
- {% if variable is defined %}
- value of variable: {{ variable }}
- {% else %}
- variable is not defined
- {% endif %}
-
- See the :func:`default` filter for a simple way to set undefined
- variables.
- """
- return not isinstance(value, Undefined)
-
-
-def test_undefined(value):
- """Like :func:`defined` but the other way round."""
- return isinstance(value, Undefined)
-
-
-def test_none(value):
- """Return true if the variable is none."""
- return value is None
-
-
-def test_lower(value):
- """Return true if the variable is lowercased."""
- return unicode(value).islower()
-
-
-def test_upper(value):
- """Return true if the variable is uppercased."""
- return unicode(value).isupper()
-
-
-def test_string(value):
- """Return true if the object is a string."""
- return isinstance(value, basestring)
-
-
-def test_number(value):
- """Return true if the variable is a number."""
- return isinstance(value, (int, long, float, complex))
-
-
-def test_sequence(value):
- """Return true if the variable is a sequence. Sequences are variables
- that are iterable.
- """
- try:
- len(value)
- value.__getitem__
- except:
- return False
- return True
-
-
-def test_sameas(value, other):
- """Check if an object points to the same memory address than another
- object:
-
- .. sourcecode:: jinja
-
- {% if foo.attribute is sameas false %}
- the foo attribute really is the `False` singleton
- {% endif %}
- """
- return value is other
-
-
-def test_iterable(value):
- """Check if it's possible to iterate over an object."""
- try:
- iter(value)
- except TypeError:
- return False
- return True
-
-
-def test_escaped(value):
- """Check if the value is escaped."""
- return hasattr(value, '__html__')
-
-
-TESTS = {
- 'odd': test_odd,
- 'even': test_even,
- 'divisibleby': test_divisibleby,
- 'defined': test_defined,
- 'undefined': test_undefined,
- 'none': test_none,
- 'lower': test_lower,
- 'upper': test_upper,
- 'string': test_string,
- 'number': test_number,
- 'sequence': test_sequence,
- 'iterable': test_iterable,
- 'callable': test_callable,
- 'sameas': test_sameas,
- 'escaped': test_escaped
-}
diff --git a/module/lib/jinja2/utils.py b/module/lib/jinja2/utils.py
deleted file mode 100644
index 7b77b8eb7..000000000
--- a/module/lib/jinja2/utils.py
+++ /dev/null
@@ -1,601 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.utils
- ~~~~~~~~~~~~
-
- Utility functions.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import re
-import sys
-import errno
-try:
- from thread import allocate_lock
-except ImportError:
- from dummy_thread import allocate_lock
-from collections import deque
-from itertools import imap
-
-
-_word_split_re = re.compile(r'(\s+)')
-_punctuation_re = re.compile(
- '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
- '|'.join(imap(re.escape, ('(', '<', '&lt;'))),
- '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
- )
-)
-_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
-_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
-_entity_re = re.compile(r'&([^;]+);')
-_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
-_digits = '0123456789'
-
-# special singleton representing missing values for the runtime
-missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
-
-# internal code
-internal_code = set()
-
-
-# concatenate a list of strings and convert them to unicode.
-# unfortunately there is a bug in python 2.4 and lower that causes
-# unicode.join trash the traceback.
-_concat = u''.join
-try:
- def _test_gen_bug():
- raise TypeError(_test_gen_bug)
- yield None
- _concat(_test_gen_bug())
-except TypeError, _error:
- if not _error.args or _error.args[0] is not _test_gen_bug:
- def concat(gen):
- try:
- return _concat(list(gen))
- except:
- # this hack is needed so that the current frame
- # does not show up in the traceback.
- exc_type, exc_value, tb = sys.exc_info()
- raise exc_type, exc_value, tb.tb_next
- else:
- concat = _concat
- del _test_gen_bug, _error
-
-
-# for python 2.x we create outselves a next() function that does the
-# basics without exception catching.
-try:
- next = next
-except NameError:
- def next(x):
- return x.next()
-
-
-# if this python version is unable to deal with unicode filenames
-# when passed to encode we let this function encode it properly.
-# This is used in a couple of places. As far as Jinja is concerned
-# filenames are unicode *or* bytestrings in 2.x and unicode only in
-# 3.x because compile cannot handle bytes
-if sys.version_info < (3, 0):
- def _encode_filename(filename):
- if isinstance(filename, unicode):
- return filename.encode('utf-8')
- return filename
-else:
- def _encode_filename(filename):
- assert filename is None or isinstance(filename, str), \
- 'filenames must be strings'
- return filename
-
-from keyword import iskeyword as is_python_keyword
-
-
-# common types. These do exist in the special types module too which however
-# does not exist in IronPython out of the box. Also that way we don't have
-# to deal with implementation specific stuff here
-class _C(object):
- def method(self): pass
-def _func():
- yield None
-FunctionType = type(_func)
-GeneratorType = type(_func())
-MethodType = type(_C.method)
-CodeType = type(_C.method.func_code)
-try:
- raise TypeError()
-except TypeError:
- _tb = sys.exc_info()[2]
- TracebackType = type(_tb)
- FrameType = type(_tb.tb_frame)
-del _C, _tb, _func
-
-
-def contextfunction(f):
- """This decorator can be used to mark a function or method context callable.
- A context callable is passed the active :class:`Context` as first argument when
- called from the template. This is useful if a function wants to get access
- to the context or functions provided on the context object. For example
- a function that returns a sorted list of template variables the current
- template exports could look like this::
-
- @contextfunction
- def get_exported_names(context):
- return sorted(context.exported_vars)
- """
- f.contextfunction = True
- return f
-
-
-def evalcontextfunction(f):
- """This decoraotr can be used to mark a function or method as an eval
- context callable. This is similar to the :func:`contextfunction`
- but instead of passing the context, an evaluation context object is
- passed. For more information about the eval context, see
- :ref:`eval-context`.
-
- .. versionadded:: 2.4
- """
- f.evalcontextfunction = True
- return f
-
-
-def environmentfunction(f):
- """This decorator can be used to mark a function or method as environment
- callable. This decorator works exactly like the :func:`contextfunction`
- decorator just that the first argument is the active :class:`Environment`
- and not context.
- """
- f.environmentfunction = True
- return f
-
-
-def internalcode(f):
- """Marks the function as internally used"""
- internal_code.add(f.func_code)
- return f
-
-
-def is_undefined(obj):
- """Check if the object passed is undefined. This does nothing more than
- performing an instance check against :class:`Undefined` but looks nicer.
- This can be used for custom filters or tests that want to react to
- undefined variables. For example a custom default filter can look like
- this::
-
- def default(var, default=''):
- if is_undefined(var):
- return default
- return var
- """
- from jinja2.runtime import Undefined
- return isinstance(obj, Undefined)
-
-
-def consume(iterable):
- """Consumes an iterable without doing anything with it."""
- for event in iterable:
- pass
-
-
-def clear_caches():
- """Jinja2 keeps internal caches for environments and lexers. These are
- used so that Jinja2 doesn't have to recreate environments and lexers all
- the time. Normally you don't have to care about that but if you are
- messuring memory consumption you may want to clean the caches.
- """
- from jinja2.environment import _spontaneous_environments
- from jinja2.lexer import _lexer_cache
- _spontaneous_environments.clear()
- _lexer_cache.clear()
-
-
-def import_string(import_name, silent=False):
- """Imports an object based on a string. This use useful if you want to
- use import paths as endpoints or something similar. An import path can
- be specified either in dotted notation (``xml.sax.saxutils.escape``)
- or with a colon as object delimiter (``xml.sax.saxutils:escape``).
-
- If the `silent` is True the return value will be `None` if the import
- fails.
-
- :return: imported object
- """
- try:
- if ':' in import_name:
- module, obj = import_name.split(':', 1)
- elif '.' in import_name:
- items = import_name.split('.')
- module = '.'.join(items[:-1])
- obj = items[-1]
- else:
- return __import__(import_name)
- return getattr(__import__(module, None, None, [obj]), obj)
- except (ImportError, AttributeError):
- if not silent:
- raise
-
-
-def open_if_exists(filename, mode='rb'):
- """Returns a file descriptor for the filename if that file exists,
- otherwise `None`.
- """
- try:
- return open(filename, mode)
- except IOError, e:
- if e.errno not in (errno.ENOENT, errno.EISDIR):
- raise
-
-
-def object_type_repr(obj):
- """Returns the name of the object's type. For some recognized
- singletons the name of the object is returned instead. (For
- example for `None` and `Ellipsis`).
- """
- if obj is None:
- return 'None'
- elif obj is Ellipsis:
- return 'Ellipsis'
- # __builtin__ in 2.x, builtins in 3.x
- if obj.__class__.__module__ in ('__builtin__', 'builtins'):
- name = obj.__class__.__name__
- else:
- name = obj.__class__.__module__ + '.' + obj.__class__.__name__
- return '%s object' % name
-
-
-def pformat(obj, verbose=False):
- """Prettyprint an object. Either use the `pretty` library or the
- builtin `pprint`.
- """
- try:
- from pretty import pretty
- return pretty(obj, verbose=verbose)
- except ImportError:
- from pprint import pformat
- return pformat(obj)
-
-
-def urlize(text, trim_url_limit=None, nofollow=False):
- """Converts any URLs in text into clickable links. Works on http://,
- https:// and www. links. Links can have trailing punctuation (periods,
- commas, close-parens) and leading punctuation (opening parens) and
- it'll still do the right thing.
-
- If trim_url_limit is not None, the URLs in link text will be limited
- to trim_url_limit characters.
-
- If nofollow is True, the URLs in link text will get a rel="nofollow"
- attribute.
- """
- trim_url = lambda x, limit=trim_url_limit: limit is not None \
- and (x[:limit] + (len(x) >=limit and '...'
- or '')) or x
- words = _word_split_re.split(unicode(escape(text)))
- nofollow_attr = nofollow and ' rel="nofollow"' or ''
- for i, word in enumerate(words):
- match = _punctuation_re.match(word)
- if match:
- lead, middle, trail = match.groups()
- if middle.startswith('www.') or (
- '@' not in middle and
- not middle.startswith('http://') and
- len(middle) > 0 and
- middle[0] in _letters + _digits and (
- middle.endswith('.org') or
- middle.endswith('.net') or
- middle.endswith('.com')
- )):
- middle = '<a href="http://%s"%s>%s</a>' % (middle,
- nofollow_attr, trim_url(middle))
- if middle.startswith('http://') or \
- middle.startswith('https://'):
- middle = '<a href="%s"%s>%s</a>' % (middle,
- nofollow_attr, trim_url(middle))
- if '@' in middle and not middle.startswith('www.') and \
- not ':' in middle and _simple_email_re.match(middle):
- middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
- if lead + middle + trail != word:
- words[i] = lead + middle + trail
- return u''.join(words)
-
-
-def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
- """Generate some lorem impsum for the template."""
- from jinja2.constants import LOREM_IPSUM_WORDS
- from random import choice, randrange
- words = LOREM_IPSUM_WORDS.split()
- result = []
-
- for _ in xrange(n):
- next_capitalized = True
- last_comma = last_fullstop = 0
- word = None
- last = None
- p = []
-
- # each paragraph contains out of 20 to 100 words.
- for idx, _ in enumerate(xrange(randrange(min, max))):
- while True:
- word = choice(words)
- if word != last:
- last = word
- break
- if next_capitalized:
- word = word.capitalize()
- next_capitalized = False
- # add commas
- if idx - randrange(3, 8) > last_comma:
- last_comma = idx
- last_fullstop += 2
- word += ','
- # add end of sentences
- if idx - randrange(10, 20) > last_fullstop:
- last_comma = last_fullstop = idx
- word += '.'
- next_capitalized = True
- p.append(word)
-
- # ensure that the paragraph ends with a dot.
- p = u' '.join(p)
- if p.endswith(','):
- p = p[:-1] + '.'
- elif not p.endswith('.'):
- p += '.'
- result.append(p)
-
- if not html:
- return u'\n\n'.join(result)
- return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
-
-
-class LRUCache(object):
- """A simple LRU Cache implementation."""
-
- # this is fast for small capacities (something below 1000) but doesn't
- # scale. But as long as it's only used as storage for templates this
- # won't do any harm.
-
- def __init__(self, capacity):
- self.capacity = capacity
- self._mapping = {}
- self._queue = deque()
- self._postinit()
-
- def _postinit(self):
- # alias all queue methods for faster lookup
- self._popleft = self._queue.popleft
- self._pop = self._queue.pop
- if hasattr(self._queue, 'remove'):
- self._remove = self._queue.remove
- self._wlock = allocate_lock()
- self._append = self._queue.append
-
- def _remove(self, obj):
- """Python 2.4 compatibility."""
- for idx, item in enumerate(self._queue):
- if item == obj:
- del self._queue[idx]
- break
-
- def __getstate__(self):
- return {
- 'capacity': self.capacity,
- '_mapping': self._mapping,
- '_queue': self._queue
- }
-
- def __setstate__(self, d):
- self.__dict__.update(d)
- self._postinit()
-
- def __getnewargs__(self):
- return (self.capacity,)
-
- def copy(self):
- """Return an shallow copy of the instance."""
- rv = self.__class__(self.capacity)
- rv._mapping.update(self._mapping)
- rv._queue = deque(self._queue)
- return rv
-
- def get(self, key, default=None):
- """Return an item from the cache dict or `default`"""
- try:
- return self[key]
- except KeyError:
- return default
-
- def setdefault(self, key, default=None):
- """Set `default` if the key is not in the cache otherwise
- leave unchanged. Return the value of this key.
- """
- try:
- return self[key]
- except KeyError:
- self[key] = default
- return default
-
- def clear(self):
- """Clear the cache."""
- self._wlock.acquire()
- try:
- self._mapping.clear()
- self._queue.clear()
- finally:
- self._wlock.release()
-
- def __contains__(self, key):
- """Check if a key exists in this cache."""
- return key in self._mapping
-
- def __len__(self):
- """Return the current size of the cache."""
- return len(self._mapping)
-
- def __repr__(self):
- return '<%s %r>' % (
- self.__class__.__name__,
- self._mapping
- )
-
- def __getitem__(self, key):
- """Get an item from the cache. Moves the item up so that it has the
- highest priority then.
-
- Raise an `KeyError` if it does not exist.
- """
- rv = self._mapping[key]
- if self._queue[-1] != key:
- try:
- self._remove(key)
- except ValueError:
- # if something removed the key from the container
- # when we read, ignore the ValueError that we would
- # get otherwise.
- pass
- self._append(key)
- return rv
-
- def __setitem__(self, key, value):
- """Sets the value for an item. Moves the item up so that it
- has the highest priority then.
- """
- self._wlock.acquire()
- try:
- if key in self._mapping:
- try:
- self._remove(key)
- except ValueError:
- # __getitem__ is not locked, it might happen
- pass
- elif len(self._mapping) == self.capacity:
- del self._mapping[self._popleft()]
- self._append(key)
- self._mapping[key] = value
- finally:
- self._wlock.release()
-
- def __delitem__(self, key):
- """Remove an item from the cache dict.
- Raise an `KeyError` if it does not exist.
- """
- self._wlock.acquire()
- try:
- del self._mapping[key]
- try:
- self._remove(key)
- except ValueError:
- # __getitem__ is not locked, it might happen
- pass
- finally:
- self._wlock.release()
-
- def items(self):
- """Return a list of items."""
- result = [(key, self._mapping[key]) for key in list(self._queue)]
- result.reverse()
- return result
-
- def iteritems(self):
- """Iterate over all items."""
- return iter(self.items())
-
- def values(self):
- """Return a list of all values."""
- return [x[1] for x in self.items()]
-
- def itervalue(self):
- """Iterate over all values."""
- return iter(self.values())
-
- def keys(self):
- """Return a list of all keys ordered by most recent usage."""
- return list(self)
-
- def iterkeys(self):
- """Iterate over all keys in the cache dict, ordered by
- the most recent usage.
- """
- return reversed(tuple(self._queue))
-
- __iter__ = iterkeys
-
- def __reversed__(self):
- """Iterate over the values in the cache dict, oldest items
- coming first.
- """
- return iter(tuple(self._queue))
-
- __copy__ = copy
-
-
-# register the LRU cache as mutable mapping if possible
-try:
- from collections import MutableMapping
- MutableMapping.register(LRUCache)
-except ImportError:
- pass
-
-
-class Cycler(object):
- """A cycle helper for templates."""
-
- def __init__(self, *items):
- if not items:
- raise RuntimeError('at least one item has to be provided')
- self.items = items
- self.reset()
-
- def reset(self):
- """Resets the cycle."""
- self.pos = 0
-
- @property
- def current(self):
- """Returns the current item."""
- return self.items[self.pos]
-
- def next(self):
- """Goes one item ahead and returns it."""
- rv = self.current
- self.pos = (self.pos + 1) % len(self.items)
- return rv
-
-
-class Joiner(object):
- """A joining helper for templates."""
-
- def __init__(self, sep=u', '):
- self.sep = sep
- self.used = False
-
- def __call__(self):
- if not self.used:
- self.used = True
- return u''
- return self.sep
-
-
-# try markupsafe first, if that fails go with Jinja2's bundled version
-# of markupsafe. Markupsafe was previously Jinja2's implementation of
-# the Markup object but was moved into a separate package in a patchleve
-# release
-try:
- from markupsafe import Markup, escape, soft_unicode
-except ImportError:
- from jinja2._markupsafe import Markup, escape, soft_unicode
-
-
-# partials
-try:
- from functools import partial
-except ImportError:
- class partial(object):
- def __init__(self, _func, *args, **kwargs):
- self._func = _func
- self._args = args
- self._kwargs = kwargs
- def __call__(self, *args, **kwargs):
- kwargs.update(self._kwargs)
- return self._func(*(self._args + args), **kwargs)
diff --git a/module/lib/jinja2/visitor.py b/module/lib/jinja2/visitor.py
deleted file mode 100644
index 413e7c309..000000000
--- a/module/lib/jinja2/visitor.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.visitor
- ~~~~~~~~~~~~~~
-
- This module implements a visitor for the nodes.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-from jinja2.nodes import Node
-
-
-class NodeVisitor(object):
- """Walks the abstract syntax tree and call visitor functions for every
- node found. The visitor functions may return values which will be
- forwarded by the `visit` method.
-
- Per default the visitor functions for the nodes are ``'visit_'`` +
- class name of the node. So a `TryFinally` node visit function would
- be `visit_TryFinally`. This behavior can be changed by overriding
- the `get_visitor` function. If no visitor function exists for a node
- (return value `None`) the `generic_visit` visitor is used instead.
- """
-
- def get_visitor(self, node):
- """Return the visitor function for this node or `None` if no visitor
- exists for this node. In that case the generic visit function is
- used instead.
- """
- method = 'visit_' + node.__class__.__name__
- return getattr(self, method, None)
-
- def visit(self, node, *args, **kwargs):
- """Visit a node."""
- f = self.get_visitor(node)
- if f is not None:
- return f(node, *args, **kwargs)
- return self.generic_visit(node, *args, **kwargs)
-
- def generic_visit(self, node, *args, **kwargs):
- """Called if no explicit visitor function exists for a node."""
- for node in node.iter_child_nodes():
- self.visit(node, *args, **kwargs)
-
-
-class NodeTransformer(NodeVisitor):
- """Walks the abstract syntax tree and allows modifications of nodes.
-
- The `NodeTransformer` will walk the AST and use the return value of the
- visitor functions to replace or remove the old node. If the return
- value of the visitor function is `None` the node will be removed
- from the previous location otherwise it's replaced with the return
- value. The return value may be the original node in which case no
- replacement takes place.
- """
-
- def generic_visit(self, node, *args, **kwargs):
- for field, old_value in node.iter_fields():
- if isinstance(old_value, list):
- new_values = []
- for value in old_value:
- if isinstance(value, Node):
- value = self.visit(value, *args, **kwargs)
- if value is None:
- continue
- elif not isinstance(value, Node):
- new_values.extend(value)
- continue
- new_values.append(value)
- old_value[:] = new_values
- elif isinstance(old_value, Node):
- new_node = self.visit(old_value, *args, **kwargs)
- if new_node is None:
- delattr(node, field)
- else:
- setattr(node, field, new_node)
- return node
-
- def visit_list(self, node, *args, **kwargs):
- """As transformers may return lists in some places this method
- can be used to enforce a list as return value.
- """
- rv = self.visit(node, *args, **kwargs)
- if not isinstance(rv, list):
- rv = [rv]
- return rv
diff --git a/module/lib/rename_process.py b/module/lib/rename_process.py
deleted file mode 100644
index 2527cef39..000000000
--- a/module/lib/rename_process.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import sys
-
-def renameProcess(new_name):
- """ Renames the process calling the function to the given name. """
- if sys.platform != 'linux2':
- return False
- try:
- from ctypes import CDLL
- libc = CDLL('libc.so.6')
- libc.prctl(15, new_name, 0, 0, 0)
- return True
- except Exception, e:
- #print "Rename process failed", e
- return False
diff --git a/module/lib/simplejson/__init__.py b/module/lib/simplejson/__init__.py
deleted file mode 100644
index ef5c0db48..000000000
--- a/module/lib/simplejson/__init__.py
+++ /dev/null
@@ -1,466 +0,0 @@
-r"""JSON (JavaScript Object Notation) <http://json.org> is a subset of
-JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data
-interchange format.
-
-:mod:`simplejson` exposes an API familiar to users of the standard library
-:mod:`marshal` and :mod:`pickle` modules. It is the externally maintained
-version of the :mod:`json` library contained in Python 2.6, but maintains
-compatibility with Python 2.4 and Python 2.5 and (currently) has
-significant performance advantages, even without using the optional C
-extension for speedups.
-
-Encoding basic Python object hierarchies::
-
- >>> import simplejson as json
- >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
- '["foo", {"bar": ["baz", null, 1.0, 2]}]'
- >>> print json.dumps("\"foo\bar")
- "\"foo\bar"
- >>> print json.dumps(u'\u1234')
- "\u1234"
- >>> print json.dumps('\\')
- "\\"
- >>> print json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
- {"a": 0, "b": 0, "c": 0}
- >>> from StringIO import StringIO
- >>> io = StringIO()
- >>> json.dump(['streaming API'], io)
- >>> io.getvalue()
- '["streaming API"]'
-
-Compact encoding::
-
- >>> import simplejson as json
- >>> json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
- '[1,2,3,{"4":5,"6":7}]'
-
-Pretty printing::
-
- >>> import simplejson as json
- >>> s = json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=' ')
- >>> print '\n'.join([l.rstrip() for l in s.splitlines()])
- {
- "4": 5,
- "6": 7
- }
-
-Decoding JSON::
-
- >>> import simplejson as json
- >>> obj = [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
- >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj
- True
- >>> json.loads('"\\"foo\\bar"') == u'"foo\x08ar'
- True
- >>> from StringIO import StringIO
- >>> io = StringIO('["streaming API"]')
- >>> json.load(io)[0] == 'streaming API'
- True
-
-Specializing JSON object decoding::
-
- >>> import simplejson as json
- >>> def as_complex(dct):
- ... if '__complex__' in dct:
- ... return complex(dct['real'], dct['imag'])
- ... return dct
- ...
- >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}',
- ... object_hook=as_complex)
- (1+2j)
- >>> from decimal import Decimal
- >>> json.loads('1.1', parse_float=Decimal) == Decimal('1.1')
- True
-
-Specializing JSON object encoding::
-
- >>> import simplejson as json
- >>> def encode_complex(obj):
- ... if isinstance(obj, complex):
- ... return [obj.real, obj.imag]
- ... raise TypeError(repr(o) + " is not JSON serializable")
- ...
- >>> json.dumps(2 + 1j, default=encode_complex)
- '[2.0, 1.0]'
- >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j)
- '[2.0, 1.0]'
- >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j))
- '[2.0, 1.0]'
-
-
-Using simplejson.tool from the shell to validate and pretty-print::
-
- $ echo '{"json":"obj"}' | python -m simplejson.tool
- {
- "json": "obj"
- }
- $ echo '{ 1.2:3.4}' | python -m simplejson.tool
- Expecting property name: line 1 column 2 (char 2)
-"""
-__version__ = '2.2.1'
-__all__ = [
- 'dump', 'dumps', 'load', 'loads',
- 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
- 'OrderedDict',
-]
-
-__author__ = 'Bob Ippolito <bob@redivi.com>'
-
-from decimal import Decimal
-
-from decoder import JSONDecoder, JSONDecodeError
-from encoder import JSONEncoder
-def _import_OrderedDict():
- import collections
- try:
- return collections.OrderedDict
- except AttributeError:
- import ordered_dict
- return ordered_dict.OrderedDict
-OrderedDict = _import_OrderedDict()
-
-def _import_c_make_encoder():
- try:
- from simplejson._speedups import make_encoder
- return make_encoder
- except ImportError:
- return None
-
-_default_encoder = JSONEncoder(
- skipkeys=False,
- ensure_ascii=True,
- check_circular=True,
- allow_nan=True,
- indent=None,
- separators=None,
- encoding='utf-8',
- default=None,
- use_decimal=True,
- namedtuple_as_object=True,
- tuple_as_array=True,
-)
-
-def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
- allow_nan=True, cls=None, indent=None, separators=None,
- encoding='utf-8', default=None, use_decimal=True,
- namedtuple_as_object=True, tuple_as_array=True,
- **kw):
- """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
- ``.write()``-supporting file-like object).
-
- If ``skipkeys`` is true then ``dict`` keys that are not basic types
- (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
- will be skipped instead of raising a ``TypeError``.
-
- If ``ensure_ascii`` is false, then the some chunks written to ``fp``
- may be ``unicode`` instances, subject to normal Python ``str`` to
- ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
- understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
- to cause an error.
-
- If ``check_circular`` is false, then the circular reference check
- for container types will be skipped and a circular reference will
- result in an ``OverflowError`` (or worse).
-
- If ``allow_nan`` is false, then it will be a ``ValueError`` to
- serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
- in strict compliance of the JSON specification, instead of using the
- JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
-
- If *indent* is a string, then JSON array elements and object members
- will be pretty-printed with a newline followed by that string repeated
- for each level of nesting. ``None`` (the default) selects the most compact
- representation without any newlines. For backwards compatibility with
- versions of simplejson earlier than 2.1.0, an integer is also accepted
- and is converted to a string with that many spaces.
-
- If ``separators`` is an ``(item_separator, dict_separator)`` tuple
- then it will be used instead of the default ``(', ', ': ')`` separators.
- ``(',', ':')`` is the most compact JSON representation.
-
- ``encoding`` is the character encoding for str instances, default is UTF-8.
-
- ``default(obj)`` is a function that should return a serializable version
- of obj or raise TypeError. The default simply raises TypeError.
-
- If *use_decimal* is true (default: ``True``) then decimal.Decimal
- will be natively serialized to JSON with full precision.
-
- If *namedtuple_as_object* is true (default: ``True``),
- :class:`tuple` subclasses with ``_asdict()`` methods will be encoded
- as JSON objects.
-
- If *tuple_as_array* is true (default: ``True``),
- :class:`tuple` (and subclasses) will be encoded as JSON arrays.
-
- To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
- ``.default()`` method to serialize additional types), specify it with
- the ``cls`` kwarg.
-
- """
- # cached encoder
- if (not skipkeys and ensure_ascii and
- check_circular and allow_nan and
- cls is None and indent is None and separators is None and
- encoding == 'utf-8' and default is None and use_decimal
- and namedtuple_as_object and tuple_as_array and not kw):
- iterable = _default_encoder.iterencode(obj)
- else:
- if cls is None:
- cls = JSONEncoder
- iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
- check_circular=check_circular, allow_nan=allow_nan, indent=indent,
- separators=separators, encoding=encoding,
- default=default, use_decimal=use_decimal,
- namedtuple_as_object=namedtuple_as_object,
- tuple_as_array=tuple_as_array,
- **kw).iterencode(obj)
- # could accelerate with writelines in some versions of Python, at
- # a debuggability cost
- for chunk in iterable:
- fp.write(chunk)
-
-
-def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
- allow_nan=True, cls=None, indent=None, separators=None,
- encoding='utf-8', default=None, use_decimal=True,
- namedtuple_as_object=True,
- tuple_as_array=True,
- **kw):
- """Serialize ``obj`` to a JSON formatted ``str``.
-
- If ``skipkeys`` is false then ``dict`` keys that are not basic types
- (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
- will be skipped instead of raising a ``TypeError``.
-
- If ``ensure_ascii`` is false, then the return value will be a
- ``unicode`` instance subject to normal Python ``str`` to ``unicode``
- coercion rules instead of being escaped to an ASCII ``str``.
-
- If ``check_circular`` is false, then the circular reference check
- for container types will be skipped and a circular reference will
- result in an ``OverflowError`` (or worse).
-
- If ``allow_nan`` is false, then it will be a ``ValueError`` to
- serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
- strict compliance of the JSON specification, instead of using the
- JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
-
- If ``indent`` is a string, then JSON array elements and object members
- will be pretty-printed with a newline followed by that string repeated
- for each level of nesting. ``None`` (the default) selects the most compact
- representation without any newlines. For backwards compatibility with
- versions of simplejson earlier than 2.1.0, an integer is also accepted
- and is converted to a string with that many spaces.
-
- If ``separators`` is an ``(item_separator, dict_separator)`` tuple
- then it will be used instead of the default ``(', ', ': ')`` separators.
- ``(',', ':')`` is the most compact JSON representation.
-
- ``encoding`` is the character encoding for str instances, default is UTF-8.
-
- ``default(obj)`` is a function that should return a serializable version
- of obj or raise TypeError. The default simply raises TypeError.
-
- If *use_decimal* is true (default: ``True``) then decimal.Decimal
- will be natively serialized to JSON with full precision.
-
- If *namedtuple_as_object* is true (default: ``True``),
- :class:`tuple` subclasses with ``_asdict()`` methods will be encoded
- as JSON objects.
-
- If *tuple_as_array* is true (default: ``True``),
- :class:`tuple` (and subclasses) will be encoded as JSON arrays.
-
- To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
- ``.default()`` method to serialize additional types), specify it with
- the ``cls`` kwarg.
-
- """
- # cached encoder
- if (not skipkeys and ensure_ascii and
- check_circular and allow_nan and
- cls is None and indent is None and separators is None and
- encoding == 'utf-8' and default is None and use_decimal
- and namedtuple_as_object and tuple_as_array and not kw):
- return _default_encoder.encode(obj)
- if cls is None:
- cls = JSONEncoder
- return cls(
- skipkeys=skipkeys, ensure_ascii=ensure_ascii,
- check_circular=check_circular, allow_nan=allow_nan, indent=indent,
- separators=separators, encoding=encoding, default=default,
- use_decimal=use_decimal,
- namedtuple_as_object=namedtuple_as_object,
- tuple_as_array=tuple_as_array,
- **kw).encode(obj)
-
-
-_default_decoder = JSONDecoder(encoding=None, object_hook=None,
- object_pairs_hook=None)
-
-
-def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
- parse_int=None, parse_constant=None, object_pairs_hook=None,
- use_decimal=False, namedtuple_as_object=True, tuple_as_array=True,
- **kw):
- """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
- a JSON document) to a Python object.
-
- *encoding* determines the encoding used to interpret any
- :class:`str` objects decoded by this instance (``'utf-8'`` by
- default). It has no effect when decoding :class:`unicode` objects.
-
- Note that currently only encodings that are a superset of ASCII work,
- strings of other encodings should be passed in as :class:`unicode`.
-
- *object_hook*, if specified, will be called with the result of every
- JSON object decoded and its return value will be used in place of the
- given :class:`dict`. This can be used to provide custom
- deserializations (e.g. to support JSON-RPC class hinting).
-
- *object_pairs_hook* is an optional function that will be called with
- the result of any object literal decode with an ordered list of pairs.
- The return value of *object_pairs_hook* will be used instead of the
- :class:`dict`. This feature can be used to implement custom decoders
- that rely on the order that the key and value pairs are decoded (for
- example, :func:`collections.OrderedDict` will remember the order of
- insertion). If *object_hook* is also defined, the *object_pairs_hook*
- takes priority.
-
- *parse_float*, if specified, will be called with the string of every
- JSON float to be decoded. By default, this is equivalent to
- ``float(num_str)``. This can be used to use another datatype or parser
- for JSON floats (e.g. :class:`decimal.Decimal`).
-
- *parse_int*, if specified, will be called with the string of every
- JSON int to be decoded. By default, this is equivalent to
- ``int(num_str)``. This can be used to use another datatype or parser
- for JSON integers (e.g. :class:`float`).
-
- *parse_constant*, if specified, will be called with one of the
- following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This
- can be used to raise an exception if invalid JSON numbers are
- encountered.
-
- If *use_decimal* is true (default: ``False``) then it implies
- parse_float=decimal.Decimal for parity with ``dump``.
-
- To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
- kwarg.
-
- """
- return loads(fp.read(),
- encoding=encoding, cls=cls, object_hook=object_hook,
- parse_float=parse_float, parse_int=parse_int,
- parse_constant=parse_constant, object_pairs_hook=object_pairs_hook,
- use_decimal=use_decimal, **kw)
-
-
-def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
- parse_int=None, parse_constant=None, object_pairs_hook=None,
- use_decimal=False, **kw):
- """Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
- document) to a Python object.
-
- *encoding* determines the encoding used to interpret any
- :class:`str` objects decoded by this instance (``'utf-8'`` by
- default). It has no effect when decoding :class:`unicode` objects.
-
- Note that currently only encodings that are a superset of ASCII work,
- strings of other encodings should be passed in as :class:`unicode`.
-
- *object_hook*, if specified, will be called with the result of every
- JSON object decoded and its return value will be used in place of the
- given :class:`dict`. This can be used to provide custom
- deserializations (e.g. to support JSON-RPC class hinting).
-
- *object_pairs_hook* is an optional function that will be called with
- the result of any object literal decode with an ordered list of pairs.
- The return value of *object_pairs_hook* will be used instead of the
- :class:`dict`. This feature can be used to implement custom decoders
- that rely on the order that the key and value pairs are decoded (for
- example, :func:`collections.OrderedDict` will remember the order of
- insertion). If *object_hook* is also defined, the *object_pairs_hook*
- takes priority.
-
- *parse_float*, if specified, will be called with the string of every
- JSON float to be decoded. By default, this is equivalent to
- ``float(num_str)``. This can be used to use another datatype or parser
- for JSON floats (e.g. :class:`decimal.Decimal`).
-
- *parse_int*, if specified, will be called with the string of every
- JSON int to be decoded. By default, this is equivalent to
- ``int(num_str)``. This can be used to use another datatype or parser
- for JSON integers (e.g. :class:`float`).
-
- *parse_constant*, if specified, will be called with one of the
- following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This
- can be used to raise an exception if invalid JSON numbers are
- encountered.
-
- If *use_decimal* is true (default: ``False``) then it implies
- parse_float=decimal.Decimal for parity with ``dump``.
-
- To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
- kwarg.
-
- """
- if (cls is None and encoding is None and object_hook is None and
- parse_int is None and parse_float is None and
- parse_constant is None and object_pairs_hook is None
- and not use_decimal and not kw):
- return _default_decoder.decode(s)
- if cls is None:
- cls = JSONDecoder
- if object_hook is not None:
- kw['object_hook'] = object_hook
- if object_pairs_hook is not None:
- kw['object_pairs_hook'] = object_pairs_hook
- if parse_float is not None:
- kw['parse_float'] = parse_float
- if parse_int is not None:
- kw['parse_int'] = parse_int
- if parse_constant is not None:
- kw['parse_constant'] = parse_constant
- if use_decimal:
- if parse_float is not None:
- raise TypeError("use_decimal=True implies parse_float=Decimal")
- kw['parse_float'] = Decimal
- return cls(encoding=encoding, **kw).decode(s)
-
-
-def _toggle_speedups(enabled):
- import simplejson.decoder as dec
- import simplejson.encoder as enc
- import simplejson.scanner as scan
- c_make_encoder = _import_c_make_encoder()
- if enabled:
- dec.scanstring = dec.c_scanstring or dec.py_scanstring
- enc.c_make_encoder = c_make_encoder
- enc.encode_basestring_ascii = (enc.c_encode_basestring_ascii or
- enc.py_encode_basestring_ascii)
- scan.make_scanner = scan.c_make_scanner or scan.py_make_scanner
- else:
- dec.scanstring = dec.py_scanstring
- enc.c_make_encoder = None
- enc.encode_basestring_ascii = enc.py_encode_basestring_ascii
- scan.make_scanner = scan.py_make_scanner
- dec.make_scanner = scan.make_scanner
- global _default_decoder
- _default_decoder = JSONDecoder(
- encoding=None,
- object_hook=None,
- object_pairs_hook=None,
- )
- global _default_encoder
- _default_encoder = JSONEncoder(
- skipkeys=False,
- ensure_ascii=True,
- check_circular=True,
- allow_nan=True,
- indent=None,
- separators=None,
- encoding='utf-8',
- default=None,
- )
diff --git a/module/lib/simplejson/decoder.py b/module/lib/simplejson/decoder.py
deleted file mode 100644
index e5496d6e7..000000000
--- a/module/lib/simplejson/decoder.py
+++ /dev/null
@@ -1,421 +0,0 @@
-"""Implementation of JSONDecoder
-"""
-import re
-import sys
-import struct
-
-from simplejson.scanner import make_scanner
-def _import_c_scanstring():
- try:
- from simplejson._speedups import scanstring
- return scanstring
- except ImportError:
- return None
-c_scanstring = _import_c_scanstring()
-
-__all__ = ['JSONDecoder']
-
-FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
-
-def _floatconstants():
- _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
- # The struct module in Python 2.4 would get frexp() out of range here
- # when an endian is specified in the format string. Fixed in Python 2.5+
- if sys.byteorder != 'big':
- _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
- nan, inf = struct.unpack('dd', _BYTES)
- return nan, inf, -inf
-
-NaN, PosInf, NegInf = _floatconstants()
-
-
-class JSONDecodeError(ValueError):
- """Subclass of ValueError with the following additional properties:
-
- msg: The unformatted error message
- doc: The JSON document being parsed
- pos: The start index of doc where parsing failed
- end: The end index of doc where parsing failed (may be None)
- lineno: The line corresponding to pos
- colno: The column corresponding to pos
- endlineno: The line corresponding to end (may be None)
- endcolno: The column corresponding to end (may be None)
-
- """
- def __init__(self, msg, doc, pos, end=None):
- ValueError.__init__(self, errmsg(msg, doc, pos, end=end))
- self.msg = msg
- self.doc = doc
- self.pos = pos
- self.end = end
- self.lineno, self.colno = linecol(doc, pos)
- if end is not None:
- self.endlineno, self.endcolno = linecol(doc, end)
- else:
- self.endlineno, self.endcolno = None, None
-
-
-def linecol(doc, pos):
- lineno = doc.count('\n', 0, pos) + 1
- if lineno == 1:
- colno = pos
- else:
- colno = pos - doc.rindex('\n', 0, pos)
- return lineno, colno
-
-
-def errmsg(msg, doc, pos, end=None):
- # Note that this function is called from _speedups
- lineno, colno = linecol(doc, pos)
- if end is None:
- #fmt = '{0}: line {1} column {2} (char {3})'
- #return fmt.format(msg, lineno, colno, pos)
- fmt = '%s: line %d column %d (char %d)'
- return fmt % (msg, lineno, colno, pos)
- endlineno, endcolno = linecol(doc, end)
- #fmt = '{0}: line {1} column {2} - line {3} column {4} (char {5} - {6})'
- #return fmt.format(msg, lineno, colno, endlineno, endcolno, pos, end)
- fmt = '%s: line %d column %d - line %d column %d (char %d - %d)'
- return fmt % (msg, lineno, colno, endlineno, endcolno, pos, end)
-
-
-_CONSTANTS = {
- '-Infinity': NegInf,
- 'Infinity': PosInf,
- 'NaN': NaN,
-}
-
-STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
-BACKSLASH = {
- '"': u'"', '\\': u'\\', '/': u'/',
- 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
-}
-
-DEFAULT_ENCODING = "utf-8"
-
-def py_scanstring(s, end, encoding=None, strict=True,
- _b=BACKSLASH, _m=STRINGCHUNK.match):
- """Scan the string s for a JSON string. End is the index of the
- character in s after the quote that started the JSON string.
- Unescapes all valid JSON string escape sequences and raises ValueError
- on attempt to decode an invalid string. If strict is False then literal
- control characters are allowed in the string.
-
- Returns a tuple of the decoded string and the index of the character in s
- after the end quote."""
- if encoding is None:
- encoding = DEFAULT_ENCODING
- chunks = []
- _append = chunks.append
- begin = end - 1
- while 1:
- chunk = _m(s, end)
- if chunk is None:
- raise JSONDecodeError(
- "Unterminated string starting at", s, begin)
- end = chunk.end()
- content, terminator = chunk.groups()
- # Content is contains zero or more unescaped string characters
- if content:
- if not isinstance(content, unicode):
- content = unicode(content, encoding)
- _append(content)
- # Terminator is the end of string, a literal control character,
- # or a backslash denoting that an escape sequence follows
- if terminator == '"':
- break
- elif terminator != '\\':
- if strict:
- msg = "Invalid control character %r at" % (terminator,)
- #msg = "Invalid control character {0!r} at".format(terminator)
- raise JSONDecodeError(msg, s, end)
- else:
- _append(terminator)
- continue
- try:
- esc = s[end]
- except IndexError:
- raise JSONDecodeError(
- "Unterminated string starting at", s, begin)
- # If not a unicode escape sequence, must be in the lookup table
- if esc != 'u':
- try:
- char = _b[esc]
- except KeyError:
- msg = "Invalid \\escape: " + repr(esc)
- raise JSONDecodeError(msg, s, end)
- end += 1
- else:
- # Unicode escape sequence
- esc = s[end + 1:end + 5]
- next_end = end + 5
- if len(esc) != 4:
- msg = "Invalid \\uXXXX escape"
- raise JSONDecodeError(msg, s, end)
- uni = int(esc, 16)
- # Check for surrogate pair on UCS-4 systems
- if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
- msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
- if not s[end + 5:end + 7] == '\\u':
- raise JSONDecodeError(msg, s, end)
- esc2 = s[end + 7:end + 11]
- if len(esc2) != 4:
- raise JSONDecodeError(msg, s, end)
- uni2 = int(esc2, 16)
- uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
- next_end += 6
- char = unichr(uni)
- end = next_end
- # Append the unescaped character
- _append(char)
- return u''.join(chunks), end
-
-
-# Use speedup if available
-scanstring = c_scanstring or py_scanstring
-
-WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
-WHITESPACE_STR = ' \t\n\r'
-
-def JSONObject((s, end), encoding, strict, scan_once, object_hook,
- object_pairs_hook, memo=None,
- _w=WHITESPACE.match, _ws=WHITESPACE_STR):
- # Backwards compatibility
- if memo is None:
- memo = {}
- memo_get = memo.setdefault
- pairs = []
- # Use a slice to prevent IndexError from being raised, the following
- # check will raise a more specific ValueError if the string is empty
- nextchar = s[end:end + 1]
- # Normally we expect nextchar == '"'
- if nextchar != '"':
- if nextchar in _ws:
- end = _w(s, end).end()
- nextchar = s[end:end + 1]
- # Trivial empty object
- if nextchar == '}':
- if object_pairs_hook is not None:
- result = object_pairs_hook(pairs)
- return result, end + 1
- pairs = {}
- if object_hook is not None:
- pairs = object_hook(pairs)
- return pairs, end + 1
- elif nextchar != '"':
- raise JSONDecodeError("Expecting property name", s, end)
- end += 1
- while True:
- key, end = scanstring(s, end, encoding, strict)
- key = memo_get(key, key)
-
- # To skip some function call overhead we optimize the fast paths where
- # the JSON key separator is ": " or just ":".
- if s[end:end + 1] != ':':
- end = _w(s, end).end()
- if s[end:end + 1] != ':':
- raise JSONDecodeError("Expecting : delimiter", s, end)
-
- end += 1
-
- try:
- if s[end] in _ws:
- end += 1
- if s[end] in _ws:
- end = _w(s, end + 1).end()
- except IndexError:
- pass
-
- try:
- value, end = scan_once(s, end)
- except StopIteration:
- raise JSONDecodeError("Expecting object", s, end)
- pairs.append((key, value))
-
- try:
- nextchar = s[end]
- if nextchar in _ws:
- end = _w(s, end + 1).end()
- nextchar = s[end]
- except IndexError:
- nextchar = ''
- end += 1
-
- if nextchar == '}':
- break
- elif nextchar != ',':
- raise JSONDecodeError("Expecting , delimiter", s, end - 1)
-
- try:
- nextchar = s[end]
- if nextchar in _ws:
- end += 1
- nextchar = s[end]
- if nextchar in _ws:
- end = _w(s, end + 1).end()
- nextchar = s[end]
- except IndexError:
- nextchar = ''
-
- end += 1
- if nextchar != '"':
- raise JSONDecodeError("Expecting property name", s, end - 1)
-
- if object_pairs_hook is not None:
- result = object_pairs_hook(pairs)
- return result, end
- pairs = dict(pairs)
- if object_hook is not None:
- pairs = object_hook(pairs)
- return pairs, end
-
-def JSONArray((s, end), scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
- values = []
- nextchar = s[end:end + 1]
- if nextchar in _ws:
- end = _w(s, end + 1).end()
- nextchar = s[end:end + 1]
- # Look-ahead for trivial empty array
- if nextchar == ']':
- return values, end + 1
- _append = values.append
- while True:
- try:
- value, end = scan_once(s, end)
- except StopIteration:
- raise JSONDecodeError("Expecting object", s, end)
- _append(value)
- nextchar = s[end:end + 1]
- if nextchar in _ws:
- end = _w(s, end + 1).end()
- nextchar = s[end:end + 1]
- end += 1
- if nextchar == ']':
- break
- elif nextchar != ',':
- raise JSONDecodeError("Expecting , delimiter", s, end)
-
- try:
- if s[end] in _ws:
- end += 1
- if s[end] in _ws:
- end = _w(s, end + 1).end()
- except IndexError:
- pass
-
- return values, end
-
-class JSONDecoder(object):
- """Simple JSON <http://json.org> decoder
-
- Performs the following translations in decoding by default:
-
- +---------------+-------------------+
- | JSON | Python |
- +===============+===================+
- | object | dict |
- +---------------+-------------------+
- | array | list |
- +---------------+-------------------+
- | string | unicode |
- +---------------+-------------------+
- | number (int) | int, long |
- +---------------+-------------------+
- | number (real) | float |
- +---------------+-------------------+
- | true | True |
- +---------------+-------------------+
- | false | False |
- +---------------+-------------------+
- | null | None |
- +---------------+-------------------+
-
- It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
- their corresponding ``float`` values, which is outside the JSON spec.
-
- """
-
- def __init__(self, encoding=None, object_hook=None, parse_float=None,
- parse_int=None, parse_constant=None, strict=True,
- object_pairs_hook=None):
- """
- *encoding* determines the encoding used to interpret any
- :class:`str` objects decoded by this instance (``'utf-8'`` by
- default). It has no effect when decoding :class:`unicode` objects.
-
- Note that currently only encodings that are a superset of ASCII work,
- strings of other encodings should be passed in as :class:`unicode`.
-
- *object_hook*, if specified, will be called with the result of every
- JSON object decoded and its return value will be used in place of the
- given :class:`dict`. This can be used to provide custom
- deserializations (e.g. to support JSON-RPC class hinting).
-
- *object_pairs_hook* is an optional function that will be called with
- the result of any object literal decode with an ordered list of pairs.
- The return value of *object_pairs_hook* will be used instead of the
- :class:`dict`. This feature can be used to implement custom decoders
- that rely on the order that the key and value pairs are decoded (for
- example, :func:`collections.OrderedDict` will remember the order of
- insertion). If *object_hook* is also defined, the *object_pairs_hook*
- takes priority.
-
- *parse_float*, if specified, will be called with the string of every
- JSON float to be decoded. By default, this is equivalent to
- ``float(num_str)``. This can be used to use another datatype or parser
- for JSON floats (e.g. :class:`decimal.Decimal`).
-
- *parse_int*, if specified, will be called with the string of every
- JSON int to be decoded. By default, this is equivalent to
- ``int(num_str)``. This can be used to use another datatype or parser
- for JSON integers (e.g. :class:`float`).
-
- *parse_constant*, if specified, will be called with one of the
- following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This
- can be used to raise an exception if invalid JSON numbers are
- encountered.
-
- *strict* controls the parser's behavior when it encounters an
- invalid control character in a string. The default setting of
- ``True`` means that unescaped control characters are parse errors, if
- ``False`` then control characters will be allowed in strings.
-
- """
- self.encoding = encoding
- self.object_hook = object_hook
- self.object_pairs_hook = object_pairs_hook
- self.parse_float = parse_float or float
- self.parse_int = parse_int or int
- self.parse_constant = parse_constant or _CONSTANTS.__getitem__
- self.strict = strict
- self.parse_object = JSONObject
- self.parse_array = JSONArray
- self.parse_string = scanstring
- self.memo = {}
- self.scan_once = make_scanner(self)
-
- def decode(self, s, _w=WHITESPACE.match):
- """Return the Python representation of ``s`` (a ``str`` or ``unicode``
- instance containing a JSON document)
-
- """
- obj, end = self.raw_decode(s, idx=_w(s, 0).end())
- end = _w(s, end).end()
- if end != len(s):
- raise JSONDecodeError("Extra data", s, end, len(s))
- return obj
-
- def raw_decode(self, s, idx=0):
- """Decode a JSON document from ``s`` (a ``str`` or ``unicode``
- beginning with a JSON document) and return a 2-tuple of the Python
- representation and the index in ``s`` where the document ended.
-
- This can be used to decode a JSON document from a string that may
- have extraneous data at the end.
-
- """
- try:
- obj, end = self.scan_once(s, idx)
- except StopIteration:
- raise JSONDecodeError("No JSON object could be decoded", s, idx)
- return obj, end
diff --git a/module/lib/simplejson/encoder.py b/module/lib/simplejson/encoder.py
deleted file mode 100644
index 5ec7440f1..000000000
--- a/module/lib/simplejson/encoder.py
+++ /dev/null
@@ -1,534 +0,0 @@
-"""Implementation of JSONEncoder
-"""
-import re
-from decimal import Decimal
-
-def _import_speedups():
- try:
- from simplejson import _speedups
- return _speedups.encode_basestring_ascii, _speedups.make_encoder
- except ImportError:
- return None, None
-c_encode_basestring_ascii, c_make_encoder = _import_speedups()
-
-from simplejson.decoder import PosInf
-
-ESCAPE = re.compile(ur'[\x00-\x1f\\"\b\f\n\r\t\u2028\u2029]')
-ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
-HAS_UTF8 = re.compile(r'[\x80-\xff]')
-ESCAPE_DCT = {
- '\\': '\\\\',
- '"': '\\"',
- '\b': '\\b',
- '\f': '\\f',
- '\n': '\\n',
- '\r': '\\r',
- '\t': '\\t',
- u'\u2028': '\\u2028',
- u'\u2029': '\\u2029',
-}
-for i in range(0x20):
- #ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
- ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
-
-FLOAT_REPR = repr
-
-def encode_basestring(s):
- """Return a JSON representation of a Python string
-
- """
- if isinstance(s, str) and HAS_UTF8.search(s) is not None:
- s = s.decode('utf-8')
- def replace(match):
- return ESCAPE_DCT[match.group(0)]
- return u'"' + ESCAPE.sub(replace, s) + u'"'
-
-
-def py_encode_basestring_ascii(s):
- """Return an ASCII-only JSON representation of a Python string
-
- """
- if isinstance(s, str) and HAS_UTF8.search(s) is not None:
- s = s.decode('utf-8')
- def replace(match):
- s = match.group(0)
- try:
- return ESCAPE_DCT[s]
- except KeyError:
- n = ord(s)
- if n < 0x10000:
- #return '\\u{0:04x}'.format(n)
- return '\\u%04x' % (n,)
- else:
- # surrogate pair
- n -= 0x10000
- s1 = 0xd800 | ((n >> 10) & 0x3ff)
- s2 = 0xdc00 | (n & 0x3ff)
- #return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
- return '\\u%04x\\u%04x' % (s1, s2)
- return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
-
-
-encode_basestring_ascii = (
- c_encode_basestring_ascii or py_encode_basestring_ascii)
-
-class JSONEncoder(object):
- """Extensible JSON <http://json.org> encoder for Python data structures.
-
- Supports the following objects and types by default:
-
- +-------------------+---------------+
- | Python | JSON |
- +===================+===============+
- | dict, namedtuple | object |
- +-------------------+---------------+
- | list, tuple | array |
- +-------------------+---------------+
- | str, unicode | string |
- +-------------------+---------------+
- | int, long, float | number |
- +-------------------+---------------+
- | True | true |
- +-------------------+---------------+
- | False | false |
- +-------------------+---------------+
- | None | null |
- +-------------------+---------------+
-
- To extend this to recognize other objects, subclass and implement a
- ``.default()`` method with another method that returns a serializable
- object for ``o`` if possible, otherwise it should call the superclass
- implementation (to raise ``TypeError``).
-
- """
- item_separator = ', '
- key_separator = ': '
- def __init__(self, skipkeys=False, ensure_ascii=True,
- check_circular=True, allow_nan=True, sort_keys=False,
- indent=None, separators=None, encoding='utf-8', default=None,
- use_decimal=True, namedtuple_as_object=True,
- tuple_as_array=True):
- """Constructor for JSONEncoder, with sensible defaults.
-
- If skipkeys is false, then it is a TypeError to attempt
- encoding of keys that are not str, int, long, float or None. If
- skipkeys is True, such items are simply skipped.
-
- If ensure_ascii is true, the output is guaranteed to be str
- objects with all incoming unicode characters escaped. If
- ensure_ascii is false, the output will be unicode object.
-
- If check_circular is true, then lists, dicts, and custom encoded
- objects will be checked for circular references during encoding to
- prevent an infinite recursion (which would cause an OverflowError).
- Otherwise, no such check takes place.
-
- If allow_nan is true, then NaN, Infinity, and -Infinity will be
- encoded as such. This behavior is not JSON specification compliant,
- but is consistent with most JavaScript based encoders and decoders.
- Otherwise, it will be a ValueError to encode such floats.
-
- If sort_keys is true, then the output of dictionaries will be
- sorted by key; this is useful for regression tests to ensure
- that JSON serializations can be compared on a day-to-day basis.
-
- If indent is a string, then JSON array elements and object members
- will be pretty-printed with a newline followed by that string repeated
- for each level of nesting. ``None`` (the default) selects the most compact
- representation without any newlines. For backwards compatibility with
- versions of simplejson earlier than 2.1.0, an integer is also accepted
- and is converted to a string with that many spaces.
-
- If specified, separators should be a (item_separator, key_separator)
- tuple. The default is (', ', ': '). To get the most compact JSON
- representation you should specify (',', ':') to eliminate whitespace.
-
- If specified, default is a function that gets called for objects
- that can't otherwise be serialized. It should return a JSON encodable
- version of the object or raise a ``TypeError``.
-
- If encoding is not None, then all input strings will be
- transformed into unicode using that encoding prior to JSON-encoding.
- The default is UTF-8.
-
- If use_decimal is true (not the default), ``decimal.Decimal`` will
- be supported directly by the encoder. For the inverse, decode JSON
- with ``parse_float=decimal.Decimal``.
-
- If namedtuple_as_object is true (the default), tuple subclasses with
- ``_asdict()`` methods will be encoded as JSON objects.
-
- If tuple_as_array is true (the default), tuple (and subclasses) will
- be encoded as JSON arrays.
- """
-
- self.skipkeys = skipkeys
- self.ensure_ascii = ensure_ascii
- self.check_circular = check_circular
- self.allow_nan = allow_nan
- self.sort_keys = sort_keys
- self.use_decimal = use_decimal
- self.namedtuple_as_object = namedtuple_as_object
- self.tuple_as_array = tuple_as_array
- if isinstance(indent, (int, long)):
- indent = ' ' * indent
- self.indent = indent
- if separators is not None:
- self.item_separator, self.key_separator = separators
- elif indent is not None:
- self.item_separator = ','
- if default is not None:
- self.default = default
- self.encoding = encoding
-
- def default(self, o):
- """Implement this method in a subclass such that it returns
- a serializable object for ``o``, or calls the base implementation
- (to raise a ``TypeError``).
-
- For example, to support arbitrary iterators, you could
- implement default like this::
-
- def default(self, o):
- try:
- iterable = iter(o)
- except TypeError:
- pass
- else:
- return list(iterable)
- return JSONEncoder.default(self, o)
-
- """
- raise TypeError(repr(o) + " is not JSON serializable")
-
- def encode(self, o):
- """Return a JSON string representation of a Python data structure.
-
- >>> from simplejson import JSONEncoder
- >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
- '{"foo": ["bar", "baz"]}'
-
- """
- # This is for extremely simple cases and benchmarks.
- if isinstance(o, basestring):
- if isinstance(o, str):
- _encoding = self.encoding
- if (_encoding is not None
- and not (_encoding == 'utf-8')):
- o = o.decode(_encoding)
- if self.ensure_ascii:
- return encode_basestring_ascii(o)
- else:
- return encode_basestring(o)
- # This doesn't pass the iterator directly to ''.join() because the
- # exceptions aren't as detailed. The list call should be roughly
- # equivalent to the PySequence_Fast that ''.join() would do.
- chunks = self.iterencode(o, _one_shot=True)
- if not isinstance(chunks, (list, tuple)):
- chunks = list(chunks)
- if self.ensure_ascii:
- return ''.join(chunks)
- else:
- return u''.join(chunks)
-
- def iterencode(self, o, _one_shot=False):
- """Encode the given object and yield each string
- representation as available.
-
- For example::
-
- for chunk in JSONEncoder().iterencode(bigobject):
- mysocket.write(chunk)
-
- """
- if self.check_circular:
- markers = {}
- else:
- markers = None
- if self.ensure_ascii:
- _encoder = encode_basestring_ascii
- else:
- _encoder = encode_basestring
- if self.encoding != 'utf-8':
- def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
- if isinstance(o, str):
- o = o.decode(_encoding)
- return _orig_encoder(o)
-
- def floatstr(o, allow_nan=self.allow_nan,
- _repr=FLOAT_REPR, _inf=PosInf, _neginf=-PosInf):
- # Check for specials. Note that this type of test is processor
- # and/or platform-specific, so do tests which don't depend on
- # the internals.
-
- if o != o:
- text = 'NaN'
- elif o == _inf:
- text = 'Infinity'
- elif o == _neginf:
- text = '-Infinity'
- else:
- return _repr(o)
-
- if not allow_nan:
- raise ValueError(
- "Out of range float values are not JSON compliant: " +
- repr(o))
-
- return text
-
-
- key_memo = {}
- if (_one_shot and c_make_encoder is not None
- and self.indent is None):
- _iterencode = c_make_encoder(
- markers, self.default, _encoder, self.indent,
- self.key_separator, self.item_separator, self.sort_keys,
- self.skipkeys, self.allow_nan, key_memo, self.use_decimal,
- self.namedtuple_as_object, self.tuple_as_array)
- else:
- _iterencode = _make_iterencode(
- markers, self.default, _encoder, self.indent, floatstr,
- self.key_separator, self.item_separator, self.sort_keys,
- self.skipkeys, _one_shot, self.use_decimal,
- self.namedtuple_as_object, self.tuple_as_array)
- try:
- return _iterencode(o, 0)
- finally:
- key_memo.clear()
-
-
-class JSONEncoderForHTML(JSONEncoder):
- """An encoder that produces JSON safe to embed in HTML.
-
- To embed JSON content in, say, a script tag on a web page, the
- characters &, < and > should be escaped. They cannot be escaped
- with the usual entities (e.g. &amp;) because they are not expanded
- within <script> tags.
- """
-
- def encode(self, o):
- # Override JSONEncoder.encode because it has hacks for
- # performance that make things more complicated.
- chunks = self.iterencode(o, True)
- if self.ensure_ascii:
- return ''.join(chunks)
- else:
- return u''.join(chunks)
-
- def iterencode(self, o, _one_shot=False):
- chunks = super(JSONEncoderForHTML, self).iterencode(o, _one_shot)
- for chunk in chunks:
- chunk = chunk.replace('&', '\\u0026')
- chunk = chunk.replace('<', '\\u003c')
- chunk = chunk.replace('>', '\\u003e')
- yield chunk
-
-
-def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
- _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
- _use_decimal, _namedtuple_as_object, _tuple_as_array,
- ## HACK: hand-optimized bytecode; turn globals into locals
- False=False,
- True=True,
- ValueError=ValueError,
- basestring=basestring,
- Decimal=Decimal,
- dict=dict,
- float=float,
- id=id,
- int=int,
- isinstance=isinstance,
- list=list,
- long=long,
- str=str,
- tuple=tuple,
- ):
-
- def _iterencode_list(lst, _current_indent_level):
- if not lst:
- yield '[]'
- return
- if markers is not None:
- markerid = id(lst)
- if markerid in markers:
- raise ValueError("Circular reference detected")
- markers[markerid] = lst
- buf = '['
- if _indent is not None:
- _current_indent_level += 1
- newline_indent = '\n' + (_indent * _current_indent_level)
- separator = _item_separator + newline_indent
- buf += newline_indent
- else:
- newline_indent = None
- separator = _item_separator
- first = True
- for value in lst:
- if first:
- first = False
- else:
- buf = separator
- if isinstance(value, basestring):
- yield buf + _encoder(value)
- elif value is None:
- yield buf + 'null'
- elif value is True:
- yield buf + 'true'
- elif value is False:
- yield buf + 'false'
- elif isinstance(value, (int, long)):
- yield buf + str(value)
- elif isinstance(value, float):
- yield buf + _floatstr(value)
- elif _use_decimal and isinstance(value, Decimal):
- yield buf + str(value)
- else:
- yield buf
- if isinstance(value, list):
- chunks = _iterencode_list(value, _current_indent_level)
- elif (_namedtuple_as_object and isinstance(value, tuple) and
- hasattr(value, '_asdict')):
- chunks = _iterencode_dict(value._asdict(),
- _current_indent_level)
- elif _tuple_as_array and isinstance(value, tuple):
- chunks = _iterencode_list(value, _current_indent_level)
- elif isinstance(value, dict):
- chunks = _iterencode_dict(value, _current_indent_level)
- else:
- chunks = _iterencode(value, _current_indent_level)
- for chunk in chunks:
- yield chunk
- if newline_indent is not None:
- _current_indent_level -= 1
- yield '\n' + (_indent * _current_indent_level)
- yield ']'
- if markers is not None:
- del markers[markerid]
-
- def _iterencode_dict(dct, _current_indent_level):
- if not dct:
- yield '{}'
- return
- if markers is not None:
- markerid = id(dct)
- if markerid in markers:
- raise ValueError("Circular reference detected")
- markers[markerid] = dct
- yield '{'
- if _indent is not None:
- _current_indent_level += 1
- newline_indent = '\n' + (_indent * _current_indent_level)
- item_separator = _item_separator + newline_indent
- yield newline_indent
- else:
- newline_indent = None
- item_separator = _item_separator
- first = True
- if _sort_keys:
- items = dct.items()
- items.sort(key=lambda kv: kv[0])
- else:
- items = dct.iteritems()
- for key, value in items:
- if isinstance(key, basestring):
- pass
- # JavaScript is weakly typed for these, so it makes sense to
- # also allow them. Many encoders seem to do something like this.
- elif isinstance(key, float):
- key = _floatstr(key)
- elif key is True:
- key = 'true'
- elif key is False:
- key = 'false'
- elif key is None:
- key = 'null'
- elif isinstance(key, (int, long)):
- key = str(key)
- elif _skipkeys:
- continue
- else:
- raise TypeError("key " + repr(key) + " is not a string")
- if first:
- first = False
- else:
- yield item_separator
- yield _encoder(key)
- yield _key_separator
- if isinstance(value, basestring):
- yield _encoder(value)
- elif value is None:
- yield 'null'
- elif value is True:
- yield 'true'
- elif value is False:
- yield 'false'
- elif isinstance(value, (int, long)):
- yield str(value)
- elif isinstance(value, float):
- yield _floatstr(value)
- elif _use_decimal and isinstance(value, Decimal):
- yield str(value)
- else:
- if isinstance(value, list):
- chunks = _iterencode_list(value, _current_indent_level)
- elif (_namedtuple_as_object and isinstance(value, tuple) and
- hasattr(value, '_asdict')):
- chunks = _iterencode_dict(value._asdict(),
- _current_indent_level)
- elif _tuple_as_array and isinstance(value, tuple):
- chunks = _iterencode_list(value, _current_indent_level)
- elif isinstance(value, dict):
- chunks = _iterencode_dict(value, _current_indent_level)
- else:
- chunks = _iterencode(value, _current_indent_level)
- for chunk in chunks:
- yield chunk
- if newline_indent is not None:
- _current_indent_level -= 1
- yield '\n' + (_indent * _current_indent_level)
- yield '}'
- if markers is not None:
- del markers[markerid]
-
- def _iterencode(o, _current_indent_level):
- if isinstance(o, basestring):
- yield _encoder(o)
- elif o is None:
- yield 'null'
- elif o is True:
- yield 'true'
- elif o is False:
- yield 'false'
- elif isinstance(o, (int, long)):
- yield str(o)
- elif isinstance(o, float):
- yield _floatstr(o)
- elif isinstance(o, list):
- for chunk in _iterencode_list(o, _current_indent_level):
- yield chunk
- elif (_namedtuple_as_object and isinstance(o, tuple) and
- hasattr(o, '_asdict')):
- for chunk in _iterencode_dict(o._asdict(), _current_indent_level):
- yield chunk
- elif (_tuple_as_array and isinstance(o, tuple)):
- for chunk in _iterencode_list(o, _current_indent_level):
- yield chunk
- elif isinstance(o, dict):
- for chunk in _iterencode_dict(o, _current_indent_level):
- yield chunk
- elif _use_decimal and isinstance(o, Decimal):
- yield str(o)
- else:
- if markers is not None:
- markerid = id(o)
- if markerid in markers:
- raise ValueError("Circular reference detected")
- markers[markerid] = o
- o = _default(o)
- for chunk in _iterencode(o, _current_indent_level):
- yield chunk
- if markers is not None:
- del markers[markerid]
-
- return _iterencode
diff --git a/module/lib/simplejson/ordered_dict.py b/module/lib/simplejson/ordered_dict.py
deleted file mode 100644
index 87ad88824..000000000
--- a/module/lib/simplejson/ordered_dict.py
+++ /dev/null
@@ -1,119 +0,0 @@
-"""Drop-in replacement for collections.OrderedDict by Raymond Hettinger
-
-http://code.activestate.com/recipes/576693/
-
-"""
-from UserDict import DictMixin
-
-# Modified from original to support Python 2.4, see
-# http://code.google.com/p/simplejson/issues/detail?id=53
-try:
- all
-except NameError:
- def all(seq):
- for elem in seq:
- if not elem:
- return False
- return True
-
-class OrderedDict(dict, DictMixin):
-
- def __init__(self, *args, **kwds):
- if len(args) > 1:
- raise TypeError('expected at most 1 arguments, got %d' % len(args))
- try:
- self.__end
- except AttributeError:
- self.clear()
- self.update(*args, **kwds)
-
- def clear(self):
- self.__end = end = []
- end += [None, end, end] # sentinel node for doubly linked list
- self.__map = {} # key --> [key, prev, next]
- dict.clear(self)
-
- def __setitem__(self, key, value):
- if key not in self:
- end = self.__end
- curr = end[1]
- curr[2] = end[1] = self.__map[key] = [key, curr, end]
- dict.__setitem__(self, key, value)
-
- def __delitem__(self, key):
- dict.__delitem__(self, key)
- key, prev, next = self.__map.pop(key)
- prev[2] = next
- next[1] = prev
-
- def __iter__(self):
- end = self.__end
- curr = end[2]
- while curr is not end:
- yield curr[0]
- curr = curr[2]
-
- def __reversed__(self):
- end = self.__end
- curr = end[1]
- while curr is not end:
- yield curr[0]
- curr = curr[1]
-
- def popitem(self, last=True):
- if not self:
- raise KeyError('dictionary is empty')
- # Modified from original to support Python 2.4, see
- # http://code.google.com/p/simplejson/issues/detail?id=53
- if last:
- key = reversed(self).next()
- else:
- key = iter(self).next()
- value = self.pop(key)
- return key, value
-
- def __reduce__(self):
- items = [[k, self[k]] for k in self]
- tmp = self.__map, self.__end
- del self.__map, self.__end
- inst_dict = vars(self).copy()
- self.__map, self.__end = tmp
- if inst_dict:
- return (self.__class__, (items,), inst_dict)
- return self.__class__, (items,)
-
- def keys(self):
- return list(self)
-
- setdefault = DictMixin.setdefault
- update = DictMixin.update
- pop = DictMixin.pop
- values = DictMixin.values
- items = DictMixin.items
- iterkeys = DictMixin.iterkeys
- itervalues = DictMixin.itervalues
- iteritems = DictMixin.iteritems
-
- def __repr__(self):
- if not self:
- return '%s()' % (self.__class__.__name__,)
- return '%s(%r)' % (self.__class__.__name__, self.items())
-
- def copy(self):
- return self.__class__(self)
-
- @classmethod
- def fromkeys(cls, iterable, value=None):
- d = cls()
- for key in iterable:
- d[key] = value
- return d
-
- def __eq__(self, other):
- if isinstance(other, OrderedDict):
- return len(self)==len(other) and \
- all(p==q for p, q in zip(self.items(), other.items()))
- return dict.__eq__(self, other)
-
- def __ne__(self, other):
- return not self == other
diff --git a/module/lib/simplejson/scanner.py b/module/lib/simplejson/scanner.py
deleted file mode 100644
index 54593a371..000000000
--- a/module/lib/simplejson/scanner.py
+++ /dev/null
@@ -1,77 +0,0 @@
-"""JSON token scanner
-"""
-import re
-def _import_c_make_scanner():
- try:
- from simplejson._speedups import make_scanner
- return make_scanner
- except ImportError:
- return None
-c_make_scanner = _import_c_make_scanner()
-
-__all__ = ['make_scanner']
-
-NUMBER_RE = re.compile(
- r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
- (re.VERBOSE | re.MULTILINE | re.DOTALL))
-
-def py_make_scanner(context):
- parse_object = context.parse_object
- parse_array = context.parse_array
- parse_string = context.parse_string
- match_number = NUMBER_RE.match
- encoding = context.encoding
- strict = context.strict
- parse_float = context.parse_float
- parse_int = context.parse_int
- parse_constant = context.parse_constant
- object_hook = context.object_hook
- object_pairs_hook = context.object_pairs_hook
- memo = context.memo
-
- def _scan_once(string, idx):
- try:
- nextchar = string[idx]
- except IndexError:
- raise StopIteration
-
- if nextchar == '"':
- return parse_string(string, idx + 1, encoding, strict)
- elif nextchar == '{':
- return parse_object((string, idx + 1), encoding, strict,
- _scan_once, object_hook, object_pairs_hook, memo)
- elif nextchar == '[':
- return parse_array((string, idx + 1), _scan_once)
- elif nextchar == 'n' and string[idx:idx + 4] == 'null':
- return None, idx + 4
- elif nextchar == 't' and string[idx:idx + 4] == 'true':
- return True, idx + 4
- elif nextchar == 'f' and string[idx:idx + 5] == 'false':
- return False, idx + 5
-
- m = match_number(string, idx)
- if m is not None:
- integer, frac, exp = m.groups()
- if frac or exp:
- res = parse_float(integer + (frac or '') + (exp or ''))
- else:
- res = parse_int(integer)
- return res, m.end()
- elif nextchar == 'N' and string[idx:idx + 3] == 'NaN':
- return parse_constant('NaN'), idx + 3
- elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity':
- return parse_constant('Infinity'), idx + 8
- elif nextchar == '-' and string[idx:idx + 9] == '-Infinity':
- return parse_constant('-Infinity'), idx + 9
- else:
- raise StopIteration
-
- def scan_once(string, idx):
- try:
- return _scan_once(string, idx)
- finally:
- memo.clear()
-
- return scan_once
-
-make_scanner = c_make_scanner or py_make_scanner
diff --git a/module/lib/simplejson/tool.py b/module/lib/simplejson/tool.py
deleted file mode 100644
index 73370db55..000000000
--- a/module/lib/simplejson/tool.py
+++ /dev/null
@@ -1,39 +0,0 @@
-r"""Command-line tool to validate and pretty-print JSON
-
-Usage::
-
- $ echo '{"json":"obj"}' | python -m simplejson.tool
- {
- "json": "obj"
- }
- $ echo '{ 1.2:3.4}' | python -m simplejson.tool
- Expecting property name: line 1 column 2 (char 2)
-
-"""
-import sys
-import simplejson as json
-
-def main():
- if len(sys.argv) == 1:
- infile = sys.stdin
- outfile = sys.stdout
- elif len(sys.argv) == 2:
- infile = open(sys.argv[1], 'rb')
- outfile = sys.stdout
- elif len(sys.argv) == 3:
- infile = open(sys.argv[1], 'rb')
- outfile = open(sys.argv[2], 'wb')
- else:
- raise SystemExit(sys.argv[0] + " [infile [outfile]]")
- try:
- obj = json.load(infile,
- object_pairs_hook=json.OrderedDict,
- use_decimal=True)
- except ValueError, e:
- raise SystemExit(e)
- json.dump(obj, outfile, sort_keys=True, indent=' ', use_decimal=True)
- outfile.write('\n')
-
-
-if __name__ == '__main__':
- main()
diff --git a/module/lib/thrift/TSCons.py b/module/lib/thrift/TSCons.py
deleted file mode 100644
index 24046256c..000000000
--- a/module/lib/thrift/TSCons.py
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from os import path
-from SCons.Builder import Builder
-
-def scons_env(env, add=''):
- opath = path.dirname(path.abspath('$TARGET'))
- lstr = 'thrift --gen cpp -o ' + opath + ' ' + add + ' $SOURCE'
- cppbuild = Builder(action = lstr)
- env.Append(BUILDERS = {'ThriftCpp' : cppbuild})
-
-def gen_cpp(env, dir, file):
- scons_env(env)
- suffixes = ['_types.h', '_types.cpp']
- targets = map(lambda s: 'gen-cpp/' + file + s, suffixes)
- return env.ThriftCpp(targets, dir+file+'.thrift')
diff --git a/module/lib/thrift/TSerialization.py b/module/lib/thrift/TSerialization.py
deleted file mode 100644
index b19f98aa8..000000000
--- a/module/lib/thrift/TSerialization.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from protocol import TBinaryProtocol
-from transport import TTransport
-
-def serialize(thrift_object, protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()):
- transport = TTransport.TMemoryBuffer()
- protocol = protocol_factory.getProtocol(transport)
- thrift_object.write(protocol)
- return transport.getvalue()
-
-def deserialize(base, buf, protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()):
- transport = TTransport.TMemoryBuffer(buf)
- protocol = protocol_factory.getProtocol(transport)
- base.read(protocol)
- return base
-
diff --git a/module/lib/thrift/Thrift.py b/module/lib/thrift/Thrift.py
deleted file mode 100644
index 1d271fcff..000000000
--- a/module/lib/thrift/Thrift.py
+++ /dev/null
@@ -1,154 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-import sys
-
-class TType:
- STOP = 0
- VOID = 1
- BOOL = 2
- BYTE = 3
- I08 = 3
- DOUBLE = 4
- I16 = 6
- I32 = 8
- I64 = 10
- STRING = 11
- UTF7 = 11
- STRUCT = 12
- MAP = 13
- SET = 14
- LIST = 15
- UTF8 = 16
- UTF16 = 17
-
- _VALUES_TO_NAMES = ( 'STOP',
- 'VOID',
- 'BOOL',
- 'BYTE',
- 'DOUBLE',
- None,
- 'I16',
- None,
- 'I32',
- None,
- 'I64',
- 'STRING',
- 'STRUCT',
- 'MAP',
- 'SET',
- 'LIST',
- 'UTF8',
- 'UTF16' )
-
-class TMessageType:
- CALL = 1
- REPLY = 2
- EXCEPTION = 3
- ONEWAY = 4
-
-class TProcessor:
-
- """Base class for procsessor, which works on two streams."""
-
- def process(iprot, oprot):
- pass
-
-class TException(Exception):
-
- """Base class for all thrift exceptions."""
-
- # BaseException.message is deprecated in Python v[2.6,3.0)
- if (2,6,0) <= sys.version_info < (3,0):
- def _get_message(self):
- return self._message
- def _set_message(self, message):
- self._message = message
- message = property(_get_message, _set_message)
-
- def __init__(self, message=None):
- Exception.__init__(self, message)
- self.message = message
-
-class TApplicationException(TException):
-
- """Application level thrift exceptions."""
-
- UNKNOWN = 0
- UNKNOWN_METHOD = 1
- INVALID_MESSAGE_TYPE = 2
- WRONG_METHOD_NAME = 3
- BAD_SEQUENCE_ID = 4
- MISSING_RESULT = 5
- INTERNAL_ERROR = 6
- PROTOCOL_ERROR = 7
-
- def __init__(self, type=UNKNOWN, message=None):
- TException.__init__(self, message)
- self.type = type
-
- def __str__(self):
- if self.message:
- return self.message
- elif self.type == self.UNKNOWN_METHOD:
- return 'Unknown method'
- elif self.type == self.INVALID_MESSAGE_TYPE:
- return 'Invalid message type'
- elif self.type == self.WRONG_METHOD_NAME:
- return 'Wrong method name'
- elif self.type == self.BAD_SEQUENCE_ID:
- return 'Bad sequence ID'
- elif self.type == self.MISSING_RESULT:
- return 'Missing result'
- else:
- return 'Default (unknown) TApplicationException'
-
- def read(self, iprot):
- iprot.readStructBegin()
- while True:
- (fname, ftype, fid) = iprot.readFieldBegin()
- if ftype == TType.STOP:
- break
- if fid == 1:
- if ftype == TType.STRING:
- self.message = iprot.readString();
- else:
- iprot.skip(ftype)
- elif fid == 2:
- if ftype == TType.I32:
- self.type = iprot.readI32();
- else:
- iprot.skip(ftype)
- else:
- iprot.skip(ftype)
- iprot.readFieldEnd()
- iprot.readStructEnd()
-
- def write(self, oprot):
- oprot.writeStructBegin('TApplicationException')
- if self.message != None:
- oprot.writeFieldBegin('message', TType.STRING, 1)
- oprot.writeString(self.message)
- oprot.writeFieldEnd()
- if self.type != None:
- oprot.writeFieldBegin('type', TType.I32, 2)
- oprot.writeI32(self.type)
- oprot.writeFieldEnd()
- oprot.writeFieldStop()
- oprot.writeStructEnd()
diff --git a/module/lib/thrift/__init__.py b/module/lib/thrift/__init__.py
deleted file mode 100644
index 48d659c40..000000000
--- a/module/lib/thrift/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-__all__ = ['Thrift', 'TSCons']
diff --git a/module/lib/thrift/protocol/TBase.py b/module/lib/thrift/protocol/TBase.py
deleted file mode 100644
index e675c7dc0..000000000
--- a/module/lib/thrift/protocol/TBase.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from thrift.Thrift import *
-from thrift.protocol import TBinaryProtocol
-from thrift.transport import TTransport
-
-try:
- from thrift.protocol import fastbinary
-except:
- fastbinary = None
-
-class TBase(object):
- __slots__ = []
-
- def __repr__(self):
- L = ['%s=%r' % (key, getattr(self, key))
- for key in self.__slots__ ]
- return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
-
- def __eq__(self, other):
- if not isinstance(other, self.__class__):
- return False
- for attr in self.__slots__:
- my_val = getattr(self, attr)
- other_val = getattr(other, attr)
- if my_val != other_val:
- return False
- return True
-
- def __ne__(self, other):
- return not (self == other)
-
- def read(self, iprot):
- if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None:
- fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec))
- return
- iprot.readStruct(self, self.thrift_spec)
-
- def write(self, oprot):
- if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None:
- oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec)))
- return
- oprot.writeStruct(self, self.thrift_spec)
-
-class TExceptionBase(Exception):
- # old style class so python2.4 can raise exceptions derived from this
- # This can't inherit from TBase because of that limitation.
- __slots__ = []
-
- __repr__ = TBase.__repr__.im_func
- __eq__ = TBase.__eq__.im_func
- __ne__ = TBase.__ne__.im_func
- read = TBase.read.im_func
- write = TBase.write.im_func
-
diff --git a/module/lib/thrift/protocol/TBinaryProtocol.py b/module/lib/thrift/protocol/TBinaryProtocol.py
deleted file mode 100644
index 50c6aa896..000000000
--- a/module/lib/thrift/protocol/TBinaryProtocol.py
+++ /dev/null
@@ -1,259 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from TProtocol import *
-from struct import pack, unpack
-
-class TBinaryProtocol(TProtocolBase):
-
- """Binary implementation of the Thrift protocol driver."""
-
- # NastyHaxx. Python 2.4+ on 32-bit machines forces hex constants to be
- # positive, converting this into a long. If we hardcode the int value
- # instead it'll stay in 32 bit-land.
-
- # VERSION_MASK = 0xffff0000
- VERSION_MASK = -65536
-
- # VERSION_1 = 0x80010000
- VERSION_1 = -2147418112
-
- TYPE_MASK = 0x000000ff
-
- def __init__(self, trans, strictRead=False, strictWrite=True):
- TProtocolBase.__init__(self, trans)
- self.strictRead = strictRead
- self.strictWrite = strictWrite
-
- def writeMessageBegin(self, name, type, seqid):
- if self.strictWrite:
- self.writeI32(TBinaryProtocol.VERSION_1 | type)
- self.writeString(name)
- self.writeI32(seqid)
- else:
- self.writeString(name)
- self.writeByte(type)
- self.writeI32(seqid)
-
- def writeMessageEnd(self):
- pass
-
- def writeStructBegin(self, name):
- pass
-
- def writeStructEnd(self):
- pass
-
- def writeFieldBegin(self, name, type, id):
- self.writeByte(type)
- self.writeI16(id)
-
- def writeFieldEnd(self):
- pass
-
- def writeFieldStop(self):
- self.writeByte(TType.STOP);
-
- def writeMapBegin(self, ktype, vtype, size):
- self.writeByte(ktype)
- self.writeByte(vtype)
- self.writeI32(size)
-
- def writeMapEnd(self):
- pass
-
- def writeListBegin(self, etype, size):
- self.writeByte(etype)
- self.writeI32(size)
-
- def writeListEnd(self):
- pass
-
- def writeSetBegin(self, etype, size):
- self.writeByte(etype)
- self.writeI32(size)
-
- def writeSetEnd(self):
- pass
-
- def writeBool(self, bool):
- if bool:
- self.writeByte(1)
- else:
- self.writeByte(0)
-
- def writeByte(self, byte):
- buff = pack("!b", byte)
- self.trans.write(buff)
-
- def writeI16(self, i16):
- buff = pack("!h", i16)
- self.trans.write(buff)
-
- def writeI32(self, i32):
- buff = pack("!i", i32)
- self.trans.write(buff)
-
- def writeI64(self, i64):
- buff = pack("!q", i64)
- self.trans.write(buff)
-
- def writeDouble(self, dub):
- buff = pack("!d", dub)
- self.trans.write(buff)
-
- def writeString(self, str):
- self.writeI32(len(str))
- self.trans.write(str)
-
- def readMessageBegin(self):
- sz = self.readI32()
- if sz < 0:
- version = sz & TBinaryProtocol.VERSION_MASK
- if version != TBinaryProtocol.VERSION_1:
- raise TProtocolException(type=TProtocolException.BAD_VERSION, message='Bad version in readMessageBegin: %d' % (sz))
- type = sz & TBinaryProtocol.TYPE_MASK
- name = self.readString()
- seqid = self.readI32()
- else:
- if self.strictRead:
- raise TProtocolException(type=TProtocolException.BAD_VERSION, message='No protocol version header')
- name = self.trans.readAll(sz)
- type = self.readByte()
- seqid = self.readI32()
- return (name, type, seqid)
-
- def readMessageEnd(self):
- pass
-
- def readStructBegin(self):
- pass
-
- def readStructEnd(self):
- pass
-
- def readFieldBegin(self):
- type = self.readByte()
- if type == TType.STOP:
- return (None, type, 0)
- id = self.readI16()
- return (None, type, id)
-
- def readFieldEnd(self):
- pass
-
- def readMapBegin(self):
- ktype = self.readByte()
- vtype = self.readByte()
- size = self.readI32()
- return (ktype, vtype, size)
-
- def readMapEnd(self):
- pass
-
- def readListBegin(self):
- etype = self.readByte()
- size = self.readI32()
- return (etype, size)
-
- def readListEnd(self):
- pass
-
- def readSetBegin(self):
- etype = self.readByte()
- size = self.readI32()
- return (etype, size)
-
- def readSetEnd(self):
- pass
-
- def readBool(self):
- byte = self.readByte()
- if byte == 0:
- return False
- return True
-
- def readByte(self):
- buff = self.trans.readAll(1)
- val, = unpack('!b', buff)
- return val
-
- def readI16(self):
- buff = self.trans.readAll(2)
- val, = unpack('!h', buff)
- return val
-
- def readI32(self):
- buff = self.trans.readAll(4)
- val, = unpack('!i', buff)
- return val
-
- def readI64(self):
- buff = self.trans.readAll(8)
- val, = unpack('!q', buff)
- return val
-
- def readDouble(self):
- buff = self.trans.readAll(8)
- val, = unpack('!d', buff)
- return val
-
- def readString(self):
- len = self.readI32()
- str = self.trans.readAll(len)
- return str
-
-
-class TBinaryProtocolFactory:
- def __init__(self, strictRead=False, strictWrite=True):
- self.strictRead = strictRead
- self.strictWrite = strictWrite
-
- def getProtocol(self, trans):
- prot = TBinaryProtocol(trans, self.strictRead, self.strictWrite)
- return prot
-
-
-class TBinaryProtocolAccelerated(TBinaryProtocol):
-
- """C-Accelerated version of TBinaryProtocol.
-
- This class does not override any of TBinaryProtocol's methods,
- but the generated code recognizes it directly and will call into
- our C module to do the encoding, bypassing this object entirely.
- We inherit from TBinaryProtocol so that the normal TBinaryProtocol
- encoding can happen if the fastbinary module doesn't work for some
- reason. (TODO(dreiss): Make this happen sanely in more cases.)
-
- In order to take advantage of the C module, just use
- TBinaryProtocolAccelerated instead of TBinaryProtocol.
-
- NOTE: This code was contributed by an external developer.
- The internal Thrift team has reviewed and tested it,
- but we cannot guarantee that it is production-ready.
- Please feel free to report bugs and/or success stories
- to the public mailing list.
- """
-
- pass
-
-
-class TBinaryProtocolAcceleratedFactory:
- def getProtocol(self, trans):
- return TBinaryProtocolAccelerated(trans)
diff --git a/module/lib/thrift/protocol/TCompactProtocol.py b/module/lib/thrift/protocol/TCompactProtocol.py
deleted file mode 100644
index 016a33171..000000000
--- a/module/lib/thrift/protocol/TCompactProtocol.py
+++ /dev/null
@@ -1,395 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from TProtocol import *
-from struct import pack, unpack
-
-__all__ = ['TCompactProtocol', 'TCompactProtocolFactory']
-
-CLEAR = 0
-FIELD_WRITE = 1
-VALUE_WRITE = 2
-CONTAINER_WRITE = 3
-BOOL_WRITE = 4
-FIELD_READ = 5
-CONTAINER_READ = 6
-VALUE_READ = 7
-BOOL_READ = 8
-
-def make_helper(v_from, container):
- def helper(func):
- def nested(self, *args, **kwargs):
- assert self.state in (v_from, container), (self.state, v_from, container)
- return func(self, *args, **kwargs)
- return nested
- return helper
-writer = make_helper(VALUE_WRITE, CONTAINER_WRITE)
-reader = make_helper(VALUE_READ, CONTAINER_READ)
-
-def makeZigZag(n, bits):
- return (n << 1) ^ (n >> (bits - 1))
-
-def fromZigZag(n):
- return (n >> 1) ^ -(n & 1)
-
-def writeVarint(trans, n):
- out = []
- while True:
- if n & ~0x7f == 0:
- out.append(n)
- break
- else:
- out.append((n & 0xff) | 0x80)
- n = n >> 7
- trans.write(''.join(map(chr, out)))
-
-def readVarint(trans):
- result = 0
- shift = 0
- while True:
- x = trans.readAll(1)
- byte = ord(x)
- result |= (byte & 0x7f) << shift
- if byte >> 7 == 0:
- return result
- shift += 7
-
-class CompactType:
- STOP = 0x00
- TRUE = 0x01
- FALSE = 0x02
- BYTE = 0x03
- I16 = 0x04
- I32 = 0x05
- I64 = 0x06
- DOUBLE = 0x07
- BINARY = 0x08
- LIST = 0x09
- SET = 0x0A
- MAP = 0x0B
- STRUCT = 0x0C
-
-CTYPES = {TType.STOP: CompactType.STOP,
- TType.BOOL: CompactType.TRUE, # used for collection
- TType.BYTE: CompactType.BYTE,
- TType.I16: CompactType.I16,
- TType.I32: CompactType.I32,
- TType.I64: CompactType.I64,
- TType.DOUBLE: CompactType.DOUBLE,
- TType.STRING: CompactType.BINARY,
- TType.STRUCT: CompactType.STRUCT,
- TType.LIST: CompactType.LIST,
- TType.SET: CompactType.SET,
- TType.MAP: CompactType.MAP
- }
-
-TTYPES = {}
-for k, v in CTYPES.items():
- TTYPES[v] = k
-TTYPES[CompactType.FALSE] = TType.BOOL
-del k
-del v
-
-class TCompactProtocol(TProtocolBase):
- "Compact implementation of the Thrift protocol driver."
-
- PROTOCOL_ID = 0x82
- VERSION = 1
- VERSION_MASK = 0x1f
- TYPE_MASK = 0xe0
- TYPE_SHIFT_AMOUNT = 5
-
- def __init__(self, trans):
- TProtocolBase.__init__(self, trans)
- self.state = CLEAR
- self.__last_fid = 0
- self.__bool_fid = None
- self.__bool_value = None
- self.__structs = []
- self.__containers = []
-
- def __writeVarint(self, n):
- writeVarint(self.trans, n)
-
- def writeMessageBegin(self, name, type, seqid):
- assert self.state == CLEAR
- self.__writeUByte(self.PROTOCOL_ID)
- self.__writeUByte(self.VERSION | (type << self.TYPE_SHIFT_AMOUNT))
- self.__writeVarint(seqid)
- self.__writeString(name)
- self.state = VALUE_WRITE
-
- def writeMessageEnd(self):
- assert self.state == VALUE_WRITE
- self.state = CLEAR
-
- def writeStructBegin(self, name):
- assert self.state in (CLEAR, CONTAINER_WRITE, VALUE_WRITE), self.state
- self.__structs.append((self.state, self.__last_fid))
- self.state = FIELD_WRITE
- self.__last_fid = 0
-
- def writeStructEnd(self):
- assert self.state == FIELD_WRITE
- self.state, self.__last_fid = self.__structs.pop()
-
- def writeFieldStop(self):
- self.__writeByte(0)
-
- def __writeFieldHeader(self, type, fid):
- delta = fid - self.__last_fid
- if 0 < delta <= 15:
- self.__writeUByte(delta << 4 | type)
- else:
- self.__writeByte(type)
- self.__writeI16(fid)
- self.__last_fid = fid
-
- def writeFieldBegin(self, name, type, fid):
- assert self.state == FIELD_WRITE, self.state
- if type == TType.BOOL:
- self.state = BOOL_WRITE
- self.__bool_fid = fid
- else:
- self.state = VALUE_WRITE
- self.__writeFieldHeader(CTYPES[type], fid)
-
- def writeFieldEnd(self):
- assert self.state in (VALUE_WRITE, BOOL_WRITE), self.state
- self.state = FIELD_WRITE
-
- def __writeUByte(self, byte):
- self.trans.write(pack('!B', byte))
-
- def __writeByte(self, byte):
- self.trans.write(pack('!b', byte))
-
- def __writeI16(self, i16):
- self.__writeVarint(makeZigZag(i16, 16))
-
- def __writeSize(self, i32):
- self.__writeVarint(i32)
-
- def writeCollectionBegin(self, etype, size):
- assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state
- if size <= 14:
- self.__writeUByte(size << 4 | CTYPES[etype])
- else:
- self.__writeUByte(0xf0 | CTYPES[etype])
- self.__writeSize(size)
- self.__containers.append(self.state)
- self.state = CONTAINER_WRITE
- writeSetBegin = writeCollectionBegin
- writeListBegin = writeCollectionBegin
-
- def writeMapBegin(self, ktype, vtype, size):
- assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state
- if size == 0:
- self.__writeByte(0)
- else:
- self.__writeSize(size)
- self.__writeUByte(CTYPES[ktype] << 4 | CTYPES[vtype])
- self.__containers.append(self.state)
- self.state = CONTAINER_WRITE
-
- def writeCollectionEnd(self):
- assert self.state == CONTAINER_WRITE, self.state
- self.state = self.__containers.pop()
- writeMapEnd = writeCollectionEnd
- writeSetEnd = writeCollectionEnd
- writeListEnd = writeCollectionEnd
-
- def writeBool(self, bool):
- if self.state == BOOL_WRITE:
- if bool:
- ctype = CompactType.TRUE
- else:
- ctype = CompactType.FALSE
- self.__writeFieldHeader(ctype, self.__bool_fid)
- elif self.state == CONTAINER_WRITE:
- if bool:
- self.__writeByte(CompactType.TRUE)
- else:
- self.__writeByte(CompactType.FALSE)
- else:
- raise AssertionError, "Invalid state in compact protocol"
-
- writeByte = writer(__writeByte)
- writeI16 = writer(__writeI16)
-
- @writer
- def writeI32(self, i32):
- self.__writeVarint(makeZigZag(i32, 32))
-
- @writer
- def writeI64(self, i64):
- self.__writeVarint(makeZigZag(i64, 64))
-
- @writer
- def writeDouble(self, dub):
- self.trans.write(pack('!d', dub))
-
- def __writeString(self, s):
- self.__writeSize(len(s))
- self.trans.write(s)
- writeString = writer(__writeString)
-
- def readFieldBegin(self):
- assert self.state == FIELD_READ, self.state
- type = self.__readUByte()
- if type & 0x0f == TType.STOP:
- return (None, 0, 0)
- delta = type >> 4
- if delta == 0:
- fid = self.__readI16()
- else:
- fid = self.__last_fid + delta
- self.__last_fid = fid
- type = type & 0x0f
- if type == CompactType.TRUE:
- self.state = BOOL_READ
- self.__bool_value = True
- elif type == CompactType.FALSE:
- self.state = BOOL_READ
- self.__bool_value = False
- else:
- self.state = VALUE_READ
- return (None, self.__getTType(type), fid)
-
- def readFieldEnd(self):
- assert self.state in (VALUE_READ, BOOL_READ), self.state
- self.state = FIELD_READ
-
- def __readUByte(self):
- result, = unpack('!B', self.trans.readAll(1))
- return result
-
- def __readByte(self):
- result, = unpack('!b', self.trans.readAll(1))
- return result
-
- def __readVarint(self):
- return readVarint(self.trans)
-
- def __readZigZag(self):
- return fromZigZag(self.__readVarint())
-
- def __readSize(self):
- result = self.__readVarint()
- if result < 0:
- raise TException("Length < 0")
- return result
-
- def readMessageBegin(self):
- assert self.state == CLEAR
- proto_id = self.__readUByte()
- if proto_id != self.PROTOCOL_ID:
- raise TProtocolException(TProtocolException.BAD_VERSION,
- 'Bad protocol id in the message: %d' % proto_id)
- ver_type = self.__readUByte()
- type = (ver_type & self.TYPE_MASK) >> self.TYPE_SHIFT_AMOUNT
- version = ver_type & self.VERSION_MASK
- if version != self.VERSION:
- raise TProtocolException(TProtocolException.BAD_VERSION,
- 'Bad version: %d (expect %d)' % (version, self.VERSION))
- seqid = self.__readVarint()
- name = self.__readString()
- return (name, type, seqid)
-
- def readMessageEnd(self):
- assert self.state == CLEAR
- assert len(self.__structs) == 0
-
- def readStructBegin(self):
- assert self.state in (CLEAR, CONTAINER_READ, VALUE_READ), self.state
- self.__structs.append((self.state, self.__last_fid))
- self.state = FIELD_READ
- self.__last_fid = 0
-
- def readStructEnd(self):
- assert self.state == FIELD_READ
- self.state, self.__last_fid = self.__structs.pop()
-
- def readCollectionBegin(self):
- assert self.state in (VALUE_READ, CONTAINER_READ), self.state
- size_type = self.__readUByte()
- size = size_type >> 4
- type = self.__getTType(size_type)
- if size == 15:
- size = self.__readSize()
- self.__containers.append(self.state)
- self.state = CONTAINER_READ
- return type, size
- readSetBegin = readCollectionBegin
- readListBegin = readCollectionBegin
-
- def readMapBegin(self):
- assert self.state in (VALUE_READ, CONTAINER_READ), self.state
- size = self.__readSize()
- types = 0
- if size > 0:
- types = self.__readUByte()
- vtype = self.__getTType(types)
- ktype = self.__getTType(types >> 4)
- self.__containers.append(self.state)
- self.state = CONTAINER_READ
- return (ktype, vtype, size)
-
- def readCollectionEnd(self):
- assert self.state == CONTAINER_READ, self.state
- self.state = self.__containers.pop()
- readSetEnd = readCollectionEnd
- readListEnd = readCollectionEnd
- readMapEnd = readCollectionEnd
-
- def readBool(self):
- if self.state == BOOL_READ:
- return self.__bool_value == CompactType.TRUE
- elif self.state == CONTAINER_READ:
- return self.__readByte() == CompactType.TRUE
- else:
- raise AssertionError, "Invalid state in compact protocol: %d" % self.state
-
- readByte = reader(__readByte)
- __readI16 = __readZigZag
- readI16 = reader(__readZigZag)
- readI32 = reader(__readZigZag)
- readI64 = reader(__readZigZag)
-
- @reader
- def readDouble(self):
- buff = self.trans.readAll(8)
- val, = unpack('!d', buff)
- return val
-
- def __readString(self):
- len = self.__readSize()
- return self.trans.readAll(len)
- readString = reader(__readString)
-
- def __getTType(self, byte):
- return TTYPES[byte & 0x0f]
-
-
-class TCompactProtocolFactory:
- def __init__(self):
- pass
-
- def getProtocol(self, trans):
- return TCompactProtocol(trans)
diff --git a/module/lib/thrift/protocol/TProtocol.py b/module/lib/thrift/protocol/TProtocol.py
deleted file mode 100644
index 7338ff68a..000000000
--- a/module/lib/thrift/protocol/TProtocol.py
+++ /dev/null
@@ -1,404 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from thrift.Thrift import *
-
-class TProtocolException(TException):
-
- """Custom Protocol Exception class"""
-
- UNKNOWN = 0
- INVALID_DATA = 1
- NEGATIVE_SIZE = 2
- SIZE_LIMIT = 3
- BAD_VERSION = 4
-
- def __init__(self, type=UNKNOWN, message=None):
- TException.__init__(self, message)
- self.type = type
-
-class TProtocolBase:
-
- """Base class for Thrift protocol driver."""
-
- def __init__(self, trans):
- self.trans = trans
-
- def writeMessageBegin(self, name, type, seqid):
- pass
-
- def writeMessageEnd(self):
- pass
-
- def writeStructBegin(self, name):
- pass
-
- def writeStructEnd(self):
- pass
-
- def writeFieldBegin(self, name, type, id):
- pass
-
- def writeFieldEnd(self):
- pass
-
- def writeFieldStop(self):
- pass
-
- def writeMapBegin(self, ktype, vtype, size):
- pass
-
- def writeMapEnd(self):
- pass
-
- def writeListBegin(self, etype, size):
- pass
-
- def writeListEnd(self):
- pass
-
- def writeSetBegin(self, etype, size):
- pass
-
- def writeSetEnd(self):
- pass
-
- def writeBool(self, bool):
- pass
-
- def writeByte(self, byte):
- pass
-
- def writeI16(self, i16):
- pass
-
- def writeI32(self, i32):
- pass
-
- def writeI64(self, i64):
- pass
-
- def writeDouble(self, dub):
- pass
-
- def writeString(self, str):
- pass
-
- def readMessageBegin(self):
- pass
-
- def readMessageEnd(self):
- pass
-
- def readStructBegin(self):
- pass
-
- def readStructEnd(self):
- pass
-
- def readFieldBegin(self):
- pass
-
- def readFieldEnd(self):
- pass
-
- def readMapBegin(self):
- pass
-
- def readMapEnd(self):
- pass
-
- def readListBegin(self):
- pass
-
- def readListEnd(self):
- pass
-
- def readSetBegin(self):
- pass
-
- def readSetEnd(self):
- pass
-
- def readBool(self):
- pass
-
- def readByte(self):
- pass
-
- def readI16(self):
- pass
-
- def readI32(self):
- pass
-
- def readI64(self):
- pass
-
- def readDouble(self):
- pass
-
- def readString(self):
- pass
-
- def skip(self, type):
- if type == TType.STOP:
- return
- elif type == TType.BOOL:
- self.readBool()
- elif type == TType.BYTE:
- self.readByte()
- elif type == TType.I16:
- self.readI16()
- elif type == TType.I32:
- self.readI32()
- elif type == TType.I64:
- self.readI64()
- elif type == TType.DOUBLE:
- self.readDouble()
- elif type == TType.STRING:
- self.readString()
- elif type == TType.STRUCT:
- name = self.readStructBegin()
- while True:
- (name, type, id) = self.readFieldBegin()
- if type == TType.STOP:
- break
- self.skip(type)
- self.readFieldEnd()
- self.readStructEnd()
- elif type == TType.MAP:
- (ktype, vtype, size) = self.readMapBegin()
- for i in range(size):
- self.skip(ktype)
- self.skip(vtype)
- self.readMapEnd()
- elif type == TType.SET:
- (etype, size) = self.readSetBegin()
- for i in range(size):
- self.skip(etype)
- self.readSetEnd()
- elif type == TType.LIST:
- (etype, size) = self.readListBegin()
- for i in range(size):
- self.skip(etype)
- self.readListEnd()
-
- # tuple of: ( 'reader method' name, is_container boolean, 'writer_method' name )
- _TTYPE_HANDLERS = (
- (None, None, False), # 0 == TType,STOP
- (None, None, False), # 1 == TType.VOID # TODO: handle void?
- ('readBool', 'writeBool', False), # 2 == TType.BOOL
- ('readByte', 'writeByte', False), # 3 == TType.BYTE and I08
- ('readDouble', 'writeDouble', False), # 4 == TType.DOUBLE
- (None, None, False), # 5, undefined
- ('readI16', 'writeI16', False), # 6 == TType.I16
- (None, None, False), # 7, undefined
- ('readI32', 'writeI32', False), # 8 == TType.I32
- (None, None, False), # 9, undefined
- ('readI64', 'writeI64', False), # 10 == TType.I64
- ('readString', 'writeString', False), # 11 == TType.STRING and UTF7
- ('readContainerStruct', 'writeContainerStruct', True), # 12 == TType.STRUCT
- ('readContainerMap', 'writeContainerMap', True), # 13 == TType.MAP
- ('readContainerSet', 'writeContainerSet', True), # 14 == TType.SET
- ('readContainerList', 'writeContainerList', True), # 15 == TType.LIST
- (None, None, False), # 16 == TType.UTF8 # TODO: handle utf8 types?
- (None, None, False)# 17 == TType.UTF16 # TODO: handle utf16 types?
- )
-
- def readFieldByTType(self, ttype, spec):
- try:
- (r_handler, w_handler, is_container) = self._TTYPE_HANDLERS[ttype]
- except IndexError:
- raise TProtocolException(type=TProtocolException.INVALID_DATA,
- message='Invalid field type %d' % (ttype))
- if r_handler is None:
- raise TProtocolException(type=TProtocolException.INVALID_DATA,
- message='Invalid field type %d' % (ttype))
- reader = getattr(self, r_handler)
- if not is_container:
- return reader()
- return reader(spec)
-
- def readContainerList(self, spec):
- results = []
- ttype, tspec = spec[0], spec[1]
- r_handler = self._TTYPE_HANDLERS[ttype][0]
- reader = getattr(self, r_handler)
- (list_type, list_len) = self.readListBegin()
- if tspec is None:
- # list values are simple types
- for idx in xrange(list_len):
- results.append(reader())
- else:
- # this is like an inlined readFieldByTType
- container_reader = self._TTYPE_HANDLERS[list_type][0]
- val_reader = getattr(self, container_reader)
- for idx in xrange(list_len):
- val = val_reader(tspec)
- results.append(val)
- self.readListEnd()
- return results
-
- def readContainerSet(self, spec):
- results = set()
- ttype, tspec = spec[0], spec[1]
- r_handler = self._TTYPE_HANDLERS[ttype][0]
- reader = getattr(self, r_handler)
- (set_type, set_len) = self.readSetBegin()
- if tspec is None:
- # set members are simple types
- for idx in xrange(set_len):
- results.add(reader())
- else:
- container_reader = self._TTYPE_HANDLERS[set_type][0]
- val_reader = getattr(self, container_reader)
- for idx in xrange(set_len):
- results.add(val_reader(tspec))
- self.readSetEnd()
- return results
-
- def readContainerStruct(self, spec):
- (obj_class, obj_spec) = spec
- obj = obj_class()
- obj.read(self)
- return obj
-
- def readContainerMap(self, spec):
- results = dict()
- key_ttype, key_spec = spec[0], spec[1]
- val_ttype, val_spec = spec[2], spec[3]
- (map_ktype, map_vtype, map_len) = self.readMapBegin()
- # TODO: compare types we just decoded with thrift_spec and abort/skip if types disagree
- key_reader = getattr(self, self._TTYPE_HANDLERS[key_ttype][0])
- val_reader = getattr(self, self._TTYPE_HANDLERS[val_ttype][0])
- # list values are simple types
- for idx in xrange(map_len):
- if key_spec is None:
- k_val = key_reader()
- else:
- k_val = self.readFieldByTType(key_ttype, key_spec)
- if val_spec is None:
- v_val = val_reader()
- else:
- v_val = self.readFieldByTType(val_ttype, val_spec)
- # this raises a TypeError with unhashable keys types. i.e. d=dict(); d[[0,1]] = 2 fails
- results[k_val] = v_val
- self.readMapEnd()
- return results
-
- def readStruct(self, obj, thrift_spec):
- self.readStructBegin()
- while True:
- (fname, ftype, fid) = self.readFieldBegin()
- if ftype == TType.STOP:
- break
- try:
- field = thrift_spec[fid]
- except IndexError:
- self.skip(ftype)
- else:
- if field is not None and ftype == field[1]:
- fname = field[2]
- fspec = field[3]
- val = self.readFieldByTType(ftype, fspec)
- setattr(obj, fname, val)
- else:
- self.skip(ftype)
- self.readFieldEnd()
- self.readStructEnd()
-
- def writeContainerStruct(self, val, spec):
- val.write(self)
-
- def writeContainerList(self, val, spec):
- self.writeListBegin(spec[0], len(val))
- r_handler, w_handler, is_container = self._TTYPE_HANDLERS[spec[0]]
- e_writer = getattr(self, w_handler)
- if not is_container:
- for elem in val:
- e_writer(elem)
- else:
- for elem in val:
- e_writer(elem, spec[1])
- self.writeListEnd()
-
- def writeContainerSet(self, val, spec):
- self.writeSetBegin(spec[0], len(val))
- r_handler, w_handler, is_container = self._TTYPE_HANDLERS[spec[0]]
- e_writer = getattr(self, w_handler)
- if not is_container:
- for elem in val:
- e_writer(elem)
- else:
- for elem in val:
- e_writer(elem, spec[1])
- self.writeSetEnd()
-
- def writeContainerMap(self, val, spec):
- k_type = spec[0]
- v_type = spec[2]
- ignore, ktype_name, k_is_container = self._TTYPE_HANDLERS[k_type]
- ignore, vtype_name, v_is_container = self._TTYPE_HANDLERS[v_type]
- k_writer = getattr(self, ktype_name)
- v_writer = getattr(self, vtype_name)
- self.writeMapBegin(k_type, v_type, len(val))
- for m_key, m_val in val.iteritems():
- if not k_is_container:
- k_writer(m_key)
- else:
- k_writer(m_key, spec[1])
- if not v_is_container:
- v_writer(m_val)
- else:
- v_writer(m_val, spec[3])
- self.writeMapEnd()
-
- def writeStruct(self, obj, thrift_spec):
- self.writeStructBegin(obj.__class__.__name__)
- for field in thrift_spec:
- if field is None:
- continue
- fname = field[2]
- val = getattr(obj, fname)
- if val is None:
- # skip writing out unset fields
- continue
- fid = field[0]
- ftype = field[1]
- fspec = field[3]
- # get the writer method for this value
- self.writeFieldBegin(fname, ftype, fid)
- self.writeFieldByTType(ftype, val, fspec)
- self.writeFieldEnd()
- self.writeFieldStop()
- self.writeStructEnd()
-
- def writeFieldByTType(self, ttype, val, spec):
- r_handler, w_handler, is_container = self._TTYPE_HANDLERS[ttype]
- writer = getattr(self, w_handler)
- if is_container:
- writer(val, spec)
- else:
- writer(val)
-
-class TProtocolFactory:
- def getProtocol(self, trans):
- pass
-
diff --git a/module/lib/thrift/protocol/__init__.py b/module/lib/thrift/protocol/__init__.py
deleted file mode 100644
index d53359b28..000000000
--- a/module/lib/thrift/protocol/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-__all__ = ['TProtocol', 'TBinaryProtocol', 'fastbinary', 'TBase']
diff --git a/module/lib/thrift/server/THttpServer.py b/module/lib/thrift/server/THttpServer.py
deleted file mode 100644
index 3047d9c00..000000000
--- a/module/lib/thrift/server/THttpServer.py
+++ /dev/null
@@ -1,82 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-import BaseHTTPServer
-
-from thrift.server import TServer
-from thrift.transport import TTransport
-
-class ResponseException(Exception):
- """Allows handlers to override the HTTP response
-
- Normally, THttpServer always sends a 200 response. If a handler wants
- to override this behavior (e.g., to simulate a misconfigured or
- overloaded web server during testing), it can raise a ResponseException.
- The function passed to the constructor will be called with the
- RequestHandler as its only argument.
- """
- def __init__(self, handler):
- self.handler = handler
-
-
-class THttpServer(TServer.TServer):
- """A simple HTTP-based Thrift server
-
- This class is not very performant, but it is useful (for example) for
- acting as a mock version of an Apache-based PHP Thrift endpoint."""
-
- def __init__(self, processor, server_address,
- inputProtocolFactory, outputProtocolFactory = None,
- server_class = BaseHTTPServer.HTTPServer):
- """Set up protocol factories and HTTP server.
-
- See BaseHTTPServer for server_address.
- See TServer for protocol factories."""
-
- if outputProtocolFactory is None:
- outputProtocolFactory = inputProtocolFactory
-
- TServer.TServer.__init__(self, processor, None, None, None,
- inputProtocolFactory, outputProtocolFactory)
-
- thttpserver = self
-
- class RequestHander(BaseHTTPServer.BaseHTTPRequestHandler):
- def do_POST(self):
- # Don't care about the request path.
- itrans = TTransport.TFileObjectTransport(self.rfile)
- otrans = TTransport.TFileObjectTransport(self.wfile)
- itrans = TTransport.TBufferedTransport(itrans, int(self.headers['Content-Length']))
- otrans = TTransport.TMemoryBuffer()
- iprot = thttpserver.inputProtocolFactory.getProtocol(itrans)
- oprot = thttpserver.outputProtocolFactory.getProtocol(otrans)
- try:
- thttpserver.processor.process(iprot, oprot)
- except ResponseException, exn:
- exn.handler(self)
- else:
- self.send_response(200)
- self.send_header("content-type", "application/x-thrift")
- self.end_headers()
- self.wfile.write(otrans.getvalue())
-
- self.httpd = server_class(server_address, RequestHander)
-
- def serve(self):
- self.httpd.serve_forever()
diff --git a/module/lib/thrift/server/TNonblockingServer.py b/module/lib/thrift/server/TNonblockingServer.py
deleted file mode 100644
index ea348a0b6..000000000
--- a/module/lib/thrift/server/TNonblockingServer.py
+++ /dev/null
@@ -1,310 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-"""Implementation of non-blocking server.
-
-The main idea of the server is reciving and sending requests
-only from main thread.
-
-It also makes thread pool server in tasks terms, not connections.
-"""
-import threading
-import socket
-import Queue
-import select
-import struct
-import logging
-
-from thrift.transport import TTransport
-from thrift.protocol.TBinaryProtocol import TBinaryProtocolFactory
-
-__all__ = ['TNonblockingServer']
-
-class Worker(threading.Thread):
- """Worker is a small helper to process incoming connection."""
- def __init__(self, queue):
- threading.Thread.__init__(self)
- self.queue = queue
-
- def run(self):
- """Process queries from task queue, stop if processor is None."""
- while True:
- try:
- processor, iprot, oprot, otrans, callback = self.queue.get()
- if processor is None:
- break
- processor.process(iprot, oprot)
- callback(True, otrans.getvalue())
- except Exception:
- logging.exception("Exception while processing request")
- callback(False, '')
-
-WAIT_LEN = 0
-WAIT_MESSAGE = 1
-WAIT_PROCESS = 2
-SEND_ANSWER = 3
-CLOSED = 4
-
-def locked(func):
- "Decorator which locks self.lock."
- def nested(self, *args, **kwargs):
- self.lock.acquire()
- try:
- return func(self, *args, **kwargs)
- finally:
- self.lock.release()
- return nested
-
-def socket_exception(func):
- "Decorator close object on socket.error."
- def read(self, *args, **kwargs):
- try:
- return func(self, *args, **kwargs)
- except socket.error:
- self.close()
- return read
-
-class Connection:
- """Basic class is represented connection.
-
- It can be in state:
- WAIT_LEN --- connection is reading request len.
- WAIT_MESSAGE --- connection is reading request.
- WAIT_PROCESS --- connection has just read whole request and
- waits for call ready routine.
- SEND_ANSWER --- connection is sending answer string (including length
- of answer).
- CLOSED --- socket was closed and connection should be deleted.
- """
- def __init__(self, new_socket, wake_up):
- self.socket = new_socket
- self.socket.setblocking(False)
- self.status = WAIT_LEN
- self.len = 0
- self.message = ''
- self.lock = threading.Lock()
- self.wake_up = wake_up
-
- def _read_len(self):
- """Reads length of request.
-
- It's really paranoic routine and it may be replaced by
- self.socket.recv(4)."""
- read = self.socket.recv(4 - len(self.message))
- if len(read) == 0:
- # if we read 0 bytes and self.message is empty, it means client close
- # connection
- if len(self.message) != 0:
- logging.error("can't read frame size from socket")
- self.close()
- return
- self.message += read
- if len(self.message) == 4:
- self.len, = struct.unpack('!i', self.message)
- if self.len < 0:
- logging.error("negative frame size, it seems client"\
- " doesn't use FramedTransport")
- self.close()
- elif self.len == 0:
- logging.error("empty frame, it's really strange")
- self.close()
- else:
- self.message = ''
- self.status = WAIT_MESSAGE
-
- @socket_exception
- def read(self):
- """Reads data from stream and switch state."""
- assert self.status in (WAIT_LEN, WAIT_MESSAGE)
- if self.status == WAIT_LEN:
- self._read_len()
- # go back to the main loop here for simplicity instead of
- # falling through, even though there is a good chance that
- # the message is already available
- elif self.status == WAIT_MESSAGE:
- read = self.socket.recv(self.len - len(self.message))
- if len(read) == 0:
- logging.error("can't read frame from socket (get %d of %d bytes)" %
- (len(self.message), self.len))
- self.close()
- return
- self.message += read
- if len(self.message) == self.len:
- self.status = WAIT_PROCESS
-
- @socket_exception
- def write(self):
- """Writes data from socket and switch state."""
- assert self.status == SEND_ANSWER
- sent = self.socket.send(self.message)
- if sent == len(self.message):
- self.status = WAIT_LEN
- self.message = ''
- self.len = 0
- else:
- self.message = self.message[sent:]
-
- @locked
- def ready(self, all_ok, message):
- """Callback function for switching state and waking up main thread.
-
- This function is the only function witch can be called asynchronous.
-
- The ready can switch Connection to three states:
- WAIT_LEN if request was oneway.
- SEND_ANSWER if request was processed in normal way.
- CLOSED if request throws unexpected exception.
-
- The one wakes up main thread.
- """
- assert self.status == WAIT_PROCESS
- if not all_ok:
- self.close()
- self.wake_up()
- return
- self.len = ''
- if len(message) == 0:
- # it was a oneway request, do not write answer
- self.message = ''
- self.status = WAIT_LEN
- else:
- self.message = struct.pack('!i', len(message)) + message
- self.status = SEND_ANSWER
- self.wake_up()
-
- @locked
- def is_writeable(self):
- "Returns True if connection should be added to write list of select."
- return self.status == SEND_ANSWER
-
- # it's not necessary, but...
- @locked
- def is_readable(self):
- "Returns True if connection should be added to read list of select."
- return self.status in (WAIT_LEN, WAIT_MESSAGE)
-
- @locked
- def is_closed(self):
- "Returns True if connection is closed."
- return self.status == CLOSED
-
- def fileno(self):
- "Returns the file descriptor of the associated socket."
- return self.socket.fileno()
-
- def close(self):
- "Closes connection"
- self.status = CLOSED
- self.socket.close()
-
-class TNonblockingServer:
- """Non-blocking server."""
- def __init__(self, processor, lsocket, inputProtocolFactory=None,
- outputProtocolFactory=None, threads=10):
- self.processor = processor
- self.socket = lsocket
- self.in_protocol = inputProtocolFactory or TBinaryProtocolFactory()
- self.out_protocol = outputProtocolFactory or self.in_protocol
- self.threads = int(threads)
- self.clients = {}
- self.tasks = Queue.Queue()
- self._read, self._write = socket.socketpair()
- self.prepared = False
-
- def setNumThreads(self, num):
- """Set the number of worker threads that should be created."""
- # implement ThreadPool interface
- assert not self.prepared, "You can't change number of threads for working server"
- self.threads = num
-
- def prepare(self):
- """Prepares server for serve requests."""
- self.socket.listen()
- for _ in xrange(self.threads):
- thread = Worker(self.tasks)
- thread.setDaemon(True)
- thread.start()
- self.prepared = True
-
- def wake_up(self):
- """Wake up main thread.
-
- The server usualy waits in select call in we should terminate one.
- The simplest way is using socketpair.
-
- Select always wait to read from the first socket of socketpair.
-
- In this case, we can just write anything to the second socket from
- socketpair."""
- self._write.send('1')
-
- def _select(self):
- """Does select on open connections."""
- readable = [self.socket.handle.fileno(), self._read.fileno()]
- writable = []
- for i, connection in self.clients.items():
- if connection.is_readable():
- readable.append(connection.fileno())
- if connection.is_writeable():
- writable.append(connection.fileno())
- if connection.is_closed():
- del self.clients[i]
- return select.select(readable, writable, readable)
-
- def handle(self):
- """Handle requests.
-
- WARNING! You must call prepare BEFORE calling handle.
- """
- assert self.prepared, "You have to call prepare before handle"
- rset, wset, xset = self._select()
- for readable in rset:
- if readable == self._read.fileno():
- # don't care i just need to clean readable flag
- self._read.recv(1024)
- elif readable == self.socket.handle.fileno():
- client = self.socket.accept().handle
- self.clients[client.fileno()] = Connection(client, self.wake_up)
- else:
- connection = self.clients[readable]
- connection.read()
- if connection.status == WAIT_PROCESS:
- itransport = TTransport.TMemoryBuffer(connection.message)
- otransport = TTransport.TMemoryBuffer()
- iprot = self.in_protocol.getProtocol(itransport)
- oprot = self.out_protocol.getProtocol(otransport)
- self.tasks.put([self.processor, iprot, oprot,
- otransport, connection.ready])
- for writeable in wset:
- self.clients[writeable].write()
- for oob in xset:
- self.clients[oob].close()
- del self.clients[oob]
-
- def close(self):
- """Closes the server."""
- for _ in xrange(self.threads):
- self.tasks.put([None, None, None, None, None])
- self.socket.close()
- self.prepared = False
-
- def serve(self):
- """Serve forever."""
- self.prepare()
- while True:
- self.handle()
diff --git a/module/lib/thrift/server/TProcessPoolServer.py b/module/lib/thrift/server/TProcessPoolServer.py
deleted file mode 100644
index 7ed814a88..000000000
--- a/module/lib/thrift/server/TProcessPoolServer.py
+++ /dev/null
@@ -1,125 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-
-import logging
-from multiprocessing import Process, Value, Condition, reduction
-
-from TServer import TServer
-from thrift.transport.TTransport import TTransportException
-
-class TProcessPoolServer(TServer):
-
- """
- Server with a fixed size pool of worker subprocesses which service requests.
- Note that if you need shared state between the handlers - it's up to you!
- Written by Dvir Volk, doat.com
- """
-
- def __init__(self, * args):
- TServer.__init__(self, *args)
- self.numWorkers = 10
- self.workers = []
- self.isRunning = Value('b', False)
- self.stopCondition = Condition()
- self.postForkCallback = None
-
- def setPostForkCallback(self, callback):
- if not callable(callback):
- raise TypeError("This is not a callback!")
- self.postForkCallback = callback
-
- def setNumWorkers(self, num):
- """Set the number of worker threads that should be created"""
- self.numWorkers = num
-
- def workerProcess(self):
- """Loop around getting clients from the shared queue and process them."""
-
- if self.postForkCallback:
- self.postForkCallback()
-
- while self.isRunning.value == True:
- try:
- client = self.serverTransport.accept()
- self.serveClient(client)
- except (KeyboardInterrupt, SystemExit):
- return 0
- except Exception, x:
- logging.exception(x)
-
- def serveClient(self, client):
- """Process input/output from a client for as long as possible"""
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
- iprot = self.inputProtocolFactory.getProtocol(itrans)
- oprot = self.outputProtocolFactory.getProtocol(otrans)
-
- try:
- while True:
- self.processor.process(iprot, oprot)
- except TTransportException, tx:
- pass
- except Exception, x:
- logging.exception(x)
-
- itrans.close()
- otrans.close()
-
-
- def serve(self):
- """Start a fixed number of worker threads and put client into a queue"""
-
- #this is a shared state that can tell the workers to exit when set as false
- self.isRunning.value = True
-
- #first bind and listen to the port
- self.serverTransport.listen()
-
- #fork the children
- for i in range(self.numWorkers):
- try:
- w = Process(target=self.workerProcess)
- w.daemon = True
- w.start()
- self.workers.append(w)
- except Exception, x:
- logging.exception(x)
-
- #wait until the condition is set by stop()
-
- while True:
-
- self.stopCondition.acquire()
- try:
- self.stopCondition.wait()
- break
- except (SystemExit, KeyboardInterrupt):
- break
- except Exception, x:
- logging.exception(x)
-
- self.isRunning.value = False
-
- def stop(self):
- self.isRunning.value = False
- self.stopCondition.acquire()
- self.stopCondition.notify()
- self.stopCondition.release()
-
diff --git a/module/lib/thrift/server/TServer.py b/module/lib/thrift/server/TServer.py
deleted file mode 100644
index 8456e2d40..000000000
--- a/module/lib/thrift/server/TServer.py
+++ /dev/null
@@ -1,274 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-import logging
-import sys
-import os
-import traceback
-import threading
-import Queue
-
-from thrift.Thrift import TProcessor
-from thrift.transport import TTransport
-from thrift.protocol import TBinaryProtocol
-
-class TServer:
-
- """Base interface for a server, which must have a serve method."""
-
- """ 3 constructors for all servers:
- 1) (processor, serverTransport)
- 2) (processor, serverTransport, transportFactory, protocolFactory)
- 3) (processor, serverTransport,
- inputTransportFactory, outputTransportFactory,
- inputProtocolFactory, outputProtocolFactory)"""
- def __init__(self, *args):
- if (len(args) == 2):
- self.__initArgs__(args[0], args[1],
- TTransport.TTransportFactoryBase(),
- TTransport.TTransportFactoryBase(),
- TBinaryProtocol.TBinaryProtocolFactory(),
- TBinaryProtocol.TBinaryProtocolFactory())
- elif (len(args) == 4):
- self.__initArgs__(args[0], args[1], args[2], args[2], args[3], args[3])
- elif (len(args) == 6):
- self.__initArgs__(args[0], args[1], args[2], args[3], args[4], args[5])
-
- def __initArgs__(self, processor, serverTransport,
- inputTransportFactory, outputTransportFactory,
- inputProtocolFactory, outputProtocolFactory):
- self.processor = processor
- self.serverTransport = serverTransport
- self.inputTransportFactory = inputTransportFactory
- self.outputTransportFactory = outputTransportFactory
- self.inputProtocolFactory = inputProtocolFactory
- self.outputProtocolFactory = outputProtocolFactory
-
- def serve(self):
- pass
-
-class TSimpleServer(TServer):
-
- """Simple single-threaded server that just pumps around one transport."""
-
- def __init__(self, *args):
- TServer.__init__(self, *args)
-
- def serve(self):
- self.serverTransport.listen()
- while True:
- client = self.serverTransport.accept()
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
- iprot = self.inputProtocolFactory.getProtocol(itrans)
- oprot = self.outputProtocolFactory.getProtocol(otrans)
- try:
- while True:
- self.processor.process(iprot, oprot)
- except TTransport.TTransportException, tx:
- pass
- except Exception, x:
- logging.exception(x)
-
- itrans.close()
- otrans.close()
-
-class TThreadedServer(TServer):
-
- """Threaded server that spawns a new thread per each connection."""
-
- def __init__(self, *args, **kwargs):
- TServer.__init__(self, *args)
- self.daemon = kwargs.get("daemon", False)
-
- def serve(self):
- self.serverTransport.listen()
- while True:
- try:
- client = self.serverTransport.accept()
- t = threading.Thread(target = self.handle, args=(client,))
- t.setDaemon(self.daemon)
- t.start()
- except KeyboardInterrupt:
- raise
- except Exception, x:
- logging.exception(x)
-
- def handle(self, client):
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
- iprot = self.inputProtocolFactory.getProtocol(itrans)
- oprot = self.outputProtocolFactory.getProtocol(otrans)
- try:
- while True:
- self.processor.process(iprot, oprot)
- except TTransport.TTransportException, tx:
- pass
- except Exception, x:
- logging.exception(x)
-
- itrans.close()
- otrans.close()
-
-class TThreadPoolServer(TServer):
-
- """Server with a fixed size pool of threads which service requests."""
-
- def __init__(self, *args, **kwargs):
- TServer.__init__(self, *args)
- self.clients = Queue.Queue()
- self.threads = 10
- self.daemon = kwargs.get("daemon", False)
-
- def setNumThreads(self, num):
- """Set the number of worker threads that should be created"""
- self.threads = num
-
- def serveThread(self):
- """Loop around getting clients from the shared queue and process them."""
- while True:
- try:
- client = self.clients.get()
- self.serveClient(client)
- except Exception, x:
- logging.exception(x)
-
- def serveClient(self, client):
- """Process input/output from a client for as long as possible"""
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
- iprot = self.inputProtocolFactory.getProtocol(itrans)
- oprot = self.outputProtocolFactory.getProtocol(otrans)
- try:
- while True:
- self.processor.process(iprot, oprot)
- except TTransport.TTransportException, tx:
- pass
- except Exception, x:
- logging.exception(x)
-
- itrans.close()
- otrans.close()
-
- def serve(self):
- """Start a fixed number of worker threads and put client into a queue"""
- for i in range(self.threads):
- try:
- t = threading.Thread(target = self.serveThread)
- t.setDaemon(self.daemon)
- t.start()
- except Exception, x:
- logging.exception(x)
-
- # Pump the socket for clients
- self.serverTransport.listen()
- while True:
- try:
- client = self.serverTransport.accept()
- self.clients.put(client)
- except Exception, x:
- logging.exception(x)
-
-
-class TForkingServer(TServer):
-
- """A Thrift server that forks a new process for each request"""
- """
- This is more scalable than the threaded server as it does not cause
- GIL contention.
-
- Note that this has different semantics from the threading server.
- Specifically, updates to shared variables will no longer be shared.
- It will also not work on windows.
-
- This code is heavily inspired by SocketServer.ForkingMixIn in the
- Python stdlib.
- """
-
- def __init__(self, *args):
- TServer.__init__(self, *args)
- self.children = []
-
- def serve(self):
- def try_close(file):
- try:
- file.close()
- except IOError, e:
- logging.warning(e, exc_info=True)
-
-
- self.serverTransport.listen()
- while True:
- client = self.serverTransport.accept()
- try:
- pid = os.fork()
-
- if pid: # parent
- # add before collect, otherwise you race w/ waitpid
- self.children.append(pid)
- self.collect_children()
-
- # Parent must close socket or the connection may not get
- # closed promptly
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
- try_close(itrans)
- try_close(otrans)
- else:
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
-
- iprot = self.inputProtocolFactory.getProtocol(itrans)
- oprot = self.outputProtocolFactory.getProtocol(otrans)
-
- ecode = 0
- try:
- try:
- while True:
- self.processor.process(iprot, oprot)
- except TTransport.TTransportException, tx:
- pass
- except Exception, e:
- logging.exception(e)
- ecode = 1
- finally:
- try_close(itrans)
- try_close(otrans)
-
- os._exit(ecode)
-
- except TTransport.TTransportException, tx:
- pass
- except Exception, x:
- logging.exception(x)
-
-
- def collect_children(self):
- while self.children:
- try:
- pid, status = os.waitpid(0, os.WNOHANG)
- except os.error:
- pid = None
-
- if pid:
- self.children.remove(pid)
- else:
- break
-
-
diff --git a/module/lib/thrift/server/__init__.py b/module/lib/thrift/server/__init__.py
deleted file mode 100644
index 1bf6e254e..000000000
--- a/module/lib/thrift/server/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-__all__ = ['TServer', 'TNonblockingServer']
diff --git a/module/lib/thrift/transport/THttpClient.py b/module/lib/thrift/transport/THttpClient.py
deleted file mode 100644
index 50269785c..000000000
--- a/module/lib/thrift/transport/THttpClient.py
+++ /dev/null
@@ -1,126 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from TTransport import *
-from cStringIO import StringIO
-
-import urlparse
-import httplib
-import warnings
-import socket
-
-class THttpClient(TTransportBase):
-
- """Http implementation of TTransport base."""
-
- def __init__(self, uri_or_host, port=None, path=None):
- """THttpClient supports two different types constructor parameters.
-
- THttpClient(host, port, path) - deprecated
- THttpClient(uri)
-
- Only the second supports https."""
-
- if port is not None:
- warnings.warn("Please use the THttpClient('http://host:port/path') syntax", DeprecationWarning, stacklevel=2)
- self.host = uri_or_host
- self.port = port
- assert path
- self.path = path
- self.scheme = 'http'
- else:
- parsed = urlparse.urlparse(uri_or_host)
- self.scheme = parsed.scheme
- assert self.scheme in ('http', 'https')
- if self.scheme == 'http':
- self.port = parsed.port or httplib.HTTP_PORT
- elif self.scheme == 'https':
- self.port = parsed.port or httplib.HTTPS_PORT
- self.host = parsed.hostname
- self.path = parsed.path
- if parsed.query:
- self.path += '?%s' % parsed.query
- self.__wbuf = StringIO()
- self.__http = None
- self.__timeout = None
-
- def open(self):
- if self.scheme == 'http':
- self.__http = httplib.HTTP(self.host, self.port)
- else:
- self.__http = httplib.HTTPS(self.host, self.port)
-
- def close(self):
- self.__http.close()
- self.__http = None
-
- def isOpen(self):
- return self.__http != None
-
- def setTimeout(self, ms):
- if not hasattr(socket, 'getdefaulttimeout'):
- raise NotImplementedError
-
- if ms is None:
- self.__timeout = None
- else:
- self.__timeout = ms/1000.0
-
- def read(self, sz):
- return self.__http.file.read(sz)
-
- def write(self, buf):
- self.__wbuf.write(buf)
-
- def __withTimeout(f):
- def _f(*args, **kwargs):
- orig_timeout = socket.getdefaulttimeout()
- socket.setdefaulttimeout(args[0].__timeout)
- result = f(*args, **kwargs)
- socket.setdefaulttimeout(orig_timeout)
- return result
- return _f
-
- def flush(self):
- if self.isOpen():
- self.close()
- self.open();
-
- # Pull data out of buffer
- data = self.__wbuf.getvalue()
- self.__wbuf = StringIO()
-
- # HTTP request
- self.__http.putrequest('POST', self.path)
-
- # Write headers
- self.__http.putheader('Host', self.host)
- self.__http.putheader('Content-Type', 'application/x-thrift')
- self.__http.putheader('Content-Length', str(len(data)))
- self.__http.endheaders()
-
- # Write payload
- self.__http.send(data)
-
- # Get reply to flush the request
- self.code, self.message, self.headers = self.__http.getreply()
-
- # Decorate if we know how to timeout
- if hasattr(socket, 'getdefaulttimeout'):
- flush = __withTimeout(flush)
diff --git a/module/lib/thrift/transport/TSocket.py b/module/lib/thrift/transport/TSocket.py
deleted file mode 100644
index 4e0e1874f..000000000
--- a/module/lib/thrift/transport/TSocket.py
+++ /dev/null
@@ -1,163 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from TTransport import *
-import os
-import errno
-import socket
-import sys
-
-class TSocketBase(TTransportBase):
- def _resolveAddr(self):
- if self._unix_socket is not None:
- return [(socket.AF_UNIX, socket.SOCK_STREAM, None, None, self._unix_socket)]
- else:
- return socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE | socket.AI_ADDRCONFIG)
-
- def close(self):
- if self.handle:
- self.handle.close()
- self.handle = None
-
-class TSocket(TSocketBase):
- """Socket implementation of TTransport base."""
-
- def __init__(self, host='localhost', port=9090, unix_socket=None):
- """Initialize a TSocket
-
- @param host(str) The host to connect to.
- @param port(int) The (TCP) port to connect to.
- @param unix_socket(str) The filename of a unix socket to connect to.
- (host and port will be ignored.)
- """
-
- self.host = host
- self.port = port
- self.handle = None
- self._unix_socket = unix_socket
- self._timeout = None
-
- def setHandle(self, h):
- self.handle = h
-
- def isOpen(self):
- return self.handle is not None
-
- def setTimeout(self, ms):
- if ms is None:
- self._timeout = None
- else:
- self._timeout = ms/1000.0
-
- if self.handle is not None:
- self.handle.settimeout(self._timeout)
-
- def open(self):
- try:
- res0 = self._resolveAddr()
- for res in res0:
- self.handle = socket.socket(res[0], res[1])
- self.handle.settimeout(self._timeout)
- try:
- self.handle.connect(res[4])
- except socket.error, e:
- if res is not res0[-1]:
- continue
- else:
- raise e
- break
- except socket.error, e:
- if self._unix_socket:
- message = 'Could not connect to socket %s' % self._unix_socket
- else:
- message = 'Could not connect to %s:%d' % (self.host, self.port)
- raise TTransportException(type=TTransportException.NOT_OPEN, message=message)
-
- def read(self, sz):
- try:
- buff = self.handle.recv(sz)
- except socket.error, e:
- if (e.args[0] == errno.ECONNRESET and
- (sys.platform == 'darwin' or sys.platform.startswith('freebsd'))):
- # freebsd and Mach don't follow POSIX semantic of recv
- # and fail with ECONNRESET if peer performed shutdown.
- # See corresponding comment and code in TSocket::read()
- # in lib/cpp/src/transport/TSocket.cpp.
- self.close()
- # Trigger the check to raise the END_OF_FILE exception below.
- buff = ''
- else:
- raise
- if len(buff) == 0:
- raise TTransportException(type=TTransportException.END_OF_FILE, message='TSocket read 0 bytes')
- return buff
-
- def write(self, buff):
- if not self.handle:
- raise TTransportException(type=TTransportException.NOT_OPEN, message='Transport not open')
- sent = 0
- have = len(buff)
- while sent < have:
- plus = self.handle.send(buff)
- if plus == 0:
- raise TTransportException(type=TTransportException.END_OF_FILE, message='TSocket sent 0 bytes')
- sent += plus
- buff = buff[plus:]
-
- def flush(self):
- pass
-
-class TServerSocket(TSocketBase, TServerTransportBase):
- """Socket implementation of TServerTransport base."""
-
- def __init__(self, host=None, port=9090, unix_socket=None):
- self.host = host
- self.port = port
- self._unix_socket = unix_socket
- self.handle = None
-
- def listen(self):
- res0 = self._resolveAddr()
- for res in res0:
- if res[0] is socket.AF_INET6 or res is res0[-1]:
- break
-
- # We need remove the old unix socket if the file exists and
- # nobody is listening on it.
- if self._unix_socket:
- tmp = socket.socket(res[0], res[1])
- try:
- tmp.connect(res[4])
- except socket.error, err:
- eno, message = err.args
- if eno == errno.ECONNREFUSED:
- os.unlink(res[4])
-
- self.handle = socket.socket(res[0], res[1])
- self.handle.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- if hasattr(self.handle, 'settimeout'):
- self.handle.settimeout(None)
- self.handle.bind(res[4])
- self.handle.listen(128)
-
- def accept(self):
- client, addr = self.handle.accept()
- result = TSocket()
- result.setHandle(client)
- return result
diff --git a/module/lib/thrift/transport/TTransport.py b/module/lib/thrift/transport/TTransport.py
deleted file mode 100644
index 12e51a9bf..000000000
--- a/module/lib/thrift/transport/TTransport.py
+++ /dev/null
@@ -1,331 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from cStringIO import StringIO
-from struct import pack,unpack
-from thrift.Thrift import TException
-
-class TTransportException(TException):
-
- """Custom Transport Exception class"""
-
- UNKNOWN = 0
- NOT_OPEN = 1
- ALREADY_OPEN = 2
- TIMED_OUT = 3
- END_OF_FILE = 4
-
- def __init__(self, type=UNKNOWN, message=None):
- TException.__init__(self, message)
- self.type = type
-
-class TTransportBase:
-
- """Base class for Thrift transport layer."""
-
- def isOpen(self):
- pass
-
- def open(self):
- pass
-
- def close(self):
- pass
-
- def read(self, sz):
- pass
-
- def readAll(self, sz):
- buff = ''
- have = 0
- while (have < sz):
- chunk = self.read(sz-have)
- have += len(chunk)
- buff += chunk
-
- if len(chunk) == 0:
- raise EOFError()
-
- return buff
-
- def write(self, buf):
- pass
-
- def flush(self):
- pass
-
-# This class should be thought of as an interface.
-class CReadableTransport:
- """base class for transports that are readable from C"""
-
- # TODO(dreiss): Think about changing this interface to allow us to use
- # a (Python, not c) StringIO instead, because it allows
- # you to write after reading.
-
- # NOTE: This is a classic class, so properties will NOT work
- # correctly for setting.
- @property
- def cstringio_buf(self):
- """A cStringIO buffer that contains the current chunk we are reading."""
- pass
-
- def cstringio_refill(self, partialread, reqlen):
- """Refills cstringio_buf.
-
- Returns the currently used buffer (which can but need not be the same as
- the old cstringio_buf). partialread is what the C code has read from the
- buffer, and should be inserted into the buffer before any more reads. The
- return value must be a new, not borrowed reference. Something along the
- lines of self._buf should be fine.
-
- If reqlen bytes can't be read, throw EOFError.
- """
- pass
-
-class TServerTransportBase:
-
- """Base class for Thrift server transports."""
-
- def listen(self):
- pass
-
- def accept(self):
- pass
-
- def close(self):
- pass
-
-class TTransportFactoryBase:
-
- """Base class for a Transport Factory"""
-
- def getTransport(self, trans):
- return trans
-
-class TBufferedTransportFactory:
-
- """Factory transport that builds buffered transports"""
-
- def getTransport(self, trans):
- buffered = TBufferedTransport(trans)
- return buffered
-
-
-class TBufferedTransport(TTransportBase,CReadableTransport):
-
- """Class that wraps another transport and buffers its I/O.
-
- The implementation uses a (configurable) fixed-size read buffer
- but buffers all writes until a flush is performed.
- """
-
- DEFAULT_BUFFER = 4096
-
- def __init__(self, trans, rbuf_size = DEFAULT_BUFFER):
- self.__trans = trans
- self.__wbuf = StringIO()
- self.__rbuf = StringIO("")
- self.__rbuf_size = rbuf_size
-
- def isOpen(self):
- return self.__trans.isOpen()
-
- def open(self):
- return self.__trans.open()
-
- def close(self):
- return self.__trans.close()
-
- def read(self, sz):
- ret = self.__rbuf.read(sz)
- if len(ret) != 0:
- return ret
-
- self.__rbuf = StringIO(self.__trans.read(max(sz, self.__rbuf_size)))
- return self.__rbuf.read(sz)
-
- def write(self, buf):
- self.__wbuf.write(buf)
-
- def flush(self):
- out = self.__wbuf.getvalue()
- # reset wbuf before write/flush to preserve state on underlying failure
- self.__wbuf = StringIO()
- self.__trans.write(out)
- self.__trans.flush()
-
- # Implement the CReadableTransport interface.
- @property
- def cstringio_buf(self):
- return self.__rbuf
-
- def cstringio_refill(self, partialread, reqlen):
- retstring = partialread
- if reqlen < self.__rbuf_size:
- # try to make a read of as much as we can.
- retstring += self.__trans.read(self.__rbuf_size)
-
- # but make sure we do read reqlen bytes.
- if len(retstring) < reqlen:
- retstring += self.__trans.readAll(reqlen - len(retstring))
-
- self.__rbuf = StringIO(retstring)
- return self.__rbuf
-
-class TMemoryBuffer(TTransportBase, CReadableTransport):
- """Wraps a cStringIO object as a TTransport.
-
- NOTE: Unlike the C++ version of this class, you cannot write to it
- then immediately read from it. If you want to read from a
- TMemoryBuffer, you must either pass a string to the constructor.
- TODO(dreiss): Make this work like the C++ version.
- """
-
- def __init__(self, value=None):
- """value -- a value to read from for stringio
-
- If value is set, this will be a transport for reading,
- otherwise, it is for writing"""
- if value is not None:
- self._buffer = StringIO(value)
- else:
- self._buffer = StringIO()
-
- def isOpen(self):
- return not self._buffer.closed
-
- def open(self):
- pass
-
- def close(self):
- self._buffer.close()
-
- def read(self, sz):
- return self._buffer.read(sz)
-
- def write(self, buf):
- self._buffer.write(buf)
-
- def flush(self):
- pass
-
- def getvalue(self):
- return self._buffer.getvalue()
-
- # Implement the CReadableTransport interface.
- @property
- def cstringio_buf(self):
- return self._buffer
-
- def cstringio_refill(self, partialread, reqlen):
- # only one shot at reading...
- raise EOFError()
-
-class TFramedTransportFactory:
-
- """Factory transport that builds framed transports"""
-
- def getTransport(self, trans):
- framed = TFramedTransport(trans)
- return framed
-
-
-class TFramedTransport(TTransportBase, CReadableTransport):
-
- """Class that wraps another transport and frames its I/O when writing."""
-
- def __init__(self, trans,):
- self.__trans = trans
- self.__rbuf = StringIO()
- self.__wbuf = StringIO()
-
- def isOpen(self):
- return self.__trans.isOpen()
-
- def open(self):
- return self.__trans.open()
-
- def close(self):
- return self.__trans.close()
-
- def read(self, sz):
- ret = self.__rbuf.read(sz)
- if len(ret) != 0:
- return ret
-
- self.readFrame()
- return self.__rbuf.read(sz)
-
- def readFrame(self):
- buff = self.__trans.readAll(4)
- sz, = unpack('!i', buff)
- self.__rbuf = StringIO(self.__trans.readAll(sz))
-
- def write(self, buf):
- self.__wbuf.write(buf)
-
- def flush(self):
- wout = self.__wbuf.getvalue()
- wsz = len(wout)
- # reset wbuf before write/flush to preserve state on underlying failure
- self.__wbuf = StringIO()
- # N.B.: Doing this string concatenation is WAY cheaper than making
- # two separate calls to the underlying socket object. Socket writes in
- # Python turn out to be REALLY expensive, but it seems to do a pretty
- # good job of managing string buffer operations without excessive copies
- buf = pack("!i", wsz) + wout
- self.__trans.write(buf)
- self.__trans.flush()
-
- # Implement the CReadableTransport interface.
- @property
- def cstringio_buf(self):
- return self.__rbuf
-
- def cstringio_refill(self, prefix, reqlen):
- # self.__rbuf will already be empty here because fastbinary doesn't
- # ask for a refill until the previous buffer is empty. Therefore,
- # we can start reading new frames immediately.
- while len(prefix) < reqlen:
- self.readFrame()
- prefix += self.__rbuf.getvalue()
- self.__rbuf = StringIO(prefix)
- return self.__rbuf
-
-
-class TFileObjectTransport(TTransportBase):
- """Wraps a file-like object to make it work as a Thrift transport."""
-
- def __init__(self, fileobj):
- self.fileobj = fileobj
-
- def isOpen(self):
- return True
-
- def close(self):
- self.fileobj.close()
-
- def read(self, sz):
- return self.fileobj.read(sz)
-
- def write(self, buf):
- self.fileobj.write(buf)
-
- def flush(self):
- self.fileobj.flush()
diff --git a/module/lib/thrift/transport/TTwisted.py b/module/lib/thrift/transport/TTwisted.py
deleted file mode 100644
index b6dcb4e0b..000000000
--- a/module/lib/thrift/transport/TTwisted.py
+++ /dev/null
@@ -1,219 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-from zope.interface import implements, Interface, Attribute
-from twisted.internet.protocol import Protocol, ServerFactory, ClientFactory, \
- connectionDone
-from twisted.internet import defer
-from twisted.protocols import basic
-from twisted.python import log
-from twisted.web import server, resource, http
-
-from thrift.transport import TTransport
-from cStringIO import StringIO
-
-
-class TMessageSenderTransport(TTransport.TTransportBase):
-
- def __init__(self):
- self.__wbuf = StringIO()
-
- def write(self, buf):
- self.__wbuf.write(buf)
-
- def flush(self):
- msg = self.__wbuf.getvalue()
- self.__wbuf = StringIO()
- self.sendMessage(msg)
-
- def sendMessage(self, message):
- raise NotImplementedError
-
-
-class TCallbackTransport(TMessageSenderTransport):
-
- def __init__(self, func):
- TMessageSenderTransport.__init__(self)
- self.func = func
-
- def sendMessage(self, message):
- self.func(message)
-
-
-class ThriftClientProtocol(basic.Int32StringReceiver):
-
- MAX_LENGTH = 2 ** 31 - 1
-
- def __init__(self, client_class, iprot_factory, oprot_factory=None):
- self._client_class = client_class
- self._iprot_factory = iprot_factory
- if oprot_factory is None:
- self._oprot_factory = iprot_factory
- else:
- self._oprot_factory = oprot_factory
-
- self.recv_map = {}
- self.started = defer.Deferred()
-
- def dispatch(self, msg):
- self.sendString(msg)
-
- def connectionMade(self):
- tmo = TCallbackTransport(self.dispatch)
- self.client = self._client_class(tmo, self._oprot_factory)
- self.started.callback(self.client)
-
- def connectionLost(self, reason=connectionDone):
- for k,v in self.client._reqs.iteritems():
- tex = TTransport.TTransportException(
- type=TTransport.TTransportException.END_OF_FILE,
- message='Connection closed')
- v.errback(tex)
-
- def stringReceived(self, frame):
- tr = TTransport.TMemoryBuffer(frame)
- iprot = self._iprot_factory.getProtocol(tr)
- (fname, mtype, rseqid) = iprot.readMessageBegin()
-
- try:
- method = self.recv_map[fname]
- except KeyError:
- method = getattr(self.client, 'recv_' + fname)
- self.recv_map[fname] = method
-
- method(iprot, mtype, rseqid)
-
-
-class ThriftServerProtocol(basic.Int32StringReceiver):
-
- MAX_LENGTH = 2 ** 31 - 1
-
- def dispatch(self, msg):
- self.sendString(msg)
-
- def processError(self, error):
- self.transport.loseConnection()
-
- def processOk(self, _, tmo):
- msg = tmo.getvalue()
-
- if len(msg) > 0:
- self.dispatch(msg)
-
- def stringReceived(self, frame):
- tmi = TTransport.TMemoryBuffer(frame)
- tmo = TTransport.TMemoryBuffer()
-
- iprot = self.factory.iprot_factory.getProtocol(tmi)
- oprot = self.factory.oprot_factory.getProtocol(tmo)
-
- d = self.factory.processor.process(iprot, oprot)
- d.addCallbacks(self.processOk, self.processError,
- callbackArgs=(tmo,))
-
-
-class IThriftServerFactory(Interface):
-
- processor = Attribute("Thrift processor")
-
- iprot_factory = Attribute("Input protocol factory")
-
- oprot_factory = Attribute("Output protocol factory")
-
-
-class IThriftClientFactory(Interface):
-
- client_class = Attribute("Thrift client class")
-
- iprot_factory = Attribute("Input protocol factory")
-
- oprot_factory = Attribute("Output protocol factory")
-
-
-class ThriftServerFactory(ServerFactory):
-
- implements(IThriftServerFactory)
-
- protocol = ThriftServerProtocol
-
- def __init__(self, processor, iprot_factory, oprot_factory=None):
- self.processor = processor
- self.iprot_factory = iprot_factory
- if oprot_factory is None:
- self.oprot_factory = iprot_factory
- else:
- self.oprot_factory = oprot_factory
-
-
-class ThriftClientFactory(ClientFactory):
-
- implements(IThriftClientFactory)
-
- protocol = ThriftClientProtocol
-
- def __init__(self, client_class, iprot_factory, oprot_factory=None):
- self.client_class = client_class
- self.iprot_factory = iprot_factory
- if oprot_factory is None:
- self.oprot_factory = iprot_factory
- else:
- self.oprot_factory = oprot_factory
-
- def buildProtocol(self, addr):
- p = self.protocol(self.client_class, self.iprot_factory,
- self.oprot_factory)
- p.factory = self
- return p
-
-
-class ThriftResource(resource.Resource):
-
- allowedMethods = ('POST',)
-
- def __init__(self, processor, inputProtocolFactory,
- outputProtocolFactory=None):
- resource.Resource.__init__(self)
- self.inputProtocolFactory = inputProtocolFactory
- if outputProtocolFactory is None:
- self.outputProtocolFactory = inputProtocolFactory
- else:
- self.outputProtocolFactory = outputProtocolFactory
- self.processor = processor
-
- def getChild(self, path, request):
- return self
-
- def _cbProcess(self, _, request, tmo):
- msg = tmo.getvalue()
- request.setResponseCode(http.OK)
- request.setHeader("content-type", "application/x-thrift")
- request.write(msg)
- request.finish()
-
- def render_POST(self, request):
- request.content.seek(0, 0)
- data = request.content.read()
- tmi = TTransport.TMemoryBuffer(data)
- tmo = TTransport.TMemoryBuffer()
-
- iprot = self.inputProtocolFactory.getProtocol(tmi)
- oprot = self.outputProtocolFactory.getProtocol(tmo)
-
- d = self.processor.process(iprot, oprot)
- d.addCallback(self._cbProcess, request, tmo)
- return server.NOT_DONE_YET
diff --git a/module/lib/thrift/transport/TZlibTransport.py b/module/lib/thrift/transport/TZlibTransport.py
deleted file mode 100644
index 784d4e1e0..000000000
--- a/module/lib/thrift/transport/TZlibTransport.py
+++ /dev/null
@@ -1,261 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-'''
-TZlibTransport provides a compressed transport and transport factory
-class, using the python standard library zlib module to implement
-data compression.
-'''
-
-from __future__ import division
-import zlib
-from cStringIO import StringIO
-from TTransport import TTransportBase, CReadableTransport
-
-class TZlibTransportFactory(object):
- '''
- Factory transport that builds zlib compressed transports.
-
- This factory caches the last single client/transport that it was passed
- and returns the same TZlibTransport object that was created.
-
- This caching means the TServer class will get the _same_ transport
- object for both input and output transports from this factory.
- (For non-threaded scenarios only, since the cache only holds one object)
-
- The purpose of this caching is to allocate only one TZlibTransport where
- only one is really needed (since it must have separate read/write buffers),
- and makes the statistics from getCompSavings() and getCompRatio()
- easier to understand.
- '''
-
- # class scoped cache of last transport given and zlibtransport returned
- _last_trans = None
- _last_z = None
-
- def getTransport(self, trans, compresslevel=9):
- '''Wrap a transport , trans, with the TZlibTransport
- compressed transport class, returning a new
- transport to the caller.
-
- @param compresslevel: The zlib compression level, ranging
- from 0 (no compression) to 9 (best compression). Defaults to 9.
- @type compresslevel: int
-
- This method returns a TZlibTransport which wraps the
- passed C{trans} TTransport derived instance.
- '''
- if trans == self._last_trans:
- return self._last_z
- ztrans = TZlibTransport(trans, compresslevel)
- self._last_trans = trans
- self._last_z = ztrans
- return ztrans
-
-
-class TZlibTransport(TTransportBase, CReadableTransport):
- '''
- Class that wraps a transport with zlib, compressing writes
- and decompresses reads, using the python standard
- library zlib module.
- '''
-
- # Read buffer size for the python fastbinary C extension,
- # the TBinaryProtocolAccelerated class.
- DEFAULT_BUFFSIZE = 4096
-
- def __init__(self, trans, compresslevel=9):
- '''
- Create a new TZlibTransport, wrapping C{trans}, another
- TTransport derived object.
-
- @param trans: A thrift transport object, i.e. a TSocket() object.
- @type trans: TTransport
- @param compresslevel: The zlib compression level, ranging
- from 0 (no compression) to 9 (best compression). Default is 9.
- @type compresslevel: int
- '''
- self.__trans = trans
- self.compresslevel = compresslevel
- self.__rbuf = StringIO()
- self.__wbuf = StringIO()
- self._init_zlib()
- self._init_stats()
-
- def _reinit_buffers(self):
- '''
- Internal method to initialize/reset the internal StringIO objects
- for read and write buffers.
- '''
- self.__rbuf = StringIO()
- self.__wbuf = StringIO()
-
- def _init_stats(self):
- '''
- Internal method to reset the internal statistics counters
- for compression ratios and bandwidth savings.
- '''
- self.bytes_in = 0
- self.bytes_out = 0
- self.bytes_in_comp = 0
- self.bytes_out_comp = 0
-
- def _init_zlib(self):
- '''
- Internal method for setting up the zlib compression and
- decompression objects.
- '''
- self._zcomp_read = zlib.decompressobj()
- self._zcomp_write = zlib.compressobj(self.compresslevel)
-
- def getCompRatio(self):
- '''
- Get the current measured compression ratios (in,out) from
- this transport.
-
- Returns a tuple of:
- (inbound_compression_ratio, outbound_compression_ratio)
-
- The compression ratios are computed as:
- compressed / uncompressed
-
- E.g., data that compresses by 10x will have a ratio of: 0.10
- and data that compresses to half of ts original size will
- have a ratio of 0.5
-
- None is returned if no bytes have yet been processed in
- a particular direction.
- '''
- r_percent, w_percent = (None, None)
- if self.bytes_in > 0:
- r_percent = self.bytes_in_comp / self.bytes_in
- if self.bytes_out > 0:
- w_percent = self.bytes_out_comp / self.bytes_out
- return (r_percent, w_percent)
-
- def getCompSavings(self):
- '''
- Get the current count of saved bytes due to data
- compression.
-
- Returns a tuple of:
- (inbound_saved_bytes, outbound_saved_bytes)
-
- Note: if compression is actually expanding your
- data (only likely with very tiny thrift objects), then
- the values returned will be negative.
- '''
- r_saved = self.bytes_in - self.bytes_in_comp
- w_saved = self.bytes_out - self.bytes_out_comp
- return (r_saved, w_saved)
-
- def isOpen(self):
- '''Return the underlying transport's open status'''
- return self.__trans.isOpen()
-
- def open(self):
- """Open the underlying transport"""
- self._init_stats()
- return self.__trans.open()
-
- def listen(self):
- '''Invoke the underlying transport's listen() method'''
- self.__trans.listen()
-
- def accept(self):
- '''Accept connections on the underlying transport'''
- return self.__trans.accept()
-
- def close(self):
- '''Close the underlying transport,'''
- self._reinit_buffers()
- self._init_zlib()
- return self.__trans.close()
-
- def read(self, sz):
- '''
- Read up to sz bytes from the decompressed bytes buffer, and
- read from the underlying transport if the decompression
- buffer is empty.
- '''
- ret = self.__rbuf.read(sz)
- if len(ret) > 0:
- return ret
- # keep reading from transport until something comes back
- while True:
- if self.readComp(sz):
- break
- ret = self.__rbuf.read(sz)
- return ret
-
- def readComp(self, sz):
- '''
- Read compressed data from the underlying transport, then
- decompress it and append it to the internal StringIO read buffer
- '''
- zbuf = self.__trans.read(sz)
- zbuf = self._zcomp_read.unconsumed_tail + zbuf
- buf = self._zcomp_read.decompress(zbuf)
- self.bytes_in += len(zbuf)
- self.bytes_in_comp += len(buf)
- old = self.__rbuf.read()
- self.__rbuf = StringIO(old + buf)
- if len(old) + len(buf) == 0:
- return False
- return True
-
- def write(self, buf):
- '''
- Write some bytes, putting them into the internal write
- buffer for eventual compression.
- '''
- self.__wbuf.write(buf)
-
- def flush(self):
- '''
- Flush any queued up data in the write buffer and ensure the
- compression buffer is flushed out to the underlying transport
- '''
- wout = self.__wbuf.getvalue()
- if len(wout) > 0:
- zbuf = self._zcomp_write.compress(wout)
- self.bytes_out += len(wout)
- self.bytes_out_comp += len(zbuf)
- else:
- zbuf = ''
- ztail = self._zcomp_write.flush(zlib.Z_SYNC_FLUSH)
- self.bytes_out_comp += len(ztail)
- if (len(zbuf) + len(ztail)) > 0:
- self.__wbuf = StringIO()
- self.__trans.write(zbuf + ztail)
- self.__trans.flush()
-
- @property
- def cstringio_buf(self):
- '''Implement the CReadableTransport interface'''
- return self.__rbuf
-
- def cstringio_refill(self, partialread, reqlen):
- '''Implement the CReadableTransport interface for refill'''
- retstring = partialread
- if reqlen < self.DEFAULT_BUFFSIZE:
- retstring += self.read(self.DEFAULT_BUFFSIZE)
- while len(retstring) < reqlen:
- retstring += self.read(reqlen - len(retstring))
- self.__rbuf = StringIO(retstring)
- return self.__rbuf
diff --git a/module/lib/thrift/transport/__init__.py b/module/lib/thrift/transport/__init__.py
deleted file mode 100644
index 46e54fe6b..000000000
--- a/module/lib/thrift/transport/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-__all__ = ['TTransport', 'TSocket', 'THttpClient','TZlibTransport']
diff --git a/module/lib/wsgiserver/LICENSE.txt b/module/lib/wsgiserver/LICENSE.txt
deleted file mode 100644
index a15165ee2..000000000
--- a/module/lib/wsgiserver/LICENSE.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (c) 2004-2007, CherryPy Team (team@cherrypy.org)
-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 CherryPy Team 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.
diff --git a/module/lib/wsgiserver/__init__.py b/module/lib/wsgiserver/__init__.py
deleted file mode 100644
index c380e18b0..000000000
--- a/module/lib/wsgiserver/__init__.py
+++ /dev/null
@@ -1,1794 +0,0 @@
-"""A high-speed, production ready, thread pooled, generic WSGI server.
-
-Simplest example on how to use this module directly
-(without using CherryPy's application machinery):
-
- from cherrypy import wsgiserver
-
- def my_crazy_app(environ, start_response):
- status = '200 OK'
- response_headers = [('Content-type','text/plain')]
- start_response(status, response_headers)
- return ['Hello world!\n']
-
- server = wsgiserver.CherryPyWSGIServer(
- ('0.0.0.0', 8070), my_crazy_app,
- server_name='www.cherrypy.example')
-
-The CherryPy WSGI server can serve as many WSGI applications
-as you want in one instance by using a WSGIPathInfoDispatcher:
-
- d = WSGIPathInfoDispatcher({'/': my_crazy_app, '/blog': my_blog_app})
- server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
-
-Want SSL support? Just set these attributes:
-
- server.ssl_certificate = <filename>
- server.ssl_private_key = <filename>
-
- if __name__ == '__main__':
- try:
- server.start()
- except KeyboardInterrupt:
- server.stop()
-
-This won't call the CherryPy engine (application side) at all, only the
-WSGI server, which is independant from the rest of CherryPy. Don't
-let the name "CherryPyWSGIServer" throw you; the name merely reflects
-its origin, not its coupling.
-
-For those of you wanting to understand internals of this module, here's the
-basic call flow. The server's listening thread runs a very tight loop,
-sticking incoming connections onto a Queue:
-
- server = CherryPyWSGIServer(...)
- server.start()
- while True:
- tick()
- # This blocks until a request comes in:
- child = socket.accept()
- conn = HTTPConnection(child, ...)
- server.requests.put(conn)
-
-Worker threads are kept in a pool and poll the Queue, popping off and then
-handling each connection in turn. Each connection can consist of an arbitrary
-number of requests and their responses, so we run a nested loop:
-
- while True:
- conn = server.requests.get()
- conn.communicate()
- -> while True:
- req = HTTPRequest(...)
- req.parse_request()
- -> # Read the Request-Line, e.g. "GET /page HTTP/1.1"
- req.rfile.readline()
- req.read_headers()
- req.respond()
- -> response = wsgi_app(...)
- try:
- for chunk in response:
- if chunk:
- req.write(chunk)
- finally:
- if hasattr(response, "close"):
- response.close()
- if req.close_connection:
- return
-"""
-
-
-import base64
-import os
-import Queue
-import re
-quoted_slash = re.compile("(?i)%2F")
-import rfc822
-import socket
-try:
- import cStringIO as StringIO
-except ImportError:
- import StringIO
-
-_fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
-
-import sys
-import threading
-import time
-import traceback
-from urllib import unquote
-from urlparse import urlparse
-import warnings
-
-try:
- from OpenSSL import SSL
- from OpenSSL import crypto
-except ImportError:
- SSL = None
-
-import errno
-
-def plat_specific_errors(*errnames):
- """Return error numbers for all errors in errnames on this platform.
-
- The 'errno' module contains different global constants depending on
- the specific platform (OS). This function will return the list of
- numeric values for a given list of potential names.
- """
- errno_names = dir(errno)
- nums = [getattr(errno, k) for k in errnames if k in errno_names]
- # de-dupe the list
- return dict.fromkeys(nums).keys()
-
-socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
-
-socket_errors_to_ignore = plat_specific_errors(
- "EPIPE",
- "EBADF", "WSAEBADF",
- "ENOTSOCK", "WSAENOTSOCK",
- "ETIMEDOUT", "WSAETIMEDOUT",
- "ECONNREFUSED", "WSAECONNREFUSED",
- "ECONNRESET", "WSAECONNRESET",
- "ECONNABORTED", "WSAECONNABORTED",
- "ENETRESET", "WSAENETRESET",
- "EHOSTDOWN", "EHOSTUNREACH",
- )
-socket_errors_to_ignore.append("timed out")
-
-socket_errors_nonblocking = plat_specific_errors(
- 'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
-
-comma_separated_headers = ['ACCEPT', 'ACCEPT-CHARSET', 'ACCEPT-ENCODING',
- 'ACCEPT-LANGUAGE', 'ACCEPT-RANGES', 'ALLOW', 'CACHE-CONTROL',
- 'CONNECTION', 'CONTENT-ENCODING', 'CONTENT-LANGUAGE', 'EXPECT',
- 'IF-MATCH', 'IF-NONE-MATCH', 'PRAGMA', 'PROXY-AUTHENTICATE', 'TE',
- 'TRAILER', 'TRANSFER-ENCODING', 'UPGRADE', 'VARY', 'VIA', 'WARNING',
- 'WWW-AUTHENTICATE']
-
-
-class WSGIPathInfoDispatcher(object):
- """A WSGI dispatcher for dispatch based on the PATH_INFO.
-
- apps: a dict or list of (path_prefix, app) pairs.
- """
-
- def __init__(self, apps):
- try:
- apps = apps.items()
- except AttributeError:
- pass
-
- # Sort the apps by len(path), descending
- apps.sort()
- apps.reverse()
-
- # The path_prefix strings must start, but not end, with a slash.
- # Use "" instead of "/".
- self.apps = [(p.rstrip("/"), a) for p, a in apps]
-
- def __call__(self, environ, start_response):
- path = environ["PATH_INFO"] or "/"
- for p, app in self.apps:
- # The apps list should be sorted by length, descending.
- if path.startswith(p + "/") or path == p:
- environ = environ.copy()
- environ["SCRIPT_NAME"] = environ["SCRIPT_NAME"] + p
- environ["PATH_INFO"] = path[len(p):]
- return app(environ, start_response)
-
- start_response('404 Not Found', [('Content-Type', 'text/plain'),
- ('Content-Length', '0')])
- return ['']
-
-
-class MaxSizeExceeded(Exception):
- pass
-
-class SizeCheckWrapper(object):
- """Wraps a file-like object, raising MaxSizeExceeded if too large."""
-
- def __init__(self, rfile, maxlen):
- self.rfile = rfile
- self.maxlen = maxlen
- self.bytes_read = 0
-
- def _check_length(self):
- if self.maxlen and self.bytes_read > self.maxlen:
- raise MaxSizeExceeded()
-
- def read(self, size=None):
- data = self.rfile.read(size)
- self.bytes_read += len(data)
- self._check_length()
- return data
-
- def readline(self, size=None):
- if size is not None:
- data = self.rfile.readline(size)
- self.bytes_read += len(data)
- self._check_length()
- return data
-
- # User didn't specify a size ...
- # We read the line in chunks to make sure it's not a 100MB line !
- res = []
- while True:
- data = self.rfile.readline(256)
- self.bytes_read += len(data)
- self._check_length()
- res.append(data)
- # See http://www.cherrypy.org/ticket/421
- if len(data) < 256 or data[-1:] == "\n":
- return ''.join(res)
-
- def readlines(self, sizehint=0):
- # Shamelessly stolen from StringIO
- total = 0
- lines = []
- line = self.readline()
- while line:
- lines.append(line)
- total += len(line)
- if 0 < sizehint <= total:
- break
- line = self.readline()
- return lines
-
- def close(self):
- self.rfile.close()
-
- def __iter__(self):
- return self
-
- def next(self):
- data = self.rfile.next()
- self.bytes_read += len(data)
- self._check_length()
- return data
-
-
-class HTTPRequest(object):
- """An HTTP Request (and response).
-
- A single HTTP connection may consist of multiple request/response pairs.
-
- send: the 'send' method from the connection's socket object.
- wsgi_app: the WSGI application to call.
- environ: a partial WSGI environ (server and connection entries).
- The caller MUST set the following entries:
- * All wsgi.* entries, including .input
- * SERVER_NAME and SERVER_PORT
- * Any SSL_* entries
- * Any custom entries like REMOTE_ADDR and REMOTE_PORT
- * SERVER_SOFTWARE: the value to write in the "Server" response header.
- * ACTUAL_SERVER_PROTOCOL: the value to write in the Status-Line of
- the response. From RFC 2145: "An HTTP server SHOULD send a
- response version equal to the highest version for which the
- server is at least conditionally compliant, and whose major
- version is less than or equal to the one received in the
- request. An HTTP server MUST NOT send a version for which
- it is not at least conditionally compliant."
-
- outheaders: a list of header tuples to write in the response.
- ready: when True, the request has been parsed and is ready to begin
- generating the response. When False, signals the calling Connection
- that the response should not be generated and the connection should
- close.
- close_connection: signals the calling Connection that the request
- should close. This does not imply an error! The client and/or
- server may each request that the connection be closed.
- chunked_write: if True, output will be encoded with the "chunked"
- transfer-coding. This value is set automatically inside
- send_headers.
- """
-
- max_request_header_size = 0
- max_request_body_size = 0
-
- def __init__(self, wfile, environ, wsgi_app):
- self.rfile = environ['wsgi.input']
- self.wfile = wfile
- self.environ = environ.copy()
- self.wsgi_app = wsgi_app
-
- self.ready = False
- self.started_response = False
- self.status = ""
- self.outheaders = []
- self.sent_headers = False
- self.close_connection = False
- self.chunked_write = False
-
- def parse_request(self):
- """Parse the next HTTP request start-line and message-headers."""
- self.rfile.maxlen = self.max_request_header_size
- self.rfile.bytes_read = 0
-
- try:
- self._parse_request()
- except MaxSizeExceeded:
- self.simple_response("413 Request Entity Too Large")
- return
-
- def _parse_request(self):
- # HTTP/1.1 connections are persistent by default. If a client
- # requests a page, then idles (leaves the connection open),
- # then rfile.readline() will raise socket.error("timed out").
- # Note that it does this based on the value given to settimeout(),
- # and doesn't need the client to request or acknowledge the close
- # (although your TCP stack might suffer for it: cf Apache's history
- # with FIN_WAIT_2).
- request_line = self.rfile.readline()
- if not request_line:
- # Force self.ready = False so the connection will close.
- self.ready = False
- return
-
- if request_line == "\r\n":
- # RFC 2616 sec 4.1: "...if the server is reading the protocol
- # stream at the beginning of a message and receives a CRLF
- # first, it should ignore the CRLF."
- # But only ignore one leading line! else we enable a DoS.
- request_line = self.rfile.readline()
- if not request_line:
- self.ready = False
- return
-
- environ = self.environ
-
- try:
- method, path, req_protocol = request_line.strip().split(" ", 2)
- except ValueError:
- self.simple_response(400, "Malformed Request-Line")
- return
-
- environ["REQUEST_METHOD"] = method
-
- # path may be an abs_path (including "http://host.domain.tld");
- scheme, location, path, params, qs, frag = urlparse(path)
-
- if frag:
- self.simple_response("400 Bad Request",
- "Illegal #fragment in Request-URI.")
- return
-
- if scheme:
- environ["wsgi.url_scheme"] = scheme
- if params:
- path = path + ";" + params
-
- environ["SCRIPT_NAME"] = ""
-
- # Unquote the path+params (e.g. "/this%20path" -> "this path").
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
- #
- # But note that "...a URI must be separated into its components
- # before the escaped characters within those components can be
- # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
- atoms = [unquote(x) for x in quoted_slash.split(path)]
- path = "%2F".join(atoms)
- environ["PATH_INFO"] = path
-
- # Note that, like wsgiref and most other WSGI servers,
- # we unquote the path but not the query string.
- environ["QUERY_STRING"] = qs
-
- # Compare request and server HTTP protocol versions, in case our
- # server does not support the requested protocol. Limit our output
- # to min(req, server). We want the following output:
- # request server actual written supported response
- # protocol protocol response protocol feature set
- # a 1.0 1.0 1.0 1.0
- # b 1.0 1.1 1.1 1.0
- # c 1.1 1.0 1.0 1.0
- # d 1.1 1.1 1.1 1.1
- # Notice that, in (b), the response will be "HTTP/1.1" even though
- # the client only understands 1.0. RFC 2616 10.5.6 says we should
- # only return 505 if the _major_ version is different.
- rp = int(req_protocol[5]), int(req_protocol[7])
- server_protocol = environ["ACTUAL_SERVER_PROTOCOL"]
- sp = int(server_protocol[5]), int(server_protocol[7])
- if sp[0] != rp[0]:
- self.simple_response("505 HTTP Version Not Supported")
- return
- # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol.
- environ["SERVER_PROTOCOL"] = req_protocol
- self.response_protocol = "HTTP/%s.%s" % min(rp, sp)
-
- # If the Request-URI was an absoluteURI, use its location atom.
- if location:
- environ["SERVER_NAME"] = location
-
- # then all the http headers
- try:
- self.read_headers()
- except ValueError, ex:
- self.simple_response("400 Bad Request", repr(ex.args))
- return
-
- mrbs = self.max_request_body_size
- if mrbs and int(environ.get("CONTENT_LENGTH", 0)) > mrbs:
- self.simple_response("413 Request Entity Too Large")
- return
-
- # Persistent connection support
- if self.response_protocol == "HTTP/1.1":
- # Both server and client are HTTP/1.1
- if environ.get("HTTP_CONNECTION", "") == "close":
- self.close_connection = True
- else:
- # Either the server or client (or both) are HTTP/1.0
- if environ.get("HTTP_CONNECTION", "") != "Keep-Alive":
- self.close_connection = True
-
- # Transfer-Encoding support
- te = None
- if self.response_protocol == "HTTP/1.1":
- te = environ.get("HTTP_TRANSFER_ENCODING")
- if te:
- te = [x.strip().lower() for x in te.split(",") if x.strip()]
-
- self.chunked_read = False
-
- if te:
- for enc in te:
- if enc == "chunked":
- self.chunked_read = True
- else:
- # Note that, even if we see "chunked", we must reject
- # if there is an extension we don't recognize.
- self.simple_response("501 Unimplemented")
- self.close_connection = True
- return
-
- # From PEP 333:
- # "Servers and gateways that implement HTTP 1.1 must provide
- # transparent support for HTTP 1.1's "expect/continue" mechanism.
- # This may be done in any of several ways:
- # 1. Respond to requests containing an Expect: 100-continue request
- # with an immediate "100 Continue" response, and proceed normally.
- # 2. Proceed with the request normally, but provide the application
- # with a wsgi.input stream that will send the "100 Continue"
- # response if/when the application first attempts to read from
- # the input stream. The read request must then remain blocked
- # until the client responds.
- # 3. Wait until the client decides that the server does not support
- # expect/continue, and sends the request body on its own.
- # (This is suboptimal, and is not recommended.)
- #
- # We used to do 3, but are now doing 1. Maybe we'll do 2 someday,
- # but it seems like it would be a big slowdown for such a rare case.
- if environ.get("HTTP_EXPECT", "") == "100-continue":
- self.simple_response(100)
-
- self.ready = True
-
- def read_headers(self):
- """Read header lines from the incoming stream."""
- environ = self.environ
-
- while True:
- line = self.rfile.readline()
- if not line:
- # No more data--illegal end of headers
- raise ValueError("Illegal end of headers.")
-
- if line == '\r\n':
- # Normal end of headers
- break
-
- if line[0] in ' \t':
- # It's a continuation line.
- v = line.strip()
- else:
- k, v = line.split(":", 1)
- k, v = k.strip().upper(), v.strip()
- envname = "HTTP_" + k.replace("-", "_")
-
- if k in comma_separated_headers:
- existing = environ.get(envname)
- if existing:
- v = ", ".join((existing, v))
- environ[envname] = v
-
- ct = environ.pop("HTTP_CONTENT_TYPE", None)
- if ct is not None:
- environ["CONTENT_TYPE"] = ct
- cl = environ.pop("HTTP_CONTENT_LENGTH", None)
- if cl is not None:
- environ["CONTENT_LENGTH"] = cl
-
- def decode_chunked(self):
- """Decode the 'chunked' transfer coding."""
- cl = 0
- data = StringIO.StringIO()
- while True:
- line = self.rfile.readline().strip().split(";", 1)
- chunk_size = int(line.pop(0), 16)
- if chunk_size <= 0:
- break
-## if line: chunk_extension = line[0]
- cl += chunk_size
- data.write(self.rfile.read(chunk_size))
- crlf = self.rfile.read(2)
- if crlf != "\r\n":
- self.simple_response("400 Bad Request",
- "Bad chunked transfer coding "
- "(expected '\\r\\n', got %r)" % crlf)
- return
-
- # Grab any trailer headers
- self.read_headers()
-
- data.seek(0)
- self.environ["wsgi.input"] = data
- self.environ["CONTENT_LENGTH"] = str(cl) or ""
- return True
-
- def respond(self):
- """Call the appropriate WSGI app and write its iterable output."""
- # Set rfile.maxlen to ensure we don't read past Content-Length.
- # This will also be used to read the entire request body if errors
- # are raised before the app can read the body.
- if self.chunked_read:
- # If chunked, Content-Length will be 0.
- self.rfile.maxlen = self.max_request_body_size
- else:
- cl = int(self.environ.get("CONTENT_LENGTH", 0))
- if self.max_request_body_size:
- self.rfile.maxlen = min(cl, self.max_request_body_size)
- else:
- self.rfile.maxlen = cl
- self.rfile.bytes_read = 0
-
- try:
- self._respond()
- except MaxSizeExceeded:
- if not self.sent_headers:
- self.simple_response("413 Request Entity Too Large")
- return
-
- def _respond(self):
- if self.chunked_read:
- if not self.decode_chunked():
- self.close_connection = True
- return
-
- response = self.wsgi_app(self.environ, self.start_response)
- try:
- for chunk in response:
- # "The start_response callable must not actually transmit
- # the response headers. Instead, it must store them for the
- # server or gateway to transmit only after the first
- # iteration of the application return value that yields
- # a NON-EMPTY string, or upon the application's first
- # invocation of the write() callable." (PEP 333)
- if chunk:
- self.write(chunk)
- finally:
- if hasattr(response, "close"):
- response.close()
-
- if (self.ready and not self.sent_headers):
- self.sent_headers = True
- self.send_headers()
- if self.chunked_write:
- self.wfile.sendall("0\r\n\r\n")
-
- def simple_response(self, status, msg=""):
- """Write a simple response back to the client."""
- status = str(status)
- buf = ["%s %s\r\n" % (self.environ['ACTUAL_SERVER_PROTOCOL'], status),
- "Content-Length: %s\r\n" % len(msg),
- "Content-Type: text/plain\r\n"]
-
- if status[:3] == "413" and self.response_protocol == 'HTTP/1.1':
- # Request Entity Too Large
- self.close_connection = True
- buf.append("Connection: close\r\n")
-
- buf.append("\r\n")
- if msg:
- buf.append(msg)
-
- try:
- self.wfile.sendall("".join(buf))
- except socket.error, x:
- if x.args[0] not in socket_errors_to_ignore:
- raise
-
- def start_response(self, status, headers, exc_info = None):
- """WSGI callable to begin the HTTP response."""
- # "The application may call start_response more than once,
- # if and only if the exc_info argument is provided."
- if self.started_response and not exc_info:
- raise AssertionError("WSGI start_response called a second "
- "time with no exc_info.")
-
- # "if exc_info is provided, and the HTTP headers have already been
- # sent, start_response must raise an error, and should raise the
- # exc_info tuple."
- if self.sent_headers:
- try:
- raise exc_info[0], exc_info[1], exc_info[2]
- finally:
- exc_info = None
-
- self.started_response = True
- self.status = status
- self.outheaders.extend(headers)
- return self.write
-
- def write(self, chunk):
- """WSGI callable to write unbuffered data to the client.
-
- This method is also used internally by start_response (to write
- data from the iterable returned by the WSGI application).
- """
- if not self.started_response:
- raise AssertionError("WSGI write called before start_response.")
-
- if not self.sent_headers:
- self.sent_headers = True
- self.send_headers()
-
- if self.chunked_write and chunk:
- buf = [hex(len(chunk))[2:], "\r\n", chunk, "\r\n"]
- self.wfile.sendall("".join(buf))
- else:
- self.wfile.sendall(chunk)
-
- def send_headers(self):
- """Assert, process, and send the HTTP response message-headers."""
- hkeys = [key.lower() for key, value in self.outheaders]
- status = int(self.status[:3])
-
- if status == 413:
- # Request Entity Too Large. Close conn to avoid garbage.
- self.close_connection = True
- elif "content-length" not in hkeys:
- # "All 1xx (informational), 204 (no content),
- # and 304 (not modified) responses MUST NOT
- # include a message-body." So no point chunking.
- if status < 200 or status in (204, 205, 304):
- pass
- else:
- if (self.response_protocol == 'HTTP/1.1'
- and self.environ["REQUEST_METHOD"] != 'HEAD'):
- # Use the chunked transfer-coding
- self.chunked_write = True
- self.outheaders.append(("Transfer-Encoding", "chunked"))
- else:
- # Closing the conn is the only way to determine len.
- self.close_connection = True
-
- if "connection" not in hkeys:
- if self.response_protocol == 'HTTP/1.1':
- # Both server and client are HTTP/1.1 or better
- if self.close_connection:
- self.outheaders.append(("Connection", "close"))
- else:
- # Server and/or client are HTTP/1.0
- if not self.close_connection:
- self.outheaders.append(("Connection", "Keep-Alive"))
-
- if (not self.close_connection) and (not self.chunked_read):
- # Read any remaining request body data on the socket.
- # "If an origin server receives a request that does not include an
- # Expect request-header field with the "100-continue" expectation,
- # the request includes a request body, and the server responds
- # with a final status code before reading the entire request body
- # from the transport connection, then the server SHOULD NOT close
- # the transport connection until it has read the entire request,
- # or until the client closes the connection. Otherwise, the client
- # might not reliably receive the response message. However, this
- # requirement is not be construed as preventing a server from
- # defending itself against denial-of-service attacks, or from
- # badly broken client implementations."
- size = self.rfile.maxlen - self.rfile.bytes_read
- if size > 0:
- self.rfile.read(size)
-
- if "date" not in hkeys:
- self.outheaders.append(("Date", rfc822.formatdate()))
-
- if "server" not in hkeys:
- self.outheaders.append(("Server", self.environ['SERVER_SOFTWARE']))
-
- buf = [self.environ['ACTUAL_SERVER_PROTOCOL'], " ", self.status, "\r\n"]
- try:
- buf += [k + ": " + v + "\r\n" for k, v in self.outheaders]
- except TypeError:
- if not isinstance(k, str):
- raise TypeError("WSGI response header key %r is not a string.")
- if not isinstance(v, str):
- raise TypeError("WSGI response header value %r is not a string.")
- else:
- raise
- buf.append("\r\n")
- self.wfile.sendall("".join(buf))
-
-
-class NoSSLError(Exception):
- """Exception raised when a client speaks HTTP to an HTTPS socket."""
- pass
-
-
-class FatalSSLAlert(Exception):
- """Exception raised when the SSL implementation signals a fatal alert."""
- pass
-
-
-if not _fileobject_uses_str_type:
- class CP_fileobject(socket._fileobject):
- """Faux file object attached to a socket object."""
-
- def sendall(self, data):
- """Sendall for non-blocking sockets."""
- while data:
- try:
- bytes_sent = self.send(data)
- data = data[bytes_sent:]
- except socket.error, e:
- if e.args[0] not in socket_errors_nonblocking:
- raise
-
- def send(self, data):
- return self._sock.send(data)
-
- def flush(self):
- if self._wbuf:
- buffer = "".join(self._wbuf)
- self._wbuf = []
- self.sendall(buffer)
-
- def recv(self, size):
- while True:
- try:
- return self._sock.recv(size)
- except socket.error, e:
- if (e.args[0] not in socket_errors_nonblocking
- and e.args[0] not in socket_error_eintr):
- raise
-
- def read(self, size=-1):
- # Use max, disallow tiny reads in a loop as they are very inefficient.
- # We never leave read() with any leftover data from a new recv() call
- # in our internal buffer.
- rbufsize = max(self._rbufsize, self.default_bufsize)
- # Our use of StringIO rather than lists of string objects returned by
- # recv() minimizes memory usage and fragmentation that occurs when
- # rbufsize is large compared to the typical return value of recv().
- buf = self._rbuf
- buf.seek(0, 2) # seek end
- if size < 0:
- # Read until EOF
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
- while True:
- data = self.recv(rbufsize)
- if not data:
- break
- buf.write(data)
- return buf.getvalue()
- else:
- # Read until size bytes or EOF seen, whichever comes first
- buf_len = buf.tell()
- if buf_len >= size:
- # Already have size bytes in our buffer? Extract and return.
- buf.seek(0)
- rv = buf.read(size)
- self._rbuf = StringIO.StringIO()
- self._rbuf.write(buf.read())
- return rv
-
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
- while True:
- left = size - buf_len
- # recv() will malloc the amount of memory given as its
- # parameter even though it often returns much less data
- # than that. The returned data string is short lived
- # as we copy it into a StringIO and free it. This avoids
- # fragmentation issues on many platforms.
- data = self.recv(left)
- if not data:
- break
- n = len(data)
- if n == size and not buf_len:
- # Shortcut. Avoid buffer data copies when:
- # - We have no data in our buffer.
- # AND
- # - Our call to recv returned exactly the
- # number of bytes we were asked to read.
- return data
- if n == left:
- buf.write(data)
- del data # explicit free
- break
- assert n <= left, "recv(%d) returned %d bytes" % (left, n)
- buf.write(data)
- buf_len += n
- del data # explicit free
- #assert buf_len == buf.tell()
- return buf.getvalue()
-
- def readline(self, size=-1):
- buf = self._rbuf
- buf.seek(0, 2) # seek end
- if buf.tell() > 0:
- # check if we already have it in our buffer
- buf.seek(0)
- bline = buf.readline(size)
- if bline.endswith('\n') or len(bline) == size:
- self._rbuf = StringIO.StringIO()
- self._rbuf.write(buf.read())
- return bline
- del bline
- if size < 0:
- # Read until \n or EOF, whichever comes first
- if self._rbufsize <= 1:
- # Speed up unbuffered case
- buf.seek(0)
- buffers = [buf.read()]
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
- data = None
- recv = self.recv
- while data != "\n":
- data = recv(1)
- if not data:
- break
- buffers.append(data)
- return "".join(buffers)
-
- buf.seek(0, 2) # seek end
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
- while True:
- data = self.recv(self._rbufsize)
- if not data:
- break
- nl = data.find('\n')
- if nl >= 0:
- nl += 1
- buf.write(data[:nl])
- self._rbuf.write(data[nl:])
- del data
- break
- buf.write(data)
- return buf.getvalue()
- else:
- # Read until size bytes or \n or EOF seen, whichever comes first
- buf.seek(0, 2) # seek end
- buf_len = buf.tell()
- if buf_len >= size:
- buf.seek(0)
- rv = buf.read(size)
- self._rbuf = StringIO.StringIO()
- self._rbuf.write(buf.read())
- return rv
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
- while True:
- data = self.recv(self._rbufsize)
- if not data:
- break
- left = size - buf_len
- # did we just receive a newline?
- nl = data.find('\n', 0, left)
- if nl >= 0:
- nl += 1
- # save the excess data to _rbuf
- self._rbuf.write(data[nl:])
- if buf_len:
- buf.write(data[:nl])
- break
- else:
- # Shortcut. Avoid data copy through buf when returning
- # a substring of our first recv().
- return data[:nl]
- n = len(data)
- if n == size and not buf_len:
- # Shortcut. Avoid data copy through buf when
- # returning exactly all of our first recv().
- return data
- if n >= left:
- buf.write(data[:left])
- self._rbuf.write(data[left:])
- break
- buf.write(data)
- buf_len += n
- #assert buf_len == buf.tell()
- return buf.getvalue()
-
-else:
- class CP_fileobject(socket._fileobject):
- """Faux file object attached to a socket object."""
-
- def sendall(self, data):
- """Sendall for non-blocking sockets."""
- while data:
- try:
- bytes_sent = self.send(data)
- data = data[bytes_sent:]
- except socket.error, e:
- if e.args[0] not in socket_errors_nonblocking:
- raise
-
- def send(self, data):
- return self._sock.send(data)
-
- def flush(self):
- if self._wbuf:
- buffer = "".join(self._wbuf)
- self._wbuf = []
- self.sendall(buffer)
-
- def recv(self, size):
- while True:
- try:
- return self._sock.recv(size)
- except socket.error, e:
- if (e.args[0] not in socket_errors_nonblocking
- and e.args[0] not in socket_error_eintr):
- raise
-
- def read(self, size=-1):
- if size < 0:
- # Read until EOF
- buffers = [self._rbuf]
- self._rbuf = ""
- if self._rbufsize <= 1:
- recv_size = self.default_bufsize
- else:
- recv_size = self._rbufsize
-
- while True:
- data = self.recv(recv_size)
- if not data:
- break
- buffers.append(data)
- return "".join(buffers)
- else:
- # Read until size bytes or EOF seen, whichever comes first
- data = self._rbuf
- buf_len = len(data)
- if buf_len >= size:
- self._rbuf = data[size:]
- return data[:size]
- buffers = []
- if data:
- buffers.append(data)
- self._rbuf = ""
- while True:
- left = size - buf_len
- recv_size = max(self._rbufsize, left)
- data = self.recv(recv_size)
- if not data:
- break
- buffers.append(data)
- n = len(data)
- if n >= left:
- self._rbuf = data[left:]
- buffers[-1] = data[:left]
- break
- buf_len += n
- return "".join(buffers)
-
- def readline(self, size=-1):
- data = self._rbuf
- if size < 0:
- # Read until \n or EOF, whichever comes first
- if self._rbufsize <= 1:
- # Speed up unbuffered case
- assert data == ""
- buffers = []
- while data != "\n":
- data = self.recv(1)
- if not data:
- break
- buffers.append(data)
- return "".join(buffers)
- nl = data.find('\n')
- if nl >= 0:
- nl += 1
- self._rbuf = data[nl:]
- return data[:nl]
- buffers = []
- if data:
- buffers.append(data)
- self._rbuf = ""
- while True:
- data = self.recv(self._rbufsize)
- if not data:
- break
- buffers.append(data)
- nl = data.find('\n')
- if nl >= 0:
- nl += 1
- self._rbuf = data[nl:]
- buffers[-1] = data[:nl]
- break
- return "".join(buffers)
- else:
- # Read until size bytes or \n or EOF seen, whichever comes first
- nl = data.find('\n', 0, size)
- if nl >= 0:
- nl += 1
- self._rbuf = data[nl:]
- return data[:nl]
- buf_len = len(data)
- if buf_len >= size:
- self._rbuf = data[size:]
- return data[:size]
- buffers = []
- if data:
- buffers.append(data)
- self._rbuf = ""
- while True:
- data = self.recv(self._rbufsize)
- if not data:
- break
- buffers.append(data)
- left = size - buf_len
- nl = data.find('\n', 0, left)
- if nl >= 0:
- nl += 1
- self._rbuf = data[nl:]
- buffers[-1] = data[:nl]
- break
- n = len(data)
- if n >= left:
- self._rbuf = data[left:]
- buffers[-1] = data[:left]
- break
- buf_len += n
- return "".join(buffers)
-
-
-class SSL_fileobject(CP_fileobject):
- """SSL file object attached to a socket object."""
-
- ssl_timeout = 3
- ssl_retry = .01
-
- def _safe_call(self, is_reader, call, *args, **kwargs):
- """Wrap the given call with SSL error-trapping.
-
- is_reader: if False EOF errors will be raised. If True, EOF errors
- will return "" (to emulate normal sockets).
- """
- start = time.time()
- while True:
- try:
- return call(*args, **kwargs)
- except SSL.WantReadError:
- # Sleep and try again. This is dangerous, because it means
- # the rest of the stack has no way of differentiating
- # between a "new handshake" error and "client dropped".
- # Note this isn't an endless loop: there's a timeout below.
- time.sleep(self.ssl_retry)
- except SSL.WantWriteError:
- time.sleep(self.ssl_retry)
- except SSL.SysCallError, e:
- if is_reader and e.args == (-1, 'Unexpected EOF'):
- return ""
-
- errnum = e.args[0]
- if is_reader and errnum in socket_errors_to_ignore:
- return ""
- raise socket.error(errnum)
- except SSL.Error, e:
- if is_reader and e.args == (-1, 'Unexpected EOF'):
- return ""
-
- thirdarg = None
- try:
- thirdarg = e.args[0][0][2]
- except IndexError:
- pass
-
- if thirdarg == 'http request':
- # The client is talking HTTP to an HTTPS server.
- raise NoSSLError()
- raise FatalSSLAlert(*e.args)
- except:
- raise
-
- if time.time() - start > self.ssl_timeout:
- raise socket.timeout("timed out")
-
- def recv(self, *args, **kwargs):
- buf = []
- r = super(SSL_fileobject, self).recv
- while True:
- data = self._safe_call(True, r, *args, **kwargs)
- buf.append(data)
- p = self._sock.pending()
- if not p:
- return "".join(buf)
-
- def sendall(self, *args, **kwargs):
- return self._safe_call(False, super(SSL_fileobject, self).sendall, *args, **kwargs)
-
- def send(self, *args, **kwargs):
- return self._safe_call(False, super(SSL_fileobject, self).send, *args, **kwargs)
-
-
-class HTTPConnection(object):
- """An HTTP connection (active socket).
-
- socket: the raw socket object (usually TCP) for this connection.
- wsgi_app: the WSGI application for this server/connection.
- environ: a WSGI environ template. This will be copied for each request.
-
- rfile: a fileobject for reading from the socket.
- send: a function for writing (+ flush) to the socket.
- """
-
- rbufsize = -1
- RequestHandlerClass = HTTPRequest
- environ = {"wsgi.version": (1, 0),
- "wsgi.url_scheme": "http",
- "wsgi.multithread": True,
- "wsgi.multiprocess": False,
- "wsgi.run_once": False,
- "wsgi.errors": sys.stderr,
- }
-
- def __init__(self, sock, wsgi_app, environ):
- self.socket = sock
- self.wsgi_app = wsgi_app
-
- # Copy the class environ into self.
- self.environ = self.environ.copy()
- self.environ.update(environ)
-
- if SSL and isinstance(sock, SSL.ConnectionType):
- timeout = sock.gettimeout()
- self.rfile = SSL_fileobject(sock, "rb", self.rbufsize)
- self.rfile.ssl_timeout = timeout
- self.wfile = SSL_fileobject(sock, "wb", -1)
- self.wfile.ssl_timeout = timeout
- else:
- self.rfile = CP_fileobject(sock, "rb", self.rbufsize)
- self.wfile = CP_fileobject(sock, "wb", -1)
-
- # Wrap wsgi.input but not HTTPConnection.rfile itself.
- # We're also not setting maxlen yet; we'll do that separately
- # for headers and body for each iteration of self.communicate
- # (if maxlen is 0 the wrapper doesn't check length).
- self.environ["wsgi.input"] = SizeCheckWrapper(self.rfile, 0)
-
- def communicate(self):
- """Read each request and respond appropriately."""
- try:
- while True:
- # (re)set req to None so that if something goes wrong in
- # the RequestHandlerClass constructor, the error doesn't
- # get written to the previous request.
- req = None
- req = self.RequestHandlerClass(self.wfile, self.environ,
- self.wsgi_app)
-
- # This order of operations should guarantee correct pipelining.
- req.parse_request()
- if not req.ready:
- return
-
- req.respond()
- if req.close_connection:
- return
-
- except socket.error, e:
- errnum = e.args[0]
- if errnum == 'timed out':
- if req and not req.sent_headers:
- req.simple_response("408 Request Timeout")
- elif errnum not in socket_errors_to_ignore:
- if req and not req.sent_headers:
- req.simple_response("500 Internal Server Error",
- format_exc())
- return
- except (KeyboardInterrupt, SystemExit):
- raise
- except FatalSSLAlert, e:
- # Close the connection.
- return
- except NoSSLError:
- if req and not req.sent_headers:
- # Unwrap our wfile
- req.wfile = CP_fileobject(self.socket._sock, "wb", -1)
- req.simple_response("400 Bad Request",
- "The client sent a plain HTTP request, but "
- "this server only speaks HTTPS on this port.")
- self.linger = True
- except Exception, e:
- if req and not req.sent_headers:
- req.simple_response("500 Internal Server Error", format_exc())
-
- linger = False
-
- def close(self):
- """Close the socket underlying this connection."""
- self.rfile.close()
-
- if not self.linger:
- # Python's socket module does NOT call close on the kernel socket
- # when you call socket.close(). We do so manually here because we
- # want this server to send a FIN TCP segment immediately. Note this
- # must be called *before* calling socket.close(), because the latter
- # drops its reference to the kernel socket.
- self.socket._sock.close()
- self.socket.close()
- else:
- # On the other hand, sometimes we want to hang around for a bit
- # to make sure the client has a chance to read our entire
- # response. Skipping the close() calls here delays the FIN
- # packet until the socket object is garbage-collected later.
- # Someday, perhaps, we'll do the full lingering_close that
- # Apache does, but not today.
- pass
-
-
-def format_exc(limit=None):
- """Like print_exc() but return a string. Backport for Python 2.3."""
- try:
- etype, value, tb = sys.exc_info()
- return ''.join(traceback.format_exception(etype, value, tb, limit))
- finally:
- etype = value = tb = None
-
-
-_SHUTDOWNREQUEST = None
-
-class WorkerThread(threading.Thread):
- """Thread which continuously polls a Queue for Connection objects.
-
- server: the HTTP Server which spawned this thread, and which owns the
- Queue and is placing active connections into it.
- ready: a simple flag for the calling server to know when this thread
- has begun polling the Queue.
-
- Due to the timing issues of polling a Queue, a WorkerThread does not
- check its own 'ready' flag after it has started. To stop the thread,
- it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue
- (one for each running WorkerThread).
- """
-
- conn = None
-
- def __init__(self, server):
- self.ready = False
- self.server = server
- threading.Thread.__init__(self)
-
- def run(self):
- try:
- self.ready = True
- while True:
- conn = self.server.requests.get()
- if conn is _SHUTDOWNREQUEST:
- return
-
- self.conn = conn
- try:
- conn.communicate()
- finally:
- conn.close()
- self.conn = None
- except (KeyboardInterrupt, SystemExit), exc:
- self.server.interrupt = exc
-
-
-class ThreadPool(object):
- """A Request Queue for the CherryPyWSGIServer which pools threads.
-
- ThreadPool objects must provide min, get(), put(obj), start()
- and stop(timeout) attributes.
- """
-
- def __init__(self, server, min=10, max=-1):
- self.server = server
- self.min = min
- self.max = max
- self._threads = []
- self._queue = Queue.Queue()
- self.get = self._queue.get
-
- def start(self):
- """Start the pool of threads."""
- for i in xrange(self.min):
- self._threads.append(WorkerThread(self.server))
- for worker in self._threads:
- worker.setName("CP WSGIServer " + worker.getName())
- worker.start()
- for worker in self._threads:
- while not worker.ready:
- time.sleep(.1)
-
- def _get_idle(self):
- """Number of worker threads which are idle. Read-only."""
- return len([t for t in self._threads if t.conn is None])
- idle = property(_get_idle, doc=_get_idle.__doc__)
-
- def put(self, obj):
- self._queue.put(obj)
- if obj is _SHUTDOWNREQUEST:
- return
-
- def grow(self, amount):
- """Spawn new worker threads (not above self.max)."""
- for i in xrange(amount):
- if self.max > 0 and len(self._threads) >= self.max:
- break
- worker = WorkerThread(self.server)
- worker.setName("CP WSGIServer " + worker.getName())
- self._threads.append(worker)
- worker.start()
-
- def shrink(self, amount):
- """Kill off worker threads (not below self.min)."""
- # Grow/shrink the pool if necessary.
- # Remove any dead threads from our list
- for t in self._threads:
- if not t.isAlive():
- self._threads.remove(t)
- amount -= 1
-
- if amount > 0:
- for i in xrange(min(amount, len(self._threads) - self.min)):
- # Put a number of shutdown requests on the queue equal
- # to 'amount'. Once each of those is processed by a worker,
- # that worker will terminate and be culled from our list
- # in self.put.
- self._queue.put(_SHUTDOWNREQUEST)
-
- def stop(self, timeout=5):
- # Must shut down threads here so the code that calls
- # this method can know when all threads are stopped.
- for worker in self._threads:
- self._queue.put(_SHUTDOWNREQUEST)
-
- # Don't join currentThread (when stop is called inside a request).
- current = threading.currentThread()
- while self._threads:
- worker = self._threads.pop()
- if worker is not current and worker.isAlive():
- try:
- if timeout is None or timeout < 0:
- worker.join()
- else:
- worker.join(timeout)
- if worker.isAlive():
- # We exhausted the timeout.
- # Forcibly shut down the socket.
- c = worker.conn
- if c and not c.rfile.closed:
- if SSL and isinstance(c.socket, SSL.ConnectionType):
- # pyOpenSSL.socket.shutdown takes no args
- c.socket.shutdown()
- else:
- c.socket.shutdown(socket.SHUT_RD)
- worker.join()
- except (AssertionError,
- # Ignore repeated Ctrl-C.
- # See http://www.cherrypy.org/ticket/691.
- KeyboardInterrupt), exc1:
- pass
-
-
-
-class SSLConnection:
- """A thread-safe wrapper for an SSL.Connection.
-
- *args: the arguments to create the wrapped SSL.Connection(*args).
- """
-
- def __init__(self, *args):
- self._ssl_conn = SSL.Connection(*args)
- self._lock = threading.RLock()
-
- for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
- 'renegotiate', 'bind', 'listen', 'connect', 'accept',
- 'setblocking', 'fileno', 'shutdown', 'close', 'get_cipher_list',
- 'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
- 'makefile', 'get_app_data', 'set_app_data', 'state_string',
- 'sock_shutdown', 'get_peer_certificate', 'want_read',
- 'want_write', 'set_connect_state', 'set_accept_state',
- 'connect_ex', 'sendall', 'settimeout'):
- exec """def %s(self, *args):
- self._lock.acquire()
- try:
- return self._ssl_conn.%s(*args)
- finally:
- self._lock.release()
-""" % (f, f)
-
-
-try:
- import fcntl
-except ImportError:
- try:
- from ctypes import windll, WinError
- except ImportError:
- def prevent_socket_inheritance(sock):
- """Dummy function, since neither fcntl nor ctypes are available."""
- pass
- else:
- def prevent_socket_inheritance(sock):
- """Mark the given socket fd as non-inheritable (Windows)."""
- if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0):
- raise WinError()
-else:
- def prevent_socket_inheritance(sock):
- """Mark the given socket fd as non-inheritable (POSIX)."""
- fd = sock.fileno()
- old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
- fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
-
-
-class CherryPyWSGIServer(object):
- """An HTTP server for WSGI.
-
- bind_addr: The interface on which to listen for connections.
- For TCP sockets, a (host, port) tuple. Host values may be any IPv4
- or IPv6 address, or any valid hostname. The string 'localhost' is a
- synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
- The string '0.0.0.0' is a special IPv4 entry meaning "any active
- interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
- IPv6. The empty string or None are not allowed.
-
- For UNIX sockets, supply the filename as a string.
- wsgi_app: the WSGI 'application callable'; multiple WSGI applications
- may be passed as (path_prefix, app) pairs.
- numthreads: the number of worker threads to create (default 10).
- server_name: the string to set for WSGI's SERVER_NAME environ entry.
- Defaults to socket.gethostname().
- max: the maximum number of queued requests (defaults to -1 = no limit).
- request_queue_size: the 'backlog' argument to socket.listen();
- specifies the maximum number of queued connections (default 5).
- timeout: the timeout in seconds for accepted connections (default 10).
-
- nodelay: if True (the default since 3.1), sets the TCP_NODELAY socket
- option.
-
- protocol: the version string to write in the Status-Line of all
- HTTP responses. For example, "HTTP/1.1" (the default). This
- also limits the supported features used in the response.
-
-
- SSL/HTTPS
- ---------
- The OpenSSL module must be importable for SSL functionality.
- You can obtain it from http://pyopenssl.sourceforge.net/
-
- ssl_certificate: the filename of the server SSL certificate.
- ssl_privatekey: the filename of the server's private key file.
-
- If either of these is None (both are None by default), this server
- will not use SSL. If both are given and are valid, they will be read
- on server start and used in the SSL context for the listening socket.
- """
-
- protocol = "HTTP/1.1"
- _bind_addr = "127.0.0.1"
- version = "CherryPy/3.1.2"
- ready = False
- _interrupt = None
-
- nodelay = True
-
- ConnectionClass = HTTPConnection
- environ = {}
-
- # Paths to certificate and private key files
- ssl_certificate = None
- ssl_private_key = None
-
- def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
- max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
- self.requests = ThreadPool(self, min=numthreads or 1, max=max)
-
- if callable(wsgi_app):
- # We've been handed a single wsgi_app, in CP-2.1 style.
- # Assume it's mounted at "".
- self.wsgi_app = wsgi_app
- else:
- # We've been handed a list of (path_prefix, wsgi_app) tuples,
- # so that the server can call different wsgi_apps, and also
- # correctly set SCRIPT_NAME.
- warnings.warn("The ability to pass multiple apps is deprecated "
- "and will be removed in 3.2. You should explicitly "
- "include a WSGIPathInfoDispatcher instead.",
- DeprecationWarning)
- self.wsgi_app = WSGIPathInfoDispatcher(wsgi_app)
-
- self.bind_addr = bind_addr
- if not server_name:
- server_name = socket.gethostname()
- self.server_name = server_name
- self.request_queue_size = request_queue_size
-
- self.timeout = timeout
- self.shutdown_timeout = shutdown_timeout
-
- def _get_numthreads(self):
- return self.requests.min
- def _set_numthreads(self, value):
- self.requests.min = value
- numthreads = property(_get_numthreads, _set_numthreads)
-
- def __str__(self):
- return "%s.%s(%r)" % (self.__module__, self.__class__.__name__,
- self.bind_addr)
-
- def _get_bind_addr(self):
- return self._bind_addr
- def _set_bind_addr(self, value):
- if isinstance(value, tuple) and value[0] in ('', None):
- # Despite the socket module docs, using '' does not
- # allow AI_PASSIVE to work. Passing None instead
- # returns '0.0.0.0' like we want. In other words:
- # host AI_PASSIVE result
- # '' Y 192.168.x.y
- # '' N 192.168.x.y
- # None Y 0.0.0.0
- # None N 127.0.0.1
- # But since you can get the same effect with an explicit
- # '0.0.0.0', we deny both the empty string and None as values.
- raise ValueError("Host values of '' or None are not allowed. "
- "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead "
- "to listen on all active interfaces.")
- self._bind_addr = value
- bind_addr = property(_get_bind_addr, _set_bind_addr,
- doc="""The interface on which to listen for connections.
-
- For TCP sockets, a (host, port) tuple. Host values may be any IPv4
- or IPv6 address, or any valid hostname. The string 'localhost' is a
- synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
- The string '0.0.0.0' is a special IPv4 entry meaning "any active
- interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
- IPv6. The empty string or None are not allowed.
-
- For UNIX sockets, supply the filename as a string.""")
-
- def start(self):
- """Run the server forever."""
- # We don't have to trap KeyboardInterrupt or SystemExit here,
- # because cherrpy.server already does so, calling self.stop() for us.
- # If you're using this server with another framework, you should
- # trap those exceptions in whatever code block calls start().
- self._interrupt = None
-
- # Select the appropriate socket
- if isinstance(self.bind_addr, basestring):
- # AF_UNIX socket
-
- # So we can reuse the socket...
- try: os.unlink(self.bind_addr)
- except: pass
-
- # So everyone can access the socket...
- try: os.chmod(self.bind_addr, 0777)
- except: pass
-
- info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
- else:
- # AF_INET or AF_INET6 socket
- # Get the correct address family for our host (allows IPv6 addresses)
- host, port = self.bind_addr
- try:
- info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
- socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
- except socket.gaierror:
- # Probably a DNS issue. Assume IPv4.
- info = [(socket.AF_INET, socket.SOCK_STREAM, 0, "", self.bind_addr)]
-
- self.socket = None
- msg = "No socket could be created"
- for res in info:
- af, socktype, proto, canonname, sa = res
- try:
- self.bind(af, socktype, proto)
- except socket.error, msg:
- if self.socket:
- self.socket.close()
- self.socket = None
- continue
- break
- if not self.socket:
- raise socket.error, msg
-
- # Timeout so KeyboardInterrupt can be caught on Win32
- self.socket.settimeout(1)
- self.socket.listen(self.request_queue_size)
-
- # Create worker threads
- self.requests.start()
-
- self.ready = True
- while self.ready:
- self.tick()
- if self.interrupt:
- while self.interrupt is True:
- # Wait for self.stop() to complete. See _set_interrupt.
- time.sleep(0.1)
- if self.interrupt:
- raise self.interrupt
-
- def bind(self, family, type, proto=0):
- """Create (or recreate) the actual socket object."""
- self.socket = socket.socket(family, type, proto)
- prevent_socket_inheritance(self.socket)
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- if self.nodelay:
- self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
- if self.ssl_certificate and self.ssl_private_key:
- if SSL is None:
- raise ImportError("You must install pyOpenSSL to use HTTPS.")
-
- # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- ctx.use_privatekey_file(self.ssl_private_key)
- ctx.use_certificate_file(self.ssl_certificate)
- self.socket = SSLConnection(ctx, self.socket)
- self.populate_ssl_environ()
-
- # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
- # activate dual-stack. See http://www.cherrypy.org/ticket/871.
- if (not isinstance(self.bind_addr, basestring)
- and self.bind_addr[0] == '::' and family == socket.AF_INET6):
- try:
- self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
- except (AttributeError, socket.error):
- # Apparently, the socket option is not available in
- # this machine's TCP stack
- pass
-
- self.socket.bind(self.bind_addr)
-
- def tick(self):
- """Accept a new connection and put it on the Queue."""
- try:
- s, addr = self.socket.accept()
- prevent_socket_inheritance(s)
- if not self.ready:
- return
- if hasattr(s, 'settimeout'):
- s.settimeout(self.timeout)
-
- environ = self.environ.copy()
- # SERVER_SOFTWARE is common for IIS. It's also helpful for
- # us to pass a default value for the "Server" response header.
- if environ.get("SERVER_SOFTWARE") is None:
- environ["SERVER_SOFTWARE"] = "%s WSGI Server" % self.version
- # set a non-standard environ entry so the WSGI app can know what
- # the *real* server protocol is (and what features to support).
- # See http://www.faqs.org/rfcs/rfc2145.html.
- environ["ACTUAL_SERVER_PROTOCOL"] = self.protocol
- environ["SERVER_NAME"] = self.server_name
-
- if isinstance(self.bind_addr, basestring):
- # AF_UNIX. This isn't really allowed by WSGI, which doesn't
- # address unix domain sockets. But it's better than nothing.
- environ["SERVER_PORT"] = ""
- else:
- environ["SERVER_PORT"] = str(self.bind_addr[1])
- # optional values
- # Until we do DNS lookups, omit REMOTE_HOST
- environ["REMOTE_ADDR"] = addr[0]
- environ["REMOTE_PORT"] = str(addr[1])
-
- conn = self.ConnectionClass(s, self.wsgi_app, environ)
- self.requests.put(conn)
- except socket.timeout:
- # The only reason for the timeout in start() is so we can
- # notice keyboard interrupts on Win32, which don't interrupt
- # accept() by default
- return
- except socket.error, x:
- if x.args[0] in socket_error_eintr:
- # I *think* this is right. EINTR should occur when a signal
- # is received during the accept() call; all docs say retry
- # the call, and I *think* I'm reading it right that Python
- # will then go ahead and poll for and handle the signal
- # elsewhere. See http://www.cherrypy.org/ticket/707.
- return
- if x.args[0] in socket_errors_nonblocking:
- # Just try again. See http://www.cherrypy.org/ticket/479.
- return
- if x.args[0] in socket_errors_to_ignore:
- # Our socket was closed.
- # See http://www.cherrypy.org/ticket/686.
- return
- raise
-
- def _get_interrupt(self):
- return self._interrupt
- def _set_interrupt(self, interrupt):
- self._interrupt = True
- self.stop()
- self._interrupt = interrupt
- interrupt = property(_get_interrupt, _set_interrupt,
- doc="Set this to an Exception instance to "
- "interrupt the server.")
-
- def stop(self):
- """Gracefully shutdown a server that is serving forever."""
- self.ready = False
-
- sock = getattr(self, "socket", None)
- if sock:
- if not isinstance(self.bind_addr, basestring):
- # Touch our own socket to make accept() return immediately.
- try:
- host, port = sock.getsockname()[:2]
- except socket.error, x:
- if x.args[0] not in socket_errors_to_ignore:
- raise
- else:
- # Note that we're explicitly NOT using AI_PASSIVE,
- # here, because we want an actual IP to touch.
- # localhost won't work if we've bound to a public IP,
- # but it will if we bound to '0.0.0.0' (INADDR_ANY).
- for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
- socket.SOCK_STREAM):
- af, socktype, proto, canonname, sa = res
- s = None
- try:
- s = socket.socket(af, socktype, proto)
- # See http://groups.google.com/group/cherrypy-users/
- # browse_frm/thread/bbfe5eb39c904fe0
- s.settimeout(1.0)
- s.connect((host, port))
- s.close()
- except socket.error:
- if s:
- s.close()
- if hasattr(sock, "close"):
- sock.close()
- self.socket = None
-
- self.requests.stop(self.shutdown_timeout)
-
- def populate_ssl_environ(self):
- """Create WSGI environ entries to be merged into each request."""
- cert = open(self.ssl_certificate, 'rb').read()
- cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
- ssl_environ = {
- "wsgi.url_scheme": "https",
- "HTTPS": "on",
- # pyOpenSSL doesn't provide access to any of these AFAICT
-## 'SSL_PROTOCOL': 'SSLv2',
-## SSL_CIPHER string The cipher specification name
-## SSL_VERSION_INTERFACE string The mod_ssl program version
-## SSL_VERSION_LIBRARY string The OpenSSL program version
- }
-
- # Server certificate attributes
- ssl_environ.update({
- 'SSL_SERVER_M_VERSION': cert.get_version(),
- 'SSL_SERVER_M_SERIAL': cert.get_serial_number(),
-## 'SSL_SERVER_V_START': Validity of server's certificate (start time),
-## 'SSL_SERVER_V_END': Validity of server's certificate (end time),
- })
-
- for prefix, dn in [("I", cert.get_issuer()),
- ("S", cert.get_subject())]:
- # X509Name objects don't seem to have a way to get the
- # complete DN string. Use str() and slice it instead,
- # because str(dn) == "<X509Name object '/C=US/ST=...'>"
- dnstr = str(dn)[18:-2]
-
- wsgikey = 'SSL_SERVER_%s_DN' % prefix
- ssl_environ[wsgikey] = dnstr
-
- # The DN should be of the form: /k1=v1/k2=v2, but we must allow
- # for any value to contain slashes itself (in a URL).
- while dnstr:
- pos = dnstr.rfind("=")
- dnstr, value = dnstr[:pos], dnstr[pos + 1:]
- pos = dnstr.rfind("/")
- dnstr, key = dnstr[:pos], dnstr[pos + 1:]
- if key and value:
- wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key)
- ssl_environ[wsgikey] = value
-
- self.environ.update(ssl_environ)
-
diff --git a/module/plugins/AccountManager.py b/module/manager/Account.py
index fc521d36c..3acf70311 100644
--- a/module/plugins/AccountManager.py
+++ b/module/manager/Account.py
@@ -1,33 +1,19 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+from __future__ import with_statement
from os.path import exists
from shutil import copy
from threading import Lock
-from module.PullEvents import AccountUpdateEvent
-from module.utils import chmod, lock
+from pyload.manager.Event import AccountUpdateEvent
+from pyload.utils import chmod, lock
ACC_VERSION = 1
-class AccountManager():
+
+class AccountManager(object):
"""manages all accounts"""
#----------------------------------------------------------------------
@@ -40,6 +26,7 @@ class AccountManager():
self.initPlugins()
self.saveAccounts() # save to add categories to conf
+
def initPlugins(self):
self.accounts = {} # key = ( plugin )
self.plugins = {}
@@ -50,97 +37,110 @@ class AccountManager():
def getAccountPlugin(self, plugin):
"""get account instance for plugin or None if anonymous"""
- if plugin in self.accounts:
- if plugin not in self.plugins:
- self.plugins[plugin] = self.core.pluginManager.loadClass("accounts", plugin)(self, self.accounts[plugin])
-
- return self.plugins[plugin]
- else:
+ try:
+ if plugin in self.accounts:
+ if plugin not in self.plugins:
+ klass = self.core.pluginManager.loadClass("accounts", plugin)
+ if klass:
+ self.plugins[plugin] = klass(self, self.accounts[plugin])
+ else: #@NOTE: The account class no longer exists (blacklisted plugin). Skipping the account to avoid crash
+ raise
+
+ return self.plugins[plugin]
+ else:
+ raise
+ except Exception:
return None
+
def getAccountPlugins(self):
""" get all account instances"""
-
+
plugins = []
for plugin in self.accounts.keys():
plugins.append(self.getAccountPlugin(plugin))
-
+
return plugins
+
+
#----------------------------------------------------------------------
def loadAccounts(self):
"""loads all accounts available"""
-
- if not exists("accounts.conf"):
- f = open("accounts.conf", "wb")
- f.write("version: " + str(ACC_VERSION))
- f.close()
-
- f = open("accounts.conf", "rb")
- content = f.readlines()
- version = content[0].split(":")[1].strip() if content else ""
- f.close()
-
- if not version or int(version) < ACC_VERSION:
- copy("accounts.conf", "accounts.backup")
- f = open("accounts.conf", "wb")
- f.write("version: " + str(ACC_VERSION))
- f.close()
- self.core.log.warning(_("Account settings deleted, due to new config format."))
+
+ try:
+ with open("accounts.conf", "a+") as f:
+ content = f.readlines()
+ version = content[0].split(":")[1].strip() if content else ""
+
+ if not version or int(version) < ACC_VERSION:
+ copy("accounts.conf", "accounts.backup")
+ f.seek(0)
+ f.write("version: " + str(ACC_VERSION))
+
+ self.core.log.warning(_("Account settings deleted, due to new config format"))
+ return
+
+ except IOError, e:
+ self.core.log.error(str(e))
return
-
-
-
+
plugin = ""
name = ""
-
+
for line in content[1:]:
line = line.strip()
-
+
if not line: continue
if line.startswith("#"): continue
if line.startswith("version"): continue
-
+
if line.endswith(":") and line.count(":") == 1:
plugin = line[:-1]
self.accounts[plugin] = {}
-
+
elif line.startswith("@"):
try:
option = line[1:].split()
- self.accounts[plugin][name]["options"][option[0]] = [] if len(option) < 2 else ([option[1]] if len(option) < 3 else option[1:])
- except:
+ self.accounts[plugin][name]['options'][option[0]] = [] if len(option) < 2 else ([option[1]] if len(option) < 3 else option[1:])
+ except Exception:
pass
-
+
elif ":" in line:
name, sep, pw = line.partition(":")
self.accounts[plugin][name] = {"password": pw, "options": {}, "valid": True}
+
+
#----------------------------------------------------------------------
def saveAccounts(self):
"""save all account information"""
-
- f = open("accounts.conf", "wb")
- f.write("version: " + str(ACC_VERSION) + "\n")
-
- for plugin, accounts in self.accounts.iteritems():
- f.write("\n")
- f.write(plugin+":\n")
-
- for name,data in accounts.iteritems():
- f.write("\n\t%s:%s\n" % (name,data["password"]) )
- if data["options"]:
- for option, values in data["options"].iteritems():
- f.write("\t@%s %s\n" % (option, " ".join(values)))
-
- f.close()
- chmod(f.name, 0600)
-
-
+
+ try:
+ with open("accounts.conf", "wb") as f:
+ f.write("version: " + str(ACC_VERSION) + "\n")
+
+ for plugin, accounts in self.accounts.iteritems():
+ f.write("\n")
+ f.write(plugin + ":\n")
+
+ for name,data in accounts.iteritems():
+ f.write("\n\t%s:%s\n" % (name,data['password']) )
+ if data['options']:
+ for option, values in data['options'].iteritems():
+ f.write("\t@%s %s\n" % (option, " ".join(values)))
+
+ chmod(f.name, 0600)
+
+ except Exception, e:
+ self.core.log.error(str(e))
+
+
#----------------------------------------------------------------------
def initAccountPlugins(self):
"""init names"""
for name in self.core.pluginManager.getAccountPlugins():
self.accounts[name] = {}
-
+
+
@lock
def updateAccount(self, plugin , user, password=None, options={}):
"""add or update account"""
@@ -148,20 +148,22 @@ class AccountManager():
p = self.getAccountPlugin(plugin)
updated = p.updateAccounts(user, password, options)
#since accounts is a ref in plugin self.accounts doesnt need to be updated here
-
+
self.saveAccounts()
if updated: p.scheduleRefresh(user, force=False)
-
+
+
@lock
def removeAccount(self, plugin, user):
"""remove account"""
-
+
if plugin in self.accounts:
p = self.getAccountPlugin(plugin)
p.removeAccount(user)
self.saveAccounts()
+
@lock
def getAccountInfos(self, force=True, refresh=False):
data = {}
@@ -169,17 +171,21 @@ class AccountManager():
if refresh:
self.core.scheduler.addJob(0, self.core.accountManager.getAccountInfos)
force = False
-
+
for p in self.accounts.keys():
if self.accounts[p]:
p = self.getAccountPlugin(p)
- data[p.__name__] = p.getAllAccounts(force)
+ if p:
+ data[p.__name__] = p.getAllAccounts(force)
+ else: #@NOTE: When an account has been skipped, p is None
+ data[p] = []
else:
data[p] = []
e = AccountUpdateEvent()
self.core.pullManager.addEvent(e)
return data
-
+
+
def sendChange(self):
e = AccountUpdateEvent()
self.core.pullManager.addEvent(e)
diff --git a/module/manager/Addon.py b/module/manager/Addon.py
new file mode 100644
index 000000000..d293109c7
--- /dev/null
+++ b/module/manager/Addon.py
@@ -0,0 +1,304 @@
+# -*- coding: utf-8 -*-
+# @author: RaNaN, mkaay
+# @interface-version: 0.1
+
+import __builtin__
+
+import traceback
+from threading import RLock, Thread
+
+from types import MethodType
+
+from pyload.manager.thread.Addon import AddonThread
+from pyload.manager.Plugin import literal_eval
+from pyload.utils import lock
+
+
+class AddonManager(object):
+ """Manages addons, delegates and handles Events.
+
+ Every plugin can define events, \
+ but some very usefull events are called by the Core.
+ Contrary to overwriting addon methods you can use event listener,
+ which provides additional entry point in the control flow.
+ Only do very short tasks or use threads.
+
+ **Known Events:**
+ Most addon methods exists as events. These are the additional known events.
+
+ ======================= ============== ==================================
+ Name Arguments Description
+ ======================= ============== ==================================
+ download-preparing fid A download was just queued and will be prepared now.
+ download-start fid A plugin will immediately starts the download afterwards.
+ links-added links, pid Someone just added links, you are able to modify the links.
+ all_downloads-processed Every link was handled, pyload would idle afterwards.
+ all_downloads-finished Every download in queue is finished.
+ config-changed The config was changed via the api.
+ pluginConfigChanged The plugin config changed, due to api or internal process.
+ ======================= ============== ==================================
+
+ | Notes:
+ | all_downloads-processed is *always* called before all_downloads-finished.
+ | config-changed is *always* called before pluginConfigChanged.
+
+
+ """
+
+ def __init__(self, core):
+ self.core = core
+
+ __builtin__.addonManager = self #: needed to let addons register themself
+
+ self.plugins = []
+ self.pluginMap = {}
+ self.methods = {} #: dict of names and list of methods usable by rpc
+
+ self.events = {} #: contains events
+
+ # registering callback for config event
+ self.core.config.pluginCB = MethodType(self.dispatchEvent, "pluginConfigChanged", basestring) #@TODO: Rename event pluginConfigChanged
+
+ self.addEvent("pluginConfigChanged", self.manageAddon)
+
+ self.lock = RLock()
+ self.createIndex()
+
+
+ def try_catch(func):
+
+ def new(*args):
+ try:
+ return func(*args)
+ except Exception, e:
+ args[0].log.error(_("Error executing addon: %s") % e)
+ if args[0].core.debug:
+ traceback.print_exc()
+
+ return new
+
+
+ def addRPC(self, plugin, func, doc):
+ plugin = plugin.rpartition(".")[2]
+ doc = doc.strip() if doc else ""
+
+ if plugin in self.methods:
+ self.methods[plugin][func] = doc
+ else:
+ self.methods[plugin] = {func: doc}
+
+
+ def callRPC(self, plugin, func, args, parse):
+ if not args:
+ args = tuple()
+ if parse:
+ args = tuple([literal_eval(x) for x in args])
+ plugin = self.pluginMap[plugin]
+ f = getattr(plugin, func)
+ return f(*args)
+
+
+ def createIndex(self):
+ plugins = []
+ active = []
+ deactive = []
+
+ for pluginname in self.core.pluginManager.addonPlugins:
+ try:
+ # hookClass = getattr(plugin, plugin.__name__)
+ if self.core.config.getPlugin(pluginname, "activated"):
+ pluginClass = self.core.pluginManager.loadClass("addon", pluginname)
+ if not pluginClass:
+ continue
+
+ plugin = pluginClass(self.core, self)
+ plugins.append(plugin)
+ self.pluginMap[pluginClass.__name__] = plugin
+ if plugin.isActivated():
+ active.append(pluginClass.__name__)
+ else:
+ deactive.append(pluginname)
+
+ except Exception:
+ self.core.log.warning(_("Failed activating %(name)s") % {"name": pluginname})
+ if self.core.debug:
+ traceback.print_exc()
+
+ self.core.log.info(_("Activated addons: %s") % ", ".join(sorted(active)))
+ self.core.log.info(_("Deactivated addons: %s") % ", ".join(sorted(deactive)))
+
+ self.plugins = plugins
+
+
+ def manageAddon(self, plugin, name, value):
+ if name == "activated" and value:
+ self.activateAddon(plugin)
+
+ elif name == "activated" and not value:
+ self.deactivateAddon(plugin)
+
+
+ def activateAddon(self, pluginname):
+ # check if already loaded
+ for inst in self.plugins:
+ if inst.__name__ == pluginname:
+ return
+
+ pluginClass = self.core.pluginManager.loadClass("addon", pluginname)
+
+ if not pluginClass:
+ return
+
+ self.core.log.debug("Activate addon: %s" % pluginname)
+
+ addon = pluginClass(self.core, self)
+ self.plugins.append(addon)
+ self.pluginMap[pluginClass.__name__] = addon
+
+ addon.activate()
+
+
+ def deactivateAddon(self, pluginname):
+ for plugin in self.plugins:
+ if plugin.__name__ == pluginname:
+ addon = plugin
+ break
+ else:
+ return
+
+ self.core.log.debug("Deactivate addon: %s" % pluginname)
+
+ addon.deactivate()
+
+ #remove periodic call
+ self.core.log.debug("Removed callback: %s" % self.core.scheduler.removeJob(addon.cb))
+
+ self.plugins.remove(addon)
+ del self.pluginMap[addon.__name__]
+
+
+ @try_catch
+ def coreReady(self):
+ for plugin in self.plugins:
+ if plugin.isActivated():
+ plugin.activate()
+
+ self.dispatchEvent("addon-start")
+
+
+ @try_catch
+ def coreExiting(self):
+ for plugin in self.plugins:
+ if plugin.isActivated():
+ plugin.exit()
+
+ self.dispatchEvent("addon-exit")
+
+
+ @lock
+ def downloadPreparing(self, pyfile):
+ for plugin in self.plugins:
+ if plugin.isActivated():
+ plugin.downloadPreparing(pyfile)
+
+ self.dispatchEvent("download-preparing", pyfile)
+
+
+ @lock
+ def downloadFinished(self, pyfile):
+ for plugin in self.plugins:
+ if plugin.isActivated():
+ plugin.downloadFinished(pyfile)
+
+ self.dispatchEvent("download-finished", pyfile)
+
+
+ @lock
+ @try_catch
+ def downloadFailed(self, pyfile):
+ for plugin in self.plugins:
+ if plugin.isActivated():
+ plugin.downloadFailed(pyfile)
+
+ self.dispatchEvent("download-failed", pyfile)
+
+
+ @lock
+ def packageFinished(self, package):
+ for plugin in self.plugins:
+ if plugin.isActivated():
+ plugin.packageFinished(package)
+
+ self.dispatchEvent("package-finished", package)
+
+
+ @lock
+ def beforeReconnecting(self, ip):
+ for plugin in self.plugins:
+ plugin.beforeReconnecting(ip)
+
+ self.dispatchEvent("beforeReconnecting", ip)
+
+
+ @lock
+ def afterReconnecting(self, ip):
+ for plugin in self.plugins:
+ if plugin.isActivated():
+ plugin.afterReconnecting(ip)
+
+ self.dispatchEvent("afterReconnecting", ip)
+
+
+ def startThread(self, function, *args, **kwargs):
+ return AddonThread(self.core.threadManager, function, args, kwargs)
+
+
+ def activePlugins(self):
+ """ returns all active plugins """
+ return [x for x in self.plugins if x.isActivated()]
+
+
+ def getAllInfo(self):
+ """returns info stored by addon plugins"""
+ info = {}
+ for name, plugin in self.pluginMap.iteritems():
+ if plugin.info:
+ # copy and convert so str
+ info[name] = dict(
+ [(x, str(y) if not isinstance(y, basestring) else y) for x, y in plugin.info.iteritems()])
+ return info
+
+
+ def getInfo(self, plugin):
+ info = {}
+ if plugin in self.pluginMap and self.pluginMap[plugin].info:
+ info = dict((x, str(y) if not isinstance(y, basestring) else y)
+ for x, y in self.pluginMap[plugin].info.iteritems())
+ return info
+
+
+ def addEvent(self, event, func):
+ """Adds an event listener for event name"""
+ if event in self.events:
+ self.events[event].append(func)
+ else:
+ self.events[event] = [func]
+
+
+ def removeEvent(self, event, func):
+ """removes previously added event listener"""
+ if event in self.events:
+ self.events[event].remove(func)
+
+
+ def dispatchEvent(self, event, *args):
+ """dispatches event with args"""
+ if event in self.events:
+ for f in self.events[event]:
+ try:
+ f(*args)
+ except Exception, e:
+ self.core.log.warning("Error calling event handler %s: %s, %s, %s"
+ % (event, f, args, str(e)))
+ if self.core.debug:
+ traceback.print_exc()
diff --git a/module/CaptchaManager.py b/module/manager/Captcha.py
index 02cd10a11..e54eacf30 100644
--- a/module/CaptchaManager.py
+++ b/module/manager/Captcha.py
@@ -1,33 +1,19 @@
# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay, RaNaN
-"""
+# @author: RaNaN, mkaay
from time import time
from traceback import print_exc
from threading import Lock
-class CaptchaManager():
+from pyload.utils import encode
+
+
+class CaptchaManager(object):
def __init__(self, core):
self.lock = Lock()
self.core = core
- self.tasks = [] #task store, for outgoing tasks only
-
- self.ids = 0 #only for internal purpose
+ self.tasks = [] # task store, for outgoing tasks only
+ self.ids = 0 # only for internal purpose
def newTask(self, img, format, file, result_type):
task = CaptchaTask(self.ids, img, format, file, result_type)
@@ -52,48 +38,45 @@ class CaptchaManager():
def getTaskByID(self, tid):
self.lock.acquire()
for task in self.tasks:
- if task.id == str(tid): #task ids are strings
+ if task.id == str(tid): # task ids are strings
self.lock.release()
return task
self.lock.release()
return None
- def handleCaptcha(self, task):
+ def handleCaptcha(self, task, timeout=50):
cli = self.core.isClientConnected()
- if cli: #client connected -> should solve the captcha
- task.setWaiting(50) #wait 50 sec for response
+ if cli: #: client connected -> should solve the captcha
+ task.setWaiting(timeout) #wait 50 sec for response
- for plugin in self.core.hookManager.activePlugins():
+ for plugin in self.core.addonManager.activePlugins():
try:
- plugin.newCaptchaTask(task)
- except:
+ plugin.captchaTask(task)
+ except Exception:
if self.core.debug:
print_exc()
-
- if task.handler or cli: #the captcha was handled
+
+ if task.handler or cli: #: the captcha was handled
self.tasks.append(task)
return True
-
task.error = _("No Client connected for captcha decrypting")
-
return False
-class CaptchaTask():
+class CaptchaTask(object):
def __init__(self, id, img, format, file, result_type='textual'):
self.id = str(id)
self.captchaImg = img
self.captchaFormat = format
self.captchaFile = file
self.captchaResultType = result_type
- self.handler = [] #the hook plugins that will take care of the solution
+ self.handler = [] #: the hook plugins that will take care of the solution
self.result = None
self.waitUntil = None
- self.error = None #error message
-
+ self.error = None # error message
self.status = "init"
- self.data = {} #handler can store data here
+ self.data = {} # handler can store data here
def getCaptcha(self):
return self.captchaImg, self.captchaFormat, self.captchaResultType
@@ -105,16 +88,11 @@ class CaptchaTask():
try:
parts = text.split(',')
self.result = (int(parts[0]), int(parts[1]))
- except:
+ except Exception:
self.result = None
def getResult(self):
- try:
- res = self.result.encode("utf8", "replace")
- except:
- res = self.result
-
- return res
+ return encode(self.result)
def getStatus(self):
return self.status
@@ -125,10 +103,10 @@ class CaptchaTask():
self.status = "waiting"
def isWaiting(self):
- if self.result or self.error or time() > self.waitUntil:
+ if self.result or self.error or self.timedOut():
return False
-
- return True
+ else:
+ return True
def isTextual(self):
""" returns if text is written on the captcha """
@@ -149,10 +127,12 @@ class CaptchaTask():
def invalid(self):
""" indicates the captcha was not correct """
- [x.captchaInvalid(self) for x in self.handler]
+ for x in self.handler:
+ x.captchaInvalid(self)
def correct(self):
- [x.captchaCorrect(self) for x in self.handler]
+ for x in self.handler:
+ x.captchaCorrect(self)
def __str__(self):
return "<CaptchaTask '%s'>" % self.id
diff --git a/module/PullEvents.py b/module/manager/Event.py
index 5ec76765e..20897290e 100644
--- a/module/PullEvents.py
+++ b/module/manager/Event.py
@@ -1,38 +1,22 @@
# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
+# @author: mkaay
from time import time
-from module.utils import uniqify
+from pyload.utils import uniqify
-class PullManager():
+class PullManager(object):
def __init__(self, core):
self.core = core
self.clients = []
-
+
def newClient(self, uuid):
self.clients.append(Client(uuid))
-
+
def clean(self):
for n, client in enumerate(self.clients):
if client.lastActive + 30 < time():
del self.clients[n]
-
+
def getEvents(self, uuid):
events = []
validUuid = False
@@ -46,52 +30,52 @@ class PullManager():
if not validUuid:
self.newClient(uuid)
events = [ReloadAllEvent("queue").toList(), ReloadAllEvent("collector").toList()]
- return uniqify(events, repr)
-
+ return uniqify(events)
+
def addEvent(self, event):
for client in self.clients:
client.addEvent(event)
-class Client():
+class Client(object):
def __init__(self, uuid):
self.uuid = uuid
self.lastActive = time()
self.events = []
-
+
def newEvents(self):
return len(self.events) > 0
-
+
def popEvent(self):
if not len(self.events):
return None
return self.events.pop(0)
-
+
def addEvent(self, event):
self.events.append(event)
-class UpdateEvent():
+class UpdateEvent(object):
def __init__(self, itype, iid, destination):
assert itype == "pack" or itype == "file"
assert destination == "queue" or destination == "collector"
self.type = itype
self.id = iid
self.destination = destination
-
+
def toList(self):
return ["update", self.destination, self.type, self.id]
-class RemoveEvent():
+class RemoveEvent(object):
def __init__(self, itype, iid, destination):
assert itype == "pack" or itype == "file"
assert destination == "queue" or destination == "collector"
self.type = itype
self.id = iid
self.destination = destination
-
+
def toList(self):
return ["remove", self.destination, self.type, self.id]
-class InsertEvent():
+class InsertEvent(object):
def __init__(self, itype, iid, after, destination):
assert itype == "pack" or itype == "file"
assert destination == "queue" or destination == "collector"
@@ -99,22 +83,22 @@ class InsertEvent():
self.id = iid
self.after = after
self.destination = destination
-
+
def toList(self):
return ["insert", self.destination, self.type, self.id, self.after]
-class ReloadAllEvent():
+class ReloadAllEvent(object):
def __init__(self, destination):
assert destination == "queue" or destination == "collector"
self.destination = destination
-
+
def toList(self):
return ["reload", self.destination]
-class AccountUpdateEvent():
+class AccountUpdateEvent(object):
def toList(self):
return ["account"]
-class ConfigUpdateEvent():
+class ConfigUpdateEvent(object):
def toList(self):
return ["config"]
diff --git a/module/manager/Plugin.py b/module/manager/Plugin.py
new file mode 100644
index 000000000..879430be2
--- /dev/null
+++ b/module/manager/Plugin.py
@@ -0,0 +1,404 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import with_statement
+
+import re
+import sys
+
+from itertools import chain
+from os import listdir, makedirs
+from os.path import isdir, isfile, join, exists, abspath
+from sys import version_info
+from traceback import print_exc
+from urllib import unquote
+
+from SafeEval import const_eval as literal_eval
+
+
+class PluginManager(object):
+ ROOT = "pyload.plugin."
+ USERROOT = "userplugins."
+ TYPES = ["account", "addon", "container", "crypter", "hook", "hoster", "internal", "ocr"]
+
+ PATTERN = re.compile(r'__pattern__\s*=\s*u?r("|\')([^"\']+)')
+ VERSION = re.compile(r'__version__\s*=\s*("|\')([\d.]+)')
+ CONFIG = re.compile(r'__config__\s*=\s*\[([^\]]+)', re.M)
+ DESC = re.compile(r'__description__\s*=\s*("|"""|\')([^"\']+)')
+
+
+ def __init__(self, core):
+ self.core = core
+
+ self.plugins = {}
+ self.createIndex()
+
+ #register for import addon
+ sys.meta_path.append(self)
+
+
+ def loadTypes(self):
+ rootdir = join(pypath, "pyload", "plugins")
+ userdir = "userplugins"
+
+ types = set().union(*[[d for d in listdir(p) if isdir(join(p, d))]
+ for p in (rootdir, userdir) if exists(p)])
+
+ if not types:
+ self.log.critical(_("No plugins found!"))
+
+ self.TYPES = list(set(self.TYPES) | types)
+
+
+ def createIndex(self):
+ """create information for all plugins available"""
+
+ sys.path.append(abspath(""))
+
+ self.loadTypes()
+
+ for type in self.TYPES:
+ self.plugins[type] = self.parse(type)
+ setattr(self, "%sPlugins" % type, self.plugins[type])
+
+ self.plugins['addon'] = self.addonPlugins.update(self.hookPlugins)
+
+ self.core.log.debug("Created index of plugins")
+
+
+ def parse(self, folder, rootplugins={}):
+ """
+ returns dict with information
+ home contains parsed plugins from pyload.
+ """
+
+ plugins = {}
+
+ if rootplugins:
+ try:
+ pfolder = join("userplugins", folder)
+ if not exists(pfolder):
+ makedirs(pfolder)
+
+ for ifile in (join("userplugins", "__init__.py"),
+ join(pfolder, "__init__.py")):
+ if not exists(ifile):
+ f = open(ifile, "wb")
+ f.close()
+
+ except IOError, e:
+ self.core.log.critical(str(e))
+ return rootplugins
+
+ else:
+ pfolder = join(pypath, "pyload", "plugins", folder)
+
+ for f in listdir(pfolder):
+ if (isfile(join(pfolder, f)) and f.endswith(".py") or f.endswith("_25.pyc") or f.endswith(
+ "_26.pyc") or f.endswith("_27.pyc")) and not f.startswith("_"):
+
+ try:
+ with open(join(pfolder, f)) as data:
+ content = data.read()
+
+ except IOError, e:
+ self.core.log.error(str(e))
+ continue
+
+ if f.endswith("_25.pyc") and version_info[0:2] != (2, 5): #@TODO: Remove in 0.4.10
+ continue
+
+ elif f.endswith("_26.pyc") and version_info[0:2] != (2, 6): #@TODO: Remove in 0.4.10
+ continue
+
+ elif f.endswith("_27.pyc") and version_info[0:2] != (2, 7): #@TODO: Remove in 0.4.10
+ continue
+
+ name = f[:-3]
+ if name[-1] == ".":
+ name = name[:-4]
+
+ version = self.VERSION.findall(content)
+ if version:
+ version = float(version[0][1])
+ else:
+ version = 0
+
+ if rootplugins and name in rootplugins:
+ if rootplugins[name]['version'] >= version:
+ continue
+
+ plugins[name] = {}
+ plugins[name]['version'] = version
+
+ module = f.replace(".pyc", "").replace(".py", "")
+
+ # the plugin is loaded from user directory
+ plugins[name]['user'] = True if rootplugins else False
+ plugins[name]['name'] = module
+
+ pattern = self.PATTERN.findall(content)
+
+ if pattern:
+ pattern = pattern[0][1]
+
+ try:
+ regexp = re.compile(pattern)
+ except Exception:
+ self.core.log.error(_("%s has a invalid pattern") % name)
+ pattern = r'^unmatchable$'
+ regexp = re.compile(pattern)
+
+ plugins[name]['pattern'] = pattern
+ plugins[name]['re'] = regexp
+
+ # internals have no config
+ if folder == "internal":
+ self.core.config.deleteConfig(name)
+ continue
+
+ config = self.CONFIG.findall(content)
+ if config:
+ try:
+ config = literal_eval(config[0].strip().replace("\n", "").replace("\r", ""))
+ desc = self.DESC.findall(content)
+ desc = desc[0][1] if desc else ""
+
+ if type(config[0]) == tuple:
+ config = [list(x) for x in config]
+ else:
+ config = [list(config)]
+
+ if folder not in ("account", "internal") and not [True for item in config if item[0] == "activated"]:
+ config.insert(0, ["activated", "bool", "Activated", False if folder in ("addon", "hook") else True])
+
+ self.core.config.addPluginConfig(name, config, desc)
+ except Exception:
+ self.core.log.error("Invalid config in %s: %s" % (name, config))
+
+ elif folder in ("addon", "hook"): #force config creation
+ desc = self.DESC.findall(content)
+ desc = desc[0][1] if desc else ""
+ config = (["activated", "bool", "Activated", False],)
+
+ try:
+ self.core.config.addPluginConfig(name, config, desc)
+ except Exception:
+ self.core.log.error("Invalid config in %s: %s" % (name, config))
+
+ if not rootplugins and plugins: #: Double check
+ plugins.update(self.parse(folder, plugins))
+
+ return plugins
+
+
+ def parseUrls(self, urls):
+ """parse plugins for given list of urls"""
+
+ last = None
+ res = [] #: tupels of (url, plugintype, pluginname)
+
+ for url in urls:
+ if type(url) not in (str, unicode, buffer):
+ continue
+
+ url = unquote(url)
+
+ if last and last[2]['re'].match(url):
+ res.append((url, last[0], last[1]))
+ continue
+
+ for type in self.TYPES:
+ for name, plugin in self.plugins[type]:
+
+ m = None
+ try:
+ if 'pattern' in plugin:
+ m = plugin['re'].match(url)
+
+ except KeyError:
+ self.core.log.error(_("Plugin [%(type)s] %(name)s skipped due broken pattern")
+ % {'name': name, 'type': type})
+
+ if m:
+ res.append((url, type, name))
+ last = (type, name, plugin)
+ break
+ else:
+ res.append((url, "internal", "BasePlugin"))
+
+ return res
+
+
+ def findPlugin(self, type, name):
+ if type not in self.plugins:
+ return None
+
+ elif name not in self.plugins[type]:
+ self.core.log.warning(_("Plugin [%(type)s] %(name)s not found | Using plugin: [internal] BasePlugin")
+ % {'name': name, 'type': type})
+ return self.internalPlugins["BasePlugin"]
+
+ else:
+ return self.plugins[type][name]
+
+
+ def getPlugin(self, type, name, original=False):
+ """return plugin module from hoster|decrypter|container"""
+ plugin = self.findPlugin(type, name)
+
+ if plugin is None:
+ return {}
+
+ if "new_module" in plugin and not original:
+ return plugin['new_module']
+ else:
+ return self.loadModule(type, name)
+
+
+ def getPluginName(self, type, name):
+ """ used to obtain new name if other plugin was injected"""
+ plugin = self.findPlugin(type, name)
+
+ if plugin is None:
+ return ""
+
+ if "new_name" in plugin:
+ return plugin['new_name']
+
+ return name
+
+
+ def loadModule(self, type, name):
+ """ Returns loaded module for plugin
+
+ :param type: plugin type, subfolder of pyload.plugins
+ :param name:
+ """
+ plugins = self.plugins[type]
+
+ if name in plugins:
+ if "module" in plugins[name]:
+ return plugins[name]['module']
+
+ try:
+ module = __import__(self.ROOT + "%s.%s" % (type, plugins[name]['name']), globals(), locals(),
+ plugins[name]['name'])
+
+ except Exception, e:
+ self.core.log.error(_("Error importing plugin: [%(type)s] %(name)s (v%(version).2f) | %(errmsg)s")
+ % {'name': name, 'type': type, 'version': plugins[name]['version'], "errmsg": str(e)})
+ if self.core.debug:
+ print_exc()
+
+ else:
+ plugins[name]['module'] = module #: cache import, maybe unneeded
+
+ self.core.log.debug(_("Loaded plugin: [%(type)s] %(name)s (v%(version).2f)")
+ % {'name': name, 'type': type, 'version': plugins[name]['version']})
+ return module
+
+
+ def loadClass(self, type, name):
+ """Returns the class of a plugin with the same name"""
+ module = self.loadModule(type, name)
+ if module:
+ return getattr(module, name)
+ else:
+ return None
+
+
+ def getAccountPlugins(self):
+ """return list of account plugin names"""
+ return self.accountPlugins.keys()
+
+
+ def find_module(self, fullname, path=None):
+ #redirecting imports if necesarry
+ if fullname.startswith(self.ROOT) or fullname.startswith(self.USERROOT): #seperate pyload plugins
+ if fullname.startswith(self.USERROOT): user = 1
+ else: user = 0 #used as bool and int
+
+ split = fullname.split(".")
+ if len(split) != 4 - user: return
+ type, name = split[2 - user:4 - user]
+
+ if type in self.plugins and name in self.plugins[type]:
+ #userplugin is a newer version
+ if not user and self.plugins[type][name]['user']:
+ return self
+ #imported from userdir, but pyloads is newer
+ if user and not self.plugins[type][name]['user']:
+ return self
+
+
+ def load_module(self, name, replace=True):
+ if name not in sys.modules: #could be already in modules
+ if replace:
+ if self.ROOT in name:
+ newname = name.replace(self.ROOT, self.USERROOT)
+ else:
+ newname = name.replace(self.USERROOT, self.ROOT)
+ else:
+ newname = name
+
+ base, plugin = newname.rsplit(".", 1)
+
+ self.core.log.debug("Redirected import %s -> %s" % (name, newname))
+
+ module = __import__(newname, globals(), locals(), [plugin])
+ #inject under new an old name
+ sys.modules[name] = module
+ sys.modules[newname] = module
+
+ return sys.modules[name]
+
+
+ def reloadPlugins(self, type_plugins):
+ """ reload and reindex plugins """
+ if not type_plugins:
+ return None
+
+ self.core.log.debug("Request reload of plugins: %s" % type_plugins)
+
+ reloaded = []
+
+ as_dict = {}
+ for t,n in type_plugins:
+ if t in as_dict:
+ as_dict[t].append(n)
+ else:
+ as_dict[t] = [n]
+
+ for type in as_dict.iterkeys():
+ if type in ("addon", "internal"): #: do not reload them because would cause to much side effects
+ self.core.log.debug("Skipping reload for plugin: [%(type)s] %(name)s" % {'name': plugin, 'type': type})
+ continue
+
+ for plugin in as_dict[type]:
+ if plugin in self.plugins[type] and "module" in self.plugins[type][plugin]:
+ self.core.log.debug(_("Reloading plugin: [%(type)s] %(name)s") % {'name': plugin, 'type': type})
+
+ try:
+ reload(self.plugins[type][plugin]['module'])
+
+ except Exception, e:
+ self.core.log.error(_("Error when reloading plugin: [%(type)s] %(name)s") % {'name': plugin, 'type': type}, e)
+ continue
+
+ else:
+ reloaded.append((type, plugin))
+
+ #index creation
+ self.plugins[type] = self.parse(type)
+ setattr(self, "%sPlugins" % type, self.plugins[type])
+
+ if "account" in as_dict: #: accounts needs to be reloaded
+ self.core.accountManager.initPlugins()
+ self.core.scheduler.addJob(0, self.core.accountManager.getAccountInfos)
+
+ return reloaded #: return a list of the plugins successfully reloaded
+
+
+ def reloadPlugin(self, type_plugin):
+ """ reload and reindex ONE plugin """
+ return True if self.reloadPlugins(type_plugin) else False
diff --git a/module/remote/RemoteManager.py b/module/manager/Remote.py
index 36eb52a4a..910881164 100644
--- a/module/remote/RemoteManager.py
+++ b/module/manager/Remote.py
@@ -1,20 +1,5 @@
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
+# @author: RaNaN
from threading import Thread
from traceback import print_exc
@@ -55,7 +40,7 @@ class BackendBase(Thread):
self.shutdown()
-class RemoteManager():
+class RemoteManager(object):
available = []
def __init__(self, core):
@@ -73,7 +58,7 @@ class RemoteManager():
port = self.core.config["remote"]["port"]
for b in self.available:
- klass = getattr(__import__("module.remote.%s" % b, globals(), locals(), [b], -1), b)
+ klass = getattr(__import__("pyload.remote.%s" % b, globals(), locals(), [b], -1), b)
backend = klass(self)
if not backend.checkDeps():
continue
diff --git a/module/ThreadManager.py b/module/manager/Thread.py
index 8937f4a29..2ace9c789 100644
--- a/module/ThreadManager.py
+++ b/module/manager/Thread.py
@@ -1,22 +1,5 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
from os.path import exists, join
import re
@@ -28,23 +11,24 @@ from random import choice
import pycurl
-import PluginThread
-from module.PyFile import PyFile
-from module.network.RequestFactory import getURL
-from module.utils import freeSpace, lock
+from pyload.manager.thread.Decrypter import DecrypterThread
+from pyload.manager.thread.Download import DownloadThread
+from pyload.manager.thread.Info import InfoThread
+from pyload.datatype.File import PyFile
+from pyload.network.RequestFactory import getURL
+from pyload.utils import freeSpace, lock
-class ThreadManager:
+class ThreadManager(object):
"""manages the download threads, assign jobs, reconnect etc"""
def __init__(self, core):
"""Constructor"""
self.core = core
- self.log = core.log
- self.threads = [] # thread list
- self.localThreads = [] #hook+decrypter threads
+ self.threads = [] #: thread list
+ self.localThreads = [] #: addon+decrypter threads
self.pause = True
@@ -75,7 +59,7 @@ class ThreadManager:
def createThread(self):
"""create a download thread"""
- thread = PluginThread.DownloadThread(self)
+ thread = DownloadThread(self)
self.threads.append(thread)
def createInfoThread(self, data, pid):
@@ -85,7 +69,7 @@ class ThreadManager:
"""
self.timestamp = time() + 5 * 60
- PluginThread.InfoThread(self, data, pid)
+ InfoThread(self, data, pid)
@lock
def createResultThread(self, data, add=False):
@@ -95,7 +79,7 @@ class ThreadManager:
rid = self.resultIDs
self.resultIDs += 1
- PluginThread.InfoThread(self, data, rid=rid, add=add)
+ InfoThread(self, data, rid=rid, add=add)
return rid
@@ -134,7 +118,7 @@ class ThreadManager:
try:
self.tryReconnect()
except Exception, e:
- self.log.error(_("Reconnect Failed: %s") % str(e) )
+ self.core.log.error(_("Reconnect Failed: %s") % str(e) )
self.reconnecting.clear()
if self.core.debug:
print_exc()
@@ -143,10 +127,10 @@ class ThreadManager:
try:
self.assignJob()
except Exception, e:
- self.log.warning("Assign job error", e)
+ self.core.log.warning("Assign job error", e)
if self.core.debug:
print_exc()
-
+
sleep(0.5)
self.assignJob()
#it may be failed non critical so we try it again
@@ -154,9 +138,9 @@ class ThreadManager:
if (self.infoCache or self.infoResults) and self.timestamp < time():
self.infoCache.clear()
self.infoResults.clear()
- self.log.debug("Cleared Result cache")
+ self.core.log.debug("Cleared Result cache")
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def tryReconnect(self):
"""checks if reconnect needed"""
@@ -173,27 +157,27 @@ class ThreadManager:
self.core.config['reconnect']['method'] = join(pypath, self.core.config['reconnect']['method'])
else:
self.core.config["reconnect"]["activated"] = False
- self.log.warning(_("Reconnect script not found!"))
+ self.core.log.warning(_("Reconnect script not found!"))
return
self.reconnecting.set()
#Do reconnect
- self.log.info(_("Starting reconnect"))
+ self.core.log.info(_("Starting reconnect"))
while [x.active.plugin.waiting for x in self.threads if x.active].count(True) != 0:
sleep(0.25)
ip = self.getIP()
- self.core.hookManager.beforeReconnecting(ip)
+ self.core.addonManager.beforeReconnecting(ip)
- self.log.debug("Old IP: %s" % ip)
+ self.core.log.debug("Old IP: %s" % ip)
try:
reconn = Popen(self.core.config['reconnect']['method'], bufsize=-1, shell=True)#, stdout=subprocess.PIPE)
- except:
- self.log.warning(_("Failed executing reconnect script!"))
+ except Exception:
+ self.core.log.warning(_("Failed executing reconnect script!"))
self.core.config["reconnect"]["activated"] = False
self.reconnecting.clear()
if self.core.debug:
@@ -203,9 +187,9 @@ class ThreadManager:
reconn.wait()
sleep(1)
ip = self.getIP()
- self.core.hookManager.afterReconnecting(ip)
+ self.core.addonManager.afterReconnecting(ip)
- self.log.info(_("Reconnected, new IP: %s") % ip)
+ self.core.log.info(_("Reconnected, new IP: %s") % ip)
self.reconnecting.clear()
@@ -221,13 +205,13 @@ class ThreadManager:
ip = getURL(sv[0])
ip = re.match(sv[1], ip).group(1)
break
- except:
+ except Exception:
ip = ""
sleep(1)
return ip
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def checkThreadCount(self):
"""checks if there are need for increasing or reducing thread count"""
@@ -248,10 +232,10 @@ class ThreadManager:
pycurl.global_cleanup()
pycurl.global_init(pycurl.GLOBAL_DEFAULT)
self.downloaded = 0
- self.log.debug("Cleaned up pycurl")
+ self.core.log.debug("Cleaned up pycurl")
return True
- #----------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def assignJob(self):
"""assing a job to a thread if possible"""
@@ -262,12 +246,12 @@ class ThreadManager:
free = [x for x in self.threads if not x.active]
- inuse = set([(x.active.pluginname,self.getLimit(x)) for x in self.threads if x.active and x.active.hasPlugin() and x.active.plugin.account])
- inuse = map(lambda x : (x[0], x[1], len([y for y in self.threads if y.active and y.active.pluginname == x[0]])) ,inuse)
+ inuse = set([(x.active.pluginname, self.getLimit(x)) for x in self.threads if x.active and x.active.hasPlugin() and x.active.plugin.account])
+ inuse = map(lambda x: (x[0], x[1], len([y for y in self.threads if y.active and y.active.pluginname == x[0]])) ,inuse)
onlimit = [x[0] for x in inuse if x[1] > 0 and x[2] >= x[1]]
occ = [x.active.pluginname for x in self.threads if x.active and x.active.hasPlugin() and not x.active.plugin.multiDL] + onlimit
-
+
occ.sort()
occ = tuple(set(occ))
job = self.core.files.getJob(occ)
@@ -275,7 +259,7 @@ class ThreadManager:
try:
job.initPlugin()
except Exception, e:
- self.log.critical(str(e))
+ self.core.log.critical(str(e))
print_exc()
job.setStatus("failed")
job.error = str(e)
@@ -285,7 +269,7 @@ class ThreadManager:
if job.plugin.__type__ == "hoster":
spaceLeft = freeSpace(self.core.config["general"]["download_folder"]) / 1024 / 1024
if spaceLeft < self.core.config["general"]["min_free_space"]:
- self.log.warning(_("Not enough space left on device"))
+ self.core.log.warning(_("Not enough space left on device"))
self.pause = True
if free and not self.pause:
@@ -303,14 +287,14 @@ class ThreadManager:
job = self.core.files.getDecryptJob()
if job:
job.initPlugin()
- thread = PluginThread.DecrypterThread(self, job)
+ thread = DecrypterThread(self, job)
else:
- thread = PluginThread.DecrypterThread(self, job)
+ thread = DecrypterThread(self, job)
def getLimit(self, thread):
- limit = thread.active.plugin.account.getAccountData(thread.active.plugin.user)["options"].get("limitDL",["0"])[0]
+ limit = thread.active.plugin.account.getAccountData(thread.active.plugin.user)["options"].get("limitDL", ["0"])[0]
return int(limit)
def cleanup(self):
diff --git a/module/manager/__init__.py b/module/manager/__init__.py
new file mode 100644
index 000000000..40a96afc6
--- /dev/null
+++ b/module/manager/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/Scheduler.py b/module/manager/event/Scheduler.py
index 0bc396b69..fd428a956 100644
--- a/module/Scheduler.py
+++ b/module/manager/event/Scheduler.py
@@ -1,32 +1,15 @@
# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
+# @author: mkaay
from time import time
from heapq import heappop, heappush
-from thread import start_new_thread
-from threading import Lock
+from threading import Lock, Thread
class AlreadyCalled(Exception):
pass
-class Deferred():
+class Deferred(object):
def __init__(self):
self.call = []
self.result = ()
@@ -44,7 +27,7 @@ class Deferred():
f(*args ** kwargs)
-class Scheduler():
+class Scheduler(object):
def __init__(self, core):
self.core = core
@@ -88,7 +71,7 @@ class Scheduler():
break
-class Job():
+class Job(object):
def __init__(self, time, call, args=[], kwargs={}, deferred=None, threaded=True):
self.time = float(time)
self.call = call
@@ -106,12 +89,14 @@ class Job():
def start(self):
if self.threaded:
- start_new_thread(self.run, ())
+ t = Thread(target=self.run)
+ t.setDaemon(True)
+ t.start()
else:
self.run()
-class PriorityQueue():
+class PriorityQueue(object):
""" a non blocking priority queue """
def __init__(self):
@@ -138,4 +123,4 @@ class PriorityQueue():
except IndexError:
return None, None
finally:
- self.lock.release() \ No newline at end of file
+ self.lock.release()
diff --git a/module/manager/event/__init__.py b/module/manager/event/__init__.py
new file mode 100644
index 000000000..40a96afc6
--- /dev/null
+++ b/module/manager/event/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/manager/thread/Addon.py b/module/manager/thread/Addon.py
new file mode 100644
index 000000000..7feec227e
--- /dev/null
+++ b/module/manager/thread/Addon.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+# @author: RaNaN
+
+from Queue import Queue
+from threading import Thread
+from os import listdir, stat
+from os.path import join
+from time import sleep, time, strftime, gmtime
+from traceback import print_exc, format_exc
+from pprint import pformat
+from sys import exc_info, exc_clear
+from copy import copy
+from types import MethodType
+
+from pycurl import error
+
+from pyload.manager.thread.Plugin import PluginThread
+
+
+class AddonThread(PluginThread):
+ """thread for addons"""
+
+ #--------------------------------------------------------------------------
+ def __init__(self, m, function, args, kwargs):
+ """Constructor"""
+ PluginThread.__init__(self, m)
+
+ self.f = function
+ self.args = args
+ self.kwargs = kwargs
+
+ self.active = []
+
+ m.localThreads.append(self)
+
+ self.start()
+
+ def getActiveFiles(self):
+ return self.active
+
+ def addActive(self, pyfile):
+ """ Adds a pyfile to active list and thus will be displayed on overview"""
+ if pyfile not in self.active:
+ self.active.append(pyfile)
+
+ def finishFile(self, pyfile):
+ if pyfile in self.active:
+ self.active.remove(pyfile)
+
+ pyfile.finishIfDone()
+
+ def run(self):
+ try:
+ try:
+ self.kwargs["thread"] = self
+ self.f(*self.args, **self.kwargs)
+ except TypeError, e:
+ #dirty method to filter out exceptions
+ if "unexpected keyword argument 'thread'" not in e.args[0]:
+ raise
+
+ del self.kwargs["thread"]
+ self.f(*self.args, **self.kwargs)
+ finally:
+ local = copy(self.active)
+ for x in local:
+ self.finishFile(x)
+
+ self.m.localThreads.remove(self)
diff --git a/module/manager/thread/Decrypter.py b/module/manager/thread/Decrypter.py
new file mode 100644
index 000000000..51544d1b9
--- /dev/null
+++ b/module/manager/thread/Decrypter.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+# @author: RaNaN
+
+from Queue import Queue
+from threading import Thread
+from os import listdir, stat
+from os.path import join
+from time import sleep, time, strftime, gmtime
+from traceback import print_exc, format_exc
+from pprint import pformat
+from sys import exc_info, exc_clear
+from copy import copy
+from types import MethodType
+
+from pycurl import error
+
+from pyload.manager.thread.Plugin import PluginThread
+from pyload.plugin.Plugin import Abort, Fail, Retry
+
+
+class DecrypterThread(PluginThread):
+ """thread for decrypting"""
+
+ def __init__(self, manager, pyfile):
+ """constructor"""
+ PluginThread.__init__(self, manager)
+
+ self.active = pyfile
+ manager.localThreads.append(self)
+
+ pyfile.setStatus("decrypting")
+
+ self.start()
+
+ def getActiveFiles(self):
+ return [self.active]
+
+ def run(self):
+ """run method"""
+
+ pyfile = self.active
+ retry = False
+
+ try:
+ self.m.log.info(_("Decrypting starts: %s") % pyfile.name)
+ pyfile.error = ""
+ pyfile.plugin.preprocessing(self)
+
+ except NotImplementedError:
+ self.m.log.error(_("Plugin %s is missing a function.") % pyfile.pluginname)
+ return
+
+ except Fail, e:
+ msg = e.args[0]
+
+ if msg == "offline":
+ pyfile.setStatus("offline")
+ self.m.log.warning(_("Download is offline: %s") % pyfile.name)
+ else:
+ pyfile.setStatus("failed")
+ self.m.log.error(_("Decrypting failed: %(name)s | %(msg)s") % {"name": pyfile.name, "msg": msg})
+ pyfile.error = msg
+
+ if self.m.core.debug:
+ print_exc()
+ return
+
+ except Abort:
+ self.m.log.info(_("Download aborted: %s") % pyfile.name)
+ pyfile.setStatus("aborted")
+
+ if self.m.core.debug:
+ print_exc()
+ return
+
+ except Retry:
+ self.m.log.info(_("Retrying %s") % pyfile.name)
+ retry = True
+ return self.run()
+
+ except Exception, e:
+ pyfile.setStatus("failed")
+ self.m.log.error(_("Decrypting failed: %(name)s | %(msg)s") % {"name": pyfile.name, "msg": str(e)})
+ pyfile.error = str(e)
+
+ if self.m.core.debug:
+ print_exc()
+ self.writeDebugReport(pyfile)
+
+ return
+
+ finally:
+ if not retry:
+ pyfile.release()
+ self.active = False
+ self.m.core.files.save()
+ self.m.localThreads.remove(self)
+ exc_clear()
+
+ if not retry:
+ pyfile.delete()
diff --git a/module/manager/thread/Download.py b/module/manager/thread/Download.py
new file mode 100644
index 000000000..c7d21a4ba
--- /dev/null
+++ b/module/manager/thread/Download.py
@@ -0,0 +1,213 @@
+# -*- coding: utf-8 -*-
+# @author: RaNaN
+
+from Queue import Queue
+from threading import Thread
+from os import listdir, stat
+from os.path import join
+from time import sleep, time, strftime, gmtime
+from traceback import print_exc, format_exc
+from pprint import pformat
+from sys import exc_info, exc_clear
+from copy import copy
+from types import MethodType
+
+from pycurl import error
+
+from pyload.manager.thread.Plugin import PluginThread
+from pyload.plugin.Plugin import Abort, Fail, Reconnect, Retry, SkipDownload
+
+
+class DownloadThread(PluginThread):
+ """thread for downloading files from 'real' hoster plugins"""
+
+ #--------------------------------------------------------------------------
+ def __init__(self, manager):
+ """Constructor"""
+ PluginThread.__init__(self, manager)
+
+ self.queue = Queue() #: job queue
+ self.active = False
+
+ self.start()
+
+ #--------------------------------------------------------------------------
+ def run(self):
+ """run method"""
+ pyfile = None
+
+ while True:
+ del pyfile
+ self.active = self.queue.get()
+ pyfile = self.active
+
+ if self.active == "quit":
+ self.active = False
+ self.m.threads.remove(self)
+ return True
+
+ try:
+ if not pyfile.hasPlugin():
+ continue
+ #this pyfile was deleted while queueing
+
+ pyfile.plugin.checkForSameFiles(starting=True)
+ self.m.log.info(_("Download starts: %s" % pyfile.name))
+
+ # start download
+ self.m.core.addonManager.downloadPreparing(pyfile)
+ pyfile.error = ""
+ pyfile.plugin.preprocessing(self)
+
+ self.m.log.info(_("Download finished: %s") % pyfile.name)
+ self.m.core.addonManager.downloadFinished(pyfile)
+ self.m.core.files.checkPackageFinished(pyfile)
+
+ except NotImplementedError:
+ self.m.log.error(_("Plugin %s is missing a function.") % pyfile.pluginname)
+ pyfile.setStatus("failed")
+ pyfile.error = "Plugin does not work"
+ self.clean(pyfile)
+ continue
+
+ except Abort:
+ try:
+ self.m.log.info(_("Download aborted: %s") % pyfile.name)
+ except Exception:
+ pass
+
+ pyfile.setStatus("aborted")
+
+ if self.m.core.debug:
+ print_exc()
+
+ self.clean(pyfile)
+ continue
+
+ except Reconnect:
+ self.queue.put(pyfile)
+ #pyfile.req.clearCookies()
+
+ while self.m.reconnecting.isSet():
+ sleep(0.5)
+
+ continue
+
+ except Retry, e:
+ reason = e.args[0]
+ self.m.log.info(_("Download restarted: %(name)s | %(msg)s") % {"name": pyfile.name, "msg": reason})
+ self.queue.put(pyfile)
+ continue
+
+ except Fail, e:
+ msg = e.args[0]
+
+ if msg == "offline":
+ pyfile.setStatus("offline")
+ self.m.log.warning(_("Download is offline: %s") % pyfile.name)
+ elif msg == "temp. offline":
+ pyfile.setStatus("temp. offline")
+ self.m.log.warning(_("Download is temporary offline: %s") % pyfile.name)
+ else:
+ pyfile.setStatus("failed")
+ self.m.log.warning(_("Download failed: %(name)s | %(msg)s") % {"name": pyfile.name, "msg": msg})
+ pyfile.error = msg
+
+ if self.m.core.debug:
+ print_exc()
+
+ self.m.core.addonManager.downloadFailed(pyfile)
+ self.clean(pyfile)
+ continue
+
+ except error, e:
+ if len(e.args) == 2:
+ code, msg = e.args
+ else:
+ code = 0
+ msg = e.args
+
+ self.m.log.debug("pycurl exception %s: %s" % (code, msg))
+
+ if code in (7, 18, 28, 52, 56):
+ self.m.log.warning(_("Couldn't connect to host or connection reset, waiting 1 minute and retry."))
+ wait = time() + 60
+
+ pyfile.waitUntil = wait
+ pyfile.setStatus("waiting")
+ while time() < wait:
+ sleep(1)
+ if pyfile.abort:
+ break
+
+ if pyfile.abort:
+ self.m.log.info(_("Download aborted: %s") % pyfile.name)
+ pyfile.setStatus("aborted")
+
+ self.clean(pyfile)
+ else:
+ self.queue.put(pyfile)
+
+ continue
+
+ else:
+ pyfile.setStatus("failed")
+ self.m.log.error("pycurl error %s: %s" % (code, msg))
+ if self.m.core.debug:
+ print_exc()
+ self.writeDebugReport(pyfile)
+
+ self.m.core.addonManager.downloadFailed(pyfile)
+
+ self.clean(pyfile)
+ continue
+
+ except SkipDownload, e:
+ pyfile.setStatus("skipped")
+
+ self.m.log.info(
+ _("Download skipped: %(name)s due to %(plugin)s") % {"name": pyfile.name, "plugin": e.message})
+
+ self.clean(pyfile)
+
+ self.m.core.files.checkPackageFinished(pyfile)
+
+ self.active = False
+ self.m.core.files.save()
+
+ continue
+
+
+ except Exception, e:
+ pyfile.setStatus("failed")
+ self.m.log.warning(_("Download failed: %(name)s | %(msg)s") % {"name": pyfile.name, "msg": str(e)})
+ pyfile.error = str(e)
+
+ if self.m.core.debug:
+ print_exc()
+ self.writeDebugReport(pyfile)
+
+ self.m.core.addonManager.downloadFailed(pyfile)
+ self.clean(pyfile)
+ continue
+
+ finally:
+ self.m.core.files.save()
+ pyfile.checkIfProcessed()
+ exc_clear()
+
+ #pyfile.plugin.req.clean()
+
+ self.active = False
+ pyfile.finishIfDone()
+ self.m.core.files.save()
+
+
+ def put(self, job):
+ """assing job to thread"""
+ self.queue.put(job)
+
+
+ def stop(self):
+ """stops the thread"""
+ self.put("quit")
diff --git a/module/manager/thread/Info.py b/module/manager/thread/Info.py
new file mode 100644
index 000000000..4526a07ed
--- /dev/null
+++ b/module/manager/thread/Info.py
@@ -0,0 +1,225 @@
+# -*- coding: utf-8 -*-
+# @author: RaNaN
+
+from Queue import Queue
+from threading import Thread
+from os import listdir, stat
+from os.path import join
+from time import sleep, time, strftime, gmtime
+from traceback import print_exc, format_exc
+from pprint import pformat
+from sys import exc_info, exc_clear
+from copy import copy
+from types import MethodType
+
+from pycurl import error
+
+from pyload.datatype.File import PyFile
+from pyload.manager.thread.Plugin import PluginThread
+from pyload.api import OnlineStatus
+
+
+class InfoThread(PluginThread):
+
+ def __init__(self, manager, data, pid=-1, rid=-1, add=False):
+ """Constructor"""
+ PluginThread.__init__(self, manager)
+
+ self.data = data
+ self.pid = pid # package id
+ # [ .. (name, plugin) .. ]
+
+ self.rid = rid #result id
+ self.add = add #add packages instead of return result
+
+ self.cache = [] #accumulated data
+
+ self.start()
+
+ def run(self):
+ """run method"""
+
+ plugins = {}
+ container = []
+
+ for url, plugintype, pluginname in data:
+ try:
+ plugins[plugintype][pluginname].append(url)
+ except Exception:
+ plugins[plugintype][pluginname] = [url]
+
+ # filter out container plugins
+ for name in self.m.core.pluginManager.containerPlugins:
+ if name in plugins:
+ container.extend([(name, url) for url in plugins[name]])
+
+ del plugins[name]
+
+ #directly write to database
+ if self.pid > -1:
+ for plugintype, pluginname, urls in plugins.iteritems():
+ plugin = self.m.core.pluginManager.getPlugin(plugintype, pluginname, True)
+ if hasattr(plugin, "getInfo"):
+ self.fetchForPlugin(pluginname, plugin, urls, self.updateDB)
+ self.m.core.files.save()
+
+ elif self.add:
+ for plugintype, pluginname, urls in plugins.iteritems():
+ plugin = self.m.core.pluginManager.getPlugin(plugintype, pluginname, True)
+ if hasattr(plugin, "getInfo"):
+ self.fetchForPlugin(pluginname, plugin, urls, self.updateCache, True)
+
+ else:
+ #generate default result
+ result = [(url, 0, 3, url) for url in urls]
+
+ self.updateCache(pluginname, result)
+
+ packs = parseNames([(name, url) for name, x, y, url in self.cache])
+
+ self.m.log.debug("Fetched and generated %d packages" % len(packs))
+
+ for k, v in packs:
+ self.m.core.api.addPackage(k, v)
+
+ #empty cache
+ del self.cache[:]
+
+ else: #post the results
+
+
+ for name, url in container:
+ #attach container content
+ try:
+ data = self.decryptContainer(name, url)
+ except Exception:
+ print_exc()
+ self.m.log.error("Could not decrypt container.")
+ data = []
+
+ for url, plugintype, pluginname in data:
+ try:
+ plugins[plugintype][pluginname].append(url)
+ except Exception:
+ plugins[plugintype][pluginname] = [url]
+
+ self.m.infoResults[self.rid] = {}
+
+ for plugintype, pluginname, urls in plugins.iteritems():
+ plugin = self.m.core.pluginManager.getPlugin(plugintype, pluginname, True)
+ if hasattr(plugin, "getInfo"):
+ self.fetchForPlugin(pluginname, plugin, urls, self.updateResult, True)
+
+ #force to process cache
+ if self.cache:
+ self.updateResult(pluginname, [], True)
+
+ else:
+ #generate default result
+ result = [(url, 0, 3, url) for url in urls]
+
+ self.updateResult(pluginname, result, True)
+
+ self.m.infoResults[self.rid]["ALL_INFO_FETCHED"] = {}
+
+ self.m.timestamp = time() + 5 * 60
+
+
+ def updateDB(self, plugin, result):
+ self.m.core.files.updateFileInfo(result, self.pid)
+
+ def updateResult(self, plugin, result, force=False):
+ #parse package name and generate result
+ #accumulate results
+
+ self.cache.extend(result)
+
+ if len(self.cache) >= 20 or force:
+ #used for package generating
+ tmp = [(name, (url, OnlineStatus(name, plugin, "unknown", status, int(size))))
+ for name, size, status, url in self.cache]
+
+ data = parseNames(tmp)
+ result = {}
+ for k, v in data.iteritems():
+ for url, status in v:
+ status.packagename = k
+ result[url] = status
+
+ self.m.setInfoResults(self.rid, result)
+
+ self.cache = []
+
+ def updateCache(self, plugin, result):
+ self.cache.extend(result)
+
+ def fetchForPlugin(self, pluginname, plugin, urls, cb, err=None):
+ try:
+ result = [] #result loaded from cache
+ process = [] #urls to process
+ for url in urls:
+ if url in self.m.infoCache:
+ result.append(self.m.infoCache[url])
+ else:
+ process.append(url)
+
+ if result:
+ self.m.log.debug("Fetched %d values from cache for %s" % (len(result), pluginname))
+ cb(pluginname, result)
+
+ if process:
+ self.m.log.debug("Run Info Fetching for %s" % pluginname)
+ for result in plugin.getInfo(process):
+ #result = [ .. (name, size, status, url) .. ]
+ if not type(result) == list:
+ result = [result]
+
+ for res in result:
+ self.m.infoCache[res[3]] = res #: why don't assign res dict directly?
+
+ cb(pluginname, result)
+
+ self.m.log.debug("Finished Info Fetching for %s" % pluginname)
+ except Exception, e:
+ self.m.log.warning(_("Info Fetching for %(name)s failed | %(err)s") %
+ {"name": pluginname, "err": str(e)})
+ if self.m.core.debug:
+ print_exc()
+
+ # generate default results
+ if err:
+ result = [(url, 0, 3, url) for url in urls]
+ cb(pluginname, result)
+
+
+ def decryptContainer(self, plugin, url):
+ data = []
+ # only works on container plugins
+
+ self.m.log.debug("Pre decrypting %s with %s" % (url, plugin))
+
+ # dummy pyfile
+ pyfile = PyFile(self.m.core.files, -1, url, url, 0, 0, "", plugin, -1, -1)
+
+ pyfile.initPlugin()
+
+ # little plugin lifecycle
+ try:
+ pyfile.plugin.setup()
+ pyfile.plugin.loadToDisk()
+ pyfile.plugin.decrypt(pyfile)
+ pyfile.plugin.deleteTmp()
+
+ for pack in pyfile.plugin.packages:
+ pyfile.plugin.urls.extend(pack[1])
+
+ data = self.m.core.pluginManager.parseUrls(pyfile.plugin.urls)
+
+ self.m.log.debug("Got %d links." % len(data))
+
+ except Exception, e:
+ self.m.log.debug("Pre decrypting error: %s" % str(e))
+ finally:
+ pyfile.release()
+
+ return data
diff --git a/module/manager/thread/Plugin.py b/module/manager/thread/Plugin.py
new file mode 100644
index 000000000..20d57c933
--- /dev/null
+++ b/module/manager/thread/Plugin.py
@@ -0,0 +1,130 @@
+# -*- coding: utf-8 -*-
+# @author: RaNaN
+
+from Queue import Queue
+from threading import Thread
+from os import listdir, stat
+from os.path import join
+from time import sleep, time, strftime, gmtime
+from traceback import print_exc, format_exc
+from pprint import pformat
+from sys import exc_info, exc_clear
+from copy import copy
+from types import MethodType
+
+from pycurl import error
+
+from pyload.datatype.File import PyFile
+from pyload.plugin.Plugin import Abort, Fail, Reconnect, Retry, SkipDownload
+from pyload.utils.packagetools import parseNames
+from pyload.utils import safe_join
+from pyload.api import OnlineStatus
+
+class PluginThread(Thread):
+ """abstract base class for thread types"""
+
+ #--------------------------------------------------------------------------
+ def __init__(self, manager):
+ """Constructor"""
+ Thread.__init__(self)
+ self.setDaemon(True)
+ self.m = manager #thread manager
+
+
+ def writeDebugReport(self, pyfile):
+ """ writes a
+ :return:
+ """
+
+ dump_name = "debug_%s_%s.zip" % (pyfile.pluginname, strftime("%d-%m-%Y_%H-%M-%S"))
+ dump = self.getDebugDump(pyfile)
+
+ try:
+ import zipfile
+
+ zip = zipfile.ZipFile(dump_name, "w")
+
+ for f in listdir(join("tmp", pyfile.pluginname)):
+ try:
+ # avoid encoding errors
+ zip.write(join("tmp", pyfile.pluginname, f), safe_join(pyfile.pluginname, f))
+ except Exception:
+ pass
+
+ info = zipfile.ZipInfo(safe_join(pyfile.pluginname, "debug_Report.txt"), gmtime())
+ info.external_attr = 0644 << 16L # change permissions
+
+ zip.writestr(info, dump)
+ zip.close()
+
+ if not stat(dump_name).st_size:
+ raise Exception("Empty Zipfile")
+
+ except Exception, e:
+ self.m.log.debug("Error creating zip file: %s" % e)
+
+ dump_name = dump_name.replace(".zip", ".txt")
+ f = open(dump_name, "wb")
+ f.write(dump)
+ f.close()
+
+ self.m.core.log.info("Debug Report written to %s" % dump_name)
+
+ def getDebugDump(self, pyfile):
+ dump = "pyLoad %s Debug Report of %s %s \n\nTRACEBACK:\n %s \n\nFRAMESTACK:\n" % (
+ self.m.core.api.getServerVersion(), pyfile.pluginname, pyfile.plugin.__version__, format_exc())
+
+ tb = exc_info()[2]
+ stack = []
+ while tb:
+ stack.append(tb.tb_frame)
+ tb = tb.tb_next
+
+ for frame in stack[1:]:
+ dump += "\nFrame %s in %s at line %s\n" % (frame.f_code.co_name,
+ frame.f_code.co_filename,
+ frame.f_lineno)
+
+ for key, value in frame.f_locals.items():
+ dump += "\t%20s = " % key
+ try:
+ dump += pformat(value) + "\n"
+ except Exception, e:
+ dump += "<ERROR WHILE PRINTING VALUE> " + str(e) + "\n"
+
+ del frame
+
+ del stack #delete it just to be sure...
+
+ dump += "\n\nPLUGIN OBJECT DUMP: \n\n"
+
+ for name in dir(pyfile.plugin):
+ attr = getattr(pyfile.plugin, name)
+ if not name.endswith("__") and type(attr) != MethodType:
+ dump += "\t%20s = " % name
+ try:
+ dump += pformat(attr) + "\n"
+ except Exception, e:
+ dump += "<ERROR WHILE PRINTING VALUE> " + str(e) + "\n"
+
+ dump += "\nPYFILE OBJECT DUMP: \n\n"
+
+ for name in dir(pyfile):
+ attr = getattr(pyfile, name)
+ if not name.endswith("__") and type(attr) != MethodType:
+ dump += "\t%20s = " % name
+ try:
+ dump += pformat(attr) + "\n"
+ except Exception, e:
+ dump += "<ERROR WHILE PRINTING VALUE> " + str(e) + "\n"
+
+ if pyfile.pluginname in self.m.core.config.plugin:
+ dump += "\n\nCONFIG: \n\n"
+ dump += pformat(self.m.core.config.plugin[pyfile.pluginname]) + "\n"
+
+ return dump
+
+ def clean(self, pyfile):
+ """ set thread unactive and release pyfile """
+ self.active = False
+ pyfile.release()
diff --git a/module/web/ServerThread.py b/module/manager/thread/Server.py
index 84667e5f6..f3f174e74 100644
--- a/module/web/ServerThread.py
+++ b/module/manager/thread/Server.py
@@ -1,5 +1,7 @@
-#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
from __future__ import with_statement
+
from os.path import exists
import os
@@ -27,48 +29,48 @@ class WebServer(threading.Thread):
self.setDaemon(True)
def run(self):
- import webinterface
+ import pyload.webui as webinterface
global webinterface
- if self.https:
- if not exists(self.cert) or not exists(self.key):
- log.warning(_("SSL certificates not found."))
- self.https = False
+ reset = False
+
+ if self.https and (not exists(self.cert) or not exists(self.key)):
+ log.warning(_("SSL certificates not found."))
+ self.https = False
if self.server in ("lighttpd", "nginx"):
log.warning(_("Sorry, we dropped support for starting %s directly within pyLoad") % self.server)
log.warning(_("You can use the threaded server which offers good performance and ssl,"))
log.warning(_("of course you can still use your existing %s with pyLoads fastcgi server") % self.server)
- log.warning(_("sample configs are located in the module/web/servers directory"))
- self.server = "builtin"
-
- if self.server == "fastcgi":
+ log.warning(_("sample configs are located in the pyload/web/servers directory"))
+ reset = True
+ elif self.server == "fastcgi":
try:
import flup
- except:
+ except Exception:
log.warning(_("Can't use %(server)s, python-flup is not installed!") % {
"server": self.server})
- self.server = "builtin"
- elif self.server == "lightweight":
- try:
- import bjoern
- except Exception, e:
- log.error(_("Error importing lightweight server: %s") % e)
- log.warning(_("You need to download and compile bjoern, https://github.com/jonashaag/bjoern"))
- log.warning(_("Copy the boern.so to module/lib folder or use setup.py install"))
- log.warning(_("Of course you need to be familiar with linux and know how to compile software"))
- self.server = "builtin"
-
- if os.name == "nt":
- self.core.log.info(_("Server set to threaded, due to known performance problems on windows."))
- self.core.config['webinterface']['server'] = "threaded"
- self.server = "threaded"
-
-
- if self.server == "fastcgi":
- self.start_fcgi()
- elif self.server == "threaded":
+ reset = True
+
+ if reset or self.server == "lightweight":
+ if os.name != "nt":
+ try:
+ import bjoern
+ except Exception, e:
+ log.error(_("Error importing lightweight server: %s") % e)
+ log.warning(_("You need to download and compile bjoern, https://github.com/jonashaag/bjoern"))
+ log.warning(_("Copy the boern.so to the lib folder or use setup.py install"))
+ log.warning(_("Of course you need to be familiar with linux and know how to compile software"))
+ self.server = "builtin"
+ else:
+ self.core.log.info(_("Server set to threaded, due to known performance problems on windows."))
+ self.core.config['webinterface']['server'] = "threaded"
+ self.server = "threaded"
+
+ if self.server == "threaded":
self.start_threaded()
+ elif self.server == "fastcgi":
+ self.start_fcgi()
elif self.server == "lightweight":
self.start_lightweight()
else:
diff --git a/module/manager/thread/__init__.py b/module/manager/thread/__init__.py
new file mode 100644
index 000000000..40a96afc6
--- /dev/null
+++ b/module/manager/thread/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/network/Browser.py b/module/network/Browser.py
index d68a23687..89341ff25 100644
--- a/module/network/Browser.py
+++ b/module/network/Browser.py
@@ -1,10 +1,9 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
from logging import getLogger
-from HTTPRequest import HTTPRequest
-from HTTPDownload import HTTPDownload
+from pyload.network.HTTPRequest import HTTPRequest
+from pyload.network.HTTPDownload import HTTPDownload
class Browser(object):
@@ -131,16 +130,3 @@ class Browser(object):
del self.dl
if hasattr(self, "cj"):
del self.cj
-
-if __name__ == "__main__":
- browser = Browser()#proxies={"socks5": "localhost:5000"})
- ip = "http://www.whatismyip.com/automation/n09230945.asp"
- #browser.getPage("http://google.com/search?q=bar")
- #browser.getPage("https://encrypted.google.com/")
- #print browser.getPage(ip)
- #print browser.getRedirectLocation("http://google.com/")
- #browser.getPage("https://encrypted.google.com/")
- #browser.getPage("http://google.com/search?q=bar")
-
- browser.httpDownload("http://speedtest.netcologne.de/test_10mb.bin", "test_10mb.bin")
-
diff --git a/module/network/Bucket.py b/module/network/Bucket.py
index 69da277ae..408a1e240 100644
--- a/module/network/Bucket.py
+++ b/module/network/Bucket.py
@@ -1,45 +1,37 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
from time import time
from threading import Lock
-class Bucket:
+MIN_RATE = 10240 #: 10kb minimum rate
+
+
+class Bucket(object):
+
def __init__(self):
- self.rate = 0
- self.tokens = 0
+ self.rate = 0 # bytes per second, maximum targeted throughput
+ self.tokens = 0
self.timestamp = time()
- self.lock = Lock()
+ self.lock = Lock()
+
def __nonzero__(self):
- return False if self.rate < 10240 else True
+ return False if self.rate < MIN_RATE else True
+
def setRate(self, rate):
self.lock.acquire()
self.rate = int(rate)
self.lock.release()
+
def consumed(self, amount):
- """ return time the process have to sleep, after consumed specified amount """
- if self.rate < 10240: return 0 #min. 10kb, may become unresponsive otherwise
- self.lock.acquire()
+ """ return the time the process has to sleep, after it consumed a specified amount """
+ if self.rate < MIN_RATE:
+ return 0 #: May become unresponsive otherwise
+ self.lock.acquire()
self.calc_tokens()
self.tokens -= amount
@@ -47,15 +39,14 @@ class Bucket:
time = -self.tokens/float(self.rate)
else:
time = 0
-
self.lock.release()
return time
+
def calc_tokens(self):
if self.tokens < self.rate:
now = time()
delta = self.rate * (now - self.timestamp)
self.tokens = min(self.rate, self.tokens + delta)
self.timestamp = now
-
diff --git a/module/network/CookieJar.py b/module/network/CookieJar.py
index c05812334..a2b302776 100644
--- a/module/network/CookieJar.py
+++ b/module/network/CookieJar.py
@@ -1,50 +1,42 @@
# -*- coding: utf-8 -*-
+# @author: RaNaN
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
+import Cookie
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
+from datetime import datetime, timedelta
+from time import time
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
- @author: mkaay, RaNaN
-"""
+# monkey patch for 32 bit systems
+def _getdate(future=0, weekdayname=Cookie._weekdayname, monthname=Cookie._monthname):
+ dt = datetime.now() + timedelta(seconds=int(future))
+ return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \
+ (weekdayname[dt.weekday()], dt.day, monthname[dt.month], dt.year, dt.hour, dt.minute, dt.second)
+
+Cookie._getdate = _getdate
-from time import time
-class CookieJar():
- def __init__(self, pluginname, account=None):
- self.cookies = {}
- self.plugin = pluginname
- self.account = account
+class CookieJar(Cookie.SimpleCookie):
- def addCookies(self, clist):
- for c in clist:
- name = c.split("\t")[5]
- self.cookies[name] = c
+ def getCookie(self, name):
+ return self[name].value
- def getCookies(self):
- return self.cookies.values()
+ def setCookie(self, domain, name, value, path="/", exp=None, secure="FALSE"):
+ self[name] = value
+ self[name]["domain"] = domain
+ self[name]["path"] = path
- def parseCookie(self, name):
- if name in self.cookies:
- return self.cookies[name].split("\t")[6]
+ # Value of expires should be integer if possible
+ # otherwise the cookie won't be used
+ if not exp:
+ expires = time() + 3600 * 24 * 180
else:
- return None
-
- def getCookie(self, name):
- return self.parseCookie(name)
+ try:
+ expires = int(exp)
+ except ValueError:
+ expires = exp
- def setCookie(self, domain, name, value, path="/", exp=time()+3600*24*180):
- s = ".%s TRUE %s FALSE %s %s %s" % (domain, path, exp, name, value)
- self.cookies[name] = s
+ self[name]["expires"] = expires
- def clear(self):
- self.cookies = {}
+ if secure == "TRUE":
+ self[name]["secure"] = secure
diff --git a/module/network/HTTPChunk.py b/module/network/HTTPChunk.py
index b637aef32..41752b940 100644
--- a/module/network/HTTPChunk.py
+++ b/module/network/HTTPChunk.py
@@ -1,36 +1,22 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
+
from os import remove, stat, fsync
from os.path import exists
from time import sleep
from re import search
-from module.utils import fs_encode
+from pyload.utils import fs_encode
import codecs
import pycurl
+import urllib
-from HTTPRequest import HTTPRequest
+from pyload.network.HTTPRequest import HTTPRequest
class WrongFormat(Exception):
pass
-class ChunkInfo():
+class ChunkInfo(object):
def __init__(self, name):
self.name = unicode(name)
self.size = 0
@@ -208,7 +194,7 @@ class HTTPChunk(HTTPRequest):
# as first chunk, we will parse the headers
if not self.range and self.header.endswith("\r\n\r\n"):
self.parseHeader()
- elif not self.range and buf.startswith("150") and "data connection" in buf: #ftp file size parsing
+ elif not self.range and buf.startswith("150") and "data connection" in buf.lower(): #: ftp file size parsing
size = search(r"(\d+) bytes", buf)
if size:
self.p.size = int(size.group(1))
@@ -253,13 +239,23 @@ class HTTPChunk(HTTPRequest):
"""parse data from recieved header"""
for orgline in self.decodeResponse(self.header).splitlines():
line = orgline.strip().lower()
+
if line.startswith("accept-ranges") and "bytes" in line:
self.p.chunkSupport = True
- if line.startswith("content-disposition") and "filename=" in line:
- name = orgline.partition("filename=")[2]
- name = name.replace('"', "").replace("'", "").replace(";", "").strip()
+ if line.startswith("content-disposition") and ("filename=" in line or "filename*=" in line):
+ if "filename*=" in line:
+ # extended version according to RFC 6266 and RFC 5987.
+ encoding = line.partition("*=")[2].partition("''")[0]
+ name = orgline.partition("''")[2]
+ name = urllib.unquote(str(name)).decode(charEnc(encoding))
+ else:
+ # basic version, US-ASCII only
+ name = orgline.partition("filename=")[2]
+
+ name = name.replace('"', "").replace("'", "").replace(";", "").replace("/", "_").strip()
self.p.nameDisposition = name
+
self.log.debug("Content-Disposition: %s" % name)
if not self.resume and line.startswith("content-length"):
@@ -269,7 +265,7 @@ class HTTPChunk(HTTPRequest):
def stop(self):
"""The download will not proceed after next call of writeBody"""
- self.range = [0,0]
+ self.range = [0, 0]
self.size = 0
def resetRange(self):
@@ -290,4 +286,9 @@ class HTTPChunk(HTTPRequest):
""" closes everything, unusable after this """
if self.fp: self.fp.close()
self.c.close()
- if hasattr(self, "p"): del self.p \ No newline at end of file
+ if hasattr(self, "p"): del self.p
+
+
+def charEnc(enc):
+ return {'utf-8' : "utf_8",
+ 'iso-8859-1': "latin_1"}.get(enc, "unknown")
diff --git a/module/network/HTTPDownload.py b/module/network/HTTPDownload.py
index fe8075539..3b2bf26ca 100644
--- a/module/network/HTTPDownload.py
+++ b/module/network/HTTPDownload.py
@@ -1,21 +1,5 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
from os import remove, fsync
from os.path import dirname
@@ -25,17 +9,18 @@ from logging import getLogger
import pycurl
-from HTTPChunk import ChunkInfo, HTTPChunk
-from HTTPRequest import BadHeader
+from pyload.network.HTTPChunk import ChunkInfo, HTTPChunk
+from pyload.network.HTTPRequest import BadHeader
-from module.plugins.Plugin import Abort
-from module.utils import save_join, fs_encode
+from pyload.plugin.Plugin import Abort
+from pyload.utils import safe_join, fs_encode
-class HTTPDownload():
+
+class HTTPDownload(object):
""" loads a url http + ftp """
def __init__(self, url, filename, get={}, post={}, referer=None, cj=None, bucket=None,
- options={}, progressNotify=None, disposition=False):
+ options={}, progress=None, disposition=False):
self.url = url
self.filename = filename #complete file destination, not only name
self.get = get
@@ -63,7 +48,7 @@ class HTTPDownload():
except IOError:
self.info = ChunkInfo(filename)
- self.chunkSupport = None
+ self.chunkSupport = True
self.m = pycurl.CurlMulti()
#needed for speed calculation
@@ -71,7 +56,7 @@ class HTTPDownload():
self.speeds = []
self.lastSpeeds = [0, 0]
- self.progressNotify = progressNotify
+ self.progress = progress
@property
def speed(self):
@@ -114,7 +99,7 @@ class HTTPDownload():
fo.close()
if self.nameDisposition and self.disposition:
- self.filename = save_join(dirname(self.filename), self.nameDisposition)
+ self.filename = safe_join(dirname(self.filename), self.nameDisposition)
move(init, fs_encode(self.filename))
self.info.remove() #remove info file
@@ -130,7 +115,7 @@ class HTTPDownload():
except pycurl.error, e:
#code 33 - no resume
code = e.args[0]
- if code == 33:
+ if resume is True and code == 33:
# try again without resume
self.log.debug("Errno 33 -> Restart without resume")
@@ -151,6 +136,7 @@ class HTTPDownload():
if not resume:
self.info.clear()
self.info.addChunk("%s.chunk0" % self.filename, (0, 0)) #create an initial entry
+ self.info.save()
self.chunks = []
@@ -164,8 +150,8 @@ class HTTPDownload():
chunksDone = set() # list of curl handles that are finished
chunksCreated = False
done = False
- if self.info.getCount() > 1: # This is a resume, if we were chunked originally assume still can
- self.chunkSupport = True
+ if self.info.getCount() is 0: # This is a resume, if we were chunked originally assume still can
+ self.chunkSupport = False
while 1:
#need to create chunks
@@ -284,9 +270,9 @@ class HTTPDownload():
self.updateProgress()
if self.abort:
- raise Abort()
+ raise Abort
- #sleep(0.003) #supress busy waiting - limits dl speed to (1 / x) * buffersize
+ #sleep(0.003) #supress busy waiting - limits dl speed to (1 / x) * buffersize
self.m.select(1)
for chunk in self.chunks:
@@ -295,8 +281,8 @@ class HTTPDownload():
self._copyChunks()
def updateProgress(self):
- if self.progressNotify:
- self.progressNotify(self.percent)
+ if self.progress:
+ self.progress(self.percent)
def findChunk(self, handle):
""" linear search to find a chunk (should be ok since chunk size is usually low) """
@@ -324,17 +310,3 @@ class HTTPDownload():
del self.cj
if hasattr(self, "info"):
del self.info
-
-if __name__ == "__main__":
- url = "http://speedtest.netcologne.de/test_100mb.bin"
-
- from Bucket import Bucket
-
- bucket = Bucket()
- bucket.setRate(200 * 1024)
- bucket = None
-
- print "starting"
-
- dwnld = HTTPDownload(url, "test_100mb.bin", bucket=bucket)
- dwnld.download(chunks=3, resume=True)
diff --git a/module/network/HTTPRequest.py b/module/network/HTTPRequest.py
index 4747d937f..eac03a365 100644
--- a/module/network/HTTPRequest.py
+++ b/module/network/HTTPRequest.py
@@ -1,21 +1,7 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
+
+from __future__ import with_statement
import pycurl
@@ -25,15 +11,17 @@ from httplib import responses
from logging import getLogger
from cStringIO import StringIO
-from module.plugins.Plugin import Abort
+from pyload.plugin.Plugin import Abort, Fail
+
+from pyload.utils import encode
+
def myquote(url):
- return quote(url.encode('utf_8') if isinstance(url, unicode) else url, safe="%/:=&?~#+!$,;'@()*[]")
-
+ return quote(encode(url), safe="%/:=&?~#+!$,;'@()*[]")
+
def myurlencode(data):
data = dict(data)
- return urlencode(dict((x.encode('utf_8') if isinstance(x, unicode) else x, \
- y.encode('utf_8') if isinstance(y, unicode) else y ) for x, y in data.iteritems()))
+ return urlencode(dict((encode(x), encode(y)) for x, y in data.iteritems()))
bad_headers = range(400, 404) + range(405, 418) + range(500, 506)
@@ -44,7 +32,7 @@ class BadHeader(Exception):
self.content = content
-class HTTPRequest():
+class HTTPRequest(object):
def __init__(self, cookies=None, options=None):
self.c = pycurl.Curl()
self.rep = StringIO()
@@ -79,8 +67,9 @@ class HTTPRequest():
if hasattr(pycurl, "AUTOREFERER"):
self.c.setopt(pycurl.AUTOREFERER, 1)
self.c.setopt(pycurl.SSL_VERIFYPEER, 0)
- self.c.setopt(pycurl.LOW_SPEED_TIME, 30)
+ self.c.setopt(pycurl.LOW_SPEED_TIME, 60)
self.c.setopt(pycurl.LOW_SPEED_LIMIT, 5)
+ self.c.setopt(pycurl.USE_SSL, pycurl.CURLUSESSL_TRY)
#self.c.setopt(pycurl.VERBOSE, 1)
@@ -89,8 +78,8 @@ class HTTPRequest():
if pycurl.version_info()[7]:
self.c.setopt(pycurl.ENCODING, "gzip, deflate")
self.c.setopt(pycurl.HTTPHEADER, ["Accept: */*",
- "Accept-Language: en-US,en",
- "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7",
+ "Accept-Language: en-US, en",
+ "Accept-Charset: ISO-8859-1, utf-8;q=0.7,*;q=0.7",
"Connection: keep-alive",
"Keep-Alive: 300",
"Expect:"])
@@ -167,7 +156,7 @@ class HTTPRequest():
self.c.setopt(pycurl.POSTFIELDS, post)
else:
- post = [(x, y.encode('utf8') if type(y) == unicode else y ) for x, y in post.iteritems()]
+ post = [(x, encode(y)) for x, y in post.iteritems()]
self.c.setopt(pycurl.HTTPPOST, post)
else:
self.c.setopt(pycurl.POST, 0)
@@ -181,7 +170,7 @@ class HTTPRequest():
self.getCookies()
- def load(self, url, get={}, post={}, referer=True, cookies=True, just_header=False, multipart=False, decode=False):
+ def load(self, url, get={}, post={}, referer=True, cookies=True, just_header=False, multipart=False, decode=False, follow_location=True, save_cookies=True):
""" load and returns a given page """
self.setRequestContext(url, get, post, referer, cookies, multipart)
@@ -190,24 +179,32 @@ class HTTPRequest():
self.c.setopt(pycurl.HTTPHEADER, self.headers)
- if just_header:
+ if post:
+ self.c.setopt(pycurl.POST, 1)
+ else:
+ self.c.setopt(pycurl.HTTPGET, 1)
+
+ if not follow_location:
self.c.setopt(pycurl.FOLLOWLOCATION, 0)
+
+ if just_header:
self.c.setopt(pycurl.NOBODY, 1)
- self.c.perform()
- rep = self.header
+ self.c.perform()
+ rep = self.header if just_header else self.getResponse()
+
+ if not follow_location:
self.c.setopt(pycurl.FOLLOWLOCATION, 1)
- self.c.setopt(pycurl.NOBODY, 0)
- else:
- self.c.perform()
- rep = self.getResponse()
+ if just_header:
+ self.c.setopt(pycurl.NOBODY, 0)
self.c.setopt(pycurl.POSTFIELDS, "")
self.lastEffectiveURL = self.c.getinfo(pycurl.EFFECTIVE_URL)
self.code = self.verifyHeader()
- self.addCookies()
+ if save_cookies:
+ self.addCookies()
if decode:
rep = self.decodeResponse(rep)
@@ -228,11 +225,13 @@ class HTTPRequest():
def getResponse(self):
""" retrieve response from string io """
- if self.rep is None: return ""
- value = self.rep.getvalue()
- self.rep.close()
- self.rep = StringIO()
- return value
+ if self.rep is None:
+ return ""
+ else:
+ value = self.rep.getvalue()
+ self.rep.close()
+ self.rep = StringIO()
+ return value
def decodeResponse(self, rep):
""" decode with correct encoding, relies on header """
@@ -255,7 +254,7 @@ class HTTPRequest():
#self.log.debug("Decoded %s" % encoding )
if lookup(encoding).name == 'utf-8' and rep.startswith(BOM_UTF8):
encoding = 'utf-8-sig'
-
+
decoder = getincrementaldecoder(encoding)("replace")
rep = decoder.decode(rep, True)
@@ -263,6 +262,7 @@ class HTTPRequest():
except LookupError:
self.log.debug("No Decoder foung for %s" % encoding)
+
except Exception:
self.log.debug("Error when decoding string from %s." % encoding)
@@ -272,13 +272,15 @@ class HTTPRequest():
""" writes response """
if self.rep.tell() > 1000000 or self.abort:
rep = self.getResponse()
- if self.abort: raise Abort()
- f = open("response.dump", "wb")
- f.write(rep)
- f.close()
- raise Exception("Loaded Url exceeded limit")
- self.rep.write(buf)
+ if self.abort:
+ raise Abort
+
+ with open("response.dump", "wb") as f:
+ f.write(rep)
+ raise Fail("Loaded url exceeded size limit")
+ else:
+ self.rep.write(buf)
def writeHeader(self, buf):
""" writes header """
@@ -298,9 +300,3 @@ class HTTPRequest():
if hasattr(self, "c"):
self.c.close()
del self.c
-
-if __name__ == "__main__":
- url = "http://pyload.org"
- c = HTTPRequest()
- print c.load(url)
-
diff --git a/module/network/JsEngine.py b/module/network/JsEngine.py
new file mode 100644
index 000000000..2e98fa37d
--- /dev/null
+++ b/module/network/JsEngine.py
@@ -0,0 +1,256 @@
+# -*- coding: utf-8 -*-
+# @author: vuolter
+
+import subprocess
+import sys
+
+from os import path
+from urllib import quote
+
+from pyload.utils import encode, decode, uniqify
+
+
+class JsEngine(object):
+ """ JS Engine superclass """
+
+ def __init__(self, core, engine=None):
+ self.core = core
+ self.engine = None #: Engine Instance
+
+ if not engine:
+ engine = self.core.config.get("general", "jsengine")
+
+ if engine != "auto" and self.set(engine) is False:
+ engine = "auto"
+ self.core.log.warning("JS Engine set to \"auto\" for safely")
+
+ if engine == "auto":
+ for E in self.find():
+ if self.set(E) is True:
+ break
+ else:
+ self.core.log.error("No JS Engine available")
+
+
+ @classmethod
+ def find(cls):
+ """ Check if there is any engine available """
+ return [E for E in ENGINES if E.find()]
+
+
+ def get(self, engine=None):
+ """ Convert engine name (string) to relative JSE class (AbstractEngine extended) """
+ if not engine:
+ JSE = self.engine
+
+ elif isinstance(engine, basestring):
+ engine_name = engine.lower()
+ for E in ENGINES:
+ if E.__name == engine_name: #: doesn't check if E(NGINE) is available, just convert string to class
+ JSE = E
+ break
+ else:
+ raise ValueError("JSE")
+
+ elif issubclass(engine, AbstractEngine):
+ JSE = engine
+
+ else:
+ raise TypeError("engine")
+
+ return JSE
+
+
+ def set(self, engine):
+ """ Set engine name (string) or JSE class (AbstractEngine extended) as default engine """
+ if isinstance(engine, basestring):
+ return self.set(self.get(engine))
+
+ elif issubclass(engine, AbstractEngine) and engine.find():
+ self.engine = engine
+ return True
+
+ else:
+ return False
+
+
+ def eval(self, script, engine=None): #: engine can be a jse name """string""" or an AbstractEngine """class"""
+ JSE = self.get(engine)
+
+ if not JSE:
+ raise TypeError("engine")
+
+ script = encode(script)
+
+ out, err = JSE.eval(script)
+
+ results = [out]
+
+ if self.core.config.get("general", "debug"):
+ if err:
+ self.core.log.debug(JSE.__name + ":", err)
+
+ engines = self.find()
+ engines.remove(JSE)
+ for E in engines:
+ out, err = E.eval(script)
+ res = err or out
+ self.core.log.debug(E.__name + ":", res)
+ results.append(res)
+
+ if len(results) > 1 and len(uniqify(results)) > 1:
+ self.core.log.warning("JS output of two or more engines mismatch")
+
+ return results[0]
+
+
+class AbstractEngine(object):
+ """ JSE base class """
+
+ __name = ""
+
+
+ def __init__(self):
+ self.setup()
+ self.available = self.find()
+
+
+ def setup(self):
+ pass
+
+
+ @classmethod
+ def find(cls):
+ """ Check if the engine is available """
+ try:
+ __import__(cls.__name)
+ except Exception:
+ try:
+ out, err = cls().eval("print(23+19)")
+ except Exception:
+ res = False
+ else:
+ res = out == "42"
+ else:
+ res = True
+
+ return res
+
+
+ def _eval(args):
+ if not self.available:
+ return None, "JS Engine \"%s\" not found" % self.__name
+
+ try:
+ p = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ bufsize=-1)
+ return map(lambda x: x.strip(), p.communicate())
+ except Exception, e:
+ return None, e
+
+
+ def eval(script):
+ raise NotImplementedError
+
+
+class Pyv8Engine(AbstractEngine):
+
+ __name = "pyv8"
+
+
+ def eval(self, script):
+ if not self.available:
+ return None, "JS Engine \"%s\" not found" % self.__name
+
+ try:
+ rt = PyV8.JSContext()
+ rt.enter()
+ res = rt.eval(script), None #@TODO: parse stderr
+ except Exception, e:
+ res = None, e
+
+ return res
+
+
+class CommonEngine(AbstractEngine):
+
+ __name = "js"
+
+
+ def setup(self):
+ subprocess.Popen(["js", "-v"], bufsize=-1).communicate()
+
+
+ def eval(self, script):
+ script = "print(eval(unescape('%s')))" % quote(script)
+ args = ["js", "-e", script]
+ return self._eval(args)
+
+
+class NodeEngine(AbstractEngine):
+
+ __name = "nodejs"
+
+
+ def setup(self):
+ subprocess.Popen(["node", "-v"], bufsize=-1).communicate()
+
+
+ def eval(self, script):
+ script = "console.log(eval(unescape('%s')))" % quote(script)
+ args = ["node", "-e", script]
+ return self._eval(args)
+
+
+class RhinoEngine(AbstractEngine):
+
+ __name = "rhino"
+
+
+ def setup(self):
+ jspath = [
+ "/usr/share/java*/js.jar",
+ "js.jar",
+ path.join(pypath, "js.jar")
+ ]
+ for p in jspath:
+ if path.exists(p):
+ self.path = p
+ break
+ else:
+ self.path = ""
+
+
+ def eval(self, script):
+ script = "print(eval(unescape('%s')))" % quote(script)
+ args = ["java", "-cp", self.path, "org.mozilla.javascript.tools.shell.Main", "-e", script]
+ res = decode(self._eval(args))
+ try:
+ return res.encode("ISO-8859-1")
+ finally:
+ return res
+
+
+class JscEngine(AbstractEngine):
+
+ __name = "javascriptcore"
+
+
+ def setup(self):
+ jspath = "/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc"
+ self.path = jspath if path.exists(jspath) else ""
+
+
+ def eval(self, script):
+ script = "print(eval(unescape('%s')))" % quote(script)
+ args = [self.path, "-e", script]
+ return self._eval(args)
+
+
+#@NOTE: Priority ordered
+ENGINES = [CommonEngine, Pyv8Engine, NodeEngine, RhinoEngine]
+
+if sys.platform == "darwin":
+ ENGINES.insert(JscEngine)
diff --git a/module/network/RequestFactory.py b/module/network/RequestFactory.py
index 5b1528281..579eafea7 100644
--- a/module/network/RequestFactory.py
+++ b/module/network/RequestFactory.py
@@ -1,32 +1,16 @@
# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay, RaNaN
-"""
+# @author: RaNaN, mkaay
from threading import Lock
-from Browser import Browser
-from Bucket import Bucket
-from HTTPRequest import HTTPRequest
-from CookieJar import CookieJar
+from pyload.network.Browser import Browser
+from pyload.network.Bucket import Bucket
+from pyload.network.HTTPRequest import HTTPRequest
+from pyload.network.CookieJar import CookieJar
-from XDCCRequest import XDCCRequest
+from pyload.network.XDCCRequest import XDCCRequest
-class RequestFactory():
+class RequestFactory(object):
def __init__(self, core):
self.lock = Lock()
self.core = core
@@ -62,12 +46,23 @@ class RequestFactory():
def getURL(self, *args, **kwargs):
""" see HTTPRequest for argument list """
- h = HTTPRequest(None, self.getOptions())
+ cj = None
+
+ if 'cookies' in kwargs:
+ if isinstance(kwargs['cookies'], CookieJar):
+ cj = kwargs['cookies']
+ elif isinstance(kwargs['cookies'], list):
+ cj = CookieJar(None)
+ for cookie in kwargs['cookies']:
+ if isinstance(cookie, tuple) and len(cookie) == 3:
+ cj.setCookie(*cookie)
+
+ h = HTTPRequest(cj, self.getOptions())
try:
rep = h.load(*args, **kwargs)
finally:
h.close()
-
+
return rep
def getCookieJar(self, pluginName, account=None):
diff --git a/module/network/XDCCRequest.py b/module/network/XDCCRequest.py
index f03798c17..c49f418c4 100644
--- a/module/network/XDCCRequest.py
+++ b/module/network/XDCCRequest.py
@@ -1,21 +1,5 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: jeix
-"""
+# @author: jeix
import socket
import re
@@ -28,22 +12,21 @@ from time import time
import struct
from select import select
-from module.plugins.Plugin import Abort
+from pyload.plugin.Plugin import Abort
-class XDCCRequest():
+class XDCCRequest(object):
def __init__(self, timeout=30, proxies={}):
-
+
self.proxies = proxies
self.timeout = timeout
-
+
self.filesize = 0
self.recv = 0
self.speed = 0
-
+
self.abort = False
-
def createSocket(self):
# proxytype = None
# proxy = None
@@ -60,78 +43,77 @@ class XDCCRequest():
# else:
# sock = socket.socket()
# return sock
-
+
return socket.socket()
-
- def download(self, ip, port, filename, irc, progressNotify=None):
+
+ def download(self, ip, port, filename, irc, progress=None):
ircbuffer = ""
lastUpdate = time()
cumRecvLen = 0
-
+
dccsock = self.createSocket()
-
+
dccsock.settimeout(self.timeout)
dccsock.connect((ip, port))
-
+
if exists(filename):
i = 0
nameParts = filename.rpartition(".")
while True:
newfilename = "%s-%d%s%s" % (nameParts[0], i, nameParts[1], nameParts[2])
i += 1
-
+
if not exists(newfilename):
filename = newfilename
break
-
+
fh = open(filename, "wb")
-
+
# recv loop for dcc socket
while True:
if self.abort:
dccsock.close()
fh.close()
remove(filename)
- raise Abort()
-
+ raise Abort
+
self._keepAlive(irc, ircbuffer)
-
+
data = dccsock.recv(4096)
dataLen = len(data)
self.recv += dataLen
-
+
cumRecvLen += dataLen
-
+
now = time()
timespan = now - lastUpdate
- if timespan > 1:
+ if timespan > 1:
self.speed = cumRecvLen / timespan
cumRecvLen = 0
lastUpdate = now
-
- if progressNotify:
- progressNotify(self.percent)
-
-
+
+ if progress:
+ progress(self.percent)
+
if not data:
break
-
+
fh.write(data)
-
+
# acknowledge data by sending number of recceived bytes
dccsock.send(struct.pack('!I', self.recv))
-
+
dccsock.close()
fh.close()
-
+
return filename
-
- def _keepAlive(self, sock, readbuffer):
+
+ def _keepAlive(self, sock, *readbuffer):
fdset = select([sock], [], [], 0)
if sock not in fdset[0]:
return
-
+
readbuffer += sock.recv(1024)
temp = readbuffer.split("\n")
readbuffer = temp.pop()
@@ -144,7 +126,7 @@ class XDCCRequest():
def abortDownloads(self):
self.abort = True
-
+
@property
def size(self):
return self.filesize
diff --git a/module/network/__init__.py b/module/network/__init__.py
index 8b1378917..40a96afc6 100644
--- a/module/network/__init__.py
+++ b/module/network/__init__.py
@@ -1 +1 @@
-
+# -*- coding: utf-8 -*-
diff --git a/module/plugins/Account.py b/module/plugins/Account.py
index c147404e0..f8014908f 100644
--- a/module/plugins/Account.py
+++ b/module/plugins/Account.py
@@ -1,29 +1,13 @@
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
from random import choice
from time import time
from traceback import print_exc
from threading import RLock
-from Plugin import Base
-from module.utils import compare_time, parseFileSize, lock
+from pyload.plugin.Plugin import Base
+from pyload.utils import compare_time, parseFileSize, lock
+
class WrongPassword(Exception):
pass
@@ -35,17 +19,19 @@ class Account(Base):
Just overwrite `login` and cookies will be stored and account becomes accessible in\
associated hoster plugin. Plugin should also provide `loadAccountInfo`
"""
- __name__ = "Account"
- __version__ = "0.2"
- __type__ = "account"
- __description__ = """Account Plugin"""
- __author_name__ = ("mkaay")
- __author_mail__ = ("mkaay@mkaay.de")
+ __name__ = "Account"
+ __type__ = "account"
+ __version__ = "0.03"
+
+ __description__ = """Base account plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("mkaay", "mkaay@mkaay.de")]
- #: after that time [in minutes] pyload will relogin the account
- login_timeout = 600
- #: account data will be reloaded after this time
- info_threshold = 600
+
+ #: after that time (in minutes) pyload will relogin the account
+ login_timeout = 10 * 60
+ #: after that time (in minutes) account data will be reloaded
+ info_threshold = 10 * 60
def __init__(self, manager, accounts):
@@ -53,16 +39,19 @@ class Account(Base):
self.manager = manager
self.accounts = {}
- self.infos = {} # cache for account information
+ self.infos = {} #: cache for account information
self.lock = RLock()
-
self.timestamps = {}
- self.setAccounts(accounts)
+
self.init()
+ self.setAccounts(accounts)
+
+
def init(self):
pass
+
def login(self, user, data, req):
"""login into account, the cookies will be saved so user can be recognized
@@ -72,29 +61,34 @@ class Account(Base):
"""
pass
+
@lock
def _login(self, user, data):
# set timestamp for login
self.timestamps[user] = time()
-
+
req = self.getAccountRequest(user)
try:
self.login(user, data, req)
except WrongPassword:
self.logWarning(
- _("Could not login with account %(user)s | %(msg)s") % {"user": user
- , "msg": _("Wrong Password")})
- data["valid"] = False
-
+ _("Could not login with account %(user)s | %(msg)s") % {"user": user,
+ "msg": _("Wrong Password")})
+ success = data['valid'] = False
except Exception, e:
self.logWarning(
- _("Could not login with account %(user)s | %(msg)s") % {"user": user
- , "msg": e})
- data["valid"] = False
+ _("Could not login with account %(user)s | %(msg)s") % {"user": user,
+ "msg": e})
+ success = data['valid'] = False
if self.core.debug:
print_exc()
+ else:
+ success = True
finally:
- if req: req.close()
+ if req:
+ req.close()
+ return success
+
def relogin(self, user):
req = self.getAccountRequest(user)
@@ -104,7 +98,8 @@ class Account(Base):
if user in self.infos:
del self.infos[user] #delete old information
- self._login(user, self.accounts[user])
+ return self._login(user, self.accounts[user])
+
def setAccounts(self, accounts):
self.accounts = accounts
@@ -112,24 +107,26 @@ class Account(Base):
self._login(user, data)
self.infos[user] = {}
+
def updateAccounts(self, user, password=None, options={}):
""" updates account and return true if anything changed """
if user in self.accounts:
- self.accounts[user]["valid"] = True #do not remove or accounts will not login
+ self.accounts[user]['valid'] = True #do not remove or accounts will not login
if password:
- self.accounts[user]["password"] = password
+ self.accounts[user]['password'] = password
self.relogin(user)
return True
if options:
- before = self.accounts[user]["options"]
- self.accounts[user]["options"].update(options)
- return self.accounts[user]["options"] != before
+ before = self.accounts[user]['options']
+ self.accounts[user]['options'].update(options)
+ return self.accounts[user]['options'] != before
else:
self.accounts[user] = {"password": password, "options": options, "valid": True}
self._login(user, self.accounts[user])
return True
+
def removeAccount(self, user):
if user in self.accounts:
del self.accounts[user]
@@ -138,6 +135,7 @@ class Account(Base):
if user in self.timestamps:
del self.timestamps[user]
+
@lock
def getAccountInfo(self, name, force=False):
"""retrieve account infos for an user, do **not** overwrite this method!\\
@@ -159,12 +157,14 @@ class Account(Base):
raise Exception("Wrong return format")
except Exception, e:
infos = {"error": str(e)}
+ print_exc()
- if req: req.close()
+ if req:
+ req.close()
- self.logDebug("Account Info: %s" % str(infos))
+ self.logDebug("Account Info: %s" % infos)
- infos["timestamp"] = time()
+ infos['timestamp'] = time()
self.infos[name] = infos
elif "timestamp" in self.infos[name] and self.infos[name][
"timestamp"] + self.info_threshold * 60 < time():
@@ -174,9 +174,11 @@ class Account(Base):
data.update(self.infos[name])
return data
+
def isPremium(self, user):
info = self.getAccountInfo(user)
- return info["premium"]
+ return info['premium']
+
def loadAccountInfo(self, name, req=None):
"""this should be overwritten in account plugin,\
@@ -186,22 +188,22 @@ class Account(Base):
:param req: `Request` instance
:return:
"""
- return {
- "validuntil": None, # -1 for unlimited
- "login": name,
- #"password": self.accounts[name]["password"], #@XXX: security
- "options": self.accounts[name]["options"],
- "valid": self.accounts[name]["valid"],
- "trafficleft": None, # in kb, -1 for unlimited
- "maxtraffic": None,
- "premium": True, #useful for free accounts
- "timestamp": 0, #time this info was retrieved
- "type": self.__name__,
- }
+ return {"validuntil" : None, #: -1 for unlimited
+ "login" : name,
+ # "password" : self.accounts[name]['password'], #: commented due security reason
+ "options" : self.accounts[name]['options'],
+ "valid" : self.accounts[name]['valid'],
+ "trafficleft": None, #: in bytes, -1 for unlimited
+ "maxtraffic" : None,
+ "premium" : None,
+ "timestamp" : 0, #: time this info was retrieved
+ "type" : self.__name__}
+
def getAllAccounts(self, force=False):
return [self.getAccountInfo(user, force) for user, data in self.accounts.iteritems()]
+
def getAccountRequest(self, user=None):
if not user:
user, data = self.selectAccount()
@@ -211,6 +213,7 @@ class Account(Base):
req = self.core.requestFactory.getRequest(self.__name__, user)
return req
+
def getAccountCookies(self, user=None):
if not user:
user, data = self.selectAccount()
@@ -220,31 +223,33 @@ class Account(Base):
cj = self.core.requestFactory.getCookieJar(self.__name__, user)
return cj
+
def getAccountData(self, user):
return self.accounts[user]
+
def selectAccount(self):
""" returns an valid account name and data"""
usable = []
for user, data in self.accounts.iteritems():
- if not data["valid"]: continue
+ if not data['valid']: continue
- if "time" in data["options"] and data["options"]["time"]:
+ if "time" in data['options'] and data['options']['time']:
time_data = ""
try:
- time_data = data["options"]["time"][0]
+ time_data = data['options']['time'][0]
start, end = time_data.split("-")
if not compare_time(start.split(":"), end.split(":")):
continue
- except:
+ except Exception:
self.logWarning(_("Your Time %s has wrong format, use: 1:22-3:44") % time_data)
if user in self.infos:
if "validuntil" in self.infos[user]:
- if self.infos[user]["validuntil"] > 0 and time() > self.infos[user]["validuntil"]:
+ if self.infos[user]['validuntil'] > 0 and time() > self.infos[user]['validuntil']:
continue
if "trafficleft" in self.infos[user]:
- if self.infos[user]["trafficleft"] == 0:
+ if self.infos[user]['trafficleft'] == 0:
continue
usable.append((user, data))
@@ -252,15 +257,21 @@ class Account(Base):
if not usable: return None, None
return choice(usable)
+
def canUse(self):
return False if self.selectAccount() == (None, None) else True
- def parseTraffic(self, string): #returns kbyte
- return parseFileSize(string) / 1024
+
+ def parseTraffic(self, value, unit=None): #: return bytes
+ if not unit and not isinstance(value, basestring):
+ unit = "KB"
+ return parseFileSize(value, unit)
+
def wrongPassword(self):
raise WrongPassword
+
def empty(self, user):
if user in self.infos:
self.logWarning(_("Account %s has not enough traffic, checking again in 30min") % user)
@@ -268,6 +279,7 @@ class Account(Base):
self.infos[user].update({"trafficleft": 0})
self.scheduleRefresh(user, 30 * 60)
+
def expired(self, user):
if user in self.infos:
self.logWarning(_("Account %s is expired, checking again in 1h") % user)
@@ -275,18 +287,21 @@ class Account(Base):
self.infos[user].update({"validuntil": time() - 1})
self.scheduleRefresh(user, 60 * 60)
+
def scheduleRefresh(self, user, time=0, force=True):
""" add task to refresh account info to sheduler """
self.logDebug("Scheduled Account refresh for %s in %s seconds." % (user, time))
self.core.scheduler.addJob(time, self.getAccountInfo, [user, force])
+
@lock
def checkLogin(self, user):
""" checks if user is still logged in """
if user in self.timestamps:
- if self.timestamps[user] + self.login_timeout * 60 < time():
+ if self.login_timeout > 0 and self.timestamps[user] + self.login_timeout * 60 < time():
self.logDebug("Reached login timeout for %s" % user)
- self.relogin(user)
- return False
-
- return True
+ return self.relogin(user)
+ else:
+ return True
+ else:
+ return False
diff --git a/module/plugins/Addon.py b/module/plugins/Addon.py
new file mode 100644
index 000000000..ca36fe4ae
--- /dev/null
+++ b/module/plugins/Addon.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+
+from traceback import print_exc
+
+from pyload.plugin.Plugin import Base
+from pyload.utils import has_method
+
+
+class Expose(object):
+ """ used for decoration to declare rpc services """
+
+ def __new__(cls, f, *args, **kwargs):
+ addonManager.addRPC(f.__module__, f.func_name, f.func_doc)
+ return f
+
+
+def threaded(fn):
+
+ def run(*args,**kwargs):
+ addonManager.startThread(fn, *args, **kwargs)
+
+ return run
+
+
+class Addon(Base):
+ """
+ Base class for addon plugins.
+ """
+ __name__ = "Addon"
+ __type__ = "addon"
+ __version__ = "0.03"
+
+ __config__ = [] #: [("name", "type", "desc", "default")]
+
+ __description__ = """Base addon/hook plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("mkaay", "mkaay@mkaay.de"),
+ ("RaNaN", "RaNaN@pyload.org")]
+
+
+ #: automatically register event listeners for functions, attribute will be deleted dont use it yourself
+ event_map = {}
+
+ # Deprecated alternative to event_map
+ #: List of events the plugin can handle, name the functions exactly like eventname.
+ event_list = [] #@NOTE: dont make duplicate entries in event_map
+
+
+ def __init__(self, core, manager):
+ Base.__init__(self, core)
+
+ #: Provide information in dict here, usable by API `getInfo`
+ self.info = {}
+
+ #: Callback of periodical job task, used by AddonManager
+ self.cb = None
+ self.interval = 60
+
+ #: `AddonManager`
+ self.manager = manager
+
+ #register events
+ if self.event_map:
+ for event, funcs in self.event_map.iteritems():
+ if type(funcs) in (list, tuple):
+ for f in funcs:
+ self.manager.addEvent(event, getattr(self,f))
+ else:
+ self.manager.addEvent(event, getattr(self,funcs))
+
+ #delete for various reasons
+ self.event_map = None
+
+ if self.event_list:
+ for f in self.event_list:
+ self.manager.addEvent(f, getattr(self,f))
+
+ self.event_list = None
+
+ self.setup()
+
+ # self.initPeriodical()
+
+
+ def initPeriodical(self, delay=0, threaded=False):
+ self.cb = self.core.scheduler.addJob(delay, self._periodical, args=[threaded], threaded=threaded)
+
+
+ def _periodical(self, threaded):
+ if self.interval < 0:
+ self.cb = None
+ return
+
+ try:
+ self.periodical()
+
+ except Exception, e:
+ self.logError(_("Error executing addon: %s") % e)
+ if self.core.debug:
+ print_exc()
+
+ self.cb = self.core.scheduler.addJob(self.interval, self._periodical, threaded=threaded)
+
+
+ def __repr__(self):
+ return "<Addon %s>" % self.__name__
+
+
+ def setup(self):
+ """ more init stuff if needed """
+ pass
+
+
+ def deactivate(self):
+ """ called when addon was deactivated """
+ if has_method(self.__class__, "unload"):
+ self.unload()
+
+ def unload(self): # Deprecated, use method deactivate() instead
+ pass
+
+
+ def isActivated(self):
+ """ checks if addon is activated"""
+ return self.core.config.getPlugin(self.__name__, "activated")
+
+
+ # Event methods - overwrite these if needed
+ def activate(self):
+ """ called when addon was activated """
+ if has_method(self.__class__, "coreReady"):
+ self.coreReady()
+
+ def coreReady(self): # Deprecated, use method activate() instead
+ pass
+
+
+ def exit(self):
+ """ called by core.shutdown just before pyLoad exit """
+ if has_method(self.__class__, "coreExiting"):
+ self.coreExiting()
+
+ def coreExiting(self): # Deprecated, use method exit() instead
+ pass
+
+
+ def downloadPreparing(self, pyfile):
+ pass
+
+
+ def downloadFinished(self, pyfile):
+ pass
+
+
+ def downloadFailed(self, pyfile):
+ pass
+
+
+ def packageFinished(self, pypack):
+ pass
+
+
+ def beforeReconnecting(self, ip):
+ pass
+
+
+ def afterReconnecting(self, ip):
+ pass
+
+
+ def periodical(self):
+ pass
+
+
+ def captchaTask(self, task):
+ """ new captcha task for the plugin, it MUST set the handler and timeout or will be ignored """
+ pass
+
+
+ def captchaCorrect(self, task):
+ pass
+
+
+ def captchaInvalid(self, task):
+ pass
diff --git a/module/plugins/Captcha.py b/module/plugins/Captcha.py
new file mode 100644
index 000000000..ace488994
--- /dev/null
+++ b/module/plugins/Captcha.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from pyload.plugin.Plugin import Plugin
+
+
+class Captcha(Plugin):
+ __name__ = "Captcha"
+ __type__ = "captcha"
+ __version__ = "0.14"
+
+ __description__ = """Base captcha service plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("pyLoad Team", "admin@pyload.org")]
+
+
+ KEY_PATTERN = None
+
+ key = None #: last key detected
+
+
+ def __init__(self, plugin):
+ self.plugin = plugin
+
+
+ def detect_key(self, html=None):
+ if not html:
+ if hasattr(self.plugin, "html") and self.plugin.html:
+ html = self.plugin.html
+ else:
+ errmsg = _("%s html not found") % self.__name__
+ self.plugin.error(errmsg)
+ raise TypeError(errmsg)
+
+ m = re.search(self.KEY_PATTERN, html)
+ if m:
+ self.key = m.group("KEY")
+ self.plugin.logDebug("%s key: %s" % (self.__name__, self.key))
+ return self.key
+ else:
+ self.plugin.logDebug("%s key not found" % self.__name__)
+ return None
+
+
+ def challenge(self, key=None):
+ raise NotImplementedError
+
+
+ def result(self, server, challenge):
+ raise NotImplementedError
diff --git a/module/plugins/Container.py b/module/plugins/Container.py
index c233d3710..bfc5713a7 100644
--- a/module/plugins/Container.py
+++ b/module/plugins/Container.py
@@ -1,36 +1,27 @@
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from module.plugins.Crypter import Crypter
-
-from os.path import join, exists, basename
-from os import remove
+from __future__ import with_statement
+
import re
+from os import remove
+from os.path import basename, exists
+
+from pyload.plugin.internal.Crypter import Crypter
+from pyload.utils import safe_join
+
+
class Container(Crypter):
- __name__ = "Container"
- __version__ = "0.1"
- __pattern__ = None
- __type__ = "container"
- __description__ = """Base container plugin"""
- __author_name__ = ("mkaay")
- __author_mail__ = ("mkaay@mkaay.de")
+ __name__ = "Container"
+ __type__ = "container"
+ __version__ = "0.01"
+
+ __pattern__ = r'^unmatchable$'
+ __config__ = [] #: [("name", "type", "desc", "default")]
+
+ __description__ = """Base container decrypter plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("mkaay", "mkaay@mkaay.de")]
def preprocessing(self, thread):
@@ -38,38 +29,38 @@ class Container(Crypter):
self.setup()
self.thread = thread
-
+
self.loadToDisk()
self.decrypt(self.pyfile)
self.deleteTmp()
-
+
self.createPackages()
-
+
def loadToDisk(self):
- """loads container to disk if its stored remotely and overwrite url,
+ """loads container to disk if its stored remotely and overwrite url,
or check existent on several places at disk"""
-
+
if self.pyfile.url.startswith("http"):
self.pyfile.name = re.findall("([^\/=]+)", self.pyfile.url)[-1]
content = self.load(self.pyfile.url)
- self.pyfile.url = join(self.config["general"]["download_folder"], self.pyfile.name)
- f = open(self.pyfile.url, "wb" )
- f.write(content)
- f.close()
-
+ self.pyfile.url = safe_join(self.core.config['general']['download_folder'], self.pyfile.name)
+ try:
+ with open(self.pyfile.url, "wb") as f:
+ f.write(content)
+ except IOError, e:
+ self.fail(str(e))
+
else:
self.pyfile.name = basename(self.pyfile.url)
if not exists(self.pyfile.url):
- if exists(join(pypath, self.pyfile.url)):
- self.pyfile.url = join(pypath, self.pyfile.url)
+ if exists(safe_join(pypath, self.pyfile.url)):
+ self.pyfile.url = safe_join(pypath, self.pyfile.url)
else:
- self.fail(_("File not exists."))
-
+ self.fail(_("File not exists"))
+
def deleteTmp(self):
if self.pyfile.name.startswith("tmp_"):
remove(self.pyfile.url)
-
-
diff --git a/module/plugins/Crypter.py b/module/plugins/Crypter.py
index d1549fe80..f93ee254a 100644
--- a/module/plugins/Crypter.py
+++ b/module/plugins/Crypter.py
@@ -1,72 +1,107 @@
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
+from urlparse import urlparse
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
+from pyload.plugin.Plugin import Plugin
+from pyload.utils import decode, safe_filename
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from module.plugins.Plugin import Plugin
class Crypter(Plugin):
- __name__ = "Crypter"
- __version__ = "0.1"
- __pattern__ = None
- __type__ = "container"
- __description__ = """Base crypter plugin"""
- __author_name__ = ("mkaay")
- __author_mail__ = ("mkaay@mkaay.de")
-
+ __name__ = "Crypter"
+ __type__ = "crypter"
+ __version__ = "0.05"
+
+ __pattern__ = r'^unmatchable$'
+ __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), #: Overrides core.config['general']['folder_per_package']
+ ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
+
+ __description__ = """Base decrypter plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ html = None #: last html loaded
+
+
def __init__(self, pyfile):
- Plugin.__init__(self, pyfile)
-
#: Put all packages here. It's a list of tuples like: ( name, [list of links], folder )
self.packages = []
#: List of urls, pyLoad will generate packagenames
self.urls = []
-
- self.multiDL = True
- self.limitDL = 0
-
-
- def preprocessing(self, thread):
- """prepare"""
- self.setup()
- self.thread = thread
-
- self.decrypt(self.pyfile)
-
+
+ Plugin.__init__(self, pyfile)
+
+
+ def process(self, pyfile):
+ """ main method """
+
+ self.decrypt(pyfile)
+
+ if self.urls:
+ self.generatePackages()
+
+ elif not self.packages:
+ self.error(_("No link extracted"), "decrypt")
+
self.createPackages()
-
+
def decrypt(self, pyfile):
raise NotImplementedError
+
+ def generatePackages(self):
+ """ generate new packages from self.urls """
+
+ packages = map(lambda name, links: (name, links, None), self.core.api.generatePackages(self.urls).iteritems())
+ self.packages.extend(packages)
+
+
def createPackages(self):
""" create new packages from self.packages """
+
+ package_folder = self.pyfile.package().folder
+ package_password = self.pyfile.package().password
+ package_queue = self.pyfile.package().queue
+
+ folder_per_package = self.core.config['general']['folder_per_package']
+ try:
+ use_subfolder = self.getConfig('use_subfolder')
+ except Exception:
+ use_subfolder = folder_per_package
+ try:
+ subfolder_per_package = self.getConfig('subfolder_per_package')
+ except Exception:
+ subfolder_per_package = True
+
for pack in self.packages:
+ name, links, folder = pack
- self.log.debug("Parsed package %(name)s with %(len)d links" % { "name" : pack[0], "len" : len(pack[1]) } )
-
- links = [x.decode("utf-8") for x in pack[1]]
-
- pid = self.core.api.addPackage(pack[0], links, self.pyfile.package().queue)
+ self.logDebug("Parsed package: %s" % name,
+ "%d links" % len(links),
+ "Saved to folder: %s" % folder if folder else "Saved to download folder")
- if self.pyfile.package().password:
- self.core.api.setPackageData(pid, {"password": self.pyfile.package().password})
+ links = map(decode, links)
- if self.urls:
- self.core.api.generateAndAddPackages(self.urls)
-
+ pid = self.core.api.addPackage(name, links, package_queue)
+
+ if package_password:
+ self.core.api.setPackageData(pid, {"password": package_password})
+
+ setFolder = lambda x: self.core.api.setPackageData(pid, {"folder": x or ""}) #: Workaround to do not break API addPackage method
+
+ if use_subfolder:
+ if not subfolder_per_package:
+ setFolder(package_folder)
+ self.logDebug("Set package %(name)s folder to: %(folder)s" % {"name": name, "folder": folder})
+
+ elif not folder_per_package or name != folder:
+ if not folder:
+ folder = urlparse(name).path.split("/")[-1]
+
+ setFolder(safe_filename(folder))
+ self.logDebug("Set package %(name)s folder to: %(folder)s" % {"name": name, "folder": folder})
+
+ elif folder_per_package:
+ setFolder(None)
diff --git a/module/plugins/Hook.py b/module/plugins/Hook.py
deleted file mode 100644
index 5efd08bae..000000000
--- a/module/plugins/Hook.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
- @interface-version: 0.2
-"""
-
-from traceback import print_exc
-
-from Plugin import Base
-
-class Expose(object):
- """ used for decoration to declare rpc services """
-
- def __new__(cls, f, *args, **kwargs):
- hookManager.addRPC(f.__module__, f.func_name, f.func_doc)
- return f
-
-def threaded(f):
- def run(*args,**kwargs):
- hookManager.startThread(f, *args, **kwargs)
- return run
-
-class Hook(Base):
- """
- Base class for hook plugins.
- """
- __name__ = "Hook"
- __version__ = "0.2"
- __type__ = "hook"
- __threaded__ = []
- __config__ = [ ("name", "type", "desc" , "default") ]
- __description__ = """interface for hook"""
- __author_name__ = ("mkaay", "RaNaN")
- __author_mail__ = ("mkaay@mkaay.de", "RaNaN@pyload.org")
-
- #: automatically register event listeners for functions, attribute will be deleted dont use it yourself
- event_map = None
-
- # Alternative to event_map
- #: List of events the plugin can handle, name the functions exactly like eventname.
- event_list = None # dont make duplicate entries in event_map
-
-
- #: periodic call interval in secondc
- interval = 60
-
- def __init__(self, core, manager):
- Base.__init__(self, core)
-
- #: Provide information in dict here, usable by API `getInfo`
- self.info = None
-
- #: Callback of periodical job task, used by hookmanager
- self.cb = None
-
- #: `HookManager`
- self.manager = manager
-
- #register events
- if self.event_map:
- for event, funcs in self.event_map.iteritems():
- if type(funcs) in (list, tuple):
- for f in funcs:
- self.manager.addEvent(event, getattr(self,f))
- else:
- self.manager.addEvent(event, getattr(self,funcs))
-
- #delete for various reasons
- self.event_map = None
-
- if self.event_list:
- for f in self.event_list:
- self.manager.addEvent(f, getattr(self,f))
-
- self.event_list = None
-
- self.initPeriodical()
- self.setup()
-
- def initPeriodical(self):
- if self.interval >=1:
- self.cb = self.core.scheduler.addJob(0, self._periodical, threaded=False)
-
- def _periodical(self):
- try:
- if self.isActivated(): self.periodical()
- except Exception, e:
- self.core.log.error(_("Error executing hooks: %s") % str(e))
- if self.core.debug:
- print_exc()
-
- self.cb = self.core.scheduler.addJob(self.interval, self._periodical, threaded=False)
-
-
- def __repr__(self):
- return "<Hook %s>" % self.__name__
-
- def setup(self):
- """ more init stuff if needed """
- pass
-
- def unload(self):
- """ called when hook was deactivated """
- pass
-
- def isActivated(self):
- """ checks if hook is activated"""
- return self.config.getPlugin(self.__name__, "activated")
-
-
- #event methods - overwrite these if needed
- def coreReady(self):
- pass
-
- def coreExiting(self):
- pass
-
- def downloadPreparing(self, pyfile):
- pass
-
- def downloadFinished(self, pyfile):
- pass
-
- def downloadFailed(self, pyfile):
- pass
-
- def packageFinished(self, pypack):
- pass
-
- def beforeReconnecting(self, ip):
- pass
-
- def afterReconnecting(self, ip):
- pass
-
- def periodical(self):
- pass
-
- def newCaptchaTask(self, task):
- """ new captcha task for the plugin, it MUST set the handler and timeout or will be ignored """
- pass
-
- def captchaCorrect(self, task):
- pass
-
- def captchaInvalid(self, task):
- pass \ No newline at end of file
diff --git a/module/plugins/Hoster.py b/module/plugins/Hoster.py
index 814a70949..2d43ee845 100644
--- a/module/plugins/Hoster.py
+++ b/module/plugins/Hoster.py
@@ -1,33 +1,21 @@
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
+from pyload.plugin.Plugin import Plugin
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay
-"""
-
-from module.plugins.Plugin import Plugin
def getInfo(self):
#result = [ .. (name, size, status, url) .. ]
return
+
class Hoster(Plugin):
- __name__ = "Hoster"
- __version__ = "0.1"
- __pattern__ = None
- __type__ = "hoster"
+ __name__ = "Hoster"
+ __type__ = "hoster"
+ __version__ = "0.02"
+
+ __pattern__ = r'^unmatchable$'
+ __config__ = [] #: [("name", "type", "desc", "default")]
+
__description__ = """Base hoster plugin"""
- __author_name__ = ("mkaay")
- __author_mail__ = ("mkaay@mkaay.de")
+ __license__ = "GPLv3"
+ __authors__ = [("mkaay", "mkaay@mkaay.de")]
diff --git a/module/plugins/captcha/captcha.py b/module/plugins/OCR.py
index 1874ba07d..f309394f5 100644
--- a/module/plugins/captcha/captcha.py
+++ b/module/plugins/OCR.py
@@ -48,9 +48,9 @@ class OCR(object):
def run(self, command):
"""Run a command"""
- popen = subprocess.Popen(command, bufsize = -1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ popen = subprocess.Popen(command, bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
popen.wait()
- output = popen.stdout.read() +" | "+ popen.stderr.read()
+ output = popen.stdout.read() + " | " + popen.stderr.read()
popen.stdout.close()
popen.stderr.close()
self.logger.debug("Tesseract ReturnCode %s Output: %s" % (popen.returncode, output))
@@ -78,7 +78,7 @@ class OCR(object):
else:
tessparams = ["tesseract"]
- tessparams.extend( [os.path.abspath(tmpTif.name), os.path.abspath(tmpTxt.name).replace(".txt", "")] )
+ tessparams.extend([os.path.abspath(tmpTif.name), os.path.abspath(tmpTxt.name).replace(".txt", "")] )
if subset and (digits or lowercase or uppercase):
#tmpSub = tempfile.NamedTemporaryFile(suffix=".subset")
@@ -151,11 +151,11 @@ class OCR(object):
count = 0
try:
- if pixels[x-1, y-1] != 255:
+ if pixels[x - 1, y - 1] != 255:
count += 1
- if pixels[x-1, y] != 255:
+ if pixels[x - 1, y] != 255:
count += 1
- if pixels[x-1, y + 1] != 255:
+ if pixels[x - 1, y + 1] != 255:
count += 1
if pixels[x, y + 1] != 255:
count += 1
@@ -163,19 +163,19 @@ class OCR(object):
count += 1
if pixels[x + 1, y] != 255:
count += 1
- if pixels[x + 1, y-1] != 255:
+ if pixels[x + 1, y - 1] != 255:
count += 1
- if pixels[x, y-1] != 255:
+ if pixels[x, y - 1] != 255:
count += 1
except Exception:
pass
- # not enough neighbors are dark pixels so mark this pixel
- # to be changed to white
+ # not enough neighbors are dark pixels so mark this pixel
+ # to be changed to white
if count < allowed:
pixels[x, y] = 1
- # second pass: this time set all 1's to 255 (white)
+ # second pass: this time set all 1's to 255 (white)
for x in xrange(w):
for y in xrange(h):
if pixels[x, y] == 1:
@@ -211,7 +211,6 @@ class OCR(object):
if pixels[x, y] == 0:
pixels[x, y] = 255
-
count = {}
for x in xrange(w):
diff --git a/module/plugins/Plugin.py b/module/plugins/Plugin.py
index 15bf3971f..0a9c647fb 100644
--- a/module/plugins/Plugin.py
+++ b/module/plugins/Plugin.py
@@ -1,21 +1,6 @@
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN, spoob, mkaay
-"""
+from __future__ import with_statement
from time import time, sleep
from random import randint
@@ -30,8 +15,11 @@ if os.name != "nt":
from grp import getgrnam
from itertools import islice
+from traceback import print_exc
+from urlparse import urlparse
+
+from pyload.utils import fs_decode, fs_encode, safe_filename, safe_join
-from module.utils import save_join, save_path, fs_encode, fs_decode
def chunks(iterable, size):
it = iter(iterable)
@@ -69,28 +57,40 @@ class Base(object):
def __init__(self, core):
#: Core instance
self.core = core
- #: logging instance
- self.log = core.log
- #: core config
- self.config = core.config
- #log functions
+
+ def _log(self, type, args):
+ msg = " | ".join([encode(a).strip() for a in args if a])
+ logger = getattr(self.core.log, type)
+ logger("%s: %s" % (self.__name__, msg or _("%s MARK" % type.upper())))
+
+
+ def logDebug(self, *args):
+ if self.core.debug:
+ return self._log("debug", args)
+
+
def logInfo(self, *args):
- self.log.info("%s: %s" % (self.__name__, " | ".join([a if isinstance(a, basestring) else str(a) for a in args])))
+ return self._log("info", args)
+
def logWarning(self, *args):
- self.log.warning("%s: %s" % (self.__name__, " | ".join([a if isinstance(a, basestring) else str(a) for a in args])))
+ return self._log("warning", args)
+
def logError(self, *args):
- self.log.error("%s: %s" % (self.__name__, " | ".join([a if isinstance(a, basestring) else str(a) for a in args])))
+ return self._log("error", args)
- def logDebug(self, *args):
- self.log.debug("%s: %s" % (self.__name__, " | ".join([a if isinstance(a, basestring) else str(a) for a in args])))
+
+ def logCritical(self, *args):
+ return self._log("critical", args)
+ #: Deprecated method
def setConf(self, option, value):
""" see `setConfig` """
- self.core.config.setPlugin(self.__name__, option, value)
+ self.setConfig(option, value)
+
def setConfig(self, option, value):
""" Set config value for current plugin
@@ -99,11 +99,14 @@ class Base(object):
:param value:
:return:
"""
- self.setConf(option, value)
+ self.core.config.setPlugin(self.__name__, option, value)
+
+ #: Deprecated method
def getConf(self, option):
""" see `getConfig` """
- return self.core.config.getPlugin(self.__name__, option)
+ return self.getConfig(option)
+
def getConfig(self, option):
""" Returns config value for current plugin
@@ -111,26 +114,31 @@ class Base(object):
:param option:
:return:
"""
- return self.getConf(option)
+ return self.core.config.getPlugin(self.__name__, option)
+
def setStorage(self, key, value):
""" Saves a value persistently to the database """
self.core.db.setStorage(self.__name__, key, value)
+
def store(self, key, value):
""" same as `setStorage` """
self.core.db.setStorage(self.__name__, key, value)
+
def getStorage(self, key=None, default=None):
""" Retrieves saved value or dict of all saved entries if key is None """
- if key is not None:
+ if key:
return self.core.db.getStorage(self.__name__, key) or default
return self.core.db.getStorage(self.__name__, key)
+
def retrieve(self, *args, **kwargs):
""" same as `getStorage` """
return self.getStorage(*args, **kwargs)
+
def delStorage(self, key):
""" Delete entry in db """
self.core.db.delStorage(self.__name__, key)
@@ -141,22 +149,33 @@ class Plugin(Base):
Base plugin for hoster/crypter.
Overwrite `process` / `decrypt` in your subclassed plugin.
"""
- __name__ = "Plugin"
- __version__ = "0.4"
- __pattern__ = None
- __type__ = "hoster"
- __config__ = [("name", "type", "desc", "default")]
- __description__ = """Base Plugin"""
- __author_name__ = ("RaNaN", "spoob", "mkaay")
- __author_mail__ = ("RaNaN@pyload.org", "spoob@pyload.org", "mkaay@mkaay.de")
+ __name__ = "Plugin"
+ __type__ = "hoster"
+ __version__ = "0.07"
+
+ __pattern__ = r'^unmatchable$'
+ __config__ = [] #: [("name", "type", "desc", "default")]
+
+ __description__ = """Base plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("RaNaN", "RaNaN@pyload.org"),
+ ("spoob", "spoob@pyload.org"),
+ ("mkaay", "mkaay@mkaay.de")]
+
+
+ info = {} #: file info dict
+
def __init__(self, pyfile):
Base.__init__(self, pyfile.m.core)
+ #: engage wan reconnection
self.wantReconnect = False
- #: enables simultaneous processing of multiple downloads
+
+ #: enable simultaneous processing of multiple downloads
self.multiDL = True
self.limitDL = 0
+
#: chunk limit
self.chunkLimit = 1
self.resumeDownload = False
@@ -165,7 +184,9 @@ class Plugin(Base):
self.waitUntil = 0
self.waiting = False
- self.ocr = None #captcha reader instance
+ #: captcha reader instance
+ self.ocr = None
+
#: account handler instance, see :py:class:`Account`
self.account = pyfile.m.core.accountManager.getAccountPlugin(self.__name__)
@@ -174,7 +195,9 @@ class Plugin(Base):
#: username/login
self.user = None
- if self.account and not self.account.canUse(): self.account = None
+ if self.account and not self.account.canUse():
+ self.account = None
+
if self.account:
self.user, data = self.account.selectAccount()
#: Browser instance, see `network.Browser`
@@ -190,37 +213,46 @@ class Plugin(Base):
#: associated pyfile instance, see `PyFile`
self.pyfile = pyfile
+
self.thread = None # holds thread in future
#: location where the last call to download was saved
self.lastDownload = ""
#: re match of the last call to `checkDownload`
self.lastCheck = None
+
#: js engine, see `JsEngine`
self.js = self.core.js
- self.cTask = None #captcha task
- self.retries = 0 # amount of retries already made
- self.html = None # some plugins store html code here
+ #: captcha task
+ self.cTask = None
+
+ self.html = None #@TODO: Move to hoster class in 0.4.10
+ self.retries = 0
self.init()
+
def getChunkCount(self):
if self.chunkLimit <= 0:
- return self.config["download"]["chunks"]
- return min(self.config["download"]["chunks"], self.chunkLimit)
+ return self.core.config['download']['chunks']
+ return min(self.core.config['download']['chunks'], self.chunkLimit)
+
def __call__(self):
return self.__name__
+
def init(self):
"""initialize the plugin (in addition to `__init__`)"""
pass
+
def setup(self):
""" setup for enviroment and other things, called before downloading (possibly more than one time)"""
pass
+
def preprocessing(self, thread):
""" handles important things to do before starting """
self.thread = thread
@@ -241,12 +273,14 @@ class Plugin(Base):
"""the 'main' method of every plugin, you **have to** overwrite it"""
raise NotImplementedError
+
def resetAccount(self):
""" dont use account and retry download """
self.account = None
self.req = self.core.requestFactory.getRequest(self.__name__)
self.retry()
+
def checksum(self, local_file=None):
"""
return codes:
@@ -256,51 +290,119 @@ class Plugin(Base):
10 - not implemented
20 - unknown error
"""
- #@TODO checksum check hook
+ #@TODO checksum check addon
return True, 10
- def setWait(self, seconds, reconnect=False):
+ def setReconnect(self, reconnect):
+ reconnect = bool(reconnect)
+ self.logDebug("Set wantReconnect to: %s (previous: %s)" % (reconnect, self.wantReconnect))
+ self.wantReconnect = reconnect
+
+
+ def setWait(self, seconds, reconnect=None):
"""Set a specific wait time later used with `wait`
-
+
:param seconds: wait time in seconds
:param reconnect: True if a reconnect would avoid wait time
"""
- if reconnect:
- self.wantReconnect = True
- self.pyfile.waitUntil = time() + int(seconds)
+ wait_time = int(seconds) + 1
+ wait_until = time() + wait_time
+
+ self.logDebug("Set waitUntil to: %f (previous: %f)" % (wait_until, self.pyfile.waitUntil),
+ "Wait: %d seconds" % wait_time)
- def wait(self):
+ self.pyfile.waitUntil = wait_until
+
+ if reconnect is not None:
+ self.setReconnect(reconnect)
+
+
+ def wait(self, seconds=None, reconnect=None):
""" waits the time previously set """
+
+ pyfile = self.pyfile
+
+ if seconds is not None:
+ self.setWait(seconds)
+
+ if reconnect is not None:
+ self.setReconnect(reconnect)
+
self.waiting = True
- self.pyfile.setStatus("waiting")
- while self.pyfile.waitUntil > time():
- self.thread.m.reconnecting.wait(2)
+ status = pyfile.status
+ pyfile.setStatus("waiting")
- if self.pyfile.abort: raise Abort
- if self.thread.m.reconnecting.isSet():
- self.waiting = False
- self.wantReconnect = False
- raise Reconnect
+ self.logInfo(_("Wait: %d seconds") % (pyfile.waitUntil - time()),
+ _("Reconnect: %s") % self.wantReconnect)
+
+ if self.account:
+ self.logDebug("Ignore reconnection due account logged")
+
+ while pyfile.waitUntil > time():
+ if pyfile.abort:
+ self.abort()
+
+ sleep(1)
+ else:
+ while pyfile.waitUntil > time():
+ self.thread.m.reconnecting.wait(2)
+
+ if pyfile.abort:
+ self.abort()
+
+ if self.thread.m.reconnecting.isSet():
+ self.waiting = False
+ self.wantReconnect = False
+ raise Reconnect
+
+ sleep(1)
self.waiting = False
- self.pyfile.setStatus("starting")
+
+ pyfile.status = status
+
def fail(self, reason):
""" fail and give reason """
raise Fail(reason)
- def offline(self):
+
+ def abort(self, reason=""):
+ """ abort and give reason """
+ if reason:
+ self.pyfile.error = str(reason)
+ raise Abort
+
+
+ def error(self, reason="", type=""):
+ if not reason and not type:
+ type = "unknown"
+
+ msg = _("%s error") % _(type.strip().capitalize()) if type else _("Error")
+ msg += ": " + reason.strip() if reason else ""
+ msg += _(" | Plugin may be out of date")
+
+ raise Fail(msg)
+
+
+ def offline(self, reason=""):
""" fail and indicate file is offline """
+ if reason:
+ self.pyfile.error = str(reason)
raise Fail("offline")
- def tempOffline(self):
+
+ def tempOffline(self, reason=""):
""" fail and indicates file ist temporary offline, the core may take consequences """
+ if reason:
+ self.pyfile.error = str(reason)
raise Fail("temp. offline")
- def retry(self, max_tries=3, wait_time=1, reason=""):
+
+ def retry(self, max_tries=5, wait_time=1, reason=""):
"""Retries and begin again from the beginning
:param max_tries: number of maximum retries
@@ -308,26 +410,28 @@ class Plugin(Base):
:param reason: reason for retrying, will be passed to fail if max_tries reached
"""
if 0 < max_tries <= self.retries:
- if not reason: reason = "Max retries reached"
- raise Fail(reason)
+ self.error(reason or _("Max retries reached"), "retry")
- self.wantReconnect = False
- self.setWait(wait_time)
- self.wait()
+ self.wait(wait_time, False)
self.retries += 1
raise Retry(reason)
+
def invalidCaptcha(self):
+ self.logError(_("Invalid captcha"))
if self.cTask:
self.cTask.invalid()
+
def correctCaptcha(self):
+ self.logInfo(_("Correct captcha"))
if self.cTask:
self.cTask.correct()
+
def decryptCaptcha(self, url, get={}, post={}, cookies=False, forceUser=False, imgtype='jpg',
- result_type='textual'):
+ result_type='textual', timeout=290):
""" Loads a captcha and decrypts it with ocr, plugin, user input
:param url: url of captcha image
@@ -339,40 +443,41 @@ class Plugin(Base):
:param result_type: 'textual' if text is written on the captcha\
or 'positional' for captcha where the user have to click\
on a specific region on the captcha
-
+
:return: result of decrypting
"""
img = self.load(url, get=get, post=post, cookies=cookies)
id = ("%.2f" % time())[-6:].replace(".", "")
- temp_file = open(join("tmp", "tmpCaptcha_%s_%s.%s" % (self.__name__, id, imgtype)), "wb")
- temp_file.write(img)
- temp_file.close()
- has_plugin = self.__name__ in self.core.pluginManager.captchaPlugins
+ with open(join("tmp", "tmpCaptcha_%s_%s.%s" % (self.__name__, id, imgtype)), "wb") as tmpCaptcha:
+ tmpCaptcha.write(img)
+
+ has_plugin = self.__name__ in self.core.pluginManager.ocrPlugins
if self.core.captcha:
- Ocr = self.core.pluginManager.loadClass("captcha", self.__name__)
+ Ocr = self.core.pluginManager.loadClass("ocr", self.__name__)
else:
Ocr = None
if Ocr and not forceUser:
sleep(randint(3000, 5000) / 1000.0)
- if self.pyfile.abort: raise Abort
+ if self.pyfile.abort:
+ self.abort()
ocr = Ocr()
- result = ocr.get_captcha(temp_file.name)
+ result = ocr.get_captcha(tmpCaptcha.name)
else:
captchaManager = self.core.captchaManager
- task = captchaManager.newTask(img, imgtype, temp_file.name, result_type)
+ task = captchaManager.newTask(img, imgtype, tmpCaptcha.name, result_type)
self.cTask = task
- captchaManager.handleCaptcha(task)
+ captchaManager.handleCaptcha(task, timeout)
while task.isWaiting():
if self.pyfile.abort:
captchaManager.removeTask(task)
- raise Abort
+ self.abort()
sleep(1)
captchaManager.removeTask(task)
@@ -382,21 +487,21 @@ class Plugin(Base):
elif task.error:
self.fail(task.error)
elif not task.result:
- self.fail(_("No captcha result obtained in appropiate time by any of the plugins."))
+ self.fail(_("No captcha result obtained in appropiate time by any of the plugins"))
result = task.result
- self.log.debug("Received captcha result: %s" % str(result))
+ self.logDebug("Received captcha result: %s" % result)
if not self.core.debug:
try:
- remove(temp_file.name)
- except:
+ remove(tmpCaptcha.name)
+ except Exception:
pass
return result
- def load(self, url, get={}, post={}, ref=True, cookies=True, just_header=False, decode=False):
+ def load(self, url, get={}, post={}, ref=True, cookies=True, just_header=False, decode=False, follow_location=True, save_cookies=True):
"""Load content at url and returns it
:param url:
@@ -404,35 +509,42 @@ class Plugin(Base):
:param post:
:param ref:
:param cookies:
- :param just_header: if True only the header will be retrieved and returned as dict
+ :param just_header: If True only the header will be retrieved and returned as dict
:param decode: Wether to decode the output according to http header, should be True in most cases
+ :param follow_location: If True follow location else not
+ :param save_cookies: If True saves received cookies else discard them
:return: Loaded content
"""
- if self.pyfile.abort: raise Abort
- #utf8 vs decode -> please use decode attribute in all future plugins
- if type(url) == unicode: url = str(url)
+ if self.pyfile.abort:
+ self.abort()
+
+ if not url:
+ self.fail(_("No url given"))
- res = self.req.load(url, get, post, ref, cookies, just_header, decode=decode)
+ url = encode(url).strip() #@NOTE: utf8 vs decode -> please use decode attribute in all future plugins
if self.core.debug:
- from inspect import currentframe
+ self.logDebug("Load url: " + url, *["%s=%s" % (key, val) for key, val in locals().iteritems() if key not in ("self", "url")])
- frame = currentframe()
- if not exists(join("tmp", self.__name__)):
- makedirs(join("tmp", self.__name__))
+ res = self.req.load(url, get, post, ref, cookies, just_header, decode=decode, follow_location=follow_location, save_cookies=save_cookies)
- f = open(
- join("tmp", self.__name__, "%s_line%s.dump.html" % (frame.f_back.f_code.co_name, frame.f_back.f_lineno))
- , "wb")
- del frame # delete the frame or it wont be cleaned
+ if decode:
+ res = encode(res)
+ if self.core.debug:
+ from inspect import currentframe
+
+ frame = currentframe()
+ framefile = safe_join("tmp", self.__name__, "%s_line%s.dump.html" % (frame.f_back.f_code.co_name, frame.f_back.f_lineno))
try:
- tmp = res.encode("utf8")
- except:
- tmp = res
+ if not exists(join("tmp", self.__name__)):
+ makedirs(join("tmp", self.__name__))
- f.write(tmp)
- f.close()
+ with open(framefile, "wb") as f:
+ del frame #: delete the frame or it wont be cleaned
+ f.write(res)
+ except IOError, e:
+ self.logError(e)
if just_header:
#parse header
@@ -442,7 +554,7 @@ class Plugin(Base):
if not line or ":" not in line: continue
key, none, value = line.partition(":")
- key = key.lower().strip()
+ key = key.strip().lower()
value = value.strip()
if key in header:
@@ -456,6 +568,7 @@ class Plugin(Base):
return res
+
def download(self, url, get={}, post={}, ref=True, cookies=True, disposition=False):
"""Downloads the content at url to download folder
@@ -468,34 +581,44 @@ class Plugin(Base):
the filename will be changed if needed
:return: The location where the file was saved
"""
+ if self.pyfile.abort:
+ self.abort()
+
+ if not url:
+ self.fail(_("No url given"))
+
+ url = encode(url).strip()
+
+ if self.core.debug:
+ self.logDebug("Download url: " + url, *["%s=%s" % (key, val) for key, val in locals().iteritems() if key not in ("self", "url")])
self.checkForSameFiles()
self.pyfile.setStatus("downloading")
- download_folder = self.config['general']['download_folder']
+ download_folder = self.core.config['general']['download_folder']
- location = save_join(download_folder, self.pyfile.package().folder)
+ location = safe_join(download_folder, self.pyfile.package().folder)
if not exists(location):
- makedirs(location, int(self.core.config["permission"]["folder"], 8))
-
- if self.core.config["permission"]["change_dl"] and os.name != "nt":
- try:
- uid = getpwnam(self.config["permission"]["user"])[2]
- gid = getgrnam(self.config["permission"]["group"])[2]
+ try:
+ makedirs(location, int(self.core.config['permission']['folder'], 8))
+ if self.core.config['permission']['change_dl'] and os.name != "nt":
+ uid = getpwnam(self.core.config['permission']['user'])[2]
+ gid = getgrnam(self.core.config['permission']['group'])[2]
chown(location, uid, gid)
- except Exception, e:
- self.log.warning(_("Setting User and Group failed: %s") % str(e))
+
+ except Exception, e:
+ self.fail(e)
# convert back to unicode
location = fs_decode(location)
- name = save_path(self.pyfile.name)
+ name = safe_filename(self.pyfile.name)
filename = join(location, name)
- self.core.hookManager.dispatchEvent("downloadStarts", self.pyfile, url, filename)
+ self.core.addonManager.dispatchEvent("download-start", self.pyfile, url, filename)
try:
newname = self.req.httpDownload(url, filename, get=get, post=post, ref=ref, cookies=cookies,
@@ -504,31 +627,38 @@ class Plugin(Base):
finally:
self.pyfile.size = self.req.size
- if disposition and newname and newname != name: #triple check, just to be sure
- self.log.info("%(name)s saved as %(newname)s" % {"name": name, "newname": newname})
- self.pyfile.name = newname
- filename = join(location, newname)
+ if newname:
+ newname = urlparse(newname).path.split("/")[-1]
- fs_filename = fs_encode(filename)
+ if disposition and newname != name:
+ self.logInfo(_("%(name)s saved as %(newname)s") % {"name": name, "newname": newname})
+ self.pyfile.name = newname
+ filename = join(location, newname)
- if self.core.config["permission"]["change_file"]:
- chmod(fs_filename, int(self.core.config["permission"]["file"], 8))
+ fs_filename = fs_encode(filename)
- if self.core.config["permission"]["change_dl"] and os.name != "nt":
+ if self.core.config['permission']['change_file']:
try:
- uid = getpwnam(self.config["permission"]["user"])[2]
- gid = getgrnam(self.config["permission"]["group"])[2]
+ chmod(fs_filename, int(self.core.config['permission']['file'], 8))
+ except Exception, e:
+ self.logWarning(_("Setting file mode failed"), e)
+ if self.core.config['permission']['change_dl'] and os.name != "nt":
+ try:
+ uid = getpwnam(self.core.config['permission']['user'])[2]
+ gid = getgrnam(self.core.config['permission']['group'])[2]
chown(fs_filename, uid, gid)
+
except Exception, e:
- self.log.warning(_("Setting User and Group failed: %s") % str(e))
+ self.logWarning(_("Setting User and Group failed"), e)
self.lastDownload = filename
return self.lastDownload
+
def checkDownload(self, rules, api_size=0, max_size=50000, delete=True, read_size=0):
""" checks the content of the last downloaded file, re match is saved to `lastCheck`
-
+
:param rules: dict with names and rules to match (compiled regexp or strings)
:param api_size: expected file size
:param max_size: if the file is larger then it wont be checked
@@ -537,21 +667,23 @@ class Plugin(Base):
:return: dictionary key of the first rule that matched
"""
lastDownload = fs_encode(self.lastDownload)
- if not exists(lastDownload): return None
+ if not exists(lastDownload):
+ return None
size = stat(lastDownload)
size = size.st_size
if api_size and api_size <= size: return None
elif size > max_size and not read_size: return None
- self.log.debug("Download Check triggered")
- f = open(lastDownload, "rb")
- content = f.read(read_size if read_size else -1)
- f.close()
+ self.logDebug("Download Check triggered")
+
+ with open(lastDownload, "rb") as f:
+ content = f.read(read_size if read_size else -1)
+
#produces encoding errors, better log to other file in the future?
- #self.log.debug("Content: %s" % content)
+ #self.logDebug("Content: %s" % content)
for name, rule in rules.iteritems():
- if type(rule) in (str, unicode):
+ if isinstance(rule, basestring):
if rule in content:
if delete:
remove(lastDownload)
@@ -589,29 +721,33 @@ class Plugin(Base):
5, 7) and starting: #a download is waiting/starting and was appenrently started before
raise SkipDownload(pyfile.pluginname)
- download_folder = self.config['general']['download_folder']
- location = save_join(download_folder, pack.folder, self.pyfile.name)
+ download_folder = self.core.config['general']['download_folder']
+ location = safe_join(download_folder, pack.folder, self.pyfile.name)
if starting and self.core.config['download']['skip_existing'] and exists(location):
size = os.stat(location).st_size
if size >= self.pyfile.size:
- raise SkipDownload("File exists.")
+ raise SkipDownload("File exists")
pyfile = self.core.db.findDuplicates(self.pyfile.id, self.pyfile.package().folder, self.pyfile.name)
if pyfile:
if exists(location):
raise SkipDownload(pyfile[0])
- self.log.debug("File %s not skipped, because it does not exists." % self.pyfile.name)
+ self.logDebug("File %s not skipped, because it does not exists." % self.pyfile.name)
+
def clean(self):
""" clean everything and remove references """
if hasattr(self, "pyfile"):
del self.pyfile
+
if hasattr(self, "req"):
self.req.close()
del self.req
+
if hasattr(self, "thread"):
del self.thread
+
if hasattr(self, "html"):
del self.html
diff --git a/module/plugins/PluginManager.py b/module/plugins/PluginManager.py
deleted file mode 100644
index f3f5f47bc..000000000
--- a/module/plugins/PluginManager.py
+++ /dev/null
@@ -1,380 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay, RaNaN
-"""
-
-import re
-import sys
-
-from os import listdir, makedirs
-from os.path import isfile, join, exists, abspath
-from sys import version_info
-from itertools import chain
-from traceback import print_exc
-
-from module.lib.SafeEval import const_eval as literal_eval
-from module.ConfigParser import IGNORE
-
-class PluginManager:
- ROOT = "module.plugins."
- USERROOT = "userplugins."
- TYPES = ("crypter", "container", "hoster", "captcha", "accounts", "hooks", "internal")
-
- PATTERN = re.compile(r'__pattern__.*=.*r("|\')([^"\']+)')
- VERSION = re.compile(r'__version__.*=.*("|\')([0-9.]+)')
- CONFIG = re.compile(r'__config__.*=.*\[([^\]]+)', re.MULTILINE)
- DESC = re.compile(r'__description__.?=.?("|"""|\')([^"\']+)')
-
-
- def __init__(self, core):
- self.core = core
-
- #self.config = self.core.config
- self.log = core.log
-
- self.plugins = {}
- self.createIndex()
-
- #register for import hook
- sys.meta_path.append(self)
-
-
- def createIndex(self):
- """create information for all plugins available"""
-
- sys.path.append(abspath(""))
-
- if not exists("userplugins"):
- makedirs("userplugins")
- if not exists(join("userplugins", "__init__.py")):
- f = open(join("userplugins", "__init__.py"), "wb")
- f.close()
-
- self.plugins["crypter"] = self.crypterPlugins = self.parse("crypter", pattern=True)
- self.plugins["container"] = self.containerPlugins = self.parse("container", pattern=True)
- self.plugins["hoster"] = self.hosterPlugins = self.parse("hoster", pattern=True)
-
- self.plugins["captcha"] = self.captchaPlugins = self.parse("captcha")
- self.plugins["accounts"] = self.accountPlugins = self.parse("accounts")
- self.plugins["hooks"] = self.hookPlugins = self.parse("hooks")
- self.plugins["internal"] = self.internalPlugins = self.parse("internal")
-
- self.log.debug("created index of plugins")
-
- def parse(self, folder, pattern=False, home={}):
- """
- returns dict with information
- home contains parsed plugins from module.
-
- {
- name : {path, version, config, (pattern, re), (plugin, class)}
- }
-
- """
- plugins = {}
- if home:
- pfolder = join("userplugins", folder)
- if not exists(pfolder):
- makedirs(pfolder)
- if not exists(join(pfolder, "__init__.py")):
- f = open(join(pfolder, "__init__.py"), "wb")
- f.close()
-
- else:
- pfolder = join(pypath, "module", "plugins", folder)
-
- for f in listdir(pfolder):
- if (isfile(join(pfolder, f)) and f.endswith(".py") or f.endswith("_25.pyc") or f.endswith(
- "_26.pyc") or f.endswith("_27.pyc")) and not f.startswith("_"):
- data = open(join(pfolder, f))
- content = data.read()
- data.close()
-
- if f.endswith("_25.pyc") and version_info[0:2] != (2, 5):
- continue
- elif f.endswith("_26.pyc") and version_info[0:2] != (2, 6):
- continue
- elif f.endswith("_27.pyc") and version_info[0:2] != (2, 7):
- continue
-
- name = f[:-3]
- if name[-1] == ".": name = name[:-4]
-
- version = self.VERSION.findall(content)
- if version:
- version = float(version[0][1])
- else:
- version = 0
-
- # home contains plugins from pyload root
- if home and name in home:
- if home[name]["v"] >= version:
- continue
-
- if name in IGNORE or (folder, name) in IGNORE:
- continue
-
- plugins[name] = {}
- plugins[name]["v"] = version
-
- module = f.replace(".pyc", "").replace(".py", "")
-
- # the plugin is loaded from user directory
- plugins[name]["user"] = True if home else False
- plugins[name]["name"] = module
-
- if pattern:
- pattern = self.PATTERN.findall(content)
-
- if pattern:
- pattern = pattern[0][1]
- else:
- pattern = "^unmachtable$"
-
- plugins[name]["pattern"] = pattern
-
- try:
- plugins[name]["re"] = re.compile(pattern)
- except:
- self.log.error(_("%s has a invalid pattern.") % name)
-
-
- # internals have no config
- if folder == "internal":
- self.core.config.deleteConfig(name)
- continue
-
- config = self.CONFIG.findall(content)
- if config:
- config = literal_eval(config[0].strip().replace("\n", "").replace("\r", ""))
- desc = self.DESC.findall(content)
- desc = desc[0][1] if desc else ""
-
- if type(config[0]) == tuple:
- config = [list(x) for x in config]
- else:
- config = [list(config)]
-
- if folder == "hooks":
- append = True
- for item in config:
- if item[0] == "activated": append = False
-
- # activated flag missing
- if append: config.append(["activated", "bool", "Activated", False])
-
- try:
- self.core.config.addPluginConfig(name, config, desc)
- except:
- self.log.error("Invalid config in %s: %s" % (name, config))
-
- elif folder == "hooks": #force config creation
- desc = self.DESC.findall(content)
- desc = desc[0][1] if desc else ""
- config = (["activated", "bool", "Activated", False],)
-
- try:
- self.core.config.addPluginConfig(name, config, desc)
- except:
- self.log.error("Invalid config in %s: %s" % (name, config))
-
- if not home:
- temp = self.parse(folder, pattern, plugins)
- plugins.update(temp)
-
- return plugins
-
-
- def parseUrls(self, urls):
- """parse plugins for given list of urls"""
-
- last = None
- res = [] # tupels of (url, plugin)
-
- for url in urls:
- if type(url) not in (str, unicode, buffer): continue
- found = False
-
- if last and last[1]["re"].match(url):
- res.append((url, last[0]))
- continue
-
- for name, value in chain(self.crypterPlugins.iteritems(), self.hosterPlugins.iteritems(),
- self.containerPlugins.iteritems()):
- if value["re"].match(url):
- res.append((url, name))
- last = (name, value)
- found = True
- break
-
- if not found:
- res.append((url, "BasePlugin"))
-
- return res
-
- def findPlugin(self, name, pluginlist=("hoster", "crypter", "container")):
- for ptype in pluginlist:
- if name in self.plugins[ptype]:
- return self.plugins[ptype][name], ptype
- return None, None
-
- def getPlugin(self, name, original=False):
- """return plugin module from hoster|decrypter|container"""
- plugin, type = self.findPlugin(name)
-
- if not plugin:
- self.log.warning("Plugin %s not found." % name)
- plugin = self.hosterPlugins["BasePlugin"]
-
- if "new_module" in plugin and not original:
- return plugin["new_module"]
-
- return self.loadModule(type, name)
-
- def getPluginName(self, name):
- """ used to obtain new name if other plugin was injected"""
- plugin, type = self.findPlugin(name)
-
- if "new_name" in plugin:
- return plugin["new_name"]
-
- return name
-
- def loadModule(self, type, name):
- """ Returns loaded module for plugin
-
- :param type: plugin type, subfolder of module.plugins
- :param name:
- """
- plugins = self.plugins[type]
- if name in plugins:
- if "module" in plugins[name]: return plugins[name]["module"]
- try:
- module = __import__(self.ROOT + "%s.%s" % (type, plugins[name]["name"]), globals(), locals(),
- plugins[name]["name"])
- plugins[name]["module"] = module #cache import, maybe unneeded
- return module
- except Exception, e:
- self.log.error(_("Error importing %(name)s: %(msg)s") % {"name": name, "msg": str(e)})
- if self.core.debug:
- print_exc()
-
- def loadClass(self, type, name):
- """Returns the class of a plugin with the same name"""
- module = self.loadModule(type, name)
- if module: return getattr(module, name)
-
- def getAccountPlugins(self):
- """return list of account plugin names"""
- return self.accountPlugins.keys()
-
- def find_module(self, fullname, path=None):
- #redirecting imports if necesarry
- if fullname.startswith(self.ROOT) or fullname.startswith(self.USERROOT): #seperate pyload plugins
- if fullname.startswith(self.USERROOT): user = 1
- else: user = 0 #used as bool and int
-
- split = fullname.split(".")
- if len(split) != 4 - user: return
- type, name = split[2 - user:4 - user]
-
- if type in self.plugins and name in self.plugins[type]:
- #userplugin is a newer version
- if not user and self.plugins[type][name]["user"]:
- return self
- #imported from userdir, but pyloads is newer
- if user and not self.plugins[type][name]["user"]:
- return self
-
-
- def load_module(self, name, replace=True):
- if name not in sys.modules: #could be already in modules
- if replace:
- if self.ROOT in name:
- newname = name.replace(self.ROOT, self.USERROOT)
- else:
- newname = name.replace(self.USERROOT, self.ROOT)
- else: newname = name
-
- base, plugin = newname.rsplit(".", 1)
-
- self.log.debug("Redirected import %s -> %s" % (name, newname))
-
- module = __import__(newname, globals(), locals(), [plugin])
- #inject under new an old name
- sys.modules[name] = module
- sys.modules[newname] = module
-
- return sys.modules[name]
-
-
- def reloadPlugins(self, type_plugins):
- """ reloads and reindexes plugins """
- if not type_plugins: return False
-
- self.log.debug("Request reload of plugins: %s" % type_plugins)
-
- as_dict = {}
- for t,n in type_plugins:
- if t in as_dict:
- as_dict[t].append(n)
- else:
- as_dict[t] = [n]
-
- # we do not reload hooks or internals, would cause to much side effects
- if "hooks" in as_dict or "internal" in as_dict:
- return False
-
- for type in as_dict.iterkeys():
- for plugin in as_dict[type]:
- if plugin in self.plugins[type]:
- if "module" in self.plugins[type][plugin]:
- self.log.debug("Reloading %s" % plugin)
- reload(self.plugins[type][plugin]["module"])
-
- #index creation
- self.plugins["crypter"] = self.crypterPlugins = self.parse("crypter", pattern=True)
- self.plugins["container"] = self.containerPlugins = self.parse("container", pattern=True)
- self.plugins["hoster"] = self.hosterPlugins = self.parse("hoster", pattern=True)
- self.plugins["captcha"] = self.captchaPlugins = self.parse("captcha")
- self.plugins["accounts"] = self.accountPlugins = self.parse("accounts")
-
- if "accounts" in as_dict: #accounts needs to be reloaded
- self.core.accountManager.initPlugins()
- self.core.scheduler.addJob(0, self.core.accountManager.getAccountInfos)
-
- return True
-
-
-
-if __name__ == "__main__":
- _ = lambda x: x
- pypath = "/home/christian/Projekte/pyload-0.4/module/plugins"
-
- from time import time
-
- p = PluginManager(None)
-
- a = time()
-
- test = ["http://www.youtube.com/watch?v=%s" % x for x in range(0, 100)]
- print p.parseUrls(test)
-
- b = time()
-
- print b - a, "s"
-
diff --git a/module/plugins/__init__.py b/module/plugins/__init__.py
index e69de29bb..40a96afc6 100644
--- a/module/plugins/__init__.py
+++ b/module/plugins/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/plugins/accounts/AlldebridCom.py b/module/plugins/accounts/AlldebridCom.py
index c830f0c09..7fc11e343 100644
--- a/module/plugins/accounts/AlldebridCom.py
+++ b/module/plugins/accounts/AlldebridCom.py
@@ -7,7 +7,7 @@ from time import time
from BeautifulSoup import BeautifulSoup
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class AlldebridCom(Account):
diff --git a/module/plugins/accounts/BillionuploadsCom.py b/module/plugins/accounts/BillionuploadsCom.py
index 11af36591..a3325c427 100644
--- a/module/plugins/accounts/BillionuploadsCom.py
+++ b/module/plugins/accounts/BillionuploadsCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class BillionuploadsCom(XFSAccount):
diff --git a/module/plugins/accounts/BitshareCom.py b/module/plugins/accounts/BitshareCom.py
index 412aae534..4774b9062 100644
--- a/module/plugins/accounts/BitshareCom.py
+++ b/module/plugins/accounts/BitshareCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class BitshareCom(Account):
@@ -10,7 +10,7 @@ class BitshareCom(Account):
__description__ = """Bitshare account plugin"""
__license__ = "GPLv3"
- __authors__ = [("Paul King", None)]
+ __authors__ = [("Paul King", "")]
def loadAccountInfo(self, user, req):
diff --git a/module/plugins/accounts/CatShareNet.py b/module/plugins/accounts/CatShareNet.py
index ec4d706ae..a604ebff1 100644
--- a/module/plugins/accounts/CatShareNet.py
+++ b/module/plugins/accounts/CatShareNet.py
@@ -4,7 +4,7 @@ import re
from time import mktime, strptime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class CatShareNet(Account):
@@ -14,7 +14,7 @@ class CatShareNet(Account):
__description__ = """CatShareNet account plugin"""
__license__ = "GPLv3"
- __authors__ = [("prOq", None)]
+ __authors__ = [("prOq", "")]
PREMIUM_PATTERN = r'<a href="/premium">Konto:[\s\n]*Premium'
diff --git a/module/plugins/accounts/CramitIn.py b/module/plugins/accounts/CramitIn.py
index a9e2274a2..21503f625 100644
--- a/module/plugins/accounts/CramitIn.py
+++ b/module/plugins/accounts/CramitIn.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class CramitIn(XFSAccount):
diff --git a/module/plugins/accounts/CzshareCom.py b/module/plugins/accounts/CzshareCom.py
index 202a88e25..dfe78c21c 100644
--- a/module/plugins/accounts/CzshareCom.py
+++ b/module/plugins/accounts/CzshareCom.py
@@ -3,7 +3,7 @@
from time import mktime, strptime
import re
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class CzshareCom(Account):
diff --git a/module/plugins/accounts/DebridItaliaCom.py b/module/plugins/accounts/DebridItaliaCom.py
index 50f9aea27..de97f6e64 100644
--- a/module/plugins/accounts/DebridItaliaCom.py
+++ b/module/plugins/accounts/DebridItaliaCom.py
@@ -4,7 +4,7 @@ import re
from time import mktime, strptime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class DebridItaliaCom(Account):
@@ -22,14 +22,14 @@ class DebridItaliaCom(Account):
def loadAccountInfo(self, user, req):
- info = {"premium": False, "validuntil": None, "trafficleft": None}
+ info = {'premium': False, 'validuntil': None, 'trafficleft': None}
html = req.load("http://debriditalia.com/")
if 'Account premium not activated' not in html:
m = re.search(self.WALID_UNTIL_PATTERN, html)
if m:
validuntil = mktime(strptime(m.group(1), "%d/%m/%Y %H:%M"))
- info = {"premium": True, "validuntil": validuntil, "trafficleft": -1}
+ info = {'premium': True, 'validuntil': validuntil, 'trafficleft': -1}
else:
self.logError(_("Unable to retrieve account information"))
diff --git a/module/plugins/accounts/DepositfilesCom.py b/module/plugins/accounts/DepositfilesCom.py
index dfe430276..b3e896d31 100644
--- a/module/plugins/accounts/DepositfilesCom.py
+++ b/module/plugins/accounts/DepositfilesCom.py
@@ -4,7 +4,7 @@ import re
from time import strptime, mktime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class DepositfilesCom(Account):
diff --git a/module/plugins/hoster/DropboxCom.py b/module/plugins/accounts/DropboxCom.py
index a8ef5b4bc..bcaa24b8e 100644
--- a/module/plugins/hoster/DropboxCom.py
+++ b/module/plugins/accounts/DropboxCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class DropboxCom(SimpleHoster):
diff --git a/module/plugins/accounts/EasybytezCom.py b/module/plugins/accounts/EasybytezCom.py
index 93d3e2c19..c7d717474 100644
--- a/module/plugins/accounts/EasybytezCom.py
+++ b/module/plugins/accounts/EasybytezCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class EasybytezCom(XFSAccount):
diff --git a/module/plugins/accounts/EuroshareEu.py b/module/plugins/accounts/EuroshareEu.py
index f92a4e821..b37fc68fa 100644
--- a/module/plugins/accounts/EuroshareEu.py
+++ b/module/plugins/accounts/EuroshareEu.py
@@ -3,7 +3,7 @@
from time import mktime, strptime
import re
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class EuroshareEu(Account):
diff --git a/module/plugins/accounts/FastixRu.py b/module/plugins/accounts/FastixRu.py
index 51be3880f..69f78c3d9 100644
--- a/module/plugins/accounts/FastixRu.py
+++ b/module/plugins/accounts/FastixRu.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class FastixRu(Account):
diff --git a/module/plugins/accounts/FastshareCz.py b/module/plugins/accounts/FastshareCz.py
index d6e94f2e3..8fe98438b 100644
--- a/module/plugins/accounts/FastshareCz.py
+++ b/module/plugins/accounts/FastshareCz.py
@@ -2,7 +2,8 @@
import re
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
+from pyload.utils import parseFileSize
class FastshareCz(Account):
diff --git a/module/plugins/accounts/File4SafeCom.py b/module/plugins/accounts/File4SafeCom.py
index 50fe1aac8..c48956d38 100644
--- a/module/plugins/accounts/File4SafeCom.py
+++ b/module/plugins/accounts/File4SafeCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class File4SafeCom(XFSAccount):
diff --git a/module/plugins/accounts/FileParadoxIn.py b/module/plugins/accounts/FileParadoxIn.py
index c12d99d6a..02b923519 100644
--- a/module/plugins/accounts/FileParadoxIn.py
+++ b/module/plugins/accounts/FileParadoxIn.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class FileParadoxIn(XFSAccount):
diff --git a/module/plugins/accounts/FilecloudIo.py b/module/plugins/accounts/FilecloudIo.py
index 8ca55b1bc..6d2dcb92a 100644
--- a/module/plugins/accounts/FilecloudIo.py
+++ b/module/plugins/accounts/FilecloudIo.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class FilecloudIo(Account):
diff --git a/module/plugins/accounts/FilefactoryCom.py b/module/plugins/accounts/FilefactoryCom.py
index 426d572db..3395b3f90 100644
--- a/module/plugins/accounts/FilefactoryCom.py
+++ b/module/plugins/accounts/FilefactoryCom.py
@@ -5,7 +5,7 @@ from time import mktime, strptime
from pycurl import REFERER
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class FilefactoryCom(Account):
diff --git a/module/plugins/accounts/FilejungleCom.py b/module/plugins/accounts/FilejungleCom.py
index 9f7474207..2c476bffb 100644
--- a/module/plugins/accounts/FilejungleCom.py
+++ b/module/plugins/accounts/FilejungleCom.py
@@ -3,7 +3,7 @@
import re
from time import mktime, strptime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class FilejungleCom(Account):
diff --git a/module/plugins/accounts/FileomCom.py b/module/plugins/accounts/FileomCom.py
index 7c743f56a..36a11e411 100644
--- a/module/plugins/accounts/FileomCom.py
+++ b/module/plugins/accounts/FileomCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class FileomCom(XFSAccount):
diff --git a/module/plugins/accounts/FilerNet.py b/module/plugins/accounts/FilerNet.py
index 4067445af..f3cc42367 100644
--- a/module/plugins/accounts/FilerNet.py
+++ b/module/plugins/accounts/FilerNet.py
@@ -3,7 +3,7 @@
import re
import time
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class FilerNet(Account):
diff --git a/module/plugins/accounts/FilerioCom.py b/module/plugins/accounts/FilerioCom.py
index 4c6755293..1d9f8744b 100644
--- a/module/plugins/accounts/FilerioCom.py
+++ b/module/plugins/accounts/FilerioCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class FilerioCom(XFSAccount):
diff --git a/module/plugins/accounts/FilesMailRu.py b/module/plugins/accounts/FilesMailRu.py
index 15926589e..e6afd0168 100644
--- a/module/plugins/accounts/FilesMailRu.py
+++ b/module/plugins/accounts/FilesMailRu.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class FilesMailRu(Account):
diff --git a/module/plugins/accounts/FileserveCom.py b/module/plugins/accounts/FileserveCom.py
index 1cf2a3a3c..9aab88d2b 100644
--- a/module/plugins/accounts/FileserveCom.py
+++ b/module/plugins/accounts/FileserveCom.py
@@ -2,8 +2,8 @@
from time import mktime, strptime
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class FileserveCom(Account):
diff --git a/module/plugins/accounts/FourSharedCom.py b/module/plugins/accounts/FourSharedCom.py
index 2777a142a..127f9d58a 100644
--- a/module/plugins/accounts/FourSharedCom.py
+++ b/module/plugins/accounts/FourSharedCom.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class FourSharedCom(Account):
diff --git a/module/plugins/accounts/FreakshareCom.py b/module/plugins/accounts/FreakshareCom.py
index 83f4a9a84..9c61ac513 100644
--- a/module/plugins/accounts/FreakshareCom.py
+++ b/module/plugins/accounts/FreakshareCom.py
@@ -4,7 +4,7 @@ import re
from time import strptime, mktime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class FreakshareCom(Account):
diff --git a/module/plugins/accounts/FreeWayMe.py b/module/plugins/accounts/FreeWayMe.py
index 14b9f1e9a..dcd9d34cf 100644
--- a/module/plugins/accounts/FreeWayMe.py
+++ b/module/plugins/accounts/FreeWayMe.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class FreeWayMe(Account):
diff --git a/module/plugins/accounts/FshareVn.py b/module/plugins/accounts/FshareVn.py
index 66d912958..282a17751 100644
--- a/module/plugins/accounts/FshareVn.py
+++ b/module/plugins/accounts/FshareVn.py
@@ -4,7 +4,7 @@ import re
from time import mktime, strptime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class FshareVn(Account):
diff --git a/module/plugins/accounts/Ftp.py b/module/plugins/accounts/Ftp.py
index f978d2fa0..67cde2cdd 100644
--- a/module/plugins/accounts/Ftp.py
+++ b/module/plugins/accounts/Ftp.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class Ftp(Account):
@@ -13,5 +13,5 @@ class Ftp(Account):
__authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
- info_threshold = 1000000
- login_timeout = 1000000
+ login_timeout = -1 #: Unlimited
+ info_threshold = -1 #: Unlimited
diff --git a/module/plugins/accounts/HellshareCz.py b/module/plugins/accounts/HellshareCz.py
index e559b28e1..94467b375 100644
--- a/module/plugins/accounts/HellshareCz.py
+++ b/module/plugins/accounts/HellshareCz.py
@@ -3,7 +3,7 @@
import re
import time
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class HellshareCz(Account):
diff --git a/module/plugins/accounts/Http.py b/module/plugins/accounts/Http.py
index 07e46eb07..2571ef712 100644
--- a/module/plugins/accounts/Http.py
+++ b/module/plugins/accounts/Http.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class Http(Account):
@@ -13,5 +13,5 @@ class Http(Account):
__authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
- info_threshold = 1000000
- login_timeout = 1000000
+ login_timeout = -1 #: Unlimited
+ info_threshold = -1 #: Unlimited
diff --git a/module/plugins/accounts/HugefilesNet.py b/module/plugins/accounts/HugefilesNet.py
index 5da3bbc37..eb383fb17 100644
--- a/module/plugins/accounts/HugefilesNet.py
+++ b/module/plugins/accounts/HugefilesNet.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class HugefilesNet(XFSAccount):
diff --git a/module/plugins/accounts/HundredEightyUploadCom.py b/module/plugins/accounts/HundredEightyUploadCom.py
index 319a3feee..72185a4bb 100644
--- a/module/plugins/accounts/HundredEightyUploadCom.py
+++ b/module/plugins/accounts/HundredEightyUploadCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class HundredEightyUploadCom(XFSAccount):
diff --git a/module/plugins/accounts/JunocloudMe.py b/module/plugins/accounts/JunocloudMe.py
index b0fc160f3..0ffa92eb6 100644
--- a/module/plugins/accounts/JunocloudMe.py
+++ b/module/plugins/accounts/JunocloudMe.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class JunocloudMe(XFSAccount):
diff --git a/module/plugins/accounts/Keep2ShareCc.py b/module/plugins/accounts/Keep2ShareCc.py
index 9f28799a2..7ed15dc62 100644
--- a/module/plugins/accounts/Keep2ShareCc.py
+++ b/module/plugins/accounts/Keep2ShareCc.py
@@ -4,30 +4,29 @@ import re
from time import gmtime, mktime, strptime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
-class Keep2ShareCc(Account):
- __name__ = "Keep2ShareCc"
+class Keep2shareCc(Account):
+ __name__ = "Keep2shareCc"
__type__ = "account"
- __version__ = "0.05"
+ __version__ = "0.02"
- __description__ = """Keep2Share.cc account plugin"""
+ __description__ = """Keep2share.cc account plugin"""
__license__ = "GPLv3"
- __authors__ = [("aeronaut", "aeronaut@pianoguy.de"),
- ("Walter Purcaro", "vuolter@gmail.com")]
+ __authors__ = [("aeronaut", "aeronaut@pianoguy.de")]
- VALID_UNTIL_PATTERN = r'Premium expires:\s*<b>(.+?)<'
- TRAFFIC_LEFT_PATTERN = r'Available traffic \(today\):\s*<b><a href="/user/statistic.html">(.+?)<'
+ VALID_UNTIL_PATTERN = r'Premium expires: <b>(.+?)</b>'
+ TRAFFIC_LEFT_PATTERN = r'Available traffic \(today\):<b><a href="/user/statistic.html">(.+?)</a>'
LOGIN_FAIL_PATTERN = r'Please fix the following input errors'
def loadAccountInfo(self, user, req):
validuntil = None
- trafficleft = -1
- premium = False
+ trafficleft = None
+ premium = None
html = req.load("http://keep2share.cc/site/profile.html", decode=True)
@@ -36,26 +35,26 @@ class Keep2ShareCc(Account):
expiredate = m.group(1).strip()
self.logDebug("Expire date: " + expiredate)
- if expiredate == "LifeTime":
- premium = True
- validuntil = -1
- else:
- try:
- validuntil = mktime(strptime(expiredate, "%Y.%m.%d"))
+ try:
+ validuntil = mktime(strptime(expiredate, "%Y.%m.%d"))
- except Exception, e:
- self.logError(e)
+ except Exception, e:
+ self.logError(e)
+ else:
+ if validuntil > mktime(gmtime()):
+ premium = True
else:
- premium = True if validuntil > mktime(gmtime()) else False
+ premium = False
+ validuntil = None
- m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
- if m:
- try:
- trafficleft = self.parseTraffic(m.group(1))
+ m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
+ if m:
+ try:
+ trafficleft = self.parseTraffic(m.group(1))
- except Exception, e:
- self.logError(e)
+ except Exception, e:
+ self.logError(e)
return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium}
@@ -64,11 +63,7 @@ class Keep2ShareCc(Account):
req.cj.setCookie("keep2share.cc", "lang", "en")
html = req.load("http://keep2share.cc/login.html",
- post={'LoginForm[username]' : user,
- 'LoginForm[password]' : data['password'],
- 'LoginForm[rememberMe]': 1,
- 'yt0' : ""},
- decode=True)
+ post={'LoginForm[username]': user, 'LoginForm[password]': data['password']})
if re.search(self.LOGIN_FAIL_PATTERN, html):
self.wrongPassword()
diff --git a/module/plugins/accounts/LetitbitNet.py b/module/plugins/accounts/LetitbitNet.py
index 7f973d2d3..d0f08d0bb 100644
--- a/module/plugins/accounts/LetitbitNet.py
+++ b/module/plugins/accounts/LetitbitNet.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
-# from module.common.json_layer import json_loads, json_dumps
+from pyload.plugin.Account import Account
+# from pyload.utils import json_loads, json_dumps
class LetitbitNet(Account):
diff --git a/module/plugins/accounts/LinestorageCom.py b/module/plugins/accounts/LinestorageCom.py
index a48d5beb9..7a5d63a47 100644
--- a/module/plugins/accounts/LinestorageCom.py
+++ b/module/plugins/accounts/LinestorageCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class LinestorageCom(XFSAccount):
diff --git a/module/plugins/accounts/LinksnappyCom.py b/module/plugins/accounts/LinksnappyCom.py
index 97e368285..0b1176ee9 100644
--- a/module/plugins/accounts/LinksnappyCom.py
+++ b/module/plugins/accounts/LinksnappyCom.py
@@ -2,8 +2,8 @@
from hashlib import md5
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class LinksnappyCom(Account):
diff --git a/module/plugins/accounts/MegaDebridEu.py b/module/plugins/accounts/MegaDebridEu.py
index a082b97af..c2e64bcc7 100644
--- a/module/plugins/accounts/MegaDebridEu.py
+++ b/module/plugins/accounts/MegaDebridEu.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class MegaDebridEu(Account):
diff --git a/module/plugins/accounts/MegaRapidCz.py b/module/plugins/accounts/MegaRapidCz.py
index b229fe47d..5596fd623 100644
--- a/module/plugins/accounts/MegaRapidCz.py
+++ b/module/plugins/accounts/MegaRapidCz.py
@@ -3,7 +3,7 @@
import re
from time import mktime, strptime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class MegaRapidCz(Account):
diff --git a/module/plugins/accounts/MegasharesCom.py b/module/plugins/accounts/MegasharesCom.py
index 127ebadc8..53b854f65 100644
--- a/module/plugins/accounts/MegasharesCom.py
+++ b/module/plugins/accounts/MegasharesCom.py
@@ -3,7 +3,7 @@
import re
from time import mktime, strptime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class MegasharesCom(Account):
diff --git a/module/plugins/accounts/MovReelCom.py b/module/plugins/accounts/MovReelCom.py
index 6128cddc8..4d2855de1 100644
--- a/module/plugins/accounts/MovReelCom.py
+++ b/module/plugins/accounts/MovReelCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class MovReelCom(XFSAccount):
diff --git a/module/plugins/accounts/MultishareCz.py b/module/plugins/accounts/MultishareCz.py
index 0ac764ee1..9b4f4447b 100644
--- a/module/plugins/accounts/MultishareCz.py
+++ b/module/plugins/accounts/MultishareCz.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class MultishareCz(Account):
diff --git a/module/plugins/accounts/MyfastfileCom.py b/module/plugins/accounts/MyfastfileCom.py
index 4c75b27f0..838a1eefd 100644
--- a/module/plugins/accounts/MyfastfileCom.py
+++ b/module/plugins/accounts/MyfastfileCom.py
@@ -2,8 +2,8 @@
from time import time
-from module.common.json_layer import json_loads
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class MyfastfileCom(Account):
diff --git a/module/plugins/accounts/NetloadIn.py b/module/plugins/accounts/NetloadIn.py
index 1abd7fa84..68982e828 100644
--- a/module/plugins/accounts/NetloadIn.py
+++ b/module/plugins/accounts/NetloadIn.py
@@ -3,7 +3,7 @@
import re
from time import time
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class NetloadIn(Account):
diff --git a/module/plugins/accounts/NosuploadCom.py b/module/plugins/accounts/NosuploadCom.py
index e523ee2f4..7fc8b49de 100644
--- a/module/plugins/accounts/NosuploadCom.py
+++ b/module/plugins/accounts/NosuploadCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class NosuploadCom(XFSAccount):
diff --git a/module/plugins/accounts/NovafileCom.py b/module/plugins/accounts/NovafileCom.py
index ab61bf0fc..71a7dc2dc 100644
--- a/module/plugins/accounts/NovafileCom.py
+++ b/module/plugins/accounts/NovafileCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class NovafileCom(XFSAccount):
diff --git a/module/plugins/accounts/NowVideoSx.py b/module/plugins/accounts/NowVideoSx.py
index f44ae3865..8359e0410 100644
--- a/module/plugins/accounts/NowVideoSx.py
+++ b/module/plugins/accounts/NowVideoSx.py
@@ -4,7 +4,7 @@ import re
from time import gmtime, mktime, strptime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class NowVideoSx(Account):
diff --git a/module/plugins/accounts/OboomCom.py b/module/plugins/accounts/OboomCom.py
index 0acacbb2a..012fb42c3 100644
--- a/module/plugins/accounts/OboomCom.py
+++ b/module/plugins/accounts/OboomCom.py
@@ -4,8 +4,8 @@ import time
from beaker.crypto.pbkdf2 import PBKDF2
-from module.common.json_layer import json_loads
-from module.plugins.Account import Account
+from pyload.utils import json_loads
+from pyload.plugin.Account import Account
class OboomCom(Account):
diff --git a/module/plugins/accounts/OneFichierCom.py b/module/plugins/accounts/OneFichierCom.py
index 1fc8d994d..65ec841c4 100644
--- a/module/plugins/accounts/OneFichierCom.py
+++ b/module/plugins/accounts/OneFichierCom.py
@@ -6,7 +6,7 @@ from time import strptime, mktime
from pycurl import REFERER
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class OneFichierCom(Account):
diff --git a/module/plugins/accounts/OverLoadMe.py b/module/plugins/accounts/OverLoadMe.py
index d59944e63..d945dd7bd 100644
--- a/module/plugins/accounts/OverLoadMe.py
+++ b/module/plugins/accounts/OverLoadMe.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class OverLoadMe(Account):
diff --git a/module/plugins/accounts/PremiumTo.py b/module/plugins/accounts/PremiumTo.py
index c8ea2fa26..04bbc10d5 100644
--- a/module/plugins/accounts/PremiumTo.py
+++ b/module/plugins/accounts/PremiumTo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class PremiumTo(Account):
@@ -15,6 +15,7 @@ class PremiumTo(Account):
("stickell", "l.stickell@yahoo.it")]
+
def loadAccountInfo(self, user, req):
traffic = req.load("http://premium.to/api/straffic.php",
get={'username': self.username, 'password': self.password})
diff --git a/module/plugins/accounts/PremiumizeMe.py b/module/plugins/accounts/PremiumizeMe.py
index 7d061ec2d..3cd15ce23 100644
--- a/module/plugins/accounts/PremiumizeMe.py
+++ b/module/plugins/accounts/PremiumizeMe.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
-from module.common.json_layer import json_loads
+from pyload.utils import json_loads
class PremiumizeMe(Account):
diff --git a/module/plugins/accounts/QuickshareCz.py b/module/plugins/accounts/QuickshareCz.py
index 16141d63e..2bcde1c9d 100644
--- a/module/plugins/accounts/QuickshareCz.py
+++ b/module/plugins/accounts/QuickshareCz.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class QuickshareCz(Account):
diff --git a/module/plugins/accounts/RPNetBiz.py b/module/plugins/accounts/RPNetBiz.py
index 829e54a46..e0b35b68c 100644
--- a/module/plugins/accounts/RPNetBiz.py
+++ b/module/plugins/accounts/RPNetBiz.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class RPNetBiz(Account):
diff --git a/module/plugins/accounts/RapidfileshareNet.py b/module/plugins/accounts/RapidfileshareNet.py
index c0dd7eaee..ec0bf8db4 100644
--- a/module/plugins/accounts/RapidfileshareNet.py
+++ b/module/plugins/accounts/RapidfileshareNet.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class RapidfileshareNet(XFSAccount):
diff --git a/module/plugins/accounts/RapidgatorNet.py b/module/plugins/accounts/RapidgatorNet.py
index 23d7aff53..7643f07d2 100644
--- a/module/plugins/accounts/RapidgatorNet.py
+++ b/module/plugins/accounts/RapidgatorNet.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class RapidgatorNet(Account):
diff --git a/module/plugins/accounts/RarefileNet.py b/module/plugins/accounts/RarefileNet.py
index 577a6c8f6..1dc93681c 100644
--- a/module/plugins/accounts/RarefileNet.py
+++ b/module/plugins/accounts/RarefileNet.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class RarefileNet(XFSAccount):
diff --git a/module/plugins/accounts/RealdebridCom.py b/module/plugins/accounts/RealdebridCom.py
index 41d8a0975..07ff70496 100644
--- a/module/plugins/accounts/RealdebridCom.py
+++ b/module/plugins/accounts/RealdebridCom.py
@@ -2,7 +2,7 @@
import xml.dom.minidom as dom
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class RealdebridCom(Account):
diff --git a/module/plugins/accounts/RehostTo.py b/module/plugins/accounts/RehostTo.py
index 04e71c9ad..d62e1918a 100644
--- a/module/plugins/accounts/RehostTo.py
+++ b/module/plugins/accounts/RehostTo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class RehostTo(Account):
diff --git a/module/plugins/accounts/RyushareCom.py b/module/plugins/accounts/RyushareCom.py
index 8c56ff20f..466d971f6 100644
--- a/module/plugins/accounts/RyushareCom.py
+++ b/module/plugins/accounts/RyushareCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class RyushareCom(XFSAccount):
diff --git a/module/plugins/accounts/SafesharingEu.py b/module/plugins/accounts/SafesharingEu.py
index 2e58d33b3..f5cbf050e 100644
--- a/module/plugins/accounts/SafesharingEu.py
+++ b/module/plugins/accounts/SafesharingEu.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class SafesharingEu(XFSAccount):
diff --git a/module/plugins/accounts/SecureUploadEu.py b/module/plugins/accounts/SecureUploadEu.py
index b335c94da..bb47bcba3 100644
--- a/module/plugins/accounts/SecureUploadEu.py
+++ b/module/plugins/accounts/SecureUploadEu.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class SecureUploadEu(XFSAccount):
diff --git a/module/plugins/accounts/SendmywayCom.py b/module/plugins/accounts/SendmywayCom.py
index 4fcbe0b7a..d64658de3 100644
--- a/module/plugins/accounts/SendmywayCom.py
+++ b/module/plugins/accounts/SendmywayCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class SendmywayCom(XFSAccount):
diff --git a/module/plugins/accounts/ShareonlineBiz.py b/module/plugins/accounts/ShareonlineBiz.py
index 7e05e2e76..28bc3b9bc 100644
--- a/module/plugins/accounts/ShareonlineBiz.py
+++ b/module/plugins/accounts/ShareonlineBiz.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class ShareonlineBiz(Account):
diff --git a/module/plugins/accounts/SimplyPremiumCom.py b/module/plugins/accounts/SimplyPremiumCom.py
index 8caf600f9..298ad8d59 100644
--- a/module/plugins/accounts/SimplyPremiumCom.py
+++ b/module/plugins/accounts/SimplyPremiumCom.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.common.json_layer import json_loads
-from module.plugins.Account import Account
+from pyload.utils import json_loads
+from pyload.plugin.Account import Account
class SimplyPremiumCom(Account):
diff --git a/module/plugins/accounts/SimplydebridCom.py b/module/plugins/accounts/SimplydebridCom.py
index 29be2f73d..a826e44c7 100644
--- a/module/plugins/accounts/SimplydebridCom.py
+++ b/module/plugins/accounts/SimplydebridCom.py
@@ -2,7 +2,7 @@
from time import mktime, strptime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class SimplydebridCom(Account):
diff --git a/module/plugins/accounts/StahnuTo.py b/module/plugins/accounts/StahnuTo.py
index 882dbd2c3..ed8df3b77 100644
--- a/module/plugins/accounts/StahnuTo.py
+++ b/module/plugins/accounts/StahnuTo.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class StahnuTo(Account):
diff --git a/module/plugins/accounts/StreamcloudEu.py b/module/plugins/accounts/StreamcloudEu.py
index aa1eafcbd..3ac74fbd0 100644
--- a/module/plugins/accounts/StreamcloudEu.py
+++ b/module/plugins/accounts/StreamcloudEu.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class StreamcloudEu(XFSAccount):
diff --git a/module/plugins/accounts/TurbobitNet.py b/module/plugins/accounts/TurbobitNet.py
index a857649eb..e3c07da2a 100644
--- a/module/plugins/accounts/TurbobitNet.py
+++ b/module/plugins/accounts/TurbobitNet.py
@@ -3,7 +3,7 @@
import re
from time import mktime, strptime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class TurbobitNet(Account):
diff --git a/module/plugins/accounts/TusfilesNet.py b/module/plugins/accounts/TusfilesNet.py
index 279dfd00a..84e9ef9c6 100644
--- a/module/plugins/accounts/TusfilesNet.py
+++ b/module/plugins/accounts/TusfilesNet.py
@@ -4,7 +4,7 @@ import re
from time import mktime, strptime, gmtime
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class TusfilesNet(XFSAccount):
@@ -12,7 +12,7 @@ class TusfilesNet(XFSAccount):
__type__ = "account"
__version__ = "0.06"
- __description__ = """ Tusfile.net account plugin """
+ __description__ = """Tusfile.net account plugin"""
__license__ = "GPLv3"
__authors__ = [("guidobelix", "guidobelix@hotmail.it")]
diff --git a/module/plugins/accounts/UlozTo.py b/module/plugins/accounts/UlozTo.py
index 7236a4fa8..1570419b0 100644
--- a/module/plugins/accounts/UlozTo.py
+++ b/module/plugins/accounts/UlozTo.py
@@ -4,7 +4,7 @@ import re
from urlparse import urljoin
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class UlozTo(Account):
@@ -15,7 +15,7 @@ class UlozTo(Account):
__description__ = """Uloz.to account plugin"""
__license__ = "GPLv3"
__authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("pulpe", None)]
+ ("pulpe", "")]
TRAFFIC_LEFT_PATTERN = r'<li class="menu-kredit"><a .*?title="[^"]*?GB = ([\d.]+) MB"'
diff --git a/module/plugins/accounts/UnrestrictLi.py b/module/plugins/accounts/UnrestrictLi.py
index 6a8187234..d8d7789bb 100644
--- a/module/plugins/accounts/UnrestrictLi.py
+++ b/module/plugins/accounts/UnrestrictLi.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
+from pyload.plugin.Account import Account
+from pyload.utils import json_loads
class UnrestrictLi(Account):
diff --git a/module/plugins/accounts/UploadcCom.py b/module/plugins/accounts/UploadcCom.py
index d1e1a2ead..66863c456 100644
--- a/module/plugins/accounts/UploadcCom.py
+++ b/module/plugins/accounts/UploadcCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class UploadcCom(XFSAccount):
diff --git a/module/plugins/accounts/UploadedTo.py b/module/plugins/accounts/UploadedTo.py
index 4e5c8035b..3b0d957a5 100644
--- a/module/plugins/accounts/UploadedTo.py
+++ b/module/plugins/accounts/UploadedTo.py
@@ -3,7 +3,7 @@
import re
from time import time
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class UploadedTo(Account):
diff --git a/module/plugins/accounts/UploadheroCom.py b/module/plugins/accounts/UploadheroCom.py
index 714f5b0a6..c73fc30f5 100644
--- a/module/plugins/accounts/UploadheroCom.py
+++ b/module/plugins/accounts/UploadheroCom.py
@@ -4,7 +4,7 @@ import re
import datetime
import time
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class UploadheroCom(Account):
diff --git a/module/plugins/accounts/UploadingCom.py b/module/plugins/accounts/UploadingCom.py
index c70d2ec11..6d54469e8 100644
--- a/module/plugins/accounts/UploadingCom.py
+++ b/module/plugins/accounts/UploadingCom.py
@@ -4,8 +4,8 @@ import re
from time import time, strptime, mktime
-from module.plugins.Account import Account
-from module.plugins.internal.SimpleHoster import set_cookies
+from pyload.plugin.Account import Account
+from pyload.plugin.internal.SimpleHoster import set_cookies
class UploadingCom(Account):
diff --git a/module/plugins/accounts/UptoboxCom.py b/module/plugins/accounts/UptoboxCom.py
index c40dbd6e6..f7cb7a82e 100644
--- a/module/plugins/accounts/UptoboxCom.py
+++ b/module/plugins/accounts/UptoboxCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class UptoboxCom(XFSAccount):
diff --git a/module/plugins/accounts/VidPlayNet.py b/module/plugins/accounts/VidPlayNet.py
index 5bfc24963..390520a00 100644
--- a/module/plugins/accounts/VidPlayNet.py
+++ b/module/plugins/accounts/VidPlayNet.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class VidPlayNet(XFSAccount):
diff --git a/module/plugins/accounts/XFileSharingPro.py b/module/plugins/accounts/XFileSharingPro.py
index 8dc7f3a30..216af5385 100644
--- a/module/plugins/accounts/XFileSharingPro.py
+++ b/module/plugins/accounts/XFileSharingPro.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSAccount import XFSAccount
+from pyload.plugin.internal.XFSAccount import XFSAccount
class XFileSharingPro(XFSAccount):
diff --git a/module/plugins/accounts/YibaishiwuCom.py b/module/plugins/accounts/YibaishiwuCom.py
index 33602a2fe..150b0d931 100644
--- a/module/plugins/accounts/YibaishiwuCom.py
+++ b/module/plugins/accounts/YibaishiwuCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class YibaishiwuCom(Account):
diff --git a/module/plugins/accounts/ZeveraCom.py b/module/plugins/accounts/ZeveraCom.py
index d1585111a..db23170f3 100644
--- a/module/plugins/accounts/ZeveraCom.py
+++ b/module/plugins/accounts/ZeveraCom.py
@@ -2,7 +2,7 @@
from time import mktime, strptime
-from module.plugins.Account import Account
+from pyload.plugin.Account import Account
class ZeveraCom(Account):
diff --git a/module/plugins/accounts/__init__.py b/module/plugins/accounts/__init__.py
index e69de29bb..40a96afc6 100644
--- a/module/plugins/accounts/__init__.py
+++ b/module/plugins/accounts/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/plugins/hooks/Checksum.py b/module/plugins/addon/Checksum.py
index 064375a41..35be60773 100644
--- a/module/plugins/hooks/Checksum.py
+++ b/module/plugins/addon/Checksum.py
@@ -9,8 +9,8 @@ import zlib
from os import remove
from os.path import getsize, isfile, splitext
-from module.plugins.Hook import Hook
-from module.utils import save_join, fs_encode
+from pyload.plugin.Addon import Addon
+from pyload.utils import safe_join, fs_encode
def computeChecksum(local_file, algorithm):
@@ -37,16 +37,17 @@ def computeChecksum(local_file, algorithm):
return None
-class Checksum(Hook):
+class Checksum(Addon):
__name__ = "Checksum"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "0.16"
- __config__ = [("check_checksum", "bool", "Check checksum? (If False only size will be verified)", True),
- ("check_action", "fail;retry;nothing", "What to do if check fails?", "retry"),
- ("max_tries", "int", "Number of retries", 2),
- ("retry_action", "fail;nothing", "What to do if all retries fail?", "fail"),
- ("wait_time", "int", "Time to wait before each retry (seconds)", 1)]
+ __config__ = [("activated" , "bool" , "Activated" , True ),
+ ("check_checksum", "bool" , "Check checksum? (If False only size will be verified)", True ),
+ ("check_action" , "fail;retry;nothing", "What to do if check fails?" , "retry"),
+ ("max_tries" , "int" , "Number of retries" , 2 ),
+ ("retry_action" , "fail;nothing" , "What to do if all retries fail?" , "fail" ),
+ ("wait_time" , "int" , "Time to wait before each retry (seconds)" , 1 )]
__description__ = """Verify downloaded file size and checksum"""
__license__ = "GPLv3"
@@ -64,12 +65,7 @@ class Checksum(Hook):
'default': r'^(?P<HASH>[0-9A-Fa-f]+)\s+\*?(?P<NAME>.+)$'}
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
- def coreReady(self):
+ def activate(self):
if not self.getConfig("check_checksum"):
self.logInfo(_("Checksum validation is disabled in plugin configuration"))
@@ -108,7 +104,7 @@ class Checksum(Hook):
local_file = fs_encode(pyfile.plugin.lastDownload)
#download_folder = self.config['general']['download_folder']
- #local_file = fs_encode(save_join(download_folder, pyfile.package().folder, pyfile.name))
+ #local_file = fs_encode(safe_join(download_folder, pyfile.package().folder, pyfile.name))
if not isfile(local_file):
self.checkFailed(pyfile, None, "File does not exist")
@@ -168,7 +164,7 @@ class Checksum(Hook):
def packageFinished(self, pypack):
- download_folder = save_join(self.config['general']['download_folder'], pypack.folder, "")
+ download_folder = safe_join(self.config['general']['download_folder'], pypack.folder, "")
for link in pypack.getChildren().itervalues():
file_type = splitext(link['name'])[1][1:].lower()
@@ -176,7 +172,7 @@ class Checksum(Hook):
if file_type not in self.formats:
continue
- hash_file = fs_encode(save_join(download_folder, link['name']))
+ hash_file = fs_encode(safe_join(download_folder, link['name']))
if not isfile(hash_file):
self.logWarning(_("File not found"), link['name'])
continue
@@ -188,7 +184,7 @@ class Checksum(Hook):
data = m.groupdict()
self.logDebug(link['name'], data)
- local_file = fs_encode(save_join(download_folder, data['NAME']))
+ local_file = fs_encode(safe_join(download_folder, data['NAME']))
algorithm = self.methods.get(file_type, file_type)
checksum = computeChecksum(local_file, algorithm)
if checksum == data['HASH']:
diff --git a/module/plugins/addon/ClickAndLoad.py b/module/plugins/addon/ClickAndLoad.py
new file mode 100644
index 000000000..5fe6e4bec
--- /dev/null
+++ b/module/plugins/addon/ClickAndLoad.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+
+from socket import socket, error
+from threading import Thread
+
+from pyload.plugin.Addon import Addon
+
+
+def forward(source, destination):
+ string = ' '
+ while string:
+ string = source.recv(1024)
+ if string:
+ destination.sendall(string)
+ else:
+ #source.shutdown(socket.SHUT_RD)
+ destination.shutdown(socket.SHUT_WR)
+
+
+class ClickAndLoad(Addon):
+ __name__ = "ClickAndLoad"
+ __type__ = "addon"
+ __version__ = "0.23"
+
+ __config__ = [("activated", "bool", "Activated" , True ),
+ ("port" , "int" , "Port" , 9666 ),
+ ("extern" , "bool", "Allow external link adding", False)]
+
+ __description__ = """Click'N'Load hook plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("RaNaN", "RaNaN@pyload.de"),
+ ("mkaay", "mkaay@mkaay.de"),
+ ("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ def setup(self):
+ self.interval = 300
+
+
+ def activate(self):
+ self.initPeriodical()
+
+
+ def periodical(self):
+ webip = "0.0.0.0" if self.getConfig("extern") else "127.0.0.1"
+ webport = self.config['webinterface']['port']
+ cnlport = self.getConfig("port"))
+
+ try:
+ s = socket()
+ s.bind((webip, cnlport))
+ s.listen(5)
+
+ client = s.accept()[0]
+ server = socket()
+
+ server.connect(("127.0.0.1", webport))
+
+ except error, e:
+ if hasattr(e, "errno"):
+ errno = e.errno
+ else:
+ errno = e.args[0]
+
+ if errno == 98:
+ self.logWarning(_("Port %d already in use") % cnlport)
+ else:
+ self.logDebug(e)
+
+ else:
+ self.core.scheduler.removeJob(self.cb)
+ t = Thread(target=forward, args=[client, server])
+ t.setDaemon(True)
+ t.start()
diff --git a/module/plugins/hooks/DeleteFinished.py b/module/plugins/addon/DeleteFinished.py
index 5d2b78d50..59f2e3321 100644
--- a/module/plugins/hooks/DeleteFinished.py
+++ b/module/plugins/addon/DeleteFinished.py
@@ -1,17 +1,16 @@
# -*- coding: utf-8 -*-
-from module.database import style
-from module.plugins.Hook import Hook
+from pyload.database import style
+from pyload.plugin.Addon import Addon
-class DeleteFinished(Hook):
+class DeleteFinished(Addon):
__name__ = "DeleteFinished"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "1.11"
- __config__ = [('activated', 'bool', 'Activated', 'False'),
- ('interval', 'int', 'Delete every (hours)', '72'),
- ('deloffline', 'bool', 'Delete packages with offline links', 'False')]
+ __config__ = [('interval' , 'int' , 'Delete every (hours)' , '72' ),
+ ('deloffline', 'bool', 'Delete packages with offline links', 'False')]
__description__ = """Automatically delete all finished packages from queue"""
__license__ = "GPLv3"
@@ -39,11 +38,11 @@ class DeleteFinished(Hook):
self.initPeriodical()
- def unload(self):
+ def deactivate(self):
self.removeEvent('packageFinished', self.wakeup)
- def coreReady(self):
+ def activate(self):
self.info = {'sleep': True}
interval = self.getConfig('interval')
self.pluginConfigChanged(self.__name__, 'interval', interval)
@@ -75,5 +74,6 @@ class DeleteFinished(Hook):
def setup(self):
+ self.interval = 0
self.m = self.manager
self.removeEvent = self.m.removeEvent
diff --git a/module/plugins/hooks/DownloadScheduler.py b/module/plugins/addon/DownloadScheduler.py
index 4996e212d..e5e25e389 100644
--- a/module/plugins/hooks/DownloadScheduler.py
+++ b/module/plugins/addon/DownloadScheduler.py
@@ -4,17 +4,16 @@ import re
from time import localtime
-from module.plugins.Hook import Hook
+from pyload.plugin.Addon import Addon
-class DownloadScheduler(Hook):
+class DownloadScheduler(Addon):
__name__ = "DownloadScheduler"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "0.22"
- __config__ = [("timetable", "str", "List time periods as hh:mm full or number(kB/s)",
- "0:00 full, 7:00 250, 10:00 0, 17:00 150"),
- ("abort", "bool", "Abort active downloads when start period with speed 0", False)]
+ __config__ = [("timetable", "str" , "List time periods as hh:mm full or number(kB/s)" , "0:00 full, 7:00 250, 10:00 0, 17:00 150"),
+ ("abort" , "bool", "Abort active downloads when start period with speed 0", False )]
__description__ = """Download Scheduler"""
__license__ = "GPLv3"
@@ -22,16 +21,11 @@ class DownloadScheduler(Hook):
("stickell", "l.stickell@yahoo.it")]
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
def setup(self):
- self.cb = None # callback to scheduler job; will be by removed hookmanager when hook unloaded
+ self.cb = None #: callback to scheduler job; will be by removed AddonManager when addon unloaded
- def coreReady(self):
+ def activate(self):
self.updateSchedule()
diff --git a/module/plugins/hooks/ExternalScripts.py b/module/plugins/addon/ExternalScripts.py
index 8bd803308..5aebf2338 100644
--- a/module/plugins/hooks/ExternalScripts.py
+++ b/module/plugins/addon/ExternalScripts.py
@@ -5,13 +5,13 @@ import subprocess
from itertools import chain
-from module.plugins.Hook import Hook
-from module.utils import save_join
+from pyload.plugin.Addon import Addon
+from pyload.utils import safe_join
-class ExternalScripts(Hook):
+class ExternalScripts(Addon):
__name__ = "ExternalScripts"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "0.29"
__config__ = [("activated", "bool", "Activated" , True ),
@@ -20,18 +20,17 @@ class ExternalScripts(Hook):
__description__ = """Run external scripts"""
__license__ = "GPLv3"
__authors__ = [("mkaay", "mkaay@mkaay.de"),
- ("RaNaN", "ranan@pyload.org"),
- ("spoob", "spoob@pyload.org"),
- ("Walter Purcaro", "vuolter@gmail.com")]
+ ("RaNaN", "ranan@pyload.org"),
+ ("spoob", "spoob@pyload.org"),
+ ("Walter Purcaro", "vuolter@gmail.com")]
- event_list = ["archive_extracted", "package_extracted", "all_archives_extracted", "all_archives_processed",
- "allDownloadsFinished", "allDownloadsProcessed"]
-
-
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
+ event_map = {'archive-extracted' : "archive_extracted",
+ 'package-extracted' : "package_extracted",
+ 'all_archives-extracted' : "all_archives_extracted",
+ 'all_archives-processed' : "all_archives_processed",
+ 'all_downloads-finished' : "allDownloadsFinished",
+ 'all_downloads-processed': "allDownloadsProcessed"}
def setup(self):
@@ -96,14 +95,14 @@ class ExternalScripts(Hook):
def downloadFinished(self, pyfile):
download_folder = self.config['general']['download_folder']
for script in self.scripts['download_finished']:
- filename = save_join(download_folder, pyfile.package().folder, pyfile.name)
+ filename = safe_join(download_folder, pyfile.package().folder, pyfile.name)
self.callScript(script, pyfile.pluginname, pyfile.url, pyfile.name, filename, pyfile.id)
def packageFinished(self, pypack):
download_folder = self.config['general']['download_folder']
for script in self.scripts['package_finished']:
- folder = save_join(download_folder, pypack.folder)
+ folder = safe_join(download_folder, pypack.folder)
self.callScript(script, pypack.name, folder, pypack.password, pypack.id)
@@ -127,7 +126,7 @@ class ExternalScripts(Hook):
def package_extracted(self, pypack):
download_folder = self.config['general']['download_folder']
for script in self.scripts['package_extracted']:
- folder = save_join(download_folder, pypack.folder)
+ folder = safe_join(download_folder, pypack.folder)
self.callScript(script, pypack.name, folder, pypack.password, pypack.id)
diff --git a/module/plugins/hooks/ExtractArchive.py b/module/plugins/addon/ExtractArchive.py
index 3ea8839dc..3ea8839dc 100644
--- a/module/plugins/hooks/ExtractArchive.py
+++ b/module/plugins/addon/ExtractArchive.py
diff --git a/module/plugins/hooks/HotFolder.py b/module/plugins/addon/HotFolder.py
index 1ff02c319..eb607ac7e 100644
--- a/module/plugins/hooks/HotFolder.py
+++ b/module/plugins/addon/HotFolder.py
@@ -8,19 +8,19 @@ from os import listdir, makedirs
from os.path import exists, isfile, join
from shutil import move
-from module.plugins.Hook import Hook
-from module.utils import fs_encode, save_join
+from pyload.plugin.Addon import Addon
+from pyload.utils import fs_encode, safe_join
-class HotFolder(Hook):
+class HotFolder(Addon):
__name__ = "HotFolder"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "0.12"
- __config__ = [("folder", "str", "Folder to observe", "container"),
- ("watch_file", "bool", "Observe link file", False),
- ("keep", "bool", "Keep added containers", True),
- ("file", "str", "Link file", "links.txt")]
+ __config__ = [("folder" , "str" , "Folder to observe" , "container"),
+ ("watch_file", "bool", "Observe link file" , False ),
+ ("keep" , "bool", "Keep added containers", True ),
+ ("file" , "str" , "Link file" , "links.txt")]
__description__ = """Observe folder and file for changes and add container and links"""
__license__ = "GPLv3"
@@ -31,6 +31,10 @@ class HotFolder(Hook):
self.interval = 10
+ def activate(self):
+ self.initPeriodical()
+
+
def periodical(self):
folder = fs_encode(self.getConfig("folder"))
@@ -46,7 +50,7 @@ class HotFolder(Hook):
if content:
name = "%s_%s.txt" % (self.getConfig("file"), time.strftime("%H-%M-%S_%d%b%Y"))
- with open(save_join(folder, "finished", name), "wb") as f:
+ with open(safe_join(folder, "finished", name), "wb") as f:
f.write(content)
self.core.api.addPackage(f.name, [f.name], 1)
diff --git a/module/plugins/hooks/IRCInterface.py b/module/plugins/addon/IRCInterface.py
index 623f2d1bf..86d9ea688 100644
--- a/module/plugins/hooks/IRCInterface.py
+++ b/module/plugins/addon/IRCInterface.py
@@ -11,27 +11,27 @@ from threading import Thread
from time import sleep
from traceback import print_exc
-from module.Api import PackageDoesNotExists, FileDoesNotExists
-from module.network.RequestFactory import getURL
-from module.plugins.Hook import Hook
-from module.utils import formatSize
+from pyload.api import PackageDoesNotExists, FileDoesNotExists
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.Addon import Addon
+from pyload.utils import formatSize
-class IRCInterface(Thread, Hook):
+class IRCInterface(Thread, Addon):
__name__ = "IRCInterface"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "0.13"
- __config__ = [("host", "str", "IRC-Server Address", "Enter your server here!"),
- ("port", "int", "IRC-Server Port", 6667),
- ("ident", "str", "Clients ident", "pyload-irc"),
- ("realname", "str", "Realname", "pyload-irc"),
- ("ssl", "bool", "Use SSL", False),
- ("nick", "str", "Nickname the Client will take", "pyLoad-IRC"),
- ("owner", "str", "Nickname the Client will accept commands from", "Enter your nick here!"),
- ("info_file", "bool", "Inform about every file finished", False),
- ("info_pack", "bool", "Inform about every package finished", True),
- ("captcha", "bool", "Send captcha requests", True)]
+ __config__ = [("host" , "str" , "IRC-Server Address" , "Enter your server here!"),
+ ("port" , "int" , "IRC-Server Port" , 6667 ),
+ ("ident" , "str" , "Clients ident" , "pyload-irc" ),
+ ("realname" , "str" , "Realname" , "pyload-irc" ),
+ ("ssl" , "bool", "Use SSL" , False ),
+ ("nick" , "str" , "Nickname the Client will take" , "pyLoad-IRC" ),
+ ("owner" , "str" , "Nickname the Client will accept commands from", "Enter your nick here!" ),
+ ("info_file", "bool", "Inform about every file finished" , False ),
+ ("info_pack", "bool", "Inform about every package finished" , True ),
+ ("captcha" , "bool", "Send captcha requests" , True )]
__description__ = """Connect to irc and let owner perform different tasks"""
__license__ = "GPLv3"
@@ -40,16 +40,11 @@ class IRCInterface(Thread, Hook):
def __init__(self, core, manager):
Thread.__init__(self)
- Hook.__init__(self, core, manager)
+ Addon.__init__(self, core, manager)
self.setDaemon(True)
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
- def coreReady(self):
+ def activate(self):
self.abort = False
self.more = []
self.new_package = {}
@@ -74,7 +69,7 @@ class IRCInterface(Thread, Hook):
pass
- def newCaptchaTask(self, task):
+ def captchaTask(self, task):
if self.getConfig("captcha") and task.isTextual():
task.handler.append(self)
task.setWaiting(60)
@@ -94,7 +89,7 @@ class IRCInterface(Thread, Hook):
self.sock.connect((host, self.getConfig("port")))
if self.getConfig("ssl"):
- self.sock = ssl.wrap_socket(self.sock, cert_reqs=ssl.CERT_NONE) #@TODO: support certificate
+ self.sock = ssl.wrap_socket(self.sock, cert_reqs=ssl.CERT_NONE) #@TODO: support custom certificate
nick = self.getConfig("nick")
self.sock.send("NICK %s\r\n" % nick)
diff --git a/module/plugins/hooks/MergeFiles.py b/module/plugins/addon/MergeFiles.py
index 9f1348485..42ac3ff4d 100644
--- a/module/plugins/hooks/MergeFiles.py
+++ b/module/plugins/addon/MergeFiles.py
@@ -7,13 +7,13 @@ import re
from traceback import print_exc
-from module.plugins.Hook import Hook, threaded
-from module.utils import save_join
+from pyload.plugin.Addon import Addon, threaded
+from module.utils import safe_join
-class MergeFiles(Hook):
+class MergeFiles(Addon):
__name__ = "MergeFiles"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "0.14"
__config__ = [("activated", "bool", "Activated", True)]
@@ -26,13 +26,7 @@ class MergeFiles(Hook):
BUFFER_SIZE = 4096
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
def setup(self):
- # nothing to do
pass
@@ -51,12 +45,12 @@ class MergeFiles(Hook):
download_folder = self.config['general']['download_folder']
if self.config['general']['folder_per_package']:
- download_folder = save_join(download_folder, pack.folder)
+ download_folder = safe_join(download_folder, pack.folder)
for name, file_list in files.iteritems():
self.logInfo(_("Starting merging of"), name)
- with open(save_join(download_folder, name), "wb") as final_file:
+ final_file = open(safe_join(download_folder, name), "wb")
for splitted_file in file_list:
self.logDebug("Merging part", splitted_file)
@@ -68,6 +62,7 @@ class MergeFiles(Hook):
with open(save_join(download_folder, splitted_file), "rb") as s_file:
size_written = 0
s_file_size = int(os.path.getsize(os.path.join(download_folder, splitted_file)))
+
while True:
f_buffer = s_file.read(self.BUFFER_SIZE)
if f_buffer:
@@ -76,6 +71,7 @@ class MergeFiles(Hook):
pyfile.setProgress((size_written * 100) / s_file_size)
else:
break
+
self.logDebug("Finished merging part", splitted_file)
except Exception, e:
diff --git a/module/plugins/hooks/MultiHome.py b/module/plugins/addon/MultiHome.py
index c9f6fc30c..521749fc8 100644
--- a/module/plugins/hooks/MultiHome.py
+++ b/module/plugins/addon/MultiHome.py
@@ -2,12 +2,12 @@
from time import time
-from module.plugins.Hook import Hook
+from pyload.plugin.Addon import Addon
-class MultiHome(Hook):
+class MultiHome(Addon):
__name__ = "MultiHome"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "0.12"
__config__ = [("interfaces", "str", "Interfaces", "None")]
@@ -17,11 +17,6 @@ class MultiHome(Hook):
__authors__ = [("mkaay", "mkaay@mkaay.de")]
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
def setup(self):
self.register = {}
self.interfaces = []
@@ -42,11 +37,10 @@ class MultiHome(Hook):
self.interfaces.append(Interface(interface))
- def coreReady(self):
+ def activate(self):
requestFactory = self.core.requestFactory
oldGetRequest = requestFactory.getRequest
-
def getRequest(pluginName, account=None):
iface = self.bestInterface(pluginName, account)
if iface:
diff --git a/module/plugins/hooks/RestartFailed.py b/module/plugins/addon/RestartFailed.py
index 07fb80967..2fe5f13bf 100644
--- a/module/plugins/hooks/RestartFailed.py
+++ b/module/plugins/addon/RestartFailed.py
@@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
-from module.plugins.Hook import Hook
+from pyload.plugin.Addon import Addon
-class RestartFailed(Hook):
+class RestartFailed(Addon):
__name__ = "RestartFailed"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "1.57"
- __config__ = [("interval", "int", "Check interval in minutes", 90)]
+ __config__ = [("activated", "bool", "Activated" , True),
+ ("interval" , "int" , "Check interval in minutes", 90 )]
__description__ = """Periodically restart all failed downloads in queue"""
__license__ = "GPLv3"
@@ -37,8 +38,8 @@ class RestartFailed(Hook):
def setup(self):
- self.interval = self.MIN_INTERVAL
+ self.interval = 0
- def coreReady(self):
+ def activate(self):
self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval"))
diff --git a/module/plugins/hooks/RestartSlow.py b/module/plugins/addon/RestartSlow.py
index c3e1e5468..332047da7 100644
--- a/module/plugins/hooks/RestartSlow.py
+++ b/module/plugins/addon/RestartSlow.py
@@ -2,36 +2,32 @@
import pycurl
-from module.plugins.Hook import Hook
+from pyload.plugin.Addon import Addon
-class RestartSlow(Hook):
+class RestartSlow(Addon):
__name__ = "RestartSlow"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "0.04"
__config__ = [("free_limit" , "int" , "Transfer speed threshold in kilobytes" , 100 ),
- ("free_time" , "int" , "Sample interval in minutes" , 5 ),
- ("premium_limit", "int" , "Transfer speed threshold for premium download in kilobytes", 300 ),
- ("premium_time" , "int" , "Sample interval for premium download in minutes" , 2 ),
- ("safe_mode" , "bool", "Don't restart if download is not resumable" , True)]
+ ("free_time" , "int" , "Sample interval in minutes" , 5 ),
+ ("premium_limit", "int" , "Transfer speed threshold for premium download in kilobytes", 300 ),
+ ("premium_time" , "int" , "Sample interval for premium download in minutes" , 2 ),
+ ("safe_mode" , "bool", "Don't restart if download is not resumable" , True)]
__description__ = """Restart slow downloads"""
__license__ = "GPLv3"
__authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
- event_list = ["downloadStarts"]
+ event_map = {'download-start': "downloadStarts"}
def setup(self):
self.info = {'chunk': {}}
- def initPeriodical(self):
- pass
-
-
def periodical(self):
if not self.pyfile.plugin.req.dl:
return
@@ -58,4 +54,4 @@ class RestartSlow(Hook):
if self.cb or (self.getConfig("safe_mode") and not pyfile.plugin.resumeDownload):
return
self.pyfile = pyfile
- super(RestartSlow, self).initPeriodical()
+ self.initPeriodical()
diff --git a/module/plugins/hooks/SkipRev.py b/module/plugins/addon/SkipRev.py
index 0bbdec3b2..0bbdec3b2 100644
--- a/module/plugins/hooks/SkipRev.py
+++ b/module/plugins/addon/SkipRev.py
diff --git a/module/plugins/hooks/UnSkipOnFail.py b/module/plugins/addon/UnSkipOnFail.py
index 1becb937a..1becb937a 100644
--- a/module/plugins/hooks/UnSkipOnFail.py
+++ b/module/plugins/addon/UnSkipOnFail.py
diff --git a/module/plugins/hooks/UpdateManager.py b/module/plugins/addon/UpdateManager.py
index b6a8bac7c..efccc73fb 100644
--- a/module/plugins/hooks/UpdateManager.py
+++ b/module/plugins/addon/UpdateManager.py
@@ -8,24 +8,24 @@ import sys
from operator import itemgetter
from os import path, remove, stat
-from module.network.RequestFactory import getURL
-from module.plugins.Hook import Expose, Hook, threaded
-from module.utils import save_join
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.Addon import Expose, Addon, threaded
+from pyload.utils import safe_join
-class UpdateManager(Hook):
+class UpdateManager(Addon):
__name__ = "UpdateManager"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "0.43"
__config__ = [("activated" , "bool" , "Activated" , True ),
- ("mode" , "pyLoad + plugins;plugins only", "Check updates for" , "pyLoad + plugins"),
- ("interval" , "int" , "Check interval in hours" , 8 ),
- ("autorestart" , "bool" , "Automatically restart pyLoad when required" , True ),
- ("reloadplugins", "bool" , "Monitor plugins for code changes in debug mode", True ),
- ("nodebugupdate", "bool" , "Don't check for updates in debug mode" , False )]
+ ("mode" , "pyLoad + plugins;plugins only", "Check updates for" , "pyLoad + plugins"),
+ ("interval" , "int" , "Check interval in hours" , 8 ),
+ ("autorestart" , "bool" , "Automatically restart pyLoad when required" , True ),
+ ("reloadplugins", "bool" , "Monitor plugins for code changes in debug mode", True ),
+ ("nodebugupdate", "bool" , "Don't check for updates in debug mode" , False )]
- __description__ = """ Check for updates """
+ __description__ = """Check for updates"""
__license__ = "GPLv3"
__authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
@@ -54,19 +54,19 @@ class UpdateManager(Hook):
self.periodical2()
- def coreReady(self):
+ def activate(self):
self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval"))
x = lambda: self.pluginConfigChanged(self.__name__, "reloadplugins", self.getConfig("reloadplugins"))
self.core.scheduler.addJob(10, x, threaded=False)
- def unload(self):
+ def deactivate(self):
self.pluginConfigChanged(self.__name__, "reloadplugins", False)
def setup(self):
self.cb2 = None
- self.interval = self.MIN_INTERVAL
+ self.interval = 0
self.updating = False
self.info = {'pyload': False, 'version': None, 'plugins': False}
self.mtimes = {} #: store modification time for each plugin
@@ -83,7 +83,7 @@ class UpdateManager(Hook):
def autoreloadPlugins(self):
""" reload and reindex all modified plugins """
modules = filter(
- lambda m: m and (m.__name__.startswith("module.plugins.") or
+ lambda m: m and (m.__name__.startswith("pyload.plugin.") or
m.__name__.startswith("userplugins.")) and
m.__name__.count(".") >= 2, sys.modules.itervalues()
)
@@ -110,8 +110,10 @@ class UpdateManager(Hook):
def periodical(self):
- if not self.info['pyload'] and not (self.getConfig("nodebugupdate") and self.core.debug):
- self.updateThread()
+ if self.info['pyload'] or self.getConfig("nodebugupdate") and self.core.debug:
+ return
+
+ self.updateThread()
def server_request(self):
@@ -209,7 +211,7 @@ class UpdateManager(Hook):
for plugin in sorted(upgradable, key=itemgetter("type", "name")):
filename = plugin['name']
- prefix = plugin['type']
+ type = plugin['type']
version = plugin['version']
if filename.endswith(".pyc"):
@@ -217,15 +219,9 @@ class UpdateManager(Hook):
else:
name = filename.replace(".py", "")
- #@TODO: obsolete after 0.4.10
- if prefix.endswith("s"):
- type = prefix[:-1]
- else:
- type = prefix
-
plugins = getattr(self.core.pluginManager, "%sPlugins" % type)
- oldver = float(plugins[name]['v']) if name in plugins else None
+ oldver = float(plugins[name]['version']) if name in plugins else None
newver = float(version)
if not oldver:
@@ -244,7 +240,7 @@ class UpdateManager(Hook):
m = self.VERSION.search(content)
if m and m.group(2) == version:
- with open(save_join("userplugins", prefix, filename), "wb") as f:
+ with open(safe_join("userplugins", prefix, filename), "wb") as f:
f.write(content)
updated.append((prefix, name))
@@ -284,23 +280,23 @@ class UpdateManager(Hook):
err = False
file = name + ".py"
- for root in ("userplugins", path.join(pypath, "module", "plugins")):
+ for root in ("userplugins", path.join(pypath, "pyload", "plugins")):
- filename = save_join(root, type, file)
+ filename = safe_join(root, type, file)
try:
remove(filename)
except Exception, e:
- self.logDebug("Error removing: %s" % path.basename(filename), str(e))
+ self.logDebug("Error deleting: %s" % path.basename(filename), e)
err = True
filename += "c"
if path.isfile(filename):
try:
- if type == "hook":
- self.manager.deactivateHook(name)
+ if type == "addon":
+ self.manager.deactivateAddon(name)
remove(filename)
except Exception, e:
- self.logDebug("Error removing: %s" % path.basename(filename), str(e))
+ self.logDebug("Error deleting: %s" % path.basename(filename), e)
err = True
if not err:
diff --git a/module/plugins/hooks/XMPPInterface.py b/module/plugins/addon/XMPPInterface.py
index b8e9fc1ad..77a49af6f 100644
--- a/module/plugins/hooks/XMPPInterface.py
+++ b/module/plugins/addon/XMPPInterface.py
@@ -6,21 +6,21 @@ from pyxmpp.interface import implements
from pyxmpp.interfaces import *
from pyxmpp.jabber.client import JabberClient
-from module.plugins.hooks.IRCInterface import IRCInterface
+from pyload.plugin.addon.IRCInterface import IRCInterface
class XMPPInterface(IRCInterface, JabberClient):
__name__ = "XMPPInterface"
- __type__ = "hook"
+ __type__ = "addon"
__version__ = "0.11"
- __config__ = [("jid", "str", "Jabber ID", "user@exmaple-jabber-server.org"),
- ("pw", "str", "Password", ""),
- ("tls", "bool", "Use TLS", False),
- ("owners", "str", "List of JIDs accepting commands from", "me@icq-gateway.org;some@msn-gateway.org"),
- ("info_file", "bool", "Inform about every file finished", False),
- ("info_pack", "bool", "Inform about every package finished", True),
- ("captcha", "bool", "Send captcha requests", True)]
+ __config__ = [("jid" , "str" , "Jabber ID" , "user@exmaple-jabber-server.org" ),
+ ("pw" , "str" , "Password" , "" ),
+ ("tls" , "bool", "Use TLS" , False ),
+ ("owners" , "str" , "List of JIDs accepting commands from", "me@icq-gateway.org;some@msn-gateway.org"),
+ ("info_file", "bool", "Inform about every file finished" , False ),
+ ("info_pack", "bool", "Inform about every package finished" , True ),
+ ("captcha" , "bool", "Send captcha requests" , True )]
__description__ = """Connect to jabber and let owner perform different tasks"""
__license__ = "GPLv3"
@@ -59,7 +59,7 @@ class XMPPInterface(IRCInterface, JabberClient):
]
- def coreReady(self):
+ def activate(self):
self.new_package = {}
self.start()
diff --git a/module/plugins/addon/__init__.py b/module/plugins/addon/__init__.py
new file mode 100644
index 000000000..40a96afc6
--- /dev/null
+++ b/module/plugins/addon/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/plugins/captcha/AdYouLike.py b/module/plugins/captcha/AdYouLike.py
new file mode 100644
index 000000000..bfb8b5ccb
--- /dev/null
+++ b/module/plugins/captcha/AdYouLike.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from pyload.plugin.Captcha import Captcha
+from pyload.utils import json_loads
+
+
+class AdYouLike(Captcha):
+ __name__ = "AdYouLike"
+ __type__ = "captcha"
+ __version__ = "0.02"
+
+ __description__ = """AdYouLike captcha service plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ AYL_PATTERN = r'Adyoulike\.create\s*\((.+?)\)'
+ CALLBACK_PATTERN = r'(Adyoulike\.g\._jsonp_\d+)'
+
+
+ def detect_key(self, html=None):
+ if not html:
+ if hasattr(self.plugin, "html") and self.plugin.html:
+ html = self.plugin.html
+ else:
+ errmsg = _("AdYouLike html not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ m = re.search(self.AYL_PATTERN, html)
+ n = re.search(self.CALLBACK_PATTERN, html)
+ if m and n:
+ self.key = (m.group(1).strip(), n.group(1).strip())
+ self.plugin.logDebug("AdYouLike ayl|callback: %s | %s" % self.key)
+ return self.key #: key is the tuple(ayl, callback)
+ else:
+ self.plugin.logDebug("AdYouLike ayl or callback not found")
+ return None
+
+
+ def challenge(self, key=None):
+ if not key:
+ if self.detect_key():
+ key = self.key
+ else:
+ errmsg = _("AdYouLike key not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ ayl, callback = key
+
+ # {"adyoulike":{"key":"P~zQ~O0zV0WTiAzC-iw0navWQpCLoYEP"},
+ # "all":{"element_id":"ayl_private_cap_92300","lang":"fr","env":"prod"}}
+ ayl = json_loads(ayl)
+
+ html = self.plugin.req.load("http://api-ayl.appspot.com/challenge",
+ get={'key' : ayl['adyoulike']['key'],
+ 'env' : ayl['all']['env'],
+ 'callback': callback})
+ try:
+ challenge = json_loads(re.search(callback + r'\s*\((.+?)\)', html).group(1))
+ except Exception:
+ errmsg = _("AdYouLike challenge pattern not found")
+ self.plugin.error(errmsg)
+ raise ValueError(errmsg)
+
+ self.plugin.logDebug("AdYouLike challenge: %s" % challenge)
+
+ return self.result(ayl, challenge)
+
+
+ def result(self, server, challenge):
+ # Adyoulike.g._jsonp_5579316662423138
+ # ({"translations":{"fr":{"instructions_visual":"Recopiez « Soonnight » ci-dessous :"}},
+ # "site_under":true,"clickable":true,"pixels":{"VIDEO_050":[],"DISPLAY":[],"VIDEO_000":[],"VIDEO_100":[],
+ # "VIDEO_025":[],"VIDEO_075":[]},"medium_type":"image/adyoulike",
+ # "iframes":{"big":"<iframe src=\"http://www.soonnight.com/campagn.html\" scrolling=\"no\"
+ # height=\"250\" width=\"300\" frameborder=\"0\"></iframe>"},"shares":{},"id":256,
+ # "token":"e6QuI4aRSnbIZJg02IsV6cp4JQ9~MjA1","formats":{"small":{"y":300,"x":0,"w":300,"h":60},
+ # "big":{"y":0,"x":0,"w":300,"h":250},"hover":{"y":440,"x":0,"w":300,"h":60}},
+ # "tid":"SqwuAdxT1EZoi4B5q0T63LN2AkiCJBg5"})
+
+ if isinstance(server, basestring):
+ server = json_loads(server)
+
+ if isinstance(challenge, basestring):
+ challenge = json_loads(challenge)
+
+ try:
+ instructions_visual = challenge['translations'][server['all']['lang']]['instructions_visual']
+ result = re.search(u'«(.+?)»', instructions_visual).group(1).strip()
+ except Exception:
+ errmsg = _("AdYouLike result not found")
+ self.plugin.error(errmsg)
+ raise ValueError(errmsg)
+
+ result = {'_ayl_captcha_engine' : "adyoulike",
+ '_ayl_env' : server['all']['env'],
+ '_ayl_tid' : challenge['tid'],
+ '_ayl_token_challenge': challenge['token'],
+ '_ayl_response' : response}
+
+ self.plugin.logDebug("AdYouLike result: %s" % result)
+
+ return result
diff --git a/module/plugins/captcha/AdsCaptcha.py b/module/plugins/captcha/AdsCaptcha.py
new file mode 100644
index 000000000..5c01a422e
--- /dev/null
+++ b/module/plugins/captcha/AdsCaptcha.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from random import random
+
+from pyload.plugin.Captcha import Captcha
+
+
+class AdsCaptcha(Captcha):
+ __name__ = "AdsCaptcha"
+ __type__ = "captcha"
+ __version__ = "0.06"
+
+ __description__ = """AdsCaptcha captcha service plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("pyLoad Team", "admin@pyload.org")]
+
+
+ CAPTCHAID_PATTERN = r'api\.adscaptcha\.com/Get\.aspx\?[^"\']*CaptchaId=(\d+)'
+ PUBLICKEY_PATTERN = r'api\.adscaptcha\.com/Get\.aspx\?[^"\']*PublicKey=([\w-]+)'
+
+
+ def detect_key(self, html=None):
+ if not html:
+ if hasattr(self.plugin, "html") and self.plugin.html:
+ html = self.plugin.html
+ else:
+ errmsg = _("AdsCaptcha html not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ m = re.search(self.PUBLICKEY_PATTERN, html)
+ n = re.search(self.CAPTCHAID_PATTERN, html)
+ if m and n:
+ self.key = (m.group(1).strip(), n.group(1).strip()) #: key is the tuple(PublicKey, CaptchaId)
+ self.plugin.logDebug("AdsCaptcha key|id: %s | %s" % self.key)
+ return self.key
+ else:
+ self.plugin.logDebug("AdsCaptcha key or id not found")
+ return None
+
+
+ def challenge(self, key=None):
+ if not key:
+ if self.detect_key():
+ key = self.key
+ else:
+ errmsg = _("AdsCaptcha key not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ PublicKey, CaptchaId = key
+
+ html = self.plugin.req.load("http://api.adscaptcha.com/Get.aspx", get={'CaptchaId': CaptchaId, 'PublicKey': PublicKey})
+ try:
+ challenge = re.search("challenge: '(.+?)',", html).group(1)
+ server = re.search("server: '(.+?)',", html).group(1)
+ except Exception:
+ errmsg = _("AdsCaptcha challenge pattern not found")
+ self.plugin.error(errmsg)
+ raise ValueError(errmsg)
+
+ self.plugin.logDebug("AdsCaptcha challenge: %s" % challenge)
+
+ return challenge, self.result(server, challenge)
+
+
+ def result(self, server, challenge):
+ result = self.plugin.decryptCaptcha("%sChallenge.aspx" % server,
+ get={'cid': challenge, 'dummy': random()},
+ cookies=True,
+ imgtype="jpg")
+
+ self.plugin.logDebug("AdsCaptcha result: %s" % result)
+
+ return result
diff --git a/module/plugins/captcha/ReCaptcha.py b/module/plugins/captcha/ReCaptcha.py
new file mode 100644
index 000000000..4b900c57b
--- /dev/null
+++ b/module/plugins/captcha/ReCaptcha.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from pyload.plugin.Captcha import Captcha
+
+
+class ReCaptcha(Captcha):
+ __name__ = "ReCaptcha"
+ __type__ = "captcha"
+ __version__ = "0.08"
+
+ __description__ = """ReCaptcha captcha service plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("pyLoad Team", "admin@pyload.org")]
+
+
+ KEY_PATTERN = r'recaptcha(?:/api|\.net)/(?:challenge|noscript)\?k=([\w-]+)'
+ KEY_AJAX_PATTERN = r'Recaptcha\.create\s*\(\s*["\']([\w-]+)'
+
+
+ def detect_key(self, html=None):
+ if not html:
+ if hasattr(self.plugin, "html") and self.plugin.html:
+ html = self.plugin.html
+ else:
+ errmsg = _("ReCaptcha html not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ m = re.search(self.KEY_PATTERN, html) or re.search(self.KEY_AJAX_PATTERN, html)
+ if m:
+ self.key = m.group(1).strip()
+ self.plugin.logDebug("ReCaptcha key: %s" % self.key)
+ return self.key
+ else:
+ self.plugin.logDebug("ReCaptcha key not found")
+ return None
+
+
+ def challenge(self, key=None):
+ if not key:
+ if self.detect_key():
+ key = self.key
+ else:
+ errmsg = _("ReCaptcha key not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ html = self.plugin.req.load("http://www.google.com/recaptcha/api/challenge", get={'k': key})
+ try:
+ challenge = re.search("challenge : '(.+?)',", html).group(1)
+ server = re.search("server : '(.+?)',", html).group(1)
+ except Exception:
+ errmsg = _("ReCaptcha challenge pattern not found")
+ self.plugin.error(errmsg)
+ raise ValueError(errmsg)
+
+ self.plugin.logDebug("ReCaptcha challenge: %s" % challenge)
+
+ return challenge, self.result(server, challenge)
+
+
+ def result(self, server, challenge):
+ result = self.plugin.decryptCaptcha("%simage" % server,
+ get={'c': challenge},
+ cookies=True,
+ forceUser=True,
+ imgtype="jpg")
+
+ self.plugin.logDebug("ReCaptcha result: %s" % result)
+
+ return result
diff --git a/module/plugins/captcha/SolveMedia.py b/module/plugins/captcha/SolveMedia.py
new file mode 100644
index 000000000..6e2a6c362
--- /dev/null
+++ b/module/plugins/captcha/SolveMedia.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from pyload.plugin.Captcha import Captcha
+
+
+class SolveMedia(Captcha):
+ __name__ = "SolveMedia"
+ __type__ = "captcha"
+ __version__ = "0.06"
+
+ __description__ = """SolveMedia captcha service plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("pyLoad Team", "admin@pyload.org")]
+
+
+ KEY_PATTERN = r'api\.solvemedia\.com/papi/challenge\.(?:no)?script\?k=(.+?)["\']'
+
+
+ def challenge(self, key=None):
+ if not key:
+ if self.detect_key():
+ key = self.key
+ else:
+ errmsg = _("SolveMedia key not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ html = self.plugin.req.load("http://api.solvemedia.com/papi/challenge.noscript", get={'k': key})
+ try:
+ challenge = re.search(r'<input type=hidden name="adcopy_challenge" id="adcopy_challenge" value="([^"]+)">',
+ html).group(1)
+ server = "http://api.solvemedia.com/papi/media"
+ except Exception:
+ errmsg = _("SolveMedia challenge pattern not found")
+ self.plugin.error(errmsg)
+ raise ValueError(errmsg)
+
+ self.plugin.logDebug("SolveMedia challenge: %s" % challenge)
+
+ return challenge, self.result(server, challenge)
+
+
+ def result(self, server, challenge):
+ result = self.plugin.decryptCaptcha(server, get={'c': challenge}, imgtype="gif")
+
+ self.plugin.logDebug("SolveMedia result: %s" % result)
+
+ return result
diff --git a/module/plugins/captcha/__init__.py b/module/plugins/captcha/__init__.py
index e69de29bb..40a96afc6 100644
--- a/module/plugins/captcha/__init__.py
+++ b/module/plugins/captcha/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/plugins/container/__init__.py b/module/plugins/container/__init__.py
index e69de29bb..40a96afc6 100644
--- a/module/plugins/container/__init__.py
+++ b/module/plugins/container/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/plugins/crypter/BitshareComFolder.py b/module/plugins/crypter/BitshareCom.py
index c70a849b6..2960243fc 100644
--- a/module/plugins/crypter/BitshareComFolder.py
+++ b/module/plugins/crypter/BitshareCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from module.plugins.internal.SimpleCrypter import SimpleCrypter
-class BitshareComFolder(SimpleCrypter):
- __name__ = "BitshareComFolder"
+class BitshareCom(SimpleCrypter):
+ __name__ = "BitshareCom"
__type__ = "crypter"
__version__ = "0.03"
diff --git a/module/plugins/crypter/C1NeonCom.py b/module/plugins/crypter/C1NeonCom.py
index 926633ff7..eaccb471f 100644
--- a/module/plugins/crypter/C1NeonCom.py
+++ b/module/plugins/crypter/C1NeonCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class C1NeonCom(DeadCrypter):
diff --git a/module/plugins/crypter/ChipDe.py b/module/plugins/crypter/ChipDe.py
index 2fc36c355..2f47236e8 100644
--- a/module/plugins/crypter/ChipDe.py
+++ b/module/plugins/crypter/ChipDe.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import re
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class ChipDe(Crypter):
diff --git a/module/plugins/crypter/CrockoComFolder.py b/module/plugins/crypter/CrockoCom.py
index 57bb339ff..9fad661d2 100644
--- a/module/plugins/crypter/CrockoComFolder.py
+++ b/module/plugins/crypter/CrockoCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from module.plugins.internal.SimpleCrypter import SimpleCrypter
-class CrockoComFolder(SimpleCrypter):
- __name__ = "CrockoComFolder"
+class CrockoCom(SimpleCrypter):
+ __name__ = "CrockoCom"
__type__ = "crypter"
__version__ = "0.01"
diff --git a/module/plugins/crypter/CryptItCom.py b/module/plugins/crypter/CryptItCom.py
index 2cf4e9f62..cb3347f55 100644
--- a/module/plugins/crypter/CryptItCom.py
+++ b/module/plugins/crypter/CryptItCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class CryptItCom(DeadCrypter):
diff --git a/module/plugins/crypter/CzshareComFolder.py b/module/plugins/crypter/CzshareCom.py
index 5623a4093..e527d683f 100644
--- a/module/plugins/crypter/CzshareComFolder.py
+++ b/module/plugins/crypter/CzshareCom.py
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
import re
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
-class CzshareComFolder(Crypter):
- __name__ = "CzshareComFolder"
+class CzshareCom(Crypter):
+ __name__ = "CzshareCom"
__type__ = "crypter"
__version__ = "0.20"
diff --git a/module/plugins/crypter/DDLMusicOrg.py b/module/plugins/crypter/DDLMusicOrg.py
index 55181e9ad..2b6738799 100644
--- a/module/plugins/crypter/DDLMusicOrg.py
+++ b/module/plugins/crypter/DDLMusicOrg.py
@@ -4,7 +4,7 @@ import re
from time import sleep
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class DDLMusicOrg(Crypter):
diff --git a/module/plugins/crypter/DailymotionBatch.py b/module/plugins/crypter/DailymotionBatch.py
index 82b80ab2f..c66c7c829 100644
--- a/module/plugins/crypter/DailymotionBatch.py
+++ b/module/plugins/crypter/DailymotionBatch.py
@@ -4,9 +4,9 @@ import re
from urlparse import urljoin
-from module.common.json_layer import json_loads
-from module.plugins.Crypter import Crypter
-from module.utils import save_join
+from pyload.utils import json_loads
+from pyload.plugin.Crypter import Crypter
+from pyload.utils import safe_join
class DailymotionBatch(Crypter):
@@ -101,6 +101,6 @@ class DailymotionBatch(Crypter):
for p_id, p_name, p_owner in playlists:
p_videos = self.getVideos(p_id)
- p_folder = save_join(self.config['general']['download_folder'], p_owner, p_name)
+ p_folder = safe_join(self.config['general']['download_folder'], p_owner, p_name)
self.logDebug("%s video\s found on playlist \"%s\"" % (len(p_videos), p_name))
self.packages.append((p_name, p_videos, p_folder)) #: folder is NOT recognized by pyload 0.4.9!
diff --git a/module/plugins/crypter/DataHuFolder.py b/module/plugins/crypter/DataHu.py
index a5602d6c6..e8b3acbb7 100644
--- a/module/plugins/crypter/DataHuFolder.py
+++ b/module/plugins/crypter/DataHu.py
@@ -2,11 +2,11 @@
import re
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from module.plugins.internal.SimpleCrypter import SimpleCrypter
-class DataHuFolder(SimpleCrypter):
- __name__ = "DataHuFolder"
+class DataHu(SimpleCrypter):
+ __name__ = "DataHu"
__type__ = "crypter"
__version__ = "0.06"
@@ -16,7 +16,7 @@ class DataHuFolder(SimpleCrypter):
__description__ = """Data.hu folder decrypter plugin"""
__license__ = "GPLv3"
- __authors__ = [("crash", None),
+ __authors__ = [("crash", ""),
("stickell", "l.stickell@yahoo.it")]
@@ -25,7 +25,7 @@ class DataHuFolder(SimpleCrypter):
def prepare(self):
- super(DataHuFolder, self).prepare()
+ super(DataHu, self).prepare()
if u'K\xe9rlek add meg a jelsz\xf3t' in self.html: # Password protected
password = self.getPassword()
diff --git a/module/plugins/crypter/DdlstorageComFolder.py b/module/plugins/crypter/DdlstorageCom.py
index e02e77fda..1d87e975b 100644
--- a/module/plugins/crypter/DdlstorageComFolder.py
+++ b/module/plugins/crypter/DdlstorageCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
-class DdlstorageComFolder(DeadCrypter):
- __name__ = "DdlstorageComFolder"
+class DdlstorageCom(DeadCrypter):
+ __name__ = "DdlstorageCom"
__type__ = "crypter"
__version__ = "0.03"
@@ -17,4 +17,4 @@ class DdlstorageComFolder(DeadCrypter):
("stickell", "l.stickell@yahoo.it")]
-getInfo = create_getInfo(DdlstorageComFolder)
+getInfo = create_getInfo(DdlstorageCom)
diff --git a/module/plugins/crypter/DepositfilesComFolder.py b/module/plugins/crypter/DepositfilesCom.py
index 147f093c3..ba578a4ed 100644
--- a/module/plugins/crypter/DepositfilesComFolder.py
+++ b/module/plugins/crypter/DepositfilesCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from module.plugins.internal.SimpleCrypter import SimpleCrypter
-class DepositfilesComFolder(SimpleCrypter):
- __name__ = "DepositfilesComFolder"
+class DepositfilesCom(SimpleCrypter):
+ __name__ = "DepositfilesCom"
__type__ = "crypter"
__version__ = "0.01"
diff --git a/module/plugins/crypter/Dereferer.py b/module/plugins/crypter/Dereferer.py
index 0729c8cb6..d52bb1094 100644
--- a/module/plugins/crypter/Dereferer.py
+++ b/module/plugins/crypter/Dereferer.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleDereferer import SimpleDereferer
+from module.plugins.Crypter import Crypter
class Dereferer(SimpleDereferer):
diff --git a/module/plugins/crypter/DevhostStFolder.py b/module/plugins/crypter/DevhostStFolder.py
index 8779cae5e..2fbc90ba8 100644
--- a/module/plugins/crypter/DevhostStFolder.py
+++ b/module/plugins/crypter/DevhostStFolder.py
@@ -7,7 +7,7 @@ import re
from urlparse import urljoin
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from module.plugins.internal.SimpleCrypter import SimpleCrypter
class DevhostStFolder(SimpleCrypter):
diff --git a/module/plugins/crypter/DlProtectCom.py b/module/plugins/crypter/DlProtectCom.py
index 4a188aa83..1cfe7639a 100644
--- a/module/plugins/crypter/DlProtectCom.py
+++ b/module/plugins/crypter/DlProtectCom.py
@@ -5,7 +5,7 @@ import re
from base64 import urlsafe_b64encode
from time import time
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from module.plugins.internal.SimpleCrypter import SimpleCrypter
class DlProtectCom(SimpleCrypter):
diff --git a/module/plugins/crypter/DontKnowMe.py b/module/plugins/crypter/DontKnowMe.py
index d656cde4c..7a4b85b2d 100644
--- a/module/plugins/crypter/DontKnowMe.py
+++ b/module/plugins/crypter/DontKnowMe.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleDereferer import SimpleDereferer
+from module.plugins.Crypter import Crypter
class DontKnowMe(SimpleDereferer):
diff --git a/module/plugins/crypter/DuckCryptInfo.py b/module/plugins/crypter/DuckCryptInfo.py
index 07cc5cdc4..55681fd5e 100644
--- a/module/plugins/crypter/DuckCryptInfo.py
+++ b/module/plugins/crypter/DuckCryptInfo.py
@@ -4,7 +4,7 @@ import re
from BeautifulSoup import BeautifulSoup
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class DuckCryptInfo(Crypter):
diff --git a/module/plugins/crypter/DuploadOrgFolder.py b/module/plugins/crypter/DuploadOrg.py
index 066fbe3d7..70745b550 100644
--- a/module/plugins/crypter/DuploadOrgFolder.py
+++ b/module/plugins/crypter/DuploadOrg.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
-class DuploadOrgFolder(DeadCrypter):
- __name__ = "DuploadOrgFolder"
+class DuploadOrg(DeadCrypter):
+ __name__ = "DuploadOrg"
__type__ = "crypter"
__version__ = "0.02"
@@ -16,4 +16,4 @@ class DuploadOrgFolder(DeadCrypter):
__authors__ = [("stickell", "l.stickell@yahoo.it")]
-getInfo = create_getInfo(DuploadOrgFolder)
+getInfo = create_getInfo(DuploadOrg)
diff --git a/module/plugins/crypter/EasybytezComFolder.py b/module/plugins/crypter/EasybytezCom.py
index 6c643e83f..d9685e2f1 100644
--- a/module/plugins/crypter/EasybytezComFolder.py
+++ b/module/plugins/crypter/EasybytezCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSCrypter import XFSCrypter, create_getInfo
+from module.plugins.internal.XFSCrypter import XFSCrypter
-class EasybytezComFolder(XFSCrypter):
- __name__ = "EasybytezComFolder"
+class EasybytezCom(XFSCrypter):
+ __name__ = "EasybytezCom"
__type__ = "crypter"
__version__ = "0.10"
diff --git a/module/plugins/crypter/EmbeduploadCom.py b/module/plugins/crypter/EmbeduploadCom.py
index 456e48a32..c94e7e106 100644
--- a/module/plugins/crypter/EmbeduploadCom.py
+++ b/module/plugins/crypter/EmbeduploadCom.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
import re
-from module.plugins.Crypter import Crypter
-from module.network.HTTPRequest import BadHeader
+from pyload.plugin.Crypter import Crypter
+from pyload.network.HTTPRequest import BadHeader
class EmbeduploadCom(Crypter):
@@ -12,9 +12,9 @@ class EmbeduploadCom(Crypter):
__pattern__ = r'http://(?:www\.)?embedupload\.com/\?d=.+'
__config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True),
- ("preferedHoster", "str", "Prefered hoster list (bar-separated)", "embedupload"),
- ("ignoredHoster", "str", "Ignored hoster list (bar-separated)", "")]
+ ("subfolder_per_package", "bool", "Create a subfolder for each package", True),
+ ("preferedHoster", "str", "Prefered hoster list (bar-separated)", "embedupload"),
+ ("ignoredHoster", "str", "Ignored hoster list (bar-separated)", "")]
__description__ = """EmbedUpload.com decrypter plugin"""
__license__ = "GPLv3"
diff --git a/module/plugins/crypter/FilebeerInfoFolder.py b/module/plugins/crypter/FilebeerInfo.py
index a3c7ee74c..4e8ab1259 100644
--- a/module/plugins/crypter/FilebeerInfoFolder.py
+++ b/module/plugins/crypter/FilebeerInfo.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
-class FilebeerInfoFolder(DeadCrypter):
- __name__ = "FilebeerInfoFolder"
+class FilebeerInfo(DeadCrypter):
+ __name__ = "FilebeerInfo"
__type__ = "crypter"
__version__ = "0.02"
@@ -16,4 +16,4 @@ class FilebeerInfoFolder(DeadCrypter):
__authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-getInfo = create_getInfo(FilebeerInfoFolder)
+getInfo = create_getInfo(FilebeerInfo)
diff --git a/module/plugins/crypter/FilecloudIoFolder.py b/module/plugins/crypter/FilecloudIo.py
index 83cce352d..d0ca619de 100644
--- a/module/plugins/crypter/FilecloudIoFolder.py
+++ b/module/plugins/crypter/FilecloudIo.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from module.plugins.internal.SimpleCrypter import SimpleCrypter
-class FilecloudIoFolder(SimpleCrypter):
- __name__ = "FilecloudIoFolder"
+class FilecloudIo(SimpleCrypter):
+ __name__ = "FilecloudIo"
__type__ = "crypter"
__version__ = "0.03"
diff --git a/module/plugins/crypter/FilecryptCc.py b/module/plugins/crypter/FilecryptCc.py
index d15d2ae4b..938ceafa9 100644
--- a/module/plugins/crypter/FilecryptCc.py
+++ b/module/plugins/crypter/FilecryptCc.py
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
-# http://filecrypt.cc/Container/64E039F859.html
+#
+# Test links:
+# http://filecrypt.cc/Container/64E039F859.html
import binascii
import re
@@ -7,7 +9,7 @@ import re
from Crypto.Cipher import AES
from urlparse import urljoin
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
from module.plugins.internal.CaptchaService import ReCaptcha
@@ -20,7 +22,7 @@ class FilecryptCc(Crypter):
__description__ = """Filecrypt.cc decrypter plugin"""
__license__ = "GPLv3"
- __authors__ = [("zapp-brannigan", "")]
+ __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de")]
# URL_REPLACEMENTS = [(r'.html$', ""), (r'$', ".html")] #@TODO: Extend SimpleCrypter
diff --git a/module/plugins/crypter/FilefactoryComFolder.py b/module/plugins/crypter/FilefactoryCom.py
index 261c7e01a..c6bdb81eb 100644
--- a/module/plugins/crypter/FilefactoryComFolder.py
+++ b/module/plugins/crypter/FilefactoryCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from module.plugins.internal.SimpleCrypter import SimpleCrypter
-class FilefactoryComFolder(SimpleCrypter):
- __name__ = "FilefactoryComFolder"
+class FilefactoryCom(SimpleCrypter):
+ __name__ = "FilefactoryCom"
__type__ = "crypter"
__version__ = "0.32"
diff --git a/module/plugins/crypter/FilerNetFolder.py b/module/plugins/crypter/FilerNet.py
index d33e26ee8..054c8d3a7 100644
--- a/module/plugins/crypter/FilerNetFolder.py
+++ b/module/plugins/crypter/FilerNet.py
@@ -1,10 +1,10 @@
import re
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from module.plugins.internal.SimpleCrypter import SimpleCrypter
-class FilerNetFolder(SimpleCrypter):
- __name__ = "FilerNetFolder"
+class FilerNet(SimpleCrypter):
+ __name__ = "FilerNet"
__type__ = "crypter"
__version__ = "0.42"
diff --git a/module/plugins/crypter/FileserveComFolder.py b/module/plugins/crypter/FileserveCom.py
index e6b35fd36..ab0665fbd 100644
--- a/module/plugins/crypter/FileserveComFolder.py
+++ b/module/plugins/crypter/FileserveCom.py
@@ -2,11 +2,11 @@
import re
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
-class FileserveComFolder(Crypter):
- __name__ = "FileserveComFolder"
+class FileserveCom(Crypter):
+ __name__ = "FileserveCom"
__type__ = "crypter"
__version__ = "0.11"
diff --git a/module/plugins/crypter/FilesonicComFolder.py b/module/plugins/crypter/FilesonicCom.py
index d58516986..b1722c1bb 100644
--- a/module/plugins/crypter/FilesonicComFolder.py
+++ b/module/plugins/crypter/FilesonicCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
-class FilesonicComFolder(DeadCrypter):
- __name__ = "FilesonicComFolder"
+class FilesonicCom(DeadCrypter):
+ __name__ = "FilesonicCom"
__type__ = "crypter"
__version__ = "0.12"
@@ -15,4 +15,4 @@ class FilesonicComFolder(DeadCrypter):
__authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-getInfo = create_getInfo(FilesonicComFolder)
+getInfo = create_getInfo(FilesonicCom)
diff --git a/module/plugins/crypter/FilestubeCom.py b/module/plugins/crypter/FilestubeCom.py
index 16ebdda37..befe6f9a7 100644
--- a/module/plugins/crypter/FilestubeCom.py
+++ b/module/plugins/crypter/FilestubeCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from module.plugins.internal.SimpleCrypter import SimpleCrypter
class FilestubeCom(SimpleCrypter):
diff --git a/module/plugins/crypter/FiletramCom.py b/module/plugins/crypter/FiletramCom.py
index 76530c589..6c3c071ec 100644
--- a/module/plugins/crypter/FiletramCom.py
+++ b/module/plugins/crypter/FiletramCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from module.plugins.internal.SimpleCrypter import SimpleCrypter
class FiletramCom(SimpleCrypter):
diff --git a/module/plugins/crypter/FiredriveComFolder.py b/module/plugins/crypter/FiredriveCom.py
index 7d3a357fd..bf315ad16 100644
--- a/module/plugins/crypter/FiredriveComFolder.py
+++ b/module/plugins/crypter/FiredriveCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
-class FiredriveComFolder(DeadCrypter):
- __name__ = "FiredriveComFolder"
+class FiredriveCom(DeadCrypter):
+ __name__ = "FiredriveCom"
__type__ = "crypter"
__version__ = "0.03"
@@ -16,4 +16,4 @@ class FiredriveComFolder(DeadCrypter):
__authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-getInfo = create_getInfo(FiredriveComFolder)
+getInfo = create_getInfo(FiredriveCom)
diff --git a/module/plugins/crypter/FourChanOrg.py b/module/plugins/crypter/FourChanOrg.py
index c3fe3db4b..62fb9d3fe 100644
--- a/module/plugins/crypter/FourChanOrg.py
+++ b/module/plugins/crypter/FourChanOrg.py
@@ -4,7 +4,7 @@
import re
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class FourChanOrg(Crypter):
diff --git a/module/plugins/crypter/FreakhareComFolder.py b/module/plugins/crypter/FreakhareCom.py
index 7c1b7de2b..c5fd2ee29 100644
--- a/module/plugins/crypter/FreakhareComFolder.py
+++ b/module/plugins/crypter/FreakhareCom.py
@@ -2,11 +2,11 @@
import re
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from pyload.plugin.internal.SimpleCrypter import SimpleCrypter
-class FreakhareComFolder(SimpleCrypter):
- __name__ = "FreakhareComFolder"
+class FreakhareCom(SimpleCrypter):
+ __name__ = "FreakhareCom"
__type__ = "crypter"
__version__ = "0.03"
diff --git a/module/plugins/crypter/FreetexthostCom.py b/module/plugins/crypter/FreetexthostCom.py
index c33c9ff64..4634455b6 100644
--- a/module/plugins/crypter/FreetexthostCom.py
+++ b/module/plugins/crypter/FreetexthostCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from pyload.plugin.internal.SimpleCrypter import SimpleCrypter
class FreetexthostCom(SimpleCrypter):
diff --git a/module/plugins/crypter/FshareVnFolder.py b/module/plugins/crypter/FshareVn.py
index 474364e40..e85085cf1 100644
--- a/module/plugins/crypter/FshareVnFolder.py
+++ b/module/plugins/crypter/FshareVn.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from pyload.plugin.internal.SimpleCrypter import SimpleCrypter
-class FshareVnFolder(SimpleCrypter):
- __name__ = "FshareVnFolder"
+class FshareVn(SimpleCrypter):
+ __name__ = "FshareVn"
__type__ = "crypter"
__version__ = "0.01"
diff --git a/module/plugins/crypter/Go4UpCom.py b/module/plugins/crypter/Go4UpCom.py
index a7e16c0ab..ffd945737 100644
--- a/module/plugins/crypter/Go4UpCom.py
+++ b/module/plugins/crypter/Go4UpCom.py
@@ -4,7 +4,7 @@ import re
from urlparse import urljoin
-from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
+from pyload.plugin.internal.SimpleCrypter import SimpleCrypter, create_getInfo
class Go4UpCom(SimpleCrypter):
diff --git a/module/plugins/crypter/GooGl.py b/module/plugins/crypter/GooGl.py
index d548a3375..13ffce505 100644
--- a/module/plugins/crypter/GooGl.py
+++ b/module/plugins/crypter/GooGl.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.Crypter import Crypter
-from module.common.json_layer import json_loads
+from pyload.plugin.Crypter import Crypter
+from pyload.utils import json_loads
class GooGl(Crypter):
diff --git a/module/plugins/crypter/HoerbuchIn.py b/module/plugins/crypter/HoerbuchIn.py
index a347e4232..a12d7c02a 100644
--- a/module/plugins/crypter/HoerbuchIn.py
+++ b/module/plugins/crypter/HoerbuchIn.py
@@ -4,7 +4,7 @@ import re
from BeautifulSoup import BeautifulSoup, BeautifulStoneSoup
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class HoerbuchIn(Crypter):
diff --git a/module/plugins/crypter/HotfileComFolder.py b/module/plugins/crypter/HotfileCom.py
index 4f40587ad..72eec8f93 100644
--- a/module/plugins/crypter/HotfileComFolder.py
+++ b/module/plugins/crypter/HotfileCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
-class HotfileComFolder(DeadCrypter):
- __name__ = "HotfileComFolder"
+class HotfileCom(DeadCrypter):
+ __name__ = "HotfileCom"
__type__ = "crypter"
__version__ = "0.30"
@@ -16,4 +16,4 @@ class HotfileComFolder(DeadCrypter):
__authors__ = [("RaNaN", "RaNaN@pyload.org")]
-getInfo = create_getInfo(HotfileComFolder)
+getInfo = create_getInfo(HotfileCom)
diff --git a/module/plugins/crypter/ILoadTo.py b/module/plugins/crypter/ILoadTo.py
index f3415706d..d3e50630c 100644
--- a/module/plugins/crypter/ILoadTo.py
+++ b/module/plugins/crypter/ILoadTo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class ILoadTo(DeadCrypter):
@@ -13,7 +13,7 @@ class ILoadTo(DeadCrypter):
__description__ = """Iload.to decrypter plugin"""
__license__ = "GPLv3"
- __authors__ = [("hzpz", None)]
+ __authors__ = [("hzpz", "")]
getInfo = create_getInfo(ILoadTo)
diff --git a/module/plugins/crypter/LetitbitNetFolder.py b/module/plugins/crypter/LetitbitNet.py
index 56ecbc7f8..24bc196b3 100644
--- a/module/plugins/crypter/LetitbitNetFolder.py
+++ b/module/plugins/crypter/LetitbitNet.py
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
import re
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
-class LetitbitNetFolder(Crypter):
- __name__ = "LetitbitNetFolder"
+class LetitbitNet(Crypter):
+ __name__ = "LetitbitNet"
__type__ = "crypter"
__version__ = "0.10"
diff --git a/module/plugins/crypter/LinkCryptWs.py b/module/plugins/crypter/LinkCryptWs.py
index 018ed90ba..a3eb4f179 100644
--- a/module/plugins/crypter/LinkCryptWs.py
+++ b/module/plugins/crypter/LinkCryptWs.py
@@ -7,8 +7,8 @@ import pycurl
from Crypto.Cipher import AES
-from module.plugins.Crypter import Crypter
-from module.utils import html_unescape
+from pyload.plugin.Crypter import Crypter
+from pyload.utils import html_unescape
class LinkCryptWs(Crypter):
@@ -21,8 +21,8 @@ class LinkCryptWs(Crypter):
__description__ = """LinkCrypt.ws decrypter plugin"""
__license__ = "GPLv3"
__authors__ = [("kagenoshin", "kagenoshin[AT]gmx[DOT]ch"),
- ("glukgluk", None),
- ("Gummibaer", None)]
+ ("glukgluk", ""),
+ ("Gummibaer", "")]
CRYPTED_KEY = "crypted"
diff --git a/module/plugins/crypter/LinkdecrypterCom.py b/module/plugins/crypter/LinkdecrypterCom.py
index 7f24784c7..facec37d5 100644
--- a/module/plugins/crypter/LinkdecrypterCom.py
+++ b/module/plugins/crypter/LinkdecrypterCom.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import re
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class LinkdecrypterCom(Crypter):
@@ -16,7 +16,7 @@ class LinkdecrypterCom(Crypter):
__description__ = """Linkdecrypter.com decrypter plugin"""
__license__ = "GPLv3"
__authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("flowlee", None)]
+ ("flowlee", "")]
TEXTAREA_PATTERN = r'<textarea name="links" wrap="off" readonly="1" class="caja_des">(.+)</textarea>'
diff --git a/module/plugins/crypter/LixIn.py b/module/plugins/crypter/LixIn.py
index d899d58c7..4b978723f 100644
--- a/module/plugins/crypter/LixIn.py
+++ b/module/plugins/crypter/LixIn.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class LixIn(Crypter):
diff --git a/module/plugins/crypter/LofCc.py b/module/plugins/crypter/LofCc.py
index 3cac0fbf2..e18349877 100644
--- a/module/plugins/crypter/LofCc.py
+++ b/module/plugins/crypter/LofCc.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class LofCc(DeadCrypter):
diff --git a/module/plugins/crypter/MBLinkInfo.py b/module/plugins/crypter/MBLinkInfo.py
index 82c2d9719..7f1561756 100644
--- a/module/plugins/crypter/MBLinkInfo.py
+++ b/module/plugins/crypter/MBLinkInfo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class MBLinkInfo(DeadCrypter):
diff --git a/module/plugins/crypter/MediafireComFolder.py b/module/plugins/crypter/MediafireCom.py
index d1dc89518..a7360a6db 100644
--- a/module/plugins/crypter/MediafireComFolder.py
+++ b/module/plugins/crypter/MediafireCom.py
@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-
import re
-from module.plugins.Crypter import Crypter
-from module.plugins.hoster.MediafireCom import checkHTMLHeader
-from module.common.json_layer import json_loads
+from pyload.plugin.Crypter import Crypter
+from pyload.plugin.hoster.MediafireCom import checkHTMLHeader
+from pyload.utils import json_loads
-class MediafireComFolder(Crypter):
- __name__ = "MediafireComFolder"
+class MediafireCom(Crypter):
+ __name__ = "MediafireCom"
__type__ = "crypter"
__version__ = "0.14"
diff --git a/module/plugins/crypter/MegaRapidCzFolder.py b/module/plugins/crypter/MegaRapidCz.py
index e7dff6c8a..86235444b 100644
--- a/module/plugins/crypter/MegaRapidCzFolder.py
+++ b/module/plugins/crypter/MegaRapidCz.py
@@ -3,8 +3,8 @@
from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
-class MegaRapidCzFolder(SimpleCrypter):
- __name__ = "MegaRapidCzFolder"
+class MegaRapidCz(SimpleCrypter):
+ __name__ = "MegaRapidCz"
__type__ = "crypter"
__version__ = "0.02"
diff --git a/module/plugins/crypter/MegauploadComFolder.py b/module/plugins/crypter/MegauploadCom.py
index 08f96700d..6981cebc8 100644
--- a/module/plugins/crypter/MegauploadComFolder.py
+++ b/module/plugins/crypter/MegauploadCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
-class MegauploadComFolder(DeadCrypter):
- __name__ = "MegauploadComFolder"
+class MegauploadCom(DeadCrypter):
+ __name__ = "MegauploadCom"
__type__ = "crypter"
__version__ = "0.02"
@@ -15,4 +15,4 @@ class MegauploadComFolder(DeadCrypter):
__authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-getInfo = create_getInfo(MegauploadComFolder)
+getInfo = create_getInfo(MegauploadCom)
diff --git a/module/plugins/crypter/Movie2KTo.py b/module/plugins/crypter/Movie2KTo.py
index 76bf702ac..92bc128c7 100644
--- a/module/plugins/crypter/Movie2KTo.py
+++ b/module/plugins/crypter/Movie2KTo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class Movie2KTo(DeadCrypter):
diff --git a/module/plugins/crypter/MultiUpOrg.py b/module/plugins/crypter/MultiUpOrg.py
index 5209ebf09..7955fca6c 100644
--- a/module/plugins/crypter/MultiUpOrg.py
+++ b/module/plugins/crypter/MultiUpOrg.py
@@ -15,7 +15,7 @@ class MultiUpOrg(SimpleCrypter):
__config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
- __description__ = """MultiUp.org crypter plugin"""
+ __description__ = """MultiUp.org decrypter plugin"""
__license__ = "GPLv3"
__authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
diff --git a/module/plugins/crypter/MultiloadCz.py b/module/plugins/crypter/MultiloadCz.py
index 55f424f36..856a94a25 100644
--- a/module/plugins/crypter/MultiloadCz.py
+++ b/module/plugins/crypter/MultiloadCz.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import re
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class MultiloadCz(Crypter):
@@ -11,9 +11,9 @@ class MultiloadCz(Crypter):
__pattern__ = r'http://(?:[^/]*\.)?multiload\.cz/(stahnout|slozka)/.+'
__config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True),
- ("usedHoster", "str", "Prefered hoster list (bar-separated)", ""),
- ("ignoredHoster", "str", "Ignored hoster list (bar-separated)", "")]
+ ("subfolder_per_package", "bool", "Create a subfolder for each package", True),
+ ("usedHoster", "str", "Prefered hoster list (bar-separated)", ""),
+ ("ignoredHoster", "str", "Ignored hoster list (bar-separated)", "")]
__description__ = """Multiload.cz decrypter plugin"""
__license__ = "GPLv3"
diff --git a/module/plugins/crypter/MultiuploadCom.py b/module/plugins/crypter/MultiuploadCom.py
index 347b7e5af..98708bd95 100644
--- a/module/plugins/crypter/MultiuploadCom.py
+++ b/module/plugins/crypter/MultiuploadCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class MultiuploadCom(DeadCrypter):
@@ -9,9 +9,8 @@ class MultiuploadCom(DeadCrypter):
__version__ = "0.02"
__pattern__ = r'http://(?:www\.)?multiupload\.(com|nl)/\w+'
- __config__ = []
- __description__ = """ MultiUpload.com decrypter plugin """
+ __description__ = """MultiUpload.com decrypter plugin"""
__license__ = "GPLv3"
__authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
diff --git a/module/plugins/crypter/NCryptIn.py b/module/plugins/crypter/NCryptIn.py
index 20e7c72e2..29b45b11a 100644
--- a/module/plugins/crypter/NCryptIn.py
+++ b/module/plugins/crypter/NCryptIn.py
@@ -5,8 +5,8 @@ import re
from Crypto.Cipher import AES
-from module.plugins.Crypter import Crypter
-from module.plugins.internal.CaptchaService import ReCaptcha
+from pyload.plugin.Crypter import Crypter
+from pyload.plugin.internal.captcha import ReCaptcha
class NCryptIn(Crypter):
diff --git a/module/plugins/crypter/OneKhDe.py b/module/plugins/crypter/OneKhDe.py
index 3e75d97b5..323214df8 100644
--- a/module/plugins/crypter/OneKhDe.py
+++ b/module/plugins/crypter/OneKhDe.py
@@ -2,9 +2,9 @@
import re
-from module.unescape import unescape
+from pyload.utils import html_unescape
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class OneKhDe(Crypter):
@@ -37,6 +37,5 @@ class OneKhDe(Crypter):
self.html = self.load(url)
link_ids = re.findall(r"<a id=\"DownloadLink_(\d*)\" href=\"http://1kh.de/", self.html)
for id in link_ids:
- new_link = unescape(
- re.search("width=\"100%\" src=\"(.*)\"></iframe>", self.load("http://1kh.de/l/" + id)).group(1))
+ new_link = html_unescape(re.search("width=\"100%\" src=\"(.*)\"></iframe>", self.load("http://1kh.de/l/" + id)).group(1))
self.urls.append(new_link)
diff --git a/module/plugins/crypter/OronComFolder.py b/module/plugins/crypter/OronCom.py
index 9e06bdf32..42483c497 100644
--- a/module/plugins/crypter/OronComFolder.py
+++ b/module/plugins/crypter/OronCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
-class OronComFolder(DeadCrypter):
- __name__ = "OronComFolder"
+class OronCom(DeadCrypter):
+ __name__ = "OronCom"
__type__ = "crypter"
__version__ = "0.11"
@@ -16,4 +16,4 @@ class OronComFolder(DeadCrypter):
__authors__ = [("DHMH", "webmaster@pcProfil.de")]
-getInfo = create_getInfo(OronComFolder)
+getInfo = create_getInfo(OronCom)
diff --git a/module/plugins/crypter/QuickshareCzFolder.py b/module/plugins/crypter/QuickshareCz.py
index 70666b55a..8cd4b9c02 100644
--- a/module/plugins/crypter/QuickshareCzFolder.py
+++ b/module/plugins/crypter/QuickshareCz.py
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
import re
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
-class QuickshareCzFolder(Crypter):
- __name__ = "QuickshareCzFolder"
+class QuickshareCz(Crypter):
+ __name__ = "QuickshareCz"
__type__ = "crypter"
__version__ = "0.10"
diff --git a/module/plugins/crypter/RSLayerCom.py b/module/plugins/crypter/RSLayerCom.py
index cc3b23bbc..ebd3c509c 100644
--- a/module/plugins/crypter/RSLayerCom.py
+++ b/module/plugins/crypter/RSLayerCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class RSLayerCom(DeadCrypter):
@@ -13,7 +13,7 @@ class RSLayerCom(DeadCrypter):
__description__ = """RS-Layer.com decrypter plugin"""
__license__ = "GPLv3"
- __authors__ = [("hzpz", None)]
+ __authors__ = [("hzpz", "")]
getInfo = create_getInfo(RSLayerCom)
diff --git a/module/plugins/crypter/RelinkUs.py b/module/plugins/crypter/RelinkUs.py
index c50481af0..b361f87d8 100644
--- a/module/plugins/crypter/RelinkUs.py
+++ b/module/plugins/crypter/RelinkUs.py
@@ -7,8 +7,8 @@ import re
import os
from Crypto.Cipher import AES
-from module.plugins.Crypter import Crypter
-from module.utils import save_join
+from pyload.plugin.Crypter import Crypter
+from module.utils import safe_join
class RelinkUs(Crypter):
diff --git a/module/plugins/crypter/SafelinkingNet.py b/module/plugins/crypter/SafelinkingNet.py
index 8e46e1e41..2d5d273ed 100644
--- a/module/plugins/crypter/SafelinkingNet.py
+++ b/module/plugins/crypter/SafelinkingNet.py
@@ -2,13 +2,11 @@
import re
-from pycurl import FOLLOWLOCATION
-
from BeautifulSoup import BeautifulSoup
-from module.common.json_layer import json_loads
-from module.plugins.Crypter import Crypter
-from module.plugins.internal.CaptchaService import SolveMedia
+from pyload.utils import json_loads
+from pyload.plugin.Crypter import Crypter
+from pyload.plugin.internal.captcha import SolveMedia
class SafelinkingNet(Crypter):
diff --git a/module/plugins/crypter/SecuredIn.py b/module/plugins/crypter/SecuredIn.py
index cbfa919ac..9aee9e52e 100644
--- a/module/plugins/crypter/SecuredIn.py
+++ b/module/plugins/crypter/SecuredIn.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class SecuredIn(DeadCrypter):
diff --git a/module/plugins/crypter/SexuriaCom.py b/module/plugins/crypter/SexuriaCom.py
index 3c952fd6b..10101cd7a 100644
--- a/module/plugins/crypter/SexuriaCom.py
+++ b/module/plugins/crypter/SexuriaCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class SexuriaCom(Crypter):
diff --git a/module/plugins/crypter/ShareLinksBiz.py b/module/plugins/crypter/ShareLinksBiz.py
index 00a037b2e..1328e86aa 100644
--- a/module/plugins/crypter/ShareLinksBiz.py
+++ b/module/plugins/crypter/ShareLinksBiz.py
@@ -4,7 +4,7 @@ import binascii
import re
from Crypto.Cipher import AES
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class ShareLinksBiz(Crypter):
diff --git a/module/plugins/crypter/SharingmatrixComFolder.py b/module/plugins/crypter/SharingmatrixCom.py
index e16bdf814..be23ada49 100644
--- a/module/plugins/crypter/SharingmatrixComFolder.py
+++ b/module/plugins/crypter/SharingmatrixCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
-class SharingmatrixComFolder(DeadCrypter):
- __name__ = "SharingmatrixComFolder"
+class SharingmatrixCom(DeadCrypter):
+ __name__ = "SharingmatrixCom"
__type__ = "crypter"
__version__ = "0.01"
@@ -15,4 +15,4 @@ class SharingmatrixComFolder(DeadCrypter):
__authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-getInfo = create_getInfo(SharingmatrixComFolder)
+getInfo = create_getInfo(SharingmatrixCom)
diff --git a/module/plugins/crypter/SpeedLoadOrgFolder.py b/module/plugins/crypter/SpeedLoadOrg.py
index ddde7dec2..190ec66eb 100644
--- a/module/plugins/crypter/SpeedLoadOrgFolder.py
+++ b/module/plugins/crypter/SpeedLoadOrg.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
-class SpeedLoadOrgFolder(DeadCrypter):
- __name__ = "SpeedLoadOrgFolder"
+class SpeedLoadOrg(DeadCrypter):
+ __name__ = "SpeedLoadOrg"
__type__ = "crypter"
__version__ = "0.30"
@@ -16,4 +16,4 @@ class SpeedLoadOrgFolder(DeadCrypter):
__authors__ = [("stickell", "l.stickell@yahoo.it")]
-getInfo = create_getInfo(SpeedLoadOrgFolder)
+getInfo = create_getInfo(SpeedLoadOrg)
diff --git a/module/plugins/crypter/StealthTo.py b/module/plugins/crypter/StealthTo.py
index 5173421f1..8513fb3f8 100644
--- a/module/plugins/crypter/StealthTo.py
+++ b/module/plugins/crypter/StealthTo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class StealthTo(DeadCrypter):
diff --git a/module/plugins/crypter/TrailerzoneInfo.py b/module/plugins/crypter/TrailerzoneInfo.py
index 10780dd45..ca14e6df5 100644
--- a/module/plugins/crypter/TrailerzoneInfo.py
+++ b/module/plugins/crypter/TrailerzoneInfo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class TrailerzoneInfo(DeadCrypter):
diff --git a/module/plugins/crypter/TurbobitNetFolder.py b/module/plugins/crypter/TurbobitNet.py
index c6734c997..67f7c74d4 100644
--- a/module/plugins/crypter/TurbobitNetFolder.py
+++ b/module/plugins/crypter/TurbobitNet.py
@@ -3,11 +3,11 @@
import re
from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
-from module.common.json_layer import json_loads
+from pyload.utils import json_loads
-class TurbobitNetFolder(SimpleCrypter):
- __name__ = "TurbobitNetFolder"
+class TurbobitNet(SimpleCrypter):
+ __name__ = "TurbobitNet"
__type__ = "crypter"
__version__ = "0.05"
diff --git a/module/plugins/crypter/TusfilesNetFolder.py b/module/plugins/crypter/TusfilesNet.py
index cb8efc9a8..94b6bbb5d 100644
--- a/module/plugins/crypter/TusfilesNetFolder.py
+++ b/module/plugins/crypter/TusfilesNet.py
@@ -7,8 +7,8 @@ from urlparse import urljoin
from module.plugins.internal.XFSCrypter import XFSCrypter, create_getInfo
-class TusfilesNetFolder(XFSCrypter):
- __name__ = "TusfilesNetFolder"
+class TusfilesNet(XFSCrypter):
+ __name__ = "TusfilesNet"
__type__ = "crypter"
__version__ = "0.08"
diff --git a/module/plugins/crypter/UlozToFolder.py b/module/plugins/crypter/UlozTo.py
index a1f3ed5ea..81fbee172 100644
--- a/module/plugins/crypter/UlozToFolder.py
+++ b/module/plugins/crypter/UlozTo.py
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
import re
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
-class UlozToFolder(Crypter):
- __name__ = "UlozToFolder"
+class UlozTo(Crypter):
+ __name__ = "UlozTo"
__type__ = "crypter"
__version__ = "0.20"
diff --git a/module/plugins/crypter/UploadableChFolder.py b/module/plugins/crypter/UploadableCh.py
index 22f9ca2ed..8e4ff71c0 100644
--- a/module/plugins/crypter/UploadableChFolder.py
+++ b/module/plugins/crypter/UploadableCh.py
@@ -3,8 +3,8 @@
from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
-class UploadableChFolder(SimpleCrypter):
- __name__ = "UploadableChFolder"
+class UploadableCh(SimpleCrypter):
+ __name__ = "UploadableCh"
__type__ = "crypter"
__version__ = "0.03"
@@ -12,7 +12,7 @@ class UploadableChFolder(SimpleCrypter):
__config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
- __description__ = """ Uploadable.ch folder decrypter plugin """
+ __description__ = """Uploadable.ch folder decrypter plugin"""
__license__ = "GPLv3"
__authors__ = [("guidobelix", "guidobelix@hotmail.it"),
("Walter Purcaro", "vuolter@gmail.com")]
diff --git a/module/plugins/crypter/UploadedToFolder.py b/module/plugins/crypter/UploadedTo.py
index 0a71add70..f6bc861de 100644
--- a/module/plugins/crypter/UploadedToFolder.py
+++ b/module/plugins/crypter/UploadedTo.py
@@ -7,8 +7,8 @@ from urlparse import urljoin
from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
-class UploadedToFolder(SimpleCrypter):
- __name__ = "UploadedToFolder"
+class UploadedTo(SimpleCrypter):
+ __name__ = "UploadedTo"
__type__ = "crypter"
__version__ = "0.42"
diff --git a/module/plugins/crypter/WiiReloadedOrg.py b/module/plugins/crypter/WiiReloadedOrg.py
index c3c5b8222..7b4270e65 100644
--- a/module/plugins/crypter/WiiReloadedOrg.py
+++ b/module/plugins/crypter/WiiReloadedOrg.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
class WiiReloadedOrg(DeadCrypter):
@@ -13,7 +13,7 @@ class WiiReloadedOrg(DeadCrypter):
__description__ = """Wii-Reloaded.org decrypter plugin"""
__license__ = "GPLv3"
- __authors__ = [("hzpz", None)]
+ __authors__ = [("hzpz", "")]
getInfo = create_getInfo(WiiReloadedOrg)
diff --git a/module/plugins/crypter/WuploadComFolder.py b/module/plugins/crypter/WuploadCom.py
index 873c71fad..0a098a40c 100644
--- a/module/plugins/crypter/WuploadComFolder.py
+++ b/module/plugins/crypter/WuploadCom.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
+from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo
-class WuploadComFolder(DeadCrypter):
- __name__ = "WuploadComFolder"
+class WuploadCom(DeadCrypter):
+ __name__ = "WuploadCom"
__type__ = "crypter"
__version__ = "0.01"
@@ -15,4 +15,4 @@ class WuploadComFolder(DeadCrypter):
__authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-getInfo = create_getInfo(WuploadComFolder)
+getInfo = create_getInfo(WuploadCom)
diff --git a/module/plugins/crypter/XFileSharingProFolder.py b/module/plugins/crypter/XFileSharingPro.py
index 1d001772d..a965f0c48 100644
--- a/module/plugins/crypter/XFileSharingProFolder.py
+++ b/module/plugins/crypter/XFileSharingPro.py
@@ -5,8 +5,8 @@ import re
from module.plugins.internal.XFSCrypter import XFSCrypter, create_getInfo
-class XFileSharingProFolder(XFSCrypter):
- __name__ = "XFileSharingProFolder"
+class XFileSharingPro(XFSCrypter):
+ __name__ = "XFileSharingPro"
__type__ = "crypter"
__version__ = "0.04"
@@ -26,7 +26,7 @@ class XFileSharingProFolder(XFSCrypter):
def init(self):
- super(XFileSharingProFolder, self).init()
+ super(XFileSharingPro, self).init()
self.__pattern__ = self.core.pluginManager.crypterPlugins[self.__name__]['pattern']
diff --git a/module/plugins/crypter/XupPl.py b/module/plugins/crypter/XupPl.py
index f2ecbee5a..9d4d27b61 100644
--- a/module/plugins/crypter/XupPl.py
+++ b/module/plugins/crypter/XupPl.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.Crypter import Crypter
+from pyload.plugin.Crypter import Crypter
class XupPl(Crypter):
diff --git a/module/plugins/crypter/YoutubeBatch.py b/module/plugins/crypter/YoutubeBatch.py
index 40b4eedd2..5e4269fd2 100644
--- a/module/plugins/crypter/YoutubeBatch.py
+++ b/module/plugins/crypter/YoutubeBatch.py
@@ -4,9 +4,9 @@ import re
from urlparse import urljoin
-from module.common.json_layer import json_loads
-from module.plugins.Crypter import Crypter
-from module.utils import save_join
+from pyload.utils import json_loads
+from pyload.plugin.Crypter import Crypter
+from pyload.utils import safe_join
class YoutubeBatch(Crypter):
@@ -16,10 +16,10 @@ class YoutubeBatch(Crypter):
__pattern__ = r'https?://(?:www\.|m\.)?youtube\.com/(?P<TYPE>user|playlist|view_play_list)(/|.*?[?&](?:list|p)=)(?P<ID>[\w-]+)'
__config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True),
- ("likes", "bool", "Grab user (channel) liked videos", False),
- ("favorites", "bool", "Grab user (channel) favorite videos", False),
- ("uploads", "bool", "Grab channel unplaylisted videos", True)]
+ ("subfolder_per_package", "bool", "Create a subfolder for each package", True),
+ ("likes", "bool", "Grab user (channel) liked videos", False),
+ ("favorites", "bool", "Grab user (channel) favorite videos", False),
+ ("uploads", "bool", "Grab channel unplaylisted videos", True)]
__description__ = """Youtube.com channel & playlist decrypter plugin"""
__license__ = "GPLv3"
@@ -132,7 +132,7 @@ class YoutubeBatch(Crypter):
for p in playlists:
p_name = p['title']
p_videos = self.getVideosId(p['id'])
- p_folder = save_join(self.config['general']['download_folder'], p['channelTitle'], p_name)
+ p_folder = safe_join(self.config['general']['download_folder'], p['channelTitle'], p_name)
self.logDebug("%s video\s found on playlist \"%s\"" % (len(p_videos), p_name))
if not p_videos:
diff --git a/module/plugins/crypter/__init__.py b/module/plugins/crypter/__init__.py
index e69de29bb..40a96afc6 100644
--- a/module/plugins/crypter/__init__.py
+++ b/module/plugins/crypter/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/plugins/hooks/BypassCaptcha.py b/module/plugins/hooks/BypassCaptcha.py
index cf8754dae..bd19fe953 100644
--- a/module/plugins/hooks/BypassCaptcha.py
+++ b/module/plugins/hooks/BypassCaptcha.py
@@ -4,7 +4,7 @@ from pycurl import FORM_FILE, LOW_SPEED_TIME
from module.network.HTTPRequest import BadHeader
from module.network.RequestFactory import getURL, getRequest
-from module.plugins.Hook import Hook, threaded
+from module.plugins.Addon import Addon, threaded
class BypassCaptchaException(Exception):
@@ -25,13 +25,13 @@ class BypassCaptchaException(Exception):
return "<BypassCaptchaException %s>" % self.err
-class BypassCaptcha(Hook):
+class BypassCaptcha(Addon):
__name__ = "BypassCaptcha"
__type__ = "hook"
__version__ = "0.06"
__config__ = [("force", "bool", "Force BC even if client is connected", False),
- ("passkey", "password", "Passkey", "")]
+ ("passkey", "password", "Passkey", "")]
__description__ = """Send captchas to BypassCaptcha.com"""
__license__ = "GPLv3"
@@ -47,15 +47,6 @@ class BypassCaptcha(Hook):
GETCREDITS_URL = "http://bypasscaptcha.com/ex_left.php"
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
-
-
def getCredits(self):
res = getURL(self.GETCREDITS_URL, post={"key": self.getConfig("passkey")})
@@ -98,7 +89,7 @@ class BypassCaptcha(Hook):
self.logError(_("Could not send response"), e)
- def newCaptchaTask(self, task):
+ def captchaTask(self, task):
if "service" in task.data:
return False
diff --git a/module/plugins/hooks/Captcha9Kw.py b/module/plugins/hooks/Captcha9Kw.py
index 544965b0f..04caff345 100644
--- a/module/plugins/hooks/Captcha9Kw.py
+++ b/module/plugins/hooks/Captcha9Kw.py
@@ -7,28 +7,28 @@ import re
from base64 import b64encode
from time import sleep
-from module.network.HTTPRequest import BadHeader
-from module.network.RequestFactory import getURL
+from pyload.network.HTTPRequest import BadHeader
+from pyload.network.RequestFactory import getURL
from module.plugins.Hook import Hook, threaded
-class Captcha9Kw(Hook):
+class Captcha9kw(Hook):
__name__ = "Captcha9Kw"
__type__ = "hook"
__version__ = "0.28"
__config__ = [("ssl" , "bool" , "Use HTTPS" , True ),
- ("force" , "bool" , "Force captcha resolving even if client is connected" , True ),
- ("confirm" , "bool" , "Confirm Captcha (cost +6 credits)" , False ),
- ("captchaperhour", "int" , "Captcha per hour" , "9999" ),
- ("captchapermin" , "int" , "Captcha per minute" , "9999" ),
- ("prio" , "int" , "Priority (max 10)(cost +0 -> +10 credits)" , "0" ),
- ("queue" , "int" , "Max. Queue (max 999)" , "50" ),
- ("hoster_options", "string" , "Hoster options (format: pluginname:prio=1:selfsolfe=1:confirm=1:timeout=900|...)", "ShareonlineBiz:prio=0:timeout=999 | UploadedTo:prio=0:timeout=999"),
- ("selfsolve" , "bool" , "Selfsolve (manually solve your captcha in your 9kw client if active)" , "0" ),
- ("passkey" , "password", "API key" , "" ),
- ("timeout" , "int" , "Timeout in seconds (min 60, max 3999)" , "900" )]
+ ("force" , "bool" , "Force captcha resolving even if client is connected" , True ),
+ ("confirm" , "bool" , "Confirm Captcha (cost +6 credits)" , False ),
+ ("captchaperhour", "int" , "Captcha per hour" , "9999" ),
+ ("captchapermin" , "int" , "Captcha per minute" , "9999" ),
+ ("prio" , "int" , "Priority (max 10)(cost +0 -> +10 credits)" , "0" ),
+ ("queue" , "int" , "Max. Queue (max 999)" , "50" ),
+ ("hoster_options", "string" , "Hoster options (format: pluginname:prio=1:selfsolfe=1:confirm=1:timeout=900|...)", "ShareonlineBiz:prio=0:timeout=999 | UploadedTo:prio=0:timeout=999"),
+ ("selfsolve" , "bool" , "Selfsolve (manually solve your captcha in your 9kw client if active)" , "0" ),
+ ("passkey" , "password", "API key" , "" ),
+ ("timeout" , "int" , "Timeout in seconds (min 60, max 3999)" , "900" )]
__description__ = """Send captchas to 9kw.eu"""
__license__ = "GPLv3"
@@ -39,13 +39,7 @@ class Captcha9Kw(Hook):
API_URL = "http://www.9kw.eu/index.cgi"
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
+ def activate(self):
if self.getConfig("ssl"):
self.API_URL = self.API_URL.replace("http://", "https://")
@@ -168,7 +162,7 @@ class Captcha9Kw(Hook):
task.setResult(result)
- def newCaptchaTask(self, task):
+ def captchaTask(self, task):
if not task.isTextual() and not task.isPositional():
return
diff --git a/module/plugins/hooks/CaptchaBrotherhood.py b/module/plugins/hooks/CaptchaBrotherhood.py
index 3c08f5e36..ba9d3eb8e 100644
--- a/module/plugins/hooks/CaptchaBrotherhood.py
+++ b/module/plugins/hooks/CaptchaBrotherhood.py
@@ -13,7 +13,7 @@ except ImportError:
from time import sleep
from urllib import urlencode
-from module.network.RequestFactory import getURL, getRequest
+from pyload.network.RequestFactory import getURL, getRequest
from module.plugins.Hook import Hook, threaded
@@ -35,14 +35,14 @@ class CaptchaBrotherhoodException(Exception):
return "<CaptchaBrotherhoodException %s>" % self.err
-class CaptchaBrotherhood(Hook):
+class CaptchaBrotherhood(Addon):
__name__ = "CaptchaBrotherhood"
__type__ = "hook"
__version__ = "0.08"
__config__ = [("username", "str", "Username", ""),
- ("force", "bool", "Force CT even if client is connected", False),
- ("passkey", "password", "Password", "")]
+ ("force", "bool", "Force CT even if client is connected", False),
+ ("passkey", "password", "Password", "")]
__description__ = """Send captchas to CaptchaBrotherhood.com"""
__license__ = "GPLv3"
@@ -53,15 +53,6 @@ class CaptchaBrotherhood(Hook):
API_URL = "http://www.captchabrotherhood.com/"
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
-
-
def getCredits(self):
res = getURL(self.API_URL + "askCredits.aspx",
get={"username": self.getConfig("username"), "password": self.getConfig("passkey")})
@@ -136,7 +127,7 @@ class CaptchaBrotherhood(Hook):
return res
- def newCaptchaTask(self, task):
+ def captchaTask(self, task):
if "service" in task.data:
return False
diff --git a/module/plugins/hooks/DeathByCaptcha.py b/module/plugins/hooks/DeathByCaptcha.py
index d513c446d..8786df12f 100644
--- a/module/plugins/hooks/DeathByCaptcha.py
+++ b/module/plugins/hooks/DeathByCaptcha.py
@@ -8,9 +8,9 @@ from base64 import b64encode
from pycurl import FORM_FILE, HTTPHEADER
from time import sleep
-from module.common.json_layer import json_loads
-from module.network.HTTPRequest import BadHeader
-from module.network.RequestFactory import getRequest
+from pyload.utils import json_loads
+from pyload.network.HTTPRequest import BadHeader
+from pyload.network.RequestFactory import getRequest
from module.plugins.Hook import Hook, threaded
@@ -48,14 +48,14 @@ class DeathByCaptchaException(Exception):
return "<DeathByCaptchaException %s>" % self.err
-class DeathByCaptcha(Hook):
+class DeathByCaptcha(Addon):
__name__ = "DeathByCaptcha"
__type__ = "hook"
__version__ = "0.06"
__config__ = [("username", "str", "Username", ""),
- ("passkey", "password", "Password", ""),
- ("force", "bool", "Force DBC even if client is connected", False)]
+ ("passkey", "password", "Password", ""),
+ ("force", "bool", "Force DBC even if client is connected", False)]
__description__ = """Send captchas to DeathByCaptcha.com"""
__license__ = "GPLv3"
@@ -66,15 +66,6 @@ class DeathByCaptcha(Hook):
API_URL = "http://api.dbcapi.me/api/"
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
-
-
def api_response(self, api="captcha", post=False, multipart=False):
req = getRequest()
req.c.setopt(HTTPHEADER, ["Accept: application/json", "User-Agent: pyLoad %s" % self.core.version])
@@ -165,7 +156,7 @@ class DeathByCaptcha(Hook):
return ticket, result
- def newCaptchaTask(self, task):
+ def captchaTask(self, task):
if "service" in task.data:
return False
diff --git a/module/plugins/hooks/ExpertDecoders.py b/module/plugins/hooks/ExpertDecoders.py
index c9f8204c4..04f6d0795 100644
--- a/module/plugins/hooks/ExpertDecoders.py
+++ b/module/plugins/hooks/ExpertDecoders.py
@@ -11,13 +11,13 @@ from module.network.RequestFactory import getURL, getRequest
from module.plugins.Hook import Hook, threaded
-class ExpertDecoders(Hook):
+class ExpertDecoders(Addon):
__name__ = "ExpertDecoders"
__type__ = "hook"
__version__ = "0.04"
__config__ = [("force", "bool", "Force CT even if client is connected", False),
- ("passkey", "password", "Access key", "")]
+ ("passkey", "password", "Access key", "")]
__description__ = """Send captchas to expertdecoders.com"""
__license__ = "GPLv3"
@@ -28,15 +28,6 @@ class ExpertDecoders(Hook):
API_URL = "http://www.fasttypers.org/imagepost.ashx"
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
-
-
def getCredits(self):
res = getURL(self.API_URL, post={"key": self.getConfig("passkey"), "action": "balance"})
@@ -74,7 +65,7 @@ class ExpertDecoders(Hook):
task.setResult(result)
- def newCaptchaTask(self, task):
+ def captchaTask(self, task):
if not task.isTextual():
return False
diff --git a/module/plugins/hooks/FastixRu.py b/module/plugins/hooks/FastixRu.py
index 6373da8d9..5f339c4c0 100644
--- a/module/plugins/hooks/FastixRu.py
+++ b/module/plugins/hooks/FastixRu.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.common.json_layer import json_loads
+from pyload.utils import json_loads
from module.plugins.internal.MultiHook import MultiHook
diff --git a/module/plugins/hooks/ImageTyperz.py b/module/plugins/hooks/ImageTyperz.py
index d62fed385..8bf326f4d 100644
--- a/module/plugins/hooks/ImageTyperz.py
+++ b/module/plugins/hooks/ImageTyperz.py
@@ -29,14 +29,14 @@ class ImageTyperzException(Exception):
return "<ImageTyperzException %s>" % self.err
-class ImageTyperz(Hook):
+class ImageTyperz(Addon):
__name__ = "ImageTyperz"
__type__ = "hook"
__version__ = "0.06"
__config__ = [("username", "str", "Username", ""),
- ("passkey", "password", "Password", ""),
- ("force", "bool", "Force IT even if client is connected", False)]
+ ("passkey", "password", "Password", ""),
+ ("force", "bool", "Force IT even if client is connected", False)]
__description__ = """Send captchas to ImageTyperz.com"""
__license__ = "GPLv3"
@@ -49,15 +49,6 @@ class ImageTyperz(Hook):
GETCREDITS_URL = "http://captchatypers.com/Forms/RequestBalance.ashx"
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
-
-
def getCredits(self):
res = getURL(self.GETCREDITS_URL,
post={'action': "REQUESTBALANCE",
@@ -112,7 +103,7 @@ class ImageTyperz(Hook):
return ticket, result
- def newCaptchaTask(self, task):
+ def captchaTask(self, task):
if "service" in task.data:
return False
diff --git a/module/plugins/hooks/LinksnappyCom.py b/module/plugins/hooks/LinksnappyCom.py
index 5eb0c7f6d..7e607a3d3 100644
--- a/module/plugins/hooks/LinksnappyCom.py
+++ b/module/plugins/hooks/LinksnappyCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.common.json_layer import json_loads
+from pyload.utils import json_loads
from module.plugins.internal.MultiHook import MultiHook
diff --git a/module/plugins/hooks/MegaDebridEu.py b/module/plugins/hooks/MegaDebridEu.py
index f67fa7ac0..0711b3546 100644
--- a/module/plugins/hooks/MegaDebridEu.py
+++ b/module/plugins/hooks/MegaDebridEu.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.common.json_layer import json_loads
+from pyload.utils import json_loads
from module.plugins.internal.MultiHook import MultiHook
diff --git a/module/plugins/hooks/MyfastfileCom.py b/module/plugins/hooks/MyfastfileCom.py
index 86408cb6d..a1b5aaba0 100644
--- a/module/plugins/hooks/MyfastfileCom.py
+++ b/module/plugins/hooks/MyfastfileCom.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.common.json_layer import json_loads
from module.plugins.internal.MultiHook import MultiHook
+from pyload.utils import json_loads
class MyfastfileCom(MultiHook):
diff --git a/module/plugins/hooks/PremiumizeMe.py b/module/plugins/hooks/PremiumizeMe.py
index 293fcf339..18b6187c4 100644
--- a/module/plugins/hooks/PremiumizeMe.py
+++ b/module/plugins/hooks/PremiumizeMe.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.common.json_layer import json_loads
+from pyload.utils import json_loads
from module.plugins.internal.MultiHook import MultiHook
diff --git a/module/plugins/hooks/RPNetBiz.py b/module/plugins/hooks/RPNetBiz.py
index 0768bd6cd..2277b10ab 100644
--- a/module/plugins/hooks/RPNetBiz.py
+++ b/module/plugins/hooks/RPNetBiz.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.common.json_layer import json_loads
+from pyload.utils import json_loads
from module.plugins.internal.MultiHook import MultiHook
diff --git a/module/plugins/hooks/SimplyPremiumCom.py b/module/plugins/hooks/SimplyPremiumCom.py
index 843a3aa82..e51cbd1bc 100644
--- a/module/plugins/hooks/SimplyPremiumCom.py
+++ b/module/plugins/hooks/SimplyPremiumCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.common.json_layer import json_loads
+from pyload.utils import json_loads
from module.plugins.internal.MultiHook import MultiHook
diff --git a/module/plugins/hooks/UnrestrictLi.py b/module/plugins/hooks/UnrestrictLi.py
index cb5abb26e..b4c547883 100644
--- a/module/plugins/hooks/UnrestrictLi.py
+++ b/module/plugins/hooks/UnrestrictLi.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.common.json_layer import json_loads
+from pyload.utils import json_loads
from module.plugins.internal.MultiHook import MultiHook
diff --git a/module/plugins/hooks/XFileSharingPro.py b/module/plugins/hooks/XFileSharingPro.py
index a1ee7f5ef..78e14c179 100644
--- a/module/plugins/hooks/XFileSharingPro.py
+++ b/module/plugins/hooks/XFileSharingPro.py
@@ -2,10 +2,10 @@
import re
-from module.plugins.Hook import Hook
+from pyload.plugin.Addon import Addon
-class XFileSharingPro(Hook):
+class XFileSharingPro(Addon):
__name__ = "XFileSharingPro"
__type__ = "hook"
__version__ = "0.31"
@@ -45,20 +45,14 @@ class XFileSharingPro(Hook):
# self.loadPattern()
- #@TODO: Remove in 0.4.10
- def initPeriodical(self):
- pass
-
-
- def coreReady(self):
+ def activate(self):
self.loadPattern()
def loadPattern(self):
use_builtin_list = self.getConfig('use_builtin_list')
- for type, plugin in (("hoster", "XFileSharingPro"),
- ("crypter", "XFileSharingProFolder")):
+ for type in ("hoster", "crypter"):
every_plugin = not self.getConfig("use_%s_list" % type)
if every_plugin:
@@ -75,7 +69,7 @@ class XFileSharingPro(Hook):
if not plugin_set:
self.logInfo(_("No %s to handle") % type)
- self._unload(type, plugin)
+ self._unload(type)
return
match_list = '|'.join(sorted(plugin_set))
@@ -88,39 +82,23 @@ class XFileSharingPro(Hook):
pattern = self.regexp[type][1] % match_list.replace('.', '\.')
- dict = self.core.pluginManager.plugins[type][plugin]
+ dict = self.core.pluginManager.plugins[type]["XFileSharingPro"]
dict['pattern'] = pattern
dict['re'] = re.compile(pattern)
self.logDebug("Loaded %s pattern: %s" % (type, pattern))
- def _unload(self, type, plugin):
- dict = self.core.pluginManager.plugins[type][plugin]
+ def _unload(self, type):
+ dict = self.core.pluginManager.plugins[type]["XFileSharingPro"]
dict['pattern'] = r'^unmatchable$'
dict['re'] = re.compile(dict['pattern'])
- def unload(self):
+ def deactivate(self):
# self.unloadHoster("BasePlugin")
- for type, plugin in (("hoster", "XFileSharingPro"),
- ("crypter", "XFileSharingProFolder")):
- self._unload(type, plugin)
-
-
- def unloadHoster(self, hoster):
- hdict = self.core.pluginManager.hosterPlugins[hoster]
- if "new_name" in hdict and hdict['new_name'] == "XFileSharingPro":
- if "module" in hdict:
- hdict.pop('module', None)
-
- if "new_module" in hdict:
- hdict.pop('new_module', None)
- hdict.pop('new_name', None)
-
- return True
- else:
- return False
+ for type in ("hoster", "crypter"):
+ self._unload(type, "XFileSharingPro")
# def downloadFailed(self, pyfile):
diff --git a/module/plugins/hooks/__init__.py b/module/plugins/hooks/__init__.py
index e69de29bb..40a96afc6 100644
--- a/module/plugins/hooks/__init__.py
+++ b/module/plugins/hooks/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/plugins/hoster/BezvadataCz.py b/module/plugins/hoster/BezvadataCz.py
index aece7813d..86ef46cc3 100644
--- a/module/plugins/hoster/BezvadataCz.py
+++ b/module/plugins/hoster/BezvadataCz.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class BezvadataCz(SimpleHoster):
diff --git a/module/plugins/hoster/BillionuploadsCom.py b/module/plugins/hoster/BillionuploadsCom.py
index 7d7e2624a..fb79fe29a 100644
--- a/module/plugins/hoster/BillionuploadsCom.py
+++ b/module/plugins/hoster/BillionuploadsCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class BillionuploadsCom(XFSHoster):
diff --git a/module/plugins/hoster/BitshareCom.py b/module/plugins/hoster/BitshareCom.py
index 657e70e56..feb2b682c 100644
--- a/module/plugins/hoster/BitshareCom.py
+++ b/module/plugins/hoster/BitshareCom.py
@@ -4,8 +4,8 @@ from __future__ import with_statement
import re
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class BitshareCom(SimpleHoster):
@@ -17,7 +17,7 @@ class BitshareCom(SimpleHoster):
__description__ = """Bitshare.com hoster plugin"""
__license__ = "GPLv3"
- __authors__ = [("Paul King", None),
+ __authors__ = [("Paul King", ""),
("fragonib", "fragonib[AT]yahoo[DOT]es")]
diff --git a/module/plugins/hoster/BoltsharingCom.py b/module/plugins/hoster/BoltsharingCom.py
index 924545a29..93f6d619a 100644
--- a/module/plugins/hoster/BoltsharingCom.py
+++ b/module/plugins/hoster/BoltsharingCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class BoltsharingCom(DeadHoster):
diff --git a/module/plugins/hoster/CatShareNet.py b/module/plugins/hoster/CatShareNet.py
index 125e63481..d61856b7c 100644
--- a/module/plugins/hoster/CatShareNet.py
+++ b/module/plugins/hoster/CatShareNet.py
@@ -2,8 +2,8 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-from module.plugins.internal.CaptchaService import ReCaptcha
+from pyload.plugin.internal.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class CatShareNet(SimpleHoster):
@@ -16,7 +16,7 @@ class CatShareNet(SimpleHoster):
__description__ = """CatShare.net hoster plugin"""
__license__ = "GPLv3"
__authors__ = [("z00nx", "z00nx0@gmail.com"),
- ("prOq", None),
+ ("prOq", ""),
("Walter Purcaro", "vuolter@gmail.com")]
diff --git a/module/plugins/hoster/CloudzerNet.py b/module/plugins/hoster/CloudzerNet.py
index c5440391f..cb764e335 100644
--- a/module/plugins/hoster/CloudzerNet.py
+++ b/module/plugins/hoster/CloudzerNet.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class CloudzerNet(DeadHoster):
diff --git a/module/plugins/hoster/CramitIn.py b/module/plugins/hoster/CramitIn.py
index 44dac958d..8e239cdc5 100644
--- a/module/plugins/hoster/CramitIn.py
+++ b/module/plugins/hoster/CramitIn.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class CramitIn(XFSHoster):
diff --git a/module/plugins/hoster/CrockoCom.py b/module/plugins/hoster/CrockoCom.py
index 474042a5a..a2abc2b7b 100644
--- a/module/plugins/hoster/CrockoCom.py
+++ b/module/plugins/hoster/CrockoCom.py
@@ -2,8 +2,8 @@
import re
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class CrockoCom(SimpleHoster):
diff --git a/module/plugins/hoster/CyberlockerCh.py b/module/plugins/hoster/CyberlockerCh.py
index b26909096..1c6a04caf 100644
--- a/module/plugins/hoster/CyberlockerCh.py
+++ b/module/plugins/hoster/CyberlockerCh.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class CyberlockerCh(DeadHoster):
diff --git a/module/plugins/hoster/CzshareCom.py b/module/plugins/hoster/CzshareCom.py
index 49c7a6648..419e43a7a 100644
--- a/module/plugins/hoster/CzshareCom.py
+++ b/module/plugins/hoster/CzshareCom.py
@@ -5,8 +5,8 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-from module.utils import parseFileSize
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.utils import parseFileSize
class CzshareCom(SimpleHoster):
diff --git a/module/plugins/hoster/DailymotionCom.py b/module/plugins/hoster/DailymotionCom.py
index 02df9dde7..c212fa872 100644
--- a/module/plugins/hoster/DailymotionCom.py
+++ b/module/plugins/hoster/DailymotionCom.py
@@ -2,10 +2,10 @@
import re
-from module.PyFile import statusMap
-from module.common.json_layer import json_loads
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
+from pyload.datatype.File import statusMap
+from pyload.utils import json_loads
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.Hoster import Hoster
def getInfo(urls):
diff --git a/module/plugins/hoster/DataHu.py b/module/plugins/hoster/DataHu.py
index f4b0692a8..f24b5cdba 100644
--- a/module/plugins/hoster/DataHu.py
+++ b/module/plugins/hoster/DataHu.py
@@ -5,7 +5,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class DataHu(SimpleHoster):
@@ -17,7 +17,7 @@ class DataHu(SimpleHoster):
__description__ = """Data.hu hoster plugin"""
__license__ = "GPLv3"
- __authors__ = [("crash", None),
+ __authors__ = [("crash", ""),
("stickell", "l.stickell@yahoo.it")]
diff --git a/module/plugins/hoster/DataportCz.py b/module/plugins/hoster/DataportCz.py
index 266199616..592bece93 100644
--- a/module/plugins/hoster/DataportCz.py
+++ b/module/plugins/hoster/DataportCz.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class DataportCz(SimpleHoster):
diff --git a/module/plugins/hoster/DateiTo.py b/module/plugins/hoster/DateiTo.py
index e5061e026..66102939a 100644
--- a/module/plugins/hoster/DateiTo.py
+++ b/module/plugins/hoster/DateiTo.py
@@ -2,8 +2,8 @@
import re
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class DateiTo(SimpleHoster):
diff --git a/module/plugins/hoster/DdlstorageCom.py b/module/plugins/hoster/DdlstorageCom.py
index a45ef27e9..32a5a8bb7 100644
--- a/module/plugins/hoster/DdlstorageCom.py
+++ b/module/plugins/hoster/DdlstorageCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class DdlstorageCom(DeadHoster):
diff --git a/module/plugins/hoster/DepositfilesCom.py b/module/plugins/hoster/DepositfilesCom.py
index 6de05af9a..c9e2040f8 100644
--- a/module/plugins/hoster/DepositfilesCom.py
+++ b/module/plugins/hoster/DepositfilesCom.py
@@ -4,8 +4,8 @@ import re
from urllib import unquote
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class DepositfilesCom(SimpleHoster):
diff --git a/module/plugins/hoster/DevhostSt.py b/module/plugins/hoster/DevhostSt.py
index d786ba135..2365baa5f 100644
--- a/module/plugins/hoster/DevhostSt.py
+++ b/module/plugins/hoster/DevhostSt.py
@@ -5,7 +5,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class DevhostSt(SimpleHoster):
diff --git a/module/plugins/hoster/DlFreeFr.py b/module/plugins/hoster/DlFreeFr.py
index 5d4e9e7b8..541989120 100644
--- a/module/plugins/hoster/DlFreeFr.py
+++ b/module/plugins/hoster/DlFreeFr.py
@@ -3,10 +3,11 @@
import pycurl
import re
-from module.network.Browser import Browser
-from module.network.CookieJar import CookieJar
-from module.plugins.internal.CaptchaService import AdYouLike
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, replace_patterns
+from pyload.network.Browser import Browser
+from pyload.network.CookieJar import CookieJar
+from pyload.plugin.internal.captcha import AdYouLike
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo, replace_patterns
+from pyload.utils import json_loads
class CustomBrowser(Browser):
diff --git a/module/plugins/hoster/DuploadOrg.py b/module/plugins/hoster/DuploadOrg.py
index 73702eb67..6dd25407e 100644
--- a/module/plugins/hoster/DuploadOrg.py
+++ b/module/plugins/hoster/DuploadOrg.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class DuploadOrg(DeadHoster):
diff --git a/module/plugins/hoster/EasybytezCom.py b/module/plugins/hoster/EasybytezCom.py
index 693910c1b..6ca45f545 100644
--- a/module/plugins/hoster/EasybytezCom.py
+++ b/module/plugins/hoster/EasybytezCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class EasybytezCom(XFSHoster):
diff --git a/module/plugins/hoster/EdiskCz.py b/module/plugins/hoster/EdiskCz.py
index f8ccc972e..76e55ae3c 100644
--- a/module/plugins/hoster/EdiskCz.py
+++ b/module/plugins/hoster/EdiskCz.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class EdiskCz(SimpleHoster):
diff --git a/module/plugins/hoster/EgoFilesCom.py b/module/plugins/hoster/EgoFilesCom.py
index 9a2f50ed4..ad5ad0796 100644
--- a/module/plugins/hoster/EgoFilesCom.py
+++ b/module/plugins/hoster/EgoFilesCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class EgoFilesCom(DeadHoster):
diff --git a/module/plugins/hoster/EnteruploadCom.py b/module/plugins/hoster/EnteruploadCom.py
index bbd613f57..0be9b8d76 100644
--- a/module/plugins/hoster/EnteruploadCom.py
+++ b/module/plugins/hoster/EnteruploadCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class EnteruploadCom(DeadHoster):
diff --git a/module/plugins/hoster/EpicShareNet.py b/module/plugins/hoster/EpicShareNet.py
index 8ac8cdaf2..061782468 100644
--- a/module/plugins/hoster/EpicShareNet.py
+++ b/module/plugins/hoster/EpicShareNet.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class EpicShareNet(DeadHoster):
diff --git a/module/plugins/hoster/EuroshareEu.py b/module/plugins/hoster/EuroshareEu.py
index b4c9ace6a..41cc37087 100644
--- a/module/plugins/hoster/EuroshareEu.py
+++ b/module/plugins/hoster/EuroshareEu.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class EuroshareEu(SimpleHoster):
diff --git a/module/plugins/hoster/ExtabitCom.py b/module/plugins/hoster/ExtabitCom.py
index 68695faad..f9f76b28b 100644
--- a/module/plugins/hoster/ExtabitCom.py
+++ b/module/plugins/hoster/ExtabitCom.py
@@ -2,7 +2,7 @@
import re
-from module.common.json_layer import json_loads
+from pyload.utils import json_loads
from module.plugins.internal.CaptchaService import ReCaptcha
from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, secondsToMidnight
diff --git a/module/plugins/hoster/FastixRu.py b/module/plugins/hoster/FastixRu.py
index d534101d8..8f1b9e694 100644
--- a/module/plugins/hoster/FastixRu.py
+++ b/module/plugins/hoster/FastixRu.py
@@ -5,7 +5,7 @@ import re
from random import randrange
from urllib import unquote
-from module.common.json_layer import json_loads
+from pyload.utils import json_loads
from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo
diff --git a/module/plugins/hoster/FileApeCom.py b/module/plugins/hoster/FileApeCom.py
index db843586b..212e3ba9c 100644
--- a/module/plugins/hoster/FileApeCom.py
+++ b/module/plugins/hoster/FileApeCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class FileApeCom(DeadHoster):
@@ -12,7 +12,7 @@ class FileApeCom(DeadHoster):
__description__ = """FileApe.com hoster plugin"""
__license__ = "GPLv3"
- __authors__ = [("espes", None)]
+ __authors__ = [("espes", "")]
getInfo = create_getInfo(FileApeCom)
diff --git a/module/plugins/hoster/FileSharkPl.py b/module/plugins/hoster/FileSharkPl.py
index e4ce711bd..29c95ee8f 100644
--- a/module/plugins/hoster/FileSharkPl.py
+++ b/module/plugins/hoster/FileSharkPl.py
@@ -4,7 +4,7 @@ import re
from urlparse import urljoin
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class FileSharkPl(SimpleHoster):
@@ -16,7 +16,7 @@ class FileSharkPl(SimpleHoster):
__description__ = """FileShark.pl hoster plugin"""
__license__ = "GPLv3"
- __authors__ = [("prOq", None),
+ __authors__ = [("prOq", ""),
("Walter Purcaro", "vuolter@gmail.com")]
diff --git a/module/plugins/hoster/FileStoreTo.py b/module/plugins/hoster/FileStoreTo.py
index ad3feba1b..8a5171219 100644
--- a/module/plugins/hoster/FileStoreTo.py
+++ b/module/plugins/hoster/FileStoreTo.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class FileStoreTo(SimpleHoster):
diff --git a/module/plugins/hoster/FilebeerInfo.py b/module/plugins/hoster/FilebeerInfo.py
index 885010a2c..51a0137a0 100644
--- a/module/plugins/hoster/FilebeerInfo.py
+++ b/module/plugins/hoster/FilebeerInfo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class FilebeerInfo(DeadHoster):
diff --git a/module/plugins/hoster/FilecloudIo.py b/module/plugins/hoster/FilecloudIo.py
index bb91da7d5..6fb153658 100644
--- a/module/plugins/hoster/FilecloudIo.py
+++ b/module/plugins/hoster/FilecloudIo.py
@@ -2,9 +2,9 @@
import re
-from module.common.json_layer import json_loads
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.utils import json_loads
+from pyload.plugin.internal.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class FilecloudIo(SimpleHoster):
diff --git a/module/plugins/hoster/FilefactoryCom.py b/module/plugins/hoster/FilefactoryCom.py
index 4980ebe39..95d61cd6a 100644
--- a/module/plugins/hoster/FilefactoryCom.py
+++ b/module/plugins/hoster/FilefactoryCom.py
@@ -4,8 +4,8 @@ import re
from urlparse import urljoin
-from module.network.RequestFactory import getURL
-from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, parseFileInfo
def getInfo(urls):
diff --git a/module/plugins/hoster/FilejungleCom.py b/module/plugins/hoster/FilejungleCom.py
index 8a8aee9e2..cd890778b 100644
--- a/module/plugins/hoster/FilejungleCom.py
+++ b/module/plugins/hoster/FilejungleCom.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.hoster.FileserveCom import FileserveCom, checkFile
-from module.plugins.Plugin import chunks
+from pyload.plugin.hoster.FileserveCom import FileserveCom, checkFile
+from pyload.plugin.Plugin import chunks
class FilejungleCom(FileserveCom):
diff --git a/module/plugins/hoster/FileomCom.py b/module/plugins/hoster/FileomCom.py
index 306953b84..23f059546 100644
--- a/module/plugins/hoster/FileomCom.py
+++ b/module/plugins/hoster/FileomCom.py
@@ -3,7 +3,7 @@
# Test links:
# http://fileom.com/gycaytyzdw3g/random.bin.html
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class FileomCom(XFSHoster):
diff --git a/module/plugins/hoster/FilepostCom.py b/module/plugins/hoster/FilepostCom.py
index 21ebbc55e..89f7fe9a4 100644
--- a/module/plugins/hoster/FilepostCom.py
+++ b/module/plugins/hoster/FilepostCom.py
@@ -4,9 +4,9 @@ import re
from time import time
-from module.common.json_layer import json_loads
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.utils import json_loads
+from pyload.plugin.internal.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class FilepostCom(SimpleHoster):
diff --git a/module/plugins/hoster/FilepupNet.py b/module/plugins/hoster/FilepupNet.py
index a53773f81..6ac9994cc 100644
--- a/module/plugins/hoster/FilepupNet.py
+++ b/module/plugins/hoster/FilepupNet.py
@@ -6,7 +6,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class FilepupNet(SimpleHoster):
diff --git a/module/plugins/hoster/FilerNet.py b/module/plugins/hoster/FilerNet.py
index c5007e945..85ff7da33 100644
--- a/module/plugins/hoster/FilerNet.py
+++ b/module/plugins/hoster/FilerNet.py
@@ -9,8 +9,8 @@ import re
from urlparse import urljoin
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class FilerNet(SimpleHoster):
diff --git a/module/plugins/hoster/FilerioCom.py b/module/plugins/hoster/FilerioCom.py
index c6ebbd444..12b7e7705 100644
--- a/module/plugins/hoster/FilerioCom.py
+++ b/module/plugins/hoster/FilerioCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class FilerioCom(XFSHoster):
diff --git a/module/plugins/hoster/FilesMailRu.py b/module/plugins/hoster/FilesMailRu.py
index 7bd099282..09da46b01 100644
--- a/module/plugins/hoster/FilesMailRu.py
+++ b/module/plugins/hoster/FilesMailRu.py
@@ -2,9 +2,9 @@
import re
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
-from module.plugins.Plugin import chunks
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.Hoster import Hoster
+from pyload.plugin.Plugin import chunks
def getInfo(urls):
diff --git a/module/plugins/hoster/FileserveCom.py b/module/plugins/hoster/FileserveCom.py
index 6f316cea3..cc977ac25 100644
--- a/module/plugins/hoster/FileserveCom.py
+++ b/module/plugins/hoster/FileserveCom.py
@@ -41,7 +41,7 @@ class FileserveCom(Hoster):
__license__ = "GPLv3"
__authors__ = [("jeix", "jeix@hasnomail.de"),
("mkaay", "mkaay@mkaay.de"),
- ("Paul King", None),
+ ("Paul King", ""),
("zoidberg", "zoidberg@mujmail.cz")]
diff --git a/module/plugins/hoster/FileshareInUa.py b/module/plugins/hoster/FileshareInUa.py
index 08e10dccb..5f5089e54 100644
--- a/module/plugins/hoster/FileshareInUa.py
+++ b/module/plugins/hoster/FileshareInUa.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class FileshareInUa(DeadHoster):
diff --git a/module/plugins/hoster/FilesonicCom.py b/module/plugins/hoster/FilesonicCom.py
index 8bfa0fa2e..c2b87d4d7 100644
--- a/module/plugins/hoster/FilesonicCom.py
+++ b/module/plugins/hoster/FilesonicCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class FilesonicCom(DeadHoster):
@@ -13,7 +13,7 @@ class FilesonicCom(DeadHoster):
__description__ = """Filesonic.com hoster plugin"""
__license__ = "GPLv3"
__authors__ = [("jeix", "jeix@hasnomail.de"),
- ("paulking", None)]
+ ("paulking", "")]
getInfo = create_getInfo(FilesonicCom)
diff --git a/module/plugins/hoster/FilezyNet.py b/module/plugins/hoster/FilezyNet.py
index 4197a2858..1fcc9363d 100644
--- a/module/plugins/hoster/FilezyNet.py
+++ b/module/plugins/hoster/FilezyNet.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class FilezyNet(DeadHoster):
diff --git a/module/plugins/hoster/FiredriveCom.py b/module/plugins/hoster/FiredriveCom.py
index 0e3a4e847..3395fc261 100644
--- a/module/plugins/hoster/FiredriveCom.py
+++ b/module/plugins/hoster/FiredriveCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class FiredriveCom(DeadHoster):
diff --git a/module/plugins/hoster/FlyFilesNet.py b/module/plugins/hoster/FlyFilesNet.py
index 49705958d..5aff3e811 100644
--- a/module/plugins/hoster/FlyFilesNet.py
+++ b/module/plugins/hoster/FlyFilesNet.py
@@ -4,8 +4,8 @@ import re
from urllib import unquote
-from module.network.RequestFactory import getURL
-from module.plugins.internal.SimpleHoster import SimpleHoster
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.internal.SimpleHoster import SimpleHoster
class FlyFilesNet(SimpleHoster):
diff --git a/module/plugins/hoster/FourSharedCom.py b/module/plugins/hoster/FourSharedCom.py
index a3f53ac5e..8d13ea0b7 100644
--- a/module/plugins/hoster/FourSharedCom.py
+++ b/module/plugins/hoster/FourSharedCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class FourSharedCom(SimpleHoster):
diff --git a/module/plugins/hoster/FreevideoCz.py b/module/plugins/hoster/FreevideoCz.py
index e56d1a299..86c30e64c 100644
--- a/module/plugins/hoster/FreevideoCz.py
+++ b/module/plugins/hoster/FreevideoCz.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class FreevideoCz(DeadHoster):
diff --git a/module/plugins/hoster/FshareVn.py b/module/plugins/hoster/FshareVn.py
index 9a2b0c323..186cb2f87 100644
--- a/module/plugins/hoster/FshareVn.py
+++ b/module/plugins/hoster/FshareVn.py
@@ -5,8 +5,8 @@ import re
from time import strptime, mktime, gmtime
from urlparse import urljoin
-from module.network.RequestFactory import getURL
-from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, parseFileInfo
def getInfo(urls):
diff --git a/module/plugins/hoster/Ftp.py b/module/plugins/hoster/Ftp.py
index 22fc5f67a..6924c5910 100644
--- a/module/plugins/hoster/Ftp.py
+++ b/module/plugins/hoster/Ftp.py
@@ -6,7 +6,7 @@ import re
from urllib import quote, unquote
from urlparse import urlparse
-from module.plugins.Hoster import Hoster
+from pyload.plugin.Hoster import Hoster
class Ftp(Hoster):
diff --git a/module/plugins/hoster/GamefrontCom.py b/module/plugins/hoster/GamefrontCom.py
index c68866f87..89e188010 100644
--- a/module/plugins/hoster/GamefrontCom.py
+++ b/module/plugins/hoster/GamefrontCom.py
@@ -2,9 +2,9 @@
import re
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
-from module.utils import parseFileSize
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.Hoster import Hoster
+from pyload.utils import parseFileSize
class GamefrontCom(Hoster):
diff --git a/module/plugins/hoster/GigapetaCom.py b/module/plugins/hoster/GigapetaCom.py
index 9d3ace3b2..d12084d42 100644
--- a/module/plugins/hoster/GigapetaCom.py
+++ b/module/plugins/hoster/GigapetaCom.py
@@ -2,10 +2,9 @@
import re
-from pycurl import FOLLOWLOCATION
from random import randint
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class GigapetaCom(SimpleHoster):
@@ -31,27 +30,25 @@ class GigapetaCom(SimpleHoster):
captcha_key = str(randint(1, 100000000))
captcha_url = "http://gigapeta.com/img/captcha.gif?x=%s" % captcha_key
- self.req.http.c.setopt(FOLLOWLOCATION, 0)
-
for _i in xrange(5):
self.checkErrors()
captcha = self.decryptCaptcha(captcha_url)
- self.html = self.load(pyfile.url, post={
- "captcha_key": captcha_key,
- "captcha": captcha,
- "download": "Download"})
+ self.html = self.load(pyfile.url,
+ post={'captcha_key': captcha_key,
+ 'captcha' : captcha,
+ 'download' : "Download"},
+ follow_location=False)
m = re.search(r'Location\s*:\s*(.+)', self.req.http.header, re.I)
if m:
- download_url = m.group(1).rstrip() #@TODO: Remove .rstrip() in 0.4.10
+ download_url = m.group(1)
break
elif "Entered figures don&#96;t coincide with the picture" in self.html:
self.invalidCaptcha()
else:
self.fail(_("No valid captcha code entered"))
- self.req.http.c.setopt(FOLLOWLOCATION, 1)
self.download(download_url)
diff --git a/module/plugins/hoster/GooIm.py b/module/plugins/hoster/GooIm.py
index 2cb012e50..331d63a99 100644
--- a/module/plugins/hoster/GooIm.py
+++ b/module/plugins/hoster/GooIm.py
@@ -5,7 +5,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class GooIm(SimpleHoster):
diff --git a/module/plugins/hoster/HellshareCz.py b/module/plugins/hoster/HellshareCz.py
index 05caf052d..8a00e01b0 100644
--- a/module/plugins/hoster/HellshareCz.py
+++ b/module/plugins/hoster/HellshareCz.py
@@ -2,7 +2,7 @@
from urlparse import urljoin
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class HellshareCz(SimpleHoster):
diff --git a/module/plugins/hoster/HellspyCz.py b/module/plugins/hoster/HellspyCz.py
index 2b9b76b8a..0d33b35b9 100644
--- a/module/plugins/hoster/HellspyCz.py
+++ b/module/plugins/hoster/HellspyCz.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class HellspyCz(DeadHoster):
diff --git a/module/plugins/hoster/HotfileCom.py b/module/plugins/hoster/HotfileCom.py
index f7724faf2..b64bb5fd7 100644
--- a/module/plugins/hoster/HotfileCom.py
+++ b/module/plugins/hoster/HotfileCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class HotfileCom(DeadHoster):
diff --git a/module/plugins/hoster/HugefilesNet.py b/module/plugins/hoster/HugefilesNet.py
index b7e599a50..f5085c8c3 100644
--- a/module/plugins/hoster/HugefilesNet.py
+++ b/module/plugins/hoster/HugefilesNet.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class HugefilesNet(XFSHoster):
diff --git a/module/plugins/hoster/HundredEightyUploadCom.py b/module/plugins/hoster/HundredEightyUploadCom.py
index 32d36ddb9..2edb6310d 100644
--- a/module/plugins/hoster/HundredEightyUploadCom.py
+++ b/module/plugins/hoster/HundredEightyUploadCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class HundredEightyUploadCom(XFSHoster):
diff --git a/module/plugins/hoster/IFileWs.py b/module/plugins/hoster/IFileWs.py
index b4f225c4b..59d9c7f40 100644
--- a/module/plugins/hoster/IFileWs.py
+++ b/module/plugins/hoster/IFileWs.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class IFileWs(DeadHoster):
diff --git a/module/plugins/hoster/IcyFilesCom.py b/module/plugins/hoster/IcyFilesCom.py
index 921b64207..201626fa5 100644
--- a/module/plugins/hoster/IcyFilesCom.py
+++ b/module/plugins/hoster/IcyFilesCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class IcyFilesCom(DeadHoster):
diff --git a/module/plugins/hoster/IfolderRu.py b/module/plugins/hoster/IfolderRu.py
index 249c2feb1..33da64ba2 100644
--- a/module/plugins/hoster/IfolderRu.py
+++ b/module/plugins/hoster/IfolderRu.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class IfolderRu(SimpleHoster):
diff --git a/module/plugins/hoster/JumbofilesCom.py b/module/plugins/hoster/JumbofilesCom.py
index 7adc1a029..2331179bc 100644
--- a/module/plugins/hoster/JumbofilesCom.py
+++ b/module/plugins/hoster/JumbofilesCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class JumbofilesCom(SimpleHoster):
diff --git a/module/plugins/hoster/JunocloudMe.py b/module/plugins/hoster/JunocloudMe.py
index 415d5e2d0..ad37b01e4 100644
--- a/module/plugins/hoster/JunocloudMe.py
+++ b/module/plugins/hoster/JunocloudMe.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class JunocloudMe(XFSHoster):
diff --git a/module/plugins/hoster/Keep2ShareCc.py b/module/plugins/hoster/Keep2ShareCc.py
index 6b7b422c5..3ed11aa13 100644
--- a/module/plugins/hoster/Keep2ShareCc.py
+++ b/module/plugins/hoster/Keep2ShareCc.py
@@ -85,8 +85,6 @@ class Keep2ShareCc(SimpleHoster):
self.error(_("Free download link not found"))
self.link = m.group(1)
-
-
def handleCaptcha(self):
recaptcha = ReCaptcha(self)
post_data = {'free' : 1,
diff --git a/module/plugins/hoster/KickloadCom.py b/module/plugins/hoster/KickloadCom.py
index 1c39db46c..1f0c41f3d 100644
--- a/module/plugins/hoster/KickloadCom.py
+++ b/module/plugins/hoster/KickloadCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class KickloadCom(DeadHoster):
diff --git a/module/plugins/hoster/KingfilesNet.py b/module/plugins/hoster/KingfilesNet.py
index 2d1c3b096..73fa494b0 100644
--- a/module/plugins/hoster/KingfilesNet.py
+++ b/module/plugins/hoster/KingfilesNet.py
@@ -2,8 +2,8 @@
import re
-from module.plugins.internal.CaptchaService import SolveMedia
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.captcha import SolveMedia
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class KingfilesNet(SimpleHoster):
diff --git a/module/plugins/hoster/LemUploadsCom.py b/module/plugins/hoster/LemUploadsCom.py
index 22fbd60dd..822670348 100644
--- a/module/plugins/hoster/LemUploadsCom.py
+++ b/module/plugins/hoster/LemUploadsCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class LemUploadsCom(DeadHoster):
diff --git a/module/plugins/hoster/LoadTo.py b/module/plugins/hoster/LoadTo.py
index be330e246..0f344a88b 100644
--- a/module/plugins/hoster/LoadTo.py
+++ b/module/plugins/hoster/LoadTo.py
@@ -6,8 +6,8 @@
import re
-from module.plugins.internal.CaptchaService import SolveMedia
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.captcha import SolveMedia
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class LoadTo(SimpleHoster):
@@ -17,7 +17,7 @@ class LoadTo(SimpleHoster):
__pattern__ = r'http://(?:www\.)?load\.to/\w+'
- __description__ = """ Load.to hoster plugin """
+ __description__ = """Load.to hoster plugin"""
__license__ = "GPLv3"
__authors__ = [("halfman", "Pulpan3@gmail.com"),
("stickell", "l.stickell@yahoo.it")]
diff --git a/module/plugins/hoster/LuckyShareNet.py b/module/plugins/hoster/LuckyShareNet.py
index 74bf877f1..33f8ab393 100644
--- a/module/plugins/hoster/LuckyShareNet.py
+++ b/module/plugins/hoster/LuckyShareNet.py
@@ -4,8 +4,8 @@ import re
from bottle import json_loads
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class LuckyShareNet(SimpleHoster):
diff --git a/module/plugins/hoster/MediafireCom.py b/module/plugins/hoster/MediafireCom.py
index bc81c8202..5adf004d0 100644
--- a/module/plugins/hoster/MediafireCom.py
+++ b/module/plugins/hoster/MediafireCom.py
@@ -2,9 +2,9 @@
import re
-from module.plugins.internal.CaptchaService import SolveMedia
-from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo
-from module.network.RequestFactory import getURL
+from pyload.plugin.internal.captcha import SolveMedia
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, parseFileInfo
+from pyload.network.RequestFactory import getURL
def replace_eval(js_expr):
diff --git a/module/plugins/hoster/MegaFilesSe.py b/module/plugins/hoster/MegaFilesSe.py
index c3de57914..4120e17d6 100644
--- a/module/plugins/hoster/MegaFilesSe.py
+++ b/module/plugins/hoster/MegaFilesSe.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class MegaFilesSe(DeadHoster):
diff --git a/module/plugins/hoster/MegacrypterCom.py b/module/plugins/hoster/MegacrypterCom.py
index 10a2eb025..f8fcb97fe 100644
--- a/module/plugins/hoster/MegacrypterCom.py
+++ b/module/plugins/hoster/MegacrypterCom.py
@@ -2,9 +2,9 @@
import re
-from module.common.json_layer import json_loads, json_dumps
+from pyload.utils import json_loads, json_dumps
-from module.plugins.hoster.MegaCoNz import MegaCoNz
+from pyload.plugin.hoster.MegaCoNz import MegaCoNz
class MegacrypterCom(MegaCoNz):
diff --git a/module/plugins/hoster/MegareleaseOrg.py b/module/plugins/hoster/MegareleaseOrg.py
index 60796c1ee..6eacbd6e0 100644
--- a/module/plugins/hoster/MegareleaseOrg.py
+++ b/module/plugins/hoster/MegareleaseOrg.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class MegareleaseOrg(DeadHoster):
diff --git a/module/plugins/hoster/MegasharesCom.py b/module/plugins/hoster/MegasharesCom.py
index bdb428143..84777adeb 100644
--- a/module/plugins/hoster/MegasharesCom.py
+++ b/module/plugins/hoster/MegasharesCom.py
@@ -4,7 +4,7 @@ import re
from time import time
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class MegasharesCom(SimpleHoster):
diff --git a/module/plugins/hoster/MegauploadCom.py b/module/plugins/hoster/MegauploadCom.py
index 7f51a8a46..7896dabfb 100644
--- a/module/plugins/hoster/MegauploadCom.py
+++ b/module/plugins/hoster/MegauploadCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class MegauploadCom(DeadHoster):
diff --git a/module/plugins/hoster/MegavideoCom.py b/module/plugins/hoster/MegavideoCom.py
index 24905ce62..02054fecb 100644
--- a/module/plugins/hoster/MegavideoCom.py
+++ b/module/plugins/hoster/MegavideoCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class MegavideoCom(DeadHoster):
diff --git a/module/plugins/hoster/MovReelCom.py b/module/plugins/hoster/MovReelCom.py
index 9b8679c10..3e18a8960 100644
--- a/module/plugins/hoster/MovReelCom.py
+++ b/module/plugins/hoster/MovReelCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class MovReelCom(XFSHoster):
diff --git a/module/plugins/hoster/MultishareCz.py b/module/plugins/hoster/MultishareCz.py
index 7a6e73252..ca626a833 100644
--- a/module/plugins/hoster/MultishareCz.py
+++ b/module/plugins/hoster/MultishareCz.py
@@ -4,7 +4,7 @@ import re
from random import random
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class MultishareCz(SimpleHoster):
diff --git a/module/plugins/hoster/MyfastfileCom.py b/module/plugins/hoster/MyfastfileCom.py
index dd6294ff1..3862bfe32 100644
--- a/module/plugins/hoster/MyfastfileCom.py
+++ b/module/plugins/hoster/MyfastfileCom.py
@@ -18,6 +18,7 @@ class MyfastfileCom(MultiHoster):
__authors__ = [("stickell", "l.stickell@yahoo.it")]
+
def setup(self):
self.chunkLimit = -1
diff --git a/module/plugins/hoster/MyvideoDe.py b/module/plugins/hoster/MyvideoDe.py
index 8af4a9a61..4b5fa99ca 100644
--- a/module/plugins/hoster/MyvideoDe.py
+++ b/module/plugins/hoster/MyvideoDe.py
@@ -2,8 +2,8 @@
import re
-from module.plugins.Hoster import Hoster
-from module.unescape import unescape
+from pyload.plugin.Hoster import Hoster
+from pyload.utils import html_unescape
class MyvideoDe(Hoster):
@@ -37,8 +37,8 @@ class MyvideoDe(Hoster):
def get_file_name(self):
- file_name_pattern = r'<h1 class=\'globalHd\'>(.*)</h1>'
- return unescape(re.search(file_name_pattern, self.html).group(1).replace("/", "") + '.flv')
+ file_name_pattern = r"<h1 class='globalHd'>(.*)</h1>"
+ return html_unescape(re.search(file_name_pattern, self.html).group(1).replace("/", "") + '.flv')
def file_exists(self):
diff --git a/module/plugins/hoster/NahrajCz.py b/module/plugins/hoster/NahrajCz.py
index 6b5699408..5c5c71ae6 100644
--- a/module/plugins/hoster/NahrajCz.py
+++ b/module/plugins/hoster/NahrajCz.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class NahrajCz(DeadHoster):
diff --git a/module/plugins/hoster/NarodRu.py b/module/plugins/hoster/NarodRu.py
index 8b56c0908..1738d9724 100644
--- a/module/plugins/hoster/NarodRu.py
+++ b/module/plugins/hoster/NarodRu.py
@@ -4,7 +4,7 @@ import re
from random import random
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class NarodRu(SimpleHoster):
diff --git a/module/plugins/hoster/NetloadIn.py b/module/plugins/hoster/NetloadIn.py
index 3efbdce23..2ed298ffa 100644
--- a/module/plugins/hoster/NetloadIn.py
+++ b/module/plugins/hoster/NetloadIn.py
@@ -5,14 +5,14 @@ import re
from urlparse import urljoin
from time import time
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
-from module.plugins.Plugin import chunks
-from module.plugins.internal.CaptchaService import ReCaptcha
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.Hoster import Hoster
+from pyload.plugin.Plugin import chunks
+from pyload.plugin.captcha import ReCaptcha
def getInfo(urls):
- ## returns list of tupels (name, size (in bytes), status (see FileDatabase), url)
+ ## returns list of tupels (name, size (in bytes), status (see database.File), url)
apiurl = "http://api.netload.in/info.php"
id_regex = re.compile(NetloadIn.__pattern__)
diff --git a/module/plugins/hoster/NosuploadCom.py b/module/plugins/hoster/NosuploadCom.py
index aeedd54f6..842481a60 100644
--- a/module/plugins/hoster/NosuploadCom.py
+++ b/module/plugins/hoster/NosuploadCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class NosuploadCom(XFSHoster):
diff --git a/module/plugins/hoster/NovafileCom.py b/module/plugins/hoster/NovafileCom.py
index bdd66473b..f73570ec2 100644
--- a/module/plugins/hoster/NovafileCom.py
+++ b/module/plugins/hoster/NovafileCom.py
@@ -4,7 +4,7 @@
# http://novafile.com/vfun4z6o2cit
# http://novafile.com/s6zrr5wemuz4
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class NovafileCom(XFSHoster):
diff --git a/module/plugins/hoster/NowDownloadSx.py b/module/plugins/hoster/NowDownloadSx.py
index b69242a86..44a24471b 100644
--- a/module/plugins/hoster/NowDownloadSx.py
+++ b/module/plugins/hoster/NowDownloadSx.py
@@ -2,8 +2,8 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-from module.utils import fixup
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.utils import fixup
class NowDownloadSx(SimpleHoster):
diff --git a/module/plugins/hoster/NowVideoSx.py b/module/plugins/hoster/NowVideoSx.py
index 3f75a33f7..fc1d06d19 100644
--- a/module/plugins/hoster/NowVideoSx.py
+++ b/module/plugins/hoster/NowVideoSx.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class NowVideoSx(SimpleHoster):
diff --git a/module/plugins/hoster/OboomCom.py b/module/plugins/hoster/OboomCom.py
index 725763345..43d41c538 100644
--- a/module/plugins/hoster/OboomCom.py
+++ b/module/plugins/hoster/OboomCom.py
@@ -5,9 +5,9 @@
import re
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
-from module.plugins.internal.CaptchaService import ReCaptcha
+from pyload.utils import json_loads
+from pyload.plugin.Hoster import Hoster
+from pyload.plugin.internal.captcha import ReCaptcha
class OboomCom(Hoster):
diff --git a/module/plugins/hoster/OneFichierCom.py b/module/plugins/hoster/OneFichierCom.py
index a6e3b034c..5be05ea17 100644
--- a/module/plugins/hoster/OneFichierCom.py
+++ b/module/plugins/hoster/OneFichierCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class OneFichierCom(SimpleHoster):
@@ -17,7 +17,7 @@ class OneFichierCom(SimpleHoster):
__authors__ = [("fragonib", "fragonib[AT]yahoo[DOT]es"),
("the-razer", "daniel_ AT gmx DOT net"),
("zoidberg", "zoidberg@mujmail.cz"),
- ("imclem", None),
+ ("imclem", ""),
("stickell", "l.stickell@yahoo.it"),
("Elrick69", "elrick69[AT]rocketmail[DOT]com"),
("Walter Purcaro", "vuolter@gmail.com")]
diff --git a/module/plugins/hoster/OronCom.py b/module/plugins/hoster/OronCom.py
index 7e8423ec9..cbbaedae0 100644
--- a/module/plugins/hoster/OronCom.py
+++ b/module/plugins/hoster/OronCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class OronCom(DeadHoster):
diff --git a/module/plugins/hoster/PandaplaNet.py b/module/plugins/hoster/PandaplaNet.py
index 78a1ed177..a078ca3c9 100644
--- a/module/plugins/hoster/PandaplaNet.py
+++ b/module/plugins/hoster/PandaplaNet.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class PandaplaNet(DeadHoster):
diff --git a/module/plugins/hoster/PornhostCom.py b/module/plugins/hoster/PornhostCom.py
index 0c3b84a9d..70ce4f32e 100644
--- a/module/plugins/hoster/PornhostCom.py
+++ b/module/plugins/hoster/PornhostCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.Hoster import Hoster
+from pyload.plugin.Hoster import Hoster
class PornhostCom(Hoster):
diff --git a/module/plugins/hoster/PornhubCom.py b/module/plugins/hoster/PornhubCom.py
index 1bb787f09..9f8a5eec2 100644
--- a/module/plugins/hoster/PornhubCom.py
+++ b/module/plugins/hoster/PornhubCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.Hoster import Hoster
+from pyload.plugin.Hoster import Hoster
class PornhubCom(Hoster):
diff --git a/module/plugins/hoster/PotloadCom.py b/module/plugins/hoster/PotloadCom.py
index d6261af3a..bcbcb57d0 100644
--- a/module/plugins/hoster/PotloadCom.py
+++ b/module/plugins/hoster/PotloadCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class PotloadCom(DeadHoster):
diff --git a/module/plugins/hoster/PromptfileCom.py b/module/plugins/hoster/PromptfileCom.py
index 412a7bf2d..2aaf14736 100644
--- a/module/plugins/hoster/PromptfileCom.py
+++ b/module/plugins/hoster/PromptfileCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class PromptfileCom(SimpleHoster):
diff --git a/module/plugins/hoster/PrzeklejPl.py b/module/plugins/hoster/PrzeklejPl.py
index 3a59a2c9e..ff19c770e 100644
--- a/module/plugins/hoster/PrzeklejPl.py
+++ b/module/plugins/hoster/PrzeklejPl.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class PrzeklejPl(DeadHoster):
diff --git a/module/plugins/hoster/QuickshareCz.py b/module/plugins/hoster/QuickshareCz.py
index 210bf4cb2..5a3fd871b 100644
--- a/module/plugins/hoster/QuickshareCz.py
+++ b/module/plugins/hoster/QuickshareCz.py
@@ -2,9 +2,7 @@
import re
-from pycurl import FOLLOWLOCATION
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class QuickshareCz(SimpleHoster):
@@ -59,15 +57,13 @@ class QuickshareCz(SimpleHoster):
data = dict((x, self.jsvars[x]) for x in self.jsvars if x in ("ID1", "ID2", "ID3", "ID4"))
self.logDebug("FREE URL1:" + download_url, data)
- self.req.http.c.setopt(FOLLOWLOCATION, 0)
- self.load(download_url, post=data)
+ self.load(download_url, post=data, follow_location=False)
self.header = self.req.http.header
- self.req.http.c.setopt(FOLLOWLOCATION, 1)
m = re.search(r'Location\s*:\s*(.+)', self.header, re.I)
if m is None:
self.fail(_("File not found"))
- download_url = m.group(1).rstrip() #@TODO: Remove .rstrip() in 0.4.10
+ download_url = m.group(1)
self.logDebug("FREE URL2:" + download_url)
# check errors
diff --git a/module/plugins/hoster/RapidfileshareNet.py b/module/plugins/hoster/RapidfileshareNet.py
index 0bbaed57f..6d54f72f2 100644
--- a/module/plugins/hoster/RapidfileshareNet.py
+++ b/module/plugins/hoster/RapidfileshareNet.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class RapidfileshareNet(XFSHoster):
diff --git a/module/plugins/hoster/RapidgatorNet.py b/module/plugins/hoster/RapidgatorNet.py
index 7ec843646..2dfd8f59c 100644
--- a/module/plugins/hoster/RapidgatorNet.py
+++ b/module/plugins/hoster/RapidgatorNet.py
@@ -20,7 +20,7 @@ class RapidgatorNet(SimpleHoster):
__description__ = """Rapidgator.net hoster plugin"""
__license__ = "GPLv3"
__authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("chrox", None),
+ ("chrox", ""),
("stickell", "l.stickell@yahoo.it"),
("Walter Purcaro", "vuolter@gmail.com")]
diff --git a/module/plugins/hoster/RapiduNet.py b/module/plugins/hoster/RapiduNet.py
index 350572eef..5c12bf924 100644
--- a/module/plugins/hoster/RapiduNet.py
+++ b/module/plugins/hoster/RapiduNet.py
@@ -5,9 +5,9 @@ import re
from pycurl import HTTPHEADER
from time import time, altzone
-from module.common.json_layer import json_loads
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.utils import json_loads
+from pyload.plugin.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class RapiduNet(SimpleHoster):
diff --git a/module/plugins/hoster/RarefileNet.py b/module/plugins/hoster/RarefileNet.py
index a45e4ed4d..8f0bf8834 100644
--- a/module/plugins/hoster/RarefileNet.py
+++ b/module/plugins/hoster/RarefileNet.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class RarefileNet(XFSHoster):
diff --git a/module/plugins/hoster/RedtubeCom.py b/module/plugins/hoster/RedtubeCom.py
index d68fbe262..1f18d09c7 100644
--- a/module/plugins/hoster/RedtubeCom.py
+++ b/module/plugins/hoster/RedtubeCom.py
@@ -2,8 +2,8 @@
import re
-from module.plugins.Hoster import Hoster
-from module.unescape import unescape
+from pyload.plugin.Hoster import Hoster
+from pyload.utils import html_unescape
class RedtubeCom(Hoster):
@@ -38,7 +38,7 @@ class RedtubeCom(Hoster):
if not self.html:
self.download_html()
- file_url = unescape(re.search(r'hashlink=(http.*?)"', self.html).group(1))
+ file_url = html_unescape(re.search(r'hashlink=(http.*?)"', self.html).group(1))
return file_url
diff --git a/module/plugins/hoster/RemixshareCom.py b/module/plugins/hoster/RemixshareCom.py
index ed64b6a89..b1587ad6d 100644
--- a/module/plugins/hoster/RemixshareCom.py
+++ b/module/plugins/hoster/RemixshareCom.py
@@ -6,11 +6,11 @@
# Note:
# The remixshare.com website is very very slow, so
# if your download not starts because of pycurl timeouts:
-# Adjust timeouts in /usr/share/pyload/module/network/HTTPRequest.py
+# Adjust timeouts in /usr/share/pyload/pyload/network/HTTPRequest.py
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class RemixshareCom(SimpleHoster):
diff --git a/module/plugins/hoster/RgHostNet.py b/module/plugins/hoster/RgHostNet.py
index aa4830563..7a16598b2 100644
--- a/module/plugins/hoster/RgHostNet.py
+++ b/module/plugins/hoster/RgHostNet.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class RgHostNet(SimpleHoster):
diff --git a/module/plugins/hoster/SafesharingEu.py b/module/plugins/hoster/SafesharingEu.py
index 08612e413..c1fc9c68d 100644
--- a/module/plugins/hoster/SafesharingEu.py
+++ b/module/plugins/hoster/SafesharingEu.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class SafesharingEu(XFSHoster):
diff --git a/module/plugins/hoster/SecureUploadEu.py b/module/plugins/hoster/SecureUploadEu.py
index 6bfbce328..0b2b603bf 100644
--- a/module/plugins/hoster/SecureUploadEu.py
+++ b/module/plugins/hoster/SecureUploadEu.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class SecureUploadEu(XFSHoster):
diff --git a/module/plugins/hoster/SendspaceCom.py b/module/plugins/hoster/SendspaceCom.py
index ffd2995b8..5310051d1 100644
--- a/module/plugins/hoster/SendspaceCom.py
+++ b/module/plugins/hoster/SendspaceCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class SendspaceCom(SimpleHoster):
diff --git a/module/plugins/hoster/Share4WebCom.py b/module/plugins/hoster/Share4WebCom.py
index 7a276c1fe..0c2ee358e 100644
--- a/module/plugins/hoster/Share4WebCom.py
+++ b/module/plugins/hoster/Share4WebCom.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.hoster.UnibytesCom import UnibytesCom
-from module.plugins.internal.SimpleHoster import create_getInfo
+from pyload.plugin.hoster.UnibytesCom import UnibytesCom
+from pyload.plugin.internal.SimpleHoster import create_getInfo
class Share4WebCom(UnibytesCom):
diff --git a/module/plugins/hoster/Share76Com.py b/module/plugins/hoster/Share76Com.py
index 1ac8a64e7..7f2cea757 100644
--- a/module/plugins/hoster/Share76Com.py
+++ b/module/plugins/hoster/Share76Com.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class Share76Com(DeadHoster):
diff --git a/module/plugins/hoster/ShareFilesCo.py b/module/plugins/hoster/ShareFilesCo.py
index 4996014d8..dd5c9b85d 100644
--- a/module/plugins/hoster/ShareFilesCo.py
+++ b/module/plugins/hoster/ShareFilesCo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class ShareFilesCo(DeadHoster):
diff --git a/module/plugins/hoster/SharebeesCom.py b/module/plugins/hoster/SharebeesCom.py
index c0fd22c91..3843a352b 100644
--- a/module/plugins/hoster/SharebeesCom.py
+++ b/module/plugins/hoster/SharebeesCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class SharebeesCom(DeadHoster):
diff --git a/module/plugins/hoster/ShareonlineBiz.py b/module/plugins/hoster/ShareonlineBiz.py
index e00bf5ada..b37b4b0bc 100644
--- a/module/plugins/hoster/ShareonlineBiz.py
+++ b/module/plugins/hoster/ShareonlineBiz.py
@@ -6,9 +6,9 @@ from time import time
from urllib import unquote
from urlparse import urlparse
-from module.network.RequestFactory import getURL
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class ShareonlineBiz(SimpleHoster):
diff --git a/module/plugins/hoster/ShareplaceCom.py b/module/plugins/hoster/ShareplaceCom.py
index 07724a9d1..61e0f8723 100644
--- a/module/plugins/hoster/ShareplaceCom.py
+++ b/module/plugins/hoster/ShareplaceCom.py
@@ -4,7 +4,7 @@ import re
from urllib import unquote
-from module.plugins.Hoster import Hoster
+from pyload.plugin.Hoster import Hoster
class ShareplaceCom(Hoster):
@@ -16,7 +16,7 @@ class ShareplaceCom(Hoster):
__description__ = """Shareplace.com hoster plugin"""
__license__ = "GPLv3"
- __authors__ = [("ACCakut", None)]
+ __authors__ = [("ACCakut", "")]
def process(self, pyfile):
diff --git a/module/plugins/hoster/SharingmatrixCom.py b/module/plugins/hoster/SharingmatrixCom.py
index fa08a4a8f..a20a36543 100644
--- a/module/plugins/hoster/SharingmatrixCom.py
+++ b/module/plugins/hoster/SharingmatrixCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class SharingmatrixCom(DeadHoster):
@@ -13,7 +13,7 @@ class SharingmatrixCom(DeadHoster):
__description__ = """Sharingmatrix.com hoster plugin"""
__license__ = "GPLv3"
__authors__ = [("jeix", "jeix@hasnomail.de"),
- ("paulking", None)]
+ ("paulking", "")]
getInfo = create_getInfo(SharingmatrixCom)
diff --git a/module/plugins/hoster/ShragleCom.py b/module/plugins/hoster/ShragleCom.py
index bec30f6f2..cf975b2f5 100644
--- a/module/plugins/hoster/ShragleCom.py
+++ b/module/plugins/hoster/ShragleCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class ShragleCom(DeadHoster):
diff --git a/module/plugins/hoster/SockshareCom.py b/module/plugins/hoster/SockshareCom.py
index aabb8dcd1..e85a74e49 100644
--- a/module/plugins/hoster/SockshareCom.py
+++ b/module/plugins/hoster/SockshareCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class SockshareCom(DeadHoster):
diff --git a/module/plugins/hoster/SoundcloudCom.py b/module/plugins/hoster/SoundcloudCom.py
index c7c9c3880..fd5d1ea6c 100644
--- a/module/plugins/hoster/SoundcloudCom.py
+++ b/module/plugins/hoster/SoundcloudCom.py
@@ -3,7 +3,7 @@
import pycurl
import re
-from module.plugins.Hoster import Hoster
+from pyload.plugin.Hoster import Hoster
class SoundcloudCom(Hoster):
diff --git a/module/plugins/hoster/SpeedLoadOrg.py b/module/plugins/hoster/SpeedLoadOrg.py
index a13220eab..588eb26a3 100644
--- a/module/plugins/hoster/SpeedLoadOrg.py
+++ b/module/plugins/hoster/SpeedLoadOrg.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class SpeedLoadOrg(DeadHoster):
diff --git a/module/plugins/hoster/SpeedfileCz.py b/module/plugins/hoster/SpeedfileCz.py
index f23c8d4c7..3f17bbbe6 100644
--- a/module/plugins/hoster/SpeedfileCz.py
+++ b/module/plugins/hoster/SpeedfileCz.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class SpeedfileCz(DeadHoster):
diff --git a/module/plugins/hoster/SpeedyshareCom.py b/module/plugins/hoster/SpeedyshareCom.py
index f21b0a3a7..078cbf6c8 100644
--- a/module/plugins/hoster/SpeedyshareCom.py
+++ b/module/plugins/hoster/SpeedyshareCom.py
@@ -7,7 +7,7 @@ import re
from urlparse import urljoin
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class SpeedyshareCom(SimpleHoster):
diff --git a/module/plugins/hoster/StorageTo.py b/module/plugins/hoster/StorageTo.py
index bedc2934f..4b18c69da 100644
--- a/module/plugins/hoster/StorageTo.py
+++ b/module/plugins/hoster/StorageTo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class StorageTo(DeadHoster):
diff --git a/module/plugins/hoster/StreamCz.py b/module/plugins/hoster/StreamCz.py
index 11d4efcdb..95e69abf4 100644
--- a/module/plugins/hoster/StreamCz.py
+++ b/module/plugins/hoster/StreamCz.py
@@ -2,8 +2,8 @@
import re
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.Hoster import Hoster
def getInfo(urls):
diff --git a/module/plugins/hoster/StreamcloudEu.py b/module/plugins/hoster/StreamcloudEu.py
index 54f430508..c47c2c6a1 100644
--- a/module/plugins/hoster/StreamcloudEu.py
+++ b/module/plugins/hoster/StreamcloudEu.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class StreamcloudEu(XFSHoster):
diff --git a/module/plugins/hoster/TurbobitNet.py b/module/plugins/hoster/TurbobitNet.py
index fbd054713..811136405 100644
--- a/module/plugins/hoster/TurbobitNet.py
+++ b/module/plugins/hoster/TurbobitNet.py
@@ -9,9 +9,9 @@ from binascii import hexlify, unhexlify
from pycurl import HTTPHEADER
from urllib import quote
-from module.network.RequestFactory import getURL
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, timestamp
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.internal.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo, timestamp
class TurbobitNet(SimpleHoster):
@@ -21,10 +21,10 @@ class TurbobitNet(SimpleHoster):
__pattern__ = r'http://(?:www\.)?turbobit\.net/(?:download/free/)?(?P<ID>\w+)'
- __description__ = """ Turbobit.net hoster plugin """
+ __description__ = """Turbobit.net hoster plugin"""
__license__ = "GPLv3"
__authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("prOq", None)]
+ ("prOq", "")]
URL_REPLACEMENTS = [(__pattern__ + ".*", "http://turbobit.net/\g<ID>.html")]
diff --git a/module/plugins/hoster/TurbouploadCom.py b/module/plugins/hoster/TurbouploadCom.py
index f0893cef6..f821438dd 100644
--- a/module/plugins/hoster/TurbouploadCom.py
+++ b/module/plugins/hoster/TurbouploadCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class TurbouploadCom(DeadHoster):
diff --git a/module/plugins/hoster/TwoSharedCom.py b/module/plugins/hoster/TwoSharedCom.py
index c6ca2ab29..53911762d 100644
--- a/module/plugins/hoster/TwoSharedCom.py
+++ b/module/plugins/hoster/TwoSharedCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class TwoSharedCom(SimpleHoster):
diff --git a/module/plugins/hoster/UlozTo.py b/module/plugins/hoster/UlozTo.py
index 3552942ff..104209a66 100644
--- a/module/plugins/hoster/UlozTo.py
+++ b/module/plugins/hoster/UlozTo.py
@@ -3,8 +3,8 @@
import re
import time
-from module.common.json_layer import json_loads
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.utils import json_loads
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
def convertDecimalPrefix(m):
diff --git a/module/plugins/hoster/UloziskoSk.py b/module/plugins/hoster/UloziskoSk.py
index 0267d4b61..862429af5 100644
--- a/module/plugins/hoster/UloziskoSk.py
+++ b/module/plugins/hoster/UloziskoSk.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class UloziskoSk(SimpleHoster):
diff --git a/module/plugins/hoster/UnibytesCom.py b/module/plugins/hoster/UnibytesCom.py
index 16b1f82ab..dadbe2fec 100644
--- a/module/plugins/hoster/UnibytesCom.py
+++ b/module/plugins/hoster/UnibytesCom.py
@@ -4,9 +4,7 @@ import re
from urlparse import urljoin
-from pycurl import FOLLOWLOCATION
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class UnibytesCom(SimpleHoster):
@@ -33,11 +31,10 @@ class UnibytesCom(SimpleHoster):
domain = "http://www.%s/" % self.HOSTER_DOMAIN
action, post_data = self.parseHtmlForm('id="startForm"')
- self.req.http.c.setopt(FOLLOWLOCATION, 0)
for _i in xrange(8):
self.logDebug(action, post_data)
- self.html = self.load(urljoin(domain, action), post=post_data)
+ self.html = self.load(urljoin(domain, action), post=post_data, follow_location=False)
m = re.search(r'location:\s*(\S+)', self.req.http.header, re.I)
if m:
@@ -70,8 +67,6 @@ class UnibytesCom(SimpleHoster):
else:
self.fail(_("No valid captcha code entered"))
- self.req.http.c.setopt(FOLLOWLOCATION, 1)
-
self.download(url)
diff --git a/module/plugins/hoster/UpleaCom.py b/module/plugins/hoster/UpleaCom.py
index 59c593c93..d14f2fdc5 100644
--- a/module/plugins/hoster/UpleaCom.py
+++ b/module/plugins/hoster/UpleaCom.py
@@ -4,7 +4,7 @@ import re
from urlparse import urljoin
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class UpleaCom(XFSHoster):
@@ -16,7 +16,7 @@ class UpleaCom(XFSHoster):
__description__ = """Uplea.com hoster plugin"""
__license__ = "GPLv3"
- __authors__ = [("Redleon", None)]
+ __authors__ = [("Redleon", "")]
NAME_PATTERN = r'class="agmd size18">(?P<N>.+?)<'
diff --git a/module/plugins/hoster/UploadStationCom.py b/module/plugins/hoster/UploadStationCom.py
index d987770fe..daa7a9e81 100644
--- a/module/plugins/hoster/UploadStationCom.py
+++ b/module/plugins/hoster/UploadStationCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class UploadStationCom(DeadHoster):
diff --git a/module/plugins/hoster/UploadableCh.py b/module/plugins/hoster/UploadableCh.py
index 11cf3d321..bddd26f57 100644
--- a/module/plugins/hoster/UploadableCh.py
+++ b/module/plugins/hoster/UploadableCh.py
@@ -4,8 +4,8 @@ import re
from time import sleep
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class UploadableCh(SimpleHoster):
diff --git a/module/plugins/hoster/UploadboxCom.py b/module/plugins/hoster/UploadboxCom.py
index 031c5f761..6285b56c5 100644
--- a/module/plugins/hoster/UploadboxCom.py
+++ b/module/plugins/hoster/UploadboxCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class UploadboxCom(DeadHoster):
diff --git a/module/plugins/hoster/UploadhereCom.py b/module/plugins/hoster/UploadhereCom.py
index 8da30be46..64b235eda 100644
--- a/module/plugins/hoster/UploadhereCom.py
+++ b/module/plugins/hoster/UploadhereCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class UploadhereCom(DeadHoster):
diff --git a/module/plugins/hoster/UploadheroCom.py b/module/plugins/hoster/UploadheroCom.py
index ac0b4b562..a0a9cffcb 100644
--- a/module/plugins/hoster/UploadheroCom.py
+++ b/module/plugins/hoster/UploadheroCom.py
@@ -5,7 +5,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class UploadheroCom(SimpleHoster):
diff --git a/module/plugins/hoster/UploadingCom.py b/module/plugins/hoster/UploadingCom.py
index 164b7b243..a315d3466 100644
--- a/module/plugins/hoster/UploadingCom.py
+++ b/module/plugins/hoster/UploadingCom.py
@@ -4,8 +4,8 @@ import re
from pycurl import HTTPHEADER
-from module.common.json_layer import json_loads
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, timestamp
+from pyload.utils import json_loads
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo, timestamp
class UploadingCom(SimpleHoster):
diff --git a/module/plugins/hoster/UploadkingCom.py b/module/plugins/hoster/UploadkingCom.py
index 743e700eb..a490d0d48 100644
--- a/module/plugins/hoster/UploadkingCom.py
+++ b/module/plugins/hoster/UploadkingCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class UploadkingCom(DeadHoster):
diff --git a/module/plugins/hoster/UpstoreNet.py b/module/plugins/hoster/UpstoreNet.py
index 13119eefd..b1ceb7df1 100644
--- a/module/plugins/hoster/UpstoreNet.py
+++ b/module/plugins/hoster/UpstoreNet.py
@@ -2,8 +2,8 @@
import re
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.captcha import ReCaptcha
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class UpstoreNet(SimpleHoster):
diff --git a/module/plugins/hoster/UptoboxCom.py b/module/plugins/hoster/UptoboxCom.py
index 3f79e509e..3d3505f90 100644
--- a/module/plugins/hoster/UptoboxCom.py
+++ b/module/plugins/hoster/UptoboxCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class UptoboxCom(XFSHoster):
diff --git a/module/plugins/hoster/VeehdCom.py b/module/plugins/hoster/VeehdCom.py
index d894dab24..91d7cc443 100644
--- a/module/plugins/hoster/VeehdCom.py
+++ b/module/plugins/hoster/VeehdCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.Hoster import Hoster
+from pyload.plugin.Hoster import Hoster
class VeehdCom(Hoster):
@@ -12,7 +12,7 @@ class VeehdCom(Hoster):
__pattern__ = r'http://veehd\.com/video/\d+_\S+'
__config__ = [("filename_spaces", "bool", "Allow spaces in filename", False),
- ("replacement_char", "str", "Filename replacement character", "_")]
+ ("replacement_char", "str", "Filename replacement character", "_")]
__description__ = """Veehd.com hoster plugin"""
__license__ = "GPLv3"
diff --git a/module/plugins/hoster/VeohCom.py b/module/plugins/hoster/VeohCom.py
index 219efa8e1..cb3e896d6 100644
--- a/module/plugins/hoster/VeohCom.py
+++ b/module/plugins/hoster/VeohCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class VeohCom(SimpleHoster):
diff --git a/module/plugins/hoster/VidPlayNet.py b/module/plugins/hoster/VidPlayNet.py
index f1a32a897..5f979e856 100644
--- a/module/plugins/hoster/VidPlayNet.py
+++ b/module/plugins/hoster/VidPlayNet.py
@@ -3,7 +3,7 @@
# Test links:
# BigBuckBunny_320x180.mp4 - 61.7 Mb - http://vidplay.net/38lkev0h3jv0
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class VidPlayNet(XFSHoster):
diff --git a/module/plugins/hoster/VimeoCom.py b/module/plugins/hoster/VimeoCom.py
index d427c1511..7ca6c962b 100644
--- a/module/plugins/hoster/VimeoCom.py
+++ b/module/plugins/hoster/VimeoCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class VimeoCom(SimpleHoster):
@@ -12,7 +12,7 @@ class VimeoCom(SimpleHoster):
__pattern__ = r'https?://(?:www\.)?(player\.)?vimeo\.com/(video/)?(?P<ID>\d+)'
__config__ = [("quality", "Lowest;Mobile;SD;HD;Highest", "Quality", "Highest"),
- ("original", "bool", "Try to download the original file first", True)]
+ ("original", "bool", "Try to download the original file first", True)]
__description__ = """Vimeo.com hoster plugin"""
__license__ = "GPLv3"
diff --git a/module/plugins/hoster/Vipleech4UCom.py b/module/plugins/hoster/Vipleech4UCom.py
index 2ae41b942..dec67e7a4 100644
--- a/module/plugins/hoster/Vipleech4UCom.py
+++ b/module/plugins/hoster/Vipleech4UCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class Vipleech4UCom(DeadHoster):
diff --git a/module/plugins/hoster/WarserverCz.py b/module/plugins/hoster/WarserverCz.py
index c83d5c03e..1631a021d 100644
--- a/module/plugins/hoster/WarserverCz.py
+++ b/module/plugins/hoster/WarserverCz.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class WarserverCz(DeadHoster):
diff --git a/module/plugins/hoster/WrzucTo.py b/module/plugins/hoster/WrzucTo.py
index 457e72865..631f9d3c6 100644
--- a/module/plugins/hoster/WrzucTo.py
+++ b/module/plugins/hoster/WrzucTo.py
@@ -4,7 +4,7 @@ import re
from pycurl import HTTPHEADER
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class WrzucTo(SimpleHoster):
diff --git a/module/plugins/hoster/WuploadCom.py b/module/plugins/hoster/WuploadCom.py
index 75ce59353..a73ac92e0 100644
--- a/module/plugins/hoster/WuploadCom.py
+++ b/module/plugins/hoster/WuploadCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class WuploadCom(DeadHoster):
@@ -13,7 +13,7 @@ class WuploadCom(DeadHoster):
__description__ = """Wupload.com hoster plugin"""
__license__ = "GPLv3"
__authors__ = [("jeix", "jeix@hasnomail.de"),
- ("Paul King", None)]
+ ("Paul King", "")]
getInfo = create_getInfo(WuploadCom)
diff --git a/module/plugins/hoster/X7To.py b/module/plugins/hoster/X7To.py
index a4e4b04bd..6ccc6d637 100644
--- a/module/plugins/hoster/X7To.py
+++ b/module/plugins/hoster/X7To.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class X7To(DeadHoster):
diff --git a/module/plugins/hoster/XFileSharingPro.py b/module/plugins/hoster/XFileSharingPro.py
index c66f25ad4..23af6e7fc 100644
--- a/module/plugins/hoster/XFileSharingPro.py
+++ b/module/plugins/hoster/XFileSharingPro.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
+from pyload.plugin.internal.XFSHoster import XFSHoster, create_getInfo
class XFileSharingPro(XFSHoster):
diff --git a/module/plugins/hoster/XHamsterCom.py b/module/plugins/hoster/XHamsterCom.py
index c6e789fa8..92340152f 100644
--- a/module/plugins/hoster/XHamsterCom.py
+++ b/module/plugins/hoster/XHamsterCom.py
@@ -4,8 +4,8 @@ import re
from urllib import unquote
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
+from pyload.utils import json_loads
+from pyload.plugin.Hoster import Hoster
def clean_json(json_expr):
diff --git a/module/plugins/hoster/XVideosCom.py b/module/plugins/hoster/XVideosCom.py
index 9bb2da424..c5e2921cb 100644
--- a/module/plugins/hoster/XVideosCom.py
+++ b/module/plugins/hoster/XVideosCom.py
@@ -4,7 +4,7 @@ import re
from urllib import unquote
-from module.plugins.Hoster import Hoster
+from pyload.plugin.Hoster import Hoster
class XVideosCom(Hoster):
diff --git a/module/plugins/hoster/Xdcc.py b/module/plugins/hoster/Xdcc.py
index bfa62b8db..277b7ad0c 100644
--- a/module/plugins/hoster/Xdcc.py
+++ b/module/plugins/hoster/Xdcc.py
@@ -10,8 +10,8 @@ from os.path import exists, join
from select import select
from time import time
-from module.plugins.Hoster import Hoster
-from module.utils import save_join
+from pyload.plugin.Hoster import Hoster
+from pyload.utils import safe_join
class Xdcc(Hoster):
@@ -20,8 +20,8 @@ class Xdcc(Hoster):
__version__ = "0.32"
__config__ = [("nick", "str", "Nickname", "pyload"),
- ("ident", "str", "Ident", "pyloadident"),
- ("realname", "str", "Realname", "pyloadreal")]
+ ("ident", "str", "Ident", "pyloadident"),
+ ("realname", "str", "Realname", "pyloadreal")]
__description__ = """Download from IRC XDCC bot"""
__license__ = "GPLv3"
@@ -192,7 +192,7 @@ class Xdcc(Hoster):
self.pyfile.name = packname
download_folder = self.config['general']['download_folder']
- filename = save_join(download_folder, packname)
+ filename = safe_join(download_folder, packname)
self.logInfo(_("Downloading %s from %s:%d") % (packname, ip, port))
diff --git a/module/plugins/hoster/YibaishiwuCom.py b/module/plugins/hoster/YibaishiwuCom.py
index cf1550ebc..6da5cff33 100644
--- a/module/plugins/hoster/YibaishiwuCom.py
+++ b/module/plugins/hoster/YibaishiwuCom.py
@@ -2,8 +2,8 @@
import re
-from module.common.json_layer import json_loads
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.utils import json_loads
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class YibaishiwuCom(SimpleHoster):
diff --git a/module/plugins/hoster/YoupornCom.py b/module/plugins/hoster/YoupornCom.py
index 4bb2520e6..75606935d 100644
--- a/module/plugins/hoster/YoupornCom.py
+++ b/module/plugins/hoster/YoupornCom.py
@@ -2,7 +2,7 @@
import re
-from module.plugins.Hoster import Hoster
+from pyload.plugin.Hoster import Hoster
class YoupornCom(Hoster):
diff --git a/module/plugins/hoster/YourfilesTo.py b/module/plugins/hoster/YourfilesTo.py
index e0def108e..a600d822f 100644
--- a/module/plugins/hoster/YourfilesTo.py
+++ b/module/plugins/hoster/YourfilesTo.py
@@ -4,7 +4,7 @@ import re
from urllib import unquote
-from module.plugins.Hoster import Hoster
+from pyload.plugin.Hoster import Hoster
class YourfilesTo(Hoster):
diff --git a/module/plugins/hoster/YoutubeCom.py b/module/plugins/hoster/YoutubeCom.py
index d00bf2a0e..bf8785022 100644
--- a/module/plugins/hoster/YoutubeCom.py
+++ b/module/plugins/hoster/YoutubeCom.py
@@ -6,9 +6,9 @@ import subprocess
from urllib import unquote
-from module.plugins.Hoster import Hoster
-from module.plugins.internal.SimpleHoster import replace_patterns
-from module.utils import html_unescape
+from pyload.plugin.Hoster import Hoster
+from pyload.plugin.internal.SimpleHoster import replace_patterns
+from pyload.utils import html_unescape
def which(program):
diff --git a/module/plugins/hoster/ZDF.py b/module/plugins/hoster/ZDF.py
index 8d3de5b26..da6d6448e 100644
--- a/module/plugins/hoster/ZDF.py
+++ b/module/plugins/hoster/ZDF.py
@@ -4,7 +4,7 @@ import re
from xml.etree.ElementTree import fromstring
-from module.plugins.Hoster import Hoster
+from pyload.plugin.Hoster import Hoster
# Based on zdfm by Roland Beermann (http://github.com/enkore/zdfm/)
diff --git a/module/plugins/hoster/ZShareNet.py b/module/plugins/hoster/ZShareNet.py
index dc96facbe..a6a34143f 100644
--- a/module/plugins/hoster/ZShareNet.py
+++ b/module/plugins/hoster/ZShareNet.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
+from pyload.plugin.internal.DeadHoster import DeadHoster, create_getInfo
class ZShareNet(DeadHoster):
@@ -12,8 +12,8 @@ class ZShareNet(DeadHoster):
__description__ = """ZShare.net hoster plugin"""
__license__ = "GPLv3"
- __authors__ = [("espes", None),
- ("Cptn Sandwich", None)]
+ __authors__ = [("espes", ""),
+ ("Cptn Sandwich", "")]
getInfo = create_getInfo(ZShareNet)
diff --git a/module/plugins/hoster/ZippyshareCom.py b/module/plugins/hoster/ZippyshareCom.py
index a43f70cbd..f08fb7dae 100644
--- a/module/plugins/hoster/ZippyshareCom.py
+++ b/module/plugins/hoster/ZippyshareCom.py
@@ -3,7 +3,7 @@
import re
from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo
class ZippyshareCom(SimpleHoster):
diff --git a/module/plugins/hoster/__init__.py b/module/plugins/hoster/__init__.py
index e69de29bb..40a96afc6 100644
--- a/module/plugins/hoster/__init__.py
+++ b/module/plugins/hoster/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/plugins/hoster/BasePlugin.py b/module/plugins/internal/BasePlugin.py
index f9eb695ca..792497449 100644
--- a/module/plugins/hoster/BasePlugin.py
+++ b/module/plugins/internal/BasePlugin.py
@@ -17,7 +17,7 @@ class BasePlugin(Hoster):
__pattern__ = r'^unmatchable$'
- __description__ = """Base Plugin when any other didnt fit"""
+ __description__ = """Base plugin when any other didnt fit"""
__license__ = "GPLv3"
__authors__ = [("RaNaN", "RaNaN@pyload.org"),
("Walter Purcaro", "vuolter@gmail.com")]
diff --git a/module/plugins/internal/DeadCrypter.py b/module/plugins/internal/DeadCrypter.py
index 866d177cf..ce56947fc 100644
--- a/module/plugins/internal/DeadCrypter.py
+++ b/module/plugins/internal/DeadCrypter.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleCrypter import create_getInfo
-from module.plugins.Crypter import Crypter as _Crypter
+from pyload.plugin.Crypter import Crypter as _Crypter
+from pyload.plugin.internal.SimpleCrypter import create_getInfo
class DeadCrypter(_Crypter):
@@ -11,7 +11,7 @@ class DeadCrypter(_Crypter):
__pattern__ = r'^unmatchable$'
- __description__ = """ Crypter is no longer available """
+ __description__ = """Crypter is no longer available"""
__license__ = "GPLv3"
__authors__ = [("stickell", "l.stickell@yahoo.it")]
diff --git a/module/plugins/internal/DeadHoster.py b/module/plugins/internal/DeadHoster.py
index a6ad92607..132e4741a 100644
--- a/module/plugins/internal/DeadHoster.py
+++ b/module/plugins/internal/DeadHoster.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.SimpleHoster import create_getInfo
-from module.plugins.Hoster import Hoster as _Hoster
+from pyload.plugin.Hoster import Hoster as _Hoster
+from pyload.plugin.internal.SimpleHoster import create_getInfo
class DeadHoster(_Hoster):
@@ -11,7 +11,7 @@ class DeadHoster(_Hoster):
__pattern__ = r'^unmatchable$'
- __description__ = """ Hoster is no longer available """
+ __description__ = """Hoster is no longer available"""
__license__ = "GPLv3"
__authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
diff --git a/module/plugins/internal/SimpleCrypter.py b/module/plugins/internal/SimpleCrypter.py
index 856b548f0..dc34f864f 100644
--- a/module/plugins/internal/SimpleCrypter.py
+++ b/module/plugins/internal/SimpleCrypter.py
@@ -4,9 +4,9 @@ import re
from urlparse import urljoin, urlparse
-from module.plugins.Crypter import Crypter
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, replace_patterns, set_cookies
-from module.utils import fixup
+from pyload.plugin.Crypter import Crypter
+from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo, replace_patterns, set_cookies
+from pyload.utils import fixup
class SimpleCrypter(Crypter, SimpleHoster):
@@ -68,19 +68,6 @@ class SimpleCrypter(Crypter, SimpleHoster):
LOGIN_PREMIUM = False
- #@TODO: Remove in 0.4.10
- def init(self):
- account_name = (self.__name__ + ".py").replace("Folder.py", "").replace(".py", "")
- account = self.pyfile.m.core.accountManager.getAccountPlugin(account_name)
-
- if account and account.canUse():
- self.user, data = account.selectAccount()
- self.req = account.getAccountRequest(self.user)
- self.premium = account.isPremium(self.user)
-
- self.account = account
-
-
def prepare(self):
self.pyfile.error = "" #@TODO: Remove in 0.4.10
diff --git a/module/plugins/internal/UpdateManager.py b/module/plugins/internal/UpdateManager.py
new file mode 100644
index 000000000..7bbfc0c79
--- /dev/null
+++ b/module/plugins/internal/UpdateManager.py
@@ -0,0 +1,300 @@
+# -*- coding: utf-8 -*-
+
+import re
+import sys
+
+from operator import itemgetter
+from os import path, remove, stat
+
+from pyload.network.RequestFactory import getURL
+from pyload.plugin.Addon import Expose, Addon, threaded
+from pyload.utils import safe_join
+
+
+class UpdateManager(Addon):
+ __name__ = "UpdateManager"
+ __type__ = "addon"
+ __version__ = "0.40"
+
+ __config__ = [("activated" , "bool" , "Activated" , True ),
+ ("mode" , "pyLoad + plugins;plugins only", "Check updates for" , "pyLoad + plugins"),
+ ("interval" , "int" , "Check interval in hours" , 8 ),
+ ("reloadplugins", "bool" , "Monitor plugins for code changes (debug mode only)", True ),
+ ("nodebugupdate", "bool" , "Don't check for updates in debug mode" , True )]
+
+ __description__ = """Check for updates"""
+ __license__ = "GPLv3"
+ __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ # event_list = ["pluginConfigChanged"]
+
+ SERVER_URL = "http://updatemanager.pyload.org"
+ MIN_INTERVAL = 6 * 60 * 60 #: 6h minimum check interval (value is in seconds)
+
+
+ def pluginConfigChanged(self, plugin, name, value):
+ if name == "interval":
+ interval = value * 60 * 60
+ if self.MIN_INTERVAL <= interval != self.interval:
+ self.core.scheduler.removeJob(self.cb)
+ self.interval = interval
+ self.initPeriodical()
+ else:
+ self.logDebug("Invalid interval value, kept current")
+
+ elif name == "reloadplugins":
+ if self.cb2:
+ self.core.scheduler.removeJob(self.cb2)
+ if value is True and self.core.debug:
+ self.periodical2()
+
+
+ def activate(self):
+ self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval"))
+ x = lambda: self.pluginConfigChanged(self.__name__, "reloadplugins", self.getConfig("reloadplugins"))
+ self.core.scheduler.addJob(10, x, threaded=False)
+
+
+ def deactivate(self):
+ self.pluginConfigChanged(self.__name__, "reloadplugins", False)
+
+
+ def setup(self):
+ self.cb2 = None
+ self.interval = self.MIN_INTERVAL
+ self.updating = False
+ self.info = {'pyload': False, 'version': None, 'plugins': False}
+ self.mtimes = {} #: store modification time for each plugin
+
+
+ def periodical2(self):
+ if not self.updating:
+ self.autoreloadPlugins()
+
+ self.cb2 = self.core.scheduler.addJob(4, self.periodical2, threaded=False)
+
+
+ @Expose
+ def autoreloadPlugins(self):
+ """ reload and reindex all modified plugins """
+ modules = filter(
+ lambda m: m and (m.__name__.startswith("pyload.plugin.") or
+ m.__name__.startswith("userplugins.")) and
+ m.__name__.count(".") >= 2, sys.modules.itervalues()
+ )
+
+ reloads = []
+
+ for m in modules:
+ root, type, name = m.__name__.rsplit(".", 2)
+ id = (type, name)
+ if type in self.core.pluginManager.plugins:
+ f = m.__file__.replace(".pyc", ".py")
+ if not path.isfile(f):
+ continue
+
+ mtime = stat(f).st_mtime
+
+ if id not in self.mtimes:
+ self.mtimes[id] = mtime
+ elif self.mtimes[id] < mtime:
+ reloads.append(id)
+ self.mtimes[id] = mtime
+
+ return True if self.core.pluginManager.reloadPlugins(reloads) else False
+
+
+ def periodical(self):
+ if self.info['pyload'] or self.getConfig("nodebugupdate") and self.core.debug:
+ return
+
+ self.updateThread()
+
+
+ def server_request(self):
+ try:
+ return getURL(self.SERVER_URL, get={'v': self.core.api.getServerVersion()}).splitlines()
+ except Exception:
+ self.logWarning(_("Unable to contact server to get updates"))
+
+
+ @threaded
+ def updateThread(self):
+ self.updating = True
+
+ status = self.update(onlyplugin=self.getConfig("mode") == "plugins only")
+
+ if status == 2:
+ self.core.api.restart()
+ else:
+ self.updating = False
+
+
+ @Expose
+ def updatePlugins(self):
+ """ simple wrapper for calling plugin update quickly """
+ return self.update(onlyplugin=True)
+
+
+ @Expose
+ def update(self, onlyplugin=False):
+ """ check for updates """
+ data = self.server_request()
+
+ if not data:
+ exitcode = 0
+
+ elif data[0] == "None":
+ self.logInfo(_("No new pyLoad version available"))
+ updates = data[1:]
+ exitcode = self._updatePlugins(updates)
+
+ elif onlyplugin:
+ exitcode = 0
+
+ else:
+ newversion = data[0]
+ self.logInfo(_("*** New pyLoad Version %s available ***") % newversion)
+ self.logInfo(_("*** Get it here: https://github.com/pyload/pyload/releases ***"))
+ exitcode = 3
+ self.info['pyload'] = True
+ self.info['version'] = newversion
+
+ return exitcode #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required; 3 = No plugins updated, new pyLoad version available
+
+
+ def _updatePlugins(self, updates):
+ """ check for plugin updates """
+
+ if self.info['plugins']:
+ return False #: plugins were already updated
+
+ exitcode = 0
+ updated = []
+
+ vre = re.compile(r'__version__.*=.*("|\')([\d.]+)')
+ url = updates[0]
+ schema = updates[1].split('|')
+
+ if "BLACKLIST" in updates:
+ blacklist = updates[updates.index('BLACKLIST') + 1:]
+ updates = updates[2:updates.index('BLACKLIST')]
+ else:
+ blacklist = None
+ updates = updates[2:]
+
+ upgradable = sorted(map(lambda x: dict(zip(schema, x.split('|'))), updates),
+ key=itemgetter("type", "name"))
+
+ for plugin in upgradable:
+ filename = plugin['name']
+ type = plugin['type']
+ version = plugin['version']
+
+ if filename.endswith(".pyc"):
+ name = filename[:filename.find("_")]
+ else:
+ name = filename.replace(".py", "")
+
+ plugins = getattr(self.core.pluginManager, "%sPlugins" % type)
+
+ oldver = float(plugins[name]['version']) if name in plugins else None
+ newver = float(version)
+
+ if not oldver:
+ msg = "New plugin: [%(type)s] %(name)s (v%(newver).2f)"
+ elif newver > oldver:
+ msg = "New version of plugin: [%(type)s] %(name)s (v%(oldver).2f -> v%(newver).2f)"
+ else:
+ continue
+
+ self.logInfo(_(msg) % {'type' : type,
+ 'name' : name,
+ 'oldver': oldver,
+ 'newver': newver})
+ try:
+ content = getURL(url % plugin)
+ m = vre.search(content)
+
+ if m and m.group(2) == version:
+ f = open(safe_join("userplugins", prefix, filename), "wb")
+ f.write(content)
+ f.close()
+ updated.append((prefix, name))
+ else:
+ raise Exception, _("Version mismatch")
+
+ except Exception, e:
+ self.logError(_("Error updating plugin %s") % filename, e)
+
+ if blacklist:
+ blacklisted = map(lambda x: (x.split('|')[0], x.split('|')[1].rsplit('.', 1)[0]), blacklist)
+
+ # Always protect internal plugins from removing
+ for i, n, t in blacklisted.enumerate():
+ if t == "internal":
+ del blacklisted[i]
+
+ blacklisted = sorted(blacklisted)
+ removed = self.removePlugins(blacklisted)
+ for t, n in removed:
+ self.logInfo(_("Removed blacklisted plugin [%(type)s] %(name)s") % {
+ 'type': t,
+ 'name': n,
+ })
+
+ if updated:
+ reloaded = self.core.pluginManager.reloadPlugins(updated)
+ if reloaded:
+ self.logInfo(_("Plugins updated and reloaded"))
+ exitcode = 1
+ else:
+ self.logInfo(_("*** Plugins have been updated, but need a pyLoad restart to be reloaded ***"))
+ self.info['plugins'] = True
+ exitcode = 2
+ else:
+ self.logInfo(_("No plugin updates available"))
+
+ return exitcode #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required
+
+
+ @Expose
+ def removePlugins(self, type_plugins):
+ """ delete plugins from disk """
+
+ if not type_plugins:
+ return
+
+ self.logDebug("Requested deletion of plugins: %s" % type_plugins)
+
+ removed = []
+
+ for type, name in type_plugins:
+ err = False
+ file = name + ".py"
+
+ for root in ("userplugins", path.join(pypath, "pyload", "plugins")):
+
+ filename = safe_join(root, type, file)
+ try:
+ remove(filename)
+ except Exception, e:
+ self.logDebug("Error deleting: %s" % path.basename(filename), e)
+ err = True
+
+ filename += "c"
+ if path.isfile(filename):
+ try:
+ if type == "addon":
+ self.manager.deactivateAddon(name)
+ remove(filename)
+ except Exception, e:
+ self.logDebug("Error deleting: %s" % path.basename(filename), e)
+ err = True
+
+ if not err:
+ id = (type, name)
+ removed.append(id)
+
+ return removed #: return a list of the plugins successfully removed
diff --git a/module/plugins/internal/XFSAccount.py b/module/plugins/internal/XFSAccount.py
index 845ea3230..2e6b7dc50 100644
--- a/module/plugins/internal/XFSAccount.py
+++ b/module/plugins/internal/XFSAccount.py
@@ -5,8 +5,8 @@ import re
from time import gmtime, mktime, strptime
from urlparse import urljoin
-from module.plugins.Account import Account
-from module.plugins.internal.SimpleHoster import parseHtmlForm, set_cookies
+from pyload.plugin.Account import Account
+from pyload.plugin.internal.SimpleHoster import parseHtmlForm, set_cookies
class XFSAccount(Account):
@@ -39,11 +39,6 @@ class XFSAccount(Account):
LOGIN_FAIL_PATTERN = r'Incorrect Login or Password|account was banned|Error<'
- def __init__(self, manager, accounts): #@TODO: remove in 0.4.10
- self.init()
- return super(XFSAccount, self).__init__(manager, accounts)
-
-
def init(self):
if not self.HOSTER_DOMAIN:
self.logError(_("Missing HOSTER_DOMAIN"))
diff --git a/module/plugins/internal/XFSHoster.py b/module/plugins/internal/XFSHoster.py
index c4fc969ba..ecdd2f125 100644
--- a/module/plugins/internal/XFSHoster.py
+++ b/module/plugins/internal/XFSHoster.py
@@ -6,11 +6,9 @@ from random import random
from time import sleep
from urlparse import urljoin, urlparse
-from pycurl import FOLLOWLOCATION, LOW_SPEED_TIME
-
-from module.plugins.internal.CaptchaService import ReCaptcha, SolveMedia
+from pyload.plugin.internal.captcha import ReCaptcha, SolveMedia
from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, secondsToMidnight
-from module.utils import html_unescape
+from pyload.utils import html_unescape
class XFSHoster(SimpleHoster):
@@ -101,11 +99,7 @@ class XFSHoster(SimpleHoster):
data = self.getPostParameters()
- self.req.http.c.setopt(FOLLOWLOCATION, 0)
-
- self.html = self.load(pyfile.url, post=data, ref=True, decode=True)
-
- self.req.http.c.setopt(FOLLOWLOCATION, 1)
+ self.html = self.load(self.pyfile.url, post=data, ref=True, decode=True, follow_location=False)
m = re.search(r'Location\s*:\s*(.+)', self.req.http.header, re.I)
if m and not "op=" in m.group(1):
diff --git a/module/plugins/internal/__init__.py b/module/plugins/internal/__init__.py
index e69de29bb..40a96afc6 100644
--- a/module/plugins/internal/__init__.py
+++ b/module/plugins/internal/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/plugins/captcha/GigasizeCom.py b/module/plugins/ocr/GigasizeCom.py
index 244cf6a2a..6982e6ca9 100644
--- a/module/plugins/captcha/GigasizeCom.py
+++ b/module/plugins/ocr/GigasizeCom.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.captcha.captcha import OCR
+from pyload.plugin.OCR import OCR
class GigasizeCom(OCR):
diff --git a/module/plugins/captcha/LinksaveIn.py b/module/plugins/ocr/LinksaveIn.py
index de6b0e7ff..7ced74f4b 100644
--- a/module/plugins/captcha/LinksaveIn.py
+++ b/module/plugins/ocr/LinksaveIn.py
@@ -9,7 +9,7 @@ from glob import glob
from os import sep
from os.path import abspath, dirname
-from module.plugins.captcha.captcha import OCR
+from pyload.plugin.OCR import OCR
class LinksaveIn(OCR):
diff --git a/module/plugins/captcha/NetloadIn.py b/module/plugins/ocr/NetloadIn.py
index 28eb18fb5..8939b5318 100644
--- a/module/plugins/captcha/NetloadIn.py
+++ b/module/plugins/ocr/NetloadIn.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.captcha.captcha import OCR
+from pyload.plugin.OCR import OCR
class NetloadIn(OCR):
diff --git a/module/plugins/captcha/ShareonlineBiz.py b/module/plugins/ocr/ShareonlineBiz.py
index 8210e8859..bbc3d1762 100644
--- a/module/plugins/captcha/ShareonlineBiz.py
+++ b/module/plugins/ocr/ShareonlineBiz.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from module.plugins.captcha.captcha import OCR
+from pyload.plugin.OCR import OCR
class ShareonlineBiz(OCR):
diff --git a/module/plugins/ocr/__init__.py b/module/plugins/ocr/__init__.py
new file mode 100644
index 000000000..40a96afc6
--- /dev/null
+++ b/module/plugins/ocr/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/remote/ClickAndLoadBackend.py b/module/remote/ClickAndLoadBackend.py
index ad8031587..a73ea7f24 100644
--- a/module/remote/ClickAndLoadBackend.py
+++ b/module/remote/ClickAndLoadBackend.py
@@ -1,20 +1,6 @@
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
+
import re
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from cgi import FieldStorage
@@ -24,10 +10,10 @@ from binascii import unhexlify
try:
from Crypto.Cipher import AES
-except:
+except Exception:
pass
-from RemoteManager import BackendBase
+from pyload.manager.Remote import BackendBase
core = None
js = None
@@ -93,7 +79,7 @@ class CNLHandler(BaseHTTPRequestHandler):
resp += "\r\n"
self.start_response(resp)
self.wfile.write(resp)
- except Exception,e :
+ except Exception, e:
self.send_error(500, str(e))
else:
self.send_error(404, "Not Found")
@@ -139,7 +125,7 @@ class CNLHandler(BaseHTTPRequestHandler):
IV = Key
obj = AES.new(Key, AES.MODE_CBC, IV)
- result = obj.decrypt(crypted).replace("\x00", "").replace("\r","").split("\n")
+ result = obj.decrypt(crypted).replace("\x00", "").replace("\r", "").split("\n")
result = filter(lambda x: x != "", result)
diff --git a/module/remote/SocketBackend.py b/module/remote/SocketBackend.py
index 1a157cf1d..a1c885347 100644
--- a/module/remote/SocketBackend.py
+++ b/module/remote/SocketBackend.py
@@ -2,7 +2,7 @@
import SocketServer
-from RemoteManager import BackendBase
+from pyload.manager.Remote import BackendBase
class RequestHandler(SocketServer.BaseRequestHandler):
diff --git a/module/remote/ThriftBackend.py b/module/remote/ThriftBackend.py
index b4a2bb25e..16917cfc9 100644
--- a/module/remote/ThriftBackend.py
+++ b/module/remote/ThriftBackend.py
@@ -1,29 +1,15 @@
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay, RaNaN
-"""
+# @author: RaNaN, mkaay
+
from os.path import exists
-from module.remote.RemoteManager import BackendBase
+from pyload.manager.Remote import BackendBase
-from thriftbackend.Processor import Processor
-from thriftbackend.Protocol import ProtocolFactory
-from thriftbackend.Socket import ServerSocket
-from thriftbackend.Transport import TransportFactory
-#from thriftbackend.Transport import TransportFactoryCompressed
+from pyload.remote.thriftbackend.Processor import Processor
+from pyload.remote.thriftbackend.Protocol import ProtocolFactory
+from pyload.remote.thriftbackend.Socket import ServerSocket
+from pyload.remote.thriftbackend.Transport import TransportFactory
+#from pyload.remote.thriftbackend.Transport import TransportFactoryCompressed
from thrift.server import TServer
@@ -46,11 +32,11 @@ class ThriftBackend(BackendBase):
# tfactory = TransportFactoryCompressed()
tfactory = TransportFactory()
pfactory = ProtocolFactory()
-
+
self.server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)
#self.server = TNonblockingServer.TNonblockingServer(processor, transport, tfactory, pfactory)
-
+
#server = TServer.TThreadPoolServer(processor, transport, tfactory, pfactory)
-
+
def serve(self):
self.server.serve()
diff --git a/module/remote/__init__.py b/module/remote/__init__.py
index 9298f5337..60dfa77c7 100644
--- a/module/remote/__init__.py
+++ b/module/remote/__init__.py
@@ -1,2 +1,3 @@
# -*- coding: utf-8 -*-
+
activated = True
diff --git a/module/remote/socketbackend/__init__.py b/module/remote/socketbackend/__init__.py
index de6d13128..40a96afc6 100644
--- a/module/remote/socketbackend/__init__.py
+++ b/module/remote/socketbackend/__init__.py
@@ -1,2 +1 @@
-__author__ = 'christian'
- \ No newline at end of file
+# -*- coding: utf-8 -*-
diff --git a/module/remote/socketbackend/create_ttypes.py b/module/remote/socketbackend/create_ttypes.py
index 05662cb50..5bfbcafa0 100644
--- a/module/remote/socketbackend/create_ttypes.py
+++ b/module/remote/socketbackend/create_ttypes.py
@@ -1,18 +1,19 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
import inspect
+import os
+import platform
import sys
-from os.path import abspath, dirname, join
-path = dirname(abspath(__file__))
-module = join(path, "..", "..")
-sys.path.append(join(module, "lib"))
-sys.path.append(join(module, "remote"))
+if "64" in platform.machine():
+ sys.path.append(os.path.join(pypath, "lib64"))
+sys.path.append(os.path.join(pypath, "lib"))
-from thriftbackend.thriftgen.pyload import ttypes
-from thriftbackend.thriftgen.pyload.Pyload import Iface
+sys.path.append(os.path.join(pypath, "pyload", "remote"))
+
+from pyload.remote.thriftbackend.thriftgen.pyload import ttypes
+from pyload.remote.thriftbackend.thriftgen.pyload.Pyload import Iface
def main():
@@ -34,11 +35,10 @@ def main():
enums.append(klass)
- f = open(join(path, "ttypes.py"), "wb")
+ f = open(os.path.join(pypath, "pyload", "api", "types.py"), "wb")
f.write(
- """#!/usr/bin/env python
-# -*- coding: utf-8 -*-
+ """# -*- coding: utf-8 -*-
# Autogenerated by pyload
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
@@ -74,7 +74,7 @@ class BaseObject(object):
f.write("\n")
- f.write("class Iface:\n")
+ f.write("class Iface(object):\n")
for name in dir(Iface):
if name.startswith("_"): continue
@@ -86,6 +86,3 @@ class BaseObject(object):
f.write("\n")
f.close()
-
-if __name__ == "__main__":
- main() \ No newline at end of file
diff --git a/module/remote/socketbackend/ttypes.py b/module/remote/socketbackend/ttypes.py
deleted file mode 100644
index f8ea121fa..000000000
--- a/module/remote/socketbackend/ttypes.py
+++ /dev/null
@@ -1,383 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# Autogenerated by pyload
-# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
-
-class BaseObject(object):
- __slots__ = []
-
-class Destination:
- Collector = 0
- Queue = 1
-
-class DownloadStatus:
- Aborted = 9
- Custom = 11
- Decrypting = 10
- Downloading = 12
- Failed = 8
- Finished = 0
- Offline = 1
- Online = 2
- Processing = 13
- Queued = 3
- Skipped = 4
- Starting = 7
- TempOffline = 6
- Unknown = 14
- Waiting = 5
-
-class ElementType:
- File = 1
- Package = 0
-
-class Input:
- BOOL = 4
- CHOICE = 6
- CLICK = 5
- LIST = 8
- MULTIPLE = 7
- NONE = 0
- PASSWORD = 3
- TABLE = 9
- TEXT = 1
- TEXTBOX = 2
-
-class Output:
- CAPTCHA = 1
- NOTIFICATION = 4
- QUESTION = 2
-
-class AccountInfo(BaseObject):
- __slots__ = ['validuntil', 'login', 'options', 'valid', 'trafficleft', 'maxtraffic', 'premium', 'type']
-
- def __init__(self, validuntil=None, login=None, options=None, valid=None, trafficleft=None, maxtraffic=None, premium=None, type=None):
- self.validuntil = validuntil
- self.login = login
- self.options = options
- self.valid = valid
- self.trafficleft = trafficleft
- self.maxtraffic = maxtraffic
- self.premium = premium
- self.type = type
-
-class CaptchaTask(BaseObject):
- __slots__ = ['tid', 'data', 'type', 'resultType']
-
- def __init__(self, tid=None, data=None, type=None, resultType=None):
- self.tid = tid
- self.data = data
- self.type = type
- self.resultType = resultType
-
-class ConfigItem(BaseObject):
- __slots__ = ['name', 'description', 'value', 'type']
-
- def __init__(self, name=None, description=None, value=None, type=None):
- self.name = name
- self.description = description
- self.value = value
- self.type = type
-
-class ConfigSection(BaseObject):
- __slots__ = ['name', 'description', 'items', 'outline']
-
- def __init__(self, name=None, description=None, items=None, outline=None):
- self.name = name
- self.description = description
- self.items = items
- self.outline = outline
-
-class DownloadInfo(BaseObject):
- __slots__ = ['fid', 'name', 'speed', 'eta', 'format_eta', 'bleft', 'size', 'format_size', 'percent', 'status', 'statusmsg', 'format_wait', 'wait_until', 'packageID', 'packageName', 'plugin']
-
- def __init__(self, fid=None, name=None, speed=None, eta=None, format_eta=None, bleft=None, size=None, format_size=None, percent=None, status=None, statusmsg=None, format_wait=None, wait_until=None, packageID=None, packageName=None, plugin=None):
- self.fid = fid
- self.name = name
- self.speed = speed
- self.eta = eta
- self.format_eta = format_eta
- self.bleft = bleft
- self.size = size
- self.format_size = format_size
- self.percent = percent
- self.status = status
- self.statusmsg = statusmsg
- self.format_wait = format_wait
- self.wait_until = wait_until
- self.packageID = packageID
- self.packageName = packageName
- self.plugin = plugin
-
-class EventInfo(BaseObject):
- __slots__ = ['eventname', 'id', 'type', 'destination']
-
- def __init__(self, eventname=None, id=None, type=None, destination=None):
- self.eventname = eventname
- self.id = id
- self.type = type
- self.destination = destination
-
-class FileData(BaseObject):
- __slots__ = ['fid', 'url', 'name', 'plugin', 'size', 'format_size', 'status', 'statusmsg', 'packageID', 'error', 'order']
-
- def __init__(self, fid=None, url=None, name=None, plugin=None, size=None, format_size=None, status=None, statusmsg=None, packageID=None, error=None, order=None):
- self.fid = fid
- self.url = url
- self.name = name
- self.plugin = plugin
- self.size = size
- self.format_size = format_size
- self.status = status
- self.statusmsg = statusmsg
- self.packageID = packageID
- self.error = error
- self.order = order
-
-class FileDoesNotExists(Exception):
- __slots__ = ['fid']
-
- def __init__(self, fid=None):
- self.fid = fid
-
-class InteractionTask(BaseObject):
- __slots__ = ['iid', 'input', 'structure', 'preset', 'output', 'data', 'title', 'description', 'plugin']
-
- def __init__(self, iid=None, input=None, structure=None, preset=None, output=None, data=None, title=None, description=None, plugin=None):
- self.iid = iid
- self.input = input
- self.structure = structure
- self.preset = preset
- self.output = output
- self.data = data
- self.title = title
- self.description = description
- self.plugin = plugin
-
-class OnlineCheck(BaseObject):
- __slots__ = ['rid', 'data']
-
- def __init__(self, rid=None, data=None):
- self.rid = rid
- self.data = data
-
-class OnlineStatus(BaseObject):
- __slots__ = ['name', 'plugin', 'packagename', 'status', 'size']
-
- def __init__(self, name=None, plugin=None, packagename=None, status=None, size=None):
- self.name = name
- self.plugin = plugin
- self.packagename = packagename
- self.status = status
- self.size = size
-
-class PackageData(BaseObject):
- __slots__ = ['pid', 'name', 'folder', 'site', 'password', 'dest', 'order', 'linksdone', 'sizedone', 'sizetotal', 'linkstotal', 'links', 'fids']
-
- def __init__(self, pid=None, name=None, folder=None, site=None, password=None, dest=None, order=None, linksdone=None, sizedone=None, sizetotal=None, linkstotal=None, links=None, fids=None):
- self.pid = pid
- self.name = name
- self.folder = folder
- self.site = site
- self.password = password
- self.dest = dest
- self.order = order
- self.linksdone = linksdone
- self.sizedone = sizedone
- self.sizetotal = sizetotal
- self.linkstotal = linkstotal
- self.links = links
- self.fids = fids
-
-class PackageDoesNotExists(Exception):
- __slots__ = ['pid']
-
- def __init__(self, pid=None):
- self.pid = pid
-
-class ServerStatus(BaseObject):
- __slots__ = ['pause', 'active', 'queue', 'total', 'speed', 'download', 'reconnect']
-
- def __init__(self, pause=None, active=None, queue=None, total=None, speed=None, download=None, reconnect=None):
- self.pause = pause
- self.active = active
- self.queue = queue
- self.total = total
- self.speed = speed
- self.download = download
- self.reconnect = reconnect
-
-class ServiceCall(BaseObject):
- __slots__ = ['plugin', 'func', 'arguments', 'parseArguments']
-
- def __init__(self, plugin=None, func=None, arguments=None, parseArguments=None):
- self.plugin = plugin
- self.func = func
- self.arguments = arguments
- self.parseArguments = parseArguments
-
-class ServiceDoesNotExists(Exception):
- __slots__ = ['plugin', 'func']
-
- def __init__(self, plugin=None, func=None):
- self.plugin = plugin
- self.func = func
-
-class ServiceException(Exception):
- __slots__ = ['msg']
-
- def __init__(self, msg=None):
- self.msg = msg
-
-class UserData(BaseObject):
- __slots__ = ['name', 'email', 'role', 'permission', 'templateName']
-
- def __init__(self, name=None, email=None, role=None, permission=None, templateName=None):
- self.name = name
- self.email = email
- self.role = role
- self.permission = permission
- self.templateName = templateName
-
-class Iface:
- def addFiles(self, pid, links):
- pass
- def addPackage(self, name, links, dest):
- pass
- def call(self, info):
- pass
- def checkOnlineStatus(self, urls):
- pass
- def checkOnlineStatusContainer(self, urls, filename, data):
- pass
- def checkURLs(self, urls):
- pass
- def deleteFiles(self, fids):
- pass
- def deleteFinished(self):
- pass
- def deletePackages(self, pids):
- pass
- def freeSpace(self):
- pass
- def generateAndAddPackages(self, links, dest):
- pass
- def generatePackages(self, links):
- pass
- def getAccountTypes(self):
- pass
- def getAccounts(self, refresh):
- pass
- def getAllInfo(self):
- pass
- def getAllUserData(self):
- pass
- def getCaptchaTask(self, exclusive):
- pass
- def getCaptchaTaskStatus(self, tid):
- pass
- def getCollector(self):
- pass
- def getCollectorData(self):
- pass
- def getConfig(self):
- pass
- def getConfigValue(self, category, option, section):
- pass
- def getEvents(self, uuid):
- pass
- def getFileData(self, fid):
- pass
- def getFileOrder(self, pid):
- pass
- def getInfoByPlugin(self, plugin):
- pass
- def getLog(self, offset):
- pass
- def getPackageData(self, pid):
- pass
- def getPackageInfo(self, pid):
- pass
- def getPackageOrder(self, destination):
- pass
- def getPluginConfig(self):
- pass
- def getQueue(self):
- pass
- def getQueueData(self):
- pass
- def getServerVersion(self):
- pass
- def getServices(self):
- pass
- def getUserData(self, username, password):
- pass
- def hasService(self, plugin, func):
- pass
- def isCaptchaWaiting(self):
- pass
- def isTimeDownload(self):
- pass
- def isTimeReconnect(self):
- pass
- def kill(self):
- pass
- def login(self, username, password):
- pass
- def moveFiles(self, fids, pid):
- pass
- def movePackage(self, destination, pid):
- pass
- def orderFile(self, fid, position):
- pass
- def orderPackage(self, pid, position):
- pass
- def parseURLs(self, html, url):
- pass
- def pauseServer(self):
- pass
- def pollResults(self, rid):
- pass
- def pullFromQueue(self, pid):
- pass
- def pushToQueue(self, pid):
- pass
- def recheckPackage(self, pid):
- pass
- def removeAccount(self, plugin, account):
- pass
- def restart(self):
- pass
- def restartFailed(self):
- pass
- def restartFile(self, fid):
- pass
- def restartPackage(self, pid):
- pass
- def setCaptchaResult(self, tid, result):
- pass
- def setConfigValue(self, category, option, value, section):
- pass
- def setPackageData(self, pid, data):
- pass
- def setPackageName(self, pid, name):
- pass
- def statusDownloads(self):
- pass
- def statusServer(self):
- pass
- def stopAllDownloads(self):
- pass
- def stopDownloads(self, fids):
- pass
- def togglePause(self):
- pass
- def toggleReconnect(self):
- pass
- def unpauseServer(self):
- pass
- def updateAccount(self, plugin, account, password, options):
- pass
- def uploadContainer(self, filename, data):
- pass
-
diff --git a/module/remote/thriftbackend/Processor.py b/module/remote/thriftbackend/Processor.py
index a8b87c82c..683697d1c 100644
--- a/module/remote/thriftbackend/Processor.py
+++ b/module/remote/thriftbackend/Processor.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from thriftgen.pyload import Pyload
+from pyload.remote.thriftbackend.thriftgen.pyload import Pyload
class Processor(Pyload.Processor):
def __init__(self, *args, **kwargs):
diff --git a/module/remote/thriftbackend/Protocol.py b/module/remote/thriftbackend/Protocol.py
index c42d01459..417b45834 100644
--- a/module/remote/thriftbackend/Protocol.py
+++ b/module/remote/thriftbackend/Protocol.py
@@ -17,7 +17,7 @@ class Protocol(TBinaryProtocol.TBinaryProtocol):
str = self.trans.readAll(len)
try:
str = str.decode("utf8", "ignore")
- except:
+ except Exception:
pass
return str
@@ -27,4 +27,4 @@ class ProtocolFactory(TBinaryProtocol.TBinaryProtocolFactory):
def getProtocol(self, trans):
prot = Protocol(trans, self.strictRead, self.strictWrite)
- return prot \ No newline at end of file
+ return prot
diff --git a/module/remote/thriftbackend/Socket.py b/module/remote/thriftbackend/Socket.py
index 2243f9df2..f6edc8408 100644
--- a/module/remote/thriftbackend/Socket.py
+++ b/module/remote/thriftbackend/Socket.py
@@ -10,7 +10,7 @@ from thrift.transport.TSocket import TSocket, TServerSocket, TTransportException
WantReadError = Exception #overwritten when ssl is used
-class SecureSocketConnection:
+class SecureSocketConnection(object):
def __init__(self, connection):
self.__dict__["connection"] = connection
@@ -26,14 +26,14 @@ class SecureSocketConnection:
def accept(self):
connection, address = self.__dict__["connection"].accept()
return SecureSocketConnection(connection), address
-
+
def send(self, buff):
try:
return self.__dict__["connection"].send(buff)
except WantReadError:
sleep(0.1)
return self.send(buff)
-
+
def recv(self, buff):
try:
return self.__dict__["connection"].recv(buff)
@@ -86,7 +86,7 @@ class Socket(TSocket):
buff = ''
else:
raise
-
+
if not len(buff):
raise TTransportException(type=TTransportException.END_OF_FILE, message='TSocket read 0 bytes')
return buff
diff --git a/module/remote/thriftbackend/ThriftClient.py b/module/remote/thriftbackend/ThriftClient.py
index 74363cf62..7c2a1cb01 100644
--- a/module/remote/thriftbackend/ThriftClient.py
+++ b/module/remote/thriftbackend/ThriftClient.py
@@ -2,13 +2,17 @@
import sys
from socket import error
-from os.path import dirname, abspath, join
from traceback import print_exc
try:
import thrift
+
except ImportError:
- sys.path.append(abspath(join(dirname(abspath(__file__)), "..", "..", "lib")))
+ import platform
+
+ if "64" in platform.machine():
+ sys.path.append(os.path.join(pypath, "lib64"))
+ sys.path.append(os.path.join(pypath, "lib"))
from thrift.transport import TTransport
#from thrift.transport.TZlibTransport import TZlibTransport
@@ -17,8 +21,8 @@ from Protocol import Protocol
# modules should import ttypes from here, when want to avoid importing API
-from thriftgen.pyload import Pyload
-from thriftgen.pyload.ttypes import *
+from pyload.remote.thriftbackend.thriftgen.pyload import Pyload
+from pyload.remote.thriftbackend.thriftgen.pyload.ttypes import *
ConnectionClosed = TTransport.TTransportException
@@ -31,7 +35,7 @@ class NoConnection(Exception):
class NoSSL(Exception):
pass
-class ThriftClient:
+class ThriftClient(object):
def __init__(self, host="localhost", port=7227, user="", password=""):
self.createConnection(host, port)
@@ -85,25 +89,3 @@ class ThriftClient:
def __getattr__(self, item):
return getattr(self.client, item)
-
-if __name__ == "__main__":
-
- client = ThriftClient(user="User", password="pwhere")
-
- print client.getServerVersion()
- print client.statusServer()
- print client.statusDownloads()
- q = client.getQueue()
-
-# for p in q:
-# data = client.getPackageData(p.pid)
-# print data
-# print "Package Name: ", data.name
-
-
- print client.getServices()
- print client.call(Pyload.ServiceCall("UpdateManager", "recheckForUpdates"))
-
- print client.getConfigValue("download", "limit_speed", "core")
-
- client.close() \ No newline at end of file
diff --git a/module/remote/thriftbackend/ThriftTest.py b/module/remote/thriftbackend/ThriftTest.py
index 69ac6a745..c9c0d3cf3 100644
--- a/module/remote/thriftbackend/ThriftTest.py
+++ b/module/remote/thriftbackend/ThriftTest.py
@@ -1,14 +1,16 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
+import os
+import platform
import sys
-from os.path import join,abspath,dirname
-path = join((abspath(dirname(__file__))), "..","..", "lib")
-sys.path.append(path)
+if "64" in platform.machine():
+ sys.path.append(os.path.join(pypath, "lib64"))
+sys.path.append(os.path.join(pypath, "lib"))
-from thriftgen.pyload import Pyload
-from thriftgen.pyload.ttypes import *
+
+from pyload.remote.thriftbackend.thriftgen.pyload import Pyload
+from pyload.remote.thriftbackend.thriftgen.pyload.ttypes import *
from Socket import Socket
from thrift import Thrift
@@ -22,11 +24,11 @@ import xmlrpclib
def bench(f, *args, **kwargs):
s = time()
- ret = [f(*args, **kwargs) for i in range(0,100)]
+ ret = [f(*args, **kwargs) for i in range(0, 100)]
e = time()
try:
print "%s: %f s" % (f._Method__name, e-s)
- except :
+ except Exception:
print "%s: %f s" % (f.__name__, e-s)
return ret
diff --git a/module/remote/thriftbackend/Transport.py b/module/remote/thriftbackend/Transport.py
index 5772c5a9e..7db4ba9d7 100644
--- a/module/remote/thriftbackend/Transport.py
+++ b/module/remote/thriftbackend/Transport.py
@@ -19,12 +19,12 @@ class TransportCompressed(TZlibTransport):
self.handle = trans.handle
self.remoteaddr = trans.handle.getpeername()
-class TransportFactory:
+class TransportFactory(object):
def getTransport(self, trans):
buffered = Transport(trans)
return buffered
-class TransportFactoryCompressed:
+class TransportFactoryCompressed(object):
_last_trans = None
_last_z = None
@@ -34,4 +34,4 @@ class TransportFactoryCompressed:
ztrans = TransportCompressed(Transport(trans), compresslevel)
self._last_trans = trans
self._last_z = ztrans
- return ztrans \ No newline at end of file
+ return ztrans
diff --git a/module/remote/thriftbackend/__init__.py b/module/remote/thriftbackend/__init__.py
index e69de29bb..40a96afc6 100644
--- a/module/remote/thriftbackend/__init__.py
+++ b/module/remote/thriftbackend/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/remote/thriftbackend/pyload.thrift b/module/remote/thriftbackend/pyload.thrift
index 1542e651a..3aef7af00 100644
--- a/module/remote/thriftbackend/pyload.thrift
+++ b/module/remote/thriftbackend/pyload.thrift
@@ -194,7 +194,7 @@ struct OnlineStatus {
2: PluginName plugin,
3: string packagename,
4: DownloadStatus status,
- 5: i64 size, // size <= 0 : unknown
+ 5: i64 size, // size <= 0: unknown
}
struct OnlineCheck {
@@ -297,13 +297,13 @@ service Pyload {
//events
list<EventInfo> getEvents(1: string uuid)
-
+
//accounts
list<AccountInfo> getAccounts(1: bool refresh),
list<string> getAccountTypes()
void updateAccount(1: PluginName plugin, 2: string account, 3: string password, 4: map<string, string> options),
void removeAccount(1: PluginName plugin, 2: string account),
-
+
//auth
bool login(1: string username, 2: string password),
UserData getUserData(1: string username, 2:string password),
@@ -311,7 +311,7 @@ service Pyload {
//services
- // servicename : description
+ // servicename: description
map<PluginName, map<string, string>> getServices(),
bool hasService(1: PluginName plugin, 2: string func),
string call(1: ServiceCall info) throws (1: ServiceDoesNotExists ex, 2: ServiceException e),
@@ -319,7 +319,7 @@ service Pyload {
//info
// {plugin: {name: value}}
- map<PluginName, map<string,string>> getAllInfo(),
+ map<PluginName, map<string, string>> getAllInfo(),
map<string, string> getInfoByPlugin(1: PluginName plugin),
//scheduler
diff --git a/module/remote/thriftbackend/thriftgen/__init__.py b/module/remote/thriftbackend/thriftgen/__init__.py
index e69de29bb..40a96afc6 100644
--- a/module/remote/thriftbackend/thriftgen/__init__.py
+++ b/module/remote/thriftbackend/thriftgen/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/module/remote/thriftbackend/thriftgen/pyload/Pyload-remote b/module/remote/thriftbackend/thriftgen/pyload/Pyload-remote
index bfaf5b078..ddc1dd451 100755..100644
--- a/module/remote/thriftbackend/thriftgen/pyload/Pyload-remote
+++ b/module/remote/thriftbackend/thriftgen/pyload/Pyload-remote
@@ -1,10 +1,9 @@
-#!/usr/bin/env python
#
# Autogenerated by Thrift Compiler (0.9.0-dev)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
-# options string: py:slots,dynamic
+# options string: py:slots, dynamic
#
import sys
@@ -19,9 +18,9 @@ import Pyload
from ttypes import *
if len(sys.argv) <= 1 or sys.argv[1] == '--help':
- print ''
+ print
print 'Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] function [arg1 [arg2...]]'
- print ''
+ print
print 'Functions:'
print ' string getConfigValue(string category, string option, string section)'
print ' void setConfigValue(string category, string option, string value, string section)'
@@ -93,7 +92,7 @@ if len(sys.argv) <= 1 or sys.argv[1] == '--help':
print ' CaptchaTask getCaptchaTask(bool exclusive)'
print ' string getCaptchaTaskStatus(TaskID tid)'
print ' void setCaptchaResult(TaskID tid, string result)'
- print ''
+ print
sys.exit(0)
pp = pprint.PrettyPrinter(indent = 2)
@@ -148,13 +147,13 @@ if cmd == 'getConfigValue':
if len(args) != 3:
print 'getConfigValue requires 3 args'
sys.exit(1)
- pp.pprint(client.getConfigValue(args[0],args[1],args[2],))
+ pp.pprint(client.getConfigValue(args[0], args[1], args[2],))
elif cmd == 'setConfigValue':
if len(args) != 4:
print 'setConfigValue requires 4 args'
sys.exit(1)
- pp.pprint(client.setConfigValue(args[0],args[1],args[2],args[3],))
+ pp.pprint(client.setConfigValue(args[0], args[1], args[2], args[3],))
elif cmd == 'getConfig':
if len(args) != 0:
@@ -256,7 +255,7 @@ elif cmd == 'parseURLs':
if len(args) != 2:
print 'parseURLs requires 2 args'
sys.exit(1)
- pp.pprint(client.parseURLs(args[0],args[1],))
+ pp.pprint(client.parseURLs(args[0], args[1],))
elif cmd == 'checkOnlineStatus':
if len(args) != 1:
@@ -268,7 +267,7 @@ elif cmd == 'checkOnlineStatusContainer':
if len(args) != 3:
print 'checkOnlineStatusContainer requires 3 args'
sys.exit(1)
- pp.pprint(client.checkOnlineStatusContainer(eval(args[0]),args[1],args[2],))
+ pp.pprint(client.checkOnlineStatusContainer(eval(args[0]), args[1], args[2],))
elif cmd == 'pollResults':
if len(args) != 1:
@@ -340,25 +339,25 @@ elif cmd == 'generateAndAddPackages':
if len(args) != 2:
print 'generateAndAddPackages requires 2 args'
sys.exit(1)
- pp.pprint(client.generateAndAddPackages(eval(args[0]),eval(args[1]),))
+ pp.pprint(client.generateAndAddPackages(eval(args[0]), eval(args[1]),))
elif cmd == 'addPackage':
if len(args) != 3:
print 'addPackage requires 3 args'
sys.exit(1)
- pp.pprint(client.addPackage(args[0],eval(args[1]),eval(args[2]),))
+ pp.pprint(client.addPackage(args[0], eval(args[1]), eval(args[2]),))
elif cmd == 'addFiles':
if len(args) != 2:
print 'addFiles requires 2 args'
sys.exit(1)
- pp.pprint(client.addFiles(eval(args[0]),eval(args[1]),))
+ pp.pprint(client.addFiles(eval(args[0]), eval(args[1]),))
elif cmd == 'uploadContainer':
if len(args) != 2:
print 'uploadContainer requires 2 args'
sys.exit(1)
- pp.pprint(client.uploadContainer(args[0],args[1],))
+ pp.pprint(client.uploadContainer(args[0], args[1],))
elif cmd == 'deleteFiles':
if len(args) != 1:
@@ -418,37 +417,37 @@ elif cmd == 'setPackageName':
if len(args) != 2:
print 'setPackageName requires 2 args'
sys.exit(1)
- pp.pprint(client.setPackageName(eval(args[0]),args[1],))
+ pp.pprint(client.setPackageName(eval(args[0]), args[1],))
elif cmd == 'movePackage':
if len(args) != 2:
print 'movePackage requires 2 args'
sys.exit(1)
- pp.pprint(client.movePackage(eval(args[0]),eval(args[1]),))
+ pp.pprint(client.movePackage(eval(args[0]), eval(args[1]),))
elif cmd == 'moveFiles':
if len(args) != 2:
print 'moveFiles requires 2 args'
sys.exit(1)
- pp.pprint(client.moveFiles(eval(args[0]),eval(args[1]),))
+ pp.pprint(client.moveFiles(eval(args[0]), eval(args[1]),))
elif cmd == 'orderPackage':
if len(args) != 2:
print 'orderPackage requires 2 args'
sys.exit(1)
- pp.pprint(client.orderPackage(eval(args[0]),eval(args[1]),))
+ pp.pprint(client.orderPackage(eval(args[0]), eval(args[1]),))
elif cmd == 'orderFile':
if len(args) != 2:
print 'orderFile requires 2 args'
sys.exit(1)
- pp.pprint(client.orderFile(eval(args[0]),eval(args[1]),))
+ pp.pprint(client.orderFile(eval(args[0]), eval(args[1]),))
elif cmd == 'setPackageData':
if len(args) != 2:
print 'setPackageData requires 2 args'
sys.exit(1)
- pp.pprint(client.setPackageData(eval(args[0]),eval(args[1]),))
+ pp.pprint(client.setPackageData(eval(args[0]), eval(args[1]),))
elif cmd == 'deleteFinished':
if len(args) != 0:
@@ -484,25 +483,25 @@ elif cmd == 'updateAccount':
if len(args) != 4:
print 'updateAccount requires 4 args'
sys.exit(1)
- pp.pprint(client.updateAccount(eval(args[0]),args[1],args[2],eval(args[3]),))
+ pp.pprint(client.updateAccount(eval(args[0]), args[1], args[2], eval(args[3]),))
elif cmd == 'removeAccount':
if len(args) != 2:
print 'removeAccount requires 2 args'
sys.exit(1)
- pp.pprint(client.removeAccount(eval(args[0]),args[1],))
+ pp.pprint(client.removeAccount(eval(args[0]), args[1],))
elif cmd == 'login':
if len(args) != 2:
print 'login requires 2 args'
sys.exit(1)
- pp.pprint(client.login(args[0],args[1],))
+ pp.pprint(client.login(args[0], args[1],))
elif cmd == 'getUserData':
if len(args) != 2:
print 'getUserData requires 2 args'
sys.exit(1)
- pp.pprint(client.getUserData(args[0],args[1],))
+ pp.pprint(client.getUserData(args[0], args[1],))
elif cmd == 'getAllUserData':
if len(args) != 0:
@@ -520,7 +519,7 @@ elif cmd == 'hasService':
if len(args) != 2:
print 'hasService requires 2 args'
sys.exit(1)
- pp.pprint(client.hasService(eval(args[0]),args[1],))
+ pp.pprint(client.hasService(eval(args[0]), args[1],))
elif cmd == 'call':
if len(args) != 1:
@@ -562,7 +561,7 @@ elif cmd == 'setCaptchaResult':
if len(args) != 2:
print 'setCaptchaResult requires 2 args'
sys.exit(1)
- pp.pprint(client.setCaptchaResult(eval(args[0]),args[1],))
+ pp.pprint(client.setCaptchaResult(eval(args[0]), args[1],))
else:
print 'Unrecognized method %s' % cmd
diff --git a/module/remote/thriftbackend/thriftgen/pyload/Pyload.py b/module/remote/thriftbackend/thriftgen/pyload/Pyload.py
index 78a42f16a..e8d2cf9af 100644
--- a/module/remote/thriftbackend/thriftgen/pyload/Pyload.py
+++ b/module/remote/thriftbackend/thriftgen/pyload/Pyload.py
@@ -3,7 +3,7 @@
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
-# options string: py:slots,dynamic
+# options string: py:slots, dynamic
#
from thrift.Thrift import TType, TMessageType, TException
@@ -32,34 +32,34 @@ class Iface(object):
"""
pass
- def getConfig(self, ):
+ def getConfig(self,):
pass
- def getPluginConfig(self, ):
+ def getPluginConfig(self,):
pass
- def pauseServer(self, ):
+ def pauseServer(self,):
pass
- def unpauseServer(self, ):
+ def unpauseServer(self,):
pass
- def togglePause(self, ):
+ def togglePause(self,):
pass
- def statusServer(self, ):
+ def statusServer(self,):
pass
- def freeSpace(self, ):
+ def freeSpace(self,):
pass
- def getServerVersion(self, ):
+ def getServerVersion(self,):
pass
- def kill(self, ):
+ def kill(self,):
pass
- def restart(self, ):
+ def restart(self,):
pass
def getLog(self, offset):
@@ -69,13 +69,13 @@ class Iface(object):
"""
pass
- def isTimeDownload(self, ):
+ def isTimeDownload(self,):
pass
- def isTimeReconnect(self, ):
+ def isTimeReconnect(self,):
pass
- def toggleReconnect(self, ):
+ def toggleReconnect(self,):
pass
def generatePackages(self, links):
@@ -123,7 +123,7 @@ class Iface(object):
"""
pass
- def statusDownloads(self, ):
+ def statusDownloads(self,):
pass
def getPackageData(self, pid):
@@ -147,16 +147,16 @@ class Iface(object):
"""
pass
- def getQueue(self, ):
+ def getQueue(self,):
pass
- def getCollector(self, ):
+ def getCollector(self,):
pass
- def getQueueData(self, ):
+ def getQueueData(self,):
pass
- def getCollectorData(self, ):
+ def getCollectorData(self,):
pass
def getPackageOrder(self, destination):
@@ -255,7 +255,7 @@ class Iface(object):
"""
pass
- def stopAllDownloads(self, ):
+ def stopAllDownloads(self,):
pass
def stopDownloads(self, fids):
@@ -313,10 +313,10 @@ class Iface(object):
"""
pass
- def deleteFinished(self, ):
+ def deleteFinished(self,):
pass
- def restartFailed(self, ):
+ def restartFailed(self,):
pass
def getEvents(self, uuid):
@@ -333,7 +333,7 @@ class Iface(object):
"""
pass
- def getAccountTypes(self, ):
+ def getAccountTypes(self,):
pass
def updateAccount(self, plugin, account, password, options):
@@ -370,10 +370,10 @@ class Iface(object):
"""
pass
- def getAllUserData(self, ):
+ def getAllUserData(self,):
pass
- def getServices(self, ):
+ def getServices(self,):
pass
def hasService(self, plugin, func):
@@ -391,7 +391,7 @@ class Iface(object):
"""
pass
- def getAllInfo(self, ):
+ def getAllInfo(self,):
pass
def getInfoByPlugin(self, plugin):
@@ -401,7 +401,7 @@ class Iface(object):
"""
pass
- def isCaptchaWaiting(self, ):
+ def isCaptchaWaiting(self,):
pass
def getCaptchaTask(self, exclusive):
@@ -454,7 +454,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getConfigValue(self, ):
+ def recv_getConfigValue(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -490,7 +490,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_setConfigValue(self, ):
+ def recv_setConfigValue(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -502,18 +502,18 @@ class Client(Iface):
self._iprot.readMessageEnd()
return
- def getConfig(self, ):
+ def getConfig(self,):
self.send_getConfig()
return self.recv_getConfig()
- def send_getConfig(self, ):
+ def send_getConfig(self,):
self._oprot.writeMessageBegin('getConfig', TMessageType.CALL, self._seqid)
args = getConfig_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getConfig(self, ):
+ def recv_getConfig(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -527,18 +527,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "getConfig failed: unknown result");
- def getPluginConfig(self, ):
+ def getPluginConfig(self,):
self.send_getPluginConfig()
return self.recv_getPluginConfig()
- def send_getPluginConfig(self, ):
+ def send_getPluginConfig(self,):
self._oprot.writeMessageBegin('getPluginConfig', TMessageType.CALL, self._seqid)
args = getPluginConfig_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getPluginConfig(self, ):
+ def recv_getPluginConfig(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -552,18 +552,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "getPluginConfig failed: unknown result");
- def pauseServer(self, ):
+ def pauseServer(self,):
self.send_pauseServer()
self.recv_pauseServer()
- def send_pauseServer(self, ):
+ def send_pauseServer(self,):
self._oprot.writeMessageBegin('pauseServer', TMessageType.CALL, self._seqid)
args = pauseServer_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_pauseServer(self, ):
+ def recv_pauseServer(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -575,18 +575,18 @@ class Client(Iface):
self._iprot.readMessageEnd()
return
- def unpauseServer(self, ):
+ def unpauseServer(self,):
self.send_unpauseServer()
self.recv_unpauseServer()
- def send_unpauseServer(self, ):
+ def send_unpauseServer(self,):
self._oprot.writeMessageBegin('unpauseServer', TMessageType.CALL, self._seqid)
args = unpauseServer_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_unpauseServer(self, ):
+ def recv_unpauseServer(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -598,18 +598,18 @@ class Client(Iface):
self._iprot.readMessageEnd()
return
- def togglePause(self, ):
+ def togglePause(self,):
self.send_togglePause()
return self.recv_togglePause()
- def send_togglePause(self, ):
+ def send_togglePause(self,):
self._oprot.writeMessageBegin('togglePause', TMessageType.CALL, self._seqid)
args = togglePause_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_togglePause(self, ):
+ def recv_togglePause(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -623,18 +623,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "togglePause failed: unknown result");
- def statusServer(self, ):
+ def statusServer(self,):
self.send_statusServer()
return self.recv_statusServer()
- def send_statusServer(self, ):
+ def send_statusServer(self,):
self._oprot.writeMessageBegin('statusServer', TMessageType.CALL, self._seqid)
args = statusServer_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_statusServer(self, ):
+ def recv_statusServer(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -648,18 +648,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "statusServer failed: unknown result");
- def freeSpace(self, ):
+ def freeSpace(self,):
self.send_freeSpace()
return self.recv_freeSpace()
- def send_freeSpace(self, ):
+ def send_freeSpace(self,):
self._oprot.writeMessageBegin('freeSpace', TMessageType.CALL, self._seqid)
args = freeSpace_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_freeSpace(self, ):
+ def recv_freeSpace(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -673,18 +673,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "freeSpace failed: unknown result");
- def getServerVersion(self, ):
+ def getServerVersion(self,):
self.send_getServerVersion()
return self.recv_getServerVersion()
- def send_getServerVersion(self, ):
+ def send_getServerVersion(self,):
self._oprot.writeMessageBegin('getServerVersion', TMessageType.CALL, self._seqid)
args = getServerVersion_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getServerVersion(self, ):
+ def recv_getServerVersion(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -698,18 +698,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "getServerVersion failed: unknown result");
- def kill(self, ):
+ def kill(self,):
self.send_kill()
self.recv_kill()
- def send_kill(self, ):
+ def send_kill(self,):
self._oprot.writeMessageBegin('kill', TMessageType.CALL, self._seqid)
args = kill_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_kill(self, ):
+ def recv_kill(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -721,18 +721,18 @@ class Client(Iface):
self._iprot.readMessageEnd()
return
- def restart(self, ):
+ def restart(self,):
self.send_restart()
self.recv_restart()
- def send_restart(self, ):
+ def send_restart(self,):
self._oprot.writeMessageBegin('restart', TMessageType.CALL, self._seqid)
args = restart_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_restart(self, ):
+ def recv_restart(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -760,7 +760,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getLog(self, ):
+ def recv_getLog(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -774,18 +774,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "getLog failed: unknown result");
- def isTimeDownload(self, ):
+ def isTimeDownload(self,):
self.send_isTimeDownload()
return self.recv_isTimeDownload()
- def send_isTimeDownload(self, ):
+ def send_isTimeDownload(self,):
self._oprot.writeMessageBegin('isTimeDownload', TMessageType.CALL, self._seqid)
args = isTimeDownload_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_isTimeDownload(self, ):
+ def recv_isTimeDownload(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -799,18 +799,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "isTimeDownload failed: unknown result");
- def isTimeReconnect(self, ):
+ def isTimeReconnect(self,):
self.send_isTimeReconnect()
return self.recv_isTimeReconnect()
- def send_isTimeReconnect(self, ):
+ def send_isTimeReconnect(self,):
self._oprot.writeMessageBegin('isTimeReconnect', TMessageType.CALL, self._seqid)
args = isTimeReconnect_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_isTimeReconnect(self, ):
+ def recv_isTimeReconnect(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -824,18 +824,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "isTimeReconnect failed: unknown result");
- def toggleReconnect(self, ):
+ def toggleReconnect(self,):
self.send_toggleReconnect()
return self.recv_toggleReconnect()
- def send_toggleReconnect(self, ):
+ def send_toggleReconnect(self,):
self._oprot.writeMessageBegin('toggleReconnect', TMessageType.CALL, self._seqid)
args = toggleReconnect_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_toggleReconnect(self, ):
+ def recv_toggleReconnect(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -865,7 +865,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_generatePackages(self, ):
+ def recv_generatePackages(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -895,7 +895,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_checkURLs(self, ):
+ def recv_checkURLs(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -927,7 +927,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_parseURLs(self, ):
+ def recv_parseURLs(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -957,7 +957,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_checkOnlineStatus(self, ):
+ def recv_checkOnlineStatus(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -991,7 +991,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_checkOnlineStatusContainer(self, ):
+ def recv_checkOnlineStatusContainer(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1021,7 +1021,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_pollResults(self, ):
+ def recv_pollResults(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1035,18 +1035,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "pollResults failed: unknown result");
- def statusDownloads(self, ):
+ def statusDownloads(self,):
self.send_statusDownloads()
return self.recv_statusDownloads()
- def send_statusDownloads(self, ):
+ def send_statusDownloads(self,):
self._oprot.writeMessageBegin('statusDownloads', TMessageType.CALL, self._seqid)
args = statusDownloads_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_statusDownloads(self, ):
+ def recv_statusDownloads(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1076,7 +1076,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getPackageData(self, ):
+ def recv_getPackageData(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1108,7 +1108,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getPackageInfo(self, ):
+ def recv_getPackageInfo(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1140,7 +1140,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getFileData(self, ):
+ def recv_getFileData(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1156,18 +1156,18 @@ class Client(Iface):
raise result.e
raise TApplicationException(TApplicationException.MISSING_RESULT, "getFileData failed: unknown result");
- def getQueue(self, ):
+ def getQueue(self,):
self.send_getQueue()
return self.recv_getQueue()
- def send_getQueue(self, ):
+ def send_getQueue(self,):
self._oprot.writeMessageBegin('getQueue', TMessageType.CALL, self._seqid)
args = getQueue_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getQueue(self, ):
+ def recv_getQueue(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1181,18 +1181,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "getQueue failed: unknown result");
- def getCollector(self, ):
+ def getCollector(self,):
self.send_getCollector()
return self.recv_getCollector()
- def send_getCollector(self, ):
+ def send_getCollector(self,):
self._oprot.writeMessageBegin('getCollector', TMessageType.CALL, self._seqid)
args = getCollector_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getCollector(self, ):
+ def recv_getCollector(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1206,18 +1206,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "getCollector failed: unknown result");
- def getQueueData(self, ):
+ def getQueueData(self,):
self.send_getQueueData()
return self.recv_getQueueData()
- def send_getQueueData(self, ):
+ def send_getQueueData(self,):
self._oprot.writeMessageBegin('getQueueData', TMessageType.CALL, self._seqid)
args = getQueueData_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getQueueData(self, ):
+ def recv_getQueueData(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1231,18 +1231,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "getQueueData failed: unknown result");
- def getCollectorData(self, ):
+ def getCollectorData(self,):
self.send_getCollectorData()
return self.recv_getCollectorData()
- def send_getCollectorData(self, ):
+ def send_getCollectorData(self,):
self._oprot.writeMessageBegin('getCollectorData', TMessageType.CALL, self._seqid)
args = getCollectorData_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getCollectorData(self, ):
+ def recv_getCollectorData(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1272,7 +1272,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getPackageOrder(self, ):
+ def recv_getPackageOrder(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1302,7 +1302,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getFileOrder(self, ):
+ def recv_getFileOrder(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1334,7 +1334,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_generateAndAddPackages(self, ):
+ def recv_generateAndAddPackages(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1368,7 +1368,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_addPackage(self, ):
+ def recv_addPackage(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1400,7 +1400,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_addFiles(self, ):
+ def recv_addFiles(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1430,7 +1430,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_uploadContainer(self, ):
+ def recv_uploadContainer(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1458,7 +1458,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_deleteFiles(self, ):
+ def recv_deleteFiles(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1486,7 +1486,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_deletePackages(self, ):
+ def recv_deletePackages(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1514,7 +1514,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_pushToQueue(self, ):
+ def recv_pushToQueue(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1542,7 +1542,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_pullFromQueue(self, ):
+ def recv_pullFromQueue(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1570,7 +1570,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_restartPackage(self, ):
+ def recv_restartPackage(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1598,7 +1598,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_restartFile(self, ):
+ def recv_restartFile(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1626,7 +1626,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_recheckPackage(self, ):
+ def recv_recheckPackage(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1638,18 +1638,18 @@ class Client(Iface):
self._iprot.readMessageEnd()
return
- def stopAllDownloads(self, ):
+ def stopAllDownloads(self,):
self.send_stopAllDownloads()
self.recv_stopAllDownloads()
- def send_stopAllDownloads(self, ):
+ def send_stopAllDownloads(self,):
self._oprot.writeMessageBegin('stopAllDownloads', TMessageType.CALL, self._seqid)
args = stopAllDownloads_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_stopAllDownloads(self, ):
+ def recv_stopAllDownloads(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1677,7 +1677,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_stopDownloads(self, ):
+ def recv_stopDownloads(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1707,7 +1707,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_setPackageName(self, ):
+ def recv_setPackageName(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1737,7 +1737,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_movePackage(self, ):
+ def recv_movePackage(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1767,7 +1767,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_moveFiles(self, ):
+ def recv_moveFiles(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1797,7 +1797,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_orderPackage(self, ):
+ def recv_orderPackage(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1827,7 +1827,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_orderFile(self, ):
+ def recv_orderFile(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1857,7 +1857,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_setPackageData(self, ):
+ def recv_setPackageData(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1871,18 +1871,18 @@ class Client(Iface):
raise result.e
return
- def deleteFinished(self, ):
+ def deleteFinished(self,):
self.send_deleteFinished()
return self.recv_deleteFinished()
- def send_deleteFinished(self, ):
+ def send_deleteFinished(self,):
self._oprot.writeMessageBegin('deleteFinished', TMessageType.CALL, self._seqid)
args = deleteFinished_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_deleteFinished(self, ):
+ def recv_deleteFinished(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1896,18 +1896,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "deleteFinished failed: unknown result");
- def restartFailed(self, ):
+ def restartFailed(self,):
self.send_restartFailed()
self.recv_restartFailed()
- def send_restartFailed(self, ):
+ def send_restartFailed(self,):
self._oprot.writeMessageBegin('restartFailed', TMessageType.CALL, self._seqid)
args = restartFailed_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_restartFailed(self, ):
+ def recv_restartFailed(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1935,7 +1935,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getEvents(self, ):
+ def recv_getEvents(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1965,7 +1965,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getAccounts(self, ):
+ def recv_getAccounts(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -1979,18 +1979,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "getAccounts failed: unknown result");
- def getAccountTypes(self, ):
+ def getAccountTypes(self,):
self.send_getAccountTypes()
return self.recv_getAccountTypes()
- def send_getAccountTypes(self, ):
+ def send_getAccountTypes(self,):
self._oprot.writeMessageBegin('getAccountTypes', TMessageType.CALL, self._seqid)
args = getAccountTypes_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getAccountTypes(self, ):
+ def recv_getAccountTypes(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2026,7 +2026,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_updateAccount(self, ):
+ def recv_updateAccount(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2056,7 +2056,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_removeAccount(self, ):
+ def recv_removeAccount(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2086,7 +2086,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_login(self, ):
+ def recv_login(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2118,7 +2118,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getUserData(self, ):
+ def recv_getUserData(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2132,18 +2132,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "getUserData failed: unknown result");
- def getAllUserData(self, ):
+ def getAllUserData(self,):
self.send_getAllUserData()
return self.recv_getAllUserData()
- def send_getAllUserData(self, ):
+ def send_getAllUserData(self,):
self._oprot.writeMessageBegin('getAllUserData', TMessageType.CALL, self._seqid)
args = getAllUserData_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getAllUserData(self, ):
+ def recv_getAllUserData(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2157,18 +2157,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "getAllUserData failed: unknown result");
- def getServices(self, ):
+ def getServices(self,):
self.send_getServices()
return self.recv_getServices()
- def send_getServices(self, ):
+ def send_getServices(self,):
self._oprot.writeMessageBegin('getServices', TMessageType.CALL, self._seqid)
args = getServices_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getServices(self, ):
+ def recv_getServices(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2200,7 +2200,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_hasService(self, ):
+ def recv_hasService(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2230,7 +2230,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_call(self, ):
+ def recv_call(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2248,18 +2248,18 @@ class Client(Iface):
raise result.e
raise TApplicationException(TApplicationException.MISSING_RESULT, "call failed: unknown result");
- def getAllInfo(self, ):
+ def getAllInfo(self,):
self.send_getAllInfo()
return self.recv_getAllInfo()
- def send_getAllInfo(self, ):
+ def send_getAllInfo(self,):
self._oprot.writeMessageBegin('getAllInfo', TMessageType.CALL, self._seqid)
args = getAllInfo_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getAllInfo(self, ):
+ def recv_getAllInfo(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2289,7 +2289,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getInfoByPlugin(self, ):
+ def recv_getInfoByPlugin(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2303,18 +2303,18 @@ class Client(Iface):
return result.success
raise TApplicationException(TApplicationException.MISSING_RESULT, "getInfoByPlugin failed: unknown result");
- def isCaptchaWaiting(self, ):
+ def isCaptchaWaiting(self,):
self.send_isCaptchaWaiting()
return self.recv_isCaptchaWaiting()
- def send_isCaptchaWaiting(self, ):
+ def send_isCaptchaWaiting(self,):
self._oprot.writeMessageBegin('isCaptchaWaiting', TMessageType.CALL, self._seqid)
args = isCaptchaWaiting_args()
args.write(self._oprot)
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_isCaptchaWaiting(self, ):
+ def recv_isCaptchaWaiting(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2344,7 +2344,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getCaptchaTask(self, ):
+ def recv_getCaptchaTask(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2374,7 +2374,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_getCaptchaTaskStatus(self, ):
+ def recv_getCaptchaTaskStatus(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -2406,7 +2406,7 @@ class Client(Iface):
self._oprot.writeMessageEnd()
self._oprot.trans.flush()
- def recv_setCaptchaResult(self, ):
+ def recv_setCaptchaResult(self,):
(fname, mtype, rseqid) = self._iprot.readMessageBegin()
if mtype == TMessageType.EXCEPTION:
x = TApplicationException()
@@ -3307,7 +3307,7 @@ class getConfigValue_args(TBase):
- section
"""
- __slots__ = [
+ __slots__ = [
'category',
'option',
'section',
@@ -3315,9 +3315,9 @@ class getConfigValue_args(TBase):
thrift_spec = (
None, # 0
- (1, TType.STRING, 'category', None, None, ), # 1
- (2, TType.STRING, 'option', None, None, ), # 2
- (3, TType.STRING, 'section', None, None, ), # 3
+ (1, TType.STRING, 'category', None, None,), # 1
+ (2, TType.STRING, 'option', None, None,), # 2
+ (3, TType.STRING, 'section', None, None,), # 3
)
def __init__(self, category=None, option=None, section=None,):
@@ -3332,12 +3332,12 @@ class getConfigValue_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.STRING, 'success', None, None, ), # 0
+ (0, TType.STRING, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -3353,7 +3353,7 @@ class setConfigValue_args(TBase):
- section
"""
- __slots__ = [
+ __slots__ = [
'category',
'option',
'value',
@@ -3362,10 +3362,10 @@ class setConfigValue_args(TBase):
thrift_spec = (
None, # 0
- (1, TType.STRING, 'category', None, None, ), # 1
- (2, TType.STRING, 'option', None, None, ), # 2
- (3, TType.STRING, 'value', None, None, ), # 3
- (4, TType.STRING, 'section', None, None, ), # 4
+ (1, TType.STRING, 'category', None, None,), # 1
+ (2, TType.STRING, 'option', None, None,), # 2
+ (3, TType.STRING, 'value', None, None,), # 3
+ (4, TType.STRING, 'section', None, None,), # 4
)
def __init__(self, category=None, option=None, value=None, section=None,):
@@ -3377,7 +3377,7 @@ class setConfigValue_args(TBase):
class setConfigValue_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3386,7 +3386,7 @@ class setConfigValue_result(TBase):
class getConfig_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3399,12 +3399,12 @@ class getConfig_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.STRUCT,(ConfigSection, ConfigSection.thrift_spec)), None, ), # 0
+ (0, TType.MAP, 'success', (TType.STRING, None, TType.STRUCT, (ConfigSection, ConfigSection.thrift_spec)), None,), # 0
)
def __init__(self, success=None,):
@@ -3413,7 +3413,7 @@ class getConfig_result(TBase):
class getPluginConfig_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3426,12 +3426,12 @@ class getPluginConfig_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.STRUCT,(ConfigSection, ConfigSection.thrift_spec)), None, ), # 0
+ (0, TType.MAP, 'success', (TType.STRING, None, TType.STRUCT, (ConfigSection, ConfigSection.thrift_spec)), None,), # 0
)
def __init__(self, success=None,):
@@ -3440,7 +3440,7 @@ class getPluginConfig_result(TBase):
class pauseServer_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3449,7 +3449,7 @@ class pauseServer_args(TBase):
class pauseServer_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3458,7 +3458,7 @@ class pauseServer_result(TBase):
class unpauseServer_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3467,7 +3467,7 @@ class unpauseServer_args(TBase):
class unpauseServer_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3476,7 +3476,7 @@ class unpauseServer_result(TBase):
class togglePause_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3489,12 +3489,12 @@ class togglePause_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
+ (0, TType.BOOL, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -3503,7 +3503,7 @@ class togglePause_result(TBase):
class statusServer_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3516,12 +3516,12 @@ class statusServer_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.STRUCT, 'success', (ServerStatus, ServerStatus.thrift_spec), None, ), # 0
+ (0, TType.STRUCT, 'success', (ServerStatus, ServerStatus.thrift_spec), None,), # 0
)
def __init__(self, success=None,):
@@ -3530,7 +3530,7 @@ class statusServer_result(TBase):
class freeSpace_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3543,12 +3543,12 @@ class freeSpace_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.I64, 'success', None, None, ), # 0
+ (0, TType.I64, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -3557,7 +3557,7 @@ class freeSpace_result(TBase):
class getServerVersion_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3570,12 +3570,12 @@ class getServerVersion_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.STRING, 'success', None, None, ), # 0
+ (0, TType.STRING, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -3584,7 +3584,7 @@ class getServerVersion_result(TBase):
class kill_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3593,7 +3593,7 @@ class kill_args(TBase):
class kill_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3602,7 +3602,7 @@ class kill_result(TBase):
class restart_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3611,7 +3611,7 @@ class restart_args(TBase):
class restart_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3624,13 +3624,13 @@ class getLog_args(TBase):
- offset
"""
- __slots__ = [
+ __slots__ = [
'offset',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'offset', None, None, ), # 1
+ (1, TType.I32, 'offset', None, None,), # 1
)
def __init__(self, offset=None,):
@@ -3643,12 +3643,12 @@ class getLog_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRING,None), None, ), # 0
+ (0, TType.LIST, 'success', (TType.STRING, None), None,), # 0
)
def __init__(self, success=None,):
@@ -3657,7 +3657,7 @@ class getLog_result(TBase):
class isTimeDownload_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3670,12 +3670,12 @@ class isTimeDownload_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
+ (0, TType.BOOL, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -3684,7 +3684,7 @@ class isTimeDownload_result(TBase):
class isTimeReconnect_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3697,12 +3697,12 @@ class isTimeReconnect_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
+ (0, TType.BOOL, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -3711,7 +3711,7 @@ class isTimeReconnect_result(TBase):
class toggleReconnect_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3724,12 +3724,12 @@ class toggleReconnect_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
+ (0, TType.BOOL, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -3742,13 +3742,13 @@ class generatePackages_args(TBase):
- links
"""
- __slots__ = [
+ __slots__ = [
'links',
]
thrift_spec = (
None, # 0
- (1, TType.LIST, 'links', (TType.STRING,None), None, ), # 1
+ (1, TType.LIST, 'links', (TType.STRING, None), None,), # 1
)
def __init__(self, links=None,):
@@ -3761,12 +3761,12 @@ class generatePackages_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.LIST,(TType.STRING,None)), None, ), # 0
+ (0, TType.MAP, 'success', (TType.STRING, None, TType.LIST, (TType.STRING, None)), None,), # 0
)
def __init__(self, success=None,):
@@ -3779,13 +3779,13 @@ class checkURLs_args(TBase):
- urls
"""
- __slots__ = [
+ __slots__ = [
'urls',
]
thrift_spec = (
None, # 0
- (1, TType.LIST, 'urls', (TType.STRING,None), None, ), # 1
+ (1, TType.LIST, 'urls', (TType.STRING, None), None,), # 1
)
def __init__(self, urls=None,):
@@ -3798,12 +3798,12 @@ class checkURLs_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.LIST,(TType.STRING,None)), None, ), # 0
+ (0, TType.MAP, 'success', (TType.STRING, None, TType.LIST, (TType.STRING, None)), None,), # 0
)
def __init__(self, success=None,):
@@ -3817,15 +3817,15 @@ class parseURLs_args(TBase):
- url
"""
- __slots__ = [
+ __slots__ = [
'html',
'url',
]
thrift_spec = (
None, # 0
- (1, TType.STRING, 'html', None, None, ), # 1
- (2, TType.STRING, 'url', None, None, ), # 2
+ (1, TType.STRING, 'html', None, None,), # 1
+ (2, TType.STRING, 'url', None, None,), # 2
)
def __init__(self, html=None, url=None,):
@@ -3839,12 +3839,12 @@ class parseURLs_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.LIST,(TType.STRING,None)), None, ), # 0
+ (0, TType.MAP, 'success', (TType.STRING, None, TType.LIST, (TType.STRING, None)), None,), # 0
)
def __init__(self, success=None,):
@@ -3857,13 +3857,13 @@ class checkOnlineStatus_args(TBase):
- urls
"""
- __slots__ = [
+ __slots__ = [
'urls',
]
thrift_spec = (
None, # 0
- (1, TType.LIST, 'urls', (TType.STRING,None), None, ), # 1
+ (1, TType.LIST, 'urls', (TType.STRING, None), None,), # 1
)
def __init__(self, urls=None,):
@@ -3876,12 +3876,12 @@ class checkOnlineStatus_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.STRUCT, 'success', (OnlineCheck, OnlineCheck.thrift_spec), None, ), # 0
+ (0, TType.STRUCT, 'success', (OnlineCheck, OnlineCheck.thrift_spec), None,), # 0
)
def __init__(self, success=None,):
@@ -3896,7 +3896,7 @@ class checkOnlineStatusContainer_args(TBase):
- data
"""
- __slots__ = [
+ __slots__ = [
'urls',
'filename',
'data',
@@ -3904,9 +3904,9 @@ class checkOnlineStatusContainer_args(TBase):
thrift_spec = (
None, # 0
- (1, TType.LIST, 'urls', (TType.STRING,None), None, ), # 1
- (2, TType.STRING, 'filename', None, None, ), # 2
- (3, TType.STRING, 'data', None, None, ), # 3
+ (1, TType.LIST, 'urls', (TType.STRING, None), None,), # 1
+ (2, TType.STRING, 'filename', None, None,), # 2
+ (3, TType.STRING, 'data', None, None,), # 3
)
def __init__(self, urls=None, filename=None, data=None,):
@@ -3921,12 +3921,12 @@ class checkOnlineStatusContainer_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.STRUCT, 'success', (OnlineCheck, OnlineCheck.thrift_spec), None, ), # 0
+ (0, TType.STRUCT, 'success', (OnlineCheck, OnlineCheck.thrift_spec), None,), # 0
)
def __init__(self, success=None,):
@@ -3939,13 +3939,13 @@ class pollResults_args(TBase):
- rid
"""
- __slots__ = [
+ __slots__ = [
'rid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'rid', None, None, ), # 1
+ (1, TType.I32, 'rid', None, None,), # 1
)
def __init__(self, rid=None,):
@@ -3958,12 +3958,12 @@ class pollResults_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.STRUCT, 'success', (OnlineCheck, OnlineCheck.thrift_spec), None, ), # 0
+ (0, TType.STRUCT, 'success', (OnlineCheck, OnlineCheck.thrift_spec), None,), # 0
)
def __init__(self, success=None,):
@@ -3972,7 +3972,7 @@ class pollResults_result(TBase):
class statusDownloads_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -3985,12 +3985,12 @@ class statusDownloads_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(DownloadInfo, DownloadInfo.thrift_spec)), None, ), # 0
+ (0, TType.LIST, 'success', (TType.STRUCT, (DownloadInfo, DownloadInfo.thrift_spec)), None,), # 0
)
def __init__(self, success=None,):
@@ -4003,13 +4003,13 @@ class getPackageData_args(TBase):
- pid
"""
- __slots__ = [
+ __slots__ = [
'pid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
+ (1, TType.I32, 'pid', None, None,), # 1
)
def __init__(self, pid=None,):
@@ -4023,14 +4023,14 @@ class getPackageData_result(TBase):
- e
"""
- __slots__ = [
+ __slots__ = [
'success',
'e',
]
thrift_spec = (
- (0, TType.STRUCT, 'success', (PackageData, PackageData.thrift_spec), None, ), # 0
- (1, TType.STRUCT, 'e', (PackageDoesNotExists, PackageDoesNotExists.thrift_spec), None, ), # 1
+ (0, TType.STRUCT, 'success', (PackageData, PackageData.thrift_spec), None,), # 0
+ (1, TType.STRUCT, 'e', (PackageDoesNotExists, PackageDoesNotExists.thrift_spec), None,), # 1
)
def __init__(self, success=None, e=None,):
@@ -4044,13 +4044,13 @@ class getPackageInfo_args(TBase):
- pid
"""
- __slots__ = [
+ __slots__ = [
'pid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
+ (1, TType.I32, 'pid', None, None,), # 1
)
def __init__(self, pid=None,):
@@ -4064,14 +4064,14 @@ class getPackageInfo_result(TBase):
- e
"""
- __slots__ = [
+ __slots__ = [
'success',
'e',
]
thrift_spec = (
- (0, TType.STRUCT, 'success', (PackageData, PackageData.thrift_spec), None, ), # 0
- (1, TType.STRUCT, 'e', (PackageDoesNotExists, PackageDoesNotExists.thrift_spec), None, ), # 1
+ (0, TType.STRUCT, 'success', (PackageData, PackageData.thrift_spec), None,), # 0
+ (1, TType.STRUCT, 'e', (PackageDoesNotExists, PackageDoesNotExists.thrift_spec), None,), # 1
)
def __init__(self, success=None, e=None,):
@@ -4085,13 +4085,13 @@ class getFileData_args(TBase):
- fid
"""
- __slots__ = [
+ __slots__ = [
'fid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
+ (1, TType.I32, 'fid', None, None,), # 1
)
def __init__(self, fid=None,):
@@ -4105,14 +4105,14 @@ class getFileData_result(TBase):
- e
"""
- __slots__ = [
+ __slots__ = [
'success',
'e',
]
thrift_spec = (
- (0, TType.STRUCT, 'success', (FileData, FileData.thrift_spec), None, ), # 0
- (1, TType.STRUCT, 'e', (FileDoesNotExists, FileDoesNotExists.thrift_spec), None, ), # 1
+ (0, TType.STRUCT, 'success', (FileData, FileData.thrift_spec), None,), # 0
+ (1, TType.STRUCT, 'e', (FileDoesNotExists, FileDoesNotExists.thrift_spec), None,), # 1
)
def __init__(self, success=None, e=None,):
@@ -4122,7 +4122,7 @@ class getFileData_result(TBase):
class getQueue_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4135,12 +4135,12 @@ class getQueue_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(PackageData, PackageData.thrift_spec)), None, ), # 0
+ (0, TType.LIST, 'success', (TType.STRUCT, (PackageData, PackageData.thrift_spec)), None,), # 0
)
def __init__(self, success=None,):
@@ -4149,7 +4149,7 @@ class getQueue_result(TBase):
class getCollector_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4162,12 +4162,12 @@ class getCollector_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(PackageData, PackageData.thrift_spec)), None, ), # 0
+ (0, TType.LIST, 'success', (TType.STRUCT, (PackageData, PackageData.thrift_spec)), None,), # 0
)
def __init__(self, success=None,):
@@ -4176,7 +4176,7 @@ class getCollector_result(TBase):
class getQueueData_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4189,12 +4189,12 @@ class getQueueData_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(PackageData, PackageData.thrift_spec)), None, ), # 0
+ (0, TType.LIST, 'success', (TType.STRUCT, (PackageData, PackageData.thrift_spec)), None,), # 0
)
def __init__(self, success=None,):
@@ -4203,7 +4203,7 @@ class getQueueData_result(TBase):
class getCollectorData_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4216,12 +4216,12 @@ class getCollectorData_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(PackageData, PackageData.thrift_spec)), None, ), # 0
+ (0, TType.LIST, 'success', (TType.STRUCT, (PackageData, PackageData.thrift_spec)), None,), # 0
)
def __init__(self, success=None,):
@@ -4234,13 +4234,13 @@ class getPackageOrder_args(TBase):
- destination
"""
- __slots__ = [
+ __slots__ = [
'destination',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'destination', None, None, ), # 1
+ (1, TType.I32, 'destination', None, None,), # 1
)
def __init__(self, destination=None,):
@@ -4253,12 +4253,12 @@ class getPackageOrder_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.MAP, 'success', (TType.I16,None,TType.I32,None), None, ), # 0
+ (0, TType.MAP, 'success', (TType.I16, None, TType.I32, None), None,), # 0
)
def __init__(self, success=None,):
@@ -4271,13 +4271,13 @@ class getFileOrder_args(TBase):
- pid
"""
- __slots__ = [
+ __slots__ = [
'pid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
+ (1, TType.I32, 'pid', None, None,), # 1
)
def __init__(self, pid=None,):
@@ -4290,12 +4290,12 @@ class getFileOrder_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.MAP, 'success', (TType.I16,None,TType.I32,None), None, ), # 0
+ (0, TType.MAP, 'success', (TType.I16, None, TType.I32, None), None,), # 0
)
def __init__(self, success=None,):
@@ -4309,15 +4309,15 @@ class generateAndAddPackages_args(TBase):
- dest
"""
- __slots__ = [
+ __slots__ = [
'links',
'dest',
]
thrift_spec = (
None, # 0
- (1, TType.LIST, 'links', (TType.STRING,None), None, ), # 1
- (2, TType.I32, 'dest', None, None, ), # 2
+ (1, TType.LIST, 'links', (TType.STRING, None), None,), # 1
+ (2, TType.I32, 'dest', None, None,), # 2
)
def __init__(self, links=None, dest=None,):
@@ -4331,12 +4331,12 @@ class generateAndAddPackages_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.LIST, 'success', (TType.I32,None), None, ), # 0
+ (0, TType.LIST, 'success', (TType.I32, None), None,), # 0
)
def __init__(self, success=None,):
@@ -4351,7 +4351,7 @@ class addPackage_args(TBase):
- dest
"""
- __slots__ = [
+ __slots__ = [
'name',
'links',
'dest',
@@ -4359,9 +4359,9 @@ class addPackage_args(TBase):
thrift_spec = (
None, # 0
- (1, TType.STRING, 'name', None, None, ), # 1
- (2, TType.LIST, 'links', (TType.STRING,None), None, ), # 2
- (3, TType.I32, 'dest', None, None, ), # 3
+ (1, TType.STRING, 'name', None, None,), # 1
+ (2, TType.LIST, 'links', (TType.STRING, None), None,), # 2
+ (3, TType.I32, 'dest', None, None,), # 3
)
def __init__(self, name=None, links=None, dest=None,):
@@ -4376,12 +4376,12 @@ class addPackage_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.I32, 'success', None, None, ), # 0
+ (0, TType.I32, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -4395,15 +4395,15 @@ class addFiles_args(TBase):
- links
"""
- __slots__ = [
+ __slots__ = [
'pid',
'links',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- (2, TType.LIST, 'links', (TType.STRING,None), None, ), # 2
+ (1, TType.I32, 'pid', None, None,), # 1
+ (2, TType.LIST, 'links', (TType.STRING, None), None,), # 2
)
def __init__(self, pid=None, links=None,):
@@ -4413,7 +4413,7 @@ class addFiles_args(TBase):
class addFiles_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4427,15 +4427,15 @@ class uploadContainer_args(TBase):
- data
"""
- __slots__ = [
+ __slots__ = [
'filename',
'data',
]
thrift_spec = (
None, # 0
- (1, TType.STRING, 'filename', None, None, ), # 1
- (2, TType.STRING, 'data', None, None, ), # 2
+ (1, TType.STRING, 'filename', None, None,), # 1
+ (2, TType.STRING, 'data', None, None,), # 2
)
def __init__(self, filename=None, data=None,):
@@ -4445,7 +4445,7 @@ class uploadContainer_args(TBase):
class uploadContainer_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4458,13 +4458,13 @@ class deleteFiles_args(TBase):
- fids
"""
- __slots__ = [
+ __slots__ = [
'fids',
]
thrift_spec = (
None, # 0
- (1, TType.LIST, 'fids', (TType.I32,None), None, ), # 1
+ (1, TType.LIST, 'fids', (TType.I32, None), None,), # 1
)
def __init__(self, fids=None,):
@@ -4473,7 +4473,7 @@ class deleteFiles_args(TBase):
class deleteFiles_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4486,13 +4486,13 @@ class deletePackages_args(TBase):
- pids
"""
- __slots__ = [
+ __slots__ = [
'pids',
]
thrift_spec = (
None, # 0
- (1, TType.LIST, 'pids', (TType.I32,None), None, ), # 1
+ (1, TType.LIST, 'pids', (TType.I32, None), None,), # 1
)
def __init__(self, pids=None,):
@@ -4501,7 +4501,7 @@ class deletePackages_args(TBase):
class deletePackages_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4514,13 +4514,13 @@ class pushToQueue_args(TBase):
- pid
"""
- __slots__ = [
+ __slots__ = [
'pid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
+ (1, TType.I32, 'pid', None, None,), # 1
)
def __init__(self, pid=None,):
@@ -4529,7 +4529,7 @@ class pushToQueue_args(TBase):
class pushToQueue_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4542,13 +4542,13 @@ class pullFromQueue_args(TBase):
- pid
"""
- __slots__ = [
+ __slots__ = [
'pid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
+ (1, TType.I32, 'pid', None, None,), # 1
)
def __init__(self, pid=None,):
@@ -4557,7 +4557,7 @@ class pullFromQueue_args(TBase):
class pullFromQueue_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4570,13 +4570,13 @@ class restartPackage_args(TBase):
- pid
"""
- __slots__ = [
+ __slots__ = [
'pid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
+ (1, TType.I32, 'pid', None, None,), # 1
)
def __init__(self, pid=None,):
@@ -4585,7 +4585,7 @@ class restartPackage_args(TBase):
class restartPackage_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4598,13 +4598,13 @@ class restartFile_args(TBase):
- fid
"""
- __slots__ = [
+ __slots__ = [
'fid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
+ (1, TType.I32, 'fid', None, None,), # 1
)
def __init__(self, fid=None,):
@@ -4613,7 +4613,7 @@ class restartFile_args(TBase):
class restartFile_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4626,13 +4626,13 @@ class recheckPackage_args(TBase):
- pid
"""
- __slots__ = [
+ __slots__ = [
'pid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
+ (1, TType.I32, 'pid', None, None,), # 1
)
def __init__(self, pid=None,):
@@ -4641,7 +4641,7 @@ class recheckPackage_args(TBase):
class recheckPackage_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4650,7 +4650,7 @@ class recheckPackage_result(TBase):
class stopAllDownloads_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4659,7 +4659,7 @@ class stopAllDownloads_args(TBase):
class stopAllDownloads_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4672,13 +4672,13 @@ class stopDownloads_args(TBase):
- fids
"""
- __slots__ = [
+ __slots__ = [
'fids',
]
thrift_spec = (
None, # 0
- (1, TType.LIST, 'fids', (TType.I32,None), None, ), # 1
+ (1, TType.LIST, 'fids', (TType.I32, None), None,), # 1
)
def __init__(self, fids=None,):
@@ -4687,7 +4687,7 @@ class stopDownloads_args(TBase):
class stopDownloads_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4701,15 +4701,15 @@ class setPackageName_args(TBase):
- name
"""
- __slots__ = [
+ __slots__ = [
'pid',
'name',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- (2, TType.STRING, 'name', None, None, ), # 2
+ (1, TType.I32, 'pid', None, None,), # 1
+ (2, TType.STRING, 'name', None, None,), # 2
)
def __init__(self, pid=None, name=None,):
@@ -4719,7 +4719,7 @@ class setPackageName_args(TBase):
class setPackageName_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4733,15 +4733,15 @@ class movePackage_args(TBase):
- pid
"""
- __slots__ = [
+ __slots__ = [
'destination',
'pid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'destination', None, None, ), # 1
- (2, TType.I32, 'pid', None, None, ), # 2
+ (1, TType.I32, 'destination', None, None,), # 1
+ (2, TType.I32, 'pid', None, None,), # 2
)
def __init__(self, destination=None, pid=None,):
@@ -4751,7 +4751,7 @@ class movePackage_args(TBase):
class movePackage_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4765,15 +4765,15 @@ class moveFiles_args(TBase):
- pid
"""
- __slots__ = [
+ __slots__ = [
'fids',
'pid',
]
thrift_spec = (
None, # 0
- (1, TType.LIST, 'fids', (TType.I32,None), None, ), # 1
- (2, TType.I32, 'pid', None, None, ), # 2
+ (1, TType.LIST, 'fids', (TType.I32, None), None,), # 1
+ (2, TType.I32, 'pid', None, None,), # 2
)
def __init__(self, fids=None, pid=None,):
@@ -4783,7 +4783,7 @@ class moveFiles_args(TBase):
class moveFiles_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4797,15 +4797,15 @@ class orderPackage_args(TBase):
- position
"""
- __slots__ = [
+ __slots__ = [
'pid',
'position',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- (2, TType.I16, 'position', None, None, ), # 2
+ (1, TType.I32, 'pid', None, None,), # 1
+ (2, TType.I16, 'position', None, None,), # 2
)
def __init__(self, pid=None, position=None,):
@@ -4815,7 +4815,7 @@ class orderPackage_args(TBase):
class orderPackage_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4829,15 +4829,15 @@ class orderFile_args(TBase):
- position
"""
- __slots__ = [
+ __slots__ = [
'fid',
'position',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
- (2, TType.I16, 'position', None, None, ), # 2
+ (1, TType.I32, 'fid', None, None,), # 1
+ (2, TType.I16, 'position', None, None,), # 2
)
def __init__(self, fid=None, position=None,):
@@ -4847,7 +4847,7 @@ class orderFile_args(TBase):
class orderFile_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4861,15 +4861,15 @@ class setPackageData_args(TBase):
- data
"""
- __slots__ = [
+ __slots__ = [
'pid',
'data',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- (2, TType.MAP, 'data', (TType.STRING,None,TType.STRING,None), None, ), # 2
+ (1, TType.I32, 'pid', None, None,), # 1
+ (2, TType.MAP, 'data', (TType.STRING, None, TType.STRING, None), None,), # 2
)
def __init__(self, pid=None, data=None,):
@@ -4883,13 +4883,13 @@ class setPackageData_result(TBase):
- e
"""
- __slots__ = [
+ __slots__ = [
'e',
]
thrift_spec = (
None, # 0
- (1, TType.STRUCT, 'e', (PackageDoesNotExists, PackageDoesNotExists.thrift_spec), None, ), # 1
+ (1, TType.STRUCT, 'e', (PackageDoesNotExists, PackageDoesNotExists.thrift_spec), None,), # 1
)
def __init__(self, e=None,):
@@ -4898,7 +4898,7 @@ class setPackageData_result(TBase):
class deleteFinished_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4911,12 +4911,12 @@ class deleteFinished_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.LIST, 'success', (TType.I32,None), None, ), # 0
+ (0, TType.LIST, 'success', (TType.I32, None), None,), # 0
)
def __init__(self, success=None,):
@@ -4925,7 +4925,7 @@ class deleteFinished_result(TBase):
class restartFailed_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4934,7 +4934,7 @@ class restartFailed_args(TBase):
class restartFailed_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -4947,13 +4947,13 @@ class getEvents_args(TBase):
- uuid
"""
- __slots__ = [
+ __slots__ = [
'uuid',
]
thrift_spec = (
None, # 0
- (1, TType.STRING, 'uuid', None, None, ), # 1
+ (1, TType.STRING, 'uuid', None, None,), # 1
)
def __init__(self, uuid=None,):
@@ -4966,12 +4966,12 @@ class getEvents_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(EventInfo, EventInfo.thrift_spec)), None, ), # 0
+ (0, TType.LIST, 'success', (TType.STRUCT, (EventInfo, EventInfo.thrift_spec)), None,), # 0
)
def __init__(self, success=None,):
@@ -4984,13 +4984,13 @@ class getAccounts_args(TBase):
- refresh
"""
- __slots__ = [
+ __slots__ = [
'refresh',
]
thrift_spec = (
None, # 0
- (1, TType.BOOL, 'refresh', None, None, ), # 1
+ (1, TType.BOOL, 'refresh', None, None,), # 1
)
def __init__(self, refresh=None,):
@@ -5003,12 +5003,12 @@ class getAccounts_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(AccountInfo, AccountInfo.thrift_spec)), None, ), # 0
+ (0, TType.LIST, 'success', (TType.STRUCT, (AccountInfo, AccountInfo.thrift_spec)), None,), # 0
)
def __init__(self, success=None,):
@@ -5017,7 +5017,7 @@ class getAccounts_result(TBase):
class getAccountTypes_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -5030,12 +5030,12 @@ class getAccountTypes_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRING,None), None, ), # 0
+ (0, TType.LIST, 'success', (TType.STRING, None), None,), # 0
)
def __init__(self, success=None,):
@@ -5051,7 +5051,7 @@ class updateAccount_args(TBase):
- options
"""
- __slots__ = [
+ __slots__ = [
'plugin',
'account',
'password',
@@ -5060,10 +5060,10 @@ class updateAccount_args(TBase):
thrift_spec = (
None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
- (2, TType.STRING, 'account', None, None, ), # 2
- (3, TType.STRING, 'password', None, None, ), # 3
- (4, TType.MAP, 'options', (TType.STRING,None,TType.STRING,None), None, ), # 4
+ (1, TType.STRING, 'plugin', None, None,), # 1
+ (2, TType.STRING, 'account', None, None,), # 2
+ (3, TType.STRING, 'password', None, None,), # 3
+ (4, TType.MAP, 'options', (TType.STRING, None, TType.STRING, None), None,), # 4
)
def __init__(self, plugin=None, account=None, password=None, options=None,):
@@ -5075,7 +5075,7 @@ class updateAccount_args(TBase):
class updateAccount_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -5089,15 +5089,15 @@ class removeAccount_args(TBase):
- account
"""
- __slots__ = [
+ __slots__ = [
'plugin',
'account',
]
thrift_spec = (
None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
- (2, TType.STRING, 'account', None, None, ), # 2
+ (1, TType.STRING, 'plugin', None, None,), # 1
+ (2, TType.STRING, 'account', None, None,), # 2
)
def __init__(self, plugin=None, account=None,):
@@ -5107,7 +5107,7 @@ class removeAccount_args(TBase):
class removeAccount_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -5121,15 +5121,15 @@ class login_args(TBase):
- password
"""
- __slots__ = [
+ __slots__ = [
'username',
'password',
]
thrift_spec = (
None, # 0
- (1, TType.STRING, 'username', None, None, ), # 1
- (2, TType.STRING, 'password', None, None, ), # 2
+ (1, TType.STRING, 'username', None, None,), # 1
+ (2, TType.STRING, 'password', None, None,), # 2
)
def __init__(self, username=None, password=None,):
@@ -5143,12 +5143,12 @@ class login_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
+ (0, TType.BOOL, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -5162,15 +5162,15 @@ class getUserData_args(TBase):
- password
"""
- __slots__ = [
+ __slots__ = [
'username',
'password',
]
thrift_spec = (
None, # 0
- (1, TType.STRING, 'username', None, None, ), # 1
- (2, TType.STRING, 'password', None, None, ), # 2
+ (1, TType.STRING, 'username', None, None,), # 1
+ (2, TType.STRING, 'password', None, None,), # 2
)
def __init__(self, username=None, password=None,):
@@ -5184,12 +5184,12 @@ class getUserData_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.STRUCT, 'success', (UserData, UserData.thrift_spec), None, ), # 0
+ (0, TType.STRUCT, 'success', (UserData, UserData.thrift_spec), None,), # 0
)
def __init__(self, success=None,):
@@ -5198,7 +5198,7 @@ class getUserData_result(TBase):
class getAllUserData_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -5211,12 +5211,12 @@ class getAllUserData_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.STRUCT,(UserData, UserData.thrift_spec)), None, ), # 0
+ (0, TType.MAP, 'success', (TType.STRING, None, TType.STRUCT, (UserData, UserData.thrift_spec)), None,), # 0
)
def __init__(self, success=None,):
@@ -5225,7 +5225,7 @@ class getAllUserData_result(TBase):
class getServices_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -5238,12 +5238,12 @@ class getServices_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.MAP,(TType.STRING,None,TType.STRING,None)), None, ), # 0
+ (0, TType.MAP, 'success', (TType.STRING, None, TType.MAP, (TType.STRING, None, TType.STRING, None)), None,), # 0
)
def __init__(self, success=None,):
@@ -5257,15 +5257,15 @@ class hasService_args(TBase):
- func
"""
- __slots__ = [
+ __slots__ = [
'plugin',
'func',
]
thrift_spec = (
None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
- (2, TType.STRING, 'func', None, None, ), # 2
+ (1, TType.STRING, 'plugin', None, None,), # 1
+ (2, TType.STRING, 'func', None, None,), # 2
)
def __init__(self, plugin=None, func=None,):
@@ -5279,12 +5279,12 @@ class hasService_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
+ (0, TType.BOOL, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -5297,13 +5297,13 @@ class call_args(TBase):
- info
"""
- __slots__ = [
+ __slots__ = [
'info',
]
thrift_spec = (
None, # 0
- (1, TType.STRUCT, 'info', (ServiceCall, ServiceCall.thrift_spec), None, ), # 1
+ (1, TType.STRUCT, 'info', (ServiceCall, ServiceCall.thrift_spec), None,), # 1
)
def __init__(self, info=None,):
@@ -5318,16 +5318,16 @@ class call_result(TBase):
- e
"""
- __slots__ = [
+ __slots__ = [
'success',
'ex',
'e',
]
thrift_spec = (
- (0, TType.STRING, 'success', None, None, ), # 0
- (1, TType.STRUCT, 'ex', (ServiceDoesNotExists, ServiceDoesNotExists.thrift_spec), None, ), # 1
- (2, TType.STRUCT, 'e', (ServiceException, ServiceException.thrift_spec), None, ), # 2
+ (0, TType.STRING, 'success', None, None,), # 0
+ (1, TType.STRUCT, 'ex', (ServiceDoesNotExists, ServiceDoesNotExists.thrift_spec), None,), # 1
+ (2, TType.STRUCT, 'e', (ServiceException, ServiceException.thrift_spec), None,), # 2
)
def __init__(self, success=None, ex=None, e=None,):
@@ -5338,7 +5338,7 @@ class call_result(TBase):
class getAllInfo_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -5351,12 +5351,12 @@ class getAllInfo_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.MAP,(TType.STRING,None,TType.STRING,None)), None, ), # 0
+ (0, TType.MAP, 'success', (TType.STRING, None, TType.MAP, (TType.STRING, None, TType.STRING, None)), None,), # 0
)
def __init__(self, success=None,):
@@ -5369,13 +5369,13 @@ class getInfoByPlugin_args(TBase):
- plugin
"""
- __slots__ = [
+ __slots__ = [
'plugin',
]
thrift_spec = (
None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
+ (1, TType.STRING, 'plugin', None, None,), # 1
)
def __init__(self, plugin=None,):
@@ -5388,12 +5388,12 @@ class getInfoByPlugin_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.STRING,None), None, ), # 0
+ (0, TType.MAP, 'success', (TType.STRING, None, TType.STRING, None), None,), # 0
)
def __init__(self, success=None,):
@@ -5402,7 +5402,7 @@ class getInfoByPlugin_result(TBase):
class isCaptchaWaiting_args(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
@@ -5415,12 +5415,12 @@ class isCaptchaWaiting_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
+ (0, TType.BOOL, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -5433,13 +5433,13 @@ class getCaptchaTask_args(TBase):
- exclusive
"""
- __slots__ = [
+ __slots__ = [
'exclusive',
]
thrift_spec = (
None, # 0
- (1, TType.BOOL, 'exclusive', None, None, ), # 1
+ (1, TType.BOOL, 'exclusive', None, None,), # 1
)
def __init__(self, exclusive=None,):
@@ -5452,12 +5452,12 @@ class getCaptchaTask_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.STRUCT, 'success', (CaptchaTask, CaptchaTask.thrift_spec), None, ), # 0
+ (0, TType.STRUCT, 'success', (CaptchaTask, CaptchaTask.thrift_spec), None,), # 0
)
def __init__(self, success=None,):
@@ -5470,13 +5470,13 @@ class getCaptchaTaskStatus_args(TBase):
- tid
"""
- __slots__ = [
+ __slots__ = [
'tid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'tid', None, None, ), # 1
+ (1, TType.I32, 'tid', None, None,), # 1
)
def __init__(self, tid=None,):
@@ -5489,12 +5489,12 @@ class getCaptchaTaskStatus_result(TBase):
- success
"""
- __slots__ = [
+ __slots__ = [
'success',
]
thrift_spec = (
- (0, TType.STRING, 'success', None, None, ), # 0
+ (0, TType.STRING, 'success', None, None,), # 0
)
def __init__(self, success=None,):
@@ -5508,15 +5508,15 @@ class setCaptchaResult_args(TBase):
- result
"""
- __slots__ = [
+ __slots__ = [
'tid',
'result',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'tid', None, None, ), # 1
- (2, TType.STRING, 'result', None, None, ), # 2
+ (1, TType.I32, 'tid', None, None,), # 1
+ (2, TType.STRING, 'result', None, None,), # 2
)
def __init__(self, tid=None, result=None,):
@@ -5526,9 +5526,8 @@ class setCaptchaResult_args(TBase):
class setCaptchaResult_result(TBase):
- __slots__ = [
+ __slots__ = [
]
thrift_spec = (
)
-
diff --git a/module/remote/thriftbackend/thriftgen/pyload/__init__.py b/module/remote/thriftbackend/thriftgen/pyload/__init__.py
index ce7f52598..9a0fb88bf 100644
--- a/module/remote/thriftbackend/thriftgen/pyload/__init__.py
+++ b/module/remote/thriftbackend/thriftgen/pyload/__init__.py
@@ -1 +1,3 @@
+# -*- coding: utf-8 -*-
+
__all__ = ['ttypes', 'constants', 'Pyload']
diff --git a/module/remote/thriftbackend/thriftgen/pyload/constants.py b/module/remote/thriftbackend/thriftgen/pyload/constants.py
index f8960dc63..3bdd64cc1 100644
--- a/module/remote/thriftbackend/thriftgen/pyload/constants.py
+++ b/module/remote/thriftbackend/thriftgen/pyload/constants.py
@@ -3,9 +3,8 @@
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
-# options string: py:slots,dynamic
+# options string: py:slots, dynamic
#
from thrift.Thrift import TType, TMessageType, TException
from ttypes import *
-
diff --git a/module/remote/thriftbackend/thriftgen/pyload/ttypes.py b/module/remote/thriftbackend/thriftgen/pyload/ttypes.py
index 1299b515d..67d23161d 100644
--- a/module/remote/thriftbackend/thriftgen/pyload/ttypes.py
+++ b/module/remote/thriftbackend/thriftgen/pyload/ttypes.py
@@ -3,7 +3,7 @@
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
-# options string: py:slots,dynamic
+# options string: py:slots, dynamic
#
from thrift.Thrift import TType, TMessageType, TException
@@ -169,7 +169,7 @@ class DownloadInfo(TBase):
- plugin
"""
- __slots__ = [
+ __slots__ = [
'fid',
'name',
'speed',
@@ -190,22 +190,22 @@ class DownloadInfo(TBase):
thrift_spec = (
None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
- (2, TType.STRING, 'name', None, None, ), # 2
- (3, TType.I64, 'speed', None, None, ), # 3
- (4, TType.I32, 'eta', None, None, ), # 4
- (5, TType.STRING, 'format_eta', None, None, ), # 5
- (6, TType.I64, 'bleft', None, None, ), # 6
- (7, TType.I64, 'size', None, None, ), # 7
- (8, TType.STRING, 'format_size', None, None, ), # 8
- (9, TType.BYTE, 'percent', None, None, ), # 9
- (10, TType.I32, 'status', None, None, ), # 10
- (11, TType.STRING, 'statusmsg', None, None, ), # 11
- (12, TType.STRING, 'format_wait', None, None, ), # 12
- (13, TType.I64, 'wait_until', None, None, ), # 13
- (14, TType.I32, 'packageID', None, None, ), # 14
- (15, TType.STRING, 'packageName', None, None, ), # 15
- (16, TType.STRING, 'plugin', None, None, ), # 16
+ (1, TType.I32, 'fid', None, None,), # 1
+ (2, TType.STRING, 'name', None, None,), # 2
+ (3, TType.I64, 'speed', None, None,), # 3
+ (4, TType.I32, 'eta', None, None,), # 4
+ (5, TType.STRING, 'format_eta', None, None,), # 5
+ (6, TType.I64, 'bleft', None, None,), # 6
+ (7, TType.I64, 'size', None, None,), # 7
+ (8, TType.STRING, 'format_size', None, None,), # 8
+ (9, TType.BYTE, 'percent', None, None,), # 9
+ (10, TType.I32, 'status', None, None,), # 10
+ (11, TType.STRING, 'statusmsg', None, None,), # 11
+ (12, TType.STRING, 'format_wait', None, None,), # 12
+ (13, TType.I64, 'wait_until', None, None,), # 13
+ (14, TType.I32, 'packageID', None, None,), # 14
+ (15, TType.STRING, 'packageName', None, None,), # 15
+ (16, TType.STRING, 'plugin', None, None,), # 16
)
def __init__(self, fid=None, name=None, speed=None, eta=None, format_eta=None, bleft=None, size=None, format_size=None, percent=None, status=None, statusmsg=None, format_wait=None, wait_until=None, packageID=None, packageName=None, plugin=None,):
@@ -239,7 +239,7 @@ class ServerStatus(TBase):
- reconnect
"""
- __slots__ = [
+ __slots__ = [
'pause',
'active',
'queue',
@@ -251,13 +251,13 @@ class ServerStatus(TBase):
thrift_spec = (
None, # 0
- (1, TType.BOOL, 'pause', None, None, ), # 1
- (2, TType.I16, 'active', None, None, ), # 2
- (3, TType.I16, 'queue', None, None, ), # 3
- (4, TType.I16, 'total', None, None, ), # 4
- (5, TType.I64, 'speed', None, None, ), # 5
- (6, TType.BOOL, 'download', None, None, ), # 6
- (7, TType.BOOL, 'reconnect', None, None, ), # 7
+ (1, TType.BOOL, 'pause', None, None,), # 1
+ (2, TType.I16, 'active', None, None,), # 2
+ (3, TType.I16, 'queue', None, None,), # 3
+ (4, TType.I16, 'total', None, None,), # 4
+ (5, TType.I64, 'speed', None, None,), # 5
+ (6, TType.BOOL, 'download', None, None,), # 6
+ (7, TType.BOOL, 'reconnect', None, None,), # 7
)
def __init__(self, pause=None, active=None, queue=None, total=None, speed=None, download=None, reconnect=None,):
@@ -279,7 +279,7 @@ class ConfigItem(TBase):
- type
"""
- __slots__ = [
+ __slots__ = [
'name',
'description',
'value',
@@ -288,10 +288,10 @@ class ConfigItem(TBase):
thrift_spec = (
None, # 0
- (1, TType.STRING, 'name', None, None, ), # 1
- (2, TType.STRING, 'description', None, None, ), # 2
- (3, TType.STRING, 'value', None, None, ), # 3
- (4, TType.STRING, 'type', None, None, ), # 4
+ (1, TType.STRING, 'name', None, None,), # 1
+ (2, TType.STRING, 'description', None, None,), # 2
+ (3, TType.STRING, 'value', None, None,), # 3
+ (4, TType.STRING, 'type', None, None,), # 4
)
def __init__(self, name=None, description=None, value=None, type=None,):
@@ -310,7 +310,7 @@ class ConfigSection(TBase):
- outline
"""
- __slots__ = [
+ __slots__ = [
'name',
'description',
'items',
@@ -319,10 +319,10 @@ class ConfigSection(TBase):
thrift_spec = (
None, # 0
- (1, TType.STRING, 'name', None, None, ), # 1
- (2, TType.STRING, 'description', None, None, ), # 2
- (3, TType.LIST, 'items', (TType.STRUCT,(ConfigItem, ConfigItem.thrift_spec)), None, ), # 3
- (4, TType.STRING, 'outline', None, None, ), # 4
+ (1, TType.STRING, 'name', None, None,), # 1
+ (2, TType.STRING, 'description', None, None,), # 2
+ (3, TType.LIST, 'items', (TType.STRUCT, (ConfigItem, ConfigItem.thrift_spec)), None,), # 3
+ (4, TType.STRING, 'outline', None, None,), # 4
)
def __init__(self, name=None, description=None, items=None, outline=None,):
@@ -348,7 +348,7 @@ class FileData(TBase):
- order
"""
- __slots__ = [
+ __slots__ = [
'fid',
'url',
'name',
@@ -364,17 +364,17 @@ class FileData(TBase):
thrift_spec = (
None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
- (2, TType.STRING, 'url', None, None, ), # 2
- (3, TType.STRING, 'name', None, None, ), # 3
- (4, TType.STRING, 'plugin', None, None, ), # 4
- (5, TType.I64, 'size', None, None, ), # 5
- (6, TType.STRING, 'format_size', None, None, ), # 6
- (7, TType.I32, 'status', None, None, ), # 7
- (8, TType.STRING, 'statusmsg', None, None, ), # 8
- (9, TType.I32, 'packageID', None, None, ), # 9
- (10, TType.STRING, 'error', None, None, ), # 10
- (11, TType.I16, 'order', None, None, ), # 11
+ (1, TType.I32, 'fid', None, None,), # 1
+ (2, TType.STRING, 'url', None, None,), # 2
+ (3, TType.STRING, 'name', None, None,), # 3
+ (4, TType.STRING, 'plugin', None, None,), # 4
+ (5, TType.I64, 'size', None, None,), # 5
+ (6, TType.STRING, 'format_size', None, None,), # 6
+ (7, TType.I32, 'status', None, None,), # 7
+ (8, TType.STRING, 'statusmsg', None, None,), # 8
+ (9, TType.I32, 'packageID', None, None,), # 9
+ (10, TType.STRING, 'error', None, None,), # 10
+ (11, TType.I16, 'order', None, None,), # 11
)
def __init__(self, fid=None, url=None, name=None, plugin=None, size=None, format_size=None, status=None, statusmsg=None, packageID=None, error=None, order=None,):
@@ -409,7 +409,7 @@ class PackageData(TBase):
- fids
"""
- __slots__ = [
+ __slots__ = [
'pid',
'name',
'folder',
@@ -427,19 +427,19 @@ class PackageData(TBase):
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- (2, TType.STRING, 'name', None, None, ), # 2
- (3, TType.STRING, 'folder', None, None, ), # 3
- (4, TType.STRING, 'site', None, None, ), # 4
- (5, TType.STRING, 'password', None, None, ), # 5
- (6, TType.I32, 'dest', None, None, ), # 6
- (7, TType.I16, 'order', None, None, ), # 7
- (8, TType.I16, 'linksdone', None, None, ), # 8
- (9, TType.I64, 'sizedone', None, None, ), # 9
- (10, TType.I64, 'sizetotal', None, None, ), # 10
- (11, TType.I16, 'linkstotal', None, None, ), # 11
- (12, TType.LIST, 'links', (TType.STRUCT,(FileData, FileData.thrift_spec)), None, ), # 12
- (13, TType.LIST, 'fids', (TType.I32,None), None, ), # 13
+ (1, TType.I32, 'pid', None, None,), # 1
+ (2, TType.STRING, 'name', None, None,), # 2
+ (3, TType.STRING, 'folder', None, None,), # 3
+ (4, TType.STRING, 'site', None, None,), # 4
+ (5, TType.STRING, 'password', None, None,), # 5
+ (6, TType.I32, 'dest', None, None,), # 6
+ (7, TType.I16, 'order', None, None,), # 7
+ (8, TType.I16, 'linksdone', None, None,), # 8
+ (9, TType.I64, 'sizedone', None, None,), # 9
+ (10, TType.I64, 'sizetotal', None, None,), # 10
+ (11, TType.I16, 'linkstotal', None, None,), # 11
+ (12, TType.LIST, 'links', (TType.STRUCT, (FileData, FileData.thrift_spec)), None,), # 12
+ (13, TType.LIST, 'fids', (TType.I32, None), None,), # 13
)
def __init__(self, pid=None, name=None, folder=None, site=None, password=None, dest=None, order=None, linksdone=None, sizedone=None, sizetotal=None, linkstotal=None, links=None, fids=None,):
@@ -472,7 +472,7 @@ class InteractionTask(TBase):
- plugin
"""
- __slots__ = [
+ __slots__ = [
'iid',
'input',
'structure',
@@ -486,15 +486,15 @@ class InteractionTask(TBase):
thrift_spec = (
None, # 0
- (1, TType.I32, 'iid', None, None, ), # 1
- (2, TType.I32, 'input', None, None, ), # 2
- (3, TType.LIST, 'structure', (TType.STRING,None), None, ), # 3
- (4, TType.LIST, 'preset', (TType.STRING,None), None, ), # 4
- (5, TType.I32, 'output', None, None, ), # 5
- (6, TType.LIST, 'data', (TType.STRING,None), None, ), # 6
- (7, TType.STRING, 'title', None, None, ), # 7
- (8, TType.STRING, 'description', None, None, ), # 8
- (9, TType.STRING, 'plugin', None, None, ), # 9
+ (1, TType.I32, 'iid', None, None,), # 1
+ (2, TType.I32, 'input', None, None,), # 2
+ (3, TType.LIST, 'structure', (TType.STRING, None), None,), # 3
+ (4, TType.LIST, 'preset', (TType.STRING, None), None,), # 4
+ (5, TType.I32, 'output', None, None,), # 5
+ (6, TType.LIST, 'data', (TType.STRING, None), None,), # 6
+ (7, TType.STRING, 'title', None, None,), # 7
+ (8, TType.STRING, 'description', None, None,), # 8
+ (9, TType.STRING, 'plugin', None, None,), # 9
)
def __init__(self, iid=None, input=None, structure=None, preset=None, output=None, data=None, title=None, description=None, plugin=None,):
@@ -518,7 +518,7 @@ class CaptchaTask(TBase):
- resultType
"""
- __slots__ = [
+ __slots__ = [
'tid',
'data',
'type',
@@ -527,10 +527,10 @@ class CaptchaTask(TBase):
thrift_spec = (
None, # 0
- (1, TType.I16, 'tid', None, None, ), # 1
- (2, TType.STRING, 'data', None, None, ), # 2
- (3, TType.STRING, 'type', None, None, ), # 3
- (4, TType.STRING, 'resultType', None, None, ), # 4
+ (1, TType.I16, 'tid', None, None,), # 1
+ (2, TType.STRING, 'data', None, None,), # 2
+ (3, TType.STRING, 'type', None, None,), # 3
+ (4, TType.STRING, 'resultType', None, None,), # 4
)
def __init__(self, tid=None, data=None, type=None, resultType=None,):
@@ -549,7 +549,7 @@ class EventInfo(TBase):
- destination
"""
- __slots__ = [
+ __slots__ = [
'eventname',
'id',
'type',
@@ -558,10 +558,10 @@ class EventInfo(TBase):
thrift_spec = (
None, # 0
- (1, TType.STRING, 'eventname', None, None, ), # 1
- (2, TType.I32, 'id', None, None, ), # 2
- (3, TType.I32, 'type', None, None, ), # 3
- (4, TType.I32, 'destination', None, None, ), # 4
+ (1, TType.STRING, 'eventname', None, None,), # 1
+ (2, TType.I32, 'id', None, None,), # 2
+ (3, TType.I32, 'type', None, None,), # 3
+ (4, TType.I32, 'destination', None, None,), # 4
)
def __init__(self, eventname=None, id=None, type=None, destination=None,):
@@ -581,7 +581,7 @@ class UserData(TBase):
- templateName
"""
- __slots__ = [
+ __slots__ = [
'name',
'email',
'role',
@@ -591,11 +591,11 @@ class UserData(TBase):
thrift_spec = (
None, # 0
- (1, TType.STRING, 'name', None, None, ), # 1
- (2, TType.STRING, 'email', None, None, ), # 2
- (3, TType.I32, 'role', None, None, ), # 3
- (4, TType.I32, 'permission', None, None, ), # 4
- (5, TType.STRING, 'templateName', None, None, ), # 5
+ (1, TType.STRING, 'name', None, None,), # 1
+ (2, TType.STRING, 'email', None, None,), # 2
+ (3, TType.I32, 'role', None, None,), # 3
+ (4, TType.I32, 'permission', None, None,), # 4
+ (5, TType.STRING, 'templateName', None, None,), # 5
)
def __init__(self, name=None, email=None, role=None, permission=None, templateName=None,):
@@ -619,7 +619,7 @@ class AccountInfo(TBase):
- type
"""
- __slots__ = [
+ __slots__ = [
'validuntil',
'login',
'options',
@@ -632,14 +632,14 @@ class AccountInfo(TBase):
thrift_spec = (
None, # 0
- (1, TType.I64, 'validuntil', None, None, ), # 1
- (2, TType.STRING, 'login', None, None, ), # 2
- (3, TType.MAP, 'options', (TType.STRING,None,TType.LIST,(TType.STRING,None)), None, ), # 3
- (4, TType.BOOL, 'valid', None, None, ), # 4
- (5, TType.I64, 'trafficleft', None, None, ), # 5
- (6, TType.I64, 'maxtraffic', None, None, ), # 6
- (7, TType.BOOL, 'premium', None, None, ), # 7
- (8, TType.STRING, 'type', None, None, ), # 8
+ (1, TType.I64, 'validuntil', None, None,), # 1
+ (2, TType.STRING, 'login', None, None,), # 2
+ (3, TType.MAP, 'options', (TType.STRING, None, TType.LIST, (TType.STRING, None)), None,), # 3
+ (4, TType.BOOL, 'valid', None, None,), # 4
+ (5, TType.I64, 'trafficleft', None, None,), # 5
+ (6, TType.I64, 'maxtraffic', None, None,), # 6
+ (7, TType.BOOL, 'premium', None, None,), # 7
+ (8, TType.STRING, 'type', None, None,), # 8
)
def __init__(self, validuntil=None, login=None, options=None, valid=None, trafficleft=None, maxtraffic=None, premium=None, type=None,):
@@ -662,7 +662,7 @@ class ServiceCall(TBase):
- parseArguments
"""
- __slots__ = [
+ __slots__ = [
'plugin',
'func',
'arguments',
@@ -671,10 +671,10 @@ class ServiceCall(TBase):
thrift_spec = (
None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
- (2, TType.STRING, 'func', None, None, ), # 2
- (3, TType.LIST, 'arguments', (TType.STRING,None), None, ), # 3
- (4, TType.BOOL, 'parseArguments', None, None, ), # 4
+ (1, TType.STRING, 'plugin', None, None,), # 1
+ (2, TType.STRING, 'func', None, None,), # 2
+ (3, TType.LIST, 'arguments', (TType.STRING, None), None,), # 3
+ (4, TType.BOOL, 'parseArguments', None, None,), # 4
)
def __init__(self, plugin=None, func=None, arguments=None, parseArguments=None,):
@@ -694,7 +694,7 @@ class OnlineStatus(TBase):
- size
"""
- __slots__ = [
+ __slots__ = [
'name',
'plugin',
'packagename',
@@ -704,11 +704,11 @@ class OnlineStatus(TBase):
thrift_spec = (
None, # 0
- (1, TType.STRING, 'name', None, None, ), # 1
- (2, TType.STRING, 'plugin', None, None, ), # 2
- (3, TType.STRING, 'packagename', None, None, ), # 3
- (4, TType.I32, 'status', None, None, ), # 4
- (5, TType.I64, 'size', None, None, ), # 5
+ (1, TType.STRING, 'name', None, None,), # 1
+ (2, TType.STRING, 'plugin', None, None,), # 2
+ (3, TType.STRING, 'packagename', None, None,), # 3
+ (4, TType.I32, 'status', None, None,), # 4
+ (5, TType.I64, 'size', None, None,), # 5
)
def __init__(self, name=None, plugin=None, packagename=None, status=None, size=None,):
@@ -726,15 +726,15 @@ class OnlineCheck(TBase):
- data
"""
- __slots__ = [
+ __slots__ = [
'rid',
'data',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'rid', None, None, ), # 1
- (2, TType.MAP, 'data', (TType.STRING,None,TType.STRUCT,(OnlineStatus, OnlineStatus.thrift_spec)), None, ), # 2
+ (1, TType.I32, 'rid', None, None,), # 1
+ (2, TType.MAP, 'data', (TType.STRING, None, TType.STRUCT, (OnlineStatus, OnlineStatus.thrift_spec)), None,), # 2
)
def __init__(self, rid=None, data=None,):
@@ -748,13 +748,13 @@ class PackageDoesNotExists(TExceptionBase):
- pid
"""
- __slots__ = [
+ __slots__ = [
'pid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
+ (1, TType.I32, 'pid', None, None,), # 1
)
def __init__(self, pid=None,):
@@ -770,13 +770,13 @@ class FileDoesNotExists(TExceptionBase):
- fid
"""
- __slots__ = [
+ __slots__ = [
'fid',
]
thrift_spec = (
None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
+ (1, TType.I32, 'fid', None, None,), # 1
)
def __init__(self, fid=None,):
@@ -793,15 +793,15 @@ class ServiceDoesNotExists(TExceptionBase):
- func
"""
- __slots__ = [
+ __slots__ = [
'plugin',
'func',
]
thrift_spec = (
None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
- (2, TType.STRING, 'func', None, None, ), # 2
+ (1, TType.STRING, 'plugin', None, None,), # 1
+ (2, TType.STRING, 'func', None, None,), # 2
)
def __init__(self, plugin=None, func=None,):
@@ -818,13 +818,13 @@ class ServiceException(TExceptionBase):
- msg
"""
- __slots__ = [
+ __slots__ = [
'msg',
]
thrift_spec = (
None, # 0
- (1, TType.STRING, 'msg', None, None, ), # 1
+ (1, TType.STRING, 'msg', None, None,), # 1
)
def __init__(self, msg=None,):
@@ -832,4 +832,3 @@ class ServiceException(TExceptionBase):
def __str__(self):
return repr(self)
-
diff --git a/module/setup.py b/module/setup.py
deleted file mode 100644
index 42b24859f..000000000
--- a/module/setup.py
+++ /dev/null
@@ -1,514 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
-from getpass import getpass
-import module.common.pylgettext as gettext
-import os
-from os import makedirs
-from os.path import abspath
-from os.path import dirname
-from os.path import exists
-from os.path import join
-from subprocess import PIPE
-from subprocess import call
-import sys
-from sys import exit
-from module.utils import get_console_encoding
-
-class Setup():
- """
- pyLoads initial setup configuration assistent
- """
-
- def __init__(self, path, config):
- self.path = path
- self.config = config
- self.stdin_encoding = get_console_encoding(sys.stdin.encoding)
-
- def start(self):
- langs = self.config.getMetaData("general", "language")["type"].split(";")
- lang = self.ask(u"Choose your Language / WÀhle deine Sprache", "en", langs)
- gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None])
- translation = gettext.translation("setup", join(self.path, "locale"), languages=[lang, "en"], fallback=True)
- translation.install(True)
-
- #Input shorthand for yes
- self.yes = _("y")
- #Input shorthand for no
- self.no = _("n")
-
- # print ""
- # print _("Would you like to configure pyLoad via Webinterface?")
- # print _("You need a Browser and a connection to this PC for it.")
- # viaweb = self.ask(_("Start initial webinterface for configuration?"), "y", bool=True)
- # if viaweb:
- # try:
- # from module.web import ServerThread
- # ServerThread.setup = self
- # from module.web import webinterface
- # webinterface.run_simple()
- # return False
- # except Exception, e:
- # print "Setup failed with this error: ", e
- # print "Falling back to commandline setup."
-
-
- print ""
- print _("Welcome to the pyLoad Configuration Assistent.")
- print _("It will check your system and make a basic setup in order to run pyLoad.")
- print ""
- print _("The value in brackets [] always is the default value,")
- print _("in case you don't want to change it or you are unsure what to choose, just hit enter.")
- print _(
- "Don't forget: You can always rerun this assistent with --setup or -s parameter, when you start pyLoadCore.")
- print _("If you have any problems with this assistent hit STRG-C,")
- print _("to abort and don't let him start with pyLoadCore automatically anymore.")
- print ""
- print _("When you are ready for system check, hit enter.")
- raw_input()
-
- basic, ssl, captcha, gui, web, js = self.system_check()
- print ""
-
- if not basic:
- print _("You need pycurl, sqlite and python 2.5, 2.6 or 2.7 to run pyLoad.")
- print _("Please correct this and re-run pyLoad.")
- print _("Setup will now close.")
- raw_input()
- return False
-
- raw_input(_("System check finished, hit enter to see your status report."))
- print ""
- print _("## Status ##")
- print ""
-
- avail = []
- if self.check_module("Crypto"): avail.append(_("container decrypting"))
- if ssl: avail.append(_("ssl connection"))
- if captcha: avail.append(_("automatic captcha decryption"))
- if gui: avail.append(_("GUI"))
- if web: avail.append(_("Webinterface"))
- if js: avail.append(_("extended Click'N'Load"))
-
- string = ""
-
- for av in avail:
- string += ", " + av
-
- print _("Features available:") + string[1:]
- print ""
-
- if len(avail) < 5:
- print _("Featues missing: ")
- print
-
- if not self.check_module("Crypto"):
- print _("no py-crypto available")
- print _("You need this if you want to decrypt container files.")
- print ""
-
- if not ssl:
- print _("no SSL available")
- print _("This is needed if you want to establish a secure connection to core or webinterface.")
- print _("If you only want to access locally to pyLoad ssl is not usefull.")
- print ""
-
- if not captcha:
- print _("no Captcha Recognition available")
- print _("Only needed for some hosters and as freeuser.")
- print ""
-
- if not gui:
- print _("Gui not available")
- print _("The Graphical User Interface.")
- print ""
-
- if not js:
- print _("no JavaScript engine found")
- print _("You will need this for some Click'N'Load links. Install Spidermonkey, ossp-js, pyv8 or rhino")
-
- print _("You can abort the setup now and fix some dependicies if you want.")
-
- con = self.ask(_("Continue with setup?"), self.yes, bool=True)
-
- if not con:
- return False
-
- print ""
- print _("Do you want to change the config path? Current is %s") % abspath("")
- print _(
- "If you use pyLoad on a server or the home partition lives on an iternal flash it may be a good idea to change it.")
- path = self.ask(_("Change config path?"), self.no, bool=True)
- if path:
- self.conf_path()
- #calls exit when changed
-
- print ""
- print _("Do you want to configure login data and basic settings?")
- print _("This is recommend for first run.")
- con = self.ask(_("Make basic setup?"), self.yes, bool=True)
-
- if con:
- self.conf_basic()
-
- if ssl:
- print ""
- print _("Do you want to configure ssl?")
- ssl = self.ask(_("Configure ssl?"), self.no, bool=True)
- if ssl:
- self.conf_ssl()
-
- if web:
- print ""
- print _("Do you want to configure webinterface?")
- web = self.ask(_("Configure webinterface?"), self.yes, bool=True)
- if web:
- self.conf_web()
-
- print ""
- print _("Setup finished successfully.")
- print _("Hit enter to exit and restart pyLoad")
- raw_input()
- return True
-
- def system_check(self):
- """ make a systemcheck and return the results"""
- print _("## System Check ##")
-
- if sys.version_info[:2] > (2, 7):
- print _("Your python version is to new, Please use Python 2.6/2.7")
- python = False
- elif sys.version_info[:2] < (2, 5):
- print _("Your python version is to old, Please use at least Python 2.5")
- python = False
- else:
- print _("Python Version: OK")
- python = True
-
- curl = self.check_module("pycurl")
- self.print_dep("pycurl", curl)
-
- sqlite = self.check_module("sqlite3")
- self.print_dep("sqlite3", sqlite)
-
- basic = python and curl and sqlite
-
- print ""
-
- crypto = self.check_module("Crypto")
- self.print_dep("pycrypto", crypto)
-
- ssl = self.check_module("OpenSSL")
- self.print_dep("py-OpenSSL", ssl)
-
- print ""
-
- pil = self.check_module("Image")
- self.print_dep("py-imaging", pil)
-
- if os.name == "nt":
- tesser = self.check_prog([join(pypath, "tesseract", "tesseract.exe"), "-v"])
- else:
- tesser = self.check_prog(["tesseract", "-v"])
-
- self.print_dep("tesseract", tesser)
-
- captcha = pil and tesser
-
- print ""
-
- gui = self.check_module("PyQt4")
- self.print_dep("PyQt4", gui)
-
- print ""
- jinja = True
-
- try:
- import jinja2
-
- v = jinja2.__version__
- if v and "unknown" not in v:
- if not v.startswith("2.5") and not v.startswith("2.6"):
- print _("Your installed jinja2 version %s seems too old.") % jinja2.__version__
- print _("You can safely continue but if the webinterface is not working,")
- print _("please upgrade or deinstall it, pyLoad includes a sufficient jinja2 libary.")
- print
- jinja = False
- except:
- pass
-
- self.print_dep("jinja2", jinja)
- beaker = self.check_module("beaker")
- self.print_dep("beaker", beaker)
-
- web = sqlite and beaker
-
- from module.common import JsEngine
-
- js = True if JsEngine.ENGINE else False
- self.print_dep(_("JS engine"), js)
-
- return basic, ssl, captcha, gui, web, js
-
- def conf_basic(self):
- print ""
- print _("## Basic Setup ##")
-
- print ""
- print _("The following logindata is valid for CLI, GUI and webinterface.")
-
- from module.database import DatabaseBackend
-
- db = DatabaseBackend(None)
- db.setup()
- username = self.ask(_("Username"), "User")
- password = self.ask("", "", password=True)
- db.addUser(username, password)
- db.shutdown()
-
- print ""
- print _("External clients (GUI, CLI or other) need remote access to work over the network.")
- print _("However, if you only want to use the webinterface you may disable it to save ram.")
- self.config["remote"]["activated"] = self.ask(_("Enable remote access"), self.yes, bool=True)
-
- print ""
- langs = self.config.getMetaData("general", "language")
- self.config["general"]["language"] = self.ask(_("Language"), "en", langs["type"].split(";"))
-
- self.config["general"]["download_folder"] = self.ask(_("Downloadfolder"), "Downloads")
- self.config["download"]["max_downloads"] = self.ask(_("Max parallel downloads"), "3")
- #print _("You should disable checksum proofing, if you have low hardware requirements.")
- #self.config["general"]["checksum"] = self.ask(_("Proof checksum?"), "y", bool=True)
-
- reconnect = self.ask(_("Use Reconnect?"), self.no, bool=True)
- self.config["reconnect"]["activated"] = reconnect
- if reconnect:
- self.config["reconnect"]["method"] = self.ask(_("Reconnect script location"), "./reconnect.sh")
-
-
- def conf_web(self):
- print ""
- print _("## Webinterface Setup ##")
-
- print ""
- self.config["webinterface"]["activated"] = self.ask(_("Activate webinterface?"), self.yes, bool=True)
- print ""
- print _("Listen address, if you use 127.0.0.1 or localhost, the webinterface will only accessible locally.")
- self.config["webinterface"]["host"] = self.ask(_("Address"), "0.0.0.0")
- self.config["webinterface"]["port"] = self.ask(_("Port"), "8000")
- print ""
- print _("pyLoad offers several server backends, now following a short explanation.")
- print "builtin:", _("Default server, best choice if you dont know which one to choose.")
- print "threaded:", _("This server offers SSL and is a good alternative to builtin.")
- print "fastcgi:", _(
- "Can be used by apache, lighttpd, requires you to configure them, which is not too easy job.")
- print "lightweight:", _("Very fast alternative written in C, requires libev and linux knowlegde.")
- print "\t", _("Get it from here: https://github.com/jonashaag/bjoern, compile it")
- print "\t", _("and copy bjoern.so to module/lib")
-
- print
- print _(
- "Attention: In some rare cases the builtin server is not working, if you notice problems with the webinterface")
- print _("come back here and change the builtin server to the threaded one here.")
-
- self.config["webinterface"]["server"] = self.ask(_("Server"), "builtin",
- ["builtin", "threaded", "fastcgi", "lightweight"])
-
- def conf_ssl(self):
- print ""
- print _("## SSL Setup ##")
- print ""
- print _("Execute these commands from pyLoad config folder to make ssl certificates:")
- print ""
- print "openssl genrsa -out ssl.key 1024"
- print "openssl req -new -key ssl.key -out ssl.csr"
- print "openssl req -days 36500 -x509 -key ssl.key -in ssl.csr > ssl.crt "
- print ""
- print _("If you're done and everything went fine, you can activate ssl now.")
-
- self.config["ssl"]["activated"] = self.ask(_("Activate SSL?"), self.yes, bool=True)
-
- def set_user(self):
- gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None])
- translation = gettext.translation("setup", join(self.path, "locale"),
- languages=[self.config["general"]["language"], "en"], fallback=True)
- translation.install(True)
-
- from module.database import DatabaseBackend
-
- db = DatabaseBackend(None)
- db.setup()
-
- noaction = True
- try:
- while True:
- print _("Select action")
- print _("1 - Create/Edit user")
- print _("2 - List users")
- print _("3 - Remove user")
- print _("4 - Quit")
- action = raw_input("[1]/2/3/4: ")
- if not action in ("1", "2", "3", "4"):
- continue
- elif action == "1":
- print ""
- username = self.ask(_("Username"), "User")
- password = self.ask("", "", password=True)
- db.addUser(username, password)
- noaction = False
- elif action == "2":
- print ""
- print _("Users")
- print "-----"
- users = db.listUsers()
- noaction = False
- for user in users:
- print user
- print "-----"
- print ""
- elif action == "3":
- print ""
- username = self.ask(_("Username"), "")
- if username:
- db.removeUser(username)
- noaction = False
- elif action == "4":
- break
- finally:
- if not noaction:
- db.shutdown()
-
- def conf_path(self, trans=False):
- if trans:
- gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None])
- translation = gettext.translation("setup", join(self.path, "locale"),
- languages=[self.config["general"]["language"], "en"], fallback=True)
- translation.install(True)
-
- print _("Setting new configpath, current configuration will not be transfered!")
- path = self.ask(_("Configpath"), abspath(""))
- try:
- path = join(pypath, path)
- if not exists(path):
- makedirs(path)
- f = open(join(pypath, "module", "config", "configdir"), "wb")
- f.write(path)
- f.close()
- print _("Configpath changed, setup will now close, please restart to go on.")
- print _("Press Enter to exit.")
- raw_input()
- exit()
- except Exception, e:
- print _("Setting config path failed: %s") % str(e)
-
- def print_dep(self, name, value):
- """Print Status of dependency"""
- if value:
- print _("%s: OK") % name
- else:
- print _("%s: missing") % name
-
-
- def check_module(self, module):
- try:
- __import__(module)
- return True
- except:
- return False
-
- def check_prog(self, command):
- pipe = PIPE
- try:
- call(command, stdout=pipe, stderr=pipe)
- return True
- except:
- return False
-
- def ask(self, qst, default, answers=[], bool=False, password=False):
- """produce one line to asking for input"""
- if answers:
- info = "("
-
- for i, answer in enumerate(answers):
- info += (", " if i != 0 else "") + str((answer == default and "[%s]" % answer) or answer)
-
- info += ")"
- elif bool:
- if default == self.yes:
- info = "([%s]/%s)" % (self.yes, self.no)
- else:
- info = "(%s/[%s])" % (self.yes, self.no)
- else:
- info = "[%s]" % default
-
- if password:
- p1 = True
- p2 = False
- while p1 != p2:
- # getpass(_("Password: ")) will crash on systems with broken locales (Win, NAS)
- sys.stdout.write(_("Password: "))
- p1 = getpass("")
-
- if len(p1) < 4:
- print _("Password too short. Use at least 4 symbols.")
- continue
-
- sys.stdout.write(_("Password (again): "))
- p2 = getpass("")
-
- if p1 == p2:
- return p1
- else:
- print _("Passwords did not match.")
-
- while True:
- try:
- input = raw_input(qst + " %s: " % info)
- except KeyboardInterrupt:
- print "\nSetup interrupted"
- exit()
-
- input = input.decode(self.stdin_encoding)
-
- if input.strip() == "":
- input = default
-
- if bool:
- # yes, true,t are inputs for booleans with value true
- if input.lower().strip() in [self.yes, _("yes"), _("true"), _("t"), "yes"]:
- return True
- # no, false,f are inputs for booleans with value false
- elif input.lower().strip() in [self.no, _("no"), _("false"), _("f"), "no"]:
- return False
- else:
- print _("Invalid Input")
- continue
-
- if not answers:
- return input
-
- else:
- if input in answers:
- return input
- else:
- print _("Invalid Input")
-
-
-if __name__ == "__main__":
- test = Setup(join(abspath(dirname(__file__)), ".."), None)
- test.start()
diff --git a/module/unescape.py b/module/unescape.py
deleted file mode 100644
index d8999e077..000000000
--- a/module/unescape.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from module.utils import html_unescape
-#deprecated
-unescape = html_unescape \ No newline at end of file
diff --git a/module/utils.py b/module/utils.py
deleted file mode 100644
index 8748b7693..000000000
--- a/module/utils.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# -*- coding: utf-8 -*-
-
-""" Store all usefull functions here """
-
-import os
-import sys
-import time
-import re
-from os.path import join
-from string import maketrans
-from htmlentitydefs import name2codepoint
-
-def chmod(*args):
- try:
- os.chmod(*args)
- except:
- pass
-
-
-def decode(string):
- """ decode string with utf if possible """
- try:
- return string.decode("utf8", "replace")
- except:
- return string
-
-
-def remove_chars(string, repl):
- """ removes all chars in repl from string"""
- if type(string) == str:
- return string.translate(maketrans("", ""), repl)
- elif type(string) == unicode:
- return string.translate(dict([(ord(s), None) for s in repl]))
-
-
-def save_path(name):
- #remove some chars
- if os.name == 'nt':
- return remove_chars(name, '/\\?%*:|"<>')
- else:
- return remove_chars(name, '/\\"')
-
-
-def save_join(*args):
- """ joins a path, encoding aware """
- return fs_encode(join(*[x if type(x) == unicode else decode(x) for x in args]))
-
-
-# File System Encoding functions:
-# Use fs_encode before accesing files on disk, it will encode the string properly
-
-if sys.getfilesystemencoding().startswith('ANSI'):
- def fs_encode(string):
- try:
- string = string.encode('utf-8')
- finally:
- return string
-
- fs_decode = decode #decode utf8
-
-else:
- fs_encode = fs_decode = lambda x: x # do nothing
-
-def get_console_encoding(enc):
- if os.name == "nt":
- if enc == "cp65001": # aka UTF-8
- print "WARNING: Windows codepage 65001 is not supported."
- enc = "cp850"
- else:
- enc = "utf8"
-
- return enc
-
-def compare_time(start, end):
- start = map(int, start)
- end = map(int, end)
-
- if start == end: return True
-
- now = list(time.localtime()[3:5])
- if start < now < end: return True
- elif start > end and (now > start or now < end): return True
- elif start < now > end < start: return True
- else: return False
-
-
-def formatSize(size):
- """formats size of bytes"""
- size = int(size)
- steps = 0
- sizes = ["B", "KiB", "MiB", "GiB", "TiB"]
- while size > 1000:
- size /= 1024.0
- steps += 1
- return "%.2f %s" % (size, sizes[steps])
-
-
-def formatSpeed(speed):
- return formatSize(speed) + "/s"
-
-
-def freeSpace(folder):
- if os.name == "nt":
- import ctypes
-
- free_bytes = ctypes.c_ulonglong(0)
- ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(folder), None, None, ctypes.pointer(free_bytes))
- return free_bytes.value
- else:
- from os import statvfs
-
- s = statvfs(folder)
- return s.f_bsize * s.f_bavail
-
-
-def uniqify(seq, idfun=None):
-# order preserving
- if idfun is None:
- def idfun(x): return x
- seen = {}
- result = []
- for item in seq:
- marker = idfun(item)
- # in old Python versions:
- # if seen.has_key(marker)
- # but in new ones:
- if marker in seen: continue
- seen[marker] = 1
- result.append(item)
- return result
-
-
-def parseFileSize(string, unit=None): #returns bytes
- if not unit:
- m = re.match(r"(\d*[\.,]?\d+)(.*)", string.strip().lower())
- if m:
- traffic = float(m.group(1).replace(",", "."))
- unit = m.group(2)
- else:
- return 0
- else:
- if isinstance(string, basestring):
- traffic = float(string.replace(",", "."))
- else:
- traffic = string
-
- #ignore case
- unit = unit.lower().strip()
-
- if unit in ("gb", "gig", "gbyte", "gigabyte", "gib", "g"):
- traffic *= 1 << 30
- elif unit in ("mb", "mbyte", "megabyte", "mib", "m"):
- traffic *= 1 << 20
- elif unit in ("kb", "kib", "kilobyte", "kbyte", "k"):
- traffic *= 1 << 10
-
- return traffic
-
-
-def lock(func):
- def new(*args):
- #print "Handler: %s args: %s" % (func,args[1:])
- args[0].lock.acquire()
- try:
- return func(*args)
- finally:
- args[0].lock.release()
-
- return new
-
-
-def fixup(m):
- text = m.group(0)
- if text[:2] == "&#":
- # character reference
- try:
- if text[:3] == "&#x":
- return unichr(int(text[3:-1], 16))
- else:
- return unichr(int(text[2:-1]))
- except ValueError:
- pass
- else:
- # named entity
- try:
- name = text[1:-1]
- text = unichr(name2codepoint[name])
- except KeyError:
- pass
-
- return text # leave as is
-
-
-def html_unescape(text):
- """Removes HTML or XML character references and entities from a text string"""
- return re.sub("&#?\w+;", fixup, text)
-
-if __name__ == "__main__":
- print freeSpace(".")
-
- print remove_chars("ab'cdgdsf''ds'", "'ghd")
diff --git a/module/utils/__init__.py b/module/utils/__init__.py
new file mode 100644
index 000000000..330ee622c
--- /dev/null
+++ b/module/utils/__init__.py
@@ -0,0 +1,263 @@
+# -*- coding: utf-8 -*-
+# @author: vuolter
+
+""" Store all useful functions here """
+
+import os
+import re
+import sys
+import time
+
+from gettext import gettext
+from htmlentitydefs import name2codepoint
+from os.path import join
+from string import maketrans
+from urllib import unquote
+
+# abstraction layer for json operations
+try:
+ import simplejson as json
+except ImportError:
+ import json
+
+json_loads = json.loads
+json_dumps = json.dumps
+
+
+def chmod(*args):
+ try:
+ os.chmod(*args)
+ except Exception:
+ pass
+
+
+def decode(string):
+ """ Decode string to unicode with utf8 """
+ if type(string) == str:
+ return string.decode("utf8", "replace")
+ else:
+ return string
+
+
+def encode(string):
+ """ Decode string to utf8 """
+ if type(string) == unicode:
+ return string.encode("utf8", "replace")
+ else:
+ return string
+
+
+def remove_chars(string, repl):
+ """ removes all chars in repl from string"""
+ if type(repl) == unicode:
+ for badc in list(repl):
+ string = string.replace(badc, "")
+ return string
+ else:
+ if type(string) == str:
+ return string.translate(maketrans("", ""), repl)
+ elif type(string) == unicode:
+ return string.translate(dict((ord(s), None) for s in repl))
+
+
+def safe_filename(name):
+ """ remove bad chars """
+ name = unquote(name).encode('ascii', 'replace') # Non-ASCII chars usually breaks file saving. Replacing.
+ if os.name == 'nt':
+ return remove_chars(name, u'\00\01\02\03\04\05\06\07\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32'
+ u'\33\34\35\36\37/?%*|"<>')
+ else:
+ return remove_chars(name, u'\0\\"')
+
+#: Deprecated method
+def save_path(name):
+ return safe_filename(name)
+
+
+def safe_join(*args):
+ """ joins a path, encoding aware """
+ return fs_encode(join(*[x if type(x) == unicode else decode(x) for x in args]))
+
+#: Deprecated method
+def save_join(*args):
+ return safe_join(*args)
+
+
+# File System Encoding functions:
+# Use fs_encode before accesing files on disk, it will encode the string properly
+
+if sys.getfilesystemencoding().startswith('ANSI'):
+ def fs_encode(string):
+ return safe_filename(encode(string))
+
+ fs_decode = decode #decode utf8
+
+else:
+ fs_encode = fs_decode = lambda x: x # do nothing
+
+
+def get_console_encoding(enc):
+ if os.name == "nt":
+ if enc == "cp65001": # aka UTF-8
+ print "WARNING: Windows codepage 65001 is not supported."
+ enc = "cp850"
+ else:
+ enc = "utf8"
+
+ return enc
+
+
+def compare_time(start, end):
+ start = map(int, start)
+ end = map(int, end)
+
+ if start == end: return True
+
+ now = list(time.localtime()[3:5])
+ if start < now < end: return True
+ elif start > end and (now > start or now < end): return True
+ elif start < now > end < start: return True
+ else: return False
+
+
+def formatSize(size):
+ """formats size of bytes"""
+ size = int(size)
+ steps = 0
+ sizes = ("B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB")
+ while size > 1000:
+ size /= 1024.0
+ steps += 1
+ return "%.2f %s" % (size, sizes[steps])
+
+
+def formatSpeed(speed):
+ return formatSize(speed) + "/s"
+
+
+def freeSpace(folder):
+ if os.name == "nt":
+ import ctypes
+
+ free_bytes = ctypes.c_ulonglong(0)
+ ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(folder), None, None, ctypes.pointer(free_bytes))
+ return free_bytes.value
+ else:
+ s = os.statvfs(folder)
+ return s.f_frsize * s.f_bavail
+
+
+def fs_bsize(path):
+ """ get optimal file system buffer size (in bytes) for I/O calls """
+ path = fs_encode(path)
+
+ if os.name == "nt":
+ import ctypes
+
+ drive = "%s\\" % os.path.splitdrive(path)[0]
+ cluster_sectors, sector_size = ctypes.c_longlong(0)
+ ctypes.windll.kernel32.GetDiskFreeSpaceW(ctypes.c_wchar_p(drive), ctypes.pointer(cluster_sectors), ctypes.pointer(sector_size), None, None)
+ return cluster_sectors * sector_size
+ else:
+ return os.statvfs(path).f_frsize
+
+
+def uniqify(seq): #: Originally by Dave Kirby
+ """ Remove duplicates from list preserving order """
+ seen = set()
+ seen_add = seen.add
+ return [x for x in seq if x not in seen and not seen_add(x)]
+
+
+def parseFileSize(string, unit=None): #returns bytes
+ if not unit:
+ m = re.match(r"([\d.,]+) *([a-zA-Z]*)", string.strip().lower())
+ if m:
+ traffic = float(m.group(1).replace(",", "."))
+ unit = m.group(2)
+ else:
+ return 0
+ else:
+ if isinstance(string, basestring):
+ traffic = float(string.replace(",", "."))
+ else:
+ traffic = string
+
+ #ignore case
+ unit = unit.lower().strip()
+
+ if unit in ("eb", "ebyte", "exabyte", "eib", "e"):
+ traffic *= 1 << 60
+ elif unit in ("pb", "pbyte", "petabyte", "pib", "p"):
+ traffic *= 1 << 50
+ elif unit in ("tb", "tbyte", "terabyte", "tib", "t"):
+ traffic *= 1 << 40
+ elif unit in ("gb", "gbyte", "gigabyte", "gib", "g", "gig"):
+ traffic *= 1 << 30
+ elif unit in ("mb", "mbyte", "megabyte", "mib", "m"):
+ traffic *= 1 << 20
+ elif unit in ("kb", "kbyte", "kilobyte", "kib", "k"):
+ traffic *= 1 << 10
+
+ return traffic
+
+
+def lock(func):
+ def new(*args):
+ #print "Handler: %s args: %s" % (func, args[1:])
+ args[0].lock.acquire()
+ try:
+ return func(*args)
+ finally:
+ args[0].lock.release()
+
+ return new
+
+
+def fixup(m):
+ text = m.group(0)
+ if text[:2] == "&#":
+ # character reference
+ try:
+ if text[:3] == "&#x":
+ return unichr(int(text[3:-1], 16))
+ else:
+ return unichr(int(text[2:-1]))
+ except ValueError:
+ pass
+ else:
+ # named entity
+ try:
+ name = text[1:-1]
+ text = unichr(name2codepoint[name])
+ except KeyError:
+ pass
+
+ return text # leave as is
+
+
+def has_method(obj, name):
+ """ Check if "name" was defined in obj, (false if it was inhereted) """
+ return hasattr(obj, '__dict__') and name in obj.__dict__
+
+
+def html_unescape(text):
+ """Removes HTML or XML character references and entities from a text string"""
+ return re.sub("&#?\w+;", fixup, text)
+
+
+def versiontuple(v): #: By kindall (http://stackoverflow.com/a/11887825)
+ return tuple(map(int, (v.split("."))))
+
+
+def load_translation(name, locale, default="en"):
+ """ Load language and return its translation object or None """
+ try:
+ gettext.setpaths([path.join(os.sep, "usr", "share", "pyload", "locale"), None])
+ translation = gettext.translation(name, self.path("locale"),
+ languages=[locale, default], fallback=True)
+ except Exception:
+ return None
+ else:
+ translation.install(True)
+ return translation
diff --git a/module/utils/packagetools.py b/module/utils/packagetools.py
new file mode 100644
index 000000000..9dbde9b50
--- /dev/null
+++ b/module/utils/packagetools.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from urlparse import urlparse
+
+
+endings = ("jdeatme", "3gp", "7zip", "7z", "abr", "ac3", "aiff", "aifc", "aif", "ai",
+ "au", "avi", "apk", "bin", "bmp", "bat", "bz2", "cbr", "cbz", "ccf", "chm",
+ "cr2", "cso", "cue", "cvd", "dta", "deb", "divx", "djvu", "dlc", "dmg", "doc",
+ "docx", "dot", "eps", "epub", "exe", "ff", "flv", "flac", "f4v", "gsd", "gif",
+ "gpg", "gz", "iwd", "idx", "iso", "ipa", "ipsw", "java", "jar", "jpe?g", "load",
+ "m2ts", "m4v", "m4a", "md5", "mkv", "mp2", "mp3", "mp4", "mobi", "mov", "movie",
+ "mpeg", "mpe", "mpg", "mpq", "msi", "msu", "msp", "mv", "mws", "nfo", "npk", "oga",
+ "ogg", "ogv", "otrkey", "par2", "pkg", "png", "pdf", "pptx?", "ppsx?", "ppz", "pot",
+ "psd", "qt", "rmvb", "rm", "rar", "ram", "ra", "rev", "rnd", "rpm", "run", "rsdf",
+ "reg", "rtf", "shnf", "sh(?!tml)", "ssa", "smi", "sub", "srt", "snd", "sfv", "sfx",
+ "swf", "swc", "tar\.(gz|bz2|xz)", "tar", "tgz", "tiff?", "ts", "txt", "viv", "vivo",
+ "vob", "vtt", "webm", "wav", "wmv", "wma", "xla", "xls", "xpi", "zeno", "zip",
+ "[r-z]\d{2}", "_[_a-z]{2}", "\d{3,4}(?=\?|$|\"|\r|\n)")
+
+rarPats = [re.compile(r'(.*)(\.|_|-)pa?r?t?\.?\d+.(rar|exe)$', re.I),
+ re.compile(r'(.*)(\.|_|-)part\.?[0]*[1].(rar|exe)$', re.I),
+ re.compile(r'(.*)\.rar$', re.I),
+ re.compile(r'(.*)\.r\d+$', re.I),
+ re.compile(r'(.*)(\.|_|-)\d+$', re.I)]
+
+zipPats = [re.compile(r'(.*)\.zip$', re.I),
+ re.compile(r'(.*)\.z\d+$', re.I),
+ re.compile(r'(?is).*\.7z\.[\d]+$', re.I),
+ re.compile(r'(.*)\.a.$', re.I)]
+
+ffsjPats = [re.compile(r'(.*)\._((_[a-z])|([a-z]{2}))(\.|$)'),
+ re.compile(r'(.*)(\.|_|-)[\d]+(\.(' + '|'.join(endings) + ')$)', re.I)]
+
+iszPats = [re.compile(r'(.*)\.isz$', re.I),
+ re.compile(r'(.*)\.i\d{2}$', re.I)]
+
+pat0 = re.compile(r'www\d*\.', re.I)
+
+pat1 = re.compile(r'(\.?CD\d+)', re.I)
+pat2 = re.compile(r'(\.?part\d+)', re.I)
+
+pat3 = re.compile(r'(.+)[\.\-_]+$')
+pat4 = re.compile(r'(.+)\.\d+\.xtm$')
+
+
+def matchFirst(string, *args):
+ """ matches against list of regexp and returns first match """
+ for patternlist in args:
+ for pattern in patternlist:
+ m = pattern.search(string)
+ if m is not None:
+ name = m.group(1)
+ return name
+
+ return string
+
+
+def parseNames(files):
+ """ Generates packages names from name, data lists
+
+ :param files: list of (name, data)
+ :return: packagenames mapped to data lists (eg. urls)
+ """
+ packs = {}
+
+ for file, url in files:
+ patternMatch = False
+
+ if file is None:
+ continue
+
+ # remove trailing /
+ name = file.rstrip('/')
+
+ # extract last path part .. if there is a path
+ split = name.rsplit("/", 1)
+ if len(split) > 1:
+ name = split.pop(1)
+
+ #check if an already existing package may be ok for this file
+ # found = False
+ # for pack in packs:
+ # if pack in file:
+ # packs[pack].append(url)
+ # found = True
+ # break
+ #
+ # if found:
+ # continue
+
+ # unrar pattern, 7zip/zip and hjmerge pattern, isz pattern, FFSJ pattern
+ before = name
+ name = matchFirst(name, rarPats, zipPats, iszPats, ffsjPats)
+ if before != name:
+ patternMatch = True
+
+ # xtremsplit pattern
+ m = pat4.search(name)
+ if m is not None:
+ name = m.group(1)
+
+ # remove part and cd pattern
+ m = pat1.search(name)
+ if m is not None:
+ name = name.replace(m.group(0), "")
+ patternMatch = True
+
+ m = pat2.search(name)
+ if m is not None:
+ name = name.replace(m.group(0), "")
+ patternMatch = True
+
+ # additional checks if extension pattern matched
+ if patternMatch:
+ # remove extension
+ index = name.rfind(".")
+ if index <= 0:
+ index = name.rfind("_")
+ if index > 0:
+ length = len(name) - index
+ if length <= 4:
+ name = name[:-length]
+
+ # remove endings like . _ -
+ m = pat3.search(name)
+ if m is not None:
+ name = m.group(1)
+
+ # replace . and _ with space
+ name = name.replace(".", " ")
+ name = name.replace("_", " ")
+
+ name = name.strip()
+ else:
+ name = ""
+
+ # fallback: package by hoster
+ if not name:
+ name = urlparse(file).netloc
+ if name:
+ name = pat0.sub("", name)
+
+ # fallback : default name
+ if not name:
+ name = _("Unnamed package")
+
+ # build mapping
+ if name in packs:
+ packs[name].append(url)
+ else:
+ packs[name] = [url]
+
+ return packs
diff --git a/module/utils/printer.py b/module/utils/printer.py
new file mode 100644
index 000000000..5d333e238
--- /dev/null
+++ b/module/utils/printer.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# @author: vuolter
+
+import colorama
+
+colorama.init(autoreset=True)
+
+def color(color, text):
+ return colorama.Fore.(c.upper())(text)
+
+for c in colorama.Fore:
+ eval("%(color) = lambda msg: color(%(color), msg)" % {'color': c.lower()}
+
+
+def overline(line, msg):
+ print "\033[%(line)s;0H\033[2K%(msg)s" % {'line': str(line), 'msg': msg}
diff --git a/module/common/pylgettext.py b/module/utils/pylgettext.py
index fb36fecee..cab631cf4 100644
--- a/module/common/pylgettext.py
+++ b/module/utils/pylgettext.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
from gettext import *
diff --git a/module/web/__init__.py b/module/web/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/web/__init__.py
+++ /dev/null
diff --git a/module/web/media/default/css/MooDialog.css b/module/web/media/default/css/MooDialog.css
deleted file mode 100644
index 48c9166ad..000000000
--- a/module/web/media/default/css/MooDialog.css
+++ /dev/null
@@ -1,92 +0,0 @@
-/* Created by Arian Stolwijk <http://www.aryweb.nl> */
-
-.MooDialog {
-/* position: fixed;*/
- margin: 0 auto 0 -350px;
- width:600px;
- padding:14px;
- left:50%;
- top: 100px;
-
- position: absolute;
- left: 50%;
- z-index: 50000;
-
- background: #eef5f8;
- color: black;
- border-radius: 7px;
- -moz-border-radius: 7px;
- -webkit-border-radius: 7px;
- border-radius: 7px;
- -moz-box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
- -webkit-box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
- box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
-}
-
-.MooDialogTitle {
- padding-top: 30px;
-}
-
-.MooDialog .title {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- padding: 3px 20px;
- background: #b7c4dc;
- border-bottom: 1px solid #a1aec5;
- font-weight: bold;
- text-shadow: 1px 1px 0 #fff;
- color: black;
- border-radius: 7px;
- -moz-border-radius: 7px;
- -webkit-border-radius: 7px;
-}
-
-.MooDialog .close {
- background: url(/media/img/dialog-close.png) no-repeat;
- width: 16px;
- height: 16px;
- display: block;
- cursor: pointer;
- top: -5px;
- left: -5px;
- position: absolute;
-}
-
-.MooDialog .buttons {
- text-align: right;
- margin: 0;
- padding: 0;
- border: 0;
- background: none;
-}
-
-.MooDialog .iframe {
- width: 100%;
- height: 100%;
-}
-
-.MooDialog .textInput {
- width: 200px;
- float: left;
-}
-
-.MooDialog .MooDialogAlert,
-.MooDialog .MooDialogConfirm,
-.MooDialog .MooDialogPrompt,
-.MooDialog .MooDialogError {
- background: url(/media/img/dialog-warning.png) no-repeat;
- padding-left: 40px;
- min-height: 40px;
-}
-
-.MooDialog .MooDialogConfirm,
-.MooDialog .MooDialogPromt {
- background: url(/media/img/dialog-question.png) no-repeat;
-}
-
-.MooDialog .MooDialogError {
- background: url(/media/img/dialog-error.png) no-repeat;
-}
-
diff --git a/module/web/media/default/css/default.css b/module/web/media/default/css/default.css
deleted file mode 100644
index 116f9725a..000000000
--- a/module/web/media/default/css/default.css
+++ /dev/null
@@ -1,908 +0,0 @@
-.hidden {
- display:none;
-}
-.leftalign {
- text-align:left;
-}
-.centeralign {
- text-align:center;
-}
-.rightalign {
- text-align:right;
-}
-
-
-.dokuwiki div.plugin_translation ul li a.wikilink1:link, .dokuwiki div.plugin_translation ul li a.wikilink1:hover, .dokuwiki div.plugin_translation ul li a.wikilink1:active, .dokuwiki div.plugin_translation ul li a.wikilink1:visited {
- background-color:#000080;
- color:#fff !important;
- text-decoration:none;
- padding:0 0.2em;
- margin:0.1em 0.2em;
- border:none !important;
-}
-.dokuwiki div.plugin_translation ul li a.wikilink2:link, .dokuwiki div.plugin_translation ul li a.wikilink2:hover, .dokuwiki div.plugin_translation ul li a.wikilink2:active, .dokuwiki div.plugin_translation ul li a.wikilink2:visited {
- background-color:#808080;
- color:#fff !important;
- text-decoration:none;
- padding:0 0.2em;
- margin:0.1em 0.2em;
- border:none !important;
-}
-
-.dokuwiki div.plugin_translation ul li a:hover img {
- opacity:1.0;
- height:15px;
-}
-
-body {
- margin:0;
- padding:0;
- background-color:white;
- color:black;
- font-size:12px;
- font-family:Verdana, Helvetica, "Lucida Grande", Lucida, Arial, sans-serif;
- font-family:sans-serif;
- font-size:99, 96%;
- font-size-adjust:none;
- font-style:normal;
- font-variant:normal;
- font-weight:normal;
- line-height:normal;
-}
-hr {
- border-width:0;
- border-bottom:1px #aaa dotted;
-}
-img {
- border:none;
-}
-form {
- margin:0px;
- padding:0px;
- border:none;
- display:inline;
- background:transparent;
-}
-ul li {
- margin:5px;
-}
-textarea {
- font-family:monospace;
-}
-table {
- margin:0.5em 0;
- border-collapse:collapse;
-}
-td {
- padding:0.25em;
- border:1pt solid #ADB9CC;
-}
-a {
- color:#3465a4;
- text-decoration:none;
-}
-a:hover {
- text-decoration:underline;
-}
-
-option {
- border:0 none #fff;
-}
-strong.highlight {
- background-color:#fc9;
- padding:1pt;
-}
-#pagebottom {
- clear:both;
-}
-hr {
- height:1px;
- color:#c0c0c0;
- background-color:#c0c0c0;
- border:none;
- margin:.2em 0 .2em 0;
-}
-
-.invisible {
- margin:0px;
- border:0px;
- padding:0px;
- height:0px;
- visibility:hidden;
-}
-.left {
- float:left !important;
-}
-.right {
- float:right !important;
-}
-.center {
- text-align:center;
-}
-div#body-wrapper {
- padding:40px 40px 10px 40px;
- font-size:127%;
-}
-div#content {
- margin-top:-20px;
- padding:0;
- font-size:14px;
- color:black;
- line-height:1.5em;
-}
-h1, h2, h3, h4, h5, h6 {
- background:transparent none repeat scroll 0 0;
- border-bottom:1px solid #aaa;
- color:black;
- font-weight:normal;
- margin:0;
- padding:0;
- padding-bottom:0.17em;
- padding-top:0.5em;
-}
-h1 {
- font-size:188%;
- line-height:1.2em;
- margin-bottom:0.1em;
- padding-bottom:0;
-}
-h2 {
- font-size:150%;
-}
-h3, h4, h5, h6 {
- border-bottom:none;
- font-weight:bold;
-}
-h3 {
- font-size:132%;
-}
-h4 {
- font-size:116%;
-}
-h5 {
- font-size:100%;
-}
-h6 {
- font-size:80%;
-}
-ul#page-actions, ul#page-actions-more {
- float:right;
- margin:10px 10px 0 10px;
- padding:6px;
- color:black;
- background-color:#ececec;
- list-style-type:none;
- white-space: nowrap;
- border-radius:5px;
- -moz-border-radius:5px;
-}
-ul#user-actions {
- padding:5px;
- margin:0;
- display:inline;
- color:black;
- background-color:#ececec;
- list-style-type:none;
- -moz-border-radius:3px;
- border-radius:3px;
-}
-ul#page-actions li, ul#user-actions li, ul#page-actions-more li {
- display:inline;
-}
-ul#page-actions a, ul#user-actions a, ul#page-actions-more a {
- text-decoration:none;
- color:black;
- display:inline;
- margin:0 3px;
- padding:2px 0px 2px 18px;
-}
-ul#page-actions a:hover, ul#page-actions a:focus, ul#user-actions a:hover, ul#user-actions a:focus {
- /*text-decoration:underline;*/
-}
-/***************************/
-ul#page-actions2 {
- float:left;
- margin:10px 10px 0 10px;
- padding:6px;
- color:black;
- background-color:#ececec;
- list-style-type:none;
- border-radius:5px;
- -moz-border-radius:5px;
-}
-ul#user-actions2 {
- padding:5px;
- margin:0;
- display:inline;
- color:black;
- background-color:#ececec;
- list-style-type:none;
- border-radius:3px;
- -moz-border-radius:3px;
-}
-ul#page-actions2 li, ul#user-actions2 li {
- display:inline;
-}
-ul#page-actions2 a, ul#user-actions2 a {
- text-decoration:none;
- color:black;
- display:inline;
- margin:0 3px;
- padding:2px 0px 2px 18px;
-}
-ul#page-actions2 a:hover, ul#page-actions2 a:focus, ul#user-actions2 a:hover, ul#user-actions2 a:focus,
-ul#page-actions-more a:hover, ul#page-actions-more a:focus{
- color: #4e7bb4;
-}
-/****************************/
-.hidden {
- display:none;
-}
-
-a.action.index {
- background:transparent url(/media/default/img/wiki-tools-index.png) 0px 1px no-repeat;
-}
-a.action.recent {
- background:transparent url(/media/default/img/wiki-tools-recent.png) 0px 1px no-repeat;
-}
-a.logout {
- background:transparent url(/media/default/img/user-actions-logout.png) 0px 1px no-repeat;
-}
-
-a.info {
- background:transparent url(/media/default/img/user-info.png) 0px 1px no-repeat;
-}
-
-a.admin {
- background:transparent url(/media/default/img/user-actions-admin.png) 0px 1px no-repeat;
-}
-a.profile {
- background:transparent url(/media/default/img/user-actions-profile.png) 0px 1px no-repeat;
-}
-a.create, a.edit {
- background:transparent url(/media/default/img/page-tools-edit.png) 0px 1px no-repeat;
-}
-a.source, a.show {
- background:transparent url(/media/default/img/page-tools-source.png) 0px 1px no-repeat;
-}
-a.revisions {
- background:transparent url(/media/default/img/page-tools-revisions.png) 0px 1px no-repeat;
-}
-a.subscribe, a.unsubscribe {
- background:transparent url(/media/default/img/page-tools-subscribe.png) 0px 1px no-repeat;
-}
-a.backlink {
- background:transparent url(/media/default/img/page-tools-backlinks.png) 0px 1px no-repeat;
-}
-a.play {
- background:transparent url(/media/default/img/control_play.png) 0px 1px no-repeat;
-}
-.time {
- background:transparent url(/media/default/img/status_None.png) 0px 1px no-repeat;
- padding: 2px 0px 2px 18px;
- margin: 0px 3px;
-}
-.reconnect {
- background:transparent url(/media/default/img/reconnect.png) 0px 1px no-repeat;
- padding: 2px 0px 2px 18px;
- margin: 0px 3px;
-}
-a.play:hover {
- background:transparent url(/media/default/img/control_play_blue.png) 0px 1px no-repeat;
-}
-a.cancel {
- background:transparent url(/media/default/img/control_cancel.png) 0px 1px no-repeat;
-}
-a.cancel:hover {
- background:transparent url(/media/default/img/control_cancel_blue.png) 0px 1px no-repeat;
-}
-a.pause {
- background:transparent url(/media/default/img/control_pause.png) 0px 1px no-repeat;
-}
-a.pause:hover {
- background:transparent url(/media/default/img/control_pause_blue.png) 0px 1px no-repeat;
- font-weight: bold;
-}
-a.stop {
- background:transparent url(/media/default/img/control_stop.png) 0px 1px no-repeat;
-}
-a.stop:hover {
- background:transparent url(/media/default/img/control_stop_blue.png) 0px 1px no-repeat;
-}
-a.add {
- background:transparent url(/media/default/img/control_add.png) 0px 1px no-repeat;
-}
-a.add:hover {
- background:transparent url(/media/default/img/control_add_blue.png) 0px 1px no-repeat;
-}
-a.cog {
- background:transparent url(/media/default/img/cog.png) 0px 1px no-repeat;
-}
-#head-panel {
- background:#525252 url(/media/default/img/head_bg1.png) bottom left repeat-x;
-}
-#head-panel h1 {
- display:none;
- margin:0;
- text-decoration:none;
- padding-top:0.8em;
- padding-left:3.3em;
- font-size:2.6em;
- color:#eeeeec;
-}
-#head-panel #head-logo {
- float:left;
- margin:5px 0 -15px 5px;
- padding:0;
- overflow:visible;
-}
-#head-menu {
- background:transparent url(/media/default/img/tabs-border-bottom.png) 0 100% repeat-x;
- width:100%;
- float:left;
- margin:0;
- padding:0;
- padding-top:0.8em;
-}
-#head-menu ul {
- list-style:none;
- margin:0 1em 0 2em;
-}
-#head-menu ul li {
- float:left;
- margin:0;
- margin-left:0.3em;
- font-size:14px;
- margin-bottom:4px;
-}
-#head-menu ul li.selected, #head-menu ul li:hover {
- margin-bottom:0px;
-}
-#head-menu ul li a img {
- height:22px;
- width:22px;
- vertical-align:middle;
-}
-#head-menu ul li a, #head-menu ul li a:link {
- float:left;
- text-decoration:none;
- color:#555;
- background:#eaeaea url(/media/default/img/tab-background.png) 0 100% repeat-x;
- padding:3px 7px 3px 7px;
- border:2px solid #ccc;
- border-bottom:0px solid transparent;
- padding-bottom:3px;
- -moz-border-radius:5px;
- border-radius:5px;
-}
-#head-menu ul li a:hover, #head-menu ul li a:focus {
- color:#111;
- padding-bottom:7px;
- border-bottom:0px none transparent;
- outline:none;
- border-bottom-left-radius: 0px;
- border-bottom-right-radius: 0px;
- -moz-border-radius-bottomright:0px;
- -moz-border-radius-bottomleft:0px;
-}
-#head-menu ul li a:focus {
- margin-bottom:-4px;
-}
-#head-menu ul li.selected a {
- color:#3566A5;
- background:#fff;
- padding-bottom:7px;
- border-bottom:0px none transparent;
- border-bottom-left-radius: 0px;
- border-bottom-right-radius: 0px;
- -moz-border-radius-bottomright:0px;
- -moz-border-radius-bottomleft:0px;
-}
-#head-menu ul li.selected a:hover, #head-menu ul li.selected a:focus {
- color:#111;
-}
-div#head-search-and-login {
- float:right;
- margin:0 1em 0 0;
- background-color:#222;
- padding:7px 7px 5px 5px;
- color:white;
- white-space: nowrap;
- border-bottom-left-radius: 6px;
- border-bottom-right-radius: 6px;
- -moz-border-radius-bottomright:6px;
- -moz-border-radius-bottomleft:6px;
-}
-div#head-search-and-login form {
- display:inline;
- padding:0 3px;
-}
-div#head-search-and-login form input {
- border:2px solid #888;
- background:#eee;
- font-size:14px;
- padding:2px;
- border-radius:3px;
- -moz-border-radius:3px;
-}
-div#head-search-and-login form input:focus {
- background:#fff;
-}
-#head-search {
- font-size:14px;
-}
-#head-username, #head-password {
- width:80px;
- font-size:14px;
-}
-#pageinfo {
- clear:both;
- color:#888;
- padding:0.6em 0;
- margin:0;
-}
-#foot {
- font-style:normal;
- color:#888;
- text-align:center;
-}
-#foot a {
- color:#aaf;
-}
-#foot img {
- vertical-align:middle;
-}
-div.toc {
- border:1px dotted #888;
- background:#f0f0f0;
- margin:1em 0 1em 1em;
- float:right;
- font-size:95%;
-}
-div.toc .tocheader {
- font-weight:bold;
- margin:0.5em 1em;
-}
-div.toc ol {
- margin:1em 0.5em 1em 1em;
- padding:0;
-}
-div.toc ol li {
- margin:0;
- padding:0;
- margin-left:1em;
-}
-div.toc ol ol {
- margin:0.5em 0.5em 0.5em 1em;
- padding:0;
-}
-div.recentchanges table {
- clear:both;
-}
-div#editor-help {
- font-size:90%;
- border:1px dotted #888;
- padding:0ex 1ex 1ex 1ex;
- background:#f7f6f2;
-}
-div#preview {
- margin-top:1em;
-}
-label.block {
- display:block;
- text-align:right;
- font-weight:bold;
-}
-label.simple {
- display:block;
- text-align:left;
- font-weight:normal;
-}
-label.block input.edit {
- width:50%;
-}
-/*fieldset {
- width:300px;
- text-align:center;
- padding:0.5em;
- margin:auto;
-}
-*/
-div.editor {
- margin:0 0 0 0;
-}
-table {
- margin:0.5em 0;
- border-collapse:collapse;
-}
-td {
- padding:0.25em;
- border:1pt solid #ADB9CC;
-}
-td p {
- margin:0;
- padding:0;
-}
-.u {
- text-decoration:underline;
-}
-.footnotes ul {
- padding:0 2em;
- margin:0 0 1em;
-}
-.footnotes li {
- list-style:none;
-}
-.userpref table, .userpref td {
- border:none;
-}
-#message {
- clear:both;
- padding:5px 10px;
- background-color:#eee;
- border-bottom:2px solid #ccc;
-}
-#message p {
- margin:5px 0;
- padding:0;
- font-weight:bold;
-}
-#message div.buttons {
- font-weight:normal;
-}
-.diff {
- width:99%;
-}
-.diff-title {
- background-color:#C0C0C0;
-}
-.searchresult dd span {
- font-weight:bold;
-}
-.boxtext {
- font-family:tahoma, arial, sans-serif;
- font-size:11px;
- color:#000;
- float:none;
- padding:3px 0 0 10px;
-}
-.statusbutton {
- width:32px;
- height:32px;
- float:left;
- margin-left:-32px;
- margin-right:5px;
- opacity:0;
- cursor:pointer
-}
-.dlsize {
- float:left;
- padding-right: 8px;
-}
-.dlspeed {
- float:left;
- padding-right: 8px;
-}
-.package {
- margin-bottom: 10px;
-}
-.packagename {
- font-weight: bold;
-}
-
-.child {
- margin-left: 20px;
-}
-.child_status {
- margin-right: 10px;
-}
-.child_secrow {
- font-size: 10px;
-}
-
-.header, .header th {
- text-align: left;
- font-weight: normal;
- background-color:#ececec;
- -moz-border-radius:5px;
- border-radius:5px;
-}
-.progress_bar {
- background: #0C0;
- height: 5px;
-
-}
-
-.queue {
- border: none
-}
-
-.queue tr td {
- border: none
-}
-
-.header, .header th{
- text-align: left;
- font-weight: normal;
-}
-
-
-.clearer
-{
- clear: both;
- height: 1px;
-}
-
-.left
-{
- float: left;
-}
-
-.right
-{
- float: right;
-}
-
-
-.setfield
-{
- display: table-cell;
-}
-
-ul.tabs li a
-{
- padding: 5px 16px 4px 15px;
- border: none;
- font-weight: bold;
-
- border-radius: 5px 5px 0 0;
- -moz-border-radius: 5px 5px 0 0;
-
-}
-
-
-#tabs span
-{
- display: none;
-}
-
-#tabs span.selected
-{
- display: inline;
-}
-
-#tabsback
-{
- background-color: #525252;
- margin: 2px 0 0;
- padding: 6px 4px 1px 4px;
-
- border-top-right-radius: 30px;
- border-top-left-radius: 3px;
- -moz-border-radius-topright: 30px;
- -moz-border-radius-topleft: 3px;
-}
-ul.tabs
-{
- list-style-type: none;
- margin:0;
- padding: 0 40px 0 0;
-}
-
-ul.tabs li
-{
- display: inline;
- margin-left: 8px;
-}
-
-
-ul.tabs li a
-{
- color: #42454a;
- background-color: #eaeaea;
- border: 1px none #c9c3ba;
- margin: 0;
- text-decoration: none;
-
- outline: 0;
-
- padding: 5px 16px 4px 15px;
- font-weight: bold;
-
- border-radius: 5px 5px 0 0;
- -moz-border-radius: 5px 5px 0 0;
-
-}
-
-ul.tabs li a.selected, ul.tabs li a:hover
-{
- color: #000;
- background-color: white;
-
- border-bottom-right-radius: 0;
- border-bottom-left-radius: 0;
- -moz-border-radius-bottomright: 0;
- -moz-border-radius-bottomleft: 0;
-}
-
-ul.tabs li a:hover
-{
- background-color: #f1f4ee;
-}
-
-ul.tabs li a.selected
-{
- font-weight: bold;
- background-color: #525252;
- padding-bottom: 5px;
- color: white;
-}
-
-
-#tabs-body {
- position: relative;
- overflow: hidden;
-}
-
-
-span.tabContent
-{
- border: 2px solid #525252;
- margin: 0;
- padding: 0;
- padding-bottom: 10px;
-}
-
-#tabs-body > span {
- display: none;
-}
-
-#tabs-body > span.active {
- display: block;
-}
-
-.hide
-{
- display: none;
-}
-
-.settable
-{
- margin: 20px;
- border: none;
-}
-.settable td
-{
- border: none;
- margin: 0;
- padding: 5px;
-}
-
-.settable th{
- padding-bottom: 8px;
-}
-
-.settable.wide td , .settable.wide th {
- padding-left: 15px;
- padding-right: 15px;
-}
-
-
-/*settings navbar*/
-ul.nav {
- margin: -30px 0 0;
- padding: 0;
- list-style: none;
- position: absolute;
-}
-
-
-ul.nav li {
- position: relative;
- float: left;
- padding: 5px;
-}
-
-ul.nav > li a {
- background: white;
- -moz-border-radius: 4px 4px 4px 4px;
- border: 1px solid #C9C3BA;
- border-bottom: medium none;
- color: black;
-}
-
-ul.nav ul {
- position: absolute;
- top: 26px;
- left: 10px;
- margin: 0;
- padding: 0;
- list-style: none;
- border: 1px solid #AAA;
- background: #f1f1f1;
- -webkit-box-shadow: 1px 1px 5px #AAA;
- -moz-box-shadow: 1px 1px 5px #AAA;
- box-shadow: 1px 1px 5px #AAA;
- cursor: pointer;
-}
-
-ul.nav .open {
- display: block;
-}
-
-ul.nav .close {
- display: none;
-}
-
-ul.nav ul li {
- float: none;
- padding: 0;
-}
-
-ul.nav ul li a {
- width: 130px;
- background: #f1f1f1;
- padding: 3px;
- display: block;
- font-weight: normal;
-}
-
-ul.nav ul li a:hover {
- background: #CDCDCD;
-}
-
-ul.nav ul ul {
- left: 137px;
- top: 0;
-}
-
-.purr-wrapper{
- margin:10px;
-}
-
-/*Purr alert styles*/
-
-.purr-alert{
- margin-bottom:10px;
- padding:10px;
- background:#000;
- font-size:13px;
- font-weight:bold;
- color:#FFF;
- -moz-border-radius:5px;
- -webkit-border-radius:5px;
- /*-moz-box-shadow: 0 0 10px rgba(255,255,0,.25);*/
- width:300px;
-}
-.purr-alert.error{
- color:#F55;
- padding-left:30px;
- background:url(/media/default/img/error.png) no-repeat #000 7px 10px;
- width:280px;
-}
-.purr-alert.success{
- color:#5F5;
- padding-left:30px;
- background:url(/media/default/img/success.png) no-repeat #000 7px 10px;
- width:280px;
-}
-.purr-alert.notice{
- color:#99F;
- padding-left:30px;
- background:url(/media/default/img//notice.png) no-repeat #000 7px 10px;
- width:280px;
-}
-
-table.system {
- border: none;
- margin-left: 10px;
-}
-
-table.system td {
- border: none
-}
-
-table.system tr > td:first-child {
- font-weight: bold;
- padding-right: 10px;
-} \ No newline at end of file
diff --git a/module/web/media/default/css/log.css b/module/web/media/default/css/log.css
deleted file mode 100644
index 73786bfb4..000000000
--- a/module/web/media/default/css/log.css
+++ /dev/null
@@ -1,72 +0,0 @@
-
-html, body, #content
-{
- height: 100%;
-}
-#body-wrapper
-{
- height: 70%;
-}
-.logdiv
-{
- height: 90%;
- width: 100%;
- overflow: auto;
- border: 2px solid #CCC;
- outline: 1px solid #666;
- background-color: #FFE;
- margin-right: auto;
- margin-left: auto;
-}
-.logform
-{
- display: table;
- margin: 0 auto 0 auto;
- padding-top: 5px;
-}
-.logtable
-{
-
- margin: 0px;
-}
-.logtable td
-{
- border: none;
- white-space: nowrap;
-
-
- font-family: monospace;
- font-size: 16px;
- margin: 0px;
- padding: 0px 10px 0px 10px;
- line-height: 110%;
-}
-td.logline
-{
- background-color: #EEE;
- text-align:right;
- padding: 0px 5px 0px 5px;
-}
-td.loglevel
-{
- text-align:right;
-}
-.logperpage
-{
- float: right;
- padding-bottom: 8px;
-}
-.logpaginator
-{
- float: left;
- padding-top: 5px;
-}
-.logpaginator a
-{
- padding: 0px 8px 0px 8px;
-}
-.logwarn
-{
- text-align: center;
- color: red;
-} \ No newline at end of file
diff --git a/module/web/media/default/css/pathchooser.css b/module/web/media/default/css/pathchooser.css
deleted file mode 100644
index 894cc335e..000000000
--- a/module/web/media/default/css/pathchooser.css
+++ /dev/null
@@ -1,68 +0,0 @@
-table {
- width: 90%;
- border: 1px dotted #888888;
- font-family: sans-serif;
- font-size: 10pt;
-}
-
-th {
- background-color: #525252;
- color: #E0E0E0;
-}
-
-table, tr, td {
- background-color: #F0F0F0;
-}
-
-a, a:visited {
- text-decoration: none;
- font-weight: bold;
-}
-
-#paths {
- width: 90%;
- text-align: left;
-}
-
-.file_directory {
- color: #c0c0c0;
-}
-.path_directory {
- color: #3c3c3c;
-}
-.file_file {
- color: #3c3c3c;
-}
-.path_file {
- color: #c0c0c0;
-}
-
-.parentdir {
- color: #000000;
- font-size: 10pt;
-}
-.name {
- text-align: left;
-}
-.size {
- text-align: right;
-}
-.type {
- text-align: left;
-}
-.mtime {
- text-align: center;
-}
-
-.path_abs_rel {
- color: #3c3c3c;
- text-decoration: none;
- font-weight: bold;
- font-family: sans-serif;
- font-size: 10pt;
-}
-
-.path_abs_rel a {
- color: #3c3c3c;
- font-style: italic;
-}
diff --git a/module/web/media/default/css/window.css b/module/web/media/default/css/window.css
deleted file mode 100644
index 12829868b..000000000
--- a/module/web/media/default/css/window.css
+++ /dev/null
@@ -1,73 +0,0 @@
-/* ----------- stylized ----------- */
-.window_box h1{
- font-size:14px;
- font-weight:bold;
- margin-bottom:8px;
-}
-.window_box p{
- font-size:11px;
- color:#666666;
- margin-bottom:20px;
- border-bottom:solid 1px #b7ddf2;
- padding-bottom:10px;
-}
-.window_box label{
- display:block;
- font-weight:bold;
- text-align:right;
- width:240px;
- float:left;
-}
-.window_box .small{
- color:#666666;
- display:block;
- font-size:11px;
- font-weight:normal;
- text-align:right;
- width:240px;
-}
-.window_box select, .window_box input{
- float:left;
- font-size:12px;
- padding:4px 2px;
- border:solid 1px #aacfe4;
- width:300px;
- margin:2px 0 20px 10px;
-}
-.window_box .cont{
- float:left;
- font-size:12px;
- padding: 0px 10px 15px 0px;
- width:300px;
- margin:0px 0px 0px 10px;
-}
-.window_box .cont input{
- float: none;
- margin: 0px 15px 0px 1px;
-}
-.window_box textarea{
- float:left;
- font-size:12px;
- padding:4px 2px;
- border:solid 1px #aacfe4;
- width:300px;
- margin:2px 0 20px 10px;
-}
-.window_box button, .styled_button{
- clear:both;
- margin-left:150px;
- width:125px;
- height:31px;
- background:#666666 url(../img/button.png) no-repeat;
- text-align:center;
- line-height:31px;
- color:#FFFFFF;
- font-size:11px;
- font-weight:bold;
- border: 0px;
-}
-
-.styled_button {
- margin-left: 15px;
- cursor: pointer;
-}
diff --git a/module/web/media/img/favicon.ico b/module/web/media/img/favicon.ico
deleted file mode 100644
index 58b1f4b89..000000000
--- a/module/web/media/img/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/module/web/media/js/MooDialog_static.js b/module/web/media/js/MooDialog_static.js
deleted file mode 100644
index d497d3d57..000000000
--- a/module/web/media/js/MooDialog_static.js
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
----
-
-name: Overlay
-
-authors:
- - David Walsh (http://davidwalsh.name)
-
-license:
- - MIT-style license
-
-requires: [Core/Class, Core/Element.Style, Core/Element.Event, Core/Element.Dimensions, Core/Fx.Tween]
-
-provides:
- - Overlay
-...
-*/
-
-var Overlay = new Class({
-
- Implements: [Options, Events],
-
- options: {
- id: 'overlay',
- color: '#000',
- duration: 500,
- opacity: 0.5,
- zIndex: 5000/*,
- onClick: $empty,
- onClose: $empty,
- onHide: $empty,
- onOpen: $empty,
- onShow: $empty
- */
- },
-
- initialize: function(container, options){
- this.setOptions(options);
- this.container = document.id(container);
-
- this.bound = {
- 'window': {
- resize: this.resize.bind(this),
- scroll: this.scroll.bind(this)
- },
- overlayClick: this.overlayClick.bind(this),
- tweenStart: this.tweenStart.bind(this),
- tweenComplete: this.tweenComplete.bind(this)
- };
-
- this.build().attach();
- },
-
- build: function(){
- this.overlay = new Element('div', {
- id: this.options.id,
- opacity: 0,
- styles: {
- position: (Browser.ie6) ? 'absolute' : 'fixed',
- background: this.options.color,
- left: 0,
- top: 0,
- 'z-index': this.options.zIndex
- }
- }).inject(this.container);
- this.tween = new Fx.Tween(this.overlay, {
- duration: this.options.duration,
- link: 'cancel',
- property: 'opacity'
- });
- this.tween.set('opacity', 0)
- return this;
- }.protect(),
-
- attach: function(){
- window.addEvents(this.bound.window);
- this.overlay.addEvent('click', this.bound.overlayClick);
- this.tween.addEvents({
- onStart: this.bound.tweenStart,
- onComplete: this.bound.tweenComplete
- });
- return this;
- },
-
- detach: function(){
- var args = Array.prototype.slice.call(arguments);
- args.each(function(item){
- if(item == 'window') window.removeEvents(this.bound.window);
- if(item == 'overlay') this.overlay.removeEvent('click', this.bound.overlayClick);
- }, this);
- return this;
- },
-
- overlayClick: function(){
- this.fireEvent('click');
- return this;
- },
-
- tweenStart: function(){
- this.overlay.setStyles({
- width: '100%',
- height: this.container.getScrollSize().y
- });
- return this;
- },
-
- tweenComplete: function(){
- this.fireEvent(this.overlay.get('opacity') == this.options.opacity ? 'show' : 'hide');
- return this;
- },
-
- open: function(){
- this.fireEvent('open');
- this.tween.set('display', 'block');
- this.tween.start(this.options.opacity);
- return this;
- },
-
- close: function(){
- this.fireEvent('close');
- this.tween.start(0).chain(function(){
- this.tween.set('display', 'none');
- }.bind(this));
- return this;
- },
-
- resize: function(){
- this.fireEvent('resize');
- this.overlay.setStyle('height', this.container.getScrollSize().y);
- return this;
- },
-
- scroll: function(){
- this.fireEvent('scroll');
- if (Browser.ie6) this.overlay.setStyle('left', window.getScroll().x);
- return this;
- }
-
-});
-/*
----
-name: MooDialog
-description: The base class of MooDialog
-authors: Arian Stolwijk
-license: MIT-style license
-requires: [Core/Class, Core/Element, Core/Element.Styles, Core/Element.Event]
-provides: [MooDialog, Element.MooDialog]
-...
-*/
-
-
-var MooDialog = new Class({
-
- Implements: [Options, Events],
-
- options: {
- 'class': 'MooDialog',
- title: null,
- scroll: true, // IE
- forceScroll: false,
- useEscKey: true,
- destroyOnHide: true,
- autoOpen: true,
- closeButton: true,
- onInitialize: function(){
- this.wrapper.setStyle('display', 'none');
- },
- onBeforeOpen: function(){
- this.wrapper.setStyle('display', 'block');
- this.fireEvent('show');
- },
- onBeforeClose: function(){
- this.wrapper.setStyle('display', 'none');
- this.fireEvent('hide');
- }/*,
- onOpen: function(){},
- onClose: function(){},
- onShow: function(){},
- onHide: function(){}*/
- },
-
- initialize: function(options){
- this.setOptions(options);
- this.options.inject = this.options.inject || document.body;
- options = this.options;
-
- var wrapper = this.wrapper = new Element('div.' + options['class'].replace(' ', '.')).inject(options.inject);
- this.content = new Element('div.content').inject(wrapper);
- wrapper.setStyle('display', 'none');
-
- if (options.title){
- this.title = new Element('div.title').set('text', options.title).inject(wrapper);
- wrapper.addClass('MooDialogTitle');
- }
-
- if (options.closeButton){
- this.closeButton = new Element('a.close', {
- events: {click: this.close.bind(this)}
- }).inject(wrapper);
- }
-
-
- /*<ie6>*/// IE 6 scroll
- if ((options.scroll && Browser.ie6) || options.forceScroll){
- wrapper.setStyle('position', 'absolute');
- var position = wrapper.getPosition(options.inject);
- window.addEvent('scroll', function(){
- var scroll = document.getScroll();
- wrapper.setPosition({
- x: position.x + scroll.x,
- y: position.y + scroll.y
- });
- });
- }
- /*</ie6>*/
-
- if (options.useEscKey){
- // Add event for the esc key
- document.addEvent('keydown', function(e){
- if (e.key == 'esc') this.close();
- }.bind(this));
- }
-
- this.addEvent('hide', function(){
- if (options.destroyOnHide) this.destroy();
- }.bind(this));
-
- this.fireEvent('initialize', wrapper);
- },
-
- setContent: function(){
- var content = Array.from(arguments);
- if (content.length == 1) content = content[0];
-
- this.content.empty();
-
- var type = typeOf(content);
- if (['string', 'number'].contains(type)) this.content.set('text', content);
- else this.content.adopt(content);
-
- return this;
- },
-
- open: function(){
- this.fireEvent('beforeOpen', this.wrapper).fireEvent('open');
- this.opened = true;
- return this;
- },
-
- close: function(){
- this.fireEvent('beforeClose', this.wrapper).fireEvent('close');
- this.opened = false;
- return this;
- },
-
- destroy: function(){
- this.wrapper.destroy();
- },
-
- toElement: function(){
- return this.wrapper;
- }
-
-});
-
-
-Element.implement({
-
- MooDialog: function(options){
- this.store('MooDialog',
- new MooDialog(options).setContent(this).open()
- );
- return this;
- }
-
-});
-/*
----
-name: MooDialog.Fx
-description: Overwrite the default events so the Dialogs are using Fx on open and close
-authors: Arian Stolwijk
-license: MIT-style license
-requires: [Cores/Fx.Tween, Overlay]
-provides: MooDialog.Fx
-...
-*/
-
-
-MooDialog.implement('options', {
-
- duration: 400,
- closeOnOverlayClick: true,
-
- onInitialize: function(wrapper){
- this.fx = new Fx.Tween(wrapper, {
- property: 'opacity',
- duration: this.options.duration
- }).set(0);
- this.overlay = new Overlay(this.options.inject, {
- duration: this.options.duration
- });
- if (this.options.closeOnOverlayClick) this.overlay.addEvent('click', this.close.bind(this));
- },
-
- onBeforeOpen: function(wrapper){
- this.overlay.open();
- wrapper.setStyle('display', 'block');
- this.fx.start(1).chain(function(){
- this.fireEvent('show');
- }.bind(this));
- },
-
- onBeforeClose: function(wrapper){
- this.overlay.close();
- this.fx.start(0).chain(function(){
- this.fireEvent('hide');
- wrapper.setStyle('display', 'none');
- }.bind(this));
- }
-
-});
-/*
----
-name: MooDialog.Confirm
-description: Creates an Confirm Dialog
-authors: Arian Stolwijk
-license: MIT-style license
-requires: MooDialog
-provides: [MooDialog.Confirm, Element.confirmLinkClick, Element.confirmFormSubmit]
-...
-*/
-
-
-MooDialog.Confirm = new Class({
-
- Extends: MooDialog,
-
- options: {
- okText: 'Ok',
- cancelText: 'Cancel',
- focus: true,
- textPClass: 'MooDialogConfirm'
- },
-
- initialize: function(msg, fn, fn1, options){
- this.parent(options);
- var emptyFn = function(){},
- self = this;
-
- var buttons = [
- {fn: fn || emptyFn, txt: this.options.okText},
- {fn: fn1 || emptyFn, txt: this.options.cancelText}
- ].map(function(button){
- return new Element('input[type=button]', {
- events: {
- click: function(){
- button.fn();
- self.close();
- }
- },
- value: button.txt
- });
- });
-
- this.setContent(
- new Element('p.' + this.options.textPClass, {text: msg}),
- new Element('div.buttons').adopt(buttons)
- );
- if (this.options.autoOpen) this.open();
-
- if(this.options.focus) this.addEvent('show', function(){
- buttons[1].focus();
- });
-
- }
-});
-
-
-Element.implement({
-
- confirmLinkClick: function(msg, options){
- this.addEvent('click', function(e){
- e.stop();
- new MooDialog.Confirm(msg, function(){
- location.href = this.get('href');
- }.bind(this), null, options)
- });
- return this;
- },
-
- confirmFormSubmit: function(msg, options){
- this.addEvent('submit', function(e){
- e.stop();
- new MooDialog.Confirm(msg, function(){
- this.submit();
- }.bind(this), null, options)
- }.bind(this));
- return this;
- }
-
-});
diff --git a/module/web/media/js/admin.coffee b/module/web/media/js/admin.coffee
deleted file mode 100644
index 82b0dd3ec..000000000
--- a/module/web/media/js/admin.coffee
+++ /dev/null
@@ -1,58 +0,0 @@
-root = this
-
-window.addEvent "domready", ->
-
- root.passwordDialog = new MooDialog {destroyOnHide: false}
- root.passwordDialog.setContent $ 'password_box'
-
- $("login_password_reset").addEvent "click", (e) -> root.passwordDialog.close()
- $("login_password_button").addEvent "click", (e) ->
-
- newpw = $("login_new_password").get("value")
- newpw2 = $("login_new_password2").get("value")
-
- if newpw is newpw2
- form = $("password_form")
- form.set "send", {
- onSuccess: (data) ->
- root.notify.alert "Success", {
- 'className': 'success'
- }
- onFailure: (data) ->
- root.notify.alert "Error", {
- 'className': 'error'
- }
- }
-
- form.send()
-
- root.passwordDialog.close()
- else
- alert '{{_("Passwords did not match.")}}'
-
- e.stop()
-
- for item in $$(".change_password")
- id = item.get("id")
- user = id.split("|")[1]
- $("user_login").set("value", user)
- item.addEvent "click", (e) -> root.passwordDialog.open()
-
- $('quit-pyload').addEvent "click", (e) ->
- new MooDialog.Confirm "{{_('You are really sure you want to quit pyLoad?')}}", ->
- new Request.JSON({
- url: '/api/kill'
- method: 'get'
- }).send()
- , ->
- e.stop()
-
- $('restart-pyload').addEvent "click", (e) ->
- new MooDialog.Confirm "{{_('Are you sure you want to restart pyLoad?')}}", ->
- new Request.JSON({
- url: '/api/restart'
- method: 'get'
- onSuccess: (data) -> alert "{{_('pyLoad restarted')}}"
- }).send()
- , ->
- e.stop() \ No newline at end of file
diff --git a/module/web/media/js/base.coffee b/module/web/media/js/base.coffee
deleted file mode 100644
index 3b5d33e82..000000000
--- a/module/web/media/js/base.coffee
+++ /dev/null
@@ -1,173 +0,0 @@
-# External scope
-root = this
-
-# helper functions
-humanFileSize = (size) ->
- filesizename = new Array("B", "KiB", "MiB", "GiB", "TiB", "PiB")
- loga = Math.log(size) / Math.log(1024)
- i = Math.floor(loga)
- a = Math.pow(1024, i)
- if size is 0 then "0 B" else (Math.round(size * 100 / a) / 100 + " " + filesizename[i])
-
-parseUri = () ->
- oldString = $("add_links").value
- regxp = new RegExp('(ht|f)tp(s?):\/\/[a-zA-Z0-9\-\.\/\?=_&%#]+[<| |\"|\'|\r|\n|\t]{1}', 'g')
- resu = oldString.match regxp
- return if resu == null
- res = "";
-
- for part in resu
- if part.indexOf(" ") != -1
- res = res + part.replace(" ", " \n")
- else if part.indexOf("\t") != -1
- res = res + part.replace("\t", " \n")
- else if part.indexOf("\r") != -1
- res = res + part.replace("\r", " \n")
- else if part.indexOf("\"") != -1
- res = res + part.replace("\"", " \n")
- else if part.indexOf("<") != -1
- res = res + part.replace("<", " \n")
- else if part.indexOf("'") != -1
- res = res + part.replace("'", " \n")
- else
- res = res + part.replace("\n", " \n")
-
- $("add_links").value = res;
-
-
-Array::remove = (from, to) ->
- rest = this.slice((to || from) + 1 || this.length)
- this.length = from < 0 ? this.length + from : from
- return [] if this.length == 0
- return this.push.apply(this, rest)
-
-
-document.addEvent "domready", ->
-
- # global notification
- root.notify = new Purr {
- 'mode': 'top'
- 'position': 'center'
- }
-
- root.captchaBox = new MooDialog {destroyOnHide: false}
- root.captchaBox.setContent $ 'cap_box'
-
- root.addBox = new MooDialog {destroyOnHide: false}
- root.addBox.setContent $ 'add_box'
-
- $('add_form').onsubmit = ->
- $('add_form').target = 'upload_target'
- if $('add_name').value is "" and $('add_file').value is ""
- alert '{{_("Please Enter a packagename.")}}'
- return false
- else
- root.addBox.close()
- return true
-
- $('add_reset').addEvent 'click', -> root.addBox.close()
-
- $('action_add').addEvent 'click', -> $("add_form").reset(); root.addBox.open()
- $('action_play').addEvent 'click', -> new Request({method: 'get', url: '/api/unpauseServer'}).send()
- $('action_cancel').addEvent 'click', -> new Request({method: 'get', url: '/api/stopAllDownloads'}).send()
- $('action_stop').addEvent 'click', -> new Request({method: 'get', url: '/api/pauseServer'}).send()
-
-
- # captcha events
-
- $('cap_info').addEvent 'click', ->
- load_captcha "get", ""
- root.captchaBox.open()
- $('cap_reset').addEvent 'click', -> root.captchaBox.close()
- $('cap_form').addEvent 'submit', (e) ->
- submit_captcha()
- e.stop()
-
- $('cap_positional').addEvent 'click', on_captcha_click
-
- new Request.JSON({
- url: "/json/status"
- onSuccess: LoadJsonToContent
- secure: false
- async: true
- initialDelay: 0
- delay: 4000
- limit: 3000
- }).startTimer()
-
-LoadJsonToContent = (data) ->
- $("speed").set 'text', humanFileSize(data.speed)+"/s"
- $("aktiv").set 'text', data.active
- $("aktiv_from").set 'text', data.queue
- $("aktiv_total").set 'text', data.total
-
- if data.captcha
- if $("cap_info").getStyle("display") != "inline"
- $("cap_info").setStyle 'display', 'inline'
- root.notify.alert '{{_("New Captcha Request")}}', {
- 'className': 'notify'
- }
- else
- $("cap_info").setStyle 'display', 'none'
-
-
- if data.download
- $("time").set 'text', ' {{_("on")}}'
- $("time").setStyle 'background-color', "#8ffc25"
- else
- $("time").set 'text', ' {{_("off")}}'
- $("time").setStyle 'background-color', "#fc6e26"
-
- if data.reconnect
- $("reconnect").set 'text', ' {{_("on")}}'
- $("reconnect").setStyle 'background-color', "#8ffc25"
- else
- $("reconnect").set 'text', ' {{_("off")}}'
- $("reconnect").setStyle 'background-color', "#fc6e26"
-
- return null
-
-
-set_captcha = (data) ->
- $('cap_id').set 'value', data.id
- if (data.result_type is 'textual')
- $('cap_textual_img').set 'src', data.src
- $('cap_title').set 'text', '{{_("Please read the text on the captcha.")}}'
- $('cap_submit').setStyle 'display', 'inline'
- $('cap_textual').setStyle 'display', 'block'
- $('cap_positional').setStyle 'display', 'none'
-
- else if (data.result_type == 'positional')
- $('cap_positional_img').set('src', data.src)
- $('cap_title').set('text', '{{_("Please click on the right captcha position.")}}')
- $('cap_submit').setStyle('display', 'none')
- $('cap_textual').setStyle('display', 'none')
-
-
-load_captcha = (method, post) ->
- new Request.JSON({
- url: "/json/set_captcha"
- onSuccess: (data) -> set_captcha(data) if data.captcha else clear_captcha()
- secure: false
- async: true
- method: method
- }).send(post)
-
-clear_captcha = ->
- $('cap_textual').setStyle 'display', 'none'
- $('cap_textual_img').set 'src', ''
- $('cap_positional').setStyle 'display', 'none'
- $('cap_positional_img').set 'src', ''
- $('cap_title').set 'text', '{{_("No Captchas to read.")}}'
-
-submit_captcha = ->
- load_captcha("post", "cap_id=" + $('cap_id').get('value') + "&cap_result=" + $('cap_result').get('value') );
- $('cap_result').set('value', '')
- false
-
-on_captcha_click = (e) ->
- position = e.target.getPosition()
- x = e.page.x - position.x
- y = e.page.y - position.y
- $('cap_result').value = x + "," + y
- submit_captcha() \ No newline at end of file
diff --git a/module/web/media/js/settings.coffee b/module/web/media/js/settings.coffee
deleted file mode 100644
index 9205233e3..000000000
--- a/module/web/media/js/settings.coffee
+++ /dev/null
@@ -1,107 +0,0 @@
-root = this
-
-window.addEvent 'domready', ->
- root.accountDialog = new MooDialog {destroyOnHide: false}
- root.accountDialog.setContent $ 'account_box'
-
- new TinyTab $$('#toptabs li a'), $$('#tabs-body > span')
-
- $$('ul.nav').each (nav) ->
- new MooDropMenu nav, {
- onOpen: (el) -> el.fade 'in'
- onClose: (el) -> el.fade 'out'
- onInitialize: (el) -> el.fade('hide').set 'tween', {duration:500}
- }
-
- new SettingsUI()
-
-
-class SettingsUI
- constructor: ->
- @menu = $$ "#general-menu li"
- @menu.append $$ "#plugin-menu li"
-
- @name = $ "tabsback"
- @general = $ "general_form_content"
- @plugin = $ "plugin_form_content"
-
- el.addEvent 'click', @menuClick.bind(this) for el in @menu
-
- $("general|submit").addEvent "click", @configSubmit.bind(this)
- $("plugin|submit").addEvent "click", @configSubmit.bind(this)
-
- $("account_add").addEvent "click", (e) ->
- root.accountDialog.open()
- e.stop()
-
- $("account_reset").addEvent "click", (e) ->
- root.accountDialog.close()
-
- $("account_add_button").addEvent "click", @addAccount.bind(this)
- $("account_submit").addEvent "click", @submitAccounts.bind(this)
-
-
- menuClick: (e) ->
- [category, section] = e.target.get("id").split("|")
- name = e.target.get "text"
-
-
- target = if category is "general" then @general else @plugin
- target.dissolve()
-
- new Request({
- "method" : "get"
- "url" : "/json/load_config/#{category}/#{section}"
- "onSuccess": (data) =>
- target.set "html", data
- target.reveal()
- this.name.set "text", name
- }).send()
-
-
- configSubmit: (e) ->
- category = e.target.get("id").split("|")[0];
- form = $("#{category}_form");
-
- form.set "send", {
- "method": "post"
- "url": "/json/save_config/#{category}"
- "onSuccess" : ->
- root.notify.alert '{{ _("Settings saved.")}}', {
- 'className': 'success'
- }
- "onFailure": ->
- root.notify.alert '{{ _("Error occured.")}}', {
- 'className': 'error'
- }
- }
- form.send()
- e.stop()
-
- addAccount: (e) ->
- form = $ "add_account_form"
- form.set "send", {
- "method": "post"
- "onSuccess" : -> window.location.reload()
- "onFailure": ->
- root.notify.alert '{{_("Error occured.")}}', {
- 'className': 'error'
- }
- }
-
- form.send()
- e.stop()
-
- submitAccounts: (e) ->
- form = $ "account_form"
- form.set "send", {
- "method": "post",
- "onSuccess" : -> window.location.reload()
- "onFailure": ->
- root.notify.alert('{{ _("Error occured.") }}', {
- 'className': 'error'
- });
- }
-
- form.send()
- e.stop() \ No newline at end of file
diff --git a/module/web/servers/lighttpd_default.conf b/module/web/servers/lighttpd_default.conf
deleted file mode 100644
index e56dda35f..000000000
--- a/module/web/servers/lighttpd_default.conf
+++ /dev/null
@@ -1,153 +0,0 @@
-# lighttpd configuration file
-#
-# use it as a base for lighttpd 1.0.0 and above
-#
-# $Id: lighttpd.conf,v 1.7 2004/11/03 22:26:05 weigon Exp $
-
-############ Options you really have to take care of ####################
-
-## modules to load
-# at least mod_access and mod_accesslog should be loaded
-# all other module should only be loaded if really neccesary
-# - saves some time
-# - saves memory
-server.modules = (
- "mod_rewrite",
- "mod_redirect",
- "mod_alias",
- "mod_access",
-# "mod_trigger_b4_dl",
-# "mod_auth",
-# "mod_status",
-# "mod_setenv",
- "mod_fastcgi",
-# "mod_proxy",
-# "mod_simple_vhost",
-# "mod_evhost",
-# "mod_userdir",
-# "mod_cgi",
-# "mod_compress",
-# "mod_ssi",
-# "mod_usertrack",
-# "mod_expire",
-# "mod_secdownload",
-# "mod_rrdtool",
-# "mod_accesslog"
- )
-
-## A static document-root. For virtual hosting take a look at the
-## mod_simple_vhost module.
-server.document-root = "%(path)"
-
-## where to send error-messages to
-server.errorlog = "%(path)/error.log"
-
-# files to check for if .../ is requested
-index-file.names = ( "index.php", "index.html",
- "index.htm", "default.htm" )
-
-## set the event-handler (read the performance section in the manual)
-# server.event-handler = "freebsd-kqueue" # needed on OS X
-
-# mimetype mapping
-mimetype.assign = (
- ".pdf" => "application/pdf",
- ".sig" => "application/pgp-signature",
- ".spl" => "application/futuresplash",
- ".class" => "application/octet-stream",
- ".ps" => "application/postscript",
- ".torrent" => "application/x-bittorrent",
- ".dvi" => "application/x-dvi",
- ".gz" => "application/x-gzip",
- ".pac" => "application/x-ns-proxy-autoconfig",
- ".swf" => "application/x-shockwave-flash",
- ".tar.gz" => "application/x-tgz",
- ".tgz" => "application/x-tgz",
- ".tar" => "application/x-tar",
- ".zip" => "application/zip",
- ".mp3" => "audio/mpeg",
- ".m3u" => "audio/x-mpegurl",
- ".wma" => "audio/x-ms-wma",
- ".wax" => "audio/x-ms-wax",
- ".ogg" => "application/ogg",
- ".wav" => "audio/x-wav",
- ".gif" => "image/gif",
- ".jar" => "application/x-java-archive",
- ".jpg" => "image/jpeg",
- ".jpeg" => "image/jpeg",
- ".png" => "image/png",
- ".xbm" => "image/x-xbitmap",
- ".xpm" => "image/x-xpixmap",
- ".xwd" => "image/x-xwindowdump",
- ".css" => "text/css",
- ".html" => "text/html",
- ".htm" => "text/html",
- ".js" => "text/javascript",
- ".asc" => "text/plain",
- ".c" => "text/plain",
- ".cpp" => "text/plain",
- ".log" => "text/plain",
- ".conf" => "text/plain",
- ".text" => "text/plain",
- ".txt" => "text/plain",
- ".dtd" => "text/xml",
- ".xml" => "text/xml",
- ".mpeg" => "video/mpeg",
- ".mpg" => "video/mpeg",
- ".mov" => "video/quicktime",
- ".qt" => "video/quicktime",
- ".avi" => "video/x-msvideo",
- ".asf" => "video/x-ms-asf",
- ".asx" => "video/x-ms-asf",
- ".wmv" => "video/x-ms-wmv",
- ".bz2" => "application/x-bzip",
- ".tbz" => "application/x-bzip-compressed-tar",
- ".tar.bz2" => "application/x-bzip-compressed-tar",
- # default mime type
- "" => "application/octet-stream",
- )
-
-# Use the "Content-Type" extended attribute to obtain mime type if possible
-#mimetype.use-xattr = "enable"
-
-#### accesslog module
-accesslog.filename = "%(path)/access.log"
-
-url.access-deny = ( "~", ".inc" )
-
-$HTTP["url"] =~ "\.pdf$" {
- server.range-requests = "disable"
-}
-static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
-
-server.pid-file = "%(path)/lighttpd.pid"
-
-server.bind = "%(host)"
-server.port = %(port)
-
-#server.document-root = "/home/user/public_html"
-fastcgi.server = (
- "/pyload.fcgi" => (
- "main" => (
- "host" => "127.0.0.1",
- "port" => 9295,
- "check-local" => "disable",
- "docroot" => "/",
- )
- ),
-)
-
-alias.url = (
- "/media/" => "%(media)/",
- "/admin/media/" => "/usr/lib/python%(version)/site-packages/django/contrib/admin/media/",
-)
-
-url.rewrite-once = (
- "^(/media.*)$" => "$1",
- "^(/admin/media.*)$" => "$1",
- "^/favicon\.ico$" => "/media/img/favicon.ico",
- "^(/pyload.fcgi.*)$" => "$1",
- "^(/.*)$" => "/pyload.fcgi$1",
-)
-
-%(ssl) \ No newline at end of file
diff --git a/module/web/templates/500.html b/module/web/templates/500.html
deleted file mode 100644
index e15090b66..000000000
--- a/module/web/templates/500.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
- "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
- <title>Server Error</title>
-</head>
-<body>
-<h1>Server Error occured. Please enable debug mode to get a more detailed report.</h1>
-</body>
-</html>
diff --git a/module/web/templates/default/captcha.html b/module/web/templates/default/captcha.html
deleted file mode 100644
index 288375b76..000000000
--- a/module/web/templates/default/captcha.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<!-- Captcha box -->
-<div id="cap_box" class="window_box">
-
- <form id="cap_form" action="/json/set_captcha" method="POST" enctype="multipart/form-data" onsubmit="return false;">
-
- <h1>{{_("Captcha reading")}}</h1>
- <p id="cap_title">{{_("Please read the text on the captcha.")}}</p>
-
- <div id="cap_textual">
-
- <input id="cap_id" name="cap_id" type="hidden" value="" />
-
- <label>{{_("Captcha")}}
- <span class="small">{{_("The captcha.")}}</span>
- </label>
- <span class="cont">
- <img id="cap_textual_img" src="">
- </span>
-
- <label>{{_("Text")}}
- <span class="small">{{_("Input the text on the captcha.")}}</span>
- </label>
- <input id="cap_result" name="cap_result" type="text" size="20" />
-
- </div>
-
- <div id="cap_positional" style="text-align: center">
- <img id="cap_positional_img" src="" style="margin: 10px; cursor:pointer">
- </div>
-
- <div id="button_bar" style="text-align: center">
- <span>
- <button id="cap_submit" type="submit" style="margin-left: 0">{{_("Submit")}}</button>
- <button id="cap_reset" type="reset" style="margin-left: 0">{{_("Close")}}</button>
- </span>
- </div>
-
- <div class="spacer"></div>
-
- </form>
-
-</div> \ No newline at end of file
diff --git a/module/web/templates/default/folder.html b/module/web/templates/default/folder.html
deleted file mode 100644
index b385e80cb..000000000
--- a/module/web/templates/default/folder.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<li class="folder">
- <input type="hidden" name="path" class="path" value="{{ path }}" />
- <input type="hidden" name="name" class="name" value="{{ name }}" />
- <span>
- <b>{{ name }}</b>
- <span class="buttons" style="opacity:0">
- <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/media/default/img/pencil.png" />
- &nbsp;&nbsp;
- <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/media/default/img/delete.png" />
- &nbsp;&nbsp;
- <img title="{{_("Add subdirectory")}}" class="mkdir" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/media/default/img/add_folder.png" />
- </span>
- </span>
- <div style="display:none">{{ _("Folder is empty") }}</div>
-</li> \ No newline at end of file
diff --git a/module/web/templates/default/setup.html b/module/web/templates/default/setup.html
deleted file mode 100644
index 39ef6f1e8..000000000
--- a/module/web/templates/default/setup.html
+++ /dev/null
@@ -1,13 +0,0 @@
-{% extends 'default/base.html' %}
-
-{% block title %}{{ _("Setup") }} - {{ super() }} {% endblock %}
-{% block subtitle %}{{ _("Setup") }}{% endblock %}
-{% block headpanel %}Welcome to pyLoad{% endblock %}
-{% block menu %}
- <li style="height: 25px"> <!-- Needed to get enough margin -->
- </li>
-{% endblock %}
-
-{% block content %}
- Comming Soon.
-{% endblock %} \ No newline at end of file
diff --git a/module/web/webinterface.py b/module/webui/__init__.py
index ec8b2e56c..0d2f1ca36 100644
--- a/module/web/webinterface.py
+++ b/module/webui/__init__.py
@@ -1,36 +1,19 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN, vuolter
import sys
-import module.common.pylgettext as gettext
+import pyload.utils.pylgettext as gettext
import os
from os.path import join, abspath, dirname, exists
from os import makedirs
-PROJECT_DIR = abspath(dirname(__file__))
-PYLOAD_DIR = abspath(join(PROJECT_DIR, "..", ".."))
+THEME_DIR = abspath(join(dirname(__file__), "themes"))
+PYLOAD_DIR = abspath(join(THEME_DIR, "..", "..", ".."))
sys.path.append(PYLOAD_DIR)
-from module import InitHomeDir
-from module.utils import decode, formatSize
+from pyload.utils import decode, formatSize
import bottle
from bottle import run, app
@@ -41,23 +24,22 @@ from middlewares import StripPathMiddleware, GZipMiddleWare, PrefixMiddleware
SETUP = None
PYLOAD = None
-from module.web import ServerThread
+from pyload.manager.thread import ServerThread
+from pyload.network.JsEngine import JsEngine
if not ServerThread.core:
if ServerThread.setup:
SETUP = ServerThread.setup
config = SETUP.config
+ JS = JsEngine(SETUP)
else:
raise Exception("Could not access pyLoad Core")
else:
PYLOAD = ServerThread.core.api
config = ServerThread.core.config
+ JS = JsEngine(ServerThread.core)
-from module.common.JsEngine import JsEngine
-
-JS = JsEngine()
-
-TEMPLATE = config.get('webinterface', 'template')
+THEME = config.get('webinterface', 'theme')
DL_ROOT = config.get('general', 'download_folder')
LOG_ROOT = config.get('log', 'log_folder')
PREFIX = config.get('webinterface', 'prefix')
@@ -75,13 +57,11 @@ if not exists(cache):
makedirs(cache)
bcc = FileSystemBytecodeCache(cache, '%s.cache')
-loader = PrefixLoader({
- "default": FileSystemLoader(join(PROJECT_DIR, "templates", "default")),
- 'js': FileSystemLoader(join(PROJECT_DIR, 'media', 'js'))
-})
+
+loader = FileSystemLoader(THEME_DIR)
env = Environment(loader=loader, extensions=['jinja2.ext.i18n', 'jinja2.ext.autoescape'], trim_blocks=True, auto_reload=False,
- bytecode_cache=bcc)
+ bytecode_cache=bcc)
from filters import quotepath, path_make_relative, path_make_absolute, truncate, date
@@ -120,17 +100,14 @@ web = GZipMiddleWare(web)
if PREFIX:
web = PrefixMiddleware(web, prefix=PREFIX)
-import pyload_app
-import json_app
-import cnl_app
-import api_app
+import pyload.webui.app
def run_simple(host="0.0.0.0", port="8000"):
run(app=web, host=host, port=port, quiet=True)
def run_lightweight(host="0.0.0.0", port="8000"):
- run(app=web, host=host, port=port, quiet=True, server="bjoern")
+ run(app=web, host=host, port=port, server="bjoern", quiet=True)
def run_threaded(host="0.0.0.0", port="8000", theads=3, cert="", key=""):
@@ -142,7 +119,7 @@ def run_threaded(host="0.0.0.0", port="8000", theads=3, cert="", key=""):
CherryPyWSGIServer.numthreads = theads
- from utils import CherryPyWSGI
+ from pyload.webui.app.utils import CherryPyWSGI
run(app=web, host=host, port=port, server=CherryPyWSGI, quiet=True)
@@ -151,7 +128,3 @@ def run_fcgi(host="0.0.0.0", port="8000"):
from bottle import FlupFCGIServer
run(app=web, host=host, port=port, server=FlupFCGIServer, quiet=True)
-
-
-if __name__ == "__main__":
- run(app=web, port=8001)
diff --git a/module/webui/app/__init__.py b/module/webui/app/__init__.py
new file mode 100644
index 000000000..39d0fadd5
--- /dev/null
+++ b/module/webui/app/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from pyload.webui.app import api, cnl, json, pyload
diff --git a/module/web/api_app.py b/module/webui/app/api.py
index 1629c1677..1ee4b1608 100644
--- a/module/web/api_app.py
+++ b/module/webui/app/api.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
from urllib import unquote
@@ -7,12 +6,12 @@ from traceback import format_exc, print_exc
from bottle import route, request, response, HTTPError
-from utils import toDict, set_session
-from webinterface import PYLOAD
+from pyload.utils import toDict, set_session
+from pyload.webui import PYLOAD
-from module.common.json_layer import json
-from module.lib.SafeEval import const_eval as literal_eval
-from module.Api import BaseObject
+from pyload.utils import json
+from SafeEval import const_eval as literal_eval
+from pyload.api import BaseObject
# json encoder that accepts TBase objects
class TBaseEncoder(json.JSONEncoder):
@@ -25,8 +24,8 @@ class TBaseEncoder(json.JSONEncoder):
# accepting positional arguments, as well as kwargs via post and get
-@route("/api/:func:args#[a-zA-Z0-9\-_/\"'\[\]%{}]*#")
-@route("/api/:func:args#[a-zA-Z0-9\-_/\"'\[\]%{}]*#", method="POST")
+@route('/api/<func><args:re:[a-zA-Z0-9\-_/\"\'\[\]%{}]*>')
+@route('/api/<func><args:re:[a-zA-Z0-9\-_/\"\'\[\]%{}]*>', method='POST')
def call_api(func, args=""):
response.headers.replace("Content-type", "application/json")
response.headers.append("Cache-Control", "no-cache, must-revalidate")
@@ -61,7 +60,7 @@ def callApi(func, *args, **kwargs):
return HTTPError(404, json.dumps("Not Found"))
result = getattr(PYLOAD, func)(*[literal_eval(x) for x in args],
- **dict([(x, literal_eval(y)) for x, y in kwargs.iteritems()]))
+ **dict((x, literal_eval(y)) for x, y in kwargs.iteritems()))
# null is invalid json response
if result is None: result = True
@@ -70,7 +69,7 @@ def callApi(func, *args, **kwargs):
#post -> username, password
-@route("/api/login", method="POST")
+@route('/api/login', method='POST')
def login():
response.headers.replace("Content-type", "application/json")
response.headers.append("Cache-Control", "no-cache, must-revalidate")
@@ -89,11 +88,11 @@ def login():
try:
sid = s._headers["cookie_out"].split("=")[1].split(";")[0]
return json.dumps(sid)
- except:
+ except Exception:
return json.dumps(True)
-@route("/api/logout")
+@route('/api/logout')
def logout():
response.headers.replace("Content-type", "application/json")
response.headers.append("Cache-Control", "no-cache, must-revalidate")
diff --git a/module/web/cnl_app.py b/module/webui/app/cnl.py
index d8f7c1180..e00e47f66 100644
--- a/module/web/cnl_app.py
+++ b/module/webui/app/cnl.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
from os.path import join
import re
@@ -7,18 +6,19 @@ from base64 import standard_b64decode
from binascii import unhexlify
from bottle import route, request, HTTPError
-from webinterface import PYLOAD, DL_ROOT, JS
+from pyload.webui import PYLOAD, DL_ROOT, JS
+
try:
from Crypto.Cipher import AES
-except:
+except Exception:
pass
def local_check(function):
def _view(*args, **kwargs):
- if request.environ.get('REMOTE_ADDR', "0") in ('127.0.0.1', 'localhost') \
- or request.environ.get('HTTP_HOST','0') == '127.0.0.1:9666':
+ if request.environ.get("REMOTE_ADDR", "0") in ("127.0.0.1", "localhost") \
+ or request.environ.get("HTTP_HOST", "0") in ("127.0.0.1:9666", "localhost:9666"):
return function(*args, **kwargs)
else:
return HTTPError(403, "Forbidden")
@@ -26,14 +26,15 @@ def local_check(function):
return _view
-@route("/flash")
-@route("/flash/:id")
-@route("/flash", method="POST")
+@route('/flash')
+@route('/flash/<id>')
+@route('/flash', method='POST')
@local_check
def flash(id="0"):
return "JDownloader\r\n"
-@route("/flash/add", method="POST")
+
+@route('/flash/add', method='POST')
@local_check
def add(request):
package = request.POST.get('referer', None)
@@ -46,10 +47,10 @@ def add(request):
return ""
-@route("/flash/addcrypted", method="POST")
+
+@route('/flash/addcrypted', method='POST')
@local_check
def addcrypted():
-
package = request.forms.get('referer', 'ClickAndLoad Package')
dlc = request.forms['crypted'].replace(" ", "+")
@@ -60,15 +61,15 @@ def addcrypted():
try:
PYLOAD.addPackage(package, [dlc_path], 0)
- except:
+ except Exception:
return HTTPError()
else:
return "success\r\n"
-@route("/flash/addcrypted2", method="POST")
+
+@route('/flash/addcrypted2', method='POST')
@local_check
def addcrypted2():
-
package = request.forms.get("source", None)
crypted = request.forms["crypted"]
jk = request.forms["jk"]
@@ -81,8 +82,8 @@ def addcrypted2():
else:
try:
jk = re.findall(r"return ('|\")(.+)('|\")", jk)[0][1]
- except:
- ## Test for some known js functions to decode
+ except Exception:
+ ## Test for some known js functions to decode
if jk.find("dec") > -1 and jk.find("org") > -1:
org = re.findall(r"var org = ('|\")([^\"']+)", jk)[0][1]
jk = list(org)
@@ -93,14 +94,14 @@ def addcrypted2():
try:
Key = unhexlify(jk)
- except:
+ except Exception:
print "Could not decrypt key, please install py-spidermonkey or ossp-js"
return "failed"
IV = Key
obj = AES.new(Key, AES.MODE_CBC, IV)
- result = obj.decrypt(crypted).replace("\x00", "").replace("\r","").split("\n")
+ result = obj.decrypt(crypted).replace("\x00", "").replace("\r", "").split("\n")
result = filter(lambda x: x != "", result)
@@ -109,18 +110,19 @@ def addcrypted2():
PYLOAD.addPackage(package, result, 0)
else:
PYLOAD.generateAndAddPackages(result, 0)
- except:
+ except Exception:
return "failed can't add"
else:
return "success\r\n"
-@route("/flashgot_pyload")
-@route("/flashgot_pyload", method="POST")
-@route("/flashgot")
-@route("/flashgot", method="POST")
+
+@route('/flashgot_pyload')
+@route('/flashgot_pyload', method='POST')
+@route('/flashgot')
+@route('/flashgot', method='POST')
@local_check
def flashgot():
- if request.environ['HTTP_REFERER'] != "http://localhost:9666/flashgot" and request.environ['HTTP_REFERER'] != "http://127.0.0.1:9666/flashgot":
+ if request.environ['HTTP_REFERER'] not in ("http://localhost:9666/flashgot", "http://127.0.0.1:9666/flashgot"):
return HTTPError()
autostart = int(request.forms.get('autostart', 0))
@@ -135,7 +137,8 @@ def flashgot():
return ""
-@route("/crossdomain.xml")
+
+@route('/crossdomain.xml')
@local_check
def crossdomain():
rep = "<?xml version=\"1.0\"?>\n"
@@ -146,17 +149,17 @@ def crossdomain():
return rep
-@route("/flash/checkSupportForUrl")
+@route('/flash/checkSupportForUrl')
@local_check
def checksupport():
-
url = request.GET.get("url")
res = PYLOAD.checkURLs([url])
supported = (not res[0][1] is None)
return str(supported).lower()
-@route("/jdcheck.js")
+
+@route('/jdcheck.js')
@local_check
def jdcheck():
rep = "jdownloader=true;\n"
diff --git a/module/web/json_app.py b/module/webui/app/json.py
index f3626405c..c347c25e3 100644
--- a/module/web/json_app.py
+++ b/module/webui/app/json.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
from os.path import join
@@ -7,11 +6,11 @@ from shutil import copyfileobj
from bottle import route, request, HTTPError
-from webinterface import PYLOAD
+from pyload.webui import PYLOAD
-from utils import login_required, render_to_response, toDict
+from pyload.utils import login_required, render_to_response, toDict
-from module.utils import decode, formatSize
+from pyload.utils import decode, formatSize
def format_time(seconds):
@@ -26,20 +25,20 @@ def get_sort_key(item):
return item["order"]
-@route("/json/status")
-@route("/json/status", method="POST")
+@route('/json/status')
+@route('/json/status', method='POST')
@login_required('LIST')
def status():
try:
status = toDict(PYLOAD.statusServer())
status['captcha'] = PYLOAD.isCaptchaWaiting()
return status
- except:
+ except Exception:
return HTTPError()
-@route("/json/links")
-@route("/json/links", method="POST")
+@route('/json/links')
+@route('/json/links', method='POST')
@login_required('LIST')
def links():
try:
@@ -65,7 +64,7 @@ def links():
return HTTPError()
-@route("/json/packages")
+@route('/json/packages')
@login_required('LIST')
def packages():
print "/json/packages"
@@ -79,11 +78,11 @@ def packages():
return data
- except:
+ except Exception:
return HTTPError()
-@route("/json/package/<id:int>")
+@route('/json/package/<id:int>')
@login_required('LIST')
def package(id):
try:
@@ -113,45 +112,45 @@ def package(id):
data["links"] = tmp
return data
- except:
+ except Exception:
print_exc()
return HTTPError()
-@route("/json/package_order/:ids")
+@route('/json/package_order/<ids>')
@login_required('ADD')
def package_order(ids):
try:
pid, pos = ids.split("|")
PYLOAD.orderPackage(int(pid), int(pos))
return {"response": "success"}
- except:
+ except Exception:
return HTTPError()
-@route("/json/abort_link/<id:int>")
+@route('/json/abort_link/<id:int>')
@login_required('DELETE')
def abort_link(id):
try:
PYLOAD.stopDownloads([id])
return {"response": "success"}
- except:
+ except Exception:
return HTTPError()
-@route("/json/link_order/:ids")
+@route('/json/link_order/<ids>')
@login_required('ADD')
def link_order(ids):
try:
pid, pos = ids.split("|")
PYLOAD.orderFile(int(pid), int(pos))
return {"response": "success"}
- except:
+ except Exception:
return HTTPError()
-@route("/json/add_package")
-@route("/json/add_package", method="POST")
+@route('/json/add_package')
+@route('/json/add_package', method='POST')
@login_required('ADD')
def add_package():
name = request.forms.get("add_name", "New Package").strip()
@@ -171,7 +170,7 @@ def add_package():
copyfileobj(f.file, destination)
destination.close()
links.insert(0, fpath)
- except:
+ except Exception:
pass
name = name.decode("utf8", "ignore")
@@ -186,17 +185,17 @@ def add_package():
PYLOAD.setPackageData(pack, data)
-@route("/json/move_package/<dest:int>/<id:int>")
+@route('/json/move_package/<dest:int>/<id:int>')
@login_required('MODIFY')
def move_package(dest, id):
try:
PYLOAD.movePackage(dest, id)
return {"response": "success"}
- except:
+ except Exception:
return HTTPError()
-@route("/json/edit_package", method="POST")
+@route('/json/edit_package', method='POST')
@login_required('MODIFY')
def edit_package():
try:
@@ -208,18 +207,18 @@ def edit_package():
PYLOAD.setPackageData(id, data)
return {"response": "success"}
- except:
+ except Exception:
return HTTPError()
-@route("/json/set_captcha")
-@route("/json/set_captcha", method="POST")
+@route('/json/set_captcha')
+@route('/json/set_captcha', method='POST')
@login_required('ADD')
def set_captcha():
if request.environ.get('REQUEST_METHOD', "GET") == "POST":
try:
PYLOAD.setCaptchaResult(request.forms["cap_id"], request.forms["cap_result"])
- except:
+ except Exception:
pass
task = PYLOAD.getCaptchaTask()
@@ -232,7 +231,7 @@ def set_captcha():
return {'captcha': False}
-@route("/json/load_config/:category/:section")
+@route('/json/load_config/<category>/<section>')
@login_required("SETTINGS")
def load_config(category, section):
conf = None
@@ -242,7 +241,7 @@ def load_config(category, section):
conf = PYLOAD.getPluginConfigDict()
for key, option in conf[section].iteritems():
- if key in ("desc","outline"): continue
+ if key in ("desc", "outline"): continue
if ";" in option["type"]:
option["list"] = option["type"].split(";")
@@ -252,13 +251,13 @@ def load_config(category, section):
return render_to_response("settings_item.html", {"skey": section, "section": conf[section]})
-@route("/json/save_config/:category", method="POST")
+@route('/json/save_config/<category>', method='POST')
@login_required("SETTINGS")
def save_config(category):
for key, value in request.POST.iteritems():
try:
section, option = key.split("|")
- except:
+ except Exception:
continue
if category == "general": category = "core"
@@ -266,7 +265,7 @@ def save_config(category):
PYLOAD.setConfigValue(section, option, decode(value), category)
-@route("/json/add_account", method="POST")
+@route('/json/add_account', method='POST')
@login_required("ACCOUNTS")
def add_account():
login = request.POST["account_login"]
@@ -276,7 +275,7 @@ def add_account():
PYLOAD.updateAccount(type, login, password)
-@route("/json/update_accounts", method="POST")
+@route('/json/update_accounts', method='POST')
@login_required("ACCOUNTS")
def update_accounts():
deleted = [] #dont update deleted accs or they will be created again
@@ -284,7 +283,7 @@ def update_accounts():
for name, value in request.POST.iteritems():
value = value.strip()
if not value: continue
-
+
tmp, user = name.split(";")
plugin, action = tmp.split("|")
@@ -300,7 +299,7 @@ def update_accounts():
deleted.append((plugin,user))
PYLOAD.removeAccount(plugin, user)
-@route("/json/change_password", method="POST")
+@route('/json/change_password', method='POST')
def change_password():
user = request.POST["user_login"]
diff --git a/module/web/pyload_app.py b/module/webui/app/pyload.py
index df4a4b3d4..2d1a8eb86 100644
--- a/module/web/pyload_app.py
+++ b/module/webui/app/pyload.py
@@ -1,21 +1,6 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
+
from datetime import datetime
from operator import itemgetter, attrgetter
@@ -27,16 +12,16 @@ from os.path import isdir, isfile, join, abspath
from sys import getfilesystemencoding
from urllib import unquote
-from bottle import route, static_file, request, response, redirect, HTTPError, error
+from bottle import route, static_file, request, response, redirect, error
-from webinterface import PYLOAD, PYLOAD_DIR, PROJECT_DIR, SETUP, env
+from pyload.webui import PYLOAD, PYLOAD_DIR, THEME_DIR, SETUP, env
-from utils import render_to_response, parse_permissions, parse_userdata, \
+from pyload.utils import render_to_response, parse_permissions, parse_userdata, \
login_required, get_permission, set_permission, permlist, toDict, set_session
-from filters import relpath, unquotepath
+from pyload.webui.filters import relpath, unquotepath
-from module.utils import formatSize, save_join, fs_encode, fs_decode
+from pyload.utils import formatSize, safe_join, fs_encode, fs_decode
# Helper
@@ -55,16 +40,18 @@ def pre_processor():
# check if update check is available
if info:
- if info["pyload"] == "True": update = True
- if info["plugins"] == "True": plugins = True
+ if info["pyload"] == "True":
+ update = info["version"]
+ if info["plugins"] == "True":
+ plugins = True
- return {"user": user,
- 'status': status,
+ return {"user" : user,
+ 'status' : status,
'captcha': captcha,
- 'perms': perms,
- 'url': request.url,
- 'update': update,
+ 'perms' : perms,
+ 'url' : request.url,
+ 'update' : update,
'plugins': plugins}
@@ -73,43 +60,63 @@ def base(messages):
## Views
+@error(403)
+def error403(code):
+ return "The parameter you passed has the wrong format"
+
+
+@error(404)
+def error404(code):
+ return "Sorry, this page does not exist"
+
+
@error(500)
def error500(error):
- print "An error occured while processing the request."
- if error.traceback:
- print error.traceback
-
+ traceback = error.traceback
+ if traceback:
+ print traceback
return base(["An Error occured, please enable debug mode to get more details.", error,
- error.traceback.replace("\n", "<br>") if error.traceback else "No Traceback"])
+ traceback.replace("\n", "<br>") if traceback else "No Traceback"])
-# render js
-@route("/media/js/<path:re:.+\.js>")
-def js_dynamic(path):
- response.headers['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
- time.gmtime(time.time() + 60 * 60 * 24 * 2))
- response.headers['Cache-control'] = "public"
+
+@route('/<theme>/<file:re:(.+/)?[^/]+\.min\.[^/]+>')
+def server_min(theme, file):
+ filename = join(THEME_DIR, theme, file)
+ if not isfile(filename):
+ file = file.replace(".min.", ".")
+ if file.endswith(".js"):
+ return server_js(theme, file)
+ else:
+ return server_static(theme, file)
+
+
+@route('/<theme>/<file_static:re:.+\.js>')
+def server_js(theme, file):
response.headers['Content-Type'] = "text/javascript; charset=UTF-8"
- try:
- # static files are not rendered
- if "static" not in path and "mootools" not in path:
- t = env.get_template("js/%s" % path)
- return t.render()
- else:
- return static_file(path, root=join(PROJECT_DIR, "media", "js"))
- except:
- return HTTPError(404, "Not Found")
+ if "/render/" in file or ".render." in file:
+ response.headers['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
+ time.gmtime(time.time() + 24 * 7 * 60 * 60))
+ response.headers['Cache-control'] = "public"
+
+ path = join(theme, file)
+ return env.get_template(path).render()
+ else:
+ return server_static(theme, file)
-@route('/media/<path:path>')
-def server_static(path):
+
+@route('/<theme>/<file:path>')
+def server_static(theme, file):
response.headers['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
- time.gmtime(time.time() + 60 * 60 * 24 * 7))
+ time.gmtime(time.time() + 24 * 7 * 60 * 60))
response.headers['Cache-control'] = "public"
- return static_file(path, root=join(PROJECT_DIR, "media"))
+
+ return static_file(file, root=join(THEME_DIR, theme))
+
@route('/favicon.ico')
def favicon():
- return static_file("favicon.ico", root=join(PROJECT_DIR, "media", "img"))
+ return static_file("icon.ico", root=join(PYLOAD_DIR, "docs", "resources"))
@route('/login', method="GET")
@@ -125,7 +132,7 @@ def nopermission():
return base([_("You dont have permission to access this page.")])
-@route("/login", method="POST")
+@route('/login', method='POST')
def login_post():
user = request.forms.get("username")
password = request.forms.get("password")
@@ -139,20 +146,20 @@ def login_post():
return redirect("/")
-@route("/logout")
+@route('/logout')
def logout():
s = request.environ.get('beaker.session')
s.delete()
return render_to_response("logout.html", proc=[pre_processor])
-@route("/")
-@route("/home")
+@route('/')
+@route('/home')
@login_required("LIST")
def home():
try:
res = [toDict(x) for x in PYLOAD.statusDownloads()]
- except:
+ except Exception:
s = request.environ.get('beaker.session')
s.delete()
return redirect("/login")
@@ -164,7 +171,7 @@ def home():
return render_to_response("home.html", {"res": res}, [pre_processor])
-@route("/queue")
+@route('/queue')
@login_required("LIST")
def queue():
queue = PYLOAD.getQueue()
@@ -174,7 +181,7 @@ def queue():
return render_to_response('queue.html', {'content': queue, 'target': 1}, [pre_processor])
-@route("/collector")
+@route('/collector')
@login_required('LIST')
def collector():
queue = PYLOAD.getCollector()
@@ -184,7 +191,7 @@ def collector():
return render_to_response('queue.html', {'content': queue, 'target': 0}, [pre_processor])
-@route("/downloads")
+@route('/downloads')
@login_required('DOWNLOAD')
def downloads():
root = PYLOAD.getConfigValue("general", "download_folder")
@@ -199,18 +206,18 @@ def downloads():
items = listdir(fs_encode(root))
for item in sorted([fs_decode(x) for x in items]):
- if isdir(save_join(root, item)):
+ if isdir(safe_join(root, item)):
folder = {
'name': item,
'path': item,
'files': []
}
- files = listdir(save_join(root, item))
+ files = listdir(safe_join(root, item))
for file in sorted([fs_decode(x) for x in files]):
try:
- if isfile(save_join(root, item, file)):
+ if isfile(safe_join(root, item, file)):
folder['files'].append(file)
- except:
+ except Exception:
pass
data['folder'].append(folder)
@@ -220,7 +227,7 @@ def downloads():
return render_to_response('downloads.html', {'files': data}, [pre_processor])
-@route("/downloads/get/<path:re:.+>")
+@route('/downloads/get/<path:path>')
@login_required("DOWNLOAD")
def get_download(path):
path = unquote(path).decode("utf8")
@@ -229,16 +236,11 @@ def get_download(path):
root = PYLOAD.getConfigValue("general", "download_folder")
path = path.replace("..", "")
- try:
- return static_file(fs_encode(path), fs_encode(root))
-
- except Exception, e:
- print e
- return HTTPError(404, "File not Found.")
+ return static_file(fs_encode(path), fs_encode(root))
-@route("/settings")
+@route('/settings')
@login_required('SETTINGS')
def config():
conf = PYLOAD.getConfig()
@@ -261,7 +263,7 @@ def config():
elif not data.trafficleft:
data.trafficleft = _("not available")
else:
- data.trafficleft = formatSize(data.trafficleft * 1024)
+ data.trafficleft = formatSize(data.trafficleft)
if data.validuntil == -1:
data.validuntil = _("unlimited")
@@ -269,13 +271,12 @@ def config():
data.validuntil = _("not available")
else:
t = time.localtime(data.validuntil)
- data.validuntil = time.strftime("%d.%m.%Y", t)
+ data.validuntil = time.strftime("%d.%m.%Y - %H:%M:%S", t)
- if "time" in data.options:
- try:
- data.options["time"] = data.options["time"][0]
- except:
- data.options["time"] = "0:00-0:00"
+ try:
+ data.options["time"] = data.options["time"][0]
+ except Exception:
+ data.options["time"] = "0:00-0:00"
if "limitDL" in data.options:
data.options["limitdl"] = data.options["limitDL"][0]
@@ -287,10 +288,10 @@ def config():
[pre_processor])
-@route("/filechooser")
-@route("/pathchooser")
-@route("/filechooser/:file#.+#")
-@route("/pathchooser/:path#.+#")
+@route('/filechooser')
+@route('/pathchooser')
+@route('/filechooser/<file:path>')
+@route('/pathchooser/<path:path>')
@login_required('STATUS')
def path(file="", path=""):
if file:
@@ -319,7 +320,7 @@ def path(file="", path=""):
try:
cwd = cwd.encode("utf8")
- except:
+ except Exception:
pass
cwd = os.path.normpath(os.path.abspath(cwd))
@@ -336,7 +337,7 @@ def path(file="", path=""):
try:
folders = os.listdir(cwd)
- except:
+ except Exception:
folders = []
files = []
@@ -348,7 +349,7 @@ def path(file="", path=""):
data['sort'] = data['fullpath'].lower()
data['modified'] = datetime.fromtimestamp(int(os.path.getmtime(join(cwd, f))))
data['ext'] = os.path.splitext(f)[1]
- except:
+ except Exception:
continue
if os.path.isdir(join(cwd, f)):
@@ -377,10 +378,10 @@ def path(file="", path=""):
'absolute': abs}, [])
-@route("/logs")
-@route("/logs", method="POST")
-@route("/logs/:item")
-@route("/logs/:item", method="POST")
+@route('/logs')
+@route('/logs', method='POST')
+@route('/logs/<item>')
+@route('/logs/<item>', method='POST')
@login_required('LOGS')
def logs(item=-1):
s = request.environ.get('beaker.session')
@@ -389,7 +390,7 @@ def logs(item=-1):
reversed = s.get('reversed', False)
warning = ""
- conf = PYLOAD.getConfigValue("log","file_log")
+ conf = PYLOAD.getConfigValue("log", "file_log")
if not conf:
warning = "Warning: File log is disabled, see settings page."
@@ -399,7 +400,7 @@ def logs(item=-1):
if request.environ.get('REQUEST_METHOD', "GET") == "POST":
try:
fro = datetime.strptime(request.forms['from'], '%d.%m.%Y %H:%M:%S')
- except:
+ except Exception:
pass
try:
perpage = int(request.forms['perpage'])
@@ -407,14 +408,14 @@ def logs(item=-1):
reversed = bool(request.forms.get('reversed', False))
s['reversed'] = reversed
- except:
+ except Exception:
pass
s.save()
try:
item = int(item)
- except:
+ except Exception:
pass
log = PYLOAD.getLog()
@@ -437,7 +438,7 @@ def logs(item=-1):
try:
date, time, level, message = l.decode("utf8", "ignore").split(" ", 3)
dtime = datetime.strptime(date + ' ' + time, '%d.%m.%Y %H:%M:%S')
- except:
+ except Exception:
dtime = None
date = '?'
time = ' '
@@ -461,15 +462,15 @@ def logs(item=-1):
'reversed': reversed, 'perpage': perpage, 'perpage_p': sorted(perpage_p),
'iprev': 1 if item - perpage < 1 else item - perpage,
'inext': (item + perpage) if item + perpage < len(log) else item},
- [pre_processor])
+ [pre_processor])
-@route("/admin")
-@route("/admin", method="POST")
+@route('/admin')
+@route('/admin', method='POST')
@login_required("ADMIN")
def admin():
# convert to dict
- user = dict([(name, toDict(y)) for name, y in PYLOAD.getAllUserData().iteritems()])
+ user = dict((name, toDict(y)) for name, y in PYLOAD.getAllUserData().iteritems())
perms = permlist()
for data in user.itervalues():
@@ -492,7 +493,6 @@ def admin():
for perm in perms:
user[name]["perms"][perm] = False
-
for perm in request.POST.getall("%s|perms" % name):
user[name]["perms"][perm] = True
@@ -503,15 +503,12 @@ def admin():
return render_to_response("admin.html", {"users": user, "permlist": perms}, [pre_processor])
-@route("/setup")
+@route('/setup')
def setup():
- if PYLOAD or not SETUP:
- return base([_("Run pyLoadCore.py -s to access the setup.")])
-
- return render_to_response('setup.html', {"user": False, "perms": False})
+ return base([_("Run pyload.py -s to access the setup.")])
-@route("/info")
+@route('/info')
def info():
conf = PYLOAD.getConfigDict()
diff --git a/module/web/utils.py b/module/webui/app/utils.py
index a89c87558..fd0e14cfa 100644
--- a/module/web/utils.py
+++ b/module/webui/app/utils.py
@@ -1,37 +1,23 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this plrogram; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN, vuolter
+
+from os.path import join
+
from bottle import request, HTTPError, redirect, ServerAdapter
-from webinterface import env, TEMPLATE
+from pyload.webui import env, THEME
-from module.Api import has_permission, PERMS, ROLE
+from pyload.api import has_permission, PERMS, ROLE
-def render_to_response(name, args={}, proc=[]):
+def render_to_response(file, args={}, proc=[]):
for p in proc:
args.update(p())
-
- t = env.get_template(TEMPLATE + "/" + name)
- return t.render(**args)
+ path = join(THEME, "tml", file)
+ return env.get_template(path).render(**args)
def parse_permissions(session):
- perms = dict([(x, False) for x in dir(PERMS) if not x.startswith("_")])
+ perms = dict((x, False) for x in dir(PERMS) if not x.startswith("_"))
perms["ADMIN"] = False
perms["is_admin"] = False
diff --git a/module/web/filters.py b/module/webui/filters.py
index 13b8345fc..527b18446 100644
--- a/module/web/filters.py
+++ b/module/webui/filters.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
from os.path import abspath, commonprefix, join
@@ -7,7 +6,7 @@ quotechar = "::/"
try:
from os.path import relpath
-except:
+except Exception:
from posixpath import curdir, sep, pardir
def relpath(path, start=curdir):
"""Return a relative version of a path"""
@@ -28,7 +27,7 @@ def quotepath(path):
return path.replace("../", quotechar)
except AttributeError:
return path
- except:
+ except Exception:
return ""
def unquotepath(path):
@@ -36,7 +35,7 @@ def unquotepath(path):
return path.replace(quotechar, "../")
except AttributeError:
return path
- except:
+ except Exception:
return ""
def path_make_absolute(path):
@@ -59,4 +58,4 @@ def truncate(value, n):
return value
def date(date, format):
- return date \ No newline at end of file
+ return date
diff --git a/module/web/middlewares.py b/module/webui/middlewares.py
index e0e6c3102..5f56f81ee 100644
--- a/module/web/middlewares.py
+++ b/module/webui/middlewares.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
import gzip
@@ -130,4 +129,4 @@ class GzipResponse(object):
content_length = self.buffer.tell()
update_header(self.headers, "Content-Length" , str(content_length))
- self.start_response(self.status, self.headers) \ No newline at end of file
+ self.start_response(self.status, self.headers)
diff --git a/module/webui/servers/lighttpd_default.conf b/module/webui/servers/lighttpd_default.conf
new file mode 100644
index 000000000..aa199b3f3
--- /dev/null
+++ b/module/webui/servers/lighttpd_default.conf
@@ -0,0 +1,154 @@
+# lighttpd configuration file
+#
+# use it as a base for lighttpd 1.0.0 and above
+#
+# $Id: lighttpd.conf,v 1.7 2004/11/03 22:26:05 weigon Exp $
+
+############ Options you really have to take care of ####################
+
+## modules to load
+# at least mod_access and mod_accesslog should be loaded
+# all other module should only be loaded if really neccesary
+# - saves some time
+# - saves memory
+server.modules = (
+ "mod_rewrite",
+ "mod_redirect",
+ "mod_alias",
+ "mod_access",
+# "mod_trigger_b4_dl",
+# "mod_auth",
+# "mod_status",
+# "mod_setenv",
+ "mod_fastcgi",
+# "mod_proxy",
+# "mod_simple_vhost",
+# "mod_evhost",
+# "mod_userdir",
+# "mod_cgi",
+# "mod_compress",
+# "mod_ssi",
+# "mod_usertrack",
+# "mod_expire",
+# "mod_secdownload",
+# "mod_rrdtool",
+# "mod_accesslog"
+)
+
+## A static document-root. For virtual hosting take a look at the
+## mod_simple_vhost module.
+server.document-root = "%(path)"
+
+## where to send error-messages to
+server.errorlog = "%(path)/error.log"
+
+# files to check for if .../ is requested
+index-file.names = ( "index.php", "index.html",
+ "index.htm", "default.htm" )
+
+## set the event-handler (read the performance section in the manual)
+# server.event-handler = "freebsd-kqueue" # needed on OS X
+
+# mimetype mapping
+mimetype.assign = (
+ ".pdf" => "application/pdf",
+ ".sig" => "application/pgp-signature",
+ ".spl" => "application/futuresplash",
+ ".class" => "application/octet-stream",
+ ".ps" => "application/postscript",
+ ".torrent" => "application/x-bittorrent",
+ ".dvi" => "application/x-dvi",
+ ".gz" => "application/x-gzip",
+ ".pac" => "application/x-ns-proxy-autoconfig",
+ ".swf" => "application/x-shockwave-flash",
+ ".tar.gz" => "application/x-tgz",
+ ".tgz" => "application/x-tgz",
+ ".tar" => "application/x-tar",
+ ".zip" => "application/zip",
+ ".mp3" => "audio/mpeg",
+ ".m3u" => "audio/x-mpegurl",
+ ".wma" => "audio/x-ms-wma",
+ ".wax" => "audio/x-ms-wax",
+ ".ogg" => "application/ogg",
+ ".wav" => "audio/x-wav",
+ ".gif" => "image/gif",
+ ".jar" => "application/x-java-archive",
+ ".jpg" => "image/jpeg",
+ ".jpeg" => "image/jpeg",
+ ".png" => "image/png",
+ ".xbm" => "image/x-xbitmap",
+ ".xpm" => "image/x-xpixmap",
+ ".xwd" => "image/x-xwindowdump",
+ ".css" => "text/css",
+ ".html" => "text/html",
+ ".htm" => "text/html",
+ ".js" => "text/javascript",
+ ".asc" => "text/plain",
+ ".c" => "text/plain",
+ ".cpp" => "text/plain",
+ ".log" => "text/plain",
+ ".conf" => "text/plain",
+ ".text" => "text/plain",
+ ".txt" => "text/plain",
+ ".dtd" => "text/xml",
+ ".xml" => "text/xml",
+ ".mpeg" => "video/mpeg",
+ ".mpg" => "video/mpeg",
+ ".mov" => "video/quicktime",
+ ".qt" => "video/quicktime",
+ ".avi" => "video/x-msvideo",
+ ".asf" => "video/x-ms-asf",
+ ".asx" => "video/x-ms-asf",
+ ".wmv" => "video/x-ms-wmv",
+ ".bz2" => "application/x-bzip",
+ ".tbz" => "application/x-bzip-compressed-tar",
+ ".tar.bz2" => "application/x-bzip-compressed-tar",
+ # default mime type
+ "" => "application/octet-stream",
+)
+
+# Use the "Content-Type" extended attribute to obtain mime type if possible
+#mimetype.use-xattr = "enable"
+
+#### accesslog module
+accesslog.filename = "%(path)/access.log"
+
+url.access-deny = ( "~", ".inc" )
+
+$HTTP["url"] =~ "\.pdf$" {
+ server.range-requests = "disable"
+}
+
+static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
+
+server.pid-file = "%(path)/lighttpd.pid"
+server.bind = "%(host)"
+server.port = %(port)
+
+#server.document-root = "/home/user/public_html"
+
+fastcgi.server = (
+ "/pyload.fcgi" => (
+ "main" => (
+ "host" => "127.0.0.1",
+ "port" => 9295,
+ "check-local" => "disable",
+ "docroot" => "/",
+ )
+ ),
+)
+
+alias.url = (
+ "/media/" => "%(media)/",
+ "/admin/media/" => "/usr/lib/python%(version)/site-packages/django/contrib/admin/media/",
+)
+
+url.rewrite-once = (
+ "^(/media.*)$" => "$1",
+ "^(/admin/media.*)$" => "$1",
+ "^/favicon\.ico$" => "/media/img/favicon.ico",
+ "^(/pyload.fcgi.*)$" => "$1",
+ "^(/.*)$" => "/pyload.fcgi$1",
+)
+
+%(ssl)
diff --git a/module/web/servers/nginx_default.conf b/module/webui/servers/nginx_default.conf
index b4ebd1e02..c1cfcafca 100644
--- a/module/web/servers/nginx_default.conf
+++ b/module/webui/servers/nginx_default.conf
@@ -65,7 +65,7 @@ http {
}
location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|mov) {
access_log off;
- expires 30d;
+ expires 30d;
}
location / {
# host and port to fastcgi server
diff --git a/module/webui/themes/Dark/css/MooDialog.css b/module/webui/themes/Dark/css/MooDialog.css
new file mode 100644
index 000000000..1714e170f
--- /dev/null
+++ b/module/webui/themes/Dark/css/MooDialog.css
@@ -0,0 +1,94 @@
+/* Created by Arian Stolwijk <http://www.aryweb.nl> */
+
+.MooDialog {
+/* position: fixed;*/
+ margin: 0 auto 0 -350px;
+ width:600px;
+ padding:14px;
+ left:50%;
+ top: 100px;
+ color:white;
+
+ position: absolute;
+ left: 50%;
+ z-index: 50000;
+
+ background: url(../img/dark-bg.jpg);
+ color: black;
+ border-radius: 7px;
+ -moz-border-radius: 7px;
+ -webkit-border-radius: 7px;
+ border-radius: 7px;
+ -moz-box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
+ -webkit-box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
+ box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
+}
+
+.MooDialogTitle {
+ padding-top: 30px;
+}
+
+.MooDialog .title {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ padding: 3px 20px;
+ background: #b7c4dc;
+ border-bottom: 1px solid #a1aec5;
+ font-weight: bold;
+ text-shadow: 1px 1px 0 #fff;
+ color: black;
+ border-radius: 7px;
+ -moz-border-radius: 7px;
+ -webkit-border-radius: 7px;
+}
+
+.MooDialog .close {
+ background: url(../img/MooDialog/dialog-close.png) no-repeat;
+ width: 16px;
+ height: 16px;
+ display: block;
+ cursor: pointer;
+ top: -5px;
+ left: -5px;
+ position: absolute;
+}
+
+.MooDialog .buttons {
+ text-align: right;
+ margin: 0;
+ padding: 0;
+ border: 0;
+ background: none;
+}
+
+.MooDialog .iframe {
+ width: 100%;
+ height: 100%;
+}
+
+.MooDialog .textInput {
+ width: 200px;
+ float: left;
+ color:white;
+}
+
+.MooDialog .MooDialogAlert,
+.MooDialog .MooDialogConfirm,
+.MooDialog .MooDialogPrompt,
+.MooDialog .MooDialogError {
+ background: url(../img/MooDialog/dialog-warning.png) no-repeat;
+ padding-left: 40px;
+ min-height: 40px;
+ color:white;
+}
+
+.MooDialog .MooDialogConfirm,
+.MooDialog .MooDialogPromt {
+ background: url(../img/MooDialog/dialog-question.png) no-repeat;
+}
+
+.MooDialog .MooDialogError {
+ background: url(../img/MooDialog/dialog-error.png) no-repeat;
+}
diff --git a/module/webui/themes/Dark/css/base.css b/module/webui/themes/Dark/css/base.css
new file mode 100644
index 000000000..f0bae8f24
--- /dev/null
+++ b/module/webui/themes/Dark/css/base.css
@@ -0,0 +1,962 @@
+.hidden {
+ display:none;
+}
+.leftalign {
+ text-align:left;
+}
+.centeralign {
+ text-align:center;
+}
+.rightalign {
+ text-align:right;
+}
+
+
+.dokuwiki div.plugin_translation ul li a.wikilink1:link, .dokuwiki div.plugin_translation ul li a.wikilink1:hover, .dokuwiki div.plugin_translation ul li a.wikilink1:active, .dokuwiki div.plugin_translation ul li a.wikilink1:visited {
+ background-color:#000080;
+ color:#fff !important;
+ text-decoration:none;
+ padding:0 0.2em;
+ margin:0.1em 0.2em;
+ border:none !important;
+}
+.dokuwiki div.plugin_translation ul li a.wikilink2:link, .dokuwiki div.plugin_translation ul li a.wikilink2:hover, .dokuwiki div.plugin_translation ul li a.wikilink2:active, .dokuwiki div.plugin_translation ul li a.wikilink2:visited {
+ background-color:#808080;
+ color:#fff !important;
+ text-decoration:none;
+ padding:0 0.2em;
+ margin:0.1em 0.2em;
+ border:none !important;
+}
+
+.dokuwiki div.plugin_translation ul li a:hover img {
+ opacity:1.0;
+ height:15px;
+}
+
+body {
+ margin:0;
+ padding:0;
+ background-image: url(../img/dark-bg.jpg);
+ color:white;
+ font-size:12px;
+ font-family:Verdana, Helvetica, "Lucida Grande", Lucida, Arial, sans-serif;
+ font-family:sans-serif;
+ font-size:99, 96%;
+ font-size-adjust:none;
+ font-style:normal;
+ font-variant:normal;
+ font-weight:normal;
+ line-height:normal;
+}
+hr {
+ border-width:0;
+ border-bottom:1px #aaa dotted;
+}
+img {
+ border:none;
+}
+form {
+ margin:0px;
+ padding:0px;
+ border:none;
+ display:inline;
+ background:transparent;
+}
+ul li {
+ margin:5px;
+}
+textarea {
+ font-family:monospace;
+}
+table {
+ margin:0.5em 0;
+ border-collapse:collapse;
+}
+td {
+ padding:0.25em;
+ border:1pt solid #ADB9CC;
+}
+a {
+ color:#3465a4;
+ text-decoration:none;
+}
+a:hover {
+ text-decoration:underline;
+}
+
+option {
+ border:0 none #fff;
+}
+strong.highlight {
+ background-color:#fc9;
+ padding:1pt;
+}
+#pagebottom {
+ clear:both;
+}
+hr {
+ height:1px;
+ color:#c0c0c0;
+ background-color:#c0c0c0;
+ border:none;
+ margin:.2em 0 .2em 0;
+}
+
+.invisible {
+ margin:0px;
+ border:0px;
+ padding:0px;
+ height:0px;
+ visibility:hidden;
+}
+.left {
+ float:left !important;
+}
+.right {
+ float:right !important;
+}
+.center {
+ text-align:center;
+}
+div#body-wrapper {
+ padding:40px 40px 10px 40px;
+ font-size:127%;
+}
+div#content {
+ margin-top:-20px;
+ padding:0;
+ font-size:14px;
+ color:white;
+ line-height:1.5em;
+}
+h1, h2, h3, h4, h5, h6 {
+ background:transparent none repeat scroll 0 0;
+ border-bottom:1px solid #aaa;
+ color:white;
+ font-weight:normal;
+ margin:0;
+ padding:0;
+ padding-bottom:0.17em;
+ padding-top:0.5em;
+}
+h1 {
+ font-size:188%;
+ line-height:1.2em;
+ margin-bottom:0.1em;
+ padding-bottom:0;
+}
+h2 {
+ font-size:150%;
+}
+h3, h4, h5, h6 {
+ border-bottom:none;
+ font-weight:bold;
+}
+h3 {
+ font-size:132%;
+}
+h4 {
+ font-size:116%;
+}
+h5 {
+ font-size:100%;
+}
+h6 {
+ font-size:80%;
+}
+ul#page-actions, ul#page-actions-more {
+ float:right;
+ margin:10px 10px 0 10px;
+ padding:6px;
+ color:white;
+ background-color:#202020;
+ list-style-type:none;
+ white-space: nowrap;
+ border-radius:5px;
+ -moz-border-radius:5px;
+ border:1px solid grey;
+}
+ul#user-actions {
+ padding:5px;
+ margin:0;
+ display:inline;
+ color:white;
+ background-color:#202020;
+ list-style-type:none;
+ -moz-border-radius:3px;
+ border-radius:3px;
+}
+ul#page-actions li, ul#user-actions li, ul#page-actions-more li {
+ display:inline;
+}
+ul#page-actions a, ul#user-actions a, ul#page-actions-more a {
+ text-decoration:none;
+ color:white;
+ display:inline;
+ margin:0 3px;
+ padding:2px 0px 2px 18px;
+}
+ul#page-actions a:hover, ul#page-actions a:focus, ul#user-actions a:hover, ul#user-actions a:focus {
+ /*text-decoration:underline;*/
+}
+/***************************/
+ul#page-actions2 {
+ float:left;
+ margin:10px 10px 0 10px;
+ padding:6px;
+ color:white;
+ background-color:#202020;
+ list-style-type:none;
+ border-radius:5px;
+ -moz-border-radius:5px;
+ border:1px solid grey;
+}
+ul#user-actions2 {
+ padding:5px;
+ margin:0;
+ display:inline;
+ color:white;
+ background-color:#202020;
+ list-style-type:none;
+ border-radius:3px;
+ -moz-border-radius:3px;
+}
+ul#page-actions2 li, ul#user-actions2 li {
+ display:inline;
+}
+ul#page-actions2 a, ul#user-actions2 a {
+ text-decoration:none;
+ color:white;
+ display:inline;
+ margin:0 3px;
+ padding:2px 0px 2px 18px;
+}
+ul#page-actions2 a:hover, ul#page-actions2 a:focus, ul#user-actions2 a:hover, ul#user-actions2 a:focus,
+ul#page-actions-more a:hover, ul#page-actions-more a:focus{
+ color: #4e7bb4;
+}
+/****************************/
+.hidden {
+ display:none;
+}
+
+a.logout {
+ background:transparent url(../img/default/user-actions-logout.png) 0px 1px no-repeat;
+}
+
+a.info {
+ background:transparent url(../img/default/user-info.png) 0px 1px no-repeat;
+}
+
+a.admin {
+ background:transparent url(../img/default/user-actions-admin.png) 0px 1px no-repeat;
+}
+a.profile {
+ background:transparent url(../img/default/user-actions-profile.png) 0px 1px no-repeat;
+}
+a.create, a.edit {
+ background:transparent url(../img/default/page-tools-edit.png) 0px 1px no-repeat;
+}
+a.source, a.show {
+ background:transparent url(../img/default/page-tools-source.png) 0px 1px no-repeat;
+}
+a.revisions {
+ background:transparent url(../img/default/page-tools-revisions.png) 0px 1px no-repeat;
+}
+a.subscribe, a.unsubscribe {
+ background:transparent url(../img/default/page-tools-subscribe.png) 0px 1px no-repeat;
+}
+a.backlink {
+ background:transparent url(../img/default/page-tools-backlinks.png) 0px 1px no-repeat;
+}
+a.play {
+ background:transparent url(../img/default/control_play.png) 0px 1px no-repeat;
+}
+.time {
+ background:transparent url(../img/default/status_None.png) 0px 1px no-repeat;
+ padding: 2px 0px 2px 18px;
+ margin: 0px 3px;
+}
+.reconnect {
+ background:transparent url(../img/default/reconnect.png) 0px 1px no-repeat;
+ padding: 2px 0px 2px 18px;
+ margin: 0px 3px;
+}
+a.play:hover {
+ background:transparent url(../img/default/control_play_blue.png) 0px 1px no-repeat;
+}
+a.cancel {
+ background:transparent url(../img/default/control_cancel.png) 0px 1px no-repeat;
+}
+a.cancel:hover {
+ background:transparent url(../img/default/control_cancel_blue.png) 0px 1px no-repeat;
+}
+a.pause {
+ background:transparent url(../img/default/control_pause.png) 0px 1px no-repeat;
+}
+a.pause:hover {
+ background:transparent url(../img/default/control_pause_blue.png) 0px 1px no-repeat;
+ font-weight: bold;
+}
+a.stop {
+ background:transparent url(../img/default/control_stop.png) 0px 1px no-repeat;
+}
+a.stop:hover {
+ background:transparent url(../img/default/control_stop_blue.png) 0px 1px no-repeat;
+}
+a.add {
+ background:transparent url(../img/default/control_add.png) 0px 1px no-repeat;
+}
+a.add:hover {
+ background:transparent url(../img/default/control_add_blue.png) 0px 1px no-repeat;
+}
+a.cog {
+ background:transparent url(../img/default/cog.png) 0px 1px no-repeat;
+}
+#head-panel {
+ background:#525252 url(../img/default/head_bg1.png) bottom left repeat-x;
+}
+#head-panel h1 {
+ display:none;
+ margin:0;
+ text-decoration:none;
+ padding-top:0.8em;
+ padding-left:3.3em;
+ font-size:2.6em;
+ color:#eeeeec;
+}
+#head-panel #head-logo {
+ float:left;
+ margin:5px 0 -15px 5px;
+ padding:0;
+ overflow:visible;
+}
+#head-menu {
+ background:transparent url(../img/default/tabs-border-bottom.png) 0 100% repeat-x;
+ width:100%;
+ float:left;
+ margin:0;
+ padding:0;
+ padding-top:0.8em;
+}
+#head-menu ul {
+ list-style:none;
+ margin:0 1em 0 2em;
+}
+#head-menu ul li {
+ float:left;
+ margin:0;
+ margin-left:0.3em;
+ font-size:14px;
+ margin-bottom:4px;
+}
+#head-menu ul li.selected, #head-menu ul li:hover {
+ margin-bottom:0px;
+}
+#head-menu ul li a img {
+ height:22px;
+ width:22px;
+ vertical-align:middle;
+}
+#head-menu ul li a, #head-menu ul li a:link {
+ float:left;
+ text-decoration:none;
+ color:white;
+ background: url(../img/dark-bg.jpg) 0 100% repeat-x;
+ padding:3px 7px 3px 7px;
+ border:2px solid #ccc;
+ border-bottom:0px solid transparent;
+ padding-bottom:3px;
+ -moz-border-radius:5px;
+ border-radius:5px;
+}
+#head-menu ul li a:hover, #head-menu ul li a:focus {
+ color:#3465a4;
+ background-image: url(../img/dark-bg.jpg);
+ padding-bottom:7px;
+ border-bottom:0px none transparent;
+ outline:none;
+ border-bottom-left-radius: 0px;
+ border-bottom-right-radius: 0px;
+ -moz-border-radius-bottomright:0px;
+ -moz-border-radius-bottomleft:0px;
+}
+#head-menu ul li a:focus {
+ margin-bottom:-4px;
+}
+#head-menu ul li.selected a {
+ color:white;
+ background-image: url(../img/dark-bg.jpg);
+ padding-bottom:7px;
+ border-bottom:0px none transparent;
+ border-bottom-left-radius: 0px;
+ border-bottom-right-radius: 0px;
+ -moz-border-radius-bottomright:0px;
+ -moz-border-radius-bottomleft:0px;
+}
+#head-menu ul li.selected a:hover, #head-menu ul li.selected a:focus {
+ color:#3465a4;
+}
+div#head-search-and-login {
+ float:right;
+ margin:0 1em 0 0;
+ background-color:#222;
+ padding:7px 7px 5px 5px;
+ color:white;
+ white-space: nowrap;
+ border-bottom-left-radius: 6px;
+ border-bottom-right-radius: 6px;
+ -moz-border-radius-bottomright:6px;
+ -moz-border-radius-bottomleft:6px;
+ border-right:1px solid grey;
+ border-left:1px solid grey;
+ border-bottom:1px solid grey;
+}
+div#head-search-and-login form {
+ display:inline;
+ padding:0 3px;
+}
+div#head-search-and-login form input {
+ border:2px solid #888;
+ background:#eee;
+ font-size:14px;
+ padding:2px;
+ border-radius:3px;
+ -moz-border-radius:3px;
+}
+div#head-search-and-login form input:focus {
+ background:#fff;
+}
+#head-search {
+ font-size:14px;
+}
+#head-username, #head-password {
+ width:80px;
+ font-size:14px;
+}
+#pageinfo {
+ clear:both;
+ color:#888;
+ padding:0.6em 0;
+ margin:0;
+}
+#foot {
+ font-style:normal;
+ color:#888;
+ text-align:center;
+}
+#foot a {
+ color:#aaf;
+}
+#foot img {
+ vertical-align:middle;
+}
+div.toc {
+ border:1px dotted #888;
+ background:#f0f0f0;
+ margin:1em 0 1em 1em;
+ float:right;
+ font-size:95%;
+}
+div.toc .tocheader {
+ font-weight:bold;
+ margin:0.5em 1em;
+}
+div.toc ol {
+ margin:1em 0.5em 1em 1em;
+ padding:0;
+}
+div.toc ol li {
+ margin:0;
+ padding:0;
+ margin-left:1em;
+}
+div.toc ol ol {
+ margin:0.5em 0.5em 0.5em 1em;
+ padding:0;
+}
+div.recentchanges table {
+ clear:both;
+}
+div#editor-help {
+ font-size:90%;
+ border:1px dotted #888;
+ padding:0ex 1ex 1ex 1ex;
+ background:#f7f6f2;
+}
+div#preview {
+ margin-top:1em;
+}
+label.block {
+ display:block;
+ text-align:right;
+ font-weight:bold;
+}
+label.simple {
+ display:block;
+ text-align:left;
+ font-weight:normal;
+}
+label.block input.edit {
+ width:50%;
+}
+/*fieldset {
+ width:300px;
+ text-align:center;
+ padding:0.5em;
+ margin:auto;
+}
+*/
+div.editor {
+ margin:0 0 0 0;
+}
+table {
+ margin:0.5em 0;
+ border-collapse:collapse;
+}
+td {
+ padding:0.25em;
+ border:1pt solid #ADB9CC;
+}
+td p {
+ margin:0;
+ padding:0;
+}
+.u {
+ text-decoration:underline;
+}
+.footnotes ul {
+ padding:0 2em;
+ margin:0 0 1em;
+}
+.footnotes li {
+ list-style:none;
+}
+.userpref table, .userpref td {
+ border:none;
+}
+#message {
+ clear:both;
+ padding:5px 10px;
+ background-color:#eee;
+ border-bottom:2px solid #ccc;
+}
+#message p {
+ margin:5px 0;
+ padding:0;
+ font-weight:bold;
+}
+#message div.buttons {
+ font-weight:normal;
+}
+.diff {
+ width:99%;
+}
+.diff-title {
+ background-color:#C0C0C0;
+}
+.searchresult dd span {
+ font-weight:bold;
+}
+.boxtext {
+ font-family:tahoma, arial, sans-serif;
+ font-size:11px;
+ color:#000;
+ float:none;
+ padding:3px 0 0 10px;
+}
+.statusbutton {
+ width:32px;
+ height:32px;
+ float:left;
+ margin-left:-32px;
+ margin-right:5px;
+ opacity:0;
+ cursor:pointer
+}
+.dlsize {
+ float:left;
+ padding-right: 8px;
+}
+.dlspeed {
+ float:left;
+ padding-right: 8px;
+}
+.package {
+ margin-bottom: 10px;
+}
+.packagename {
+ font-weight: bold;
+}
+
+.child {
+ margin-left: 20px;
+}
+.child_status {
+ margin-right: 10px;
+}
+.child_secrow {
+ font-size: 10px;
+}
+
+.header, .header th {
+ text-align: left;
+ font-weight: normal;
+ background-color:#202020;
+ border-top:1px solid grey;
+ border-bottom:1px solid grey;
+ -moz-border-radius:5px;
+ border-radius:5px;
+}
+.progress_bar {
+ background: #0C0;
+ height: 5px;
+
+}
+
+.queue {
+ border: none
+}
+
+.queue tr td {
+ border: none
+}
+
+.header, .header th{
+ text-align: left;
+ font-weight: normal;
+}
+
+
+.clearer
+{
+ clear: both;
+ height: 1px;
+}
+
+.left
+{
+ float: left;
+}
+
+.right
+{
+ float: right;
+}
+
+
+.setfield
+{
+ display: table-cell;
+}
+
+ul.tabs li a
+{
+ padding: 5px 16px 4px 15px;
+ border: none;
+ font-weight: bold;
+
+ border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+
+}
+
+
+#tabs span
+{
+ display: none;
+}
+
+#tabs span.selected
+{
+ display: inline;
+}
+
+#tabsback
+{
+ background-color: #525252;
+ margin: 2px 0 0;
+ padding: 6px 4px 1px 4px;
+
+ border-top-right-radius: 30px;
+ border-top-left-radius: 3px;
+ -moz-border-radius-topright: 30px;
+ -moz-border-radius-topleft: 3px;
+}
+ul.tabs
+{
+ list-style-type: none;
+ margin:0;
+ padding: 0 40px 0 0;
+}
+
+ul.tabs li
+{
+ display: inline;
+ margin-left: 8px;
+}
+
+ul.tabs li a
+{
+ color: white;
+ background-color: #202020;
+ border: 1px solid grey;
+ border-bottom:none;
+ margin: 0;
+ text-decoration: none;
+
+ outline: 0;
+
+ padding: 5px 16px 4px 15px;
+ font-weight: bold;
+
+ border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+
+}
+
+ul.tabs li a.selected, ul.tabs li a:hover
+{
+ color: #3465a4;
+ background-color: white;
+
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-border-radius-bottomright: 0;
+ -moz-border-radius-bottomleft: 0;
+}
+
+ul.tabs li a:hover
+{
+ background-color: #202020;
+}
+
+ul.tabs li a.selected
+{
+ font-weight: bold;
+ background-color: #525252;
+ padding-bottom: 5px;
+ color: white;
+}
+
+
+#tabs-body {
+ position: relative;
+ overflow: hidden;
+}
+
+
+span.tabContent
+{
+ border: 2px solid #525252;
+ margin: 0;
+ padding: 0;
+ padding-bottom: 10px;
+}
+
+#tabs-body > span {
+ display: none;
+}
+
+#tabs-body > span.active {
+ display: block;
+}
+
+.hide
+{
+ display: none;
+}
+
+.settable
+{
+ color:white;
+ margin: 20px;
+ border: none;
+}
+.settable td
+{
+ border: none;
+ margin: 0;
+ padding: 5px;
+}
+
+.settable th{
+ padding-bottom: 8px;
+}
+
+.settable.wide td , .settable.wide th {
+ padding-left: 15px;
+ padding-right: 15px;
+}
+
+.settable input {
+background-color:#202020;
+color:white;
+}
+.settable select {
+background-color:#202020;
+color:white;
+}
+
+ul.nav {
+ margin: -30px 0 0;
+ padding: 0;
+ list-style: none;
+ position: absolute;
+}
+
+
+ul.nav li {
+ position: relative;
+ float: left;
+ padding: 5px;
+}
+
+ul.nav > li a {
+ background: #202020;
+ -moz-border-radius: 4px 4px 4px 4px;
+ border: 1px solid grey;
+ border-bottom: medium none;
+ color: white;
+}
+
+ul.nav ul {
+ position: absolute;
+ top: 26px;
+ left: 10px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ border: 1px solid #AAA;
+ background: #202020;
+ -webkit-box-shadow: 1px 1px 5px #AAA;
+ -moz-box-shadow: 1px 1px 5px #AAA;
+ box-shadow: 1px 1px 5px #AAA;
+ cursor: pointer;
+}
+
+ul.nav .open {
+ display: block;
+}
+
+ul.nav .close {
+ display: none;
+}
+
+ul.nav ul li {
+ float: none;
+ padding: 0;
+}
+
+ul.nav ul li a {
+ width: 130px;
+ background: #f1f1f1;
+ padding: 3px;
+ display: block;
+ font-weight: normal;
+}
+
+ul.nav ul li a:hover {
+ background: #CDCDCD;
+}
+
+ul.nav ul ul {
+ left: 137px;
+ top: 0;
+}
+
+.purr-wrapper{
+ margin:10px;
+}
+
+/*Purr alert styles*/
+
+.purr-alert{
+ margin-bottom:10px;
+ padding:10px;
+ background:#000;
+ font-size:13px;
+ font-weight:bold;
+ color:#FFF;
+ -moz-border-radius:5px;
+ -webkit-border-radius:5px;
+ /*-moz-box-shadow: 0 0 10px rgba(255,255,0,.25);*/
+ width:300px;
+}
+.purr-alert.error{
+ color:#F55;
+ padding-left:30px;
+ background:url(../img/default/error.png) no-repeat #000 7px 10px;
+ width:280px;
+}
+.purr-alert.success{
+ color:#5F5;
+ padding-left:30px;
+ background:url(../img/default/success.png) no-repeat #000 7px 10px;
+ width:280px;
+}
+.purr-alert.notice{
+ color:#99F;
+ padding-left:30px;
+ background:url(../img/default/notice.png) no-repeat #000 7px 10px;
+ width:280px;
+}
+
+table.system {
+ border: none;
+ margin-left: 10px;
+}
+
+table.system td {
+ border: none
+}
+
+table.system tr > td:first-child {
+ font-weight: bold;
+ padding-right: 10px;
+}
+
+#foot {
+color:white;
+}
+
+#login_table {
+margin-left:auto;
+margin-right:auto;
+}
+
+#login_table td {
+padding:5px;
+border:1px solid grey;
+}
+
+#login_table input[type=text], #login_table input[type=password] {
+width:120px;
+background-color:transparent;
+-moz-opacity: 0.10;
+color:white;
+border: 1px solid grey;
+-moz-border-radius: 2px;
+-webkit-border-radius: 2px;
+border-radius: 18px;
+padding-left:5px;
+padding-right:5px;
+}
+
+#login_table input[type=text]:focus, #login_table input[type=password]:focus {
+border:1px solid #3465a4;
+}
+
+#login_table input[type=submit] {
+background-color:transparent;
+color:white;
+border: 1px solid grey;
+-moz-border-radius: 2px;
+-webkit-border-radius: 2px;
+border-radius: 18px;
+}
+
+#login_table input[type=submit]:hover {
+border:1px solid #3465a4;
+} \ No newline at end of file
diff --git a/module/webui/themes/Dark/css/log.css b/module/webui/themes/Dark/css/log.css
new file mode 100644
index 000000000..3362e9381
--- /dev/null
+++ b/module/webui/themes/Dark/css/log.css
@@ -0,0 +1,75 @@
+
+html, body, #content
+{
+ height: 100%;
+}
+#body-wrapper
+{
+ height: 70%;
+}
+.logdiv
+{
+ height: 90%;
+ width: 100%;
+ overflow: auto;
+ border: 2px solid #CCC;
+ outline: 1px solid #666;
+ background-color: #FFE;
+ margin-right: auto;
+ margin-left: auto;
+ background-color:#202020;
+ color:white;
+}
+.logform
+{
+ display: table;
+ margin: 0 auto 0 auto;
+ padding-top: 5px;
+ color: white;
+}
+.logtable
+{
+
+ margin: 0px;
+}
+.logtable td
+{
+ border: none;
+ white-space: nowrap;
+
+
+ font-family: monospace;
+ font-size: 16px;
+ margin: 0px;
+ padding: 0px 10px 0px 10px;
+ line-height: 110%;
+}
+td.logline
+{
+ background-color: #202020;
+ text-align:right;
+ padding: 0px 5px 0px 5px;
+}
+td.loglevel
+{
+ text-align:right;
+}
+.logperpage
+{
+ float: right;
+ padding-bottom: 8px;
+}
+.logpaginator
+{
+ float: left;
+ padding-top: 5px;
+}
+.logpaginator a
+{
+ padding: 0px 8px 0px 8px;
+}
+.logwarn
+{
+ text-align: center;
+ color: red;
+} \ No newline at end of file
diff --git a/module/webui/themes/Dark/css/pathchooser.css b/module/webui/themes/Dark/css/pathchooser.css
new file mode 100644
index 000000000..204812aa1
--- /dev/null
+++ b/module/webui/themes/Dark/css/pathchooser.css
@@ -0,0 +1,68 @@
+table {
+ width: 90%;
+ border: 1px dotted #888888;
+ font-family: sans-serif;
+ font-size: 10pt;
+}
+
+th {
+ background-color: #525252;
+ color: #E0E0E0;
+}
+
+table, tr, td {
+ background-color: #F0F0F0;
+}
+
+a, a:visited {
+ text-decoration: none;
+ font-weight: bold;
+}
+
+#paths {
+ width: 90%;
+ text-align: left;
+}
+
+.file_directory {
+ color: #c0c0c0;
+}
+.path_directory {
+ color: #3c3c3c;
+}
+.file_file {
+ color: #3c3c3c;
+}
+.path_file {
+ color: #c0c0c0;
+}
+
+.parentdir {
+ color: #000000;
+ font-size: 10pt;
+}
+.name {
+ text-align: left;
+}
+.size {
+ text-align: right;
+}
+.type {
+ text-align: left;
+}
+.mtime {
+ text-align: center;
+}
+
+.path_abs_rel {
+ color: #3c3c3c;
+ text-decoration: none;
+ font-weight: bold;
+ font-family: sans-serif;
+ font-size: 10pt;
+}
+
+.path_abs_rel a {
+ color: #3c3c3c;
+ font-style: italic;
+}
diff --git a/module/webui/themes/Dark/css/window.css b/module/webui/themes/Dark/css/window.css
new file mode 100644
index 000000000..1bcc58e6c
--- /dev/null
+++ b/module/webui/themes/Dark/css/window.css
@@ -0,0 +1,92 @@
+/* ----------- stylized ----------- */
+.window_table td {
+border:none;
+text-align:left;
+}
+#add_box {
+background-image: url(../img/dark-bg.jpg);
+color:white;
+}
+#pack_box {
+background-image: url(../img/dark-bg.jpg);
+color:white;
+}
+.window_box h1{
+ font-size:14px;
+ font-weight:bold;
+ margin-bottom:8px;
+}
+.window_box p{
+ font-size:11px;
+ color:white;
+ margin-bottom:20px;
+ border-bottom:solid 1px #b7ddf2;
+ padding-bottom:10px;
+}
+.window_box label{ /*Linke Seite*/
+ display:block;
+ font-weight:bold;
+ text-align:right;
+ width:240px;
+ float:left;
+ color:white;
+}
+.window_box .small{
+ color:grey;
+ display:block;
+ font-size:11px;
+ font-weight:normal;
+ text-align:right;
+ width:240px;
+}
+.window_box select, .window_box input{
+ float:left;
+ font-size:12px;
+ padding:4px 2px;
+ border:solid 1px #aacfe4;
+ width:300px;
+ margin:2px 0 20px 10px;
+ background-color:#202020;
+ color:white;
+}
+.window_box .cont{
+ float:left;
+ font-size:12px;
+ padding: 0px 10px 15px 0px;
+ width:300px;
+ margin:0px 0px 0px 10px;
+ color:white;
+}
+.window_box .cont input{
+ float: none;
+ margin: 0px 15px 0px 1px;
+ color:white;
+}
+.window_box textarea{
+ float:left;
+ font-size:12px;
+ padding:4px 2px;
+ border:solid 1px #aacfe4;
+ width:300px;
+ margin:2px 0 20px 10px;
+ background-color:#202020;
+ color:white;
+}
+.window_box button, .styled_button{
+ clear:both;
+ margin-left:150px;
+ width:125px;
+ height:31px;
+ background:#666666 url(../img/button.png) no-repeat;
+ text-align:center;
+ line-height:31px;
+ color:#FFFFFF;
+ font-size:11px;
+ font-weight:bold;
+ border: 0px;
+}
+
+.styled_button {
+ margin-left: 15px;
+ cursor: pointer;
+}
diff --git a/module/web/media/img/dialog-close.png b/module/webui/themes/Dark/img/MooDialog/dialog-close.png
index 81ebb88b2..81ebb88b2 100644
--- a/module/web/media/img/dialog-close.png
+++ b/module/webui/themes/Dark/img/MooDialog/dialog-close.png
Binary files differ
diff --git a/module/webui/themes/Dark/img/MooDialog/dialog-error.png b/module/webui/themes/Dark/img/MooDialog/dialog-error.png
new file mode 100644
index 000000000..d70328403
--- /dev/null
+++ b/module/webui/themes/Dark/img/MooDialog/dialog-error.png
Binary files differ
diff --git a/module/web/media/img/dialog-question.png b/module/webui/themes/Dark/img/MooDialog/dialog-question.png
index b0af3db5b..b0af3db5b 100644
--- a/module/web/media/img/dialog-question.png
+++ b/module/webui/themes/Dark/img/MooDialog/dialog-question.png
Binary files differ
diff --git a/module/webui/themes/Dark/img/MooDialog/dialog-warning.png b/module/webui/themes/Dark/img/MooDialog/dialog-warning.png
new file mode 100644
index 000000000..aad64d4be
--- /dev/null
+++ b/module/webui/themes/Dark/img/MooDialog/dialog-warning.png
Binary files differ
diff --git a/module/webui/themes/Dark/img/button.png b/module/webui/themes/Dark/img/button.png
new file mode 100644
index 000000000..bb408a7d6
--- /dev/null
+++ b/module/webui/themes/Dark/img/button.png
Binary files differ
diff --git a/module/webui/themes/Dark/img/dark-bg.jpg b/module/webui/themes/Dark/img/dark-bg.jpg
new file mode 100644
index 000000000..637fa6b93
--- /dev/null
+++ b/module/webui/themes/Dark/img/dark-bg.jpg
Binary files differ
diff --git a/module/web/media/default/img/add_folder.png b/module/webui/themes/Dark/img/default/add_folder.png
index 8acbc411b..8acbc411b 100644
--- a/module/web/media/default/img/add_folder.png
+++ b/module/webui/themes/Dark/img/default/add_folder.png
Binary files differ
diff --git a/module/web/media/default/img/ajax-loader.gif b/module/webui/themes/Dark/img/default/ajax-loader.gif
index 2fd8e0737..2fd8e0737 100644
--- a/module/web/media/default/img/ajax-loader.gif
+++ b/module/webui/themes/Dark/img/default/ajax-loader.gif
Binary files differ
diff --git a/module/web/media/default/img/arrow_refresh.png b/module/webui/themes/Dark/img/default/arrow_refresh.png
index 0de26566d..0de26566d 100644
--- a/module/web/media/default/img/arrow_refresh.png
+++ b/module/webui/themes/Dark/img/default/arrow_refresh.png
Binary files differ
diff --git a/module/web/media/default/img/arrow_right.png b/module/webui/themes/Dark/img/default/arrow_right.png
index b1a181923..b1a181923 100644
--- a/module/web/media/default/img/arrow_right.png
+++ b/module/webui/themes/Dark/img/default/arrow_right.png
Binary files differ
diff --git a/module/web/media/default/img/big_button.gif b/module/webui/themes/Dark/img/default/big_button.gif
index 7680490ea..7680490ea 100644
--- a/module/web/media/default/img/big_button.gif
+++ b/module/webui/themes/Dark/img/default/big_button.gif
Binary files differ
diff --git a/module/web/media/default/img/big_button_over.gif b/module/webui/themes/Dark/img/default/big_button_over.gif
index 2e3ee10d2..2e3ee10d2 100644
--- a/module/web/media/default/img/big_button_over.gif
+++ b/module/webui/themes/Dark/img/default/big_button_over.gif
Binary files differ
diff --git a/module/web/media/default/img/body.png b/module/webui/themes/Dark/img/default/body.png
index 7ff1043e0..7ff1043e0 100644
--- a/module/web/media/default/img/body.png
+++ b/module/webui/themes/Dark/img/default/body.png
Binary files differ
diff --git a/module/web/media/default/img/closebtn.gif b/module/webui/themes/Dark/img/default/closebtn.gif
index 3e27e6030..3e27e6030 100644
--- a/module/web/media/default/img/closebtn.gif
+++ b/module/webui/themes/Dark/img/default/closebtn.gif
Binary files differ
diff --git a/module/web/media/default/img/cog.png b/module/webui/themes/Dark/img/default/cog.png
index 67de2c6cc..67de2c6cc 100644
--- a/module/web/media/default/img/cog.png
+++ b/module/webui/themes/Dark/img/default/cog.png
Binary files differ
diff --git a/module/web/media/default/img/control_add.png b/module/webui/themes/Dark/img/default/control_add.png
index d39886893..d39886893 100644
--- a/module/web/media/default/img/control_add.png
+++ b/module/webui/themes/Dark/img/default/control_add.png
Binary files differ
diff --git a/module/web/media/default/img/control_add_blue.png b/module/webui/themes/Dark/img/default/control_add_blue.png
index d11b7f41d..d11b7f41d 100644
--- a/module/web/media/default/img/control_add_blue.png
+++ b/module/webui/themes/Dark/img/default/control_add_blue.png
Binary files differ
diff --git a/module/web/media/default/img/control_cancel.png b/module/webui/themes/Dark/img/default/control_cancel.png
index 7b9bc3fba..7b9bc3fba 100644
--- a/module/web/media/default/img/control_cancel.png
+++ b/module/webui/themes/Dark/img/default/control_cancel.png
Binary files differ
diff --git a/module/web/media/default/img/control_cancel_blue.png b/module/webui/themes/Dark/img/default/control_cancel_blue.png
index 0c5c96ce3..0c5c96ce3 100644
--- a/module/web/media/default/img/control_cancel_blue.png
+++ b/module/webui/themes/Dark/img/default/control_cancel_blue.png
Binary files differ
diff --git a/module/web/media/default/img/control_pause.png b/module/webui/themes/Dark/img/default/control_pause.png
index 2d9ce9c4e..2d9ce9c4e 100644
--- a/module/web/media/default/img/control_pause.png
+++ b/module/webui/themes/Dark/img/default/control_pause.png
Binary files differ
diff --git a/module/web/media/default/img/control_pause_blue.png b/module/webui/themes/Dark/img/default/control_pause_blue.png
index ec61099b0..ec61099b0 100644
--- a/module/web/media/default/img/control_pause_blue.png
+++ b/module/webui/themes/Dark/img/default/control_pause_blue.png
Binary files differ
diff --git a/module/web/media/default/img/control_play.png b/module/webui/themes/Dark/img/default/control_play.png
index 0846555d0..0846555d0 100644
--- a/module/web/media/default/img/control_play.png
+++ b/module/webui/themes/Dark/img/default/control_play.png
Binary files differ
diff --git a/module/web/media/default/img/control_play_blue.png b/module/webui/themes/Dark/img/default/control_play_blue.png
index f8c8ec683..f8c8ec683 100644
--- a/module/web/media/default/img/control_play_blue.png
+++ b/module/webui/themes/Dark/img/default/control_play_blue.png
Binary files differ
diff --git a/module/web/media/default/img/control_stop.png b/module/webui/themes/Dark/img/default/control_stop.png
index 893bb60e5..893bb60e5 100644
--- a/module/web/media/default/img/control_stop.png
+++ b/module/webui/themes/Dark/img/default/control_stop.png
Binary files differ
diff --git a/module/web/media/default/img/control_stop_blue.png b/module/webui/themes/Dark/img/default/control_stop_blue.png
index e6f75d232..e6f75d232 100644
--- a/module/web/media/default/img/control_stop_blue.png
+++ b/module/webui/themes/Dark/img/default/control_stop_blue.png
Binary files differ
diff --git a/module/web/media/default/img/delete.png b/module/webui/themes/Dark/img/default/delete.png
index 08f249365..08f249365 100644
--- a/module/web/media/default/img/delete.png
+++ b/module/webui/themes/Dark/img/default/delete.png
Binary files differ
diff --git a/module/web/media/default/img/drag_corner.gif b/module/webui/themes/Dark/img/default/drag_corner.gif
index befb1adf1..befb1adf1 100644
--- a/module/web/media/default/img/drag_corner.gif
+++ b/module/webui/themes/Dark/img/default/drag_corner.gif
Binary files differ
diff --git a/module/web/media/default/img/error.png b/module/webui/themes/Dark/img/default/error.png
index c37bd062e..c37bd062e 100644
--- a/module/web/media/default/img/error.png
+++ b/module/webui/themes/Dark/img/default/error.png
Binary files differ
diff --git a/module/web/media/default/img/folder.png b/module/webui/themes/Dark/img/default/folder.png
index 784e8fa48..784e8fa48 100644
--- a/module/web/media/default/img/folder.png
+++ b/module/webui/themes/Dark/img/default/folder.png
Binary files differ
diff --git a/module/web/media/default/img/full.png b/module/webui/themes/Dark/img/default/full.png
index fea52af76..fea52af76 100644
--- a/module/web/media/default/img/full.png
+++ b/module/webui/themes/Dark/img/default/full.png
Binary files differ
diff --git a/module/web/media/default/img/head-login.png b/module/webui/themes/Dark/img/default/head-login.png
index b59b7cbbf..b59b7cbbf 100644
--- a/module/web/media/default/img/head-login.png
+++ b/module/webui/themes/Dark/img/default/head-login.png
Binary files differ
diff --git a/module/web/media/default/img/head-menu-collector.png b/module/webui/themes/Dark/img/default/head-menu-collector.png
index 861be40bc..861be40bc 100644
--- a/module/web/media/default/img/head-menu-collector.png
+++ b/module/webui/themes/Dark/img/default/head-menu-collector.png
Binary files differ
diff --git a/module/web/media/default/img/head-menu-config.png b/module/webui/themes/Dark/img/default/head-menu-config.png
index bbf43d4f3..bbf43d4f3 100644
--- a/module/web/media/default/img/head-menu-config.png
+++ b/module/webui/themes/Dark/img/default/head-menu-config.png
Binary files differ
diff --git a/module/web/media/default/img/head-menu-development.png b/module/webui/themes/Dark/img/default/head-menu-development.png
index fad150fe1..fad150fe1 100644
--- a/module/web/media/default/img/head-menu-development.png
+++ b/module/webui/themes/Dark/img/default/head-menu-development.png
Binary files differ
diff --git a/module/web/media/default/img/head-menu-download.png b/module/webui/themes/Dark/img/default/head-menu-download.png
index 98c5da9db..98c5da9db 100644
--- a/module/web/media/default/img/head-menu-download.png
+++ b/module/webui/themes/Dark/img/default/head-menu-download.png
Binary files differ
diff --git a/module/web/media/default/img/head-menu-home.png b/module/webui/themes/Dark/img/default/head-menu-home.png
index 9d62109aa..9d62109aa 100644
--- a/module/web/media/default/img/head-menu-home.png
+++ b/module/webui/themes/Dark/img/default/head-menu-home.png
Binary files differ
diff --git a/module/web/media/default/img/head-menu-index.png b/module/webui/themes/Dark/img/default/head-menu-index.png
index 44d631064..44d631064 100644
--- a/module/web/media/default/img/head-menu-index.png
+++ b/module/webui/themes/Dark/img/default/head-menu-index.png
Binary files differ
diff --git a/module/web/media/default/img/head-menu-news.png b/module/webui/themes/Dark/img/default/head-menu-news.png
index 43950ebc9..43950ebc9 100644
--- a/module/web/media/default/img/head-menu-news.png
+++ b/module/webui/themes/Dark/img/default/head-menu-news.png
Binary files differ
diff --git a/module/web/media/default/img/head-menu-queue.png b/module/webui/themes/Dark/img/default/head-menu-queue.png
index be98793ce..be98793ce 100644
--- a/module/web/media/default/img/head-menu-queue.png
+++ b/module/webui/themes/Dark/img/default/head-menu-queue.png
Binary files differ
diff --git a/module/web/media/default/img/head-menu-recent.png b/module/webui/themes/Dark/img/default/head-menu-recent.png
index fc9b0497f..fc9b0497f 100644
--- a/module/web/media/default/img/head-menu-recent.png
+++ b/module/webui/themes/Dark/img/default/head-menu-recent.png
Binary files differ
diff --git a/module/web/media/default/img/head-menu-wiki.png b/module/webui/themes/Dark/img/default/head-menu-wiki.png
index 07cf0102d..07cf0102d 100644
--- a/module/web/media/default/img/head-menu-wiki.png
+++ b/module/webui/themes/Dark/img/default/head-menu-wiki.png
Binary files differ
diff --git a/module/web/media/default/img/head-search-noshadow.png b/module/webui/themes/Dark/img/default/head-search-noshadow.png
index aafdae015..aafdae015 100644
--- a/module/web/media/default/img/head-search-noshadow.png
+++ b/module/webui/themes/Dark/img/default/head-search-noshadow.png
Binary files differ
diff --git a/module/web/media/default/img/head_bg1.png b/module/webui/themes/Dark/img/default/head_bg1.png
index f2848c3cc..f2848c3cc 100644
--- a/module/web/media/default/img/head_bg1.png
+++ b/module/webui/themes/Dark/img/default/head_bg1.png
Binary files differ
diff --git a/module/web/media/default/img/images.png b/module/webui/themes/Dark/img/default/images.png
index 184860d1e..184860d1e 100644
--- a/module/web/media/default/img/images.png
+++ b/module/webui/themes/Dark/img/default/images.png
Binary files differ
diff --git a/module/web/media/default/img/notice.png b/module/webui/themes/Dark/img/default/notice.png
index 12cd1aef9..12cd1aef9 100644
--- a/module/web/media/default/img/notice.png
+++ b/module/webui/themes/Dark/img/default/notice.png
Binary files differ
diff --git a/module/web/media/default/img/package_go.png b/module/webui/themes/Dark/img/default/package_go.png
index aace63ad6..aace63ad6 100644
--- a/module/web/media/default/img/package_go.png
+++ b/module/webui/themes/Dark/img/default/package_go.png
Binary files differ
diff --git a/module/web/media/default/img/page-tools-backlinks.png b/module/webui/themes/Dark/img/default/page-tools-backlinks.png
index 3eb6a9ce3..3eb6a9ce3 100644
--- a/module/web/media/default/img/page-tools-backlinks.png
+++ b/module/webui/themes/Dark/img/default/page-tools-backlinks.png
Binary files differ
diff --git a/module/web/media/default/img/page-tools-edit.png b/module/webui/themes/Dark/img/default/page-tools-edit.png
index 188e1c12b..188e1c12b 100644
--- a/module/web/media/default/img/page-tools-edit.png
+++ b/module/webui/themes/Dark/img/default/page-tools-edit.png
Binary files differ
diff --git a/module/web/media/default/img/page-tools-revisions.png b/module/webui/themes/Dark/img/default/page-tools-revisions.png
index 5c3b8587f..5c3b8587f 100644
--- a/module/web/media/default/img/page-tools-revisions.png
+++ b/module/webui/themes/Dark/img/default/page-tools-revisions.png
Binary files differ
diff --git a/module/web/media/default/img/parseUri.png b/module/webui/themes/Dark/img/default/parseUri.png
index 937bded9d..937bded9d 100644
--- a/module/web/media/default/img/parseUri.png
+++ b/module/webui/themes/Dark/img/default/parseUri.png
Binary files differ
diff --git a/module/web/media/default/img/pencil.png b/module/webui/themes/Dark/img/default/pencil.png
index 0bfecd50e..0bfecd50e 100644
--- a/module/web/media/default/img/pencil.png
+++ b/module/webui/themes/Dark/img/default/pencil.png
Binary files differ
diff --git a/module/web/media/default/img/reconnect.png b/module/webui/themes/Dark/img/default/reconnect.png
index 49b269145..49b269145 100644
--- a/module/web/media/default/img/reconnect.png
+++ b/module/webui/themes/Dark/img/default/reconnect.png
Binary files differ
diff --git a/module/web/media/default/img/status_None.png b/module/webui/themes/Dark/img/default/status_None.png
index 293b13f77..293b13f77 100644
--- a/module/web/media/default/img/status_None.png
+++ b/module/webui/themes/Dark/img/default/status_None.png
Binary files differ
diff --git a/module/web/media/default/img/status_downloading.png b/module/webui/themes/Dark/img/default/status_downloading.png
index fb4ebc850..fb4ebc850 100644
--- a/module/web/media/default/img/status_downloading.png
+++ b/module/webui/themes/Dark/img/default/status_downloading.png
Binary files differ
diff --git a/module/web/media/default/img/status_failed.png b/module/webui/themes/Dark/img/default/status_failed.png
index c37bd062e..c37bd062e 100644
--- a/module/web/media/default/img/status_failed.png
+++ b/module/webui/themes/Dark/img/default/status_failed.png
Binary files differ
diff --git a/module/web/media/default/img/status_finished.png b/module/webui/themes/Dark/img/default/status_finished.png
index 89c8129a4..89c8129a4 100644
--- a/module/web/media/default/img/status_finished.png
+++ b/module/webui/themes/Dark/img/default/status_finished.png
Binary files differ
diff --git a/module/web/media/default/img/status_offline.png b/module/webui/themes/Dark/img/default/status_offline.png
index 0cfd58596..0cfd58596 100644
--- a/module/web/media/default/img/status_offline.png
+++ b/module/webui/themes/Dark/img/default/status_offline.png
Binary files differ
diff --git a/module/web/media/default/img/status_proc.png b/module/webui/themes/Dark/img/default/status_proc.png
index 67de2c6cc..67de2c6cc 100644
--- a/module/web/media/default/img/status_proc.png
+++ b/module/webui/themes/Dark/img/default/status_proc.png
Binary files differ
diff --git a/module/web/media/default/img/status_queue.png b/module/webui/themes/Dark/img/default/status_queue.png
index 293b13f77..293b13f77 100644
--- a/module/web/media/default/img/status_queue.png
+++ b/module/webui/themes/Dark/img/default/status_queue.png
Binary files differ
diff --git a/module/web/media/default/img/status_waiting.png b/module/webui/themes/Dark/img/default/status_waiting.png
index 2842cc338..2842cc338 100644
--- a/module/web/media/default/img/status_waiting.png
+++ b/module/webui/themes/Dark/img/default/status_waiting.png
Binary files differ
diff --git a/module/web/media/default/img/success.png b/module/webui/themes/Dark/img/default/success.png
index 89c8129a4..89c8129a4 100644
--- a/module/web/media/default/img/success.png
+++ b/module/webui/themes/Dark/img/default/success.png
Binary files differ
diff --git a/module/web/media/default/img/tabs-border-bottom.png b/module/webui/themes/Dark/img/default/tabs-border-bottom.png
index 02440f428..02440f428 100644
--- a/module/web/media/default/img/tabs-border-bottom.png
+++ b/module/webui/themes/Dark/img/default/tabs-border-bottom.png
Binary files differ
diff --git a/module/web/media/default/img/user-actions-logout.png b/module/webui/themes/Dark/img/default/user-actions-logout.png
index 0010931e2..0010931e2 100644
--- a/module/web/media/default/img/user-actions-logout.png
+++ b/module/webui/themes/Dark/img/default/user-actions-logout.png
Binary files differ
diff --git a/module/web/media/default/img/user-actions-profile.png b/module/webui/themes/Dark/img/default/user-actions-profile.png
index 46573fff6..46573fff6 100644
--- a/module/web/media/default/img/user-actions-profile.png
+++ b/module/webui/themes/Dark/img/default/user-actions-profile.png
Binary files differ
diff --git a/module/web/media/default/img/user-info.png b/module/webui/themes/Dark/img/default/user-info.png
index 6e643100f..6e643100f 100644
--- a/module/web/media/default/img/user-info.png
+++ b/module/webui/themes/Dark/img/default/user-info.png
Binary files differ
diff --git a/module/webui/themes/Dark/img/pyload-logo.png b/module/webui/themes/Dark/img/pyload-logo.png
new file mode 100644
index 000000000..e878afee5
--- /dev/null
+++ b/module/webui/themes/Dark/img/pyload-logo.png
Binary files differ
diff --git a/module/webui/themes/Dark/img/tab-background.png b/module/webui/themes/Dark/img/tab-background.png
new file mode 100644
index 000000000..ee96b8407
--- /dev/null
+++ b/module/webui/themes/Dark/img/tab-background.png
Binary files differ
diff --git a/module/webui/themes/Dark/js/render/admin.coffee b/module/webui/themes/Dark/js/render/admin.coffee
new file mode 100644
index 000000000..5afbcbb66
--- /dev/null
+++ b/module/webui/themes/Dark/js/render/admin.coffee
@@ -0,0 +1,58 @@
+root = this
+
+window.addEvent "domready", ->
+
+ root.passwordDialog = new MooDialog {destroyOnHide: false}
+ root.passwordDialog.setContent $ 'password_box'
+
+ $("login_password_reset").addEvent "click", (e) -> root.passwordDialog.close()
+ $("login_password_button").addEvent "click", (e) ->
+
+ newpw = $("login_new_password").get("value")
+ newpw2 = $("login_new_password2").get("value")
+
+ if newpw is newpw2
+ form = $("password_form")
+ form.set "send", {
+ onSuccess: (data) ->
+ root.notify.alert "Success", {
+ 'className': 'success'
+ }
+ onFailure: (data) ->
+ root.notify.alert "Error", {
+ 'className': 'error'
+ }
+ }
+
+ form.send()
+
+ root.passwordDialog.close()
+ else
+ alert '{{_("Passwords did not match.")}}'
+
+ e.stop()
+
+ for item in $$(".change_password")
+ id = item.get("id")
+ user = id.split("|")[1]
+ $("user_login").set("value", user)
+ item.addEvent "click", (e) -> root.passwordDialog.open()
+
+ $('quit-pyload').addEvent "click", (e) ->
+ new MooDialog.Confirm "{{_('You are really sure you want to quit pyLoad?')}}", ->
+ new Request.JSON({
+ url: '/api/kill'
+ method: 'get'
+ }).send()
+ , ->
+ e.stop()
+
+ $('restart-pyload').addEvent "click", (e) ->
+ new MooDialog.Confirm "{{_('Are you sure you want to restart pyLoad?')}}", ->
+ new Request.JSON({
+ url: '/api/restart'
+ method: 'get'
+ onSuccess: (data) -> alert "{{_('pyLoad restarted')}}"
+ }).send()
+ , ->
+ e.stop()
diff --git a/module/web/media/js/admin.js b/module/webui/themes/Dark/js/render/admin.min.js
index d34d310a0..94a5e494d 100644
--- a/module/web/media/js/admin.js
+++ b/module/webui/themes/Dark/js/render/admin.min.js
@@ -1,3 +1,3 @@
{% autoescape true %}
var root;root=this;window.addEvent("domready",function(){var f,c,b,e,a,d;root.passwordDialog=new MooDialog({destroyOnHide:false});root.passwordDialog.setContent($("password_box"));$("login_password_reset").addEvent("click",function(g){return root.passwordDialog.close()});$("login_password_button").addEvent("click",function(j){var h,i,g;i=$("login_new_password").get("value");g=$("login_new_password2").get("value");if(i===g){h=$("password_form");h.set("send",{onSuccess:function(k){return root.notify.alert("Success",{className:"success"})},onFailure:function(k){return root.notify.alert("Error",{className:"error"})}});h.send();root.passwordDialog.close()}else{alert('{{_("Passwords did not match.")}}')}return j.stop()});d=$$(".change_password");for(e=0,a=d.length;e<a;e++){c=d[e];f=c.get("id");b=f.split("|")[1];$("user_login").set("value",b);c.addEvent("click",function(g){return root.passwordDialog.open()})}$("quit-pyload").addEvent("click",function(g){new MooDialog.Confirm("{{_('You are really sure you want to quit pyLoad?')}}",function(){return new Request.JSON({url:"/api/kill",method:"get"}).send()},function(){});return g.stop()});return $("restart-pyload").addEvent("click",function(g){new MooDialog.Confirm("{{_('Are you sure you want to restart pyLoad?')}}",function(){return new Request.JSON({url:"/api/restart",method:"get",onSuccess:function(h){return alert("{{_('pyLoad restarted')}}")}}).send()},function(){});return g.stop()})});
-{% endautoescape %} \ No newline at end of file
+{% endautoescape %}
diff --git a/module/webui/themes/Dark/js/render/base.coffee b/module/webui/themes/Dark/js/render/base.coffee
new file mode 100644
index 000000000..07b8bfb6f
--- /dev/null
+++ b/module/webui/themes/Dark/js/render/base.coffee
@@ -0,0 +1,177 @@
+# External scope
+root = this
+
+# helper functions
+humanFileSize = (size) ->
+ filesizename = new Array("B", "KiB", "MiB", "GiB", "TiB", "PiB")
+ loga = Math.log(size) / Math.log(1024)
+ i = Math.floor(loga)
+ a = Math.pow(1024, i)
+ if size is 0 then "0 B" else (Math.round(size * 100 / a) / 100 + " " + filesizename[i])
+
+
+parseUri = () ->
+ oldString = $("add_links").value
+ regxp = new RegExp('(ht|f)tp(s?):\/\/[a-zA-Z0-9\-\.\/\?=_&%#]+[<| |\"|\'|\r|\n|\t]{1}', 'g')
+ resu = oldString.match regxp
+ return if resu == null
+ res = ""
+
+ for part in resu
+ if part.indexOf(" ") != -1
+ res = res + part.replace(" ", " \n")
+ else if part.indexOf("\t") != -1
+ res = res + part.replace("\t", " \n")
+ else if part.indexOf("\r") != -1
+ res = res + part.replace("\r", " \n")
+ else if part.indexOf("\"") != -1
+ res = res + part.replace("\"", " \n")
+ else if part.indexOf("<") != -1
+ res = res + part.replace("<", " \n")
+ else if part.indexOf("'") != -1
+ res = res + part.replace("'", " \n")
+ else
+ res = res + part.replace("\n", " \n")
+
+ $("add_links").value = res
+
+
+Array::remove = (from, to) ->
+ rest = this.slice((to || from) + 1 || this.length)
+ this.length = from < 0 ? this.length + from : from
+ return [] if this.length == 0
+ return this.push.apply(this, rest)
+
+
+document.addEvent "domready", ->
+
+ # global notification
+ root.notify = new Purr {
+ 'mode': 'top'
+ 'position': 'center'
+ }
+
+ root.captchaBox = new MooDialog {destroyOnHide: false}
+ root.captchaBox.setContent $ 'cap_box'
+
+ root.addBox = new MooDialog {destroyOnHide: false}
+ root.addBox.setContent $ 'add_box'
+
+ $('add_form').onsubmit = ->
+ $('add_form').target = 'upload_target'
+ if $('add_name').value is "" and $('add_file').value is ""
+ alert '{{_("Please Enter a packagename.")}}'
+ return false
+ else
+ root.addBox.close()
+ return true
+
+ $('add_reset').addEvent 'click', -> root.addBox.close()
+
+ $('action_add').addEvent 'click', -> $("add_form").reset(); root.addBox.open()
+ $('action_play').addEvent 'click', -> new Request({method: 'get', url: '/api/unpauseServer'}).send()
+ $('action_cancel').addEvent 'click', -> new Request({method: 'get', url: '/api/stopAllDownloads'}).send()
+ $('action_stop').addEvent 'click', -> new Request({method: 'get', url: '/api/pauseServer'}).send()
+
+
+ # captcha events
+
+ $('cap_info').addEvent 'click', ->
+ load_captcha "get", ""
+ root.captchaBox.open()
+ $('cap_reset').addEvent 'click', -> root.captchaBox.close()
+ $('cap_form').addEvent 'submit', (e) ->
+ submit_captcha()
+ e.stop()
+
+ $('cap_positional').addEvent 'click', on_captcha_click
+
+ new Request.JSON({
+ url: "/json/status"
+ onSuccess: LoadJsonToContent
+ secure: false
+ async: true
+ initialDelay: 0
+ delay: 4000
+ limit: 3000
+ }).startTimer()
+
+
+LoadJsonToContent = (data) ->
+ $("speed").set 'text', humanFileSize(data.speed)+"/s"
+ $("aktiv").set 'text', data.active
+ $("aktiv_from").set 'text', data.queue
+ $("aktiv_total").set 'text', data.total
+
+ if data.captcha
+ if $("cap_info").getStyle("display") != "inline"
+ $("cap_info").setStyle 'display', 'inline'
+ root.notify.alert '{{_("New Captcha Request")}}', {
+ 'className': 'notify'
+ }
+ else
+ $("cap_info").setStyle 'display', 'none'
+
+
+ if data.download
+ $("time").set 'text', ' {{_("on")}}'
+ $("time").setStyle 'background-color', "#8ffc25"
+ else
+ $("time").set 'text', ' {{_("off")}}'
+ $("time").setStyle 'background-color', "#fc6e26"
+
+ if data.reconnect
+ $("reconnect").set 'text', ' {{_("on")}}'
+ $("reconnect").setStyle 'background-color', "#8ffc25"
+ else
+ $("reconnect").set 'text', ' {{_("off")}}'
+ $("reconnect").setStyle 'background-color', "#fc6e26"
+
+ return null
+
+
+set_captcha = (data) ->
+ $('cap_id').set 'value', data.id
+ if (data.result_type is 'textual')
+ $('cap_textual_img').set 'src', data.src
+ $('cap_title').set 'text', '{{_("Please read the text on the captcha.")}}'
+ $('cap_submit').setStyle 'display', 'inline'
+ $('cap_textual').setStyle 'display', 'block'
+ $('cap_positional').setStyle 'display', 'none'
+
+ else if (data.result_type == 'positional')
+ $('cap_positional_img').set('src', data.src)
+ $('cap_title').set('text', '{{_("Please click on the right captcha position.")}}')
+ $('cap_submit').setStyle('display', 'none')
+ $('cap_textual').setStyle('display', 'none')
+
+
+load_captcha = (method, post) ->
+ new Request.JSON({
+ url: "/json/set_captcha"
+ onSuccess: (data) -> set_captcha(data) if data.captcha else clear_captcha()
+ secure: false
+ async: true
+ method: method
+ }).send(post)
+
+
+clear_captcha = ->
+ $('cap_textual').setStyle 'display', 'none'
+ $('cap_textual_img').set 'src', ''
+ $('cap_positional').setStyle 'display', 'none'
+ $('cap_positional_img').set 'src', ''
+ $('cap_title').set 'text', '{{_("No Captchas to read.")}}'
+
+
+submit_captcha = ->
+ load_captcha("post", "cap_id=" + $('cap_id').get('value') + "&cap_result=" + $('cap_result').get('value') )
+ $('cap_result').set('value', '')
+
+
+on_captcha_click = (e) ->
+ position = e.target.getPosition()
+ x = e.page.x - position.x
+ y = e.page.y - position.y
+ $('cap_result').value = x + "," + y
+ submit_captcha()
diff --git a/module/web/media/js/base.js b/module/webui/themes/Dark/js/render/base.min.js
index c68b1047a..1ba1d73f9 100644
--- a/module/web/media/js/base.js
+++ b/module/webui/themes/Dark/js/render/base.min.js
@@ -1,3 +1,3 @@
{% autoescape true %}
var LoadJsonToContent,clear_captcha,humanFileSize,load_captcha,on_captcha_click,parseUri,root,set_captcha,submit_captcha;root=this;humanFileSize=function(f){var c,d,e,b;d=new Array("B","KiB","MiB","GiB","TiB","PiB");b=Math.log(f)/Math.log(1024);e=Math.floor(b);c=Math.pow(1024,e);if(f===0){return"0 B"}else{return Math.round(f*100/c)/100+" "+d[e]}};parseUri=function(){var b,c,g,e,d,f,a;b=$("add_links").value;g=new RegExp("(ht|f)tp(s?)://[a-zA-Z0-9-./?=_&%#]+[<| |\"|'|\r|\n|\t]{1}","g");d=b.match(g);if(d===null){return}e="";for(f=0,a=d.length;f<a;f++){c=d[f];if(c.indexOf(" ")!==-1){e=e+c.replace(" "," \n")}else{if(c.indexOf("\t")!==-1){e=e+c.replace("\t"," \n")}else{if(c.indexOf("\r")!==-1){e=e+c.replace("\r"," \n")}else{if(c.indexOf('"')!==-1){e=e+c.replace('"'," \n")}else{if(c.indexOf("<")!==-1){e=e+c.replace("<"," \n")}else{if(c.indexOf("'")!==-1){e=e+c.replace("'"," \n")}else{e=e+c.replace("\n"," \n")}}}}}}}return $("add_links").value=e};Array.prototype.remove=function(d,c){var a,b;a=this.slice((c||d)+1||this.length);this.length=(b=d<0)!=null?b:this.length+{from:d};if(this.length===0){return[]}return this.push.apply(this,a)};document.addEvent("domready",function(){root.notify=new Purr({mode:"top",position:"center"});root.captchaBox=new MooDialog({destroyOnHide:false});root.captchaBox.setContent($("cap_box"));root.addBox=new MooDialog({destroyOnHide:false});root.addBox.setContent($("add_box"));$("add_form").onsubmit=function(){$("add_form").target="upload_target";if($("add_name").value===""&&$("add_file").value===""){alert('{{_("Please Enter a packagename.")}}');return false}else{root.addBox.close();return true}};$("add_reset").addEvent("click",function(){return root.addBox.close()});$("action_add").addEvent("click",function(){$("add_form").reset();return root.addBox.open()});$("action_play").addEvent("click",function(){return new Request({method:"get",url:"/api/unpauseServer"}).send()});$("action_cancel").addEvent("click",function(){return new Request({method:"get",url:"/api/stopAllDownloads"}).send()});$("action_stop").addEvent("click",function(){return new Request({method:"get",url:"/api/pauseServer"}).send()});$("cap_info").addEvent("click",function(){load_captcha("get","");return root.captchaBox.open()});$("cap_reset").addEvent("click",function(){return root.captchaBox.close()});$("cap_form").addEvent("submit",function(a){submit_captcha();return a.stop()});$("cap_positional").addEvent("click",on_captcha_click);return new Request.JSON({url:"/json/status",onSuccess:LoadJsonToContent,secure:false,async:true,initialDelay:0,delay:4000,limit:3000}).startTimer()});LoadJsonToContent=function(a){$("speed").set("text",humanFileSize(a.speed)+"/s");$("aktiv").set("text",a.active);$("aktiv_from").set("text",a.queue);$("aktiv_total").set("text",a.total);if(a.captcha){if($("cap_info").getStyle("display")!=="inline"){$("cap_info").setStyle("display","inline");root.notify.alert('{{_("New Captcha Request")}}',{className:"notify"})}}else{$("cap_info").setStyle("display","none")}if(a.download){$("time").set("text",' {{_("on")}}');$("time").setStyle("background-color","#8ffc25")}else{$("time").set("text",' {{_("off")}}');$("time").setStyle("background-color","#fc6e26")}if(a.reconnect){$("reconnect").set("text",' {{_("on")}}');$("reconnect").setStyle("background-color","#8ffc25")}else{$("reconnect").set("text",' {{_("off")}}');$("reconnect").setStyle("background-color","#fc6e26")}return null};set_captcha=function(a){$("cap_id").set("value",a.id);if(a.result_type==="textual"){$("cap_textual_img").set("src",a.src);$("cap_title").set("text",'{{_("Please read the text on the captcha.")}}');$("cap_submit").setStyle("display","inline");$("cap_textual").setStyle("display","block");return $("cap_positional").setStyle("display","none")}else{if(a.result_type==="positional"){$("cap_positional_img").set("src",a.src);$("cap_title").set("text",'{{_("Please click on the right captcha position.")}}');$("cap_submit").setStyle("display","none");return $("cap_textual").setStyle("display","none")}}};load_captcha=function(b,a){return new Request.JSON({url:"/json/set_captcha",onSuccess:function(c){return set_captcha(c)(c.captcha?void 0:clear_captcha())},secure:false,async:true,method:b}).send(a)};clear_captcha=function(){$("cap_textual").setStyle("display","none");$("cap_textual_img").set("src","");$("cap_positional").setStyle("display","none");$("cap_positional_img").set("src","");return $("cap_title").set("text",'{{_("No Captchas to read.")}}')};submit_captcha=function(){load_captcha("post","cap_id="+$("cap_id").get("value")+"&cap_result="+$("cap_result").get("value"));$("cap_result").set("value","");return false};on_captcha_click=function(c){var b,a,d;b=c.target.getPosition();a=c.page.x-b.x;d=c.page.y-b.y;$("cap_result").value=a+","+d;return submit_captcha()};
-{% endautoescape %} \ No newline at end of file
+{% endautoescape %}
diff --git a/module/web/media/js/package_ui.js b/module/webui/themes/Dark/js/render/package.js
index 3ea965649..659a8e6fc 100644
--- a/module/web/media/js/package_ui.js
+++ b/module/webui/themes/Dark/js/render/package.js
@@ -188,28 +188,28 @@ var Package = new Class({
},
createLinks: function(data) {
- var ul = $("sort_children_{id}".substitute({"id": this.id}));
+ var ul = $("sort_children_{id}".substitute({'id': this.id}));
ul.set("html", "");
data.links.each(function(link) {
link.id = link.fid;
var li = new Element("li", {
- "style": {
- "margin-left": 0
+ 'style': {
+ 'margin-left': 0
}
});
- var html = "<span style='cursor: move' class='child_status sorthandle'><img src='/media/default/img/{icon}' style='width: 12px; height:12px;'/></span>\n".substitute({"icon": link.icon});
- html += "<span style='font-size: 15px'>{name}</span><br /><div class='child_secrow'>".substitute({"name": link.name});
- html += "<span class='child_status'>{statusmsg}</span>{error}&nbsp;".substitute({"statusmsg": link.statusmsg, "error":link.error});
- html += "<span class='child_status'>{format_size}</span>".substitute({"format_size": link.format_size});
- html += "<span class='child_status'>{plugin}</span>&nbsp;&nbsp;".substitute({"plugin": link.plugin});
- html += "<img title='{{_("Delete Link")}}' style='cursor: pointer;' width='10px' height='10px' src='/media/default/img/delete.png' />&nbsp;&nbsp;";
- html += "<img title='{{_("Restart Link")}}' style='cursor: pointer;margin-left: -4px' width='10px' height='10px' src='/media/default/img/arrow_refresh.png' /></div>";
+ var html = "<span style='cursor: move' class='child_status sorthandle'><img src='../img/{icon}' style='width: 12px; height:12px;'/></span>\n".substitute({'icon': link.icon});
+ html += "<span style='font-size: 15px'><a href=\"{url}\" target=\"_blank\">{name}</a></span><br /><div class='child_secrow'>".substitute({'url': link.url, 'name': link.name});
+ html += "<span class='child_status'>{statusmsg}</span>{error}&nbsp;".substitute({'statusmsg': link.statusmsg, 'error':link.error});
+ html += "<span class='child_status'>{format_size}</span>".substitute({'format_size': link.format_size});
+ html += "<span class='child_status'>{plugin}</span>&nbsp;&nbsp;".substitute({'plugin': link.plugin});
+ html += "<img title='{{_(\"Delete Link\")}}' style='cursor: pointer;' width='10px' height='10px' src='../img/delete.png' />&nbsp;&nbsp;";
+ html += "<img title='{{_(\"Restart Link\")}}' style='cursor: pointer;margin-left: -4px' width='10px' height='10px' src='../img/arrow_refresh.png' /></div>";
var div = new Element("div", {
- "id": "file_" + link.id,
- "class": "child",
- "html": html
+ 'id': "file_" + link.id,
+ 'class': "child",
+ 'html': html
});
li.store("order", link.order);
@@ -254,7 +254,7 @@ var Package = new Class({
onSuccess: function() {
var ele = $('file_' + this);
var imgs = ele.getElements("img");
- imgs[0].set("src", "/media/default/img/status_queue.png");
+ imgs[0].set("src", "../img/status_queue.png");
var spans = ele.getElements(".child_status");
spans[1].set("html", "queued");
indicateSuccess();
@@ -313,7 +313,7 @@ var Package = new Class({
if (child.getStyle('display') == "block") {
child.dissolve();
}
- var ul = $("sort_children_{id}".substitute({"id": this.id}));
+ var ul = $("sort_children_{id}".substitute({'id': this.id}));
ul.erase("html");
this.linksLoaded = false;
},
@@ -374,4 +374,3 @@ var Package = new Class({
}
});
-
diff --git a/module/webui/themes/Dark/js/render/settings.coffee b/module/webui/themes/Dark/js/render/settings.coffee
new file mode 100644
index 000000000..d522741b9
--- /dev/null
+++ b/module/webui/themes/Dark/js/render/settings.coffee
@@ -0,0 +1,107 @@
+root = this
+
+window.addEvent 'domready', ->
+ root.accountDialog = new MooDialog {destroyOnHide: false}
+ root.accountDialog.setContent $ 'account_box'
+
+ new TinyTab $$('#toptabs li a'), $$('#tabs-body > span')
+
+ $$('ul.nav').each (nav) ->
+ new MooDropMenu nav, {
+ onOpen: (el) -> el.fade 'in'
+ onClose: (el) -> el.fade 'out'
+ onInitialize: (el) -> el.fade('hide').set 'tween', {duration:500}
+ }
+
+ new SettingsUI()
+
+
+class SettingsUI
+ constructor: ->
+ @menu = $$ "#general-menu li"
+ @menu.append $$ "#plugin-menu li"
+
+ @name = $ "tabsback"
+ @general = $ "general_form_content"
+ @plugin = $ "plugin_form_content"
+
+ el.addEvent 'click', @menuClick.bind(this) for el in @menu
+
+ $("general|submit").addEvent "click", @configSubmit.bind(this)
+ $("plugin|submit").addEvent "click", @configSubmit.bind(this)
+
+ $("account_add").addEvent "click", (e) ->
+ root.accountDialog.open()
+ e.stop()
+
+ $("account_reset").addEvent "click", (e) ->
+ root.accountDialog.close()
+
+ $("account_add_button").addEvent "click", @addAccount.bind(this)
+ $("account_submit").addEvent "click", @submitAccounts.bind(this)
+
+
+ menuClick: (e) ->
+ [category, section] = e.target.get("id").split("|")
+ name = e.target.get "text"
+
+
+ target = if category is "general" then @general else @plugin
+ target.dissolve()
+
+ new Request({
+ "method" : "get"
+ "url" : "/json/load_config/#{category}/#{section}"
+ 'onSuccess': (data) =>
+ target.set "html", data
+ target.reveal()
+ this.name.set "text", name
+ }).send()
+
+
+ configSubmit: (e) ->
+ category = e.target.get("id").split("|")[0]
+ form = $("#{category}_form")
+
+ form.set "send", {
+ 'method': "post"
+ 'url': "/json/save_config/#{category}"
+ "onSuccess" : ->
+ root.notify.alert '{{ _("Settings saved.")}}', {
+ 'className': 'success'
+ }
+ 'onFailure': ->
+ root.notify.alert '{{ _("Error occured.")}}', {
+ 'className': 'error'
+ }
+ }
+ form.send()
+ e.stop()
+
+ addAccount: (e) ->
+ form = $ "add_account_form"
+ form.set "send", {
+ 'method': "post"
+ "onSuccess" : -> window.location.reload()
+ 'onFailure': ->
+ root.notify.alert '{{_("Error occured.")}}', {
+ 'className': 'error'
+ }
+ }
+
+ form.send()
+ e.stop()
+
+ submitAccounts: (e) ->
+ form = $ "account_form"
+ form.set "send", {
+ 'method': "post",
+ "onSuccess" : -> window.location.reload()
+ 'onFailure': ->
+ root.notify.alert('{{ _("Error occured.") }}', {
+ 'className': 'error'
+ })
+ }
+
+ form.send()
+ e.stop()
diff --git a/module/web/media/js/settings.js b/module/webui/themes/Dark/js/render/settings.min.js
index 9191fac72..41d1cb25a 100644
--- a/module/web/media/js/settings.js
+++ b/module/webui/themes/Dark/js/render/settings.min.js
@@ -1,3 +1,3 @@
{% autoescape true %}
var SettingsUI,root;var __bind=function(a,b){return function(){return a.apply(b,arguments)}};root=this;window.addEvent("domready",function(){root.accountDialog=new MooDialog({destroyOnHide:false});root.accountDialog.setContent($("account_box"));new TinyTab($$("#toptabs li a"),$$("#tabs-body > span"));$$("ul.nav").each(function(a){return new MooDropMenu(a,{onOpen:function(b){return b.fade("in")},onClose:function(b){return b.fade("out")},onInitialize:function(b){return b.fade("hide").set("tween",{duration:500})}})});return new SettingsUI()});SettingsUI=(function(){function a(){var c,e,b,d;this.menu=$$("#general-menu li");this.menu.append($$("#plugin-menu li"));this.name=$("tabsback");this.general=$("general_form_content");this.plugin=$("plugin_form_content");d=this.menu;for(e=0,b=d.length;e<b;e++){c=d[e];c.addEvent("click",this.menuClick.bind(this))}$("general|submit").addEvent("click",this.configSubmit.bind(this));$("plugin|submit").addEvent("click",this.configSubmit.bind(this));$("account_add").addEvent("click",function(f){root.accountDialog.open();return f.stop()});$("account_reset").addEvent("click",function(f){return root.accountDialog.close()});$("account_add_button").addEvent("click",this.addAccount.bind(this));$("account_submit").addEvent("click",this.submitAccounts.bind(this))}a.prototype.menuClick=function(h){var c,b,g,f,d;d=h.target.get("id").split("|"),c=d[0],g=d[1];b=h.target.get("text");f=c==="general"?this.general:this.plugin;f.dissolve();return new Request({method:"get",url:"/json/load_config/"+c+"/"+g,onSuccess:__bind(function(e){f.set("html",e);f.reveal();return this.name.set("text",b)},this)}).send()};a.prototype.configSubmit=function(d){var c,b;c=d.target.get("id").split("|")[0];b=$(""+c+"_form");b.set("send",{method:"post",url:"/json/save_config/"+c,onSuccess:function(){return root.notify.alert('{{ _("Settings saved.")}}',{className:"success"})},onFailure:function(){return root.notify.alert('{{ _("Error occured.")}}',{className:"error"})}});b.send();return d.stop()};a.prototype.addAccount=function(c){var b;b=$("add_account_form");b.set("send",{method:"post",onSuccess:function(){return window.location.reload()},onFailure:function(){return root.notify.alert('{{_("Error occured.")}}',{className:"error"})}});b.send();return c.stop()};a.prototype.submitAccounts=function(c){var b;b=$("account_form");b.set("send",{method:"post",onSuccess:function(){return window.location.reload()},onFailure:function(){return root.notify.alert('{{ _("Error occured.") }}',{className:"error"})}});b.send();return c.stop()};return a})();
-{% endautoescape %} \ No newline at end of file
+{% endautoescape %}
diff --git a/module/webui/themes/Dark/js/static/MooDialog.js b/module/webui/themes/Dark/js/static/MooDialog.js
new file mode 100644
index 000000000..45a52496f
--- /dev/null
+++ b/module/webui/themes/Dark/js/static/MooDialog.js
@@ -0,0 +1,140 @@
+/*
+---
+name: MooDialog
+description: The base class of MooDialog
+authors: Arian Stolwijk
+license: MIT-style license
+requires: [Core/Class, Core/Element, Core/Element.Style, Core/Element.Event]
+provides: [MooDialog, Element.MooDialog]
+...
+*/
+
+
+var MooDialog = new Class({
+
+ Implements: [Options, Events],
+
+ options: {
+ 'class': 'MooDialog',
+ title: null,
+ scroll: true, // IE
+ forceScroll: false,
+ useEscKey: true,
+ destroyOnHide: true,
+ autoOpen: true,
+ closeButton: true,
+ onInitialize: function(){
+ this.wrapper.setStyle('display', 'none');
+ },
+ onBeforeOpen: function(){
+ this.wrapper.setStyle('display', 'block');
+ this.fireEvent('show');
+ },
+ onBeforeClose: function(){
+ this.wrapper.setStyle('display', 'none');
+ this.fireEvent('hide');
+ }/*,
+ onOpen: function(){},
+ onClose: function(){},
+ onShow: function(){},
+ onHide: function(){},
+ onInitialize: function(wrapper){},
+ onContentChange: function(content){}*/
+ },
+
+ initialize: function(options){
+ this.setOptions(options);
+ this.options.inject = this.options.inject || document.body;
+ options = this.options;
+
+ var wrapper = this.wrapper = new Element('div.' + options['class'].replace(' ', '.')).inject(options.inject);
+ this.content = new Element('div.content').inject(wrapper);
+
+ if (options.title){
+ this.title = new Element('div.title').set('text', options.title).inject(wrapper);
+ wrapper.addClass('MooDialogTitle');
+ }
+
+ if (options.closeButton){
+ this.closeButton = new Element('a.close', {
+ events: {click: this.close.bind(this)}
+ }).inject(wrapper);
+ }
+
+
+ /*<ie6>*/// IE 6 scroll
+ if ((options.scroll && Browser.ie6) || options.forceScroll){
+ wrapper.setStyle('position', 'absolute');
+ var position = wrapper.getPosition(options.inject);
+ window.addEvent('scroll', function(){
+ var scroll = document.getScroll();
+ wrapper.setPosition({
+ x: position.x + scroll.x,
+ y: position.y + scroll.y
+ });
+ });
+ }
+ /*</ie6>*/
+
+ if (options.useEscKey){
+ // Add event for the esc key
+ document.addEvent('keydown', function(e){
+ if (e.key == 'esc') this.close();
+ }.bind(this));
+ }
+
+ this.addEvent('hide', function(){
+ if (options.destroyOnHide) this.destroy();
+ }.bind(this));
+
+ this.fireEvent('initialize', wrapper);
+ },
+
+ setContent: function(){
+ var content = Array.from(arguments);
+ if (content.length == 1) content = content[0];
+
+ this.content.empty();
+
+ var type = typeOf(content);
+ if (['string', 'number'].contains(type)) this.content.set('text', content);
+ else this.content.adopt(content);
+
+ this.fireEvent('contentChange', this.content);
+
+ return this;
+ },
+
+ open: function(){
+ this.fireEvent('beforeOpen', this.wrapper).fireEvent('open');
+ this.opened = true;
+ return this;
+ },
+
+ close: function(){
+ this.fireEvent('beforeClose', this.wrapper).fireEvent('close');
+ this.opened = false;
+ return this;
+ },
+
+ destroy: function(){
+ this.wrapper.destroy();
+ },
+
+ toElement: function(){
+ return this.wrapper;
+ }
+
+});
+
+
+Element.implement({
+
+ MooDialog: function(options){
+ this.store('MooDialog',
+ new MooDialog(options).setContent(this).open()
+ );
+ return this;
+ }
+
+});
diff --git a/module/webui/themes/Dark/js/static/MooDialog.min.js b/module/webui/themes/Dark/js/static/MooDialog.min.js
new file mode 100644
index 000000000..90b3ae100
--- /dev/null
+++ b/module/webui/themes/Dark/js/static/MooDialog.min.js
@@ -0,0 +1 @@
+var MooDialog=new Class({Implements:[Options,Events],options:{"class":"MooDialog",title:null,scroll:!0,forceScroll:!1,useEscKey:!0,destroyOnHide:!0,autoOpen:!0,closeButton:!0,onInitialize:function(){this.wrapper.setStyle("display","none")},onBeforeOpen:function(){this.wrapper.setStyle("display","block"),this.fireEvent("show")},onBeforeClose:function(){this.wrapper.setStyle("display","none"),this.fireEvent("hide")}},initialize:function(t){this.setOptions(t),this.options.inject=this.options.inject||document.body,t=this.options;var e=this.wrapper=new Element("div."+t["class"].replace(" ",".")).inject(t.inject);if(this.content=new Element("div.content").inject(e),t.title&&(this.title=new Element("div.title").set("text",t.title).inject(e),e.addClass("MooDialogTitle")),t.closeButton&&(this.closeButton=new Element("a.close",{events:{click:this.close.bind(this)}}).inject(e)),t.scroll&&Browser.ie6||t.forceScroll){e.setStyle("position","absolute");var n=e.getPosition(t.inject);window.addEvent("scroll",function(){var t=document.getScroll();e.setPosition({x:n.x+t.x,y:n.y+t.y})})}t.useEscKey&&document.addEvent("keydown",function(t){"esc"==t.key&&this.close()}.bind(this)),this.addEvent("hide",function(){t.destroyOnHide&&this.destroy()}.bind(this)),this.fireEvent("initialize",e)},setContent:function(){var t=Array.from(arguments);1==t.length&&(t=t[0]),this.content.empty();var e=typeOf(t);return["string","number"].contains(e)?this.content.set("text",t):this.content.adopt(t),this.fireEvent("contentChange",this.content),this},open:function(){return this.fireEvent("beforeOpen",this.wrapper).fireEvent("open"),this.opened=!0,this},close:function(){return this.fireEvent("beforeClose",this.wrapper).fireEvent("close"),this.opened=!1,this},destroy:function(){this.wrapper.destroy()},toElement:function(){return this.wrapper}});Element.implement({MooDialog:function(t){return this.store("MooDialog",new MooDialog(t).setContent(this).open()),this}}); \ No newline at end of file
diff --git a/module/web/media/js/MooDropMenu_static.js b/module/webui/themes/Dark/js/static/MooDropMenu.js
index b9cd8cc10..ac0fa1874 100644
--- a/module/web/media/js/MooDropMenu_static.js
+++ b/module/webui/themes/Dark/js/static/MooDropMenu.js
@@ -34,7 +34,9 @@ var MooDropMenu = new Class({
mouseoutDelay: 200,
mouseoverDelay: 0,
listSelector: 'ul',
- itemSelector: 'li'
+ itemSelector: 'li',
+ openEvent: 'mouseenter',
+ closeEvent: 'mouseleave'
},
initialize: function(menu, options, level){
@@ -50,27 +52,22 @@ var MooDropMenu = new Class({
var parent = el.getParent(options.itemSelector),
timer;
- parent.addEvents({
+ parent.addEvent(options.openEvent, function(){
+ parent.store('DropDownOpen', true);
- 'mouseenter': function(){
- parent.store('DropDownOpen', true);
+ clearTimeout(timer);
+ if (options.mouseoverDelay) timer = this.fireEvent.delay(options.mouseoverDelay, this, ['open', el]);
+ else this.fireEvent('open', el);
- clearTimeout(timer);
- if (options.mouseoverDelay) timer = this.fireEvent.delay(options.mouseoverDelay, this, ['open', el]);
- else this.fireEvent('open', el);
+ }.bind(this)).addEvent(options.closeEvent, function(){
+ parent.store('DropDownOpen', false);
- }.bind(this),
+ clearTimeout(timer);
+ timer = (function(){
+ if (!parent.retrieve('DropDownOpen')) this.fireEvent('close', el);
+ }).delay(options.mouseoutDelay, this);
- 'mouseleave': function(){
- parent.store('DropDownOpen', false);
-
- clearTimeout(timer);
- timer = (function(){
- if (!parent.retrieve('DropDownOpen')) this.fireEvent('close', el);
- }).delay(options.mouseoutDelay, this);
-
- }.bind(this)
- });
+ }.bind(this));
}, this);
},
diff --git a/module/webui/themes/Dark/js/static/MooDropMenu.min.js b/module/webui/themes/Dark/js/static/MooDropMenu.min.js
new file mode 100644
index 000000000..552ae247a
--- /dev/null
+++ b/module/webui/themes/Dark/js/static/MooDropMenu.min.js
@@ -0,0 +1 @@
+var MooDropMenu=new Class({Implements:[Options,Events],options:{onOpen:function(e){e.removeClass("close").addClass("open")},onClose:function(e){e.removeClass("open").addClass("close")},onInitialize:function(e){e.removeClass("open").addClass("close")},mouseoutDelay:200,mouseoverDelay:0,listSelector:"ul",itemSelector:"li",openEvent:"mouseenter",closeEvent:"mouseleave"},initialize:function(e,o){this.setOptions(o),o=this.options;var e=this.menu=document.id(e);e.getElements(o.itemSelector+" > "+o.listSelector).each(function(e){this.fireEvent("initialize",e);var n,t=e.getParent(o.itemSelector);t.addEvent(o.openEvent,function(){t.store("DropDownOpen",!0),clearTimeout(n),o.mouseoverDelay?n=this.fireEvent.delay(o.mouseoverDelay,this,["open",e]):this.fireEvent("open",e)}.bind(this)).addEvent(o.closeEvent,function(){t.store("DropDownOpen",!1),clearTimeout(n),n=function(){t.retrieve("DropDownOpen")||this.fireEvent("close",e)}.delay(o.mouseoutDelay,this)}.bind(this))},this)},toElement:function(){return this.menu}});Element.implement({MooDropMenu:function(e){return this.store("MooDropMenu",new MooDropMenu(this,e))}}); \ No newline at end of file
diff --git a/module/webui/themes/Dark/js/static/mootools-core.js b/module/webui/themes/Dark/js/static/mootools-core.js
new file mode 100644
index 000000000..db83850fd
--- /dev/null
+++ b/module/webui/themes/Dark/js/static/mootools-core.js
@@ -0,0 +1,5977 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/core/8423c12ffd6a6bfcde9ea22554aec795
+
+packager build:
+ - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady
+
+...
+*/
+
+/*
+---
+
+name: Core
+
+description: The heart of MooTools.
+
+license: MIT-style license.
+
+copyright: Copyright (c) 2006-2014 [Valerio Proietti](http://mad4milk.net/).
+
+authors: The MooTools production team (http://mootools.net/developers/)
+
+inspiration:
+ - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
+ - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
+
+provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
+
+...
+*/
+
+(function(){
+
+this.MooTools = {
+ version: '1.5.0',
+ build: '0f7b690afee9349b15909f33016a25d2e4d9f4e3'
+};
+
+// typeOf, instanceOf
+
+var typeOf = this.typeOf = function(item){
+ if (item == null) return 'null';
+ if (item.$family != null) return item.$family();
+
+ if (item.nodeName){
+ if (item.nodeType == 1) return 'element';
+ if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
+ } else if (typeof item.length == 'number'){
+ if ('callee' in item) return 'arguments';
+ if ('item' in item) return 'collection';
+ }
+
+ return typeof item;
+};
+
+var instanceOf = this.instanceOf = function(item, object){
+ if (item == null) return false;
+ var constructor = item.$constructor || item.constructor;
+ while (constructor){
+ if (constructor === object) return true;
+ constructor = constructor.parent;
+ }
+ /*<ltIE8>*/
+ if (!item.hasOwnProperty) return false;
+ /*</ltIE8>*/
+ return item instanceof object;
+};
+
+// Function overloading
+
+var Function = this.Function;
+
+var enumerables = true;
+for (var i in {toString: 1}) enumerables = null;
+if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
+
+Function.prototype.overloadSetter = function(usePlural){
+ var self = this;
+ return function(a, b){
+ if (a == null) return this;
+ if (usePlural || typeof a != 'string'){
+ for (var k in a) self.call(this, k, a[k]);
+ if (enumerables) for (var i = enumerables.length; i--;){
+ k = enumerables[i];
+ if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
+ }
+ } else {
+ self.call(this, a, b);
+ }
+ return this;
+ };
+};
+
+Function.prototype.overloadGetter = function(usePlural){
+ var self = this;
+ return function(a){
+ var args, result;
+ if (typeof a != 'string') args = a;
+ else if (arguments.length > 1) args = arguments;
+ else if (usePlural) args = [a];
+ if (args){
+ result = {};
+ for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
+ } else {
+ result = self.call(this, a);
+ }
+ return result;
+ };
+};
+
+Function.prototype.extend = function(key, value){
+ this[key] = value;
+}.overloadSetter();
+
+Function.prototype.implement = function(key, value){
+ this.prototype[key] = value;
+}.overloadSetter();
+
+// From
+
+var slice = Array.prototype.slice;
+
+Function.from = function(item){
+ return (typeOf(item) == 'function') ? item : function(){
+ return item;
+ };
+};
+
+Array.from = function(item){
+ if (item == null) return [];
+ return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
+};
+
+Number.from = function(item){
+ var number = parseFloat(item);
+ return isFinite(number) ? number : null;
+};
+
+String.from = function(item){
+ return item + '';
+};
+
+// hide, protect
+
+Function.implement({
+
+ hide: function(){
+ this.$hidden = true;
+ return this;
+ },
+
+ protect: function(){
+ this.$protected = true;
+ return this;
+ }
+
+});
+
+// Type
+
+var Type = this.Type = function(name, object){
+ if (name){
+ var lower = name.toLowerCase();
+ var typeCheck = function(item){
+ return (typeOf(item) == lower);
+ };
+
+ Type['is' + name] = typeCheck;
+ if (object != null){
+ object.prototype.$family = (function(){
+ return lower;
+ }).hide();
+
+ }
+ }
+
+ if (object == null) return null;
+
+ object.extend(this);
+ object.$constructor = Type;
+ object.prototype.$constructor = object;
+
+ return object;
+};
+
+var toString = Object.prototype.toString;
+
+Type.isEnumerable = function(item){
+ return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
+};
+
+var hooks = {};
+
+var hooksOf = function(object){
+ var type = typeOf(object.prototype);
+ return hooks[type] || (hooks[type] = []);
+};
+
+var implement = function(name, method){
+ if (method && method.$hidden) return;
+
+ var hooks = hooksOf(this);
+
+ for (var i = 0; i < hooks.length; i++){
+ var hook = hooks[i];
+ if (typeOf(hook) == 'type') implement.call(hook, name, method);
+ else hook.call(this, name, method);
+ }
+
+ var previous = this.prototype[name];
+ if (previous == null || !previous.$protected) this.prototype[name] = method;
+
+ if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
+ return method.apply(item, slice.call(arguments, 1));
+ });
+};
+
+var extend = function(name, method){
+ if (method && method.$hidden) return;
+ var previous = this[name];
+ if (previous == null || !previous.$protected) this[name] = method;
+};
+
+Type.implement({
+
+ implement: implement.overloadSetter(),
+
+ extend: extend.overloadSetter(),
+
+ alias: function(name, existing){
+ implement.call(this, name, this.prototype[existing]);
+ }.overloadSetter(),
+
+ mirror: function(hook){
+ hooksOf(this).push(hook);
+ return this;
+ }
+
+});
+
+new Type('Type', Type);
+
+// Default Types
+
+var force = function(name, object, methods){
+ var isType = (object != Object),
+ prototype = object.prototype;
+
+ if (isType) object = new Type(name, object);
+
+ for (var i = 0, l = methods.length; i < l; i++){
+ var key = methods[i],
+ generic = object[key],
+ proto = prototype[key];
+
+ if (generic) generic.protect();
+ if (isType && proto) object.implement(key, proto.protect());
+ }
+
+ if (isType){
+ var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
+ object.forEachMethod = function(fn){
+ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
+ fn.call(prototype, prototype[methods[i]], methods[i]);
+ }
+ for (var key in prototype) fn.call(prototype, prototype[key], key);
+ };
+ }
+
+ return force;
+};
+
+force('String', String, [
+ 'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
+ 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
+])('Array', Array, [
+ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
+ 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
+])('Number', Number, [
+ 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
+])('Function', Function, [
+ 'apply', 'call', 'bind'
+])('RegExp', RegExp, [
+ 'exec', 'test'
+])('Object', Object, [
+ 'create', 'defineProperty', 'defineProperties', 'keys',
+ 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
+ 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
+])('Date', Date, ['now']);
+
+Object.extend = extend.overloadSetter();
+
+Date.extend('now', function(){
+ return +(new Date);
+});
+
+new Type('Boolean', Boolean);
+
+// fixes NaN returning as Number
+
+Number.prototype.$family = function(){
+ return isFinite(this) ? 'number' : 'null';
+}.hide();
+
+// Number.random
+
+Number.extend('random', function(min, max){
+ return Math.floor(Math.random() * (max - min + 1) + min);
+});
+
+// forEach, each
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+Object.extend('forEach', function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
+ }
+});
+
+Object.each = Object.forEach;
+
+Array.implement({
+
+ /*<!ES5>*/
+ forEach: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (i in this) fn.call(bind, this[i], i, this);
+ }
+ },
+ /*</!ES5>*/
+
+ each: function(fn, bind){
+ Array.forEach(this, fn, bind);
+ return this;
+ }
+
+});
+
+// Array & Object cloning, Object merging and appending
+
+var cloneOf = function(item){
+ switch (typeOf(item)){
+ case 'array': return item.clone();
+ case 'object': return Object.clone(item);
+ default: return item;
+ }
+};
+
+Array.implement('clone', function(){
+ var i = this.length, clone = new Array(i);
+ while (i--) clone[i] = cloneOf(this[i]);
+ return clone;
+});
+
+var mergeOne = function(source, key, current){
+ switch (typeOf(current)){
+ case 'object':
+ if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
+ else source[key] = Object.clone(current);
+ break;
+ case 'array': source[key] = current.clone(); break;
+ default: source[key] = current;
+ }
+ return source;
+};
+
+Object.extend({
+
+ merge: function(source, k, v){
+ if (typeOf(k) == 'string') return mergeOne(source, k, v);
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var object = arguments[i];
+ for (var key in object) mergeOne(source, key, object[key]);
+ }
+ return source;
+ },
+
+ clone: function(object){
+ var clone = {};
+ for (var key in object) clone[key] = cloneOf(object[key]);
+ return clone;
+ },
+
+ append: function(original){
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var extended = arguments[i] || {};
+ for (var key in extended) original[key] = extended[key];
+ }
+ return original;
+ }
+
+});
+
+// Object-less types
+
+['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
+ new Type(name);
+});
+
+// Unique ID
+
+var UID = Date.now();
+
+String.extend('uniqueID', function(){
+ return (UID++).toString(36);
+});
+
+
+
+})();
+
+
+/*
+---
+
+name: Array
+
+description: Contains Array Prototypes like each, contains, and erase.
+
+license: MIT-style license.
+
+requires: [Type]
+
+provides: Array
+
+...
+*/
+
+Array.implement({
+
+ /*<!ES5>*/
+ every: function(fn, bind){
+ for (var i = 0, l = this.length >>> 0; i < l; i++){
+ if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
+ }
+ return true;
+ },
+
+ filter: function(fn, bind){
+ var results = [];
+ for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this){
+ value = this[i];
+ if (fn.call(bind, value, i, this)) results.push(value);
+ }
+ return results;
+ },
+
+ indexOf: function(item, from){
+ var length = this.length >>> 0;
+ for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){
+ if (this[i] === item) return i;
+ }
+ return -1;
+ },
+
+ map: function(fn, bind){
+ var length = this.length >>> 0, results = Array(length);
+ for (var i = 0; i < length; i++){
+ if (i in this) results[i] = fn.call(bind, this[i], i, this);
+ }
+ return results;
+ },
+
+ some: function(fn, bind){
+ for (var i = 0, l = this.length >>> 0; i < l; i++){
+ if ((i in this) && fn.call(bind, this[i], i, this)) return true;
+ }
+ return false;
+ },
+ /*</!ES5>*/
+
+ clean: function(){
+ return this.filter(function(item){
+ return item != null;
+ });
+ },
+
+ invoke: function(methodName){
+ var args = Array.slice(arguments, 1);
+ return this.map(function(item){
+ return item[methodName].apply(item, args);
+ });
+ },
+
+ associate: function(keys){
+ var obj = {}, length = Math.min(this.length, keys.length);
+ for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
+ return obj;
+ },
+
+ link: function(object){
+ var result = {};
+ for (var i = 0, l = this.length; i < l; i++){
+ for (var key in object){
+ if (object[key](this[i])){
+ result[key] = this[i];
+ delete object[key];
+ break;
+ }
+ }
+ }
+ return result;
+ },
+
+ contains: function(item, from){
+ return this.indexOf(item, from) != -1;
+ },
+
+ append: function(array){
+ this.push.apply(this, array);
+ return this;
+ },
+
+ getLast: function(){
+ return (this.length) ? this[this.length - 1] : null;
+ },
+
+ getRandom: function(){
+ return (this.length) ? this[Number.random(0, this.length - 1)] : null;
+ },
+
+ include: function(item){
+ if (!this.contains(item)) this.push(item);
+ return this;
+ },
+
+ combine: function(array){
+ for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
+ return this;
+ },
+
+ erase: function(item){
+ for (var i = this.length; i--;){
+ if (this[i] === item) this.splice(i, 1);
+ }
+ return this;
+ },
+
+ empty: function(){
+ this.length = 0;
+ return this;
+ },
+
+ flatten: function(){
+ var array = [];
+ for (var i = 0, l = this.length; i < l; i++){
+ var type = typeOf(this[i]);
+ if (type == 'null') continue;
+ array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
+ }
+ return array;
+ },
+
+ pick: function(){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (this[i] != null) return this[i];
+ }
+ return null;
+ },
+
+ hexToRgb: function(array){
+ if (this.length != 3) return null;
+ var rgb = this.map(function(value){
+ if (value.length == 1) value += value;
+ return parseInt(value, 16);
+ });
+ return (array) ? rgb : 'rgb(' + rgb + ')';
+ },
+
+ rgbToHex: function(array){
+ if (this.length < 3) return null;
+ if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
+ var hex = [];
+ for (var i = 0; i < 3; i++){
+ var bit = (this[i] - 0).toString(16);
+ hex.push((bit.length == 1) ? '0' + bit : bit);
+ }
+ return (array) ? hex : '#' + hex.join('');
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: String
+
+description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
+
+license: MIT-style license.
+
+requires: [Type, Array]
+
+provides: String
+
+...
+*/
+
+String.implement({
+
+ //<!ES6>
+ contains: function(string, index){
+ return (index ? String(this).slice(index) : String(this)).indexOf(string) > -1;
+ },
+ //</!ES6>
+
+ test: function(regex, params){
+ return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
+ },
+
+ trim: function(){
+ return String(this).replace(/^\s+|\s+$/g, '');
+ },
+
+ clean: function(){
+ return String(this).replace(/\s+/g, ' ').trim();
+ },
+
+ camelCase: function(){
+ return String(this).replace(/-\D/g, function(match){
+ return match.charAt(1).toUpperCase();
+ });
+ },
+
+ hyphenate: function(){
+ return String(this).replace(/[A-Z]/g, function(match){
+ return ('-' + match.charAt(0).toLowerCase());
+ });
+ },
+
+ capitalize: function(){
+ return String(this).replace(/\b[a-z]/g, function(match){
+ return match.toUpperCase();
+ });
+ },
+
+ escapeRegExp: function(){
+ return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ hexToRgb: function(array){
+ var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
+ return (hex) ? hex.slice(1).hexToRgb(array) : null;
+ },
+
+ rgbToHex: function(array){
+ var rgb = String(this).match(/\d{1,3}/g);
+ return (rgb) ? rgb.rgbToHex(array) : null;
+ },
+
+ substitute: function(object, regexp){
+ return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
+ if (match.charAt(0) == '\\') return match.slice(1);
+ return (object[name] != null) ? object[name] : '';
+ });
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: Number
+
+description: Contains Number Prototypes like limit, round, times, and ceil.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Number
+
+...
+*/
+
+Number.implement({
+
+ limit: function(min, max){
+ return Math.min(max, Math.max(min, this));
+ },
+
+ round: function(precision){
+ precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
+ return Math.round(this * precision) / precision;
+ },
+
+ times: function(fn, bind){
+ for (var i = 0; i < this; i++) fn.call(bind, i, this);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ }
+
+});
+
+Number.alias('each', 'times');
+
+(function(math){
+ var methods = {};
+ math.each(function(name){
+ if (!Number[name]) methods[name] = function(){
+ return Math[name].apply(null, [this].concat(Array.from(arguments)));
+ };
+ });
+ Number.implement(methods);
+})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
+
+
+/*
+---
+
+name: Function
+
+description: Contains Function Prototypes like create, bind, pass, and delay.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Function
+
+...
+*/
+
+Function.extend({
+
+ attempt: function(){
+ for (var i = 0, l = arguments.length; i < l; i++){
+ try {
+ return arguments[i]();
+ } catch (e){}
+ }
+ return null;
+ }
+
+});
+
+Function.implement({
+
+ attempt: function(args, bind){
+ try {
+ return this.apply(bind, Array.from(args));
+ } catch (e){}
+
+ return null;
+ },
+
+ /*<!ES5-bind>*/
+ bind: function(that){
+ var self = this,
+ args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
+ F = function(){};
+
+ var bound = function(){
+ var context = that, length = arguments.length;
+ if (this instanceof bound){
+ F.prototype = self.prototype;
+ context = new F;
+ }
+ var result = (!args && !length)
+ ? self.call(context)
+ : self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
+ return context == that ? result : context;
+ };
+ return bound;
+ },
+ /*</!ES5-bind>*/
+
+ pass: function(args, bind){
+ var self = this;
+ if (args != null) args = Array.from(args);
+ return function(){
+ return self.apply(bind, args || arguments);
+ };
+ },
+
+ delay: function(delay, bind, args){
+ return setTimeout(this.pass((args == null ? [] : args), bind), delay);
+ },
+
+ periodical: function(periodical, bind, args){
+ return setInterval(this.pass((args == null ? [] : args), bind), periodical);
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: Object
+
+description: Object generic methods
+
+license: MIT-style license.
+
+requires: Type
+
+provides: [Object, Hash]
+
+...
+*/
+
+(function(){
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+Object.extend({
+
+ subset: function(object, keys){
+ var results = {};
+ for (var i = 0, l = keys.length; i < l; i++){
+ var k = keys[i];
+ if (k in object) results[k] = object[k];
+ }
+ return results;
+ },
+
+ map: function(object, fn, bind){
+ var results = {};
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
+ }
+ return results;
+ },
+
+ filter: function(object, fn, bind){
+ var results = {};
+ for (var key in object){
+ var value = object[key];
+ if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
+ }
+ return results;
+ },
+
+ every: function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
+ }
+ return true;
+ },
+
+ some: function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
+ }
+ return false;
+ },
+
+ keys: function(object){
+ var keys = [];
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) keys.push(key);
+ }
+ return keys;
+ },
+
+ values: function(object){
+ var values = [];
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) values.push(object[key]);
+ }
+ return values;
+ },
+
+ getLength: function(object){
+ return Object.keys(object).length;
+ },
+
+ keyOf: function(object, value){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && object[key] === value) return key;
+ }
+ return null;
+ },
+
+ contains: function(object, value){
+ return Object.keyOf(object, value) != null;
+ },
+
+ toQueryString: function(object, base){
+ var queryString = [];
+
+ Object.each(object, function(value, key){
+ if (base) key = base + '[' + key + ']';
+ var result;
+ switch (typeOf(value)){
+ case 'object': result = Object.toQueryString(value, key); break;
+ case 'array':
+ var qs = {};
+ value.each(function(val, i){
+ qs[i] = val;
+ });
+ result = Object.toQueryString(qs, key);
+ break;
+ default: result = key + '=' + encodeURIComponent(value);
+ }
+ if (value != null) queryString.push(result);
+ });
+
+ return queryString.join('&');
+ }
+
+});
+
+})();
+
+
+
+
+/*
+---
+
+name: Browser
+
+description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
+
+license: MIT-style license.
+
+requires: [Array, Function, Number, String]
+
+provides: [Browser, Window, Document]
+
+...
+*/
+
+(function(){
+
+var document = this.document;
+var window = document.window = this;
+
+var parse = function(ua, platform){
+ ua = ua.toLowerCase();
+ platform = (platform ? platform.toLowerCase() : '');
+
+ var UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/) || [null, 'unknown', 0];
+
+ if (UA[1] == 'trident'){
+ UA[1] = 'ie';
+ if (UA[4]) UA[2] = UA[4];
+ } else if (UA[1] == 'crios') {
+ UA[1] = 'chrome';
+ }
+
+ var platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0];
+ if (platform == 'win') platform = 'windows';
+
+ return {
+ extend: Function.prototype.extend,
+ name: (UA[1] == 'version') ? UA[3] : UA[1],
+ version: parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
+ platform: platform
+ };
+};
+
+var Browser = this.Browser = parse(navigator.userAgent, navigator.platform);
+
+if (Browser.ie){
+ Browser.version = document.documentMode;
+}
+
+Browser.extend({
+ Features: {
+ xpath: !!(document.evaluate),
+ air: !!(window.runtime),
+ query: !!(document.querySelector),
+ json: !!(window.JSON)
+ },
+ parseUA: parse
+});
+
+
+
+// Request
+
+Browser.Request = (function(){
+
+ var XMLHTTP = function(){
+ return new XMLHttpRequest();
+ };
+
+ var MSXML2 = function(){
+ return new ActiveXObject('MSXML2.XMLHTTP');
+ };
+
+ var MSXML = function(){
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ };
+
+ return Function.attempt(function(){
+ XMLHTTP();
+ return XMLHTTP;
+ }, function(){
+ MSXML2();
+ return MSXML2;
+ }, function(){
+ MSXML();
+ return MSXML;
+ });
+
+})();
+
+Browser.Features.xhr = !!(Browser.Request);
+
+
+
+// String scripts
+
+Browser.exec = function(text){
+ if (!text) return text;
+ if (window.execScript){
+ window.execScript(text);
+ } else {
+ var script = document.createElement('script');
+ script.setAttribute('type', 'text/javascript');
+ script.text = text;
+ document.head.appendChild(script);
+ document.head.removeChild(script);
+ }
+ return text;
+};
+
+String.implement('stripScripts', function(exec){
+ var scripts = '';
+ var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
+ scripts += code + '\n';
+ return '';
+ });
+ if (exec === true) Browser.exec(scripts);
+ else if (typeOf(exec) == 'function') exec(scripts, text);
+ return text;
+});
+
+// Window, Document
+
+Browser.extend({
+ Document: this.Document,
+ Window: this.Window,
+ Element: this.Element,
+ Event: this.Event
+});
+
+this.Window = this.$constructor = new Type('Window', function(){});
+
+this.$family = Function.from('window').hide();
+
+Window.mirror(function(name, method){
+ window[name] = method;
+});
+
+this.Document = document.$constructor = new Type('Document', function(){});
+
+document.$family = Function.from('document').hide();
+
+Document.mirror(function(name, method){
+ document[name] = method;
+});
+
+document.html = document.documentElement;
+if (!document.head) document.head = document.getElementsByTagName('head')[0];
+
+if (document.execCommand) try {
+ document.execCommand("BackgroundImageCache", false, true);
+} catch (e){}
+
+/*<ltIE9>*/
+if (this.attachEvent && !this.addEventListener){
+ var unloadEvent = function(){
+ this.detachEvent('onunload', unloadEvent);
+ document.head = document.html = document.window = null;
+ };
+ this.attachEvent('onunload', unloadEvent);
+}
+
+// IE fails on collections and <select>.options (refers to <select>)
+var arrayFrom = Array.from;
+try {
+ arrayFrom(document.html.childNodes);
+} catch(e){
+ Array.from = function(item){
+ if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
+ var i = item.length, array = new Array(i);
+ while (i--) array[i] = item[i];
+ return array;
+ }
+ return arrayFrom(item);
+ };
+
+ var prototype = Array.prototype,
+ slice = prototype.slice;
+ ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
+ var method = prototype[name];
+ Array[name] = function(item){
+ return method.apply(Array.from(item), slice.call(arguments, 1));
+ };
+ });
+}
+/*</ltIE9>*/
+
+
+
+})();
+
+
+/*
+---
+
+name: Event
+
+description: Contains the Event Type, to make the event object cross-browser.
+
+license: MIT-style license.
+
+requires: [Window, Document, Array, Function, String, Object]
+
+provides: Event
+
+...
+*/
+
+(function() {
+
+var _keys = {};
+
+var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){
+ if (!win) win = window;
+ event = event || win.event;
+ if (event.$extended) return event;
+ this.event = event;
+ this.$extended = true;
+ this.shift = event.shiftKey;
+ this.control = event.ctrlKey;
+ this.alt = event.altKey;
+ this.meta = event.metaKey;
+ var type = this.type = event.type;
+ var target = event.target || event.srcElement;
+ while (target && target.nodeType == 3) target = target.parentNode;
+ this.target = document.id(target);
+
+ if (type.indexOf('key') == 0){
+ var code = this.code = (event.which || event.keyCode);
+ this.key = _keys[code];
+ if (type == 'keydown' || type == 'keyup'){
+ if (code > 111 && code < 124) this.key = 'f' + (code - 111);
+ else if (code > 95 && code < 106) this.key = code - 96;
+ }
+ if (this.key == null) this.key = String.fromCharCode(code).toLowerCase();
+ } else if (type == 'click' || type == 'dblclick' || type == 'contextmenu' || type == 'DOMMouseScroll' || type.indexOf('mouse') == 0){
+ var doc = win.document;
+ doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
+ this.page = {
+ x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
+ y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
+ };
+ this.client = {
+ x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
+ y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
+ };
+ if (type == 'DOMMouseScroll' || type == 'mousewheel')
+ this.wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
+
+ this.rightClick = (event.which == 3 || event.button == 2);
+ if (type == 'mouseover' || type == 'mouseout'){
+ var related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
+ while (related && related.nodeType == 3) related = related.parentNode;
+ this.relatedTarget = document.id(related);
+ }
+ } else if (type.indexOf('touch') == 0 || type.indexOf('gesture') == 0){
+ this.rotation = event.rotation;
+ this.scale = event.scale;
+ this.targetTouches = event.targetTouches;
+ this.changedTouches = event.changedTouches;
+ var touches = this.touches = event.touches;
+ if (touches && touches[0]){
+ var touch = touches[0];
+ this.page = {x: touch.pageX, y: touch.pageY};
+ this.client = {x: touch.clientX, y: touch.clientY};
+ }
+ }
+
+ if (!this.client) this.client = {};
+ if (!this.page) this.page = {};
+});
+
+DOMEvent.implement({
+
+ stop: function(){
+ return this.preventDefault().stopPropagation();
+ },
+
+ stopPropagation: function(){
+ if (this.event.stopPropagation) this.event.stopPropagation();
+ else this.event.cancelBubble = true;
+ return this;
+ },
+
+ preventDefault: function(){
+ if (this.event.preventDefault) this.event.preventDefault();
+ else this.event.returnValue = false;
+ return this;
+ }
+
+});
+
+DOMEvent.defineKey = function(code, key){
+ _keys[code] = key;
+ return this;
+};
+
+DOMEvent.defineKeys = DOMEvent.defineKey.overloadSetter(true);
+
+DOMEvent.defineKeys({
+ '38': 'up', '40': 'down', '37': 'left', '39': 'right',
+ '27': 'esc', '32': 'space', '8': 'backspace', '9': 'tab',
+ '46': 'delete', '13': 'enter'
+});
+
+})();
+
+
+
+
+
+
+/*
+---
+
+name: Class
+
+description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
+
+license: MIT-style license.
+
+requires: [Array, String, Function, Number]
+
+provides: Class
+
+...
+*/
+
+(function(){
+
+var Class = this.Class = new Type('Class', function(params){
+ if (instanceOf(params, Function)) params = {initialize: params};
+
+ var newClass = function(){
+ reset(this);
+ if (newClass.$prototyping) return this;
+ this.$caller = null;
+ var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
+ this.$caller = this.caller = null;
+ return value;
+ }.extend(this).implement(params);
+
+ newClass.$constructor = Class;
+ newClass.prototype.$constructor = newClass;
+ newClass.prototype.parent = parent;
+
+ return newClass;
+});
+
+var parent = function(){
+ if (!this.$caller) throw new Error('The method "parent" cannot be called.');
+ var name = this.$caller.$name,
+ parent = this.$caller.$owner.parent,
+ previous = (parent) ? parent.prototype[name] : null;
+ if (!previous) throw new Error('The method "' + name + '" has no parent.');
+ return previous.apply(this, arguments);
+};
+
+var reset = function(object){
+ for (var key in object){
+ var value = object[key];
+ switch (typeOf(value)){
+ case 'object':
+ var F = function(){};
+ F.prototype = value;
+ object[key] = reset(new F);
+ break;
+ case 'array': object[key] = value.clone(); break;
+ }
+ }
+ return object;
+};
+
+var wrap = function(self, key, method){
+ if (method.$origin) method = method.$origin;
+ var wrapper = function(){
+ if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
+ var caller = this.caller, current = this.$caller;
+ this.caller = current; this.$caller = wrapper;
+ var result = method.apply(this, arguments);
+ this.$caller = current; this.caller = caller;
+ return result;
+ }.extend({$owner: self, $origin: method, $name: key});
+ return wrapper;
+};
+
+var implement = function(key, value, retain){
+ if (Class.Mutators.hasOwnProperty(key)){
+ value = Class.Mutators[key].call(this, value);
+ if (value == null) return this;
+ }
+
+ if (typeOf(value) == 'function'){
+ if (value.$hidden) return this;
+ this.prototype[key] = (retain) ? value : wrap(this, key, value);
+ } else {
+ Object.merge(this.prototype, key, value);
+ }
+
+ return this;
+};
+
+var getInstance = function(klass){
+ klass.$prototyping = true;
+ var proto = new klass;
+ delete klass.$prototyping;
+ return proto;
+};
+
+Class.implement('implement', implement.overloadSetter());
+
+Class.Mutators = {
+
+ Extends: function(parent){
+ this.parent = parent;
+ this.prototype = getInstance(parent);
+ },
+
+ Implements: function(items){
+ Array.from(items).each(function(item){
+ var instance = new item;
+ for (var key in instance) implement.call(this, key, instance[key], true);
+ }, this);
+ }
+};
+
+})();
+
+
+/*
+---
+
+name: Class.Extras
+
+description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
+
+license: MIT-style license.
+
+requires: Class
+
+provides: [Class.Extras, Chain, Events, Options]
+
+...
+*/
+
+(function(){
+
+this.Chain = new Class({
+
+ $chain: [],
+
+ chain: function(){
+ this.$chain.append(Array.flatten(arguments));
+ return this;
+ },
+
+ callChain: function(){
+ return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
+ },
+
+ clearChain: function(){
+ this.$chain.empty();
+ return this;
+ }
+
+});
+
+var removeOn = function(string){
+ return string.replace(/^on([A-Z])/, function(full, first){
+ return first.toLowerCase();
+ });
+};
+
+this.Events = new Class({
+
+ $events: {},
+
+ addEvent: function(type, fn, internal){
+ type = removeOn(type);
+
+
+
+ this.$events[type] = (this.$events[type] || []).include(fn);
+ if (internal) fn.internal = true;
+ return this;
+ },
+
+ addEvents: function(events){
+ for (var type in events) this.addEvent(type, events[type]);
+ return this;
+ },
+
+ fireEvent: function(type, args, delay){
+ type = removeOn(type);
+ var events = this.$events[type];
+ if (!events) return this;
+ args = Array.from(args);
+ events.each(function(fn){
+ if (delay) fn.delay(delay, this, args);
+ else fn.apply(this, args);
+ }, this);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ type = removeOn(type);
+ var events = this.$events[type];
+ if (events && !fn.internal){
+ var index = events.indexOf(fn);
+ if (index != -1) delete events[index];
+ }
+ return this;
+ },
+
+ removeEvents: function(events){
+ var type;
+ if (typeOf(events) == 'object'){
+ for (type in events) this.removeEvent(type, events[type]);
+ return this;
+ }
+ if (events) events = removeOn(events);
+ for (type in this.$events){
+ if (events && events != type) continue;
+ var fns = this.$events[type];
+ for (var i = fns.length; i--;) if (i in fns){
+ this.removeEvent(type, fns[i]);
+ }
+ }
+ return this;
+ }
+
+});
+
+this.Options = new Class({
+
+ setOptions: function(){
+ var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
+ if (this.addEvent) for (var option in options){
+ if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
+ this.addEvent(option, options[option]);
+ delete options[option];
+ }
+ return this;
+ }
+
+});
+
+})();
+
+
+/*
+---
+name: Slick.Parser
+description: Standalone CSS3 Selector parser
+provides: Slick.Parser
+...
+*/
+
+;(function(){
+
+var parsed,
+ separatorIndex,
+ combinatorIndex,
+ reversed,
+ cache = {},
+ reverseCache = {},
+ reUnescape = /\\/g;
+
+var parse = function(expression, isReversed){
+ if (expression == null) return null;
+ if (expression.Slick === true) return expression;
+ expression = ('' + expression).replace(/^\s+|\s+$/g, '');
+ reversed = !!isReversed;
+ var currentCache = (reversed) ? reverseCache : cache;
+ if (currentCache[expression]) return currentCache[expression];
+ parsed = {
+ Slick: true,
+ expressions: [],
+ raw: expression,
+ reverse: function(){
+ return parse(this.raw, true);
+ }
+ };
+ separatorIndex = -1;
+ while (expression != (expression = expression.replace(regexp, parser)));
+ parsed.length = parsed.expressions.length;
+ return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
+};
+
+var reverseCombinator = function(combinator){
+ if (combinator === '!') return ' ';
+ else if (combinator === ' ') return '!';
+ else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
+ else return '!' + combinator;
+};
+
+var reverse = function(expression){
+ var expressions = expression.expressions;
+ for (var i = 0; i < expressions.length; i++){
+ var exp = expressions[i];
+ var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
+
+ for (var j = 0; j < exp.length; j++){
+ var cexp = exp[j];
+ if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
+ cexp.combinator = cexp.reverseCombinator;
+ delete cexp.reverseCombinator;
+ }
+
+ exp.reverse().push(last);
+ }
+ return expression;
+};
+
+var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
+ return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
+ return '\\' + match;
+ });
+};
+
+var regexp = new RegExp(
+/*
+#!/usr/bin/env ruby
+puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
+__END__
+ "(?x)^(?:\
+ \\s* ( , ) \\s* # Separator \n\
+ | \\s* ( <combinator>+ ) \\s* # Combinator \n\
+ | ( \\s+ ) # CombinatorChildren \n\
+ | ( <unicode>+ | \\* ) # Tag \n\
+ | \\# ( <unicode>+ ) # ID \n\
+ | \\. ( <unicode>+ ) # ClassName \n\
+ | # Attribute \n\
+ \\[ \
+ \\s* (<unicode1>+) (?: \
+ \\s* ([*^$!~|]?=) (?: \
+ \\s* (?:\
+ ([\"']?)(.*?)\\9 \
+ )\
+ ) \
+ )? \\s* \
+ \\](?!\\]) \n\
+ | :+ ( <unicode>+ )(?:\
+ \\( (?:\
+ (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
+ ) \\)\
+ )?\
+ )"
+*/
+ "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
+ .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
+ .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+ .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+);
+
+function parser(
+ rawMatch,
+
+ separator,
+ combinator,
+ combinatorChildren,
+
+ tagName,
+ id,
+ className,
+
+ attributeKey,
+ attributeOperator,
+ attributeQuote,
+ attributeValue,
+
+ pseudoMarker,
+ pseudoClass,
+ pseudoQuote,
+ pseudoClassQuotedValue,
+ pseudoClassValue
+){
+ if (separator || separatorIndex === -1){
+ parsed.expressions[++separatorIndex] = [];
+ combinatorIndex = -1;
+ if (separator) return '';
+ }
+
+ if (combinator || combinatorChildren || combinatorIndex === -1){
+ combinator = combinator || ' ';
+ var currentSeparator = parsed.expressions[separatorIndex];
+ if (reversed && currentSeparator[combinatorIndex])
+ currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
+ currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
+ }
+
+ var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
+
+ if (tagName){
+ currentParsed.tag = tagName.replace(reUnescape, '');
+
+ } else if (id){
+ currentParsed.id = id.replace(reUnescape, '');
+
+ } else if (className){
+ className = className.replace(reUnescape, '');
+
+ if (!currentParsed.classList) currentParsed.classList = [];
+ if (!currentParsed.classes) currentParsed.classes = [];
+ currentParsed.classList.push(className);
+ currentParsed.classes.push({
+ value: className,
+ regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
+ });
+
+ } else if (pseudoClass){
+ pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
+ pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
+
+ if (!currentParsed.pseudos) currentParsed.pseudos = [];
+ currentParsed.pseudos.push({
+ key: pseudoClass.replace(reUnescape, ''),
+ value: pseudoClassValue,
+ type: pseudoMarker.length == 1 ? 'class' : 'element'
+ });
+
+ } else if (attributeKey){
+ attributeKey = attributeKey.replace(reUnescape, '');
+ attributeValue = (attributeValue || '').replace(reUnescape, '');
+
+ var test, regexp;
+
+ switch (attributeOperator){
+ case '^=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) ); break;
+ case '$=' : regexp = new RegExp( escapeRegExp(attributeValue) +'$' ); break;
+ case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
+ case '|=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) +'(-|$)' ); break;
+ case '=' : test = function(value){
+ return attributeValue == value;
+ }; break;
+ case '*=' : test = function(value){
+ return value && value.indexOf(attributeValue) > -1;
+ }; break;
+ case '!=' : test = function(value){
+ return attributeValue != value;
+ }; break;
+ default : test = function(value){
+ return !!value;
+ };
+ }
+
+ if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
+ return false;
+ };
+
+ if (!test) test = function(value){
+ return value && regexp.test(value);
+ };
+
+ if (!currentParsed.attributes) currentParsed.attributes = [];
+ currentParsed.attributes.push({
+ key: attributeKey,
+ operator: attributeOperator,
+ value: attributeValue,
+ test: test
+ });
+
+ }
+
+ return '';
+};
+
+// Slick NS
+
+var Slick = (this.Slick || {});
+
+Slick.parse = function(expression){
+ return parse(expression);
+};
+
+Slick.escapeRegExp = escapeRegExp;
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+
+/*
+---
+name: Slick.Finder
+description: The new, superfast css selector engine.
+provides: Slick.Finder
+requires: Slick.Parser
+...
+*/
+
+;(function(){
+
+var local = {},
+ featuresCache = {},
+ toString = Object.prototype.toString;
+
+// Feature / Bug detection
+
+local.isNativeCode = function(fn){
+ return (/\{\s*\[native code\]\s*\}/).test('' + fn);
+};
+
+local.isXML = function(document){
+ return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
+ (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
+};
+
+local.setDocument = function(document){
+
+ // convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
+ var nodeType = document.nodeType;
+ if (nodeType == 9); // document
+ else if (nodeType) document = document.ownerDocument; // node
+ else if (document.navigator) document = document.document; // window
+ else return;
+
+ // check if it's the old document
+
+ if (this.document === document) return;
+ this.document = document;
+
+ // check if we have done feature detection on this document before
+
+ var root = document.documentElement,
+ rootUid = this.getUIDXML(root),
+ features = featuresCache[rootUid],
+ feature;
+
+ if (features){
+ for (feature in features){
+ this[feature] = features[feature];
+ }
+ return;
+ }
+
+ features = featuresCache[rootUid] = {};
+
+ features.root = root;
+ features.isXMLDocument = this.isXML(document);
+
+ features.brokenStarGEBTN
+ = features.starSelectsClosedQSA
+ = features.idGetsName
+ = features.brokenMixedCaseQSA
+ = features.brokenGEBCN
+ = features.brokenCheckedQSA
+ = features.brokenEmptyAttributeQSA
+ = features.isHTMLDocument
+ = features.nativeMatchesSelector
+ = false;
+
+ var starSelectsClosed, starSelectsComments,
+ brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
+ brokenFormAttributeGetter;
+
+ var selected, id = 'slick_uniqueid';
+ var testNode = document.createElement('div');
+
+ var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
+ testRoot.appendChild(testNode);
+
+ // on non-HTML documents innerHTML and getElementsById doesnt work properly
+ try {
+ testNode.innerHTML = '<a id="'+id+'"></a>';
+ features.isHTMLDocument = !!document.getElementById(id);
+ } catch(e){};
+
+ if (features.isHTMLDocument){
+
+ testNode.style.display = 'none';
+
+ // IE returns comment nodes for getElementsByTagName('*') for some documents
+ testNode.appendChild(document.createComment(''));
+ starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
+
+ // IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
+ try {
+ testNode.innerHTML = 'foo</foo>';
+ selected = testNode.getElementsByTagName('*');
+ starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+ } catch(e){};
+
+ features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
+
+ // IE returns elements with the name instead of just id for getElementsById for some documents
+ try {
+ testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
+ features.idGetsName = document.getElementById(id) === testNode.firstChild;
+ } catch(e){};
+
+ if (testNode.getElementsByClassName){
+
+ // Safari 3.2 getElementsByClassName caches results
+ try {
+ testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
+ testNode.getElementsByClassName('b').length;
+ testNode.firstChild.className = 'b';
+ cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
+ } catch(e){};
+
+ // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
+ try {
+ testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
+ brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
+ } catch(e){};
+
+ features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
+ }
+
+ if (testNode.querySelectorAll){
+ // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
+ try {
+ testNode.innerHTML = 'foo</foo>';
+ selected = testNode.querySelectorAll('*');
+ features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+ } catch(e){};
+
+ // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
+ try {
+ testNode.innerHTML = '<a class="MiX"></a>';
+ features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
+ } catch(e){};
+
+ // Webkit and Opera dont return selected options on querySelectorAll
+ try {
+ testNode.innerHTML = '<select><option selected="selected">a</option></select>';
+ features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
+ } catch(e){};
+
+ // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
+ try {
+ testNode.innerHTML = '<a class=""></a>';
+ features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
+ } catch(e){};
+
+ }
+
+ // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
+ try {
+ testNode.innerHTML = '<form action="s"><input id="action"/></form>';
+ brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
+ } catch(e){};
+
+ // native matchesSelector function
+
+ features.nativeMatchesSelector = root.matches || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
+ if (features.nativeMatchesSelector) try {
+ // if matchesSelector trows errors on incorrect sintaxes we can use it
+ features.nativeMatchesSelector.call(root, ':slick');
+ features.nativeMatchesSelector = null;
+ } catch(e){};
+
+ }
+
+ try {
+ root.slick_expando = 1;
+ delete root.slick_expando;
+ features.getUID = this.getUIDHTML;
+ } catch(e) {
+ features.getUID = this.getUIDXML;
+ }
+
+ testRoot.removeChild(testNode);
+ testNode = selected = testRoot = null;
+
+ // getAttribute
+
+ features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
+ var method = this.attributeGetters[name];
+ if (method) return method.call(node);
+ var attributeNode = node.getAttributeNode(name);
+ return (attributeNode) ? attributeNode.nodeValue : null;
+ } : function(node, name){
+ var method = this.attributeGetters[name];
+ return (method) ? method.call(node) : node.getAttribute(name);
+ };
+
+ // hasAttribute
+
+ features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) {
+ return node.hasAttribute(attribute);
+ } : function(node, attribute) {
+ node = node.getAttributeNode(attribute);
+ return !!(node && (node.specified || node.nodeValue));
+ };
+
+ // contains
+ // FIXME: Add specs: local.contains should be different for xml and html documents?
+ var nativeRootContains = root && this.isNativeCode(root.contains),
+ nativeDocumentContains = document && this.isNativeCode(document.contains);
+
+ features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){
+ return context.contains(node);
+ } : (nativeRootContains && !nativeDocumentContains) ? function(context, node){
+ // IE8 does not have .contains on document.
+ return context === node || ((context === document) ? document.documentElement : context).contains(node);
+ } : (root && root.compareDocumentPosition) ? function(context, node){
+ return context === node || !!(context.compareDocumentPosition(node) & 16);
+ } : function(context, node){
+ if (node) do {
+ if (node === context) return true;
+ } while ((node = node.parentNode));
+ return false;
+ };
+
+ // document order sorting
+ // credits to Sizzle (http://sizzlejs.com/)
+
+ features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
+ if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
+ return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+ } : ('sourceIndex' in root) ? function(a, b){
+ if (!a.sourceIndex || !b.sourceIndex) return 0;
+ return a.sourceIndex - b.sourceIndex;
+ } : (document.createRange) ? function(a, b){
+ if (!a.ownerDocument || !b.ownerDocument) return 0;
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+ aRange.setStart(a, 0);
+ aRange.setEnd(a, 0);
+ bRange.setStart(b, 0);
+ bRange.setEnd(b, 0);
+ return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+ } : null ;
+
+ root = null;
+
+ for (feature in features){
+ this[feature] = features[feature];
+ }
+};
+
+// Main Method
+
+var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
+ reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
+ qsaFailExpCache = {};
+
+local.search = function(context, expression, append, first){
+
+ var found = this.found = (first) ? null : (append || []);
+
+ if (!context) return found;
+ else if (context.navigator) context = context.document; // Convert the node from a window to a document
+ else if (!context.nodeType) return found;
+
+ // setup
+
+ var parsed, i,
+ uniques = this.uniques = {},
+ hasOthers = !!(append && append.length),
+ contextIsDocument = (context.nodeType == 9);
+
+ if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
+
+ // avoid duplicating items already in the append array
+ if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
+
+ // expression checks
+
+ if (typeof expression == 'string'){ // expression is a string
+
+ /*<simple-selectors-override>*/
+ var simpleSelector = expression.match(reSimpleSelector);
+ simpleSelectors: if (simpleSelector) {
+
+ var symbol = simpleSelector[1],
+ name = simpleSelector[2],
+ node, nodes;
+
+ if (!symbol){
+
+ if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
+ nodes = context.getElementsByTagName(name);
+ if (first) return nodes[0] || null;
+ for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+
+ } else if (symbol == '#'){
+
+ if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
+ node = context.getElementById(name);
+ if (!node) return found;
+ if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
+ if (first) return node || null;
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+
+ } else if (symbol == '.'){
+
+ if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
+ if (context.getElementsByClassName && !this.brokenGEBCN){
+ nodes = context.getElementsByClassName(name);
+ if (first) return nodes[0] || null;
+ for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+ } else {
+ var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
+ nodes = context.getElementsByTagName('*');
+ for (i = 0; node = nodes[i++];){
+ className = node.className;
+ if (!(className && matchClass.test(className))) continue;
+ if (first) return node;
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+ }
+
+ }
+
+ if (hasOthers) this.sort(found);
+ return (first) ? null : found;
+
+ }
+ /*</simple-selectors-override>*/
+
+ /*<query-selector-override>*/
+ querySelector: if (context.querySelectorAll) {
+
+ if (!this.isHTMLDocument
+ || qsaFailExpCache[expression]
+ //TODO: only skip when expression is actually mixed case
+ || this.brokenMixedCaseQSA
+ || (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
+ || (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
+ || (!contextIsDocument //Abort when !contextIsDocument and...
+ // there are multiple expressions in the selector
+ // since we currently only fix non-document rooted QSA for single expression selectors
+ && expression.indexOf(',') > -1
+ )
+ || Slick.disableQSA
+ ) break querySelector;
+
+ var _expression = expression, _context = context;
+ if (!contextIsDocument){
+ // non-document rooted QSA
+ // credits to Andrew Dupont
+ var currentId = _context.getAttribute('id'), slickid = 'slickid__';
+ _context.setAttribute('id', slickid);
+ _expression = '#' + slickid + ' ' + _expression;
+ context = _context.parentNode;
+ }
+
+ try {
+ if (first) return context.querySelector(_expression) || null;
+ else nodes = context.querySelectorAll(_expression);
+ } catch(e) {
+ qsaFailExpCache[expression] = 1;
+ break querySelector;
+ } finally {
+ if (!contextIsDocument){
+ if (currentId) _context.setAttribute('id', currentId);
+ else _context.removeAttribute('id');
+ context = _context;
+ }
+ }
+
+ if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
+ if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ } else for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+
+ if (hasOthers) this.sort(found);
+ return found;
+
+ }
+ /*</query-selector-override>*/
+
+ parsed = this.Slick.parse(expression);
+ if (!parsed.length) return found;
+ } else if (expression == null){ // there is no expression
+ return found;
+ } else if (expression.Slick){ // expression is a parsed Slick object
+ parsed = expression;
+ } else if (this.contains(context.documentElement || context, expression)){ // expression is a node
+ (found) ? found.push(expression) : found = expression;
+ return found;
+ } else { // other junk
+ return found;
+ }
+
+ /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+ // cache elements for the nth selectors
+
+ this.posNTH = {};
+ this.posNTHLast = {};
+ this.posNTHType = {};
+ this.posNTHTypeLast = {};
+
+ /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+ // if append is null and there is only a single selector with one expression use pushArray, else use pushUID
+ this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
+
+ if (found == null) found = [];
+
+ // default engine
+
+ var j, m, n;
+ var combinator, tag, id, classList, classes, attributes, pseudos;
+ var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
+
+ search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
+
+ combinator = 'combinator:' + currentBit.combinator;
+ if (!this[combinator]) continue search;
+
+ tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
+ id = currentBit.id;
+ classList = currentBit.classList;
+ classes = currentBit.classes;
+ attributes = currentBit.attributes;
+ pseudos = currentBit.pseudos;
+ lastBit = (j === (currentExpression.length - 1));
+
+ this.bitUniques = {};
+
+ if (lastBit){
+ this.uniques = uniques;
+ this.found = found;
+ } else {
+ this.uniques = {};
+ this.found = [];
+ }
+
+ if (j === 0){
+ this[combinator](context, tag, id, classes, attributes, pseudos, classList);
+ if (first && lastBit && found.length) break search;
+ } else {
+ if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
+ this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+ if (found.length) break search;
+ } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+ }
+
+ currentItems = this.found;
+ }
+
+ // should sort if there are nodes in append and if you pass multiple expressions.
+ if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
+
+ return (first) ? (found[0] || null) : found;
+};
+
+// Utils
+
+local.uidx = 1;
+local.uidk = 'slick-uniqueid';
+
+local.getUIDXML = function(node){
+ var uid = node.getAttribute(this.uidk);
+ if (!uid){
+ uid = this.uidx++;
+ node.setAttribute(this.uidk, uid);
+ }
+ return uid;
+};
+
+local.getUIDHTML = function(node){
+ return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
+};
+
+// sort based on the setDocument documentSorter method.
+
+local.sort = function(results){
+ if (!this.documentSorter) return results;
+ results.sort(this.documentSorter);
+ return results;
+};
+
+/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+local.cacheNTH = {};
+
+local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
+
+local.parseNTHArgument = function(argument){
+ var parsed = argument.match(this.matchNTH);
+ if (!parsed) return false;
+ var special = parsed[2] || false;
+ var a = parsed[1] || 1;
+ if (a == '-') a = -1;
+ var b = +parsed[3] || 0;
+ parsed =
+ (special == 'n') ? {a: a, b: b} :
+ (special == 'odd') ? {a: 2, b: 1} :
+ (special == 'even') ? {a: 2, b: 0} : {a: 0, b: a};
+
+ return (this.cacheNTH[argument] = parsed);
+};
+
+local.createNTHPseudo = function(child, sibling, positions, ofType){
+ return function(node, argument){
+ var uid = this.getUID(node);
+ if (!this[positions][uid]){
+ var parent = node.parentNode;
+ if (!parent) return false;
+ var el = parent[child], count = 1;
+ if (ofType){
+ var nodeName = node.nodeName;
+ do {
+ if (el.nodeName != nodeName) continue;
+ this[positions][this.getUID(el)] = count++;
+ } while ((el = el[sibling]));
+ } else {
+ do {
+ if (el.nodeType != 1) continue;
+ this[positions][this.getUID(el)] = count++;
+ } while ((el = el[sibling]));
+ }
+ }
+ argument = argument || 'n';
+ var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
+ if (!parsed) return false;
+ var a = parsed.a, b = parsed.b, pos = this[positions][uid];
+ if (a == 0) return b == pos;
+ if (a > 0){
+ if (pos < b) return false;
+ } else {
+ if (b < pos) return false;
+ }
+ return ((pos - b) % a) == 0;
+ };
+};
+
+/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+local.pushArray = function(node, tag, id, classes, attributes, pseudos){
+ if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
+};
+
+local.pushUID = function(node, tag, id, classes, attributes, pseudos){
+ var uid = this.getUID(node);
+ if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
+ this.uniques[uid] = true;
+ this.found.push(node);
+ }
+};
+
+local.matchNode = function(node, selector){
+ if (this.isHTMLDocument && this.nativeMatchesSelector){
+ try {
+ return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
+ } catch(matchError) {}
+ }
+
+ var parsed = this.Slick.parse(selector);
+ if (!parsed) return true;
+
+ // simple (single) selectors
+ var expressions = parsed.expressions, simpleExpCounter = 0, i;
+ for (i = 0; (currentExpression = expressions[i]); i++){
+ if (currentExpression.length == 1){
+ var exp = currentExpression[0];
+ if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
+ simpleExpCounter++;
+ }
+ }
+
+ if (simpleExpCounter == parsed.length) return false;
+
+ var nodes = this.search(this.document, parsed), item;
+ for (i = 0; item = nodes[i++];){
+ if (item === node) return true;
+ }
+ return false;
+};
+
+local.matchPseudo = function(node, name, argument){
+ var pseudoName = 'pseudo:' + name;
+ if (this[pseudoName]) return this[pseudoName](node, argument);
+ var attribute = this.getAttribute(node, name);
+ return (argument) ? argument == attribute : !!attribute;
+};
+
+local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
+ if (tag){
+ var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
+ if (tag == '*'){
+ if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
+ } else {
+ if (nodeName != tag) return false;
+ }
+ }
+
+ if (id && node.getAttribute('id') != id) return false;
+
+ var i, part, cls;
+ if (classes) for (i = classes.length; i--;){
+ cls = this.getAttribute(node, 'class');
+ if (!(cls && classes[i].regexp.test(cls))) return false;
+ }
+ if (attributes) for (i = attributes.length; i--;){
+ part = attributes[i];
+ if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
+ }
+ if (pseudos) for (i = pseudos.length; i--;){
+ part = pseudos[i];
+ if (!this.matchPseudo(node, part.key, part.value)) return false;
+ }
+ return true;
+};
+
+var combinators = {
+
+ ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
+
+ var i, item, children;
+
+ if (this.isHTMLDocument){
+ getById: if (id){
+ item = this.document.getElementById(id);
+ if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
+ // all[id] returns all the elements with that name or id inside node
+ // if theres just one it will return the element, else it will be a collection
+ children = node.all[id];
+ if (!children) return;
+ if (!children[0]) children = [children];
+ for (i = 0; item = children[i++];){
+ var idNode = item.getAttributeNode('id');
+ if (idNode && idNode.nodeValue == id){
+ this.push(item, tag, null, classes, attributes, pseudos);
+ break;
+ }
+ }
+ return;
+ }
+ if (!item){
+ // if the context is in the dom we return, else we will try GEBTN, breaking the getById label
+ if (this.contains(this.root, node)) return;
+ else break getById;
+ } else if (this.document !== node && !this.contains(node, item)) return;
+ this.push(item, tag, null, classes, attributes, pseudos);
+ return;
+ }
+ getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
+ children = node.getElementsByClassName(classList.join(' '));
+ if (!(children && children.length)) break getByClass;
+ for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
+ return;
+ }
+ }
+ getByTag: {
+ children = node.getElementsByTagName(tag);
+ if (!(children && children.length)) break getByTag;
+ if (!this.brokenStarGEBTN) tag = null;
+ for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '>': function(node, tag, id, classes, attributes, pseudos){ // direct children
+ if ((node = node.firstChild)) do {
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ } while ((node = node.nextSibling));
+ },
+
+ '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
+ while ((node = node.nextSibling)) if (node.nodeType == 1){
+ this.push(node, tag, id, classes, attributes, pseudos);
+ break;
+ }
+ },
+
+ '^': function(node, tag, id, classes, attributes, pseudos){ // first child
+ node = node.firstChild;
+ if (node){
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
+ while ((node = node.nextSibling)){
+ if (node.nodeType != 1) continue;
+ var uid = this.getUID(node);
+ if (this.bitUniques[uid]) break;
+ this.bitUniques[uid] = true;
+ this.push(node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
+ this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+ this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+ },
+
+ '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
+ this['combinator:~'](node, tag, id, classes, attributes, pseudos);
+ this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
+ while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
+ node = node.parentNode;
+ if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
+ while ((node = node.previousSibling)) if (node.nodeType == 1){
+ this.push(node, tag, id, classes, attributes, pseudos);
+ break;
+ }
+ },
+
+ '!^': function(node, tag, id, classes, attributes, pseudos){ // last child
+ node = node.lastChild;
+ if (node){
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
+ while ((node = node.previousSibling)){
+ if (node.nodeType != 1) continue;
+ var uid = this.getUID(node);
+ if (this.bitUniques[uid]) break;
+ this.bitUniques[uid] = true;
+ this.push(node, tag, id, classes, attributes, pseudos);
+ }
+ }
+
+};
+
+for (var c in combinators) local['combinator:' + c] = combinators[c];
+
+var pseudos = {
+
+ /*<pseudo-selectors>*/
+
+ 'empty': function(node){
+ var child = node.firstChild;
+ return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
+ },
+
+ 'not': function(node, expression){
+ return !this.matchNode(node, expression);
+ },
+
+ 'contains': function(node, text){
+ return (node.innerText || node.textContent || '').indexOf(text) > -1;
+ },
+
+ 'first-child': function(node){
+ while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
+ return true;
+ },
+
+ 'last-child': function(node){
+ while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
+ return true;
+ },
+
+ 'only-child': function(node){
+ var prev = node;
+ while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
+ var next = node;
+ while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
+ return true;
+ },
+
+ /*<nth-pseudo-selectors>*/
+
+ 'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
+
+ 'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
+
+ 'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
+
+ 'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
+
+ 'index': function(node, index){
+ return this['pseudo:nth-child'](node, '' + (index + 1));
+ },
+
+ 'even': function(node){
+ return this['pseudo:nth-child'](node, '2n');
+ },
+
+ 'odd': function(node){
+ return this['pseudo:nth-child'](node, '2n+1');
+ },
+
+ /*</nth-pseudo-selectors>*/
+
+ /*<of-type-pseudo-selectors>*/
+
+ 'first-of-type': function(node){
+ var nodeName = node.nodeName;
+ while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
+ return true;
+ },
+
+ 'last-of-type': function(node){
+ var nodeName = node.nodeName;
+ while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
+ return true;
+ },
+
+ 'only-of-type': function(node){
+ var prev = node, nodeName = node.nodeName;
+ while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
+ var next = node;
+ while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
+ return true;
+ },
+
+ /*</of-type-pseudo-selectors>*/
+
+ // custom pseudos
+
+ 'enabled': function(node){
+ return !node.disabled;
+ },
+
+ 'disabled': function(node){
+ return node.disabled;
+ },
+
+ 'checked': function(node){
+ return node.checked || node.selected;
+ },
+
+ 'focus': function(node){
+ return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
+ },
+
+ 'root': function(node){
+ return (node === this.root);
+ },
+
+ 'selected': function(node){
+ return node.selected;
+ }
+
+ /*</pseudo-selectors>*/
+};
+
+for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
+
+// attributes methods
+
+var attributeGetters = local.attributeGetters = {
+
+ 'for': function(){
+ return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
+ },
+
+ 'href': function(){
+ return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
+ },
+
+ 'style': function(){
+ return (this.style) ? this.style.cssText : this.getAttribute('style');
+ },
+
+ 'tabindex': function(){
+ var attributeNode = this.getAttributeNode('tabindex');
+ return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+ },
+
+ 'type': function(){
+ return this.getAttribute('type');
+ },
+
+ 'maxlength': function(){
+ var attributeNode = this.getAttributeNode('maxLength');
+ return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+ }
+
+};
+
+attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
+
+// Slick
+
+var Slick = local.Slick = (this.Slick || {});
+
+Slick.version = '1.1.7';
+
+// Slick finder
+
+Slick.search = function(context, expression, append){
+ return local.search(context, expression, append);
+};
+
+Slick.find = function(context, expression){
+ return local.search(context, expression, null, true);
+};
+
+// Slick containment checker
+
+Slick.contains = function(container, node){
+ local.setDocument(container);
+ return local.contains(container, node);
+};
+
+// Slick attribute getter
+
+Slick.getAttribute = function(node, name){
+ local.setDocument(node);
+ return local.getAttribute(node, name);
+};
+
+Slick.hasAttribute = function(node, name){
+ local.setDocument(node);
+ return local.hasAttribute(node, name);
+};
+
+// Slick matcher
+
+Slick.match = function(node, selector){
+ if (!(node && selector)) return false;
+ if (!selector || selector === node) return true;
+ local.setDocument(node);
+ return local.matchNode(node, selector);
+};
+
+// Slick attribute accessor
+
+Slick.defineAttributeGetter = function(name, fn){
+ local.attributeGetters[name] = fn;
+ return this;
+};
+
+Slick.lookupAttributeGetter = function(name){
+ return local.attributeGetters[name];
+};
+
+// Slick pseudo accessor
+
+Slick.definePseudo = function(name, fn){
+ local['pseudo:' + name] = function(node, argument){
+ return fn.call(node, argument);
+ };
+ return this;
+};
+
+Slick.lookupPseudo = function(name){
+ var pseudo = local['pseudo:' + name];
+ if (pseudo) return function(argument){
+ return pseudo.call(this, argument);
+ };
+ return null;
+};
+
+// Slick overrides accessor
+
+Slick.override = function(regexp, fn){
+ local.override(regexp, fn);
+ return this;
+};
+
+Slick.isXML = local.isXML;
+
+Slick.uidOf = function(node){
+ return local.getUIDHTML(node);
+};
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+
+/*
+---
+
+name: Element
+
+description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements.
+
+license: MIT-style license.
+
+requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder]
+
+provides: [Element, Elements, $, $$, IFrame, Selectors]
+
+...
+*/
+
+var Element = this.Element = function(tag, props){
+ var konstructor = Element.Constructors[tag];
+ if (konstructor) return konstructor(props);
+ if (typeof tag != 'string') return document.id(tag).set(props);
+
+ if (!props) props = {};
+
+ if (!(/^[\w-]+$/).test(tag)){
+ var parsed = Slick.parse(tag).expressions[0][0];
+ tag = (parsed.tag == '*') ? 'div' : parsed.tag;
+ if (parsed.id && props.id == null) props.id = parsed.id;
+
+ var attributes = parsed.attributes;
+ if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){
+ attr = attributes[i];
+ if (props[attr.key] != null) continue;
+
+ if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value;
+ else if (!attr.value && !attr.operator) props[attr.key] = true;
+ }
+
+ if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
+ }
+
+ return document.newElement(tag, props);
+};
+
+
+if (Browser.Element){
+ Element.prototype = Browser.Element.prototype;
+ // IE8 and IE9 require the wrapping.
+ Element.prototype._fireEvent = (function(fireEvent){
+ return function(type, event){
+ return fireEvent.call(this, type, event);
+ };
+ })(Element.prototype.fireEvent);
+}
+
+new Type('Element', Element).mirror(function(name){
+ if (Array.prototype[name]) return;
+
+ var obj = {};
+ obj[name] = function(){
+ var results = [], args = arguments, elements = true;
+ for (var i = 0, l = this.length; i < l; i++){
+ var element = this[i], result = results[i] = element[name].apply(element, args);
+ elements = (elements && typeOf(result) == 'element');
+ }
+ return (elements) ? new Elements(results) : results;
+ };
+
+ Elements.implement(obj);
+});
+
+if (!Browser.Element){
+ Element.parent = Object;
+
+ Element.Prototype = {
+ '$constructor': Element,
+ '$family': Function.from('element').hide()
+ };
+
+ Element.mirror(function(name, method){
+ Element.Prototype[name] = method;
+ });
+}
+
+Element.Constructors = {};
+
+
+
+var IFrame = new Type('IFrame', function(){
+ var params = Array.link(arguments, {
+ properties: Type.isObject,
+ iframe: function(obj){
+ return (obj != null);
+ }
+ });
+
+ var props = params.properties || {}, iframe;
+ if (params.iframe) iframe = document.id(params.iframe);
+ var onload = props.onload || function(){};
+ delete props.onload;
+ props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
+ iframe = new Element(iframe || 'iframe', props);
+
+ var onLoad = function(){
+ onload.call(iframe.contentWindow);
+ };
+
+ if (window.frames[props.id]) onLoad();
+ else iframe.addListener('load', onLoad);
+ return iframe;
+});
+
+var Elements = this.Elements = function(nodes){
+ if (nodes && nodes.length){
+ var uniques = {}, node;
+ for (var i = 0; node = nodes[i++];){
+ var uid = Slick.uidOf(node);
+ if (!uniques[uid]){
+ uniques[uid] = true;
+ this.push(node);
+ }
+ }
+ }
+};
+
+Elements.prototype = {length: 0};
+Elements.parent = Array;
+
+new Type('Elements', Elements).implement({
+
+ filter: function(filter, bind){
+ if (!filter) return this;
+ return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){
+ return item.match(filter);
+ } : filter, bind));
+ }.protect(),
+
+ push: function(){
+ var length = this.length;
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = document.id(arguments[i]);
+ if (item) this[length++] = item;
+ }
+ return (this.length = length);
+ }.protect(),
+
+ unshift: function(){
+ var items = [];
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = document.id(arguments[i]);
+ if (item) items.push(item);
+ }
+ return Array.prototype.unshift.apply(this, items);
+ }.protect(),
+
+ concat: function(){
+ var newElements = new Elements(this);
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = arguments[i];
+ if (Type.isEnumerable(item)) newElements.append(item);
+ else newElements.push(item);
+ }
+ return newElements;
+ }.protect(),
+
+ append: function(collection){
+ for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
+ return this;
+ }.protect(),
+
+ empty: function(){
+ while (this.length) delete this[--this.length];
+ return this;
+ }.protect()
+
+});
+
+
+
+(function(){
+
+// FF, IE
+var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
+
+splice.call(object, 1, 1);
+if (object[1] == 1) Elements.implement('splice', function(){
+ var length = this.length;
+ var result = splice.apply(this, arguments);
+ while (length >= this.length) delete this[length--];
+ return result;
+}.protect());
+
+Array.forEachMethod(function(method, name){
+ Elements.implement(name, method);
+});
+
+Array.mirror(Elements);
+
+/*<ltIE8>*/
+var createElementAcceptsHTML;
+try {
+ createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x');
+} catch (e){}
+
+var escapeQuotes = function(html){
+ return ('' + html).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
+};
+/*</ltIE8>*/
+
+Document.implement({
+
+ newElement: function(tag, props){
+ if (props && props.checked != null) props.defaultChecked = props.checked;
+ /*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
+ if (createElementAcceptsHTML && props){
+ tag = '<' + tag;
+ if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
+ if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
+ tag += '>';
+ delete props.name;
+ delete props.type;
+ }
+ /*</ltIE8>*/
+ return this.id(this.createElement(tag)).set(props);
+ }
+
+});
+
+})();
+
+(function(){
+
+Slick.uidOf(window);
+Slick.uidOf(document);
+
+Document.implement({
+
+ newTextNode: function(text){
+ return this.createTextNode(text);
+ },
+
+ getDocument: function(){
+ return this;
+ },
+
+ getWindow: function(){
+ return this.window;
+ },
+
+ id: (function(){
+
+ var types = {
+
+ string: function(id, nocash, doc){
+ id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
+ return (id) ? types.element(id, nocash) : null;
+ },
+
+ element: function(el, nocash){
+ Slick.uidOf(el);
+ if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){
+ var fireEvent = el.fireEvent;
+ // wrapping needed in IE7, or else crash
+ el._fireEvent = function(type, event){
+ return fireEvent(type, event);
+ };
+ Object.append(el, Element.Prototype);
+ }
+ return el;
+ },
+
+ object: function(obj, nocash, doc){
+ if (obj.toElement) return types.element(obj.toElement(doc), nocash);
+ return null;
+ }
+
+ };
+
+ types.textnode = types.whitespace = types.window = types.document = function(zero){
+ return zero;
+ };
+
+ return function(el, nocash, doc){
+ if (el && el.$family && el.uniqueNumber) return el;
+ var type = typeOf(el);
+ return (types[type]) ? types[type](el, nocash, doc || document) : null;
+ };
+
+ })()
+
+});
+
+if (window.$ == null) Window.implement('$', function(el, nc){
+ return document.id(el, nc, this.document);
+});
+
+Window.implement({
+
+ getDocument: function(){
+ return this.document;
+ },
+
+ getWindow: function(){
+ return this;
+ }
+
+});
+
+[Document, Element].invoke('implement', {
+
+ getElements: function(expression){
+ return Slick.search(this, expression, new Elements);
+ },
+
+ getElement: function(expression){
+ return document.id(Slick.find(this, expression));
+ }
+
+});
+
+var contains = {contains: function(element){
+ return Slick.contains(this, element);
+}};
+
+if (!document.contains) Document.implement(contains);
+if (!document.createElement('div').contains) Element.implement(contains);
+
+
+
+// tree walking
+
+var injectCombinator = function(expression, combinator){
+ if (!expression) return combinator;
+
+ expression = Object.clone(Slick.parse(expression));
+
+ var expressions = expression.expressions;
+ for (var i = expressions.length; i--;)
+ expressions[i][0].combinator = combinator;
+
+ return expression;
+};
+
+Object.forEach({
+ getNext: '~',
+ getPrevious: '!~',
+ getParent: '!'
+}, function(combinator, method){
+ Element.implement(method, function(expression){
+ return this.getElement(injectCombinator(expression, combinator));
+ });
+});
+
+Object.forEach({
+ getAllNext: '~',
+ getAllPrevious: '!~',
+ getSiblings: '~~',
+ getChildren: '>',
+ getParents: '!'
+}, function(combinator, method){
+ Element.implement(method, function(expression){
+ return this.getElements(injectCombinator(expression, combinator));
+ });
+});
+
+Element.implement({
+
+ getFirst: function(expression){
+ return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
+ },
+
+ getLast: function(expression){
+ return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
+ },
+
+ getWindow: function(){
+ return this.ownerDocument.window;
+ },
+
+ getDocument: function(){
+ return this.ownerDocument;
+ },
+
+ getElementById: function(id){
+ return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
+ },
+
+ match: function(expression){
+ return !expression || Slick.match(this, expression);
+ }
+
+});
+
+
+
+if (window.$$ == null) Window.implement('$$', function(selector){
+ if (arguments.length == 1){
+ if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
+ else if (Type.isEnumerable(selector)) return new Elements(selector);
+ }
+ return new Elements(arguments);
+});
+
+// Inserters
+
+var inserters = {
+
+ before: function(context, element){
+ var parent = element.parentNode;
+ if (parent) parent.insertBefore(context, element);
+ },
+
+ after: function(context, element){
+ var parent = element.parentNode;
+ if (parent) parent.insertBefore(context, element.nextSibling);
+ },
+
+ bottom: function(context, element){
+ element.appendChild(context);
+ },
+
+ top: function(context, element){
+ element.insertBefore(context, element.firstChild);
+ }
+
+};
+
+inserters.inside = inserters.bottom;
+
+
+
+// getProperty / setProperty
+
+var propertyGetters = {}, propertySetters = {};
+
+// properties
+
+var properties = {};
+Array.forEach([
+ 'type', 'value', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan',
+ 'frameBorder', 'rowSpan', 'tabIndex', 'useMap'
+], function(property){
+ properties[property.toLowerCase()] = property;
+});
+
+properties.html = 'innerHTML';
+properties.text = (document.createElement('div').textContent == null) ? 'innerText': 'textContent';
+
+Object.forEach(properties, function(real, key){
+ propertySetters[key] = function(node, value){
+ node[real] = value;
+ };
+ propertyGetters[key] = function(node){
+ return node[real];
+ };
+});
+
+// Booleans
+
+var bools = [
+ 'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked',
+ 'disabled', 'readOnly', 'multiple', 'selected', 'noresize',
+ 'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay',
+ 'loop'
+];
+
+var booleans = {};
+Array.forEach(bools, function(bool){
+ var lower = bool.toLowerCase();
+ booleans[lower] = bool;
+ propertySetters[lower] = function(node, value){
+ node[bool] = !!value;
+ };
+ propertyGetters[lower] = function(node){
+ return !!node[bool];
+ };
+});
+
+// Special cases
+
+Object.append(propertySetters, {
+
+ 'class': function(node, value){
+ ('className' in node) ? node.className = (value || '') : node.setAttribute('class', value);
+ },
+
+ 'for': function(node, value){
+ ('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value);
+ },
+
+ 'style': function(node, value){
+ (node.style) ? node.style.cssText = value : node.setAttribute('style', value);
+ },
+
+ 'value': function(node, value){
+ node.value = (value != null) ? value : '';
+ }
+
+});
+
+propertyGetters['class'] = function(node){
+ return ('className' in node) ? node.className || null : node.getAttribute('class');
+};
+
+/* <webkit> */
+var el = document.createElement('button');
+// IE sets type as readonly and throws
+try { el.type = 'button'; } catch(e){}
+if (el.type != 'button') propertySetters.type = function(node, value){
+ node.setAttribute('type', value);
+};
+el = null;
+/* </webkit> */
+
+/*<IE>*/
+var input = document.createElement('input');
+input.value = 't';
+input.type = 'submit';
+if (input.value != 't') propertySetters.type = function(node, type){
+ var value = node.value;
+ node.type = type;
+ node.value = value;
+};
+input = null;
+/*</IE>*/
+
+/* getProperty, setProperty */
+
+/* <ltIE9> */
+var pollutesGetAttribute = (function(div){
+ div.random = 'attribute';
+ return (div.getAttribute('random') == 'attribute');
+})(document.createElement('div'));
+
+var hasCloneBug = (function(test){
+ test.innerHTML = '<object><param name="should_fix" value="the unknown"></object>';
+ return test.cloneNode(true).firstChild.childNodes.length != 1;
+})(document.createElement('div'));
+/* </ltIE9> */
+
+var hasClassList = !!document.createElement('div').classList;
+
+var classes = function(className){
+ var classNames = (className || '').clean().split(" "), uniques = {};
+ return classNames.filter(function(className){
+ if (className !== "" && !uniques[className]) return uniques[className] = className;
+ });
+};
+
+var addToClassList = function(name){
+ this.classList.add(name);
+};
+
+var removeFromClassList = function(name){
+ this.classList.remove(name);
+};
+
+Element.implement({
+
+ setProperty: function(name, value){
+ var setter = propertySetters[name.toLowerCase()];
+ if (setter){
+ setter(this, value);
+ } else {
+ /* <ltIE9> */
+ var attributeWhiteList;
+ if (pollutesGetAttribute) attributeWhiteList = this.retrieve('$attributeWhiteList', {});
+ /* </ltIE9> */
+
+ if (value == null){
+ this.removeAttribute(name);
+ /* <ltIE9> */
+ if (pollutesGetAttribute) delete attributeWhiteList[name];
+ /* </ltIE9> */
+ } else {
+ this.setAttribute(name, '' + value);
+ /* <ltIE9> */
+ if (pollutesGetAttribute) attributeWhiteList[name] = true;
+ /* </ltIE9> */
+ }
+ }
+ return this;
+ },
+
+ setProperties: function(attributes){
+ for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
+ return this;
+ },
+
+ getProperty: function(name){
+ var getter = propertyGetters[name.toLowerCase()];
+ if (getter) return getter(this);
+ /* <ltIE9> */
+ if (pollutesGetAttribute){
+ var attr = this.getAttributeNode(name), attributeWhiteList = this.retrieve('$attributeWhiteList', {});
+ if (!attr) return null;
+ if (attr.expando && !attributeWhiteList[name]){
+ var outer = this.outerHTML;
+ // segment by the opening tag and find mention of attribute name
+ if (outer.substr(0, outer.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(name) < 0) return null;
+ attributeWhiteList[name] = true;
+ }
+ }
+ /* </ltIE9> */
+ var result = Slick.getAttribute(this, name);
+ return (!result && !Slick.hasAttribute(this, name)) ? null : result;
+ },
+
+ getProperties: function(){
+ var args = Array.from(arguments);
+ return args.map(this.getProperty, this).associate(args);
+ },
+
+ removeProperty: function(name){
+ return this.setProperty(name, null);
+ },
+
+ removeProperties: function(){
+ Array.each(arguments, this.removeProperty, this);
+ return this;
+ },
+
+ set: function(prop, value){
+ var property = Element.Properties[prop];
+ (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
+ }.overloadSetter(),
+
+ get: function(prop){
+ var property = Element.Properties[prop];
+ return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
+ }.overloadGetter(),
+
+ erase: function(prop){
+ var property = Element.Properties[prop];
+ (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
+ return this;
+ },
+
+ hasClass: hasClassList ? function(className){
+ return this.classList.contains(className);
+ } : function(className){
+ return this.className.clean().contains(className, ' ');
+ },
+
+ addClass: hasClassList ? function(className){
+ classes(className).forEach(addToClassList, this);
+ return this;
+ } : function(className){
+ this.className = classes(className + ' ' + this.className).join(' ');
+ return this;
+ },
+
+ removeClass: hasClassList ? function(className){
+ classes(className).forEach(removeFromClassList, this);
+ return this;
+ } : function(className){
+ var classNames = classes(this.className);
+ classes(className).forEach(classNames.erase, classNames);
+ this.className = classNames.join(' ');
+ return this;
+ },
+
+ toggleClass: function(className, force){
+ if (force == null) force = !this.hasClass(className);
+ return (force) ? this.addClass(className) : this.removeClass(className);
+ },
+
+ adopt: function(){
+ var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
+ if (length > 1) parent = fragment = document.createDocumentFragment();
+
+ for (var i = 0; i < length; i++){
+ var element = document.id(elements[i], true);
+ if (element) parent.appendChild(element);
+ }
+
+ if (fragment) this.appendChild(fragment);
+
+ return this;
+ },
+
+ appendText: function(text, where){
+ return this.grab(this.getDocument().newTextNode(text), where);
+ },
+
+ grab: function(el, where){
+ inserters[where || 'bottom'](document.id(el, true), this);
+ return this;
+ },
+
+ inject: function(el, where){
+ inserters[where || 'bottom'](this, document.id(el, true));
+ return this;
+ },
+
+ replaces: function(el){
+ el = document.id(el, true);
+ el.parentNode.replaceChild(this, el);
+ return this;
+ },
+
+ wraps: function(el, where){
+ el = document.id(el, true);
+ return this.replaces(el).grab(el, where);
+ },
+
+ getSelected: function(){
+ this.selectedIndex; // Safari 3.2.1
+ return new Elements(Array.from(this.options).filter(function(option){
+ return option.selected;
+ }));
+ },
+
+ toQueryString: function(){
+ var queryString = [];
+ this.getElements('input, select, textarea').each(function(el){
+ var type = el.type;
+ if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
+
+ var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
+ // IE
+ return document.id(opt).get('value');
+ }) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
+
+ Array.from(value).each(function(val){
+ if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
+ });
+ });
+ return queryString.join('&');
+ }
+
+});
+
+
+// appendHTML
+
+var appendInserters = {
+ before: 'beforeBegin',
+ after: 'afterEnd',
+ bottom: 'beforeEnd',
+ top: 'afterBegin',
+ inside: 'beforeEnd'
+};
+
+Element.implement('appendHTML', ('insertAdjacentHTML' in document.createElement('div')) ? function(html, where){
+ this.insertAdjacentHTML(appendInserters[where || 'bottom'], html);
+ return this;
+} : function(html, where){
+ var temp = new Element('div', {html: html}),
+ children = temp.childNodes,
+ fragment = temp.firstChild;
+
+ if (!fragment) return this;
+ if (children.length > 1){
+ fragment = document.createDocumentFragment();
+ for (var i = 0, l = children.length; i < l; i++){
+ fragment.appendChild(children[i]);
+ }
+ }
+
+ inserters[where || 'bottom'](fragment, this);
+ return this;
+});
+
+var collected = {}, storage = {};
+
+var get = function(uid){
+ return (storage[uid] || (storage[uid] = {}));
+};
+
+var clean = function(item){
+ var uid = item.uniqueNumber;
+ if (item.removeEvents) item.removeEvents();
+ if (item.clearAttributes) item.clearAttributes();
+ if (uid != null){
+ delete collected[uid];
+ delete storage[uid];
+ }
+ return item;
+};
+
+var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
+
+Element.implement({
+
+ destroy: function(){
+ var children = clean(this).getElementsByTagName('*');
+ Array.each(children, clean);
+ Element.dispose(this);
+ return null;
+ },
+
+ empty: function(){
+ Array.from(this.childNodes).each(Element.dispose);
+ return this;
+ },
+
+ dispose: function(){
+ return (this.parentNode) ? this.parentNode.removeChild(this) : this;
+ },
+
+ clone: function(contents, keepid){
+ contents = contents !== false;
+ var clone = this.cloneNode(contents), ce = [clone], te = [this], i;
+
+ if (contents){
+ ce.append(Array.from(clone.getElementsByTagName('*')));
+ te.append(Array.from(this.getElementsByTagName('*')));
+ }
+
+ for (i = ce.length; i--;){
+ var node = ce[i], element = te[i];
+ if (!keepid) node.removeAttribute('id');
+ /*<ltIE9>*/
+ if (node.clearAttributes){
+ node.clearAttributes();
+ node.mergeAttributes(element);
+ node.removeAttribute('uniqueNumber');
+ if (node.options){
+ var no = node.options, eo = element.options;
+ for (var j = no.length; j--;) no[j].selected = eo[j].selected;
+ }
+ }
+ /*</ltIE9>*/
+ var prop = formProps[element.tagName.toLowerCase()];
+ if (prop && element[prop]) node[prop] = element[prop];
+ }
+
+ /*<ltIE9>*/
+ if (hasCloneBug){
+ var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
+ for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
+ }
+ /*</ltIE9>*/
+ return document.id(clone);
+ }
+
+});
+
+[Element, Window, Document].invoke('implement', {
+
+ addListener: function(type, fn){
+ if (window.attachEvent && !window.addEventListener){
+ collected[Slick.uidOf(this)] = this;
+ }
+ if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
+ else this.attachEvent('on' + type, fn);
+ return this;
+ },
+
+ removeListener: function(type, fn){
+ if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
+ else this.detachEvent('on' + type, fn);
+ return this;
+ },
+
+ retrieve: function(property, dflt){
+ var storage = get(Slick.uidOf(this)), prop = storage[property];
+ if (dflt != null && prop == null) prop = storage[property] = dflt;
+ return prop != null ? prop : null;
+ },
+
+ store: function(property, value){
+ var storage = get(Slick.uidOf(this));
+ storage[property] = value;
+ return this;
+ },
+
+ eliminate: function(property){
+ var storage = get(Slick.uidOf(this));
+ delete storage[property];
+ return this;
+ }
+
+});
+
+/*<ltIE9>*/
+if (window.attachEvent && !window.addEventListener){
+ var gc = function(){
+ Object.each(collected, clean);
+ if (window.CollectGarbage) CollectGarbage();
+ window.removeListener('unload', gc);
+ }
+ window.addListener('unload', gc);
+}
+/*</ltIE9>*/
+
+Element.Properties = {};
+
+
+
+Element.Properties.style = {
+
+ set: function(style){
+ this.style.cssText = style;
+ },
+
+ get: function(){
+ return this.style.cssText;
+ },
+
+ erase: function(){
+ this.style.cssText = '';
+ }
+
+};
+
+Element.Properties.tag = {
+
+ get: function(){
+ return this.tagName.toLowerCase();
+ }
+
+};
+
+Element.Properties.html = {
+
+ set: function(html){
+ if (html == null) html = '';
+ else if (typeOf(html) == 'array') html = html.join('');
+ this.innerHTML = html;
+ },
+
+ erase: function(){
+ this.innerHTML = '';
+ }
+
+};
+
+var supportsHTML5Elements = true, supportsTableInnerHTML = true, supportsTRInnerHTML = true;
+
+/*<ltIE9>*/
+// technique by jdbarlett - http://jdbartlett.com/innershiv/
+var div = document.createElement('div');
+div.innerHTML = '<nav></nav>';
+supportsHTML5Elements = (div.childNodes.length == 1);
+if (!supportsHTML5Elements){
+ var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '),
+ fragment = document.createDocumentFragment(), l = tags.length;
+ while (l--) fragment.createElement(tags[l]);
+}
+div = null;
+/*</ltIE9>*/
+
+/*<IE>*/
+supportsTableInnerHTML = Function.attempt(function(){
+ var table = document.createElement('table');
+ table.innerHTML = '<tr><td></td></tr>';
+ return true;
+});
+
+/*<ltFF4>*/
+var tr = document.createElement('tr'), html = '<td></td>';
+tr.innerHTML = html;
+supportsTRInnerHTML = (tr.innerHTML == html);
+tr = null;
+/*</ltFF4>*/
+
+if (!supportsTableInnerHTML || !supportsTRInnerHTML || !supportsHTML5Elements){
+
+ Element.Properties.html.set = (function(set){
+
+ var translations = {
+ table: [1, '<table>', '</table>'],
+ select: [1, '<select>', '</select>'],
+ tbody: [2, '<table><tbody>', '</tbody></table>'],
+ tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
+ };
+
+ translations.thead = translations.tfoot = translations.tbody;
+
+ return function(html){
+ var wrap = translations[this.get('tag')];
+ if (!wrap && !supportsHTML5Elements) wrap = [0, '', ''];
+ if (!wrap) return set.call(this, html);
+
+ var level = wrap[0], wrapper = document.createElement('div'), target = wrapper;
+ if (!supportsHTML5Elements) fragment.appendChild(wrapper);
+ wrapper.innerHTML = [wrap[1], html, wrap[2]].flatten().join('');
+ while (level--) target = target.firstChild;
+ this.empty().adopt(target.childNodes);
+ if (!supportsHTML5Elements) fragment.removeChild(wrapper);
+ wrapper = null;
+ };
+
+ })(Element.Properties.html.set);
+}
+/*</IE>*/
+
+/*<ltIE9>*/
+var testForm = document.createElement('form');
+testForm.innerHTML = '<select><option>s</option></select>';
+
+if (testForm.firstChild.value != 's') Element.Properties.value = {
+
+ set: function(value){
+ var tag = this.get('tag');
+ if (tag != 'select') return this.setProperty('value', value);
+ var options = this.getElements('option');
+ value = String(value);
+ for (var i = 0; i < options.length; i++){
+ var option = options[i],
+ attr = option.getAttributeNode('value'),
+ optionValue = (attr && attr.specified) ? option.value : option.get('text');
+ if (optionValue === value) return option.selected = true;
+ }
+ },
+
+ get: function(){
+ var option = this, tag = option.get('tag');
+
+ if (tag != 'select' && tag != 'option') return this.getProperty('value');
+
+ if (tag == 'select' && !(option = option.getSelected()[0])) return '';
+
+ var attr = option.getAttributeNode('value');
+ return (attr && attr.specified) ? option.value : option.get('text');
+ }
+
+};
+testForm = null;
+/*</ltIE9>*/
+
+/*<IE>*/
+if (document.createElement('div').getAttributeNode('id')) Element.Properties.id = {
+ set: function(id){
+ this.id = this.getAttributeNode('id').value = id;
+ },
+ get: function(){
+ return this.id || null;
+ },
+ erase: function(){
+ this.id = this.getAttributeNode('id').value = '';
+ }
+};
+/*</IE>*/
+
+})();
+
+
+/*
+---
+
+name: Element.Style
+
+description: Contains methods for interacting with the styles of Elements in a fashionable way.
+
+license: MIT-style license.
+
+requires: Element
+
+provides: Element.Style
+
+...
+*/
+
+(function(){
+
+var html = document.html, el;
+
+//<ltIE9>
+// Check for oldIE, which does not remove styles when they're set to null
+el = document.createElement('div');
+el.style.color = 'red';
+el.style.color = null;
+var doesNotRemoveStyles = el.style.color == 'red';
+
+// check for oldIE, which returns border* shorthand styles in the wrong order (color-width-style instead of width-style-color)
+var border = '1px solid #123abc';
+el.style.border = border;
+var returnsBordersInWrongOrder = el.style.border != border;
+el = null;
+//</ltIE9>
+
+var hasGetComputedStyle = !!window.getComputedStyle;
+
+Element.Properties.styles = {set: function(styles){
+ this.setStyles(styles);
+}};
+
+var hasOpacity = (html.style.opacity != null),
+ hasFilter = (html.style.filter != null),
+ reAlpha = /alpha\(opacity=([\d.]+)\)/i;
+
+var setVisibility = function(element, opacity){
+ element.store('$opacity', opacity);
+ element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden';
+};
+
+//<ltIE9>
+var setFilter = function(element, regexp, value){
+ var style = element.style,
+ filter = style.filter || element.getComputedStyle('filter') || '';
+ style.filter = (regexp.test(filter) ? filter.replace(regexp, value) : filter + ' ' + value).trim();
+ if (!style.filter) style.removeAttribute('filter');
+};
+//</ltIE9>
+
+var setOpacity = (hasOpacity ? function(element, opacity){
+ element.style.opacity = opacity;
+} : (hasFilter ? function(element, opacity){
+ if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
+ if (opacity == null || opacity == 1){
+ setFilter(element, reAlpha, '');
+ if (opacity == 1 && getOpacity(element) != 1) setFilter(element, reAlpha, 'alpha(opacity=100)');
+ } else {
+ setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')');
+ }
+} : setVisibility));
+
+var getOpacity = (hasOpacity ? function(element){
+ var opacity = element.style.opacity || element.getComputedStyle('opacity');
+ return (opacity == '') ? 1 : opacity.toFloat();
+} : (hasFilter ? function(element){
+ var filter = (element.style.filter || element.getComputedStyle('filter')),
+ opacity;
+ if (filter) opacity = filter.match(reAlpha);
+ return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
+} : function(element){
+ var opacity = element.retrieve('$opacity');
+ if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1);
+ return opacity;
+}));
+
+var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat',
+ namedPositions = {left: '0%', top: '0%', center: '50%', right: '100%', bottom: '100%'},
+ hasBackgroundPositionXY = (html.style.backgroundPositionX != null);
+
+//<ltIE9>
+var removeStyle = function(style, property){
+ if (property == 'backgroundPosition'){
+ style.removeAttribute(property + 'X');
+ property += 'Y';
+ }
+ style.removeAttribute(property);
+};
+//</ltIE9>
+
+Element.implement({
+
+ getComputedStyle: function(property){
+ if (!hasGetComputedStyle && this.currentStyle) return this.currentStyle[property.camelCase()];
+ var defaultView = Element.getDocument(this).defaultView,
+ computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
+ return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : '';
+ },
+
+ setStyle: function(property, value){
+ if (property == 'opacity'){
+ if (value != null) value = parseFloat(value);
+ setOpacity(this, value);
+ return this;
+ }
+ property = (property == 'float' ? floatName : property).camelCase();
+ if (typeOf(value) != 'string'){
+ var map = (Element.Styles[property] || '@').split(' ');
+ value = Array.from(value).map(function(val, i){
+ if (!map[i]) return '';
+ return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
+ }).join(' ');
+ } else if (value == String(Number(value))){
+ value = Math.round(value);
+ }
+ this.style[property] = value;
+ //<ltIE9>
+ if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
+ removeStyle(this.style, property);
+ }
+ //</ltIE9>
+ return this;
+ },
+
+ getStyle: function(property){
+ if (property == 'opacity') return getOpacity(this);
+ property = (property == 'float' ? floatName : property).camelCase();
+ var result = this.style[property];
+ if (!result || property == 'zIndex'){
+ if (Element.ShortStyles.hasOwnProperty(property)){
+ result = [];
+ for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s));
+ return result.join(' ');
+ }
+ result = this.getComputedStyle(property);
+ }
+ if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){
+ return result.replace(/(top|right|bottom|left)/g, function(position){
+ return namedPositions[position];
+ }) || '0px';
+ }
+ if (!result && property == 'backgroundPosition') return '0px 0px';
+ if (result){
+ result = String(result);
+ var color = result.match(/rgba?\([\d\s,]+\)/);
+ if (color) result = result.replace(color[0], color[0].rgbToHex());
+ }
+ if (!hasGetComputedStyle && !this.style[property]){
+ if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){
+ var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
+ values.each(function(value){
+ size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
+ }, this);
+ return this['offset' + property.capitalize()] - size + 'px';
+ }
+ if ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
+ return '0px';
+ }
+ }
+ //<ltIE9>
+ if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){
+ return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1');
+ }
+ //</ltIE9>
+ return result;
+ },
+
+ setStyles: function(styles){
+ for (var style in styles) this.setStyle(style, styles[style]);
+ return this;
+ },
+
+ getStyles: function(){
+ var result = {};
+ Array.flatten(arguments).each(function(key){
+ result[key] = this.getStyle(key);
+ }, this);
+ return result;
+ }
+
+});
+
+Element.Styles = {
+ left: '@px', top: '@px', bottom: '@px', right: '@px',
+ width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
+ backgroundColor: 'rgb(@, @, @)', backgroundSize: '@px', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
+ fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
+ margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
+ borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
+ zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
+};
+
+
+
+
+
+Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
+
+['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
+ var Short = Element.ShortStyles;
+ var All = Element.Styles;
+ ['margin', 'padding'].each(function(style){
+ var sd = style + direction;
+ Short[style][sd] = All[sd] = '@px';
+ });
+ var bd = 'border' + direction;
+ Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
+ var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
+ Short[bd] = {};
+ Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
+ Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
+ Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
+});
+
+if (hasBackgroundPositionXY) Element.ShortStyles.backgroundPosition = {backgroundPositionX: '@', backgroundPositionY: '@'};
+})();
+
+
+/*
+---
+
+name: Element.Event
+
+description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events, if necessary.
+
+license: MIT-style license.
+
+requires: [Element, Event]
+
+provides: Element.Event
+
+...
+*/
+
+(function(){
+
+Element.Properties.events = {set: function(events){
+ this.addEvents(events);
+}};
+
+[Element, Window, Document].invoke('implement', {
+
+ addEvent: function(type, fn){
+ var events = this.retrieve('events', {});
+ if (!events[type]) events[type] = {keys: [], values: []};
+ if (events[type].keys.contains(fn)) return this;
+ events[type].keys.push(fn);
+ var realType = type,
+ custom = Element.Events[type],
+ condition = fn,
+ self = this;
+ if (custom){
+ if (custom.onAdd) custom.onAdd.call(this, fn, type);
+ if (custom.condition){
+ condition = function(event){
+ if (custom.condition.call(this, event, type)) return fn.call(this, event);
+ return true;
+ };
+ }
+ if (custom.base) realType = Function.from(custom.base).call(this, type);
+ }
+ var defn = function(){
+ return fn.call(self);
+ };
+ var nativeEvent = Element.NativeEvents[realType];
+ if (nativeEvent){
+ if (nativeEvent == 2){
+ defn = function(event){
+ event = new DOMEvent(event, self.getWindow());
+ if (condition.call(self, event) === false) event.stop();
+ };
+ }
+ this.addListener(realType, defn, arguments[2]);
+ }
+ events[type].values.push(defn);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ var list = events[type];
+ var index = list.keys.indexOf(fn);
+ if (index == -1) return this;
+ var value = list.values[index];
+ delete list.keys[index];
+ delete list.values[index];
+ var custom = Element.Events[type];
+ if (custom){
+ if (custom.onRemove) custom.onRemove.call(this, fn, type);
+ if (custom.base) type = Function.from(custom.base).call(this, type);
+ }
+ return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
+ },
+
+ addEvents: function(events){
+ for (var event in events) this.addEvent(event, events[event]);
+ return this;
+ },
+
+ removeEvents: function(events){
+ var type;
+ if (typeOf(events) == 'object'){
+ for (type in events) this.removeEvent(type, events[type]);
+ return this;
+ }
+ var attached = this.retrieve('events');
+ if (!attached) return this;
+ if (!events){
+ for (type in attached) this.removeEvents(type);
+ this.eliminate('events');
+ } else if (attached[events]){
+ attached[events].keys.each(function(fn){
+ this.removeEvent(events, fn);
+ }, this);
+ delete attached[events];
+ }
+ return this;
+ },
+
+ fireEvent: function(type, args, delay){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ args = Array.from(args);
+
+ events[type].keys.each(function(fn){
+ if (delay) fn.delay(delay, this, args);
+ else fn.apply(this, args);
+ }, this);
+ return this;
+ },
+
+ cloneEvents: function(from, type){
+ from = document.id(from);
+ var events = from.retrieve('events');
+ if (!events) return this;
+ if (!type){
+ for (var eventType in events) this.cloneEvents(from, eventType);
+ } else if (events[type]){
+ events[type].keys.each(function(fn){
+ this.addEvent(type, fn);
+ }, this);
+ }
+ return this;
+ }
+
+});
+
+Element.NativeEvents = {
+ click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
+ mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
+ mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
+ keydown: 2, keypress: 2, keyup: 2, //keyboard
+ orientationchange: 2, // mobile
+ touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
+ gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
+ focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements
+ load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
+ hashchange: 1, popstate: 2, // history
+ error: 1, abort: 1, scroll: 1 //misc
+};
+
+Element.Events = {
+ mousewheel: {
+ base: 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll'
+ }
+};
+
+var check = function(event){
+ var related = event.relatedTarget;
+ if (related == null) return true;
+ if (!related) return false;
+ return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
+};
+
+if ('onmouseenter' in document.documentElement){
+ Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2;
+ Element.MouseenterCheck = check;
+} else {
+ Element.Events.mouseenter = {
+ base: 'mouseover',
+ condition: check
+ };
+
+ Element.Events.mouseleave = {
+ base: 'mouseout',
+ condition: check
+ };
+}
+
+/*<ltIE9>*/
+if (!window.addEventListener){
+ Element.NativeEvents.propertychange = 2;
+ Element.Events.change = {
+ base: function(){
+ var type = this.type;
+ return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change';
+ },
+ condition: function(event){
+ return event.type != 'propertychange' || event.event.propertyName == 'checked';
+ }
+ };
+}
+/*</ltIE9>*/
+
+
+
+})();
+
+
+/*
+---
+
+name: Element.Delegation
+
+description: Extends the Element native object to include the delegate method for more efficient event management.
+
+license: MIT-style license.
+
+requires: [Element.Event]
+
+provides: [Element.Delegation]
+
+...
+*/
+
+(function(){
+
+var eventListenerSupport = !!window.addEventListener;
+
+Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2;
+
+var bubbleUp = function(self, match, fn, event, target){
+ while (target && target != self){
+ if (match(target, event)) return fn.call(target, event, target);
+ target = document.id(target.parentNode);
+ }
+};
+
+var map = {
+ mouseenter: {
+ base: 'mouseover',
+ condition: Element.MouseenterCheck
+ },
+ mouseleave: {
+ base: 'mouseout',
+ condition: Element.MouseenterCheck
+ },
+ focus: {
+ base: 'focus' + (eventListenerSupport ? '' : 'in'),
+ capture: true
+ },
+ blur: {
+ base: eventListenerSupport ? 'blur' : 'focusout',
+ capture: true
+ }
+};
+
+/*<ltIE9>*/
+var _key = '$delegation:';
+var formObserver = function(type){
+
+ return {
+
+ base: 'focusin',
+
+ remove: function(self, uid){
+ var list = self.retrieve(_key + type + 'listeners', {})[uid];
+ if (list && list.forms) for (var i = list.forms.length; i--;){
+ list.forms[i].removeEvent(type, list.fns[i]);
+ }
+ },
+
+ listen: function(self, match, fn, event, target, uid){
+ var form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
+ if (!form) return;
+
+ var listeners = self.retrieve(_key + type + 'listeners', {}),
+ listener = listeners[uid] || {forms: [], fns: []},
+ forms = listener.forms, fns = listener.fns;
+
+ if (forms.indexOf(form) != -1) return;
+ forms.push(form);
+
+ var _fn = function(event){
+ bubbleUp(self, match, fn, event, target);
+ };
+ form.addEvent(type, _fn);
+ fns.push(_fn);
+
+ listeners[uid] = listener;
+ self.store(_key + type + 'listeners', listeners);
+ }
+ };
+};
+
+var inputObserver = function(type){
+ return {
+ base: 'focusin',
+ listen: function(self, match, fn, event, target){
+ var events = {blur: function(){
+ this.removeEvents(events);
+ }};
+ events[type] = function(event){
+ bubbleUp(self, match, fn, event, target);
+ };
+ event.target.addEvents(events);
+ }
+ };
+};
+
+if (!eventListenerSupport) Object.append(map, {
+ submit: formObserver('submit'),
+ reset: formObserver('reset'),
+ change: inputObserver('change'),
+ select: inputObserver('select')
+});
+/*</ltIE9>*/
+
+var proto = Element.prototype,
+ addEvent = proto.addEvent,
+ removeEvent = proto.removeEvent;
+
+var relay = function(old, method){
+ return function(type, fn, useCapture){
+ if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture);
+ var parsed = Slick.parse(type).expressions[0][0];
+ if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture);
+ var newType = parsed.tag;
+ parsed.pseudos.slice(1).each(function(pseudo){
+ newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : '');
+ });
+ old.call(this, type, fn);
+ return method.call(this, newType, parsed.pseudos[0].value, fn);
+ };
+};
+
+var delegation = {
+
+ addEvent: function(type, match, fn){
+ var storage = this.retrieve('$delegates', {}), stored = storage[type];
+ if (stored) for (var _uid in stored){
+ if (stored[_uid].fn == fn && stored[_uid].match == match) return this;
+ }
+
+ var _type = type, _match = match, _fn = fn, _map = map[type] || {};
+ type = _map.base || _type;
+
+ match = function(target){
+ return Slick.match(target, _match);
+ };
+
+ var elementEvent = Element.Events[_type];
+ if (_map.condition || elementEvent && elementEvent.condition){
+ var __match = match, condition = _map.condition || elementEvent.condition;
+ match = function(target, event){
+ return __match(target, event) && condition.call(target, event, type);
+ };
+ }
+
+ var self = this, uid = String.uniqueID();
+ var delegator = _map.listen ? function(event, target){
+ if (!target && event && event.target) target = event.target;
+ if (target) _map.listen(self, match, fn, event, target, uid);
+ } : function(event, target){
+ if (!target && event && event.target) target = event.target;
+ if (target) bubbleUp(self, match, fn, event, target);
+ };
+
+ if (!stored) stored = {};
+ stored[uid] = {
+ match: _match,
+ fn: _fn,
+ delegator: delegator
+ };
+ storage[_type] = stored;
+ return addEvent.call(this, type, delegator, _map.capture);
+ },
+
+ removeEvent: function(type, match, fn, _uid){
+ var storage = this.retrieve('$delegates', {}), stored = storage[type];
+ if (!stored) return this;
+
+ if (_uid){
+ var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {};
+ type = _map.base || _type;
+ if (_map.remove) _map.remove(this, _uid);
+ delete stored[_uid];
+ storage[_type] = stored;
+ return removeEvent.call(this, type, delegator, _map.capture);
+ }
+
+ var __uid, s;
+ if (fn) for (__uid in stored){
+ s = stored[__uid];
+ if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid);
+ } else for (__uid in stored){
+ s = stored[__uid];
+ if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid);
+ }
+ return this;
+ }
+
+};
+
+[Element, Window, Document].invoke('implement', {
+ addEvent: relay(addEvent, delegation.addEvent),
+ removeEvent: relay(removeEvent, delegation.removeEvent)
+});
+
+})();
+
+
+/*
+---
+
+name: Element.Dimensions
+
+description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
+
+license: MIT-style license.
+
+credits:
+ - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
+ - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
+
+requires: [Element, Element.Style]
+
+provides: [Element.Dimensions]
+
+...
+*/
+
+(function(){
+
+var element = document.createElement('div'),
+ child = document.createElement('div');
+element.style.height = '0';
+element.appendChild(child);
+var brokenOffsetParent = (child.offsetParent === element);
+element = child = null;
+
+var isOffset = function(el){
+ return styleString(el, 'position') != 'static' || isBody(el);
+};
+
+var isOffsetStatic = function(el){
+ return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
+};
+
+Element.implement({
+
+ scrollTo: function(x, y){
+ if (isBody(this)){
+ this.getWindow().scrollTo(x, y);
+ } else {
+ this.scrollLeft = x;
+ this.scrollTop = y;
+ }
+ return this;
+ },
+
+ getSize: function(){
+ if (isBody(this)) return this.getWindow().getSize();
+ return {x: this.offsetWidth, y: this.offsetHeight};
+ },
+
+ getScrollSize: function(){
+ if (isBody(this)) return this.getWindow().getScrollSize();
+ return {x: this.scrollWidth, y: this.scrollHeight};
+ },
+
+ getScroll: function(){
+ if (isBody(this)) return this.getWindow().getScroll();
+ return {x: this.scrollLeft, y: this.scrollTop};
+ },
+
+ getScrolls: function(){
+ var element = this.parentNode, position = {x: 0, y: 0};
+ while (element && !isBody(element)){
+ position.x += element.scrollLeft;
+ position.y += element.scrollTop;
+ element = element.parentNode;
+ }
+ return position;
+ },
+
+ getOffsetParent: brokenOffsetParent ? function(){
+ var element = this;
+ if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+ var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
+ while ((element = element.parentNode)){
+ if (isOffsetCheck(element)) return element;
+ }
+ return null;
+ } : function(){
+ var element = this;
+ if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+ try {
+ return element.offsetParent;
+ } catch(e) {}
+ return null;
+ },
+
+ getOffsets: function(){
+ var hasGetBoundingClientRect = this.getBoundingClientRect;
+
+ if (hasGetBoundingClientRect){
+ var bound = this.getBoundingClientRect(),
+ html = document.id(this.getDocument().documentElement),
+ htmlScroll = html.getScroll(),
+ elemScrolls = this.getScrolls(),
+ isFixed = (styleString(this, 'position') == 'fixed');
+
+ return {
+ x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
+ y: bound.top.toInt() + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
+ };
+ }
+
+ var element = this, position = {x: 0, y: 0};
+ if (isBody(this)) return position;
+
+ while (element && !isBody(element)){
+ position.x += element.offsetLeft;
+ position.y += element.offsetTop;
+
+ element = element.offsetParent;
+ }
+
+ return position;
+ },
+
+ getPosition: function(relative){
+ var offset = this.getOffsets(),
+ scroll = this.getScrolls();
+ var position = {
+ x: offset.x - scroll.x,
+ y: offset.y - scroll.y
+ };
+
+ if (relative && (relative = document.id(relative))){
+ var relativePosition = relative.getPosition();
+ return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
+ }
+ return position;
+ },
+
+ getCoordinates: function(element){
+ if (isBody(this)) return this.getWindow().getCoordinates();
+ var position = this.getPosition(element),
+ size = this.getSize();
+ var obj = {
+ left: position.x,
+ top: position.y,
+ width: size.x,
+ height: size.y
+ };
+ obj.right = obj.left + obj.width;
+ obj.bottom = obj.top + obj.height;
+ return obj;
+ },
+
+ computePosition: function(obj){
+ return {
+ left: obj.x - styleNumber(this, 'margin-left'),
+ top: obj.y - styleNumber(this, 'margin-top')
+ };
+ },
+
+ setPosition: function(obj){
+ return this.setStyles(this.computePosition(obj));
+ }
+
+});
+
+
+[Document, Window].invoke('implement', {
+
+ getSize: function(){
+ var doc = getCompatElement(this);
+ return {x: doc.clientWidth, y: doc.clientHeight};
+ },
+
+ getScroll: function(){
+ var win = this.getWindow(), doc = getCompatElement(this);
+ return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
+ },
+
+ getScrollSize: function(){
+ var doc = getCompatElement(this),
+ min = this.getSize(),
+ body = this.getDocument().body;
+
+ return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
+ },
+
+ getPosition: function(){
+ return {x: 0, y: 0};
+ },
+
+ getCoordinates: function(){
+ var size = this.getSize();
+ return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
+ }
+
+});
+
+// private methods
+
+var styleString = Element.getComputedStyle;
+
+function styleNumber(element, style){
+ return styleString(element, style).toInt() || 0;
+}
+
+function borderBox(element){
+ return styleString(element, '-moz-box-sizing') == 'border-box';
+}
+
+function topBorder(element){
+ return styleNumber(element, 'border-top-width');
+}
+
+function leftBorder(element){
+ return styleNumber(element, 'border-left-width');
+}
+
+function isBody(element){
+ return (/^(?:body|html)$/i).test(element.tagName);
+}
+
+function getCompatElement(element){
+ var doc = element.getDocument();
+ return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
+}
+
+})();
+
+//aliases
+Element.alias({position: 'setPosition'}); //compatability
+
+[Window, Document, Element].invoke('implement', {
+
+ getHeight: function(){
+ return this.getSize().y;
+ },
+
+ getWidth: function(){
+ return this.getSize().x;
+ },
+
+ getScrollTop: function(){
+ return this.getScroll().y;
+ },
+
+ getScrollLeft: function(){
+ return this.getScroll().x;
+ },
+
+ getScrollHeight: function(){
+ return this.getScrollSize().y;
+ },
+
+ getScrollWidth: function(){
+ return this.getScrollSize().x;
+ },
+
+ getTop: function(){
+ return this.getPosition().y;
+ },
+
+ getLeft: function(){
+ return this.getPosition().x;
+ }
+
+});
+
+
+/*
+---
+
+name: Fx
+
+description: Contains the basic animation logic to be extended by all other Fx Classes.
+
+license: MIT-style license.
+
+requires: [Chain, Events, Options]
+
+provides: Fx
+
+...
+*/
+
+(function(){
+
+var Fx = this.Fx = new Class({
+
+ Implements: [Chain, Events, Options],
+
+ options: {
+ /*
+ onStart: nil,
+ onCancel: nil,
+ onComplete: nil,
+ */
+ fps: 60,
+ unit: false,
+ duration: 500,
+ frames: null,
+ frameSkip: true,
+ link: 'ignore'
+ },
+
+ initialize: function(options){
+ this.subject = this.subject || this;
+ this.setOptions(options);
+ },
+
+ getTransition: function(){
+ return function(p){
+ return -(Math.cos(Math.PI * p) - 1) / 2;
+ };
+ },
+
+ step: function(now){
+ if (this.options.frameSkip){
+ var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
+ this.time = now;
+ this.frame += frames;
+ } else {
+ this.frame++;
+ }
+
+ if (this.frame < this.frames){
+ var delta = this.transition(this.frame / this.frames);
+ this.set(this.compute(this.from, this.to, delta));
+ } else {
+ this.frame = this.frames;
+ this.set(this.compute(this.from, this.to, 1));
+ this.stop();
+ }
+ },
+
+ set: function(now){
+ return now;
+ },
+
+ compute: function(from, to, delta){
+ return Fx.compute(from, to, delta);
+ },
+
+ check: function(){
+ if (!this.isRunning()) return true;
+ switch (this.options.link){
+ case 'cancel': this.cancel(); return true;
+ case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
+ }
+ return false;
+ },
+
+ start: function(from, to){
+ if (!this.check(from, to)) return this;
+ this.from = from;
+ this.to = to;
+ this.frame = (this.options.frameSkip) ? 0 : -1;
+ this.time = null;
+ this.transition = this.getTransition();
+ var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration;
+ this.duration = Fx.Durations[duration] || duration.toInt();
+ this.frameInterval = 1000 / fps;
+ this.frames = frames || Math.round(this.duration / this.frameInterval);
+ this.fireEvent('start', this.subject);
+ pushInstance.call(this, fps);
+ return this;
+ },
+
+ stop: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ if (this.frames == this.frame){
+ this.fireEvent('complete', this.subject);
+ if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
+ } else {
+ this.fireEvent('stop', this.subject);
+ }
+ }
+ return this;
+ },
+
+ cancel: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ this.frame = this.frames;
+ this.fireEvent('cancel', this.subject).clearChain();
+ }
+ return this;
+ },
+
+ pause: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ }
+ return this;
+ },
+
+ resume: function(){
+ if (this.isPaused()) pushInstance.call(this, this.options.fps);
+ return this;
+ },
+
+ isRunning: function(){
+ var list = instances[this.options.fps];
+ return list && list.contains(this);
+ },
+
+ isPaused: function(){
+ return (this.frame < this.frames) && !this.isRunning();
+ }
+
+});
+
+Fx.compute = function(from, to, delta){
+ return (to - from) * delta + from;
+};
+
+Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
+
+// global timers
+
+var instances = {}, timers = {};
+
+var loop = function(){
+ var now = Date.now();
+ for (var i = this.length; i--;){
+ var instance = this[i];
+ if (instance) instance.step(now);
+ }
+};
+
+var pushInstance = function(fps){
+ var list = instances[fps] || (instances[fps] = []);
+ list.push(this);
+ if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
+};
+
+var pullInstance = function(fps){
+ var list = instances[fps];
+ if (list){
+ list.erase(this);
+ if (!list.length && timers[fps]){
+ delete instances[fps];
+ timers[fps] = clearInterval(timers[fps]);
+ }
+ }
+};
+
+})();
+
+
+/*
+---
+
+name: Fx.CSS
+
+description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
+
+license: MIT-style license.
+
+requires: [Fx, Element.Style]
+
+provides: Fx.CSS
+
+...
+*/
+
+Fx.CSS = new Class({
+
+ Extends: Fx,
+
+ //prepares the base from/to object
+
+ prepare: function(element, property, values){
+ values = Array.from(values);
+ var from = values[0], to = values[1];
+ if (to == null){
+ to = from;
+ from = element.getStyle(property);
+ var unit = this.options.unit;
+ // adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299
+ if (unit && from && typeof from == 'string' && from.slice(-unit.length) != unit && parseFloat(from) != 0){
+ element.setStyle(property, to + unit);
+ var value = element.getComputedStyle(property);
+ // IE and Opera support pixelLeft or pixelWidth
+ if (!(/px$/.test(value))){
+ value = element.style[('pixel-' + property).camelCase()];
+ if (value == null){
+ // adapted from Dean Edwards' http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+ var left = element.style.left;
+ element.style.left = to + unit;
+ value = element.style.pixelLeft;
+ element.style.left = left;
+ }
+ }
+ from = (to || 1) / (parseFloat(value) || 1) * (parseFloat(from) || 0);
+ element.setStyle(property, from + unit);
+ }
+ }
+ return {from: this.parse(from), to: this.parse(to)};
+ },
+
+ //parses a value into an array
+
+ parse: function(value){
+ value = Function.from(value)();
+ value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
+ return value.map(function(val){
+ val = String(val);
+ var found = false;
+ Object.each(Fx.CSS.Parsers, function(parser, key){
+ if (found) return;
+ var parsed = parser.parse(val);
+ if (parsed || parsed === 0) found = {value: parsed, parser: parser};
+ });
+ found = found || {value: val, parser: Fx.CSS.Parsers.String};
+ return found;
+ });
+ },
+
+ //computes by a from and to prepared objects, using their parsers.
+
+ compute: function(from, to, delta){
+ var computed = [];
+ (Math.min(from.length, to.length)).times(function(i){
+ computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
+ });
+ computed.$family = Function.from('fx:css:value');
+ return computed;
+ },
+
+ //serves the value as settable
+
+ serve: function(value, unit){
+ if (typeOf(value) != 'fx:css:value') value = this.parse(value);
+ var returned = [];
+ value.each(function(bit){
+ returned = returned.concat(bit.parser.serve(bit.value, unit));
+ });
+ return returned;
+ },
+
+ //renders the change to an element
+
+ render: function(element, property, value, unit){
+ element.setStyle(property, this.serve(value, unit));
+ },
+
+ //searches inside the page css to find the values for a selector
+
+ search: function(selector){
+ if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
+ var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
+
+ var searchStyles = function(rules){
+ Array.each(rules, function(rule, i){
+ if (rule.media){
+ searchStyles(rule.rules || rule.cssRules);
+ return;
+ }
+ if (!rule.style) return;
+ var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
+ return m.toLowerCase();
+ }) : null;
+ if (!selectorText || !selectorTest.test(selectorText)) return;
+ Object.each(Element.Styles, function(value, style){
+ if (!rule.style[style] || Element.ShortStyles[style]) return;
+ value = String(rule.style[style]);
+ to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
+ });
+ });
+ };
+
+ Array.each(document.styleSheets, function(sheet, j){
+ var href = sheet.href;
+ if (href && href.indexOf('://') > -1 && href.indexOf(document.domain) == -1) return;
+ var rules = sheet.rules || sheet.cssRules;
+ searchStyles(rules);
+ });
+ return Fx.CSS.Cache[selector] = to;
+ }
+
+});
+
+Fx.CSS.Cache = {};
+
+Fx.CSS.Parsers = {
+
+ Color: {
+ parse: function(value){
+ if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
+ return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
+ },
+ compute: function(from, to, delta){
+ return from.map(function(value, i){
+ return Math.round(Fx.compute(from[i], to[i], delta));
+ });
+ },
+ serve: function(value){
+ return value.map(Number);
+ }
+ },
+
+ Number: {
+ parse: parseFloat,
+ compute: Fx.compute,
+ serve: function(value, unit){
+ return (unit) ? value + unit : value;
+ }
+ },
+
+ String: {
+ parse: Function.from(false),
+ compute: function(zero, one){
+ return one;
+ },
+ serve: function(zero){
+ return zero;
+ }
+ }
+
+};
+
+
+
+
+/*
+---
+
+name: Fx.Tween
+
+description: Formerly Fx.Style, effect to transition any CSS property for an element.
+
+license: MIT-style license.
+
+requires: Fx.CSS
+
+provides: [Fx.Tween, Element.fade, Element.highlight]
+
+...
+*/
+
+Fx.Tween = new Class({
+
+ Extends: Fx.CSS,
+
+ initialize: function(element, options){
+ this.element = this.subject = document.id(element);
+ this.parent(options);
+ },
+
+ set: function(property, now){
+ if (arguments.length == 1){
+ now = property;
+ property = this.property || this.options.property;
+ }
+ this.render(this.element, property, now, this.options.unit);
+ return this;
+ },
+
+ start: function(property, from, to){
+ if (!this.check(property, from, to)) return this;
+ var args = Array.flatten(arguments);
+ this.property = this.options.property || args.shift();
+ var parsed = this.prepare(this.element, this.property, args);
+ return this.parent(parsed.from, parsed.to);
+ }
+
+});
+
+Element.Properties.tween = {
+
+ set: function(options){
+ this.get('tween').cancel().setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var tween = this.retrieve('tween');
+ if (!tween){
+ tween = new Fx.Tween(this, {link: 'cancel'});
+ this.store('tween', tween);
+ }
+ return tween;
+ }
+
+};
+
+Element.implement({
+
+ tween: function(property, from, to){
+ this.get('tween').start(property, from, to);
+ return this;
+ },
+
+ fade: function(how){
+ var fade = this.get('tween'), method, args = ['opacity'].append(arguments), toggle;
+ if (args[1] == null) args[1] = 'toggle';
+ switch (args[1]){
+ case 'in': method = 'start'; args[1] = 1; break;
+ case 'out': method = 'start'; args[1] = 0; break;
+ case 'show': method = 'set'; args[1] = 1; break;
+ case 'hide': method = 'set'; args[1] = 0; break;
+ case 'toggle':
+ var flag = this.retrieve('fade:flag', this.getStyle('opacity') == 1);
+ method = 'start';
+ args[1] = flag ? 0 : 1;
+ this.store('fade:flag', !flag);
+ toggle = true;
+ break;
+ default: method = 'start';
+ }
+ if (!toggle) this.eliminate('fade:flag');
+ fade[method].apply(fade, args);
+ var to = args[args.length - 1];
+ if (method == 'set' || to != 0) this.setStyle('visibility', to == 0 ? 'hidden' : 'visible');
+ else fade.chain(function(){
+ this.element.setStyle('visibility', 'hidden');
+ this.callChain();
+ });
+ return this;
+ },
+
+ highlight: function(start, end){
+ if (!end){
+ end = this.retrieve('highlight:original', this.getStyle('background-color'));
+ end = (end == 'transparent') ? '#fff' : end;
+ }
+ var tween = this.get('tween');
+ tween.start('background-color', start || '#ffff88', end).chain(function(){
+ this.setStyle('background-color', this.retrieve('highlight:original'));
+ tween.callChain();
+ }.bind(this));
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+name: Fx.Morph
+
+description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
+
+license: MIT-style license.
+
+requires: Fx.CSS
+
+provides: Fx.Morph
+
+...
+*/
+
+Fx.Morph = new Class({
+
+ Extends: Fx.CSS,
+
+ initialize: function(element, options){
+ this.element = this.subject = document.id(element);
+ this.parent(options);
+ },
+
+ set: function(now){
+ if (typeof now == 'string') now = this.search(now);
+ for (var p in now) this.render(this.element, p, now[p], this.options.unit);
+ return this;
+ },
+
+ compute: function(from, to, delta){
+ var now = {};
+ for (var p in from) now[p] = this.parent(from[p], to[p], delta);
+ return now;
+ },
+
+ start: function(properties){
+ if (!this.check(properties)) return this;
+ if (typeof properties == 'string') properties = this.search(properties);
+ var from = {}, to = {};
+ for (var p in properties){
+ var parsed = this.prepare(this.element, p, properties[p]);
+ from[p] = parsed.from;
+ to[p] = parsed.to;
+ }
+ return this.parent(from, to);
+ }
+
+});
+
+Element.Properties.morph = {
+
+ set: function(options){
+ this.get('morph').cancel().setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var morph = this.retrieve('morph');
+ if (!morph){
+ morph = new Fx.Morph(this, {link: 'cancel'});
+ this.store('morph', morph);
+ }
+ return morph;
+ }
+
+};
+
+Element.implement({
+
+ morph: function(props){
+ this.get('morph').start(props);
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+name: Fx.Transitions
+
+description: Contains a set of advanced transitions to be used with any of the Fx Classes.
+
+license: MIT-style license.
+
+credits:
+ - Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
+
+requires: Fx
+
+provides: Fx.Transitions
+
+...
+*/
+
+Fx.implement({
+
+ getTransition: function(){
+ var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
+ if (typeof trans == 'string'){
+ var data = trans.split(':');
+ trans = Fx.Transitions;
+ trans = trans[data[0]] || trans[data[0].capitalize()];
+ if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
+ }
+ return trans;
+ }
+
+});
+
+Fx.Transition = function(transition, params){
+ params = Array.from(params);
+ var easeIn = function(pos){
+ return transition(pos, params);
+ };
+ return Object.append(easeIn, {
+ easeIn: easeIn,
+ easeOut: function(pos){
+ return 1 - transition(1 - pos, params);
+ },
+ easeInOut: function(pos){
+ return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2;
+ }
+ });
+};
+
+Fx.Transitions = {
+
+ linear: function(zero){
+ return zero;
+ }
+
+};
+
+
+
+Fx.Transitions.extend = function(transitions){
+ for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
+};
+
+Fx.Transitions.extend({
+
+ Pow: function(p, x){
+ return Math.pow(p, x && x[0] || 6);
+ },
+
+ Expo: function(p){
+ return Math.pow(2, 8 * (p - 1));
+ },
+
+ Circ: function(p){
+ return 1 - Math.sin(Math.acos(p));
+ },
+
+ Sine: function(p){
+ return 1 - Math.cos(p * Math.PI / 2);
+ },
+
+ Back: function(p, x){
+ x = x && x[0] || 1.618;
+ return Math.pow(p, 2) * ((x + 1) * p - x);
+ },
+
+ Bounce: function(p){
+ var value;
+ for (var a = 0, b = 1; 1; a += b, b /= 2){
+ if (p >= (7 - 4 * a) / 11){
+ value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
+ break;
+ }
+ }
+ return value;
+ },
+
+ Elastic: function(p, x){
+ return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3);
+ }
+
+});
+
+['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
+ Fx.Transitions[transition] = new Fx.Transition(function(p){
+ return Math.pow(p, i + 2);
+ });
+});
+
+
+/*
+---
+
+name: Request
+
+description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
+
+license: MIT-style license.
+
+requires: [Object, Element, Chain, Events, Options, Browser]
+
+provides: Request
+
+...
+*/
+
+(function(){
+
+var empty = function(){},
+ progressSupport = ('onprogress' in new Browser.Request);
+
+var Request = this.Request = new Class({
+
+ Implements: [Chain, Events, Options],
+
+ options: {/*
+ onRequest: function(){},
+ onLoadstart: function(event, xhr){},
+ onProgress: function(event, xhr){},
+ onComplete: function(){},
+ onCancel: function(){},
+ onSuccess: function(responseText, responseXML){},
+ onFailure: function(xhr){},
+ onException: function(headerName, value){},
+ onTimeout: function(){},
+ user: '',
+ password: '',*/
+ url: '',
+ data: '',
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ },
+ async: true,
+ format: false,
+ method: 'post',
+ link: 'ignore',
+ isSuccess: null,
+ emulation: true,
+ urlEncoded: true,
+ encoding: 'utf-8',
+ evalScripts: false,
+ evalResponse: false,
+ timeout: 0,
+ noCache: false
+ },
+
+ initialize: function(options){
+ this.xhr = new Browser.Request();
+ this.setOptions(options);
+ this.headers = this.options.headers;
+ },
+
+ onStateChange: function(){
+ var xhr = this.xhr;
+ if (xhr.readyState != 4 || !this.running) return;
+ this.running = false;
+ this.status = 0;
+ Function.attempt(function(){
+ var status = xhr.status;
+ this.status = (status == 1223) ? 204 : status;
+ }.bind(this));
+ xhr.onreadystatechange = empty;
+ if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
+ clearTimeout(this.timer);
+
+ this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
+ if (this.options.isSuccess.call(this, this.status))
+ this.success(this.response.text, this.response.xml);
+ else
+ this.failure();
+ },
+
+ isSuccess: function(){
+ var status = this.status;
+ return (status >= 200 && status < 300);
+ },
+
+ isRunning: function(){
+ return !!this.running;
+ },
+
+ processScripts: function(text){
+ if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text);
+ return text.stripScripts(this.options.evalScripts);
+ },
+
+ success: function(text, xml){
+ this.onSuccess(this.processScripts(text), xml);
+ },
+
+ onSuccess: function(){
+ this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
+ },
+
+ failure: function(){
+ this.onFailure();
+ },
+
+ onFailure: function(){
+ this.fireEvent('complete').fireEvent('failure', this.xhr);
+ },
+
+ loadstart: function(event){
+ this.fireEvent('loadstart', [event, this.xhr]);
+ },
+
+ progress: function(event){
+ this.fireEvent('progress', [event, this.xhr]);
+ },
+
+ timeout: function(){
+ this.fireEvent('timeout', this.xhr);
+ },
+
+ setHeader: function(name, value){
+ this.headers[name] = value;
+ return this;
+ },
+
+ getHeader: function(name){
+ return Function.attempt(function(){
+ return this.xhr.getResponseHeader(name);
+ }.bind(this));
+ },
+
+ check: function(){
+ if (!this.running) return true;
+ switch (this.options.link){
+ case 'cancel': this.cancel(); return true;
+ case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
+ }
+ return false;
+ },
+
+ send: function(options){
+ if (!this.check(options)) return this;
+
+ this.options.isSuccess = this.options.isSuccess || this.isSuccess;
+ this.running = true;
+
+ var type = typeOf(options);
+ if (type == 'string' || type == 'element') options = {data: options};
+
+ var old = this.options;
+ options = Object.append({data: old.data, url: old.url, method: old.method}, options);
+ var data = options.data, url = String(options.url), method = options.method.toLowerCase();
+
+ switch (typeOf(data)){
+ case 'element': data = document.id(data).toQueryString(); break;
+ case 'object': case 'hash': data = Object.toQueryString(data);
+ }
+
+ if (this.options.format){
+ var format = 'format=' + this.options.format;
+ data = (data) ? format + '&' + data : format;
+ }
+
+ if (this.options.emulation && !['get', 'post'].contains(method)){
+ var _method = '_method=' + method;
+ data = (data) ? _method + '&' + data : _method;
+ method = 'post';
+ }
+
+ if (this.options.urlEncoded && ['post', 'put'].contains(method)){
+ var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
+ this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
+ }
+
+ if (!url) url = document.location.pathname;
+
+ var trimPosition = url.lastIndexOf('/');
+ if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
+
+ if (this.options.noCache)
+ url += (url.indexOf('?') > -1 ? '&' : '?') + String.uniqueID();
+
+ if (data && (method == 'get' || method == 'delete')){
+ url += (url.indexOf('?') > -1 ? '&' : '?') + data;
+ data = null;
+ }
+
+ var xhr = this.xhr;
+ if (progressSupport){
+ xhr.onloadstart = this.loadstart.bind(this);
+ xhr.onprogress = this.progress.bind(this);
+ }
+
+ xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
+ if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true;
+
+ xhr.onreadystatechange = this.onStateChange.bind(this);
+
+ Object.each(this.headers, function(value, key){
+ try {
+ xhr.setRequestHeader(key, value);
+ } catch (e){
+ this.fireEvent('exception', [key, value]);
+ }
+ }, this);
+
+ this.fireEvent('request');
+ xhr.send(data);
+ if (!this.options.async) this.onStateChange();
+ else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
+ return this;
+ },
+
+ cancel: function(){
+ if (!this.running) return this;
+ this.running = false;
+ var xhr = this.xhr;
+ xhr.abort();
+ clearTimeout(this.timer);
+ xhr.onreadystatechange = empty;
+ if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
+ this.xhr = new Browser.Request();
+ this.fireEvent('cancel');
+ return this;
+ }
+
+});
+
+var methods = {};
+['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
+ methods[method] = function(data){
+ var object = {
+ method: method
+ };
+ if (data != null) object.data = data;
+ return this.send(object);
+ };
+});
+
+Request.implement(methods);
+
+Element.Properties.send = {
+
+ set: function(options){
+ var send = this.get('send').cancel();
+ send.setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var send = this.retrieve('send');
+ if (!send){
+ send = new Request({
+ data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
+ });
+ this.store('send', send);
+ }
+ return send;
+ }
+
+};
+
+Element.implement({
+
+ send: function(url){
+ var sender = this.get('send');
+ sender.send({data: this, url: url || sender.options.url});
+ return this;
+ }
+
+});
+
+})();
+
+
+/*
+---
+
+name: Request.HTML
+
+description: Extends the basic Request Class with additional methods for interacting with HTML responses.
+
+license: MIT-style license.
+
+requires: [Element, Request]
+
+provides: Request.HTML
+
+...
+*/
+
+Request.HTML = new Class({
+
+ Extends: Request,
+
+ options: {
+ update: false,
+ append: false,
+ evalScripts: true,
+ filter: false,
+ headers: {
+ Accept: 'text/html, application/xml, text/xml, */*'
+ }
+ },
+
+ success: function(text){
+ var options = this.options, response = this.response;
+
+ response.html = text.stripScripts(function(script){
+ response.javascript = script;
+ });
+
+ var match = response.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
+ if (match) response.html = match[1];
+ var temp = new Element('div').set('html', response.html);
+
+ response.tree = temp.childNodes;
+ response.elements = temp.getElements(options.filter || '*');
+
+ if (options.filter) response.tree = response.elements;
+ if (options.update){
+ var update = document.id(options.update).empty();
+ if (options.filter) update.adopt(response.elements);
+ else update.set('html', response.html);
+ } else if (options.append){
+ var append = document.id(options.append);
+ if (options.filter) response.elements.reverse().inject(append);
+ else append.adopt(temp.getChildren());
+ }
+ if (options.evalScripts) Browser.exec(response.javascript);
+
+ this.onSuccess(response.tree, response.elements, response.html, response.javascript);
+ }
+
+});
+
+Element.Properties.load = {
+
+ set: function(options){
+ var load = this.get('load').cancel();
+ load.setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var load = this.retrieve('load');
+ if (!load){
+ load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'});
+ this.store('load', load);
+ }
+ return load;
+ }
+
+};
+
+Element.implement({
+
+ load: function(){
+ this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString}));
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+name: JSON
+
+description: JSON encoder and decoder.
+
+license: MIT-style license.
+
+SeeAlso: <http://www.json.org/>
+
+requires: [Array, String, Number, Function]
+
+provides: JSON
+
+...
+*/
+
+if (typeof JSON == 'undefined') this.JSON = {};
+
+
+
+(function(){
+
+var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
+
+var escape = function(chr){
+ return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
+};
+
+JSON.validate = function(string){
+ string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+ replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+
+ return (/^[\],:{}\s]*$/).test(string);
+};
+
+JSON.encode = JSON.stringify ? function(obj){
+ return JSON.stringify(obj);
+} : function(obj){
+ if (obj && obj.toJSON) obj = obj.toJSON();
+
+ switch (typeOf(obj)){
+ case 'string':
+ return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
+ case 'array':
+ return '[' + obj.map(JSON.encode).clean() + ']';
+ case 'object': case 'hash':
+ var string = [];
+ Object.each(obj, function(value, key){
+ var json = JSON.encode(value);
+ if (json) string.push(JSON.encode(key) + ':' + json);
+ });
+ return '{' + string + '}';
+ case 'number': case 'boolean': return '' + obj;
+ case 'null': return 'null';
+ }
+
+ return null;
+};
+
+JSON.secure = true;
+
+
+JSON.decode = function(string, secure){
+ if (!string || typeOf(string) != 'string') return null;
+
+ if (secure == null) secure = JSON.secure;
+ if (secure){
+ if (JSON.parse) return JSON.parse(string);
+ if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
+ }
+
+ return eval('(' + string + ')');
+};
+
+})();
+
+
+/*
+---
+
+name: Request.JSON
+
+description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
+
+license: MIT-style license.
+
+requires: [Request, JSON]
+
+provides: Request.JSON
+
+...
+*/
+
+Request.JSON = new Class({
+
+ Extends: Request,
+
+ options: {
+ /*onError: function(text, error){},*/
+ secure: true
+ },
+
+ initialize: function(options){
+ this.parent(options);
+ Object.append(this.headers, {
+ 'Accept': 'application/json',
+ 'X-Request': 'JSON'
+ });
+ },
+
+ success: function(text){
+ var json;
+ try {
+ json = this.response.json = JSON.decode(text, this.options.secure);
+ } catch (error){
+ this.fireEvent('error', [text, error]);
+ return;
+ }
+ if (json == null) this.onFailure();
+ else this.onSuccess(json, text);
+ }
+
+});
+
+
+/*
+---
+
+name: Cookie
+
+description: Class for creating, reading, and deleting browser Cookies.
+
+license: MIT-style license.
+
+credits:
+ - Based on the functions by Peter-Paul Koch (http://quirksmode.org).
+
+requires: [Options, Browser]
+
+provides: Cookie
+
+...
+*/
+
+var Cookie = new Class({
+
+ Implements: Options,
+
+ options: {
+ path: '/',
+ domain: false,
+ duration: false,
+ secure: false,
+ document: document,
+ encode: true
+ },
+
+ initialize: function(key, options){
+ this.key = key;
+ this.setOptions(options);
+ },
+
+ write: function(value){
+ if (this.options.encode) value = encodeURIComponent(value);
+ if (this.options.domain) value += '; domain=' + this.options.domain;
+ if (this.options.path) value += '; path=' + this.options.path;
+ if (this.options.duration){
+ var date = new Date();
+ date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
+ value += '; expires=' + date.toGMTString();
+ }
+ if (this.options.secure) value += '; secure';
+ this.options.document.cookie = this.key + '=' + value;
+ return this;
+ },
+
+ read: function(){
+ var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
+ return (value) ? decodeURIComponent(value[1]) : null;
+ },
+
+ dispose: function(){
+ new Cookie(this.key, Object.merge({}, this.options, {duration: -1})).write('');
+ return this;
+ }
+
+});
+
+Cookie.write = function(key, value, options){
+ return new Cookie(key, options).write(value);
+};
+
+Cookie.read = function(key){
+ return new Cookie(key).read();
+};
+
+Cookie.dispose = function(key, options){
+ return new Cookie(key, options).dispose();
+};
+
+
+/*
+---
+
+name: DOMReady
+
+description: Contains the custom event domready.
+
+license: MIT-style license.
+
+requires: [Browser, Element, Element.Event]
+
+provides: [DOMReady, DomReady]
+
+...
+*/
+
+(function(window, document){
+
+var ready,
+ loaded,
+ checks = [],
+ shouldPoll,
+ timer,
+ testElement = document.createElement('div');
+
+var domready = function(){
+ clearTimeout(timer);
+ if (ready) return;
+ Browser.loaded = ready = true;
+ document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
+
+ document.fireEvent('domready');
+ window.fireEvent('domready');
+};
+
+var check = function(){
+ for (var i = checks.length; i--;) if (checks[i]()){
+ domready();
+ return true;
+ }
+ return false;
+};
+
+var poll = function(){
+ clearTimeout(timer);
+ if (!check()) timer = setTimeout(poll, 10);
+};
+
+document.addListener('DOMContentLoaded', domready);
+
+/*<ltIE8>*/
+// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
+// testElement.doScroll() throws when the DOM is not ready, only in the top window
+var doScrollWorks = function(){
+ try {
+ testElement.doScroll();
+ return true;
+ } catch (e){}
+ return false;
+};
+// If doScroll works already, it can't be used to determine domready
+// e.g. in an iframe
+if (testElement.doScroll && !doScrollWorks()){
+ checks.push(doScrollWorks);
+ shouldPoll = true;
+}
+/*</ltIE8>*/
+
+if (document.readyState) checks.push(function(){
+ var state = document.readyState;
+ return (state == 'loaded' || state == 'complete');
+});
+
+if ('onreadystatechange' in document) document.addListener('readystatechange', check);
+else shouldPoll = true;
+
+if (shouldPoll) poll();
+
+Element.Events.domready = {
+ onAdd: function(fn){
+ if (ready) fn.call(this);
+ }
+};
+
+// Make sure that domready fires before load
+Element.Events.load = {
+ base: 'load',
+ onAdd: function(fn){
+ if (loaded && this == window) fn.call(this);
+ },
+ condition: function(){
+ if (this == window){
+ domready();
+ delete Element.Events.load;
+ }
+ return true;
+ }
+};
+
+// This is based on the custom load event
+window.addEvent('load', function(){
+ loaded = true;
+});
+
+})(window, document);
+
diff --git a/module/web/media/js/mootools-core-1.4.1.js b/module/webui/themes/Dark/js/static/mootools-core.min.js
index 835b4bbe2..354f94196 100644
--- a/module/web/media/js/mootools-core-1.4.1.js
+++ b/module/webui/themes/Dark/js/static/mootools-core.min.js
@@ -3,10 +3,10 @@
MooTools: the javascript framework
web build:
- - http://mootools.net/core/76bf47062d6c1983d66ce47ad66aa0e0
+ - http://mootools.net/core/8423c12ffd6a6bfcde9ea22554aec795
packager build:
- - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff
+ - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady
copyrights:
- [MooTools](http://mootools.net)
@@ -15,24 +15,26 @@ licenses:
- [MIT License](http://mootools.net/license.txt)
...
*/
-(function(){this.MooTools={version:"1.4.1",build:"d1fb25710e3c5482a219ab9dc675a4e0ad2176b6"};var o=this.typeOf=function(i){if(i==null){return"null";}if(i.$family){return i.$family();
-}if(i.nodeName){if(i.nodeType==1){return"element";}if(i.nodeType==3){return(/\S/).test(i.nodeValue)?"textnode":"whitespace";}}else{if(typeof i.length=="number"){if(i.callee){return"arguments";
+
+(function(){this.MooTools={version:"1.5.0",build:"0f7b690afee9349b15909f33016a25d2e4d9f4e3"};var o=this.typeOf=function(i){if(i==null){return"null";}if(i.$family!=null){return i.$family();
+}if(i.nodeName){if(i.nodeType==1){return"element";}if(i.nodeType==3){return(/\S/).test(i.nodeValue)?"textnode":"whitespace";}}else{if(typeof i.length=="number"){if("callee" in i){return"arguments";
}if("item" in i){return"collection";}}}return typeof i;};var j=this.instanceOf=function(t,i){if(t==null){return false;}var s=t.$constructor||t.constructor;
-while(s){if(s===i){return true;}s=s.parent;}return t instanceof i;};var f=this.Function;var p=true;for(var k in {toString:1}){p=null;}if(p){p=["hasOwnProperty","valueOf","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","constructor"];
-}f.prototype.overloadSetter=function(s){var i=this;return function(u,t){if(u==null){return this;}if(s||typeof u!="string"){for(var v in u){i.call(this,v,u[v]);
-}if(p){for(var w=p.length;w--;){v=p[w];if(u.hasOwnProperty(v)){i.call(this,v,u[v]);}}}}else{i.call(this,u,t);}return this;};};f.prototype.overloadGetter=function(s){var i=this;
-return function(u){var v,t;if(s||typeof u!="string"){v=u;}else{if(arguments.length>1){v=arguments;}}if(v){t={};for(var w=0;w<v.length;w++){t[v[w]]=i.call(this,v[w]);
-}}else{t=i.call(this,u);}return t;};};f.prototype.extend=function(i,s){this[i]=s;}.overloadSetter();f.prototype.implement=function(i,s){this.prototype[i]=s;
-}.overloadSetter();var n=Array.prototype.slice;f.from=function(i){return(o(i)=="function")?i:function(){return i;};};Array.from=function(i){if(i==null){return[];
-}return(a.isEnumerable(i)&&typeof i!="string")?(o(i)=="array")?i:n.call(i):[i];};Number.from=function(s){var i=parseFloat(s);return isFinite(i)?i:null;
-};String.from=function(i){return i+"";};f.implement({hide:function(){this.$hidden=true;return this;},protect:function(){this.$protected=true;return this;
-}});var a=this.Type=function(u,t){if(u){var s=u.toLowerCase();var i=function(v){return(o(v)==s);};a["is"+u]=i;if(t!=null){t.prototype.$family=(function(){return s;
+while(s){if(s===i){return true;}s=s.parent;}if(!t.hasOwnProperty){return false;}return t instanceof i;};var f=this.Function;var p=true;for(var k in {toString:1}){p=null;
+}if(p){p=["hasOwnProperty","valueOf","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","constructor"];}f.prototype.overloadSetter=function(s){var i=this;
+return function(u,t){if(u==null){return this;}if(s||typeof u!="string"){for(var v in u){i.call(this,v,u[v]);}if(p){for(var w=p.length;w--;){v=p[w];if(u.hasOwnProperty(v)){i.call(this,v,u[v]);
+}}}}else{i.call(this,u,t);}return this;};};f.prototype.overloadGetter=function(s){var i=this;return function(u){var v,t;if(typeof u!="string"){v=u;}else{if(arguments.length>1){v=arguments;
+}else{if(s){v=[u];}}}if(v){t={};for(var w=0;w<v.length;w++){t[v[w]]=i.call(this,v[w]);}}else{t=i.call(this,u);}return t;};};f.prototype.extend=function(i,s){this[i]=s;
+}.overloadSetter();f.prototype.implement=function(i,s){this.prototype[i]=s;}.overloadSetter();var n=Array.prototype.slice;f.from=function(i){return(o(i)=="function")?i:function(){return i;
+};};Array.from=function(i){if(i==null){return[];}return(a.isEnumerable(i)&&typeof i!="string")?(o(i)=="array")?i:n.call(i):[i];};Number.from=function(s){var i=parseFloat(s);
+return isFinite(i)?i:null;};String.from=function(i){return i+"";};f.implement({hide:function(){this.$hidden=true;return this;},protect:function(){this.$protected=true;
+return this;}});var a=this.Type=function(u,t){if(u){var s=u.toLowerCase();var i=function(v){return(o(v)==s);};a["is"+u]=i;if(t!=null){t.prototype.$family=(function(){return s;
}).hide();}}if(t==null){return null;}t.extend(this);t.$constructor=a;t.prototype.$constructor=t;return t;};var e=Object.prototype.toString;a.isEnumerable=function(i){return(i!=null&&typeof i.length=="number"&&e.call(i)!="[object Function]");
};var q={};var r=function(i){var s=o(i.prototype);return q[s]||(q[s]=[]);};var b=function(t,x){if(x&&x.$hidden){return;}var s=r(this);for(var u=0;u<s.length;
u++){var w=s[u];if(o(w)=="type"){b.call(w,t,x);}else{w.call(this,t,x);}}var v=this.prototype[t];if(v==null||!v.$protected){this.prototype[t]=x;}if(this[t]==null&&o(x)=="function"){m.call(this,t,function(i){return x.apply(i,n.call(arguments,1));
});}};var m=function(i,t){if(t&&t.$hidden){return;}var s=this[i];if(s==null||!s.$protected){this[i]=t;}};a.implement({implement:b.overloadSetter(),extend:m.overloadSetter(),alias:function(i,s){b.call(this,i,this.prototype[s]);
-}.overloadSetter(),mirror:function(i){r(this).push(i);return this;}});new a("Type",a);var d=function(s,w,u){var t=(w!=Object),A=w.prototype;if(t){w=new a(s,w);
-}for(var x=0,v=u.length;x<v;x++){var B=u[x],z=w[B],y=A[B];if(z){z.protect();}if(t&&y){delete A[B];A[B]=y.protect();}}if(t){w.implement(A);}return d;};d("String",String,["charAt","charCodeAt","concat","indexOf","lastIndexOf","match","quote","replace","search","slice","split","substr","substring","trim","toLowerCase","toUpperCase"])("Array",Array,["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice","indexOf","lastIndexOf","filter","forEach","every","map","some","reduce","reduceRight"])("Number",Number,["toExponential","toFixed","toLocaleString","toPrecision"])("Function",f,["apply","call","bind"])("RegExp",RegExp,["exec","test"])("Object",Object,["create","defineProperty","defineProperties","keys","getPrototypeOf","getOwnPropertyDescriptor","getOwnPropertyNames","preventExtensions","isExtensible","seal","isSealed","freeze","isFrozen"])("Date",Date,["now"]);
+}.overloadSetter(),mirror:function(i){r(this).push(i);return this;}});new a("Type",a);var d=function(s,x,v){var u=(x!=Object),B=x.prototype;if(u){x=new a(s,x);
+}for(var y=0,w=v.length;y<w;y++){var C=v[y],A=x[C],z=B[C];if(A){A.protect();}if(u&&z){x.implement(C,z.protect());}}if(u){var t=B.propertyIsEnumerable(v[0]);
+x.forEachMethod=function(G){if(!t){for(var F=0,D=v.length;F<D;F++){G.call(B,B[v[F]],v[F]);}}for(var E in B){G.call(B,B[E],E);}};}return d;};d("String",String,["charAt","charCodeAt","concat","contains","indexOf","lastIndexOf","match","quote","replace","search","slice","split","substr","substring","trim","toLowerCase","toUpperCase"])("Array",Array,["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice","indexOf","lastIndexOf","filter","forEach","every","map","some","reduce","reduceRight"])("Number",Number,["toExponential","toFixed","toLocaleString","toPrecision"])("Function",f,["apply","call","bind"])("RegExp",RegExp,["exec","test"])("Object",Object,["create","defineProperty","defineProperties","keys","getPrototypeOf","getOwnPropertyDescriptor","getOwnPropertyNames","preventExtensions","isExtensible","seal","isSealed","freeze","isFrozen"])("Date",Date,["now"]);
Object.extend=m.overloadSetter();Date.extend("now",function(){return +(new Date);});new a("Boolean",Boolean);Number.prototype.$family=function(){return isFinite(this)?"number":"null";
}.hide();Number.extend("random",function(s,i){return Math.floor(Math.random()*(i-s+1)+s);});var g=Object.prototype.hasOwnProperty;Object.extend("forEach",function(i,t,u){for(var s in i){if(g.call(i,s)){t.call(u,i[s],s,i);
}}});Object.each=Object.forEach;Array.implement({forEach:function(u,v){for(var t=0,s=this.length;t<s;t++){if(t in this){u.call(v,this[t],t,this);}}},each:function(i,s){Array.forEach(this,i,s);
@@ -42,21 +44,21 @@ while(s--){t[s]=l(this[s]);}return t;});var h=function(s,i,t){switch(o(t)){case"
y<s;y++){var w=arguments[y];for(var x in w){h(z,x,w[x]);}}return z;},clone:function(i){var t={};for(var s in i){t[s]=l(i[s]);}return t;},append:function(w){for(var v=1,t=arguments.length;
v<t;v++){var s=arguments[v]||{};for(var u in s){w[u]=s[u];}}return w;}});["Object","WhiteSpace","TextNode","Collection","Arguments"].each(function(i){new a(i);
});var c=Date.now();String.extend("uniqueID",function(){return(c++).toString(36);});})();Array.implement({every:function(c,d){for(var b=0,a=this.length>>>0;
-b<a;b++){if((b in this)&&!c.call(d,this[b],b,this)){return false;}}return true;},filter:function(d,e){var c=[];for(var b=0,a=this.length>>>0;b<a;b++){if((b in this)&&d.call(e,this[b],b,this)){c.push(this[b]);
-}}return c;},indexOf:function(c,d){var b=this.length>>>0;for(var a=(d<0)?Math.max(0,b+d):d||0;a<b;a++){if(this[a]===c){return a;}}return -1;},map:function(c,e){var d=this.length>>>0,b=Array(d);
-for(var a=0;a<d;a++){if(a in this){b[a]=c.call(e,this[a],a,this);}}return b;},some:function(c,d){for(var b=0,a=this.length>>>0;b<a;b++){if((b in this)&&c.call(d,this[b],b,this)){return true;
-}}return false;},clean:function(){return this.filter(function(a){return a!=null;});},invoke:function(a){var b=Array.slice(arguments,1);return this.map(function(c){return c[a].apply(c,b);
-});},associate:function(c){var d={},b=Math.min(this.length,c.length);for(var a=0;a<b;a++){d[c[a]]=this[a];}return d;},link:function(c){var a={};for(var e=0,b=this.length;
-e<b;e++){for(var d in c){if(c[d](this[e])){a[d]=this[e];delete c[d];break;}}}return a;},contains:function(a,b){return this.indexOf(a,b)!=-1;},append:function(a){this.push.apply(this,a);
-return this;},getLast:function(){return(this.length)?this[this.length-1]:null;},getRandom:function(){return(this.length)?this[Number.random(0,this.length-1)]:null;
+b<a;b++){if((b in this)&&!c.call(d,this[b],b,this)){return false;}}return true;},filter:function(d,f){var c=[];for(var e,b=0,a=this.length>>>0;b<a;b++){if(b in this){e=this[b];
+if(d.call(f,e,b,this)){c.push(e);}}}return c;},indexOf:function(c,d){var b=this.length>>>0;for(var a=(d<0)?Math.max(0,b+d):d||0;a<b;a++){if(this[a]===c){return a;
+}}return -1;},map:function(c,e){var d=this.length>>>0,b=Array(d);for(var a=0;a<d;a++){if(a in this){b[a]=c.call(e,this[a],a,this);}}return b;},some:function(c,d){for(var b=0,a=this.length>>>0;
+b<a;b++){if((b in this)&&c.call(d,this[b],b,this)){return true;}}return false;},clean:function(){return this.filter(function(a){return a!=null;});},invoke:function(a){var b=Array.slice(arguments,1);
+return this.map(function(c){return c[a].apply(c,b);});},associate:function(c){var d={},b=Math.min(this.length,c.length);for(var a=0;a<b;a++){d[c[a]]=this[a];
+}return d;},link:function(c){var a={};for(var e=0,b=this.length;e<b;e++){for(var d in c){if(c[d](this[e])){a[d]=this[e];delete c[d];break;}}}return a;},contains:function(a,b){return this.indexOf(a,b)!=-1;
+},append:function(a){this.push.apply(this,a);return this;},getLast:function(){return(this.length)?this[this.length-1]:null;},getRandom:function(){return(this.length)?this[Number.random(0,this.length-1)]:null;
},include:function(a){if(!this.contains(a)){this.push(a);}return this;},combine:function(c){for(var b=0,a=c.length;b<a;b++){this.include(c[b]);}return this;
},erase:function(b){for(var a=this.length;a--;){if(this[a]===b){this.splice(a,1);}}return this;},empty:function(){this.length=0;return this;},flatten:function(){var d=[];
for(var b=0,a=this.length;b<a;b++){var c=typeOf(this[b]);if(c=="null"){continue;}d=d.concat((c=="array"||c=="collection"||c=="arguments"||instanceOf(this[b],Array))?Array.flatten(this[b]):this[b]);
}return d;},pick:function(){for(var b=0,a=this.length;b<a;b++){if(this[b]!=null){return this[b];}}return null;},hexToRgb:function(b){if(this.length!=3){return null;
-}var a=this.map(function(c){if(c.length==1){c+=c;}return c.toInt(16);});return(b)?a:"rgb("+a+")";},rgbToHex:function(d){if(this.length<3){return null;}if(this.length==4&&this[3]==0&&!d){return"transparent";
-}var b=[];for(var a=0;a<3;a++){var c=(this[a]-0).toString(16);b.push((c.length==1)?"0"+c:c);}return(d)?b:"#"+b.join("");}});String.implement({test:function(a,b){return((typeOf(a)=="regexp")?a:new RegExp(""+a,b)).test(this);
-},contains:function(a,b){return(b)?(b+this+b).indexOf(b+a+b)>-1:String(this).indexOf(a)>-1;},trim:function(){return String(this).replace(/^\s+|\s+$/g,"");
-},clean:function(){return String(this).replace(/\s+/g," ").trim();},camelCase:function(){return String(this).replace(/-\D/g,function(a){return a.charAt(1).toUpperCase();
+}var a=this.map(function(c){if(c.length==1){c+=c;}return parseInt(c,16);});return(b)?a:"rgb("+a+")";},rgbToHex:function(d){if(this.length<3){return null;
+}if(this.length==4&&this[3]==0&&!d){return"transparent";}var b=[];for(var a=0;a<3;a++){var c=(this[a]-0).toString(16);b.push((c.length==1)?"0"+c:c);}return(d)?b:"#"+b.join("");
+}});String.implement({contains:function(b,a){return(a?String(this).slice(a):String(this)).indexOf(b)>-1;},test:function(a,b){return((typeOf(a)=="regexp")?a:new RegExp(""+a,b)).test(this);
+},trim:function(){return String(this).replace(/^\s+|\s+$/g,"");},clean:function(){return String(this).replace(/\s+/g," ").trim();},camelCase:function(){return String(this).replace(/-\D/g,function(a){return a.charAt(1).toUpperCase();
});},hyphenate:function(){return String(this).replace(/[A-Z]/g,function(a){return("-"+a.charAt(0).toLowerCase());});},capitalize:function(){return String(this).replace(/\b[a-z]/g,function(a){return a.toUpperCase();
});},escapeRegExp:function(){return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1");},toInt:function(a){return parseInt(this,a||10);},toFloat:function(){return parseFloat(this);
},hexToRgb:function(b){var a=String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);return(a)?a.slice(1).hexToRgb(b):null;},rgbToHex:function(b){var a=String(this).match(/\d{1,3}/g);
@@ -76,25 +78,25 @@ for(var e=0,b=g.length;e<b;e++){var c=g[e];if(c in d){f[c]=d[c];}}return f;},map
}}return d;},values:function(c){var b=[];for(var d in c){if(a.call(c,d)){b.push(c[d]);}}return b;},getLength:function(b){return Object.keys(b).length;},keyOf:function(b,d){for(var c in b){if(a.call(b,c)&&b[c]===d){return c;
}}return null;},contains:function(b,c){return Object.keyOf(b,c)!=null;},toQueryString:function(b,c){var d=[];Object.each(b,function(h,g){if(c){g=c+"["+g+"]";
}var f;switch(typeOf(h)){case"object":f=Object.toQueryString(h,g);break;case"array":var e={};h.each(function(k,j){e[j]=k;});f=Object.toQueryString(e,g);
-break;default:f=g+"="+encodeURIComponent(h);}if(h!=null){d.push(f);}});return d.join("&");}});})();(function(){var k=this.document;var i=k.window=this;
-var b=1;this.$uid=(i.ActiveXObject)?function(e){return(e.uid||(e.uid=[b++]))[0];}:function(e){return e.uid||(e.uid=b++);};$uid(i);$uid(k);var a=navigator.userAgent.toLowerCase(),c=navigator.platform.toLowerCase(),j=a.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,"unknown",0],f=j[1]=="ie"&&k.documentMode;
-var o=this.Browser={extend:Function.prototype.extend,name:(j[1]=="version")?j[3]:j[1],version:f||parseFloat((j[1]=="opera"&&j[4])?j[4]:j[2]),Platform:{name:a.match(/ip(?:ad|od|hone)/)?"ios":(a.match(/(?:webos|android)/)||c.match(/mac|win|linux/)||["other"])[0]},Features:{xpath:!!(k.evaluate),air:!!(i.runtime),query:!!(k.querySelector),json:!!(i.JSON)},Plugins:{}};
-o[o.name]=true;o[o.name+parseInt(o.version,10)]=true;o.Platform[o.Platform.name]=true;o.Request=(function(){var q=function(){return new XMLHttpRequest();
-};var p=function(){return new ActiveXObject("MSXML2.XMLHTTP");};var e=function(){return new ActiveXObject("Microsoft.XMLHTTP");};return Function.attempt(function(){q();
-return q;},function(){p();return p;},function(){e();return e;});})();o.Features.xhr=!!(o.Request);var h=(Function.attempt(function(){return navigator.plugins["Shockwave Flash"].description;
-},function(){return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version");})||"0 r0").match(/\d+/g);o.Plugins.Flash={version:Number(h[0]||"0."+h[1])||0,build:Number(h[2])||0};
-o.exec=function(p){if(!p){return p;}if(i.execScript){i.execScript(p);}else{var e=k.createElement("script");e.setAttribute("type","text/javascript");e.text=p;
-k.head.appendChild(e);k.head.removeChild(e);}return p;};String.implement("stripScripts",function(p){var e="";var q=this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi,function(r,s){e+=s+"\n";
-return"";});if(p===true){o.exec(e);}else{if(typeOf(p)=="function"){p(e,q);}}return q;});o.extend({Document:this.Document,Window:this.Window,Element:this.Element,Event:this.Event});
-this.Window=this.$constructor=new Type("Window",function(){});this.$family=Function.from("window").hide();Window.mirror(function(e,p){i[e]=p;});this.Document=k.$constructor=new Type("Document",function(){});
-k.$family=Function.from("document").hide();Document.mirror(function(e,p){k[e]=p;});k.html=k.documentElement;if(!k.head){k.head=k.getElementsByTagName("head")[0];
-}if(k.execCommand){try{k.execCommand("BackgroundImageCache",false,true);}catch(g){}}if(this.attachEvent&&!this.addEventListener){var d=function(){this.detachEvent("onunload",d);
-k.head=k.html=k.window=null;};this.attachEvent("onunload",d);}var m=Array.from;try{m(k.html.childNodes);}catch(g){Array.from=function(p){if(typeof p!="string"&&Type.isEnumerable(p)&&typeOf(p)!="array"){var e=p.length,q=new Array(e);
-while(e--){q[e]=p[e];}return q;}return m(p);};var l=Array.prototype,n=l.slice;["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice"].each(function(e){var p=l[e];
-Array[e]=function(q){return p.apply(Array.from(q),n.call(arguments,1));};});}})();(function(){var b={};var a=this.DOMEvent=new Type("DOMEvent",function(c,g){if(!g){g=window;
+break;default:f=g+"="+encodeURIComponent(h);}if(h!=null){d.push(f);}});return d.join("&");}});})();(function(){var f=this.document;var d=f.window=this;
+var a=function(k,e){k=k.toLowerCase();e=(e?e.toLowerCase():"");var l=k.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/)||[null,"unknown",0];
+if(l[1]=="trident"){l[1]="ie";if(l[4]){l[2]=l[4];}}else{if(l[1]=="crios"){l[1]="chrome";}}var e=k.match(/ip(?:ad|od|hone)/)?"ios":(k.match(/(?:webos|android)/)||e.match(/mac|win|linux/)||["other"])[0];
+if(e=="win"){e="windows";}return{extend:Function.prototype.extend,name:(l[1]=="version")?l[3]:l[1],version:parseFloat((l[1]=="opera"&&l[4])?l[4]:l[2]),platform:e};
+};var j=this.Browser=a(navigator.userAgent,navigator.platform);if(j.ie){j.version=f.documentMode;}j.extend({Features:{xpath:!!(f.evaluate),air:!!(d.runtime),query:!!(f.querySelector),json:!!(d.JSON)},parseUA:a});
+j.Request=(function(){var l=function(){return new XMLHttpRequest();};var k=function(){return new ActiveXObject("MSXML2.XMLHTTP");};var e=function(){return new ActiveXObject("Microsoft.XMLHTTP");
+};return Function.attempt(function(){l();return l;},function(){k();return k;},function(){e();return e;});})();j.Features.xhr=!!(j.Request);j.exec=function(k){if(!k){return k;
+}if(d.execScript){d.execScript(k);}else{var e=f.createElement("script");e.setAttribute("type","text/javascript");e.text=k;f.head.appendChild(e);f.head.removeChild(e);
+}return k;};String.implement("stripScripts",function(k){var e="";var l=this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi,function(m,n){e+=n+"\n";return"";
+});if(k===true){j.exec(e);}else{if(typeOf(k)=="function"){k(e,l);}}return l;});j.extend({Document:this.Document,Window:this.Window,Element:this.Element,Event:this.Event});
+this.Window=this.$constructor=new Type("Window",function(){});this.$family=Function.from("window").hide();Window.mirror(function(e,k){d[e]=k;});this.Document=f.$constructor=new Type("Document",function(){});
+f.$family=Function.from("document").hide();Document.mirror(function(e,k){f[e]=k;});f.html=f.documentElement;if(!f.head){f.head=f.getElementsByTagName("head")[0];
+}if(f.execCommand){try{f.execCommand("BackgroundImageCache",false,true);}catch(c){}}if(this.attachEvent&&!this.addEventListener){var b=function(){this.detachEvent("onunload",b);
+f.head=f.html=f.window=null;};this.attachEvent("onunload",b);}var g=Array.from;try{g(f.html.childNodes);}catch(c){Array.from=function(k){if(typeof k!="string"&&Type.isEnumerable(k)&&typeOf(k)!="array"){var e=k.length,l=new Array(e);
+while(e--){l[e]=k[e];}return l;}return g(k);};var h=Array.prototype,i=h.slice;["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice"].each(function(e){var k=h[e];
+Array[e]=function(l){return k.apply(Array.from(l),i.call(arguments,1));};});}})();(function(){var b={};var a=this.DOMEvent=new Type("DOMEvent",function(c,g){if(!g){g=window;
}c=c||g.event;if(c.$extended){return c;}this.event=c;this.$extended=true;this.shift=c.shiftKey;this.control=c.ctrlKey;this.alt=c.altKey;this.meta=c.metaKey;
var i=this.type=c.type;var h=c.target||c.srcElement;while(h&&h.nodeType==3){h=h.parentNode;}this.target=document.id(h);if(i.indexOf("key")==0){var d=this.code=(c.which||c.keyCode);
-this.key=b[d];if(i=="keydown"){if(d>111&&d<124){this.key="f"+(d-111);}else{if(d>95&&d<106){this.key=d-96;}}}if(this.key==null){this.key=String.fromCharCode(d).toLowerCase();
+this.key=b[d];if(i=="keydown"||i=="keyup"){if(d>111&&d<124){this.key="f"+(d-111);}else{if(d>95&&d<106){this.key=d-96;}}}if(this.key==null){this.key=String.fromCharCode(d).toLowerCase();
}}else{if(i=="click"||i=="dblclick"||i=="contextmenu"||i=="DOMMouseScroll"||i.indexOf("mouse")==0){var j=g.document;j=(!j.compatMode||j.compatMode=="CSS1Compat")?j.html:j.body;
this.page={x:(c.pageX!=null)?c.pageX:c.clientX+j.scrollLeft,y:(c.pageY!=null)?c.pageY:c.clientY+j.scrollTop};this.client={x:(c.pageX!=null)?c.pageX-g.pageXOffset:c.clientX,y:(c.pageY!=null)?c.pageY-g.pageYOffset:c.clientY};
if(i=="DOMMouseScroll"||i=="mousewheel"){this.wheel=(c.wheelDelta)?c.wheelDelta/120:-(c.detail||0)/3;}this.rightClick=(c.which==3||c.button==2);if(i=="mouseover"||i=="mouseout"){var k=c.relatedTarget||c[(i=="mouseover"?"from":"to")+"Element"];
@@ -138,32 +140,32 @@ case"!=":E=function(I){return u!=I;};break;default:E=function(I){return !!I;};}i
};}if(!o.attributes){o.attributes=[];}o.attributes.push({key:B,operator:A,value:u,test:E});}}}}}return"";}var d=(this.Slick||{});d.parse=function(o){return e(o);
};d.escapeRegExp=f;if(!this.Slick){this.Slick=d;}}).apply((typeof exports!="undefined")?exports:this);(function(){var k={},m={},d=Object.prototype.toString;
k.isNativeCode=function(c){return(/\{\s*\[native code\]\s*\}/).test(""+c);};k.isXML=function(c){return(!!c.xmlVersion)||(!!c.xml)||(d.call(c)=="[object XMLDocument]")||(c.nodeType==9&&c.documentElement.nodeName!="HTML");
-};k.setDocument=function(x){var u=x.nodeType;if(u==9){}else{if(u){x=x.ownerDocument;}else{if(x.navigator){x=x.document;}else{return;}}}if(this.document===x){return;
-}this.document=x;var z=x.documentElement,v=this.getUIDXML(z),p=m[v],B;if(p){for(B in p){this[B]=p[B];}return;}p=m[v]={};p.root=z;p.isXMLDocument=this.isXML(x);
-p.brokenStarGEBTN=p.starSelectsClosedQSA=p.idGetsName=p.brokenMixedCaseQSA=p.brokenGEBCN=p.brokenCheckedQSA=p.brokenEmptyAttributeQSA=p.isHTMLDocument=p.nativeMatchesSelector=false;
-var n,o,y,r,s;var t,c="slick_uniqueid";var A=x.createElement("div");var q=x.body||x.getElementsByTagName("body")[0]||z;q.appendChild(A);try{A.innerHTML='<a id="'+c+'"></a>';
-p.isHTMLDocument=!!x.getElementById(c);}catch(w){}if(p.isHTMLDocument){A.style.display="none";A.appendChild(x.createComment(""));o=(A.getElementsByTagName("*").length>1);
-try{A.innerHTML="foo</foo>";t=A.getElementsByTagName("*");n=(t&&!!t.length&&t[0].nodeName.charAt(0)=="/");}catch(w){}p.brokenStarGEBTN=o||n;try{A.innerHTML='<a name="'+c+'"></a><b id="'+c+'"></b>';
-p.idGetsName=x.getElementById(c)===A.firstChild;}catch(w){}if(A.getElementsByClassName){try{A.innerHTML='<a class="f"></a><a class="b"></a>';A.getElementsByClassName("b").length;
-A.firstChild.className="b";r=(A.getElementsByClassName("b").length!=2);}catch(w){}try{A.innerHTML='<a class="a"></a><a class="f b a"></a>';y=(A.getElementsByClassName("a").length!=2);
-}catch(w){}p.brokenGEBCN=r||y;}if(A.querySelectorAll){try{A.innerHTML="foo</foo>";t=A.querySelectorAll("*");p.starSelectsClosedQSA=(t&&!!t.length&&t[0].nodeName.charAt(0)=="/");
-}catch(w){}try{A.innerHTML='<a class="MiX"></a>';p.brokenMixedCaseQSA=!A.querySelectorAll(".MiX").length;}catch(w){}try{A.innerHTML='<select><option selected="selected">a</option></select>';
-p.brokenCheckedQSA=(A.querySelectorAll(":checked").length==0);}catch(w){}try{A.innerHTML='<a class=""></a>';p.brokenEmptyAttributeQSA=(A.querySelectorAll('[class*=""]').length!=0);
-}catch(w){}}try{A.innerHTML='<form action="s"><input id="action"/></form>';s=(A.firstChild.getAttribute("action")!="s");}catch(w){}p.nativeMatchesSelector=z.matchesSelector||z.mozMatchesSelector||z.webkitMatchesSelector;
-if(p.nativeMatchesSelector){try{p.nativeMatchesSelector.call(z,":slick");p.nativeMatchesSelector=null;}catch(w){}}}try{z.slick_expando=1;delete z.slick_expando;
-p.getUID=this.getUIDHTML;}catch(w){p.getUID=this.getUIDXML;}q.removeChild(A);A=t=q=null;p.getAttribute=(p.isHTMLDocument&&s)?function(E,C){var F=this.attributeGetters[C];
-if(F){return F.call(E);}var D=E.getAttributeNode(C);return(D)?D.nodeValue:null;}:function(D,C){var E=this.attributeGetters[C];return(E)?E.call(D):D.getAttribute(C);
-};p.hasAttribute=(z&&this.isNativeCode(z.hasAttribute))?function(D,C){return D.hasAttribute(C);}:function(D,C){D=D.getAttributeNode(C);return !!(D&&(D.specified||D.nodeValue));
-};p.contains=(z&&this.isNativeCode(z.contains))?function(C,D){return C.contains(D);}:(z&&z.compareDocumentPosition)?function(C,D){return C===D||!!(C.compareDocumentPosition(D)&16);
-}:function(C,D){if(D){do{if(D===C){return true;}}while((D=D.parentNode));}return false;};p.documentSorter=(z.compareDocumentPosition)?function(D,C){if(!D.compareDocumentPosition||!C.compareDocumentPosition){return 0;
-}return D.compareDocumentPosition(C)&4?-1:D===C?0:1;}:("sourceIndex" in z)?function(D,C){if(!D.sourceIndex||!C.sourceIndex){return 0;}return D.sourceIndex-C.sourceIndex;
-}:(x.createRange)?function(F,D){if(!F.ownerDocument||!D.ownerDocument){return 0;}var E=F.ownerDocument.createRange(),C=D.ownerDocument.createRange();E.setStart(F,0);
-E.setEnd(F,0);C.setStart(D,0);C.setEnd(D,0);return E.compareBoundaryPoints(Range.START_TO_END,C);}:null;z=null;for(B in p){this[B]=p[B];}};var f=/^([#.]?)((?:[\w-]+|\*))$/,h=/\[.+[*$^]=(?:""|'')?\]/,g={};
-k.search=function(U,z,H,s){var p=this.found=(s)?null:(H||[]);if(!U){return p;}else{if(U.navigator){U=U.document;}else{if(!U.nodeType){return p;}}}var F,O,V=this.uniques={},I=!!(H&&H.length),y=(U.nodeType==9);
-if(this.document!==(y?U:U.ownerDocument)){this.setDocument(U);}if(I){for(O=p.length;O--;){V[this.getUID(p[O])]=true;}}if(typeof z=="string"){var r=z.match(f);
-simpleSelectors:if(r){var u=r[1],v=r[2],A,E;if(!u){if(v=="*"&&this.brokenStarGEBTN){break simpleSelectors;}E=U.getElementsByTagName(v);if(s){return E[0]||null;
-}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{if(u=="#"){if(!this.isHTMLDocument||!y){break simpleSelectors;}A=U.getElementById(v);
-if(!A){return p;}if(this.idGetsName&&A.getAttributeNode("id").nodeValue!=v){break simpleSelectors;}if(s){return A||null;}if(!(I&&V[this.getUID(A)])){p.push(A);
+};k.setDocument=function(w){var p=w.nodeType;if(p==9){}else{if(p){w=w.ownerDocument;}else{if(w.navigator){w=w.document;}else{return;}}}if(this.document===w){return;
+}this.document=w;var A=w.documentElement,o=this.getUIDXML(A),s=m[o],r;if(s){for(r in s){this[r]=s[r];}return;}s=m[o]={};s.root=A;s.isXMLDocument=this.isXML(w);
+s.brokenStarGEBTN=s.starSelectsClosedQSA=s.idGetsName=s.brokenMixedCaseQSA=s.brokenGEBCN=s.brokenCheckedQSA=s.brokenEmptyAttributeQSA=s.isHTMLDocument=s.nativeMatchesSelector=false;
+var q,u,y,z,t;var x,v="slick_uniqueid";var c=w.createElement("div");var n=w.body||w.getElementsByTagName("body")[0]||A;n.appendChild(c);try{c.innerHTML='<a id="'+v+'"></a>';
+s.isHTMLDocument=!!w.getElementById(v);}catch(C){}if(s.isHTMLDocument){c.style.display="none";c.appendChild(w.createComment(""));u=(c.getElementsByTagName("*").length>1);
+try{c.innerHTML="foo</foo>";x=c.getElementsByTagName("*");q=(x&&!!x.length&&x[0].nodeName.charAt(0)=="/");}catch(C){}s.brokenStarGEBTN=u||q;try{c.innerHTML='<a name="'+v+'"></a><b id="'+v+'"></b>';
+s.idGetsName=w.getElementById(v)===c.firstChild;}catch(C){}if(c.getElementsByClassName){try{c.innerHTML='<a class="f"></a><a class="b"></a>';c.getElementsByClassName("b").length;
+c.firstChild.className="b";z=(c.getElementsByClassName("b").length!=2);}catch(C){}try{c.innerHTML='<a class="a"></a><a class="f b a"></a>';y=(c.getElementsByClassName("a").length!=2);
+}catch(C){}s.brokenGEBCN=z||y;}if(c.querySelectorAll){try{c.innerHTML="foo</foo>";x=c.querySelectorAll("*");s.starSelectsClosedQSA=(x&&!!x.length&&x[0].nodeName.charAt(0)=="/");
+}catch(C){}try{c.innerHTML='<a class="MiX"></a>';s.brokenMixedCaseQSA=!c.querySelectorAll(".MiX").length;}catch(C){}try{c.innerHTML='<select><option selected="selected">a</option></select>';
+s.brokenCheckedQSA=(c.querySelectorAll(":checked").length==0);}catch(C){}try{c.innerHTML='<a class=""></a>';s.brokenEmptyAttributeQSA=(c.querySelectorAll('[class*=""]').length!=0);
+}catch(C){}}try{c.innerHTML='<form action="s"><input id="action"/></form>';t=(c.firstChild.getAttribute("action")!="s");}catch(C){}s.nativeMatchesSelector=A.matches||A.mozMatchesSelector||A.webkitMatchesSelector;
+if(s.nativeMatchesSelector){try{s.nativeMatchesSelector.call(A,":slick");s.nativeMatchesSelector=null;}catch(C){}}}try{A.slick_expando=1;delete A.slick_expando;
+s.getUID=this.getUIDHTML;}catch(C){s.getUID=this.getUIDXML;}n.removeChild(c);c=x=n=null;s.getAttribute=(s.isHTMLDocument&&t)?function(G,E){var H=this.attributeGetters[E];
+if(H){return H.call(G);}var F=G.getAttributeNode(E);return(F)?F.nodeValue:null;}:function(F,E){var G=this.attributeGetters[E];return(G)?G.call(F):F.getAttribute(E);
+};s.hasAttribute=(A&&this.isNativeCode(A.hasAttribute))?function(F,E){return F.hasAttribute(E);}:function(F,E){F=F.getAttributeNode(E);return !!(F&&(F.specified||F.nodeValue));
+};var D=A&&this.isNativeCode(A.contains),B=w&&this.isNativeCode(w.contains);s.contains=(D&&B)?function(E,F){return E.contains(F);}:(D&&!B)?function(E,F){return E===F||((E===w)?w.documentElement:E).contains(F);
+}:(A&&A.compareDocumentPosition)?function(E,F){return E===F||!!(E.compareDocumentPosition(F)&16);}:function(E,F){if(F){do{if(F===E){return true;}}while((F=F.parentNode));
+}return false;};s.documentSorter=(A.compareDocumentPosition)?function(F,E){if(!F.compareDocumentPosition||!E.compareDocumentPosition){return 0;}return F.compareDocumentPosition(E)&4?-1:F===E?0:1;
+}:("sourceIndex" in A)?function(F,E){if(!F.sourceIndex||!E.sourceIndex){return 0;}return F.sourceIndex-E.sourceIndex;}:(w.createRange)?function(H,F){if(!H.ownerDocument||!F.ownerDocument){return 0;
+}var G=H.ownerDocument.createRange(),E=F.ownerDocument.createRange();G.setStart(H,0);G.setEnd(H,0);E.setStart(F,0);E.setEnd(F,0);return G.compareBoundaryPoints(Range.START_TO_END,E);
+}:null;A=null;for(r in s){this[r]=s[r];}};var f=/^([#.]?)((?:[\w-]+|\*))$/,h=/\[.+[*$^]=(?:""|'')?\]/,g={};k.search=function(U,z,H,s){var p=this.found=(s)?null:(H||[]);
+if(!U){return p;}else{if(U.navigator){U=U.document;}else{if(!U.nodeType){return p;}}}var F,O,V=this.uniques={},I=!!(H&&H.length),y=(U.nodeType==9);if(this.document!==(y?U:U.ownerDocument)){this.setDocument(U);
+}if(I){for(O=p.length;O--;){V[this.getUID(p[O])]=true;}}if(typeof z=="string"){var r=z.match(f);simpleSelectors:if(r){var u=r[1],v=r[2],A,E;if(!u){if(v=="*"&&this.brokenStarGEBTN){break simpleSelectors;
+}E=U.getElementsByTagName(v);if(s){return E[0]||null;}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{if(u=="#"){if(!this.isHTMLDocument||!y){break simpleSelectors;
+}A=U.getElementById(v);if(!A){return p;}if(this.idGetsName&&A.getAttributeNode("id").nodeValue!=v){break simpleSelectors;}if(s){return A||null;}if(!(I&&V[this.getUID(A)])){p.push(A);
}}else{if(u=="."){if(!this.isHTMLDocument||((!U.getElementsByClassName||this.brokenGEBCN)&&U.querySelectorAll)){break simpleSelectors;}if(U.getElementsByClassName&&!this.brokenGEBCN){E=U.getElementsByClassName(v);
if(s){return E[0]||null;}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{var T=new RegExp("(^|\\s)"+e.escapeRegExp(v)+"(\\s|$)");E=U.getElementsByTagName("*");
for(O=0;A=E[O++];){className=A.className;if(!(className&&T.test(className))){continue;}if(s){return A;}if(!(I&&V[this.getUID(A)])){p.push(A);}}}}}}if(I){this.sort(p);
@@ -191,7 +193,7 @@ k.matchNode=function(n,o){if(this.isHTMLDocument&&this.nativeMatchesSelector){tr
if(this.matchSelector(n,(this.isXMLDocument)?p.tag:p.tag.toUpperCase(),p.id,p.classes,p.attributes,p.pseudos)){return true;}s++;}}if(s==t.length){return false;
}var c=this.search(this.document,t),v;for(q=0;v=c[q++];){if(v===n){return true;}}return false;};k.matchPseudo=function(q,c,p){var n="pseudo:"+c;if(this[n]){return this[n](q,p);
}var o=this.getAttribute(q,c);return(p)?p==o:!!o;};k.matchSelector=function(o,v,c,p,q,s){if(v){var t=(this.isXMLDocument)?o.nodeName:o.nodeName.toUpperCase();
-if(v=="*"){if(t<"@"){return false;}}else{if(t!=v){return false;}}}if(c&&o.getAttribute("id")!=c){return false;}var r,n,u;if(p){for(r=p.length;r--;){u=o.getAttribute("class")||o.className;
+if(v=="*"){if(t<"@"){return false;}}else{if(t!=v){return false;}}}if(c&&o.getAttribute("id")!=c){return false;}var r,n,u;if(p){for(r=p.length;r--;){u=this.getAttribute(o,"class");
if(!(u&&p[r].regexp.test(u))){return false;}}}if(q){for(r=q.length;r--;){n=q[r];if(n.operator?!n.test(this.getAttribute(o,n.key)):!this.hasAttribute(o,n.key)){return false;
}}}if(s){for(r=s.length;r--;){n=s[r];if(!this.matchPseudo(o,n.key,n.value)){return false;}}}return true;};var j={" ":function(q,w,n,r,s,u,p){var t,v,o;
if(this.isHTMLDocument){getById:if(n){v=this.document.getElementById(n);if((!v&&q.all)||(this.idGetsName&&v&&v.getAttributeNode("id").nodeValue!=n)){o=q.all[n];
@@ -209,28 +211,29 @@ break;}}},"!^":function(p,c,r,o,n,q){p=p.lastChild;if(p){if(p.nodeType==1){this.
return !(n&&n.nodeType==1)&&!(c.innerText||c.textContent||"").length;},not:function(c,n){return !this.matchNode(c,n);},contains:function(c,n){return(c.innerText||c.textContent||"").indexOf(n)>-1;
},"first-child":function(c){while((c=c.previousSibling)){if(c.nodeType==1){return false;}}return true;},"last-child":function(c){while((c=c.nextSibling)){if(c.nodeType==1){return false;
}}return true;},"only-child":function(o){var n=o;while((n=n.previousSibling)){if(n.nodeType==1){return false;}}var c=o;while((c=c.nextSibling)){if(c.nodeType==1){return false;
-}}return true;},"nth-child":k.createNTHPseudo("firstChild","nextSibling","posNTH"),"nth-last-child":k.createNTHPseudo("lastChild","previousSibling","posNTHLast"),"nth-of-type":k.createNTHPseudo("firstChild","nextSibling","posNTHType",true),"nth-last-of-type":k.createNTHPseudo("lastChild","previousSibling","posNTHTypeLast",true),index:function(n,c){return this["pseudo:nth-child"](n,""+c+1);
+}}return true;},"nth-child":k.createNTHPseudo("firstChild","nextSibling","posNTH"),"nth-last-child":k.createNTHPseudo("lastChild","previousSibling","posNTHLast"),"nth-of-type":k.createNTHPseudo("firstChild","nextSibling","posNTHType",true),"nth-last-of-type":k.createNTHPseudo("lastChild","previousSibling","posNTHTypeLast",true),index:function(n,c){return this["pseudo:nth-child"](n,""+(c+1));
},even:function(c){return this["pseudo:nth-child"](c,"2n");},odd:function(c){return this["pseudo:nth-child"](c,"2n+1");},"first-of-type":function(c){var n=c.nodeName;
while((c=c.previousSibling)){if(c.nodeName==n){return false;}}return true;},"last-of-type":function(c){var n=c.nodeName;while((c=c.nextSibling)){if(c.nodeName==n){return false;
}}return true;},"only-of-type":function(o){var n=o,p=o.nodeName;while((n=n.previousSibling)){if(n.nodeName==p){return false;}}var c=o;while((c=c.nextSibling)){if(c.nodeName==p){return false;
}}return true;},enabled:function(c){return !c.disabled;},disabled:function(c){return c.disabled;},checked:function(c){return c.checked||c.selected;},focus:function(c){return this.isHTMLDocument&&this.document.activeElement===c&&(c.href||c.type||this.hasAttribute(c,"tabindex"));
-},root:function(c){return(c===this.root);},selected:function(c){return c.selected;}};for(var b in l){k["pseudo:"+b]=l[b];}var a=k.attributeGetters={"class":function(){return this.getAttribute("class")||this.className;
-},"for":function(){return("htmlFor" in this)?this.htmlFor:this.getAttribute("for");},href:function(){return("href" in this)?this.getAttribute("href",2):this.getAttribute("href");
-},style:function(){return(this.style)?this.style.cssText:this.getAttribute("style");},tabindex:function(){var c=this.getAttributeNode("tabindex");return(c&&c.specified)?c.nodeValue:null;
-},type:function(){return this.getAttribute("type");},maxlength:function(){var c=this.getAttributeNode("maxLength");return(c&&c.specified)?c.nodeValue:null;
-}};a.MAXLENGTH=a.maxLength=a.maxlength;var e=k.Slick=(this.Slick||{});e.version="1.1.6";e.search=function(n,o,c){return k.search(n,o,c);};e.find=function(c,n){return k.search(c,n,null,true);
-};e.contains=function(c,n){k.setDocument(c);return k.contains(c,n);};e.getAttribute=function(n,c){k.setDocument(n);return k.getAttribute(n,c);};e.hasAttribute=function(n,c){k.setDocument(n);
-return k.hasAttribute(n,c);};e.match=function(n,c){if(!(n&&c)){return false;}if(!c||c===n){return true;}k.setDocument(n);return k.matchNode(n,c);};e.defineAttributeGetter=function(c,n){k.attributeGetters[c]=n;
+},root:function(c){return(c===this.root);},selected:function(c){return c.selected;}};for(var b in l){k["pseudo:"+b]=l[b];}var a=k.attributeGetters={"for":function(){return("htmlFor" in this)?this.htmlFor:this.getAttribute("for");
+},href:function(){return("href" in this)?this.getAttribute("href",2):this.getAttribute("href");},style:function(){return(this.style)?this.style.cssText:this.getAttribute("style");
+},tabindex:function(){var c=this.getAttributeNode("tabindex");return(c&&c.specified)?c.nodeValue:null;},type:function(){return this.getAttribute("type");
+},maxlength:function(){var c=this.getAttributeNode("maxLength");return(c&&c.specified)?c.nodeValue:null;}};a.MAXLENGTH=a.maxLength=a.maxlength;var e=k.Slick=(this.Slick||{});
+e.version="1.1.7";e.search=function(n,o,c){return k.search(n,o,c);};e.find=function(c,n){return k.search(c,n,null,true);};e.contains=function(c,n){k.setDocument(c);
+return k.contains(c,n);};e.getAttribute=function(n,c){k.setDocument(n);return k.getAttribute(n,c);};e.hasAttribute=function(n,c){k.setDocument(n);return k.hasAttribute(n,c);
+};e.match=function(n,c){if(!(n&&c)){return false;}if(!c||c===n){return true;}k.setDocument(n);return k.matchNode(n,c);};e.defineAttributeGetter=function(c,n){k.attributeGetters[c]=n;
return this;};e.lookupAttributeGetter=function(c){return k.attributeGetters[c];};e.definePseudo=function(c,n){k["pseudo:"+c]=function(p,o){return n.call(p,o);
};return this;};e.lookupPseudo=function(c){var n=k["pseudo:"+c];if(n){return function(o){return n.call(this,o);};}return null;};e.override=function(n,c){k.override(n,c);
return this;};e.isXML=k.isXML;e.uidOf=function(c){return k.getUIDHTML(c);};if(!this.Slick){this.Slick=e;}}).apply((typeof exports!="undefined")?exports:this);
-var Element=function(b,g){var h=Element.Constructors[b];if(h){return h(g);}if(typeof b!="string"){return document.id(b).set(g);}if(!g){g={};}if(!(/^[\w-]+$/).test(b)){var e=Slick.parse(b).expressions[0][0];
-b=(e.tag=="*")?"div":e.tag;if(e.id&&g.id==null){g.id=e.id;}var d=e.attributes;if(d){for(var a,f=0,c=d.length;f<c;f++){a=d[f];if(g[a.key]!=null){continue;
-}if(a.value!=null&&a.operator=="="){g[a.key]=a.value;}else{if(!a.value&&!a.operator){g[a.key]=true;}}}}if(e.classList&&g["class"]==null){g["class"]=e.classList.join(" ");
-}}return document.newElement(b,g);};if(Browser.Element){Element.prototype=Browser.Element.prototype;}new Type("Element",Element).mirror(function(a){if(Array.prototype[a]){return;
-}var b={};b[a]=function(){var h=[],e=arguments,j=true;for(var g=0,d=this.length;g<d;g++){var f=this[g],c=h[g]=f[a].apply(f,e);j=(j&&typeOf(c)=="element");
-}return(j)?new Elements(h):h;};Elements.implement(b);});if(!Browser.Element){Element.parent=Object;Element.Prototype={"$family":Function.from("element").hide()};
-Element.mirror(function(a,b){Element.Prototype[a]=b;});}Element.Constructors={};var IFrame=new Type("IFrame",function(){var e=Array.link(arguments,{properties:Type.isObject,iframe:function(f){return(f!=null);
+var Element=this.Element=function(b,g){var h=Element.Constructors[b];if(h){return h(g);}if(typeof b!="string"){return document.id(b).set(g);}if(!g){g={};
+}if(!(/^[\w-]+$/).test(b)){var e=Slick.parse(b).expressions[0][0];b=(e.tag=="*")?"div":e.tag;if(e.id&&g.id==null){g.id=e.id;}var d=e.attributes;if(d){for(var a,f=0,c=d.length;
+f<c;f++){a=d[f];if(g[a.key]!=null){continue;}if(a.value!=null&&a.operator=="="){g[a.key]=a.value;}else{if(!a.value&&!a.operator){g[a.key]=true;}}}}if(e.classList&&g["class"]==null){g["class"]=e.classList.join(" ");
+}}return document.newElement(b,g);};if(Browser.Element){Element.prototype=Browser.Element.prototype;Element.prototype._fireEvent=(function(a){return function(b,c){return a.call(this,b,c);
+};})(Element.prototype.fireEvent);}new Type("Element",Element).mirror(function(a){if(Array.prototype[a]){return;}var b={};b[a]=function(){var h=[],e=arguments,j=true;
+for(var g=0,d=this.length;g<d;g++){var f=this[g],c=h[g]=f[a].apply(f,e);j=(j&&typeOf(c)=="element");}return(j)?new Elements(h):h;};Elements.implement(b);
+});if(!Browser.Element){Element.parent=Object;Element.Prototype={"$constructor":Element,"$family":Function.from("element").hide()};Element.mirror(function(a,b){Element.Prototype[a]=b;
+});}Element.Constructors={};var IFrame=new Type("IFrame",function(){var e=Array.link(arguments,{properties:Type.isObject,iframe:function(f){return(f!=null);
}});var c=e.properties||{},b;if(e.iframe){b=document.id(e.iframe);}var d=c.onload||function(){};delete c.onload;c.id=c.name=[c.id,c.name,b?(b.id||b.name):"IFrame_"+String.uniqueID()].pick();
b=new Element(b||"iframe",c);var a=function(){d.call(b.contentWindow);};if(window.frames[c.id]){a();}else{b.addListener("load",a);}return b;});var Elements=this.Elements=function(a){if(a&&a.length){var e={},d;
for(var c=0;d=a[c++];){var b=Slick.uidOf(d);if(!e[b]){e[b]=true;this.push(d);}}}};Elements.prototype={length:0};Elements.parent=Array;new Type("Elements",Elements).implement({filter:function(a,b){if(!a){return this;
@@ -238,101 +241,119 @@ for(var c=0;d=a[c++];){var b=Slick.uidOf(d);if(!e[b]){e[b]=true;this.push(d);}}}
b<a;b++){var c=document.id(arguments[b]);if(c){this[d++]=c;}}return(this.length=d);}.protect(),unshift:function(){var b=[];for(var c=0,a=arguments.length;
c<a;c++){var d=document.id(arguments[c]);if(d){b.push(d);}}return Array.prototype.unshift.apply(this,b);}.protect(),concat:function(){var b=new Elements(this);
for(var c=0,a=arguments.length;c<a;c++){var d=arguments[c];if(Type.isEnumerable(d)){b.append(d);}else{b.push(d);}}return b;}.protect(),append:function(c){for(var b=0,a=c.length;
-b<a;b++){this.push(c[b]);}return this;}.protect(),empty:function(){while(this.length){delete this[--this.length];}return this;}.protect()});(function(){var g=Array.prototype.splice,b={"0":0,"1":1,length:2};
-g.call(b,1,1);if(b[1]==1){Elements.implement("splice",function(){var h=this.length;var e=g.apply(this,arguments);while(h>=this.length){delete this[h--];
-}return e;}.protect());}Elements.implement(Array.prototype);Array.mirror(Elements);var f;try{var a=document.createElement("<input name=x>");f=(a.name=="x");
-}catch(c){}var d=function(e){return(""+e).replace(/&/g,"&amp;").replace(/"/g,"&quot;");};Document.implement({newElement:function(e,h){if(h&&h.checked!=null){h.defaultChecked=h.checked;
-}if(f&&h){e="<"+e;if(h.name){e+=' name="'+d(h.name)+'"';}if(h.type){e+=' type="'+d(h.type)+'"';}e+=">";delete h.name;delete h.type;}return this.id(this.createElement(e)).set(h);
-}});})();Document.implement({newTextNode:function(a){return this.createTextNode(a);},getDocument:function(){return this;},getWindow:function(){return this.window;
-},id:(function(){var a={string:function(d,c,b){d=Slick.find(b,"#"+d.replace(/(\W)/g,"\\$1"));return(d)?a.element(d,c):null;},element:function(b,c){$uid(b);
-if(!c&&!b.$family&&!(/^(?:object|embed)$/i).test(b.tagName)){Object.append(b,Element.Prototype);}return b;},object:function(c,d,b){if(c.toElement){return a.element(c.toElement(b),d);
-}return null;}};a.textnode=a.whitespace=a.window=a.document=function(b){return b;};return function(c,e,d){if(c&&c.$family&&c.uid){return c;}var b=typeOf(c);
-return(a[b])?a[b](c,e,d||document):null;};})()});if(window.$==null){Window.implement("$",function(a,b){return document.id(a,b,this.document);});}Window.implement({getDocument:function(){return this.document;
-},getWindow:function(){return this;}});[Document,Element].invoke("implement",{getElements:function(a){return Slick.search(this,a,new Elements);},getElement:function(a){return document.id(Slick.find(this,a));
-}});var contains={contains:function(a){return Slick.contains(this,a);}};if(!document.contains){Document.implement(contains);}if(!document.createElement("div").contains){Element.implement(contains);
-}var injectCombinator=function(d,c){if(!d){return c;}d=Object.clone(Slick.parse(d));var b=d.expressions;for(var a=b.length;a--;){b[a][0].combinator=c;}return d;
-};Object.forEach({getNext:"~",getPrevious:"!~",getParent:"!"},function(a,b){Element.implement(b,function(c){return this.getElement(injectCombinator(c,a));
-});});Object.forEach({getAllNext:"~",getAllPrevious:"!~",getSiblings:"~~",getChildren:">",getParents:"!"},function(a,b){Element.implement(b,function(c){return this.getElements(injectCombinator(c,a));
-});});Element.implement({getFirst:function(a){return document.id(Slick.search(this,injectCombinator(a,">"))[0]);},getLast:function(a){return document.id(Slick.search(this,injectCombinator(a,">")).getLast());
-},getWindow:function(){return this.ownerDocument.window;},getDocument:function(){return this.ownerDocument;},getElementById:function(a){return document.id(Slick.find(this,"#"+(""+a).replace(/(\W)/g,"\\$1")));
-},match:function(a){return !a||Slick.match(this,a);}});if(window.$$==null){Window.implement("$$",function(a){if(arguments.length==1){if(typeof a=="string"){return Slick.search(this.document,a,new Elements);
-}else{if(Type.isEnumerable(a)){return new Elements(a);}}}return new Elements(arguments);});}(function(){var b={before:function(n,m){var o=m.parentNode;
-if(o){o.insertBefore(n,m);}},after:function(n,m){var o=m.parentNode;if(o){o.insertBefore(n,m.nextSibling);}},bottom:function(n,m){m.appendChild(n);},top:function(n,m){m.insertBefore(n,m.firstChild);
-}};b.inside=b.bottom;var k={},d={};var i={};Array.forEach(["type","value","defaultValue","accessKey","cellPadding","cellSpacing","colSpan","frameBorder","readOnly","rowSpan","tabIndex","useMap"],function(m){i[m.toLowerCase()]=m;
-});Object.append(i,{html:"innerHTML",text:(function(){var m=document.createElement("div");return(m.textContent==null)?"innerText":"textContent";})()});
-Object.forEach(i,function(n,m){d[m]=function(o,p){o[n]=p;};k[m]=function(o){return o[n];};});var a=["compact","nowrap","ismap","declare","noshade","checked","disabled","readOnly","multiple","selected","noresize","defer","defaultChecked","autofocus","controls","autoplay","loop"];
-var h={};Array.forEach(a,function(m){var n=m.toLowerCase();h[n]=m;d[n]=function(o,p){o[m]=!!p;};k[n]=function(o){return !!o[m];};});Object.append(d,{"class":function(m,n){("className" in m)?m.className=n:m.setAttribute("class",n);
-},"for":function(m,n){("htmlFor" in m)?m.htmlFor=n:m.setAttribute("for",n);},style:function(m,n){(m.style)?m.style.cssText=n:m.setAttribute("style",n);
-}});Element.implement({setProperty:function(n,o){var m=n.toLowerCase();if(o==null){if(!h[m]){this.removeAttribute(n);return this;}o=false;}var p=d[m];if(p){p(this,o);
-}else{this.setAttribute(n,o);}return this;},setProperties:function(m){for(var n in m){this.setProperty(n,m[n]);}return this;},getProperty:function(o){var n=k[o.toLowerCase()];
-if(n){return n(this);}var m=Slick.getAttribute(this,o);return(!m&&!Slick.hasAttribute(this,o))?null:m;},getProperties:function(){var m=Array.from(arguments);
-return m.map(this.getProperty,this).associate(m);},removeProperty:function(m){return this.setProperty(m,null);},removeProperties:function(){Array.each(arguments,this.removeProperty,this);
-return this;},set:function(o,n){var m=Element.Properties[o];(m&&m.set)?m.set.call(this,n):this.setProperty(o,n);}.overloadSetter(),get:function(n){var m=Element.Properties[n];
-return(m&&m.get)?m.get.apply(this):this.getProperty(n);}.overloadGetter(),erase:function(n){var m=Element.Properties[n];(m&&m.erase)?m.erase.apply(this):this.removeProperty(n);
-return this;},hasClass:function(m){return this.className.clean().contains(m," ");},addClass:function(m){if(!this.hasClass(m)){this.className=(this.className+" "+m).clean();
-}return this;},removeClass:function(m){this.className=this.className.replace(new RegExp("(^|\\s)"+m+"(?:\\s|$)"),"$1");return this;},toggleClass:function(m,n){if(n==null){n=!this.hasClass(m);
-}return(n)?this.addClass(m):this.removeClass(m);},adopt:function(){var p=this,m,r=Array.flatten(arguments),q=r.length;if(q>1){p=m=document.createDocumentFragment();
-}for(var o=0;o<q;o++){var n=document.id(r[o],true);if(n){p.appendChild(n);}}if(m){this.appendChild(m);}return this;},appendText:function(n,m){return this.grab(this.getDocument().newTextNode(n),m);
-},grab:function(n,m){b[m||"bottom"](document.id(n,true),this);return this;},inject:function(n,m){b[m||"bottom"](this,document.id(n,true));return this;},replaces:function(m){m=document.id(m,true);
-m.parentNode.replaceChild(this,m);return this;},wraps:function(n,m){n=document.id(n,true);return this.replaces(n).grab(n,m);},getSelected:function(){this.selectedIndex;
-return new Elements(Array.from(this.options).filter(function(m){return m.selected;}));},toQueryString:function(){var m=[];this.getElements("input, select, textarea").each(function(o){var n=o.type;
-if(!o.name||o.disabled||n=="submit"||n=="reset"||n=="file"||n=="image"){return;}var p=(o.get("tag")=="select")?o.getSelected().map(function(q){return document.id(q).get("value");
-}):((n=="radio"||n=="checkbox")&&!o.checked)?null:o.get("value");Array.from(p).each(function(q){if(typeof q!="undefined"){m.push(encodeURIComponent(o.name)+"="+encodeURIComponent(q));
-}});});return m.join("&");}});var j={},e={};var c=function(m){return(e[m]||(e[m]={}));};var g=function(n){var m=n.uid;if(n.removeEvents){n.removeEvents();
-}if(n.clearAttributes){n.clearAttributes();}if(m!=null){delete j[m];delete e[m];}return n;};var l={input:"checked",option:"selected",textarea:"value"};
-Element.implement({destroy:function(){var m=g(this).getElementsByTagName("*");Array.each(m,g);Element.dispose(this);return null;},empty:function(){Array.from(this.childNodes).each(Element.dispose);
-return this;},dispose:function(){return(this.parentNode)?this.parentNode.removeChild(this):this;},clone:function(r,p){r=r!==false;var w=this.cloneNode(r),o=[w],q=[this],u;
-if(r){o.append(Array.from(w.getElementsByTagName("*")));q.append(Array.from(this.getElementsByTagName("*")));}for(u=o.length;u--;){var s=o[u],v=q[u];if(!p){s.removeAttribute("id");
-}if(s.clearAttributes){s.clearAttributes();s.mergeAttributes(v);s.removeAttribute("uid");if(s.options){var z=s.options,m=v.options;for(var t=z.length;t--;
-){z[t].selected=m[t].selected;}}}var n=l[v.tagName.toLowerCase()];if(n&&v[n]){s[n]=v[n];}}if(Browser.ie){var x=w.getElementsByTagName("object"),y=this.getElementsByTagName("object");
-for(u=x.length;u--;){x[u].outerHTML=y[u].outerHTML;}}return document.id(w);}});[Element,Window,Document].invoke("implement",{addListener:function(p,o){if(p=="unload"){var m=o,n=this;
-o=function(){n.removeListener("unload",o);m();};}else{j[$uid(this)]=this;}if(this.addEventListener){this.addEventListener(p,o,!!arguments[2]);}else{this.attachEvent("on"+p,o);
-}return this;},removeListener:function(n,m){if(this.removeEventListener){this.removeEventListener(n,m,!!arguments[2]);}else{this.detachEvent("on"+n,m);
-}return this;},retrieve:function(n,m){var p=c($uid(this)),o=p[n];if(m!=null&&o==null){o=p[n]=m;}return o!=null?o:null;},store:function(n,m){var o=c($uid(this));
-o[n]=m;return this;},eliminate:function(m){var n=c($uid(this));delete n[m];return this;}});if(window.attachEvent&&!window.addEventListener){window.addListener("unload",function(){Object.each(j,g);
-if(window.CollectGarbage){CollectGarbage();}});}Element.Properties={};Element.Properties.style={set:function(m){this.style.cssText=m;},get:function(){return this.style.cssText;
-},erase:function(){this.style.cssText="";}};Element.Properties.tag={get:function(){return this.tagName.toLowerCase();}};Element.Properties.html=(function(){var s=Function.attempt(function(){var u=document.createElement("table");
-u.innerHTML="<tr><td></td></tr>";});var t=document.createElement("div");var o={table:[1,"<table>","</table>"],select:[1,"<select>","</select>"],tbody:[2,"<table><tbody>","</tbody></table>"],tr:[3,"<table><tbody><tr>","</tr></tbody></table>"]};
-o.thead=o.tfoot=o.tbody;t.innerHTML="<nav></nav>";var n=t.childNodes.length==1;if(!n){var q="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video".split(" "),p=document.createDocumentFragment(),m=q.length;
-while(m--){p.createElement(q[m]);}p.appendChild(t);}var r={set:function(v){if(typeOf(v)=="array"){v=v.join("");}var w=(!s&&o[this.get("tag")]);if(!w&&!n){w=[0,"",""];
-}if(w){var x=t;x.innerHTML=w[1]+v+w[2];for(var u=w[0];u--;){x=x.firstChild;}this.empty().adopt(x.childNodes);}else{this.innerHTML=v;}}};r.erase=r.set;return r;
-})();var f=document.createElement("form");f.innerHTML="<select><option>s</option></select>";if(f.firstChild.value!="s"){Element.Properties.value={set:function(r){var n=this.get("tag");
-if(n!="select"){return this.setProperty("value",r);}var o=this.getElements("option");for(var p=0;p<o.length;p++){var q=o[p],m=q.getAttributeNode("value"),s=(m&&m.specified)?q.value:q.get("text");
-if(s==r){return q.selected=true;}}},get:function(){var o=this,n=o.get("tag");if(n!="select"&&n!="option"){return this.getProperty("value");}if(n=="select"&&!(o=o.getSelected()[0])){return"";
-}var m=o.getAttributeNode("value");return(m&&m.specified)?o.value:o.get("text");}};}})();(function(){var f=document.html;Element.Properties.styles={set:function(i){this.setStyles(i);
-}};var h=(f.style.opacity!=null),a=(f.style.filter!=null),g=/alpha\(opacity=([\d.]+)\)/i;var b=function(j,i){j.store("$opacity",i);j.style.visibility=i>0?"visible":"hidden";
-};var d=(h?function(j,i){j.style.opacity=i;}:(a?function(j,i){if(!j.currentStyle||!j.currentStyle.hasLayout){j.style.zoom=1;}i=(i*100).limit(0,100).round();
-i=(i==100)?"":"alpha(opacity="+i+")";var k=j.style.filter||j.getComputedStyle("filter")||"";j.style.filter=g.test(k)?k.replace(g,i):k+i;}:b));var e=(h?function(j){var i=j.style.opacity||j.getComputedStyle("opacity");
-return(i=="")?1:i.toFloat();}:(a?function(j){var k=(j.style.filter||j.getComputedStyle("filter")),i;if(k){i=k.match(g);}return(i==null||k==null)?1:(i[1]/100);
-}:function(j){var i=j.retrieve("$opacity");if(i==null){i=(j.style.visibility=="hidden"?0:1);}return i;}));var c=(f.style.cssFloat==null)?"styleFloat":"cssFloat";
-Element.implement({getComputedStyle:function(k){if(this.currentStyle){return this.currentStyle[k.camelCase()];}var j=Element.getDocument(this).defaultView,i=j?j.getComputedStyle(this,null):null;
-return(i)?i.getPropertyValue((k==c)?"float":k.hyphenate()):null;},setStyle:function(j,i){if(j=="opacity"){d(this,parseFloat(i));return this;}j=(j=="float"?c:j).camelCase();
-if(typeOf(i)!="string"){var k=(Element.Styles[j]||"@").split(" ");i=Array.from(i).map(function(m,l){if(!k[l]){return"";}return(typeOf(m)=="number")?k[l].replace("@",Math.round(m)):m;
-}).join(" ");}else{if(i==String(Number(i))){i=Math.round(i);}}this.style[j]=i;return this;},getStyle:function(o){if(o=="opacity"){return e(this);}o=(o=="float"?c:o).camelCase();
-var i=this.style[o];if(!i||o=="zIndex"){i=[];for(var n in Element.ShortStyles){if(o!=n){continue;}for(var m in Element.ShortStyles[n]){i.push(this.getStyle(m));
-}return i.join(" ");}i=this.getComputedStyle(o);}if(i){i=String(i);var k=i.match(/rgba?\([\d\s,]+\)/);if(k){i=i.replace(k[0],k[0].rgbToHex());}}if(Browser.opera||(Browser.ie&&isNaN(parseFloat(i)))){if((/^(height|width)$/).test(o)){var j=(o=="width")?["left","right"]:["top","bottom"],l=0;
-j.each(function(p){l+=this.getStyle("border-"+p+"-width").toInt()+this.getStyle("padding-"+p).toInt();},this);return this["offset"+o.capitalize()]-l+"px";
-}if(Browser.opera&&String(i).indexOf("px")!=-1){return i;}if((/^border(.+)Width|margin|padding/).test(o)){return"0px";}}return i;},setStyles:function(j){for(var i in j){this.setStyle(i,j[i]);
-}return this;},getStyles:function(){var i={};Array.flatten(arguments).each(function(j){i[j]=this.getStyle(j);},this);return i;}});Element.Styles={left:"@px",top:"@px",bottom:"@px",right:"@px",width:"@px",height:"@px",maxWidth:"@px",maxHeight:"@px",minWidth:"@px",minHeight:"@px",backgroundColor:"rgb(@, @, @)",backgroundPosition:"@px @px",color:"rgb(@, @, @)",fontSize:"@px",letterSpacing:"@px",lineHeight:"@px",clip:"rect(@px @px @px @px)",margin:"@px @px @px @px",padding:"@px @px @px @px",border:"@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)",borderWidth:"@px @px @px @px",borderStyle:"@ @ @ @",borderColor:"rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)",zIndex:"@",zoom:"@",fontWeight:"@",textIndent:"@px",opacity:"@"};
-Element.ShortStyles={margin:{},padding:{},border:{},borderWidth:{},borderStyle:{},borderColor:{}};["Top","Right","Bottom","Left"].each(function(o){var n=Element.ShortStyles;
-var j=Element.Styles;["margin","padding"].each(function(p){var q=p+o;n[p][q]=j[q]="@px";});var m="border"+o;n.border[m]=j[m]="@px @ rgb(@, @, @)";var l=m+"Width",i=m+"Style",k=m+"Color";
-n[m]={};n.borderWidth[l]=n[m][l]=j[l]="@px";n.borderStyle[i]=n[m][i]=j[i]="@";n.borderColor[k]=n[m][k]=j[k]="rgb(@, @, @)";});})();(function(){Element.Properties.events={set:function(b){this.addEvents(b);
-}};[Element,Window,Document].invoke("implement",{addEvent:function(f,h){var i=this.retrieve("events",{});if(!i[f]){i[f]={keys:[],values:[]};}if(i[f].keys.contains(h)){return this;
-}i[f].keys.push(h);var g=f,b=Element.Events[f],d=h,j=this;if(b){if(b.onAdd){b.onAdd.call(this,h,f);}if(b.condition){d=function(k){if(b.condition.call(this,k,f)){return h.call(this,k);
-}return true;};}if(b.base){g=Function.from(b.base).call(this,f);}}var e=function(){return h.call(j);};var c=Element.NativeEvents[g];if(c){if(c==2){e=function(k){k=new DOMEvent(k,j.getWindow());
-if(d.call(j,k)===false){k.stop();}};}this.addListener(g,e,arguments[2]);}i[f].values.push(e);return this;},removeEvent:function(e,d){var c=this.retrieve("events");
-if(!c||!c[e]){return this;}var h=c[e];var b=h.keys.indexOf(d);if(b==-1){return this;}var g=h.values[b];delete h.keys[b];delete h.values[b];var f=Element.Events[e];
-if(f){if(f.onRemove){f.onRemove.call(this,d,e);}if(f.base){e=Function.from(f.base).call(this,e);}}return(Element.NativeEvents[e])?this.removeListener(e,g,arguments[2]):this;
-},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this;},removeEvents:function(b){var d;if(typeOf(b)=="object"){for(d in b){this.removeEvent(d,b[d]);
-}return this;}var c=this.retrieve("events");if(!c){return this;}if(!b){for(d in c){this.removeEvents(d);}this.eliminate("events");}else{if(c[b]){c[b].keys.each(function(e){this.removeEvent(b,e);
-},this);delete c[b];}}return this;},fireEvent:function(e,c,b){var d=this.retrieve("events");if(!d||!d[e]){return this;}c=Array.from(c);d[e].keys.each(function(f){if(b){f.delay(b,this,c);
+b<a;b++){this.push(c[b]);}return this;}.protect(),empty:function(){while(this.length){delete this[--this.length];}return this;}.protect()});(function(){var f=Array.prototype.splice,a={"0":0,"1":1,length:2};
+f.call(a,1,1);if(a[1]==1){Elements.implement("splice",function(){var g=this.length;var e=f.apply(this,arguments);while(g>=this.length){delete this[g--];
+}return e;}.protect());}Array.forEachMethod(function(g,e){Elements.implement(e,g);});Array.mirror(Elements);var d;try{d=(document.createElement("<input name=x>").name=="x");
+}catch(b){}var c=function(e){return(""+e).replace(/&/g,"&amp;").replace(/"/g,"&quot;");};Document.implement({newElement:function(e,g){if(g&&g.checked!=null){g.defaultChecked=g.checked;
+}if(d&&g){e="<"+e;if(g.name){e+=' name="'+c(g.name)+'"';}if(g.type){e+=' type="'+c(g.type)+'"';}e+=">";delete g.name;delete g.type;}return this.id(this.createElement(e)).set(g);
+}});})();(function(){Slick.uidOf(window);Slick.uidOf(document);Document.implement({newTextNode:function(e){return this.createTextNode(e);},getDocument:function(){return this;
+},getWindow:function(){return this.window;},id:(function(){var e={string:function(L,K,l){L=Slick.find(l,"#"+L.replace(/(\W)/g,"\\$1"));return(L)?e.element(L,K):null;
+},element:function(K,L){Slick.uidOf(K);if(!L&&!K.$family&&!(/^(?:object|embed)$/i).test(K.tagName)){var l=K.fireEvent;K._fireEvent=function(M,N){return l(M,N);
+};Object.append(K,Element.Prototype);}return K;},object:function(K,L,l){if(K.toElement){return e.element(K.toElement(l),L);}return null;}};e.textnode=e.whitespace=e.window=e.document=function(l){return l;
+};return function(K,M,L){if(K&&K.$family&&K.uniqueNumber){return K;}var l=typeOf(K);return(e[l])?e[l](K,M,L||document):null;};})()});if(window.$==null){Window.implement("$",function(e,l){return document.id(e,l,this.document);
+});}Window.implement({getDocument:function(){return this.document;},getWindow:function(){return this;}});[Document,Element].invoke("implement",{getElements:function(e){return Slick.search(this,e,new Elements);
+},getElement:function(e){return document.id(Slick.find(this,e));}});var p={contains:function(e){return Slick.contains(this,e);}};if(!document.contains){Document.implement(p);
+}if(!document.createElement("div").contains){Element.implement(p);}var v=function(L,K){if(!L){return K;}L=Object.clone(Slick.parse(L));var l=L.expressions;
+for(var e=l.length;e--;){l[e][0].combinator=K;}return L;};Object.forEach({getNext:"~",getPrevious:"!~",getParent:"!"},function(e,l){Element.implement(l,function(K){return this.getElement(v(K,e));
+});});Object.forEach({getAllNext:"~",getAllPrevious:"!~",getSiblings:"~~",getChildren:">",getParents:"!"},function(e,l){Element.implement(l,function(K){return this.getElements(v(K,e));
+});});Element.implement({getFirst:function(e){return document.id(Slick.search(this,v(e,">"))[0]);},getLast:function(e){return document.id(Slick.search(this,v(e,">")).getLast());
+},getWindow:function(){return this.ownerDocument.window;},getDocument:function(){return this.ownerDocument;},getElementById:function(e){return document.id(Slick.find(this,"#"+(""+e).replace(/(\W)/g,"\\$1")));
+},match:function(e){return !e||Slick.match(this,e);}});if(window.$$==null){Window.implement("$$",function(e){if(arguments.length==1){if(typeof e=="string"){return Slick.search(this.document,e,new Elements);
+}else{if(Type.isEnumerable(e)){return new Elements(e);}}}return new Elements(arguments);});}var A={before:function(l,e){var K=e.parentNode;if(K){K.insertBefore(l,e);
+}},after:function(l,e){var K=e.parentNode;if(K){K.insertBefore(l,e.nextSibling);}},bottom:function(l,e){e.appendChild(l);},top:function(l,e){e.insertBefore(l,e.firstChild);
+}};A.inside=A.bottom;var n={},d={};var o={};Array.forEach(["type","value","defaultValue","accessKey","cellPadding","cellSpacing","colSpan","frameBorder","rowSpan","tabIndex","useMap"],function(e){o[e.toLowerCase()]=e;
+});o.html="innerHTML";o.text=(document.createElement("div").textContent==null)?"innerText":"textContent";Object.forEach(o,function(l,e){d[e]=function(K,L){K[l]=L;
+};n[e]=function(K){return K[l];};});var B=["compact","nowrap","ismap","declare","noshade","checked","disabled","readOnly","multiple","selected","noresize","defer","defaultChecked","autofocus","controls","autoplay","loop"];
+var k={};Array.forEach(B,function(e){var l=e.toLowerCase();k[l]=e;d[l]=function(K,L){K[e]=!!L;};n[l]=function(K){return !!K[e];};});Object.append(d,{"class":function(e,l){("className" in e)?e.className=(l||""):e.setAttribute("class",l);
+},"for":function(e,l){("htmlFor" in e)?e.htmlFor=l:e.setAttribute("for",l);},style:function(e,l){(e.style)?e.style.cssText=l:e.setAttribute("style",l);
+},value:function(e,l){e.value=(l!=null)?l:"";}});n["class"]=function(e){return("className" in e)?e.className||null:e.getAttribute("class");};var f=document.createElement("button");
+try{f.type="button";}catch(E){}if(f.type!="button"){d.type=function(e,l){e.setAttribute("type",l);};}f=null;var s=document.createElement("input");s.value="t";
+s.type="submit";if(s.value!="t"){d.type=function(l,e){var K=l.value;l.type=e;l.value=K;};}s=null;var u=(function(e){e.random="attribute";return(e.getAttribute("random")=="attribute");
+})(document.createElement("div"));var i=(function(e){e.innerHTML='<object><param name="should_fix" value="the unknown"></object>';return e.cloneNode(true).firstChild.childNodes.length!=1;
+})(document.createElement("div"));var j=!!document.createElement("div").classList;var F=function(e){var l=(e||"").clean().split(" "),K={};return l.filter(function(L){if(L!==""&&!K[L]){return K[L]=L;
+}});};var t=function(e){this.classList.add(e);};var g=function(e){this.classList.remove(e);};Element.implement({setProperty:function(l,K){var L=d[l.toLowerCase()];
+if(L){L(this,K);}else{var e;if(u){e=this.retrieve("$attributeWhiteList",{});}if(K==null){this.removeAttribute(l);if(u){delete e[l];}}else{this.setAttribute(l,""+K);
+if(u){e[l]=true;}}}return this;},setProperties:function(e){for(var l in e){this.setProperty(l,e[l]);}return this;},getProperty:function(M){var K=n[M.toLowerCase()];
+if(K){return K(this);}if(u){var l=this.getAttributeNode(M),L=this.retrieve("$attributeWhiteList",{});if(!l){return null;}if(l.expando&&!L[M]){var N=this.outerHTML;
+if(N.substr(0,N.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(M)<0){return null;}L[M]=true;}}var e=Slick.getAttribute(this,M);return(!e&&!Slick.hasAttribute(this,M))?null:e;
+},getProperties:function(){var e=Array.from(arguments);return e.map(this.getProperty,this).associate(e);},removeProperty:function(e){return this.setProperty(e,null);
+},removeProperties:function(){Array.each(arguments,this.removeProperty,this);return this;},set:function(K,l){var e=Element.Properties[K];(e&&e.set)?e.set.call(this,l):this.setProperty(K,l);
+}.overloadSetter(),get:function(l){var e=Element.Properties[l];return(e&&e.get)?e.get.apply(this):this.getProperty(l);}.overloadGetter(),erase:function(l){var e=Element.Properties[l];
+(e&&e.erase)?e.erase.apply(this):this.removeProperty(l);return this;},hasClass:j?function(e){return this.classList.contains(e);}:function(e){return this.className.clean().contains(e," ");
+},addClass:j?function(e){F(e).forEach(t,this);return this;}:function(e){this.className=F(e+" "+this.className).join(" ");return this;},removeClass:j?function(e){F(e).forEach(g,this);
+return this;}:function(e){var l=F(this.className);F(e).forEach(l.erase,l);this.className=l.join(" ");return this;},toggleClass:function(e,l){if(l==null){l=!this.hasClass(e);
+}return(l)?this.addClass(e):this.removeClass(e);},adopt:function(){var L=this,e,N=Array.flatten(arguments),M=N.length;if(M>1){L=e=document.createDocumentFragment();
+}for(var K=0;K<M;K++){var l=document.id(N[K],true);if(l){L.appendChild(l);}}if(e){this.appendChild(e);}return this;},appendText:function(l,e){return this.grab(this.getDocument().newTextNode(l),e);
+},grab:function(l,e){A[e||"bottom"](document.id(l,true),this);return this;},inject:function(l,e){A[e||"bottom"](this,document.id(l,true));return this;},replaces:function(e){e=document.id(e,true);
+e.parentNode.replaceChild(this,e);return this;},wraps:function(l,e){l=document.id(l,true);return this.replaces(l).grab(l,e);},getSelected:function(){this.selectedIndex;
+return new Elements(Array.from(this.options).filter(function(e){return e.selected;}));},toQueryString:function(){var e=[];this.getElements("input, select, textarea").each(function(K){var l=K.type;
+if(!K.name||K.disabled||l=="submit"||l=="reset"||l=="file"||l=="image"){return;}var L=(K.get("tag")=="select")?K.getSelected().map(function(M){return document.id(M).get("value");
+}):((l=="radio"||l=="checkbox")&&!K.checked)?null:K.get("value");Array.from(L).each(function(M){if(typeof M!="undefined"){e.push(encodeURIComponent(K.name)+"="+encodeURIComponent(M));
+}});});return e.join("&");}});var I={before:"beforeBegin",after:"afterEnd",bottom:"beforeEnd",top:"afterBegin",inside:"beforeEnd"};Element.implement("appendHTML",("insertAdjacentHTML" in document.createElement("div"))?function(l,e){this.insertAdjacentHTML(I[e||"bottom"],l);
+return this;}:function(P,M){var K=new Element("div",{html:P}),O=K.childNodes,L=K.firstChild;if(!L){return this;}if(O.length>1){L=document.createDocumentFragment();
+for(var N=0,e=O.length;N<e;N++){L.appendChild(O[N]);}}A[M||"bottom"](L,this);return this;});var m={},D={};var G=function(e){return(D[e]||(D[e]={}));};var z=function(l){var e=l.uniqueNumber;
+if(l.removeEvents){l.removeEvents();}if(l.clearAttributes){l.clearAttributes();}if(e!=null){delete m[e];delete D[e];}return l;};var H={input:"checked",option:"selected",textarea:"value"};
+Element.implement({destroy:function(){var e=z(this).getElementsByTagName("*");Array.each(e,z);Element.dispose(this);return null;},empty:function(){Array.from(this.childNodes).each(Element.dispose);
+return this;},dispose:function(){return(this.parentNode)?this.parentNode.removeChild(this):this;},clone:function(N,L){N=N!==false;var S=this.cloneNode(N),K=[S],M=[this],Q;
+if(N){K.append(Array.from(S.getElementsByTagName("*")));M.append(Array.from(this.getElementsByTagName("*")));}for(Q=K.length;Q--;){var O=K[Q],R=M[Q];if(!L){O.removeAttribute("id");
+}if(O.clearAttributes){O.clearAttributes();O.mergeAttributes(R);O.removeAttribute("uniqueNumber");if(O.options){var V=O.options,e=R.options;for(var P=V.length;
+P--;){V[P].selected=e[P].selected;}}}var l=H[R.tagName.toLowerCase()];if(l&&R[l]){O[l]=R[l];}}if(i){var T=S.getElementsByTagName("object"),U=this.getElementsByTagName("object");
+for(Q=T.length;Q--;){T[Q].outerHTML=U[Q].outerHTML;}}return document.id(S);}});[Element,Window,Document].invoke("implement",{addListener:function(l,e){if(window.attachEvent&&!window.addEventListener){m[Slick.uidOf(this)]=this;
+}if(this.addEventListener){this.addEventListener(l,e,!!arguments[2]);}else{this.attachEvent("on"+l,e);}return this;},removeListener:function(l,e){if(this.removeEventListener){this.removeEventListener(l,e,!!arguments[2]);
+}else{this.detachEvent("on"+l,e);}return this;},retrieve:function(l,e){var L=G(Slick.uidOf(this)),K=L[l];if(e!=null&&K==null){K=L[l]=e;}return K!=null?K:null;
+},store:function(l,e){var K=G(Slick.uidOf(this));K[l]=e;return this;},eliminate:function(e){var l=G(Slick.uidOf(this));delete l[e];return this;}});if(window.attachEvent&&!window.addEventListener){var J=function(){Object.each(m,z);
+if(window.CollectGarbage){CollectGarbage();}window.removeListener("unload",J);};window.addListener("unload",J);}Element.Properties={};Element.Properties.style={set:function(e){this.style.cssText=e;
+},get:function(){return this.style.cssText;},erase:function(){this.style.cssText="";}};Element.Properties.tag={get:function(){return this.tagName.toLowerCase();
+}};Element.Properties.html={set:function(e){if(e==null){e="";}else{if(typeOf(e)=="array"){e=e.join("");}}this.innerHTML=e;},erase:function(){this.innerHTML="";
+}};var a=true,h=true,C=true;var x=document.createElement("div");x.innerHTML="<nav></nav>";a=(x.childNodes.length==1);if(!a){var w="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video".split(" "),b=document.createDocumentFragment(),y=w.length;
+while(y--){b.createElement(w[y]);}}x=null;h=Function.attempt(function(){var e=document.createElement("table");e.innerHTML="<tr><td></td></tr>";return true;
+});var c=document.createElement("tr"),r="<td></td>";c.innerHTML=r;C=(c.innerHTML==r);c=null;if(!h||!C||!a){Element.Properties.html.set=(function(l){var e={table:[1,"<table>","</table>"],select:[1,"<select>","</select>"],tbody:[2,"<table><tbody>","</tbody></table>"],tr:[3,"<table><tbody><tr>","</tr></tbody></table>"]};
+e.thead=e.tfoot=e.tbody;return function(K){var L=e[this.get("tag")];if(!L&&!a){L=[0,"",""];}if(!L){return l.call(this,K);}var O=L[0],N=document.createElement("div"),M=N;
+if(!a){b.appendChild(N);}N.innerHTML=[L[1],K,L[2]].flatten().join("");while(O--){M=M.firstChild;}this.empty().adopt(M.childNodes);if(!a){b.removeChild(N);
+}N=null;};})(Element.Properties.html.set);}var q=document.createElement("form");q.innerHTML="<select><option>s</option></select>";if(q.firstChild.value!="s"){Element.Properties.value={set:function(N){var l=this.get("tag");
+if(l!="select"){return this.setProperty("value",N);}var K=this.getElements("option");N=String(N);for(var L=0;L<K.length;L++){var M=K[L],e=M.getAttributeNode("value"),O=(e&&e.specified)?M.value:M.get("text");
+if(O===N){return M.selected=true;}}},get:function(){var K=this,l=K.get("tag");if(l!="select"&&l!="option"){return this.getProperty("value");}if(l=="select"&&!(K=K.getSelected()[0])){return"";
+}var e=K.getAttributeNode("value");return(e&&e.specified)?K.value:K.get("text");}};}q=null;if(document.createElement("div").getAttributeNode("id")){Element.Properties.id={set:function(e){this.id=this.getAttributeNode("id").value=e;
+},get:function(){return this.id||null;},erase:function(){this.id=this.getAttributeNode("id").value="";}};}})();(function(){var l=document.html,f;f=document.createElement("div");
+f.style.color="red";f.style.color=null;var e=f.style.color=="red";var k="1px solid #123abc";f.style.border=k;var o=f.style.border!=k;f=null;var n=!!window.getComputedStyle;
+Element.Properties.styles={set:function(r){this.setStyles(r);}};var j=(l.style.opacity!=null),g=(l.style.filter!=null),q=/alpha\(opacity=([\d.]+)\)/i;var b=function(s,r){s.store("$opacity",r);
+s.style.visibility=r>0||r==null?"visible":"hidden";};var p=function(r,v,u){var t=r.style,s=t.filter||r.getComputedStyle("filter")||"";t.filter=(v.test(s)?s.replace(v,u):s+" "+u).trim();
+if(!t.filter){t.removeAttribute("filter");}};var h=(j?function(s,r){s.style.opacity=r;}:(g?function(s,r){if(!s.currentStyle||!s.currentStyle.hasLayout){s.style.zoom=1;
+}if(r==null||r==1){p(s,q,"");if(r==1&&i(s)!=1){p(s,q,"alpha(opacity=100)");}}else{p(s,q,"alpha(opacity="+(r*100).limit(0,100).round()+")");}}:b));var i=(j?function(s){var r=s.style.opacity||s.getComputedStyle("opacity");
+return(r=="")?1:r.toFloat();}:(g?function(s){var t=(s.style.filter||s.getComputedStyle("filter")),r;if(t){r=t.match(q);}return(r==null||t==null)?1:(r[1]/100);
+}:function(s){var r=s.retrieve("$opacity");if(r==null){r=(s.style.visibility=="hidden"?0:1);}return r;}));var d=(l.style.cssFloat==null)?"styleFloat":"cssFloat",a={left:"0%",top:"0%",center:"50%",right:"100%",bottom:"100%"},c=(l.style.backgroundPositionX!=null);
+var m=function(r,s){if(s=="backgroundPosition"){r.removeAttribute(s+"X");s+="Y";}r.removeAttribute(s);};Element.implement({getComputedStyle:function(t){if(!n&&this.currentStyle){return this.currentStyle[t.camelCase()];
+}var s=Element.getDocument(this).defaultView,r=s?s.getComputedStyle(this,null):null;return(r)?r.getPropertyValue((t==d)?"float":t.hyphenate()):"";},setStyle:function(s,r){if(s=="opacity"){if(r!=null){r=parseFloat(r);
+}h(this,r);return this;}s=(s=="float"?d:s).camelCase();if(typeOf(r)!="string"){var t=(Element.Styles[s]||"@").split(" ");r=Array.from(r).map(function(v,u){if(!t[u]){return"";
+}return(typeOf(v)=="number")?t[u].replace("@",Math.round(v)):v;}).join(" ");}else{if(r==String(Number(r))){r=Math.round(r);}}this.style[s]=r;if((r==""||r==null)&&e&&this.style.removeAttribute){m(this.style,s);
+}return this;},getStyle:function(x){if(x=="opacity"){return i(this);}x=(x=="float"?d:x).camelCase();var r=this.style[x];if(!r||x=="zIndex"){if(Element.ShortStyles.hasOwnProperty(x)){r=[];
+for(var w in Element.ShortStyles[x]){r.push(this.getStyle(w));}return r.join(" ");}r=this.getComputedStyle(x);}if(c&&/^backgroundPosition[XY]?$/.test(x)){return r.replace(/(top|right|bottom|left)/g,function(s){return a[s];
+})||"0px";}if(!r&&x=="backgroundPosition"){return"0px 0px";}if(r){r=String(r);var u=r.match(/rgba?\([\d\s,]+\)/);if(u){r=r.replace(u[0],u[0].rgbToHex());
+}}if(!n&&!this.style[x]){if((/^(height|width)$/).test(x)&&!(/px$/.test(r))){var t=(x=="width")?["left","right"]:["top","bottom"],v=0;t.each(function(s){v+=this.getStyle("border-"+s+"-width").toInt()+this.getStyle("padding-"+s).toInt();
+},this);return this["offset"+x.capitalize()]-v+"px";}if((/^border(.+)Width|margin|padding/).test(x)&&isNaN(parseFloat(r))){return"0px";}}if(o&&/^border(Top|Right|Bottom|Left)?$/.test(x)&&/^#/.test(r)){return r.replace(/^(.+)\s(.+)\s(.+)$/,"$2 $3 $1");
+}return r;},setStyles:function(s){for(var r in s){this.setStyle(r,s[r]);}return this;},getStyles:function(){var r={};Array.flatten(arguments).each(function(s){r[s]=this.getStyle(s);
+},this);return r;}});Element.Styles={left:"@px",top:"@px",bottom:"@px",right:"@px",width:"@px",height:"@px",maxWidth:"@px",maxHeight:"@px",minWidth:"@px",minHeight:"@px",backgroundColor:"rgb(@, @, @)",backgroundSize:"@px",backgroundPosition:"@px @px",color:"rgb(@, @, @)",fontSize:"@px",letterSpacing:"@px",lineHeight:"@px",clip:"rect(@px @px @px @px)",margin:"@px @px @px @px",padding:"@px @px @px @px",border:"@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)",borderWidth:"@px @px @px @px",borderStyle:"@ @ @ @",borderColor:"rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)",zIndex:"@",zoom:"@",fontWeight:"@",textIndent:"@px",opacity:"@"};
+Element.ShortStyles={margin:{},padding:{},border:{},borderWidth:{},borderStyle:{},borderColor:{}};["Top","Right","Bottom","Left"].each(function(x){var w=Element.ShortStyles;
+var s=Element.Styles;["margin","padding"].each(function(y){var z=y+x;w[y][z]=s[z]="@px";});var v="border"+x;w.border[v]=s[v]="@px @ rgb(@, @, @)";var u=v+"Width",r=v+"Style",t=v+"Color";
+w[v]={};w.borderWidth[u]=w[v][u]=s[u]="@px";w.borderStyle[r]=w[v][r]=s[r]="@";w.borderColor[t]=w[v][t]=s[t]="rgb(@, @, @)";});if(c){Element.ShortStyles.backgroundPosition={backgroundPositionX:"@",backgroundPositionY:"@"};
+}})();(function(){Element.Properties.events={set:function(b){this.addEvents(b);}};[Element,Window,Document].invoke("implement",{addEvent:function(f,h){var i=this.retrieve("events",{});
+if(!i[f]){i[f]={keys:[],values:[]};}if(i[f].keys.contains(h)){return this;}i[f].keys.push(h);var g=f,b=Element.Events[f],d=h,j=this;if(b){if(b.onAdd){b.onAdd.call(this,h,f);
+}if(b.condition){d=function(k){if(b.condition.call(this,k,f)){return h.call(this,k);}return true;};}if(b.base){g=Function.from(b.base).call(this,f);}}var e=function(){return h.call(j);
+};var c=Element.NativeEvents[g];if(c){if(c==2){e=function(k){k=new DOMEvent(k,j.getWindow());if(d.call(j,k)===false){k.stop();}};}this.addListener(g,e,arguments[2]);
+}i[f].values.push(e);return this;},removeEvent:function(e,d){var c=this.retrieve("events");if(!c||!c[e]){return this;}var h=c[e];var b=h.keys.indexOf(d);
+if(b==-1){return this;}var g=h.values[b];delete h.keys[b];delete h.values[b];var f=Element.Events[e];if(f){if(f.onRemove){f.onRemove.call(this,d,e);}if(f.base){e=Function.from(f.base).call(this,e);
+}}return(Element.NativeEvents[e])?this.removeListener(e,g,arguments[2]):this;},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this;
+},removeEvents:function(b){var d;if(typeOf(b)=="object"){for(d in b){this.removeEvent(d,b[d]);}return this;}var c=this.retrieve("events");if(!c){return this;
+}if(!b){for(d in c){this.removeEvents(d);}this.eliminate("events");}else{if(c[b]){c[b].keys.each(function(e){this.removeEvent(b,e);},this);delete c[b];
+}}return this;},fireEvent:function(e,c,b){var d=this.retrieve("events");if(!d||!d[e]){return this;}c=Array.from(c);d[e].keys.each(function(f){if(b){f.delay(b,this,c);
}else{f.apply(this,c);}},this);return this;},cloneEvents:function(e,d){e=document.id(e);var c=e.retrieve("events");if(!c){return this;}if(!d){for(var b in c){this.cloneEvents(e,b);
-}}else{if(c[d]){c[d].keys.each(function(f){this.addEvent(d,f);},this);}}return this;}});Element.NativeEvents={click:2,dblclick:2,mouseup:2,mousedown:2,contextmenu:2,mousewheel:2,DOMMouseScroll:2,mouseover:2,mouseout:2,mousemove:2,selectstart:2,selectend:2,keydown:2,keypress:2,keyup:2,orientationchange:2,touchstart:2,touchmove:2,touchend:2,touchcancel:2,gesturestart:2,gesturechange:2,gestureend:2,focus:2,blur:2,change:2,reset:2,select:2,submit:2,paste:2,input:2,load:2,unload:1,beforeunload:2,resize:1,move:1,DOMContentLoaded:1,readystatechange:1,error:1,abort:1,scroll:1};
-var a=function(b){var c=b.relatedTarget;if(c==null){return true;}if(!c){return false;}return(c!=this&&c.prefix!="xul"&&typeOf(this)!="document"&&!this.contains(c));
-};Element.Events={mouseenter:{base:"mouseover",condition:a},mouseleave:{base:"mouseout",condition:a},mousewheel:{base:(Browser.firefox)?"DOMMouseScroll":"mousewheel"}};
-if(!window.addEventListener){Element.NativeEvents.propertychange=2;Element.Events.change={base:function(){var b=this.type;return(this.get("tag")=="input"&&(b=="radio"||b=="checkbox"))?"propertychange":"change";
-},condition:function(b){return !!(this.type!="radio"||this.checked);}};}})();(function(){var c=!!window.addEventListener;Element.NativeEvents.focusin=Element.NativeEvents.focusout=2;
-var k=function(l,m,n,o,p){while(p&&p!=l){if(m(p,o)){return n.call(p,o,p);}p=document.id(p.parentNode);}};var a={mouseenter:{base:"mouseover"},mouseleave:{base:"mouseout"},focus:{base:"focus"+(c?"":"in"),capture:true},blur:{base:c?"blur":"focusout",capture:true}};
+}}else{if(c[d]){c[d].keys.each(function(f){this.addEvent(d,f);},this);}}return this;}});Element.NativeEvents={click:2,dblclick:2,mouseup:2,mousedown:2,contextmenu:2,mousewheel:2,DOMMouseScroll:2,mouseover:2,mouseout:2,mousemove:2,selectstart:2,selectend:2,keydown:2,keypress:2,keyup:2,orientationchange:2,touchstart:2,touchmove:2,touchend:2,touchcancel:2,gesturestart:2,gesturechange:2,gestureend:2,focus:2,blur:2,change:2,reset:2,select:2,submit:2,paste:2,input:2,load:2,unload:1,beforeunload:2,resize:1,move:1,DOMContentLoaded:1,readystatechange:1,hashchange:1,popstate:2,error:1,abort:1,scroll:1};
+Element.Events={mousewheel:{base:"onwheel" in document?"wheel":"onmousewheel" in document?"mousewheel":"DOMMouseScroll"}};var a=function(b){var c=b.relatedTarget;
+if(c==null){return true;}if(!c){return false;}return(c!=this&&c.prefix!="xul"&&typeOf(this)!="document"&&!this.contains(c));};if("onmouseenter" in document.documentElement){Element.NativeEvents.mouseenter=Element.NativeEvents.mouseleave=2;
+Element.MouseenterCheck=a;}else{Element.Events.mouseenter={base:"mouseover",condition:a};Element.Events.mouseleave={base:"mouseout",condition:a};}if(!window.addEventListener){Element.NativeEvents.propertychange=2;
+Element.Events.change={base:function(){var b=this.type;return(this.get("tag")=="input"&&(b=="radio"||b=="checkbox"))?"propertychange":"change";},condition:function(b){return b.type!="propertychange"||b.event.propertyName=="checked";
+}};}})();(function(){var c=!!window.addEventListener;Element.NativeEvents.focusin=Element.NativeEvents.focusout=2;var k=function(l,m,n,o,p){while(p&&p!=l){if(m(p,o)){return n.call(p,o,p);
+}p=document.id(p.parentNode);}};var a={mouseenter:{base:"mouseover",condition:Element.MouseenterCheck},mouseleave:{base:"mouseout",condition:Element.MouseenterCheck},focus:{base:"focus"+(c?"":"in"),capture:true},blur:{base:c?"blur":"focusout",capture:true}};
var b="$delegation:";var i=function(l){return{base:"focusin",remove:function(m,o){var p=m.retrieve(b+l+"listeners",{})[o];if(p&&p.forms){for(var n=p.forms.length;
n--;){p.forms[n].removeEvent(l,p.fns[n]);}}},listen:function(x,r,v,n,t,s){var o=(t.get("tag")=="form")?t:n.target.getParent("form");if(!o){return;}var u=x.retrieve(b+l+"listeners",{}),p=u[s]||{forms:[],fns:[]},m=p.forms,w=p.fns;
if(m.indexOf(o)!=-1){return;}m.push(o);var q=function(y){k(x,r,v,y,t);};o.addEvent(l,q);w.push(q);u[s]=p;x.store(b+l+"listeners",u);}};};var d=function(l){return{base:"focusin",listen:function(m,n,p,q,r){var o={blur:function(){this.removeEvents(o);
@@ -340,24 +361,24 @@ if(m.indexOf(o)!=-1){return;}m.push(o);var q=function(y){k(x,r,v,y,t);};o.addEve
}var h=Element.prototype,f=h.addEvent,j=h.removeEvent;var e=function(l,m){return function(r,q,n){if(r.indexOf(":relay")==-1){return l.call(this,r,q,n);
}var o=Slick.parse(r).expressions[0][0];if(o.pseudos[0].key!="relay"){return l.call(this,r,q,n);}var p=o.tag;o.pseudos.slice(1).each(function(s){p+=":"+s.key+(s.value?"("+s.value+")":"");
});l.call(this,r,q);return m.call(this,p,o.pseudos[0].value,q);};};var g={addEvent:function(v,q,x){var t=this.retrieve("$delegates",{}),r=t[v];if(r){for(var y in r){if(r[y].fn==x&&r[y].match==q){return this;
-}}}var p=v,u=q,o=x,n=a[v]||{};v=n.base||p;q=function(B){return Slick.match(B,u);};var w=Element.Events[p];if(w&&w.condition){var l=q,m=w.condition;q=function(C,B){return l(C,B)&&m.call(C,B,v);
-};}var z=this,s=String.uniqueID();var A=n.listen?function(B,C){if(!C&&B&&B.target){C=B.target;}if(C){n.listen(z,q,x,B,C,s);}}:function(B,C){if(!C&&B&&B.target){C=B.target;
-}if(C){k(z,q,x,B,C);}};if(!r){r={};}r[s]={match:u,fn:o,delegator:A};t[p]=r;return f.call(this,v,A,n.capture);},removeEvent:function(r,n,t,u){var q=this.retrieve("$delegates",{}),p=q[r];
-if(!p){return this;}if(u){var m=r,w=p[u].delegator,l=a[r]||{};r=l.base||m;if(l.remove){l.remove(this,u);}delete p[u];q[m]=p;return j.call(this,r,w);}var o,v;
-if(t){for(o in p){v=p[o];if(v.match==n&&v.fn==t){return g.removeEvent.call(this,r,n,t,o);}}}else{for(o in p){v=p[o];if(v.match==n){g.removeEvent.call(this,r,n,v.fn,o);
-}}}return this;}};[Element,Window,Document].invoke("implement",{addEvent:e(f,g.addEvent),removeEvent:e(j,g.removeEvent)});})();(function(){var h=document.createElement("div"),e=document.createElement("div");
-h.style.height="0";h.appendChild(e);var d=(e.offsetParent===h);h=e=null;var l=function(m){return k(m,"position")!="static"||a(m);};var i=function(m){return l(m)||(/^(?:table|td|th)$/i).test(m.tagName);
-};Element.implement({scrollTo:function(m,n){if(a(this)){this.getWindow().scrollTo(m,n);}else{this.scrollLeft=m;this.scrollTop=n;}return this;},getSize:function(){if(a(this)){return this.getWindow().getSize();
-}return{x:this.offsetWidth,y:this.offsetHeight};},getScrollSize:function(){if(a(this)){return this.getWindow().getScrollSize();}return{x:this.scrollWidth,y:this.scrollHeight};
-},getScroll:function(){if(a(this)){return this.getWindow().getScroll();}return{x:this.scrollLeft,y:this.scrollTop};},getScrolls:function(){var n=this.parentNode,m={x:0,y:0};
-while(n&&!a(n)){m.x+=n.scrollLeft;m.y+=n.scrollTop;n=n.parentNode;}return m;},getOffsetParent:d?function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;
-}var n=(k(m,"position")=="static")?i:l;while((m=m.parentNode)){if(n(m)){return m;}}return null;}:function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;
-}try{return m.offsetParent;}catch(n){}return null;},getOffsets:function(){if(this.getBoundingClientRect&&!Browser.Platform.ios){var r=this.getBoundingClientRect(),o=document.id(this.getDocument().documentElement),q=o.getScroll(),t=this.getScrolls(),s=(k(this,"position")=="fixed");
-return{x:r.left.toInt()+t.x+((s)?0:q.x)-o.clientLeft,y:r.top.toInt()+t.y+((s)?0:q.y)-o.clientTop};}var n=this,m={x:0,y:0};if(a(this)){return m;}while(n&&!a(n)){m.x+=n.offsetLeft;
-m.y+=n.offsetTop;if(Browser.firefox){if(!c(n)){m.x+=b(n);m.y+=g(n);}var p=n.parentNode;if(p&&k(p,"overflow")!="visible"){m.x+=b(p);m.y+=g(p);}}else{if(n!=this&&Browser.safari){m.x+=b(n);
-m.y+=g(n);}}n=n.offsetParent;}if(Browser.firefox&&!c(this)){m.x-=b(this);m.y-=g(this);}return m;},getPosition:function(p){var q=this.getOffsets(),n=this.getScrolls();
-var m={x:q.x-n.x,y:q.y-n.y};if(p&&(p=document.id(p))){var o=p.getPosition();return{x:m.x-o.x-b(p),y:m.y-o.y-g(p)};}return m;},getCoordinates:function(o){if(a(this)){return this.getWindow().getCoordinates();
-}var m=this.getPosition(o),n=this.getSize();var p={left:m.x,top:m.y,width:n.x,height:n.y};p.right=p.left+p.width;p.bottom=p.top+p.height;return p;},computePosition:function(m){return{left:m.x-j(this,"margin-left"),top:m.y-j(this,"margin-top")};
+}}}var p=v,u=q,o=x,n=a[v]||{};v=n.base||p;q=function(B){return Slick.match(B,u);};var w=Element.Events[p];if(n.condition||w&&w.condition){var l=q,m=n.condition||w.condition;
+q=function(C,B){return l(C,B)&&m.call(C,B,v);};}var z=this,s=String.uniqueID();var A=n.listen?function(B,C){if(!C&&B&&B.target){C=B.target;}if(C){n.listen(z,q,x,B,C,s);
+}}:function(B,C){if(!C&&B&&B.target){C=B.target;}if(C){k(z,q,x,B,C);}};if(!r){r={};}r[s]={match:u,fn:o,delegator:A};t[p]=r;return f.call(this,v,A,n.capture);
+},removeEvent:function(r,n,t,u){var q=this.retrieve("$delegates",{}),p=q[r];if(!p){return this;}if(u){var m=r,w=p[u].delegator,l=a[r]||{};r=l.base||m;if(l.remove){l.remove(this,u);
+}delete p[u];q[m]=p;return j.call(this,r,w,l.capture);}var o,v;if(t){for(o in p){v=p[o];if(v.match==n&&v.fn==t){return g.removeEvent.call(this,r,n,t,o);
+}}}else{for(o in p){v=p[o];if(v.match==n){g.removeEvent.call(this,r,n,v.fn,o);}}}return this;}};[Element,Window,Document].invoke("implement",{addEvent:e(f,g.addEvent),removeEvent:e(j,g.removeEvent)});
+})();(function(){var h=document.createElement("div"),e=document.createElement("div");h.style.height="0";h.appendChild(e);var d=(e.offsetParent===h);h=e=null;
+var l=function(m){return k(m,"position")!="static"||a(m);};var i=function(m){return l(m)||(/^(?:table|td|th)$/i).test(m.tagName);};Element.implement({scrollTo:function(m,n){if(a(this)){this.getWindow().scrollTo(m,n);
+}else{this.scrollLeft=m;this.scrollTop=n;}return this;},getSize:function(){if(a(this)){return this.getWindow().getSize();}return{x:this.offsetWidth,y:this.offsetHeight};
+},getScrollSize:function(){if(a(this)){return this.getWindow().getScrollSize();}return{x:this.scrollWidth,y:this.scrollHeight};},getScroll:function(){if(a(this)){return this.getWindow().getScroll();
+}return{x:this.scrollLeft,y:this.scrollTop};},getScrolls:function(){var n=this.parentNode,m={x:0,y:0};while(n&&!a(n)){m.x+=n.scrollLeft;m.y+=n.scrollTop;
+n=n.parentNode;}return m;},getOffsetParent:d?function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;}var n=(k(m,"position")=="static")?i:l;
+while((m=m.parentNode)){if(n(m)){return m;}}return null;}:function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;}try{return m.offsetParent;
+}catch(n){}return null;},getOffsets:function(){var n=this.getBoundingClientRect;if(n){var r=this.getBoundingClientRect(),p=document.id(this.getDocument().documentElement),q=p.getScroll(),t=this.getScrolls(),s=(k(this,"position")=="fixed");
+return{x:r.left.toInt()+t.x+((s)?0:q.x)-p.clientLeft,y:r.top.toInt()+t.y+((s)?0:q.y)-p.clientTop};}var o=this,m={x:0,y:0};if(a(this)){return m;}while(o&&!a(o)){m.x+=o.offsetLeft;
+m.y+=o.offsetTop;o=o.offsetParent;}return m;},getPosition:function(p){var q=this.getOffsets(),n=this.getScrolls();var m={x:q.x-n.x,y:q.y-n.y};if(p&&(p=document.id(p))){var o=p.getPosition();
+return{x:m.x-o.x-b(p),y:m.y-o.y-g(p)};}return m;},getCoordinates:function(o){if(a(this)){return this.getWindow().getCoordinates();}var m=this.getPosition(o),n=this.getSize();
+var p={left:m.x,top:m.y,width:n.x,height:n.y};p.right=p.left+p.width;p.bottom=p.top+p.height;return p;},computePosition:function(m){return{left:m.x-j(this,"margin-left"),top:m.y-j(this,"margin-top")};
},setPosition:function(m){return this.setStyles(this.computePosition(m));}});[Document,Window].invoke("implement",{getSize:function(){var m=f(this);return{x:m.clientWidth,y:m.clientHeight};
},getScroll:function(){var n=this.getWindow(),m=f(this);return{x:n.pageXOffset||m.scrollLeft,y:n.pageYOffset||m.scrollTop};},getScrollSize:function(){var o=f(this),n=this.getSize(),m=this.getDocument().body;
return{x:Math.max(o.scrollWidth,m.scrollWidth,n.x),y:Math.max(o.scrollHeight,m.scrollHeight,n.y)};},getPosition:function(){return{x:0,y:0};},getCoordinates:function(){var m=this.getSize();
@@ -377,29 +398,32 @@ this.frames=i||Math.round(this.duration/this.frameInterval);this.fireEvent("star
d.call(this,this.options.fps);if(this.frames==this.frame){this.fireEvent("complete",this.subject);if(!this.callChain()){this.fireEvent("chainComplete",this.subject);
}}else{this.fireEvent("stop",this.subject);}}return this;},cancel:function(){if(this.isRunning()){this.time=null;d.call(this,this.options.fps);this.frame=this.frames;
this.fireEvent("cancel",this.subject).clearChain();}return this;},pause:function(){if(this.isRunning()){this.time=null;d.call(this,this.options.fps);}return this;
-},resume:function(){if((this.frame<this.frames)&&!this.isRunning()){b.call(this,this.options.fps);}return this;},isRunning:function(){var g=e[this.options.fps];
-return g&&g.contains(this);}});f.compute=function(i,h,g){return(h-i)*g+i;};f.Durations={"short":250,normal:500,"long":1000};var e={},c={};var a=function(){var h=Date.now();
-for(var j=this.length;j--;){var g=this[j];if(g){g.step(h);}}};var b=function(h){var g=e[h]||(e[h]=[]);g.push(this);if(!c[h]){c[h]=a.periodical(Math.round(1000/h),g);
-}};var d=function(h){var g=e[h];if(g){g.erase(this);if(!g.length&&c[h]){delete e[h];c[h]=clearInterval(c[h]);}}};})();Fx.CSS=new Class({Extends:Fx,prepare:function(c,d,b){b=Array.from(b);
-if(b[1]==null){b[1]=b[0];b[0]=c.getStyle(d);}var a=b.map(this.parse);return{from:a[0],to:a[1]};},parse:function(a){a=Function.from(a)();a=(typeof a=="string")?a.split(" "):Array.from(a);
-return a.map(function(c){c=String(c);var b=false;Object.each(Fx.CSS.Parsers,function(f,e){if(b){return;}var d=f.parse(c);if(d||d===0){b={value:d,parser:f};
-}});b=b||{value:c,parser:Fx.CSS.Parsers.String};return b;});},compute:function(d,c,b){var a=[];(Math.min(d.length,c.length)).times(function(e){a.push({value:d[e].parser.compute(d[e].value,c[e].value,b),parser:d[e].parser});
+},resume:function(){if(this.isPaused()){b.call(this,this.options.fps);}return this;},isRunning:function(){var g=e[this.options.fps];return g&&g.contains(this);
+},isPaused:function(){return(this.frame<this.frames)&&!this.isRunning();}});f.compute=function(i,h,g){return(h-i)*g+i;};f.Durations={"short":250,normal:500,"long":1000};
+var e={},c={};var a=function(){var h=Date.now();for(var j=this.length;j--;){var g=this[j];if(g){g.step(h);}}};var b=function(h){var g=e[h]||(e[h]=[]);g.push(this);
+if(!c[h]){c[h]=a.periodical(Math.round(1000/h),g);}};var d=function(h){var g=e[h];if(g){g.erase(this);if(!g.length&&c[h]){delete e[h];c[h]=clearInterval(c[h]);
+}}};})();Fx.CSS=new Class({Extends:Fx,prepare:function(b,e,a){a=Array.from(a);var h=a[0],g=a[1];if(g==null){g=h;h=b.getStyle(e);var c=this.options.unit;
+if(c&&h&&typeof h=="string"&&h.slice(-c.length)!=c&&parseFloat(h)!=0){b.setStyle(e,g+c);var d=b.getComputedStyle(e);if(!(/px$/.test(d))){d=b.style[("pixel-"+e).camelCase()];
+if(d==null){var f=b.style.left;b.style.left=g+c;d=b.style.pixelLeft;b.style.left=f;}}h=(g||1)/(parseFloat(d)||1)*(parseFloat(h)||0);b.setStyle(e,h+c);}}return{from:this.parse(h),to:this.parse(g)};
+},parse:function(a){a=Function.from(a)();a=(typeof a=="string")?a.split(" "):Array.from(a);return a.map(function(c){c=String(c);var b=false;Object.each(Fx.CSS.Parsers,function(f,e){if(b){return;
+}var d=f.parse(c);if(d||d===0){b={value:d,parser:f};}});b=b||{value:c,parser:Fx.CSS.Parsers.String};return b;});},compute:function(d,c,b){var a=[];(Math.min(d.length,c.length)).times(function(e){a.push({value:d[e].parser.compute(d[e].value,c[e].value,b),parser:d[e].parser});
});a.$family=Function.from("fx:css:value");return a;},serve:function(c,b){if(typeOf(c)!="fx:css:value"){c=this.parse(c);}var a=[];c.each(function(d){a=a.concat(d.parser.serve(d.value,b));
-});return a;},render:function(a,d,c,b){a.setStyle(d,this.serve(c,b));},search:function(a){if(Fx.CSS.Cache[a]){return Fx.CSS.Cache[a];}var c={},b=new RegExp("^"+a.escapeRegExp()+"$");
-Array.each(document.styleSheets,function(f,e){var d=f.href;if(d&&d.contains("://")&&!d.contains(document.domain)){return;}var g=f.rules||f.cssRules;Array.each(g,function(k,h){if(!k.style){return;
-}var j=(k.selectorText)?k.selectorText.replace(/^\w+/,function(i){return i.toLowerCase();}):null;if(!j||!b.test(j)){return;}Object.each(Element.Styles,function(l,i){if(!k.style[i]||Element.ShortStyles[i]){return;
-}l=String(k.style[i]);c[i]=((/^rgb/).test(l))?l.rgbToHex():l;});});});return Fx.CSS.Cache[a]=c;}});Fx.CSS.Cache={};Fx.CSS.Parsers={Color:{parse:function(a){if(a.match(/^#[0-9a-f]{3,6}$/i)){return a.hexToRgb(true);
+});return a;},render:function(a,d,c,b){a.setStyle(d,this.serve(c,b));},search:function(a){if(Fx.CSS.Cache[a]){return Fx.CSS.Cache[a];}var d={},c=new RegExp("^"+a.escapeRegExp()+"$");
+var b=function(e){Array.each(e,function(h,f){if(h.media){b(h.rules||h.cssRules);return;}if(!h.style){return;}var g=(h.selectorText)?h.selectorText.replace(/^\w+/,function(i){return i.toLowerCase();
+}):null;if(!g||!c.test(g)){return;}Object.each(Element.Styles,function(j,i){if(!h.style[i]||Element.ShortStyles[i]){return;}j=String(h.style[i]);d[i]=((/^rgb/).test(j))?j.rgbToHex():j;
+});});};Array.each(document.styleSheets,function(g,f){var e=g.href;if(e&&e.indexOf("://")>-1&&e.indexOf(document.domain)==-1){return;}var h=g.rules||g.cssRules;
+b(h);});return Fx.CSS.Cache[a]=d;}});Fx.CSS.Cache={};Fx.CSS.Parsers={Color:{parse:function(a){if(a.match(/^#[0-9a-f]{3,6}$/i)){return a.hexToRgb(true);
}return((a=a.match(/(\d+),\s*(\d+),\s*(\d+)/)))?[a[1],a[2],a[3]]:false;},compute:function(c,b,a){return c.map(function(e,d){return Math.round(Fx.compute(c[d],b[d],a));
});},serve:function(a){return a.map(Number);}},Number:{parse:parseFloat,compute:Fx.compute,serve:function(b,a){return(a)?b+a:b;}},String:{parse:Function.from(false),compute:function(b,a){return a;
},serve:function(a){return a;}}};Fx.Tween=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=document.id(b);this.parent(a);},set:function(b,a){if(arguments.length==1){a=b;
b=this.property||this.options.property;}this.render(this.element,b,a,this.options.unit);return this;},start:function(c,e,d){if(!this.check(c,e,d)){return this;
}var b=Array.flatten(arguments);this.property=this.options.property||b.shift();var a=this.prepare(this.element,this.property,b);return this.parent(a.from,a.to);
}});Element.Properties.tween={set:function(a){this.get("tween").cancel().setOptions(a);return this;},get:function(){var a=this.retrieve("tween");if(!a){a=new Fx.Tween(this,{link:"cancel"});
-this.store("tween",a);}return a;}};Element.implement({tween:function(a,c,b){this.get("tween").start(a,c,b);return this;},fade:function(c){var d=this.get("tween"),f,e,a;
-if(c==null){c="toggle";}switch(c){case"in":f="start";e=1;break;case"out":f="start";e=0;break;case"show":f="set";e=1;break;case"hide":f="set";e=0;break;
-case"toggle":var b=this.retrieve("fade:flag",this.getStyle("opacity")==1);f="start";e=b?0:1;this.store("fade:flag",!b);a=true;break;default:f="start";e=c;
-}if(!a){this.eliminate("fade:flag");}d[f]("opacity",e);if(f=="set"||e!=0){this.setStyle("visibility",e==0?"hidden":"visible");}else{d.chain(function(){this.element.setStyle("visibility","hidden");
-});}return this;},highlight:function(c,a){if(!a){a=this.retrieve("highlight:original",this.getStyle("background-color"));a=(a=="transparent")?"#fff":a;
+this.store("tween",a);}return a;}};Element.implement({tween:function(a,c,b){this.get("tween").start(a,c,b);return this;},fade:function(d){var e=this.get("tween"),g,c=["opacity"].append(arguments),a;
+if(c[1]==null){c[1]="toggle";}switch(c[1]){case"in":g="start";c[1]=1;break;case"out":g="start";c[1]=0;break;case"show":g="set";c[1]=1;break;case"hide":g="set";
+c[1]=0;break;case"toggle":var b=this.retrieve("fade:flag",this.getStyle("opacity")==1);g="start";c[1]=b?0:1;this.store("fade:flag",!b);a=true;break;default:g="start";
+}if(!a){this.eliminate("fade:flag");}e[g].apply(e,c);var f=c[c.length-1];if(g=="set"||f!=0){this.setStyle("visibility",f==0?"hidden":"visible");}else{e.chain(function(){this.element.setStyle("visibility","hidden");
+this.callChain();});}return this;},highlight:function(c,a){if(!a){a=this.retrieve("highlight:original",this.getStyle("background-color"));a=(a=="transparent")?"#fff":a;
}var b=this.get("tween");b.start("background-color",c||"#ffff88",a).chain(function(){this.setStyle("background-color",this.retrieve("highlight:original"));
b.callChain();}.bind(this));return this;}});Fx.Morph=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=document.id(b);this.parent(a);
},set:function(a){if(typeof a=="string"){a=this.search(a);}for(var b in a){this.render(this.element,b,a[b],this.options.unit);}return this;},compute:function(e,d,c){var a={};
@@ -427,12 +451,12 @@ return this;},getHeader:function(e){return Function.attempt(function(){return th
o=Object.append({data:h.data,url:h.url,method:h.method},o);var j=o.data,f=String(o.url),e=o.method.toLowerCase();switch(typeOf(j)){case"element":j=document.id(j).toQueryString();
break;case"object":case"hash":j=Object.toQueryString(j);}if(this.options.format){var m="format="+this.options.format;j=(j)?m+"&"+j:m;}if(this.options.emulation&&!["get","post"].contains(e)){var k="_method="+e;
j=(j)?k+"&"+j:k;e="post";}if(this.options.urlEncoded&&["post","put"].contains(e)){var g=(this.options.encoding)?"; charset="+this.options.encoding:"";this.headers["Content-type"]="application/x-www-form-urlencoded"+g;
-}if(!f){f=document.location.pathname;}var i=f.lastIndexOf("/");if(i>-1&&(i=f.indexOf("#"))>-1){f=f.substr(0,i);}if(this.options.noCache){f+=(f.contains("?")?"&":"?")+String.uniqueID();
-}if(j&&e=="get"){f+=(f.contains("?")?"&":"?")+j;j=null;}var n=this.xhr;if(a){n.onloadstart=this.loadstart.bind(this);n.onprogress=this.progress.bind(this);
+}if(!f){f=document.location.pathname;}var i=f.lastIndexOf("/");if(i>-1&&(i=f.indexOf("#"))>-1){f=f.substr(0,i);}if(this.options.noCache){f+=(f.indexOf("?")>-1?"&":"?")+String.uniqueID();
+}if(j&&(e=="get"||e=="delete")){f+=(f.indexOf("?")>-1?"&":"?")+j;j=null;}var n=this.xhr;if(a){n.onloadstart=this.loadstart.bind(this);n.onprogress=this.progress.bind(this);
}n.open(e.toUpperCase(),f,this.options.async,this.options.user,this.options.password);if(this.options.user&&"withCredentials" in n){n.withCredentials=true;
}n.onreadystatechange=this.onStateChange.bind(this);Object.each(this.headers,function(q,p){try{n.setRequestHeader(p,q);}catch(r){this.fireEvent("exception",[p,q]);
-}},this);this.fireEvent("request");n.send(j);if(!this.options.async){this.onStateChange();}if(this.options.timeout){this.timer=this.timeout.delay(this.options.timeout,this);
-}return this;},cancel:function(){if(!this.running){return this;}this.running=false;var e=this.xhr;e.abort();clearTimeout(this.timer);e.onreadystatechange=d;
+}},this);this.fireEvent("request");n.send(j);if(!this.options.async){this.onStateChange();}else{if(this.options.timeout){this.timer=this.timeout.delay(this.options.timeout,this);
+}}return this;},cancel:function(){if(!this.running){return this;}this.running=false;var e=this.xhr;e.abort();clearTimeout(this.timer);e.onreadystatechange=d;
if(a){e.onprogress=e.onloadstart=d;}this.xhr=new Browser.Request();this.fireEvent("cancel");return this;}});var b={};["get","post","put","delete","GET","POST","PUT","DELETE"].each(function(e){b[e]=function(g){var f={method:e};
if(g!=null){f.data=g;}return this.send(f);};});c.implement(b);Element.Properties.send={set:function(e){var f=this.get("send").cancel();f.setOptions(e);
return this;},get:function(){var e=this.retrieve("send");if(!e){e=new c({data:this,link:"cancel",method:this.get("method")||"post",url:this.get("action")});
@@ -449,9 +473,9 @@ c.tree=b.childNodes;c.elements=b.getElements(e.filter||"*");if(e.filter){c.tree=
return(/^[\],:{}\s]*$/).test(string);};JSON.encode=JSON.stringify?function(obj){return JSON.stringify(obj);}:function(obj){if(obj&&obj.toJSON){obj=obj.toJSON();
}switch(typeOf(obj)){case"string":return'"'+obj.replace(/[\x00-\x1f\\"]/g,escape)+'"';case"array":return"["+obj.map(JSON.encode).clean()+"]";case"object":case"hash":var string=[];
Object.each(obj,function(value,key){var json=JSON.encode(value);if(json){string.push(JSON.encode(key)+":"+json);}});return"{"+string+"}";case"number":case"boolean":return""+obj;
-case"null":return"null";}return null;};JSON.decode=function(string,secure){if(!string||typeOf(string)!="string"){return null;}if(secure||JSON.secure){if(JSON.parse){return JSON.parse(string);
-}if(!JSON.validate(string)){throw new Error("JSON could not decode the input; security is enabled and the value is not secure.");}}return eval("("+string+")");
-};})();Request.JSON=new Class({Extends:Request,options:{secure:true},initialize:function(a){this.parent(a);Object.append(this.headers,{Accept:"application/json","X-Request":"JSON"});
+case"null":return"null";}return null;};JSON.secure=true;JSON.decode=function(string,secure){if(!string||typeOf(string)!="string"){return null;}if(secure==null){secure=JSON.secure;
+}if(secure){if(JSON.parse){return JSON.parse(string);}if(!JSON.validate(string)){throw new Error("JSON could not decode the input; security is enabled and the value is not secure.");
+}}return eval("("+string+")");};})();Request.JSON=new Class({Extends:Request,options:{secure:true},initialize:function(a){this.parent(a);Object.append(this.headers,{Accept:"application/json","X-Request":"JSON"});
},success:function(c){var b;try{b=this.response.json=JSON.decode(c,this.options.secure);}catch(a){this.fireEvent("error",[c,a]);return;}if(b==null){this.onFailure();
}else{this.onSuccess(b,c);}}});var Cookie=new Class({Implements:Options,options:{path:"/",domain:false,duration:false,secure:false,document:document,encode:true},initialize:function(b,a){this.key=b;
this.setOptions(a);},write:function(b){if(this.options.encode){b=encodeURIComponent(b);}if(this.options.domain){b+="; domain="+this.options.domain;}if(this.options.path){b+="; path="+this.options.path;
@@ -464,13 +488,4 @@ k.fireEvent("domready");i.fireEvent("domready");};var a=function(){for(var m=e.l
if(!a()){b=setTimeout(j,10);}};k.addListener("DOMContentLoaded",g);var h=function(){try{d.doScroll();return true;}catch(m){}return false;};if(d.doScroll&&!h()){e.push(h);
c=true;}if(k.readyState){e.push(function(){var m=k.readyState;return(m=="loaded"||m=="complete");});}if("onreadystatechange" in k){k.addListener("readystatechange",a);
}else{c=true;}if(c){j();}Element.Events.domready={onAdd:function(m){if(l){m.call(this);}}};Element.Events.load={base:"load",onAdd:function(m){if(f&&this==i){m.call(this);
-}},condition:function(){if(this==i){g();delete Element.Events.load;}return true;}};i.addEvent("load",function(){f=true;});})(window,document);(function(){var Swiff=this.Swiff=new Class({Implements:Options,options:{id:null,height:1,width:1,container:null,properties:{},params:{quality:"high",allowScriptAccess:"always",wMode:"window",swLiveConnect:true},callBacks:{},vars:{}},toElement:function(){return this.object;
-},initialize:function(path,options){this.instance="Swiff_"+String.uniqueID();this.setOptions(options);options=this.options;var id=this.id=options.id||this.instance;
-var container=document.id(options.container);Swiff.CallBacks[this.instance]={};var params=options.params,vars=options.vars,callBacks=options.callBacks;
-var properties=Object.append({height:options.height,width:options.width},options.properties);var self=this;for(var callBack in callBacks){Swiff.CallBacks[this.instance][callBack]=(function(option){return function(){return option.apply(self.object,arguments);
-};})(callBacks[callBack]);vars[callBack]="Swiff.CallBacks."+this.instance+"."+callBack;}params.flashVars=Object.toQueryString(vars);if(Browser.ie){properties.classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
-params.movie=path;}else{properties.type="application/x-shockwave-flash";}properties.data=path;var build='<object id="'+id+'"';for(var property in properties){build+=" "+property+'="'+properties[property]+'"';
-}build+=">";for(var param in params){if(params[param]){build+='<param name="'+param+'" value="'+params[param]+'" />';}}build+="</object>";this.object=((container)?container.empty():new Element("div")).set("html",build).firstChild;
-},replaces:function(element){element=document.id(element,true);element.parentNode.replaceChild(this.toElement(),element);return this;},inject:function(element){document.id(element,true).appendChild(this.toElement());
-return this;},remote:function(){return Swiff.remote.apply(Swiff,[this.toElement()].append(arguments));}});Swiff.CallBacks={};Swiff.remote=function(obj,fn){var rs=obj.CallFunction('<invoke name="'+fn+'" returntype="javascript">'+__flash__argumentsToXML(arguments,2)+"</invoke>");
-return eval(rs);};})(); \ No newline at end of file
+}},condition:function(){if(this==i){g();delete Element.Events.load;}return true;}};i.addEvent("load",function(){f=true;});})(window,document); \ No newline at end of file
diff --git a/module/webui/themes/Dark/js/static/mootools-more.js b/module/webui/themes/Dark/js/static/mootools-more.js
new file mode 100644
index 000000000..c7f4a1a0e
--- /dev/null
+++ b/module/webui/themes/Dark/js/static/mootools-more.js
@@ -0,0 +1,2856 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/more/c1cc18c2fff04bcc58921b4dff80a6f1
+
+packager build:
+ - packager build More/Form.Request More/Fx.Reveal More/Sortables More/Request.Periodical More/Color
+
+...
+*/
+
+/*
+---
+
+script: More.js
+
+name: More
+
+description: MooTools More
+
+license: MIT-style license
+
+authors:
+ - Guillermo Rauch
+ - Thomas Aylott
+ - Scott Kyle
+ - Arian Stolwijk
+ - Tim Wienk
+ - Christoph Pojer
+ - Aaron Newton
+ - Jacob Thornton
+
+requires:
+ - Core/MooTools
+
+provides: [MooTools.More]
+
+...
+*/
+
+MooTools.More = {
+ version: '1.5.0',
+ build: '73db5e24e6e9c5c87b3a27aebef2248053f7db37'
+};
+
+
+/*
+---
+
+script: Class.Binds.js
+
+name: Class.Binds
+
+description: Automagically binds specified methods in a class to the instance of the class.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Class
+ - MooTools.More
+
+provides: [Class.Binds]
+
+...
+*/
+
+Class.Mutators.Binds = function(binds){
+ if (!this.prototype.initialize) this.implement('initialize', function(){});
+ return Array.from(binds).concat(this.prototype.Binds || []);
+};
+
+Class.Mutators.initialize = function(initialize){
+ return function(){
+ Array.from(this.Binds).each(function(name){
+ var original = this[name];
+ if (original) this[name] = original.bind(this);
+ }, this);
+ return initialize.apply(this, arguments);
+ };
+};
+
+
+/*
+---
+
+script: Class.Occlude.js
+
+name: Class.Occlude
+
+description: Prevents a class from being applied to a DOM element twice.
+
+license: MIT-style license.
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Class
+ - Core/Element
+ - MooTools.More
+
+provides: [Class.Occlude]
+
+...
+*/
+
+Class.Occlude = new Class({
+
+ occlude: function(property, element){
+ element = document.id(element || this.element);
+ var instance = element.retrieve(property || this.property);
+ if (instance && !this.occluded)
+ return (this.occluded = instance);
+
+ this.occluded = false;
+ element.store(property || this.property, this);
+ return this.occluded;
+ }
+
+});
+
+
+/*
+---
+
+script: Class.Refactor.js
+
+name: Class.Refactor
+
+description: Extends a class onto itself with new property, preserving any items attached to the class's namespace.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Class
+ - MooTools.More
+
+# Some modules declare themselves dependent on Class.Refactor
+provides: [Class.refactor, Class.Refactor]
+
+...
+*/
+
+Class.refactor = function(original, refactors){
+
+ Object.each(refactors, function(item, name){
+ var origin = original.prototype[name];
+ origin = (origin && origin.$origin) || origin || function(){};
+ original.implement(name, (typeof item == 'function') ? function(){
+ var old = this.previous;
+ this.previous = origin;
+ var value = item.apply(this, arguments);
+ this.previous = old;
+ return value;
+ } : item);
+ });
+
+ return original;
+
+};
+
+
+/*
+---
+
+script: Element.Measure.js
+
+name: Element.Measure
+
+description: Extends the Element native object to include methods useful in measuring dimensions.
+
+credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Element.Style
+ - Core/Element.Dimensions
+ - MooTools.More
+
+provides: [Element.Measure]
+
+...
+*/
+
+(function(){
+
+var getStylesList = function(styles, planes){
+ var list = [];
+ Object.each(planes, function(directions){
+ Object.each(directions, function(edge){
+ styles.each(function(style){
+ list.push(style + '-' + edge + (style == 'border' ? '-width' : ''));
+ });
+ });
+ });
+ return list;
+};
+
+var calculateEdgeSize = function(edge, styles){
+ var total = 0;
+ Object.each(styles, function(value, style){
+ if (style.test(edge)) total = total + value.toInt();
+ });
+ return total;
+};
+
+var isVisible = function(el){
+ return !!(!el || el.offsetHeight || el.offsetWidth);
+};
+
+
+Element.implement({
+
+ measure: function(fn){
+ if (isVisible(this)) return fn.call(this);
+ var parent = this.getParent(),
+ toMeasure = [];
+ while (!isVisible(parent) && parent != document.body){
+ toMeasure.push(parent.expose());
+ parent = parent.getParent();
+ }
+ var restore = this.expose(),
+ result = fn.call(this);
+ restore();
+ toMeasure.each(function(restore){
+ restore();
+ });
+ return result;
+ },
+
+ expose: function(){
+ if (this.getStyle('display') != 'none') return function(){};
+ var before = this.style.cssText;
+ this.setStyles({
+ display: 'block',
+ position: 'absolute',
+ visibility: 'hidden'
+ });
+ return function(){
+ this.style.cssText = before;
+ }.bind(this);
+ },
+
+ getDimensions: function(options){
+ options = Object.merge({computeSize: false}, options);
+ var dim = {x: 0, y: 0};
+
+ var getSize = function(el, options){
+ return (options.computeSize) ? el.getComputedSize(options) : el.getSize();
+ };
+
+ var parent = this.getParent('body');
+
+ if (parent && this.getStyle('display') == 'none'){
+ dim = this.measure(function(){
+ return getSize(this, options);
+ });
+ } else if (parent){
+ try { //safari sometimes crashes here, so catch it
+ dim = getSize(this, options);
+ }catch(e){}
+ }
+
+ return Object.append(dim, (dim.x || dim.x === 0) ? {
+ width: dim.x,
+ height: dim.y
+ } : {
+ x: dim.width,
+ y: dim.height
+ }
+ );
+ },
+
+ getComputedSize: function(options){
+
+
+ options = Object.merge({
+ styles: ['padding','border'],
+ planes: {
+ height: ['top','bottom'],
+ width: ['left','right']
+ },
+ mode: 'both'
+ }, options);
+
+ var styles = {},
+ size = {width: 0, height: 0},
+ dimensions;
+
+ if (options.mode == 'vertical'){
+ delete size.width;
+ delete options.planes.width;
+ } else if (options.mode == 'horizontal'){
+ delete size.height;
+ delete options.planes.height;
+ }
+
+ getStylesList(options.styles, options.planes).each(function(style){
+ styles[style] = this.getStyle(style).toInt();
+ }, this);
+
+ Object.each(options.planes, function(edges, plane){
+
+ var capitalized = plane.capitalize(),
+ style = this.getStyle(plane);
+
+ if (style == 'auto' && !dimensions) dimensions = this.getDimensions();
+
+ style = styles[plane] = (style == 'auto') ? dimensions[plane] : style.toInt();
+ size['total' + capitalized] = style;
+
+ edges.each(function(edge){
+ var edgesize = calculateEdgeSize(edge, styles);
+ size['computed' + edge.capitalize()] = edgesize;
+ size['total' + capitalized] += edgesize;
+ });
+
+ }, this);
+
+ return Object.append(size, styles);
+ }
+
+});
+
+})();
+
+
+/*
+---
+
+script: Element.Position.js
+
+name: Element.Position
+
+description: Extends the Element native object to include methods useful positioning elements relative to others.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+ - Jacob Thornton
+
+requires:
+ - Core/Options
+ - Core/Element.Dimensions
+ - Element.Measure
+
+provides: [Element.Position]
+
+...
+*/
+
+(function(original){
+
+var local = Element.Position = {
+
+ options: {/*
+ edge: false,
+ returnPos: false,
+ minimum: {x: 0, y: 0},
+ maximum: {x: 0, y: 0},
+ relFixedPosition: false,
+ ignoreMargins: false,
+ ignoreScroll: false,
+ allowNegative: false,*/
+ relativeTo: document.body,
+ position: {
+ x: 'center', //left, center, right
+ y: 'center' //top, center, bottom
+ },
+ offset: {x: 0, y: 0}
+ },
+
+ getOptions: function(element, options){
+ options = Object.merge({}, local.options, options);
+ local.setPositionOption(options);
+ local.setEdgeOption(options);
+ local.setOffsetOption(element, options);
+ local.setDimensionsOption(element, options);
+ return options;
+ },
+
+ setPositionOption: function(options){
+ options.position = local.getCoordinateFromValue(options.position);
+ },
+
+ setEdgeOption: function(options){
+ var edgeOption = local.getCoordinateFromValue(options.edge);
+ options.edge = edgeOption ? edgeOption :
+ (options.position.x == 'center' && options.position.y == 'center') ? {x: 'center', y: 'center'} :
+ {x: 'left', y: 'top'};
+ },
+
+ setOffsetOption: function(element, options){
+ var parentOffset = {x: 0, y: 0};
+ var parentScroll = {x: 0, y: 0};
+ var offsetParent = element.measure(function(){
+ return document.id(this.getOffsetParent());
+ });
+
+ if (!offsetParent || offsetParent == element.getDocument().body) return;
+
+ parentScroll = offsetParent.getScroll();
+ parentOffset = offsetParent.measure(function(){
+ var position = this.getPosition();
+ if (this.getStyle('position') == 'fixed'){
+ var scroll = window.getScroll();
+ position.x += scroll.x;
+ position.y += scroll.y;
+ }
+ return position;
+ });
+
+ options.offset = {
+ parentPositioned: offsetParent != document.id(options.relativeTo),
+ x: options.offset.x - parentOffset.x + parentScroll.x,
+ y: options.offset.y - parentOffset.y + parentScroll.y
+ };
+ },
+
+ setDimensionsOption: function(element, options){
+ options.dimensions = element.getDimensions({
+ computeSize: true,
+ styles: ['padding', 'border', 'margin']
+ });
+ },
+
+ getPosition: function(element, options){
+ var position = {};
+ options = local.getOptions(element, options);
+ var relativeTo = document.id(options.relativeTo) || document.body;
+
+ local.setPositionCoordinates(options, position, relativeTo);
+ if (options.edge) local.toEdge(position, options);
+
+ var offset = options.offset;
+ position.left = ((position.x >= 0 || offset.parentPositioned || options.allowNegative) ? position.x : 0).toInt();
+ position.top = ((position.y >= 0 || offset.parentPositioned || options.allowNegative) ? position.y : 0).toInt();
+
+ local.toMinMax(position, options);
+
+ if (options.relFixedPosition || relativeTo.getStyle('position') == 'fixed') local.toRelFixedPosition(relativeTo, position);
+ if (options.ignoreScroll) local.toIgnoreScroll(relativeTo, position);
+ if (options.ignoreMargins) local.toIgnoreMargins(position, options);
+
+ position.left = Math.ceil(position.left);
+ position.top = Math.ceil(position.top);
+ delete position.x;
+ delete position.y;
+
+ return position;
+ },
+
+ setPositionCoordinates: function(options, position, relativeTo){
+ var offsetY = options.offset.y,
+ offsetX = options.offset.x,
+ calc = (relativeTo == document.body) ? window.getScroll() : relativeTo.getPosition(),
+ top = calc.y,
+ left = calc.x,
+ winSize = window.getSize();
+
+ switch(options.position.x){
+ case 'left': position.x = left + offsetX; break;
+ case 'right': position.x = left + offsetX + relativeTo.offsetWidth; break;
+ default: position.x = left + ((relativeTo == document.body ? winSize.x : relativeTo.offsetWidth) / 2) + offsetX; break;
+ }
+
+ switch(options.position.y){
+ case 'top': position.y = top + offsetY; break;
+ case 'bottom': position.y = top + offsetY + relativeTo.offsetHeight; break;
+ default: position.y = top + ((relativeTo == document.body ? winSize.y : relativeTo.offsetHeight) / 2) + offsetY; break;
+ }
+ },
+
+ toMinMax: function(position, options){
+ var xy = {left: 'x', top: 'y'}, value;
+ ['minimum', 'maximum'].each(function(minmax){
+ ['left', 'top'].each(function(lr){
+ value = options[minmax] ? options[minmax][xy[lr]] : null;
+ if (value != null && ((minmax == 'minimum') ? position[lr] < value : position[lr] > value)) position[lr] = value;
+ });
+ });
+ },
+
+ toRelFixedPosition: function(relativeTo, position){
+ var winScroll = window.getScroll();
+ position.top += winScroll.y;
+ position.left += winScroll.x;
+ },
+
+ toIgnoreScroll: function(relativeTo, position){
+ var relScroll = relativeTo.getScroll();
+ position.top -= relScroll.y;
+ position.left -= relScroll.x;
+ },
+
+ toIgnoreMargins: function(position, options){
+ position.left += options.edge.x == 'right'
+ ? options.dimensions['margin-right']
+ : (options.edge.x != 'center'
+ ? -options.dimensions['margin-left']
+ : -options.dimensions['margin-left'] + ((options.dimensions['margin-right'] + options.dimensions['margin-left']) / 2));
+
+ position.top += options.edge.y == 'bottom'
+ ? options.dimensions['margin-bottom']
+ : (options.edge.y != 'center'
+ ? -options.dimensions['margin-top']
+ : -options.dimensions['margin-top'] + ((options.dimensions['margin-bottom'] + options.dimensions['margin-top']) / 2));
+ },
+
+ toEdge: function(position, options){
+ var edgeOffset = {},
+ dimensions = options.dimensions,
+ edge = options.edge;
+
+ switch(edge.x){
+ case 'left': edgeOffset.x = 0; break;
+ case 'right': edgeOffset.x = -dimensions.x - dimensions.computedRight - dimensions.computedLeft; break;
+ // center
+ default: edgeOffset.x = -(Math.round(dimensions.totalWidth / 2)); break;
+ }
+
+ switch(edge.y){
+ case 'top': edgeOffset.y = 0; break;
+ case 'bottom': edgeOffset.y = -dimensions.y - dimensions.computedTop - dimensions.computedBottom; break;
+ // center
+ default: edgeOffset.y = -(Math.round(dimensions.totalHeight / 2)); break;
+ }
+
+ position.x += edgeOffset.x;
+ position.y += edgeOffset.y;
+ },
+
+ getCoordinateFromValue: function(option){
+ if (typeOf(option) != 'string') return option;
+ option = option.toLowerCase();
+
+ return {
+ x: option.test('left') ? 'left'
+ : (option.test('right') ? 'right' : 'center'),
+ y: option.test(/upper|top/) ? 'top'
+ : (option.test('bottom') ? 'bottom' : 'center')
+ };
+ }
+
+};
+
+Element.implement({
+
+ position: function(options){
+ if (options && (options.x != null || options.y != null)){
+ return (original ? original.apply(this, arguments) : this);
+ }
+ var position = this.setStyle('position', 'absolute').calculatePosition(options);
+ return (options && options.returnPos) ? position : this.setStyles(position);
+ },
+
+ calculatePosition: function(options){
+ return local.getPosition(this, options);
+ }
+
+});
+
+})(Element.prototype.position);
+
+
+/*
+---
+
+script: IframeShim.js
+
+name: IframeShim
+
+description: Defines IframeShim, a class for obscuring select lists and flash objects in IE.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Element.Event
+ - Core/Element.Style
+ - Core/Options
+ - Core/Events
+ - Element.Position
+ - Class.Occlude
+
+provides: [IframeShim]
+
+...
+*/
+
+(function(){
+
+var browsers = false;
+
+
+this.IframeShim = new Class({
+
+ Implements: [Options, Events, Class.Occlude],
+
+ options: {
+ className: 'iframeShim',
+ src: 'javascript:false;document.write("");',
+ display: false,
+ zIndex: null,
+ margin: 0,
+ offset: {x: 0, y: 0},
+ browsers: browsers
+ },
+
+ property: 'IframeShim',
+
+ initialize: function(element, options){
+ this.element = document.id(element);
+ if (this.occlude()) return this.occluded;
+ this.setOptions(options);
+ this.makeShim();
+ return this;
+ },
+
+ makeShim: function(){
+ if (this.options.browsers){
+ var zIndex = this.element.getStyle('zIndex').toInt();
+
+ if (!zIndex){
+ zIndex = 1;
+ var pos = this.element.getStyle('position');
+ if (pos == 'static' || !pos) this.element.setStyle('position', 'relative');
+ this.element.setStyle('zIndex', zIndex);
+ }
+ zIndex = ((this.options.zIndex != null || this.options.zIndex === 0) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1;
+ if (zIndex < 0) zIndex = 1;
+ this.shim = new Element('iframe', {
+ src: this.options.src,
+ scrolling: 'no',
+ frameborder: 0,
+ styles: {
+ zIndex: zIndex,
+ position: 'absolute',
+ border: 'none',
+ filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
+ },
+ 'class': this.options.className
+ }).store('IframeShim', this);
+ var inject = (function(){
+ this.shim.inject(this.element, 'after');
+ this[this.options.display ? 'show' : 'hide']();
+ this.fireEvent('inject');
+ }).bind(this);
+ if (!IframeShim.ready) window.addEvent('load', inject);
+ else inject();
+ } else {
+ this.position = this.hide = this.show = this.dispose = Function.from(this);
+ }
+ },
+
+ position: function(){
+ if (!IframeShim.ready || !this.shim) return this;
+ var size = this.element.measure(function(){
+ return this.getSize();
+ });
+ if (this.options.margin != undefined){
+ size.x = size.x - (this.options.margin * 2);
+ size.y = size.y - (this.options.margin * 2);
+ this.options.offset.x += this.options.margin;
+ this.options.offset.y += this.options.margin;
+ }
+ this.shim.set({width: size.x, height: size.y}).position({
+ relativeTo: this.element,
+ offset: this.options.offset
+ });
+ return this;
+ },
+
+ hide: function(){
+ if (this.shim) this.shim.setStyle('display', 'none');
+ return this;
+ },
+
+ show: function(){
+ if (this.shim) this.shim.setStyle('display', 'block');
+ return this.position();
+ },
+
+ dispose: function(){
+ if (this.shim) this.shim.dispose();
+ return this;
+ },
+
+ destroy: function(){
+ if (this.shim) this.shim.destroy();
+ return this;
+ }
+
+});
+
+})();
+
+window.addEvent('load', function(){
+ IframeShim.ready = true;
+});
+
+
+/*
+---
+
+script: Mask.js
+
+name: Mask
+
+description: Creates a mask element to cover another.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Options
+ - Core/Events
+ - Core/Element.Event
+ - Class.Binds
+ - Element.Position
+ - IframeShim
+
+provides: [Mask]
+
+...
+*/
+
+var Mask = new Class({
+
+ Implements: [Options, Events],
+
+ Binds: ['position'],
+
+ options: {/*
+ onShow: function(){},
+ onHide: function(){},
+ onDestroy: function(){},
+ onClick: function(event){},
+ inject: {
+ where: 'after',
+ target: null,
+ },
+ hideOnClick: false,
+ id: null,
+ destroyOnHide: false,*/
+ style: {},
+ 'class': 'mask',
+ maskMargins: false,
+ useIframeShim: true,
+ iframeShimOptions: {}
+ },
+
+ initialize: function(target, options){
+ this.target = document.id(target) || document.id(document.body);
+ this.target.store('mask', this);
+ this.setOptions(options);
+ this.render();
+ this.inject();
+ },
+
+ render: function(){
+ this.element = new Element('div', {
+ 'class': this.options['class'],
+ id: this.options.id || 'mask-' + String.uniqueID(),
+ styles: Object.merge({}, this.options.style, {
+ display: 'none'
+ }),
+ events: {
+ click: function(event){
+ this.fireEvent('click', event);
+ if (this.options.hideOnClick) this.hide();
+ }.bind(this)
+ }
+ });
+
+ this.hidden = true;
+ },
+
+ toElement: function(){
+ return this.element;
+ },
+
+ inject: function(target, where){
+ where = where || (this.options.inject ? this.options.inject.where : '') || (this.target == document.body ? 'inside' : 'after');
+ target = target || (this.options.inject && this.options.inject.target) || this.target;
+
+ this.element.inject(target, where);
+
+ if (this.options.useIframeShim){
+ this.shim = new IframeShim(this.element, this.options.iframeShimOptions);
+
+ this.addEvents({
+ show: this.shim.show.bind(this.shim),
+ hide: this.shim.hide.bind(this.shim),
+ destroy: this.shim.destroy.bind(this.shim)
+ });
+ }
+ },
+
+ position: function(){
+ this.resize(this.options.width, this.options.height);
+
+ this.element.position({
+ relativeTo: this.target,
+ position: 'topLeft',
+ ignoreMargins: !this.options.maskMargins,
+ ignoreScroll: this.target == document.body
+ });
+
+ return this;
+ },
+
+ resize: function(x, y){
+ var opt = {
+ styles: ['padding', 'border']
+ };
+ if (this.options.maskMargins) opt.styles.push('margin');
+
+ var dim = this.target.getComputedSize(opt);
+ if (this.target == document.body){
+ this.element.setStyles({width: 0, height: 0});
+ var win = window.getScrollSize();
+ if (dim.totalHeight < win.y) dim.totalHeight = win.y;
+ if (dim.totalWidth < win.x) dim.totalWidth = win.x;
+ }
+ this.element.setStyles({
+ width: Array.pick([x, dim.totalWidth, dim.x]),
+ height: Array.pick([y, dim.totalHeight, dim.y])
+ });
+
+ return this;
+ },
+
+ show: function(){
+ if (!this.hidden) return this;
+
+ window.addEvent('resize', this.position);
+ this.position();
+ this.showMask.apply(this, arguments);
+
+ return this;
+ },
+
+ showMask: function(){
+ this.element.setStyle('display', 'block');
+ this.hidden = false;
+ this.fireEvent('show');
+ },
+
+ hide: function(){
+ if (this.hidden) return this;
+
+ window.removeEvent('resize', this.position);
+ this.hideMask.apply(this, arguments);
+ if (this.options.destroyOnHide) return this.destroy();
+
+ return this;
+ },
+
+ hideMask: function(){
+ this.element.setStyle('display', 'none');
+ this.hidden = true;
+ this.fireEvent('hide');
+ },
+
+ toggle: function(){
+ this[this.hidden ? 'show' : 'hide']();
+ },
+
+ destroy: function(){
+ this.hide();
+ this.element.destroy();
+ this.fireEvent('destroy');
+ this.target.eliminate('mask');
+ }
+
+});
+
+Element.Properties.mask = {
+
+ set: function(options){
+ var mask = this.retrieve('mask');
+ if (mask) mask.destroy();
+ return this.eliminate('mask').store('mask:options', options);
+ },
+
+ get: function(){
+ var mask = this.retrieve('mask');
+ if (!mask){
+ mask = new Mask(this, this.retrieve('mask:options'));
+ this.store('mask', mask);
+ }
+ return mask;
+ }
+
+};
+
+Element.implement({
+
+ mask: function(options){
+ if (options) this.set('mask', options);
+ this.get('mask').show();
+ return this;
+ },
+
+ unmask: function(){
+ this.get('mask').hide();
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+script: Spinner.js
+
+name: Spinner
+
+description: Adds a semi-transparent overlay over a dom element with a spinnin ajax icon.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Fx.Tween
+ - Core/Request
+ - Class.refactor
+ - Mask
+
+provides: [Spinner]
+
+...
+*/
+
+var Spinner = new Class({
+
+ Extends: Mask,
+
+ Implements: Chain,
+
+ options: {/*
+ message: false,*/
+ 'class': 'spinner',
+ containerPosition: {},
+ content: {
+ 'class': 'spinner-content'
+ },
+ messageContainer: {
+ 'class': 'spinner-msg'
+ },
+ img: {
+ 'class': 'spinner-img'
+ },
+ fxOptions: {
+ link: 'chain'
+ }
+ },
+
+ initialize: function(target, options){
+ this.target = document.id(target) || document.id(document.body);
+ this.target.store('spinner', this);
+ this.setOptions(options);
+ this.render();
+ this.inject();
+
+ // Add this to events for when noFx is true; parent methods handle hide/show.
+ var deactivate = function(){ this.active = false; }.bind(this);
+ this.addEvents({
+ hide: deactivate,
+ show: deactivate
+ });
+ },
+
+ render: function(){
+ this.parent();
+
+ this.element.set('id', this.options.id || 'spinner-' + String.uniqueID());
+
+ this.content = document.id(this.options.content) || new Element('div', this.options.content);
+ this.content.inject(this.element);
+
+ if (this.options.message){
+ this.msg = document.id(this.options.message) || new Element('p', this.options.messageContainer).appendText(this.options.message);
+ this.msg.inject(this.content);
+ }
+
+ if (this.options.img){
+ this.img = document.id(this.options.img) || new Element('div', this.options.img);
+ this.img.inject(this.content);
+ }
+
+ this.element.set('tween', this.options.fxOptions);
+ },
+
+ show: function(noFx){
+ if (this.active) return this.chain(this.show.bind(this));
+ if (!this.hidden){
+ this.callChain.delay(20, this);
+ return this;
+ }
+
+ this.target.set('aria-busy', 'true');
+ this.active = true;
+
+ return this.parent(noFx);
+ },
+
+ showMask: function(noFx){
+ var pos = function(){
+ this.content.position(Object.merge({
+ relativeTo: this.element
+ }, this.options.containerPosition));
+ }.bind(this);
+
+ if (noFx){
+ this.parent();
+ pos();
+ } else {
+ if (!this.options.style.opacity) this.options.style.opacity = this.element.getStyle('opacity').toFloat();
+ this.element.setStyles({
+ display: 'block',
+ opacity: 0
+ }).tween('opacity', this.options.style.opacity);
+ pos();
+ this.hidden = false;
+ this.fireEvent('show');
+ this.callChain();
+ }
+ },
+
+ hide: function(noFx){
+ if (this.active) return this.chain(this.hide.bind(this));
+ if (this.hidden){
+ this.callChain.delay(20, this);
+ return this;
+ }
+
+ this.target.set('aria-busy', 'false');
+ this.active = true;
+
+ return this.parent(noFx);
+ },
+
+ hideMask: function(noFx){
+ if (noFx) return this.parent();
+ this.element.tween('opacity', 0).get('tween').chain(function(){
+ this.element.setStyle('display', 'none');
+ this.hidden = true;
+ this.fireEvent('hide');
+ this.callChain();
+ }.bind(this));
+ },
+
+ destroy: function(){
+ this.content.destroy();
+ this.parent();
+ this.target.eliminate('spinner');
+ }
+
+});
+
+Request = Class.refactor(Request, {
+
+ options: {
+ useSpinner: false,
+ spinnerOptions: {},
+ spinnerTarget: false
+ },
+
+ initialize: function(options){
+ this._send = this.send;
+ this.send = function(options){
+ var spinner = this.getSpinner();
+ if (spinner) spinner.chain(this._send.pass(options, this)).show();
+ else this._send(options);
+ return this;
+ };
+ this.previous(options);
+ },
+
+ getSpinner: function(){
+ if (!this.spinner){
+ var update = document.id(this.options.spinnerTarget) || document.id(this.options.update);
+ if (this.options.useSpinner && update){
+ update.set('spinner', this.options.spinnerOptions);
+ var spinner = this.spinner = update.get('spinner');
+ ['complete', 'exception', 'cancel'].each(function(event){
+ this.addEvent(event, spinner.hide.bind(spinner));
+ }, this);
+ }
+ }
+ return this.spinner;
+ }
+
+});
+
+Element.Properties.spinner = {
+
+ set: function(options){
+ var spinner = this.retrieve('spinner');
+ if (spinner) spinner.destroy();
+ return this.eliminate('spinner').store('spinner:options', options);
+ },
+
+ get: function(){
+ var spinner = this.retrieve('spinner');
+ if (!spinner){
+ spinner = new Spinner(this, this.retrieve('spinner:options'));
+ this.store('spinner', spinner);
+ }
+ return spinner;
+ }
+
+};
+
+Element.implement({
+
+ spin: function(options){
+ if (options) this.set('spinner', options);
+ this.get('spinner').show();
+ return this;
+ },
+
+ unspin: function(){
+ this.get('spinner').hide();
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+script: String.QueryString.js
+
+name: String.QueryString
+
+description: Methods for dealing with URI query strings.
+
+license: MIT-style license
+
+authors:
+ - Sebastian Markbåge
+ - Aaron Newton
+ - Lennart Pilon
+ - Valerio Proietti
+
+requires:
+ - Core/Array
+ - Core/String
+ - MooTools.More
+
+provides: [String.QueryString]
+
+...
+*/
+
+String.implement({
+
+ parseQueryString: function(decodeKeys, decodeValues){
+ if (decodeKeys == null) decodeKeys = true;
+ if (decodeValues == null) decodeValues = true;
+
+ var vars = this.split(/[&;]/),
+ object = {};
+ if (!vars.length) return object;
+
+ vars.each(function(val){
+ var index = val.indexOf('=') + 1,
+ value = index ? val.substr(index) : '',
+ keys = index ? val.substr(0, index - 1).match(/([^\]\[]+|(\B)(?=\]))/g) : [val],
+ obj = object;
+ if (!keys) return;
+ if (decodeValues) value = decodeURIComponent(value);
+ keys.each(function(key, i){
+ if (decodeKeys) key = decodeURIComponent(key);
+ var current = obj[key];
+
+ if (i < keys.length - 1) obj = obj[key] = current || {};
+ else if (typeOf(current) == 'array') current.push(value);
+ else obj[key] = current != null ? [current, value] : value;
+ });
+ });
+
+ return object;
+ },
+
+ cleanQueryString: function(method){
+ return this.split('&').filter(function(val){
+ var index = val.indexOf('='),
+ key = index < 0 ? '' : val.substr(0, index),
+ value = val.substr(index + 1);
+
+ return method ? method.call(null, key, value) : (value || value === 0);
+ }).join('&');
+ }
+
+});
+
+
+/*
+---
+
+name: Events.Pseudos
+
+description: Adds the functionality to add pseudo events
+
+license: MIT-style license
+
+authors:
+ - Arian Stolwijk
+
+requires: [Core/Class.Extras, Core/Slick.Parser, MooTools.More]
+
+provides: [Events.Pseudos]
+
+...
+*/
+
+(function(){
+
+Events.Pseudos = function(pseudos, addEvent, removeEvent){
+
+ var storeKey = '_monitorEvents:';
+
+ var storageOf = function(object){
+ return {
+ store: object.store ? function(key, value){
+ object.store(storeKey + key, value);
+ } : function(key, value){
+ (object._monitorEvents || (object._monitorEvents = {}))[key] = value;
+ },
+ retrieve: object.retrieve ? function(key, dflt){
+ return object.retrieve(storeKey + key, dflt);
+ } : function(key, dflt){
+ if (!object._monitorEvents) return dflt;
+ return object._monitorEvents[key] || dflt;
+ }
+ };
+ };
+
+ var splitType = function(type){
+ if (type.indexOf(':') == -1 || !pseudos) return null;
+
+ var parsed = Slick.parse(type).expressions[0][0],
+ parsedPseudos = parsed.pseudos,
+ l = parsedPseudos.length,
+ splits = [];
+
+ while (l--){
+ var pseudo = parsedPseudos[l].key,
+ listener = pseudos[pseudo];
+ if (listener != null) splits.push({
+ event: parsed.tag,
+ value: parsedPseudos[l].value,
+ pseudo: pseudo,
+ original: type,
+ listener: listener
+ });
+ }
+ return splits.length ? splits : null;
+ };
+
+ return {
+
+ addEvent: function(type, fn, internal){
+ var split = splitType(type);
+ if (!split) return addEvent.call(this, type, fn, internal);
+
+ var storage = storageOf(this),
+ events = storage.retrieve(type, []),
+ eventType = split[0].event,
+ args = Array.slice(arguments, 2),
+ stack = fn,
+ self = this;
+
+ split.each(function(item){
+ var listener = item.listener,
+ stackFn = stack;
+ if (listener == false) eventType += ':' + item.pseudo + '(' + item.value + ')';
+ else stack = function(){
+ listener.call(self, item, stackFn, arguments, stack);
+ };
+ });
+
+ events.include({type: eventType, event: fn, monitor: stack});
+ storage.store(type, events);
+
+ if (type != eventType) addEvent.apply(this, [type, fn].concat(args));
+ return addEvent.apply(this, [eventType, stack].concat(args));
+ },
+
+ removeEvent: function(type, fn){
+ var split = splitType(type);
+ if (!split) return removeEvent.call(this, type, fn);
+
+ var storage = storageOf(this),
+ events = storage.retrieve(type);
+ if (!events) return this;
+
+ var args = Array.slice(arguments, 2);
+
+ removeEvent.apply(this, [type, fn].concat(args));
+ events.each(function(monitor, i){
+ if (!fn || monitor.event == fn) removeEvent.apply(this, [monitor.type, monitor.monitor].concat(args));
+ delete events[i];
+ }, this);
+
+ storage.store(type, events);
+ return this;
+ }
+
+ };
+
+};
+
+var pseudos = {
+
+ once: function(split, fn, args, monitor){
+ fn.apply(this, args);
+ this.removeEvent(split.event, monitor)
+ .removeEvent(split.original, fn);
+ },
+
+ throttle: function(split, fn, args){
+ if (!fn._throttled){
+ fn.apply(this, args);
+ fn._throttled = setTimeout(function(){
+ fn._throttled = false;
+ }, split.value || 250);
+ }
+ },
+
+ pause: function(split, fn, args){
+ clearTimeout(fn._pause);
+ fn._pause = fn.delay(split.value || 250, this, args);
+ }
+
+};
+
+Events.definePseudo = function(key, listener){
+ pseudos[key] = listener;
+ return this;
+};
+
+Events.lookupPseudo = function(key){
+ return pseudos[key];
+};
+
+var proto = Events.prototype;
+Events.implement(Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
+
+['Request', 'Fx'].each(function(klass){
+ if (this[klass]) this[klass].implement(Events.prototype);
+});
+
+})();
+
+
+/*
+---
+
+name: Element.Event.Pseudos
+
+description: Adds the functionality to add pseudo events for Elements
+
+license: MIT-style license
+
+authors:
+ - Arian Stolwijk
+
+requires: [Core/Element.Event, Core/Element.Delegation, Events.Pseudos]
+
+provides: [Element.Event.Pseudos, Element.Delegation.Pseudo]
+
+...
+*/
+
+(function(){
+
+var pseudos = {relay: false},
+ copyFromEvents = ['once', 'throttle', 'pause'],
+ count = copyFromEvents.length;
+
+while (count--) pseudos[copyFromEvents[count]] = Events.lookupPseudo(copyFromEvents[count]);
+
+DOMEvent.definePseudo = function(key, listener){
+ pseudos[key] = listener;
+ return this;
+};
+
+var proto = Element.prototype;
+[Element, Window, Document].invoke('implement', Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
+
+})();
+
+
+/*
+---
+
+script: Form.Request.js
+
+name: Form.Request
+
+description: Handles the basic functionality of submitting a form and updating a dom element with the result.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Request.HTML
+ - Class.Binds
+ - Class.Occlude
+ - Spinner
+ - String.QueryString
+ - Element.Delegation.Pseudo
+
+provides: [Form.Request]
+
+...
+*/
+
+if (!window.Form) window.Form = {};
+
+(function(){
+
+ Form.Request = new Class({
+
+ Binds: ['onSubmit', 'onFormValidate'],
+
+ Implements: [Options, Events, Class.Occlude],
+
+ options: {/*
+ onFailure: function(){},
+ onSuccess: function(){}, // aliased to onComplete,
+ onSend: function(){}*/
+ requestOptions: {
+ evalScripts: true,
+ useSpinner: true,
+ emulation: false,
+ link: 'ignore'
+ },
+ sendButtonClicked: true,
+ extraData: {},
+ resetForm: true
+ },
+
+ property: 'form.request',
+
+ initialize: function(form, target, options){
+ this.element = document.id(form);
+ if (this.occlude()) return this.occluded;
+ this.setOptions(options)
+ .setTarget(target)
+ .attach();
+ },
+
+ setTarget: function(target){
+ this.target = document.id(target);
+ if (!this.request){
+ this.makeRequest();
+ } else {
+ this.request.setOptions({
+ update: this.target
+ });
+ }
+ return this;
+ },
+
+ toElement: function(){
+ return this.element;
+ },
+
+ makeRequest: function(){
+ var self = this;
+ this.request = new Request.HTML(Object.merge({
+ update: this.target,
+ emulation: false,
+ spinnerTarget: this.element,
+ method: this.element.get('method') || 'post'
+ }, this.options.requestOptions)).addEvents({
+ success: function(tree, elements, html, javascript){
+ ['complete', 'success'].each(function(evt){
+ self.fireEvent(evt, [self.target, tree, elements, html, javascript]);
+ });
+ },
+ failure: function(){
+ self.fireEvent('complete', arguments).fireEvent('failure', arguments);
+ },
+ exception: function(){
+ self.fireEvent('failure', arguments);
+ }
+ });
+ return this.attachReset();
+ },
+
+ attachReset: function(){
+ if (!this.options.resetForm) return this;
+ this.request.addEvent('success', function(){
+ Function.attempt(function(){
+ this.element.reset();
+ }.bind(this));
+ if (window.OverText) OverText.update();
+ }.bind(this));
+ return this;
+ },
+
+ attach: function(attach){
+ var method = (attach != false) ? 'addEvent' : 'removeEvent';
+ this.element[method]('click:relay(button, input[type=submit])', this.saveClickedButton.bind(this));
+
+ var fv = this.element.retrieve('validator');
+ if (fv) fv[method]('onFormValidate', this.onFormValidate);
+ else this.element[method]('submit', this.onSubmit);
+
+ return this;
+ },
+
+ detach: function(){
+ return this.attach(false);
+ },
+
+ //public method
+ enable: function(){
+ return this.attach();
+ },
+
+ //public method
+ disable: function(){
+ return this.detach();
+ },
+
+ onFormValidate: function(valid, form, event){
+ //if there's no event, then this wasn't a submit event
+ if (!event) return;
+ var fv = this.element.retrieve('validator');
+ if (valid || (fv && !fv.options.stopOnFailure)){
+ event.stop();
+ this.send();
+ }
+ },
+
+ onSubmit: function(event){
+ var fv = this.element.retrieve('validator');
+ if (fv){
+ //form validator was created after Form.Request
+ this.element.removeEvent('submit', this.onSubmit);
+ fv.addEvent('onFormValidate', this.onFormValidate);
+ fv.validate(event);
+ return;
+ }
+ if (event) event.stop();
+ this.send();
+ },
+
+ saveClickedButton: function(event, target){
+ var targetName = target.get('name');
+ if (!targetName || !this.options.sendButtonClicked) return;
+ this.options.extraData[targetName] = target.get('value') || true;
+ this.clickedCleaner = function(){
+ delete this.options.extraData[targetName];
+ this.clickedCleaner = function(){};
+ }.bind(this);
+ },
+
+ clickedCleaner: function(){},
+
+ send: function(){
+ var str = this.element.toQueryString().trim(),
+ data = Object.toQueryString(this.options.extraData);
+
+ if (str) str += "&" + data;
+ else str = data;
+
+ this.fireEvent('send', [this.element, str.parseQueryString()]);
+ this.request.send({
+ data: str,
+ url: this.options.requestOptions.url || this.element.get('action')
+ });
+ this.clickedCleaner();
+ return this;
+ }
+
+ });
+
+ Element.implement('formUpdate', function(update, options){
+ var fq = this.retrieve('form.request');
+ if (!fq){
+ fq = new Form.Request(this, update, options);
+ } else {
+ if (update) fq.setTarget(update);
+ if (options) fq.setOptions(options).makeRequest();
+ }
+ fq.send();
+ return this;
+ });
+
+})();
+
+
+/*
+---
+
+script: Element.Shortcuts.js
+
+name: Element.Shortcuts
+
+description: Extends the Element native object to include some shortcut methods.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Element.Style
+ - MooTools.More
+
+provides: [Element.Shortcuts]
+
+...
+*/
+
+Element.implement({
+
+ isDisplayed: function(){
+ return this.getStyle('display') != 'none';
+ },
+
+ isVisible: function(){
+ var w = this.offsetWidth,
+ h = this.offsetHeight;
+ return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.style.display != 'none';
+ },
+
+ toggle: function(){
+ return this[this.isDisplayed() ? 'hide' : 'show']();
+ },
+
+ hide: function(){
+ var d;
+ try {
+ //IE fails here if the element is not in the dom
+ d = this.getStyle('display');
+ } catch(e){}
+ if (d == 'none') return this;
+ return this.store('element:_originalDisplay', d || '').setStyle('display', 'none');
+ },
+
+ show: function(display){
+ if (!display && this.isDisplayed()) return this;
+ display = display || this.retrieve('element:_originalDisplay') || 'block';
+ return this.setStyle('display', (display == 'none') ? 'block' : display);
+ },
+
+ swapClass: function(remove, add){
+ return this.removeClass(remove).addClass(add);
+ }
+
+});
+
+Document.implement({
+
+ clearSelection: function(){
+ if (window.getSelection){
+ var selection = window.getSelection();
+ if (selection && selection.removeAllRanges) selection.removeAllRanges();
+ } else if (document.selection && document.selection.empty){
+ try {
+ //IE fails here if selected element is not in dom
+ document.selection.empty();
+ } catch(e){}
+ }
+ }
+
+});
+
+
+/*
+---
+
+script: Fx.Reveal.js
+
+name: Fx.Reveal
+
+description: Defines Fx.Reveal, a class that shows and hides elements with a transition.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Fx.Morph
+ - Element.Shortcuts
+ - Element.Measure
+
+provides: [Fx.Reveal]
+
+...
+*/
+
+(function(){
+
+
+var hideTheseOf = function(object){
+ var hideThese = object.options.hideInputs;
+ if (window.OverText){
+ var otClasses = [null];
+ OverText.each(function(ot){
+ otClasses.include('.' + ot.options.labelClass);
+ });
+ if (otClasses) hideThese += otClasses.join(', ');
+ }
+ return (hideThese) ? object.element.getElements(hideThese) : null;
+};
+
+
+Fx.Reveal = new Class({
+
+ Extends: Fx.Morph,
+
+ options: {/*
+ onShow: function(thisElement){},
+ onHide: function(thisElement){},
+ onComplete: function(thisElement){},
+ heightOverride: null,
+ widthOverride: null,*/
+ link: 'cancel',
+ styles: ['padding', 'border', 'margin'],
+ transitionOpacity: 'opacity' in document.documentElement,
+ mode: 'vertical',
+ display: function(){
+ return this.element.get('tag') != 'tr' ? 'block' : 'table-row';
+ },
+ opacity: 1,
+ hideInputs: !('opacity' in document.documentElement) ? 'select, input, textarea, object, embed' : null
+ },
+
+ dissolve: function(){
+ if (!this.hiding && !this.showing){
+ if (this.element.getStyle('display') != 'none'){
+ this.hiding = true;
+ this.showing = false;
+ this.hidden = true;
+ this.cssText = this.element.style.cssText;
+
+ var startStyles = this.element.getComputedSize({
+ styles: this.options.styles,
+ mode: this.options.mode
+ });
+ if (this.options.transitionOpacity) startStyles.opacity = this.options.opacity;
+
+ var zero = {};
+ Object.each(startStyles, function(style, name){
+ zero[name] = [style, 0];
+ });
+
+ this.element.setStyles({
+ display: Function.from(this.options.display).call(this),
+ overflow: 'hidden'
+ });
+
+ var hideThese = hideTheseOf(this);
+ if (hideThese) hideThese.setStyle('visibility', 'hidden');
+
+ this.$chain.unshift(function(){
+ if (this.hidden){
+ this.hiding = false;
+ this.element.style.cssText = this.cssText;
+ this.element.setStyle('display', 'none');
+ if (hideThese) hideThese.setStyle('visibility', 'visible');
+ }
+ this.fireEvent('hide', this.element);
+ this.callChain();
+ }.bind(this));
+
+ this.start(zero);
+ } else {
+ this.callChain.delay(10, this);
+ this.fireEvent('complete', this.element);
+ this.fireEvent('hide', this.element);
+ }
+ } else if (this.options.link == 'chain'){
+ this.chain(this.dissolve.bind(this));
+ } else if (this.options.link == 'cancel' && !this.hiding){
+ this.cancel();
+ this.dissolve();
+ }
+ return this;
+ },
+
+ reveal: function(){
+ if (!this.showing && !this.hiding){
+ if (this.element.getStyle('display') == 'none'){
+ this.hiding = false;
+ this.showing = true;
+ this.hidden = false;
+ this.cssText = this.element.style.cssText;
+
+ var startStyles;
+ this.element.measure(function(){
+ startStyles = this.element.getComputedSize({
+ styles: this.options.styles,
+ mode: this.options.mode
+ });
+ }.bind(this));
+ if (this.options.heightOverride != null) startStyles.height = this.options.heightOverride.toInt();
+ if (this.options.widthOverride != null) startStyles.width = this.options.widthOverride.toInt();
+ if (this.options.transitionOpacity){
+ this.element.setStyle('opacity', 0);
+ startStyles.opacity = this.options.opacity;
+ }
+
+ var zero = {
+ height: 0,
+ display: Function.from(this.options.display).call(this)
+ };
+ Object.each(startStyles, function(style, name){
+ zero[name] = 0;
+ });
+ zero.overflow = 'hidden';
+
+ this.element.setStyles(zero);
+
+ var hideThese = hideTheseOf(this);
+ if (hideThese) hideThese.setStyle('visibility', 'hidden');
+
+ this.$chain.unshift(function(){
+ this.element.style.cssText = this.cssText;
+ this.element.setStyle('display', Function.from(this.options.display).call(this));
+ if (!this.hidden) this.showing = false;
+ if (hideThese) hideThese.setStyle('visibility', 'visible');
+ this.callChain();
+ this.fireEvent('show', this.element);
+ }.bind(this));
+
+ this.start(startStyles);
+ } else {
+ this.callChain();
+ this.fireEvent('complete', this.element);
+ this.fireEvent('show', this.element);
+ }
+ } else if (this.options.link == 'chain'){
+ this.chain(this.reveal.bind(this));
+ } else if (this.options.link == 'cancel' && !this.showing){
+ this.cancel();
+ this.reveal();
+ }
+ return this;
+ },
+
+ toggle: function(){
+ if (this.element.getStyle('display') == 'none'){
+ this.reveal();
+ } else {
+ this.dissolve();
+ }
+ return this;
+ },
+
+ cancel: function(){
+ this.parent.apply(this, arguments);
+ if (this.cssText != null) this.element.style.cssText = this.cssText;
+ this.hiding = false;
+ this.showing = false;
+ return this;
+ }
+
+});
+
+Element.Properties.reveal = {
+
+ set: function(options){
+ this.get('reveal').cancel().setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var reveal = this.retrieve('reveal');
+ if (!reveal){
+ reveal = new Fx.Reveal(this);
+ this.store('reveal', reveal);
+ }
+ return reveal;
+ }
+
+};
+
+Element.Properties.dissolve = Element.Properties.reveal;
+
+Element.implement({
+
+ reveal: function(options){
+ this.get('reveal').setOptions(options).reveal();
+ return this;
+ },
+
+ dissolve: function(options){
+ this.get('reveal').setOptions(options).dissolve();
+ return this;
+ },
+
+ nix: function(options){
+ var params = Array.link(arguments, {destroy: Type.isBoolean, options: Type.isObject});
+ this.get('reveal').setOptions(options).dissolve().chain(function(){
+ this[params.destroy ? 'destroy' : 'dispose']();
+ }.bind(this));
+ return this;
+ },
+
+ wink: function(){
+ var params = Array.link(arguments, {duration: Type.isNumber, options: Type.isObject});
+ var reveal = this.get('reveal').setOptions(params.options);
+ reveal.reveal().chain(function(){
+ (function(){
+ reveal.dissolve();
+ }).delay(params.duration || 2000);
+ });
+ }
+
+});
+
+})();
+
+
+/*
+---
+
+script: Drag.js
+
+name: Drag
+
+description: The base Drag Class. Can be used to drag and resize Elements using mouse events.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+ - Tom Occhinno
+ - Jan Kassens
+
+requires:
+ - Core/Events
+ - Core/Options
+ - Core/Element.Event
+ - Core/Element.Style
+ - Core/Element.Dimensions
+ - MooTools.More
+
+provides: [Drag]
+...
+
+*/
+
+var Drag = new Class({
+
+ Implements: [Events, Options],
+
+ options: {/*
+ onBeforeStart: function(thisElement){},
+ onStart: function(thisElement, event){},
+ onSnap: function(thisElement){},
+ onDrag: function(thisElement, event){},
+ onCancel: function(thisElement){},
+ onComplete: function(thisElement, event){},*/
+ snap: 6,
+ unit: 'px',
+ grid: false,
+ style: true,
+ limit: false,
+ handle: false,
+ invert: false,
+ preventDefault: false,
+ stopPropagation: false,
+ modifiers: {x: 'left', y: 'top'}
+ },
+
+ initialize: function(){
+ var params = Array.link(arguments, {
+ 'options': Type.isObject,
+ 'element': function(obj){
+ return obj != null;
+ }
+ });
+
+ this.element = document.id(params.element);
+ this.document = this.element.getDocument();
+ this.setOptions(params.options || {});
+ var htype = typeOf(this.options.handle);
+ this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
+ this.mouse = {'now': {}, 'pos': {}};
+ this.value = {'start': {}, 'now': {}};
+
+ this.selection = 'selectstart' in document ? 'selectstart' : 'mousedown';
+
+
+ if ('ondragstart' in document && !('FileReader' in window) && !Drag.ondragstartFixed){
+ document.ondragstart = Function.from(false);
+ Drag.ondragstartFixed = true;
+ }
+
+ this.bound = {
+ start: this.start.bind(this),
+ check: this.check.bind(this),
+ drag: this.drag.bind(this),
+ stop: this.stop.bind(this),
+ cancel: this.cancel.bind(this),
+ eventStop: Function.from(false)
+ };
+ this.attach();
+ },
+
+ attach: function(){
+ this.handles.addEvent('mousedown', this.bound.start);
+ return this;
+ },
+
+ detach: function(){
+ this.handles.removeEvent('mousedown', this.bound.start);
+ return this;
+ },
+
+ start: function(event){
+ var options = this.options;
+
+ if (event.rightClick) return;
+
+ if (options.preventDefault) event.preventDefault();
+ if (options.stopPropagation) event.stopPropagation();
+ this.mouse.start = event.page;
+
+ this.fireEvent('beforeStart', this.element);
+
+ var limit = options.limit;
+ this.limit = {x: [], y: []};
+
+ var z, coordinates;
+ for (z in options.modifiers){
+ if (!options.modifiers[z]) continue;
+
+ var style = this.element.getStyle(options.modifiers[z]);
+
+ // Some browsers (IE and Opera) don't always return pixels.
+ if (style && !style.match(/px$/)){
+ if (!coordinates) coordinates = this.element.getCoordinates(this.element.getOffsetParent());
+ style = coordinates[options.modifiers[z]];
+ }
+
+ if (options.style) this.value.now[z] = (style || 0).toInt();
+ else this.value.now[z] = this.element[options.modifiers[z]];
+
+ if (options.invert) this.value.now[z] *= -1;
+
+ this.mouse.pos[z] = event.page[z] - this.value.now[z];
+
+ if (limit && limit[z]){
+ var i = 2;
+ while (i--){
+ var limitZI = limit[z][i];
+ if (limitZI || limitZI === 0) this.limit[z][i] = (typeof limitZI == 'function') ? limitZI() : limitZI;
+ }
+ }
+ }
+
+ if (typeOf(this.options.grid) == 'number') this.options.grid = {
+ x: this.options.grid,
+ y: this.options.grid
+ };
+
+ var events = {
+ mousemove: this.bound.check,
+ mouseup: this.bound.cancel
+ };
+ events[this.selection] = this.bound.eventStop;
+ this.document.addEvents(events);
+ },
+
+ check: function(event){
+ if (this.options.preventDefault) event.preventDefault();
+ var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
+ if (distance > this.options.snap){
+ this.cancel();
+ this.document.addEvents({
+ mousemove: this.bound.drag,
+ mouseup: this.bound.stop
+ });
+ this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
+ }
+ },
+
+ drag: function(event){
+ var options = this.options;
+
+ if (options.preventDefault) event.preventDefault();
+ this.mouse.now = event.page;
+
+ for (var z in options.modifiers){
+ if (!options.modifiers[z]) continue;
+ this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
+
+ if (options.invert) this.value.now[z] *= -1;
+
+ if (options.limit && this.limit[z]){
+ if ((this.limit[z][1] || this.limit[z][1] === 0) && (this.value.now[z] > this.limit[z][1])){
+ this.value.now[z] = this.limit[z][1];
+ } else if ((this.limit[z][0] || this.limit[z][0] === 0) && (this.value.now[z] < this.limit[z][0])){
+ this.value.now[z] = this.limit[z][0];
+ }
+ }
+
+ if (options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % options.grid[z]);
+
+ if (options.style) this.element.setStyle(options.modifiers[z], this.value.now[z] + options.unit);
+ else this.element[options.modifiers[z]] = this.value.now[z];
+ }
+
+ this.fireEvent('drag', [this.element, event]);
+ },
+
+ cancel: function(event){
+ this.document.removeEvents({
+ mousemove: this.bound.check,
+ mouseup: this.bound.cancel
+ });
+ if (event){
+ this.document.removeEvent(this.selection, this.bound.eventStop);
+ this.fireEvent('cancel', this.element);
+ }
+ },
+
+ stop: function(event){
+ var events = {
+ mousemove: this.bound.drag,
+ mouseup: this.bound.stop
+ };
+ events[this.selection] = this.bound.eventStop;
+ this.document.removeEvents(events);
+ if (event) this.fireEvent('complete', [this.element, event]);
+ }
+
+});
+
+Element.implement({
+
+ makeResizable: function(options){
+ var drag = new Drag(this, Object.merge({
+ modifiers: {
+ x: 'width',
+ y: 'height'
+ }
+ }, options));
+
+ this.store('resizer', drag);
+ return drag.addEvent('drag', function(){
+ this.fireEvent('resize', drag);
+ }.bind(this));
+ }
+
+});
+
+
+/*
+---
+
+script: Drag.Move.js
+
+name: Drag.Move
+
+description: A Drag extension that provides support for the constraining of draggables to containers and droppables.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+ - Tom Occhinno
+ - Jan Kassens
+ - Aaron Newton
+ - Scott Kyle
+
+requires:
+ - Core/Element.Dimensions
+ - Drag
+
+provides: [Drag.Move]
+
+...
+*/
+
+Drag.Move = new Class({
+
+ Extends: Drag,
+
+ options: {/*
+ onEnter: function(thisElement, overed){},
+ onLeave: function(thisElement, overed){},
+ onDrop: function(thisElement, overed, event){},*/
+ droppables: [],
+ container: false,
+ precalculate: false,
+ includeMargins: true,
+ checkDroppables: true
+ },
+
+ initialize: function(element, options){
+ this.parent(element, options);
+ element = this.element;
+
+ this.droppables = $$(this.options.droppables);
+ this.setContainer(this.options.container);
+
+ if (this.options.style){
+ if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){
+ var parent = element.getOffsetParent(),
+ styles = element.getStyles('left', 'top');
+ if (parent && (styles.left == 'auto' || styles.top == 'auto')){
+ element.setPosition(element.getPosition(parent));
+ }
+ }
+
+ if (element.getStyle('position') == 'static') element.setStyle('position', 'absolute');
+ }
+
+ this.addEvent('start', this.checkDroppables, true);
+ this.overed = null;
+ },
+
+ setContainer: function(container) {
+ this.container = document.id(container);
+ if (this.container && typeOf(this.container) != 'element'){
+ this.container = document.id(this.container.getDocument().body);
+ }
+ },
+
+ start: function(event){
+ if (this.container) this.options.limit = this.calculateLimit();
+
+ if (this.options.precalculate){
+ this.positions = this.droppables.map(function(el){
+ return el.getCoordinates();
+ });
+ }
+
+ this.parent(event);
+ },
+
+ calculateLimit: function(){
+ var element = this.element,
+ container = this.container,
+
+ offsetParent = document.id(element.getOffsetParent()) || document.body,
+ containerCoordinates = container.getCoordinates(offsetParent),
+ elementMargin = {},
+ elementBorder = {},
+ containerMargin = {},
+ containerBorder = {},
+ offsetParentPadding = {};
+
+ ['top', 'right', 'bottom', 'left'].each(function(pad){
+ elementMargin[pad] = element.getStyle('margin-' + pad).toInt();
+ elementBorder[pad] = element.getStyle('border-' + pad).toInt();
+ containerMargin[pad] = container.getStyle('margin-' + pad).toInt();
+ containerBorder[pad] = container.getStyle('border-' + pad).toInt();
+ offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
+ }, this);
+
+ var width = element.offsetWidth + elementMargin.left + elementMargin.right,
+ height = element.offsetHeight + elementMargin.top + elementMargin.bottom,
+ left = 0,
+ top = 0,
+ right = containerCoordinates.right - containerBorder.right - width,
+ bottom = containerCoordinates.bottom - containerBorder.bottom - height;
+
+ if (this.options.includeMargins){
+ left += elementMargin.left;
+ top += elementMargin.top;
+ } else {
+ right += elementMargin.right;
+ bottom += elementMargin.bottom;
+ }
+
+ if (element.getStyle('position') == 'relative'){
+ var coords = element.getCoordinates(offsetParent);
+ coords.left -= element.getStyle('left').toInt();
+ coords.top -= element.getStyle('top').toInt();
+
+ left -= coords.left;
+ top -= coords.top;
+ if (container.getStyle('position') != 'relative'){
+ left += containerBorder.left;
+ top += containerBorder.top;
+ }
+ right += elementMargin.left - coords.left;
+ bottom += elementMargin.top - coords.top;
+
+ if (container != offsetParent){
+ left += containerMargin.left + offsetParentPadding.left;
+ if (!offsetParentPadding.left && left < 0) left = 0;
+ top += offsetParent == document.body ? 0 : containerMargin.top + offsetParentPadding.top;
+ if (!offsetParentPadding.top && top < 0) top = 0;
+ }
+ } else {
+ left -= elementMargin.left;
+ top -= elementMargin.top;
+ if (container != offsetParent){
+ left += containerCoordinates.left + containerBorder.left;
+ top += containerCoordinates.top + containerBorder.top;
+ }
+ }
+
+ return {
+ x: [left, right],
+ y: [top, bottom]
+ };
+ },
+
+ getDroppableCoordinates: function(element){
+ var position = element.getCoordinates();
+ if (element.getStyle('position') == 'fixed'){
+ var scroll = window.getScroll();
+ position.left += scroll.x;
+ position.right += scroll.x;
+ position.top += scroll.y;
+ position.bottom += scroll.y;
+ }
+ return position;
+ },
+
+ checkDroppables: function(){
+ var overed = this.droppables.filter(function(el, i){
+ el = this.positions ? this.positions[i] : this.getDroppableCoordinates(el);
+ var now = this.mouse.now;
+ return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
+ }, this).getLast();
+
+ if (this.overed != overed){
+ if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
+ if (overed) this.fireEvent('enter', [this.element, overed]);
+ this.overed = overed;
+ }
+ },
+
+ drag: function(event){
+ this.parent(event);
+ if (this.options.checkDroppables && this.droppables.length) this.checkDroppables();
+ },
+
+ stop: function(event){
+ this.checkDroppables();
+ this.fireEvent('drop', [this.element, this.overed, event]);
+ this.overed = null;
+ return this.parent(event);
+ }
+
+});
+
+Element.implement({
+
+ makeDraggable: function(options){
+ var drag = new Drag.Move(this, options);
+ this.store('dragger', drag);
+ return drag;
+ }
+
+});
+
+
+/*
+---
+
+script: Sortables.js
+
+name: Sortables
+
+description: Class for creating a drag and drop sorting interface for lists of items.
+
+license: MIT-style license
+
+authors:
+ - Tom Occhino
+
+requires:
+ - Core/Fx.Morph
+ - Drag.Move
+
+provides: [Sortables]
+
+...
+*/
+
+var Sortables = new Class({
+
+ Implements: [Events, Options],
+
+ options: {/*
+ onSort: function(element, clone){},
+ onStart: function(element, clone){},
+ onComplete: function(element){},*/
+ opacity: 1,
+ clone: false,
+ revert: false,
+ handle: false,
+ dragOptions: {},
+ unDraggableTags: ['button', 'input', 'a', 'textarea', 'select', 'option']
+ },
+
+ initialize: function(lists, options){
+ this.setOptions(options);
+
+ this.elements = [];
+ this.lists = [];
+ this.idle = true;
+
+ this.addLists($$(document.id(lists) || lists));
+
+ if (!this.options.clone) this.options.revert = false;
+ if (this.options.revert) this.effect = new Fx.Morph(null, Object.merge({
+ duration: 250,
+ link: 'cancel'
+ }, this.options.revert));
+ },
+
+ attach: function(){
+ this.addLists(this.lists);
+ return this;
+ },
+
+ detach: function(){
+ this.lists = this.removeLists(this.lists);
+ return this;
+ },
+
+ addItems: function(){
+ Array.flatten(arguments).each(function(element){
+ this.elements.push(element);
+ var start = element.retrieve('sortables:start', function(event){
+ this.start.call(this, event, element);
+ }.bind(this));
+ (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
+ }, this);
+ return this;
+ },
+
+ addLists: function(){
+ Array.flatten(arguments).each(function(list){
+ this.lists.include(list);
+ this.addItems(list.getChildren());
+ }, this);
+ return this;
+ },
+
+ removeItems: function(){
+ return $$(Array.flatten(arguments).map(function(element){
+ this.elements.erase(element);
+ var start = element.retrieve('sortables:start');
+ (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
+
+ return element;
+ }, this));
+ },
+
+ removeLists: function(){
+ return $$(Array.flatten(arguments).map(function(list){
+ this.lists.erase(list);
+ this.removeItems(list.getChildren());
+
+ return list;
+ }, this));
+ },
+
+ getDroppableCoordinates: function (element){
+ var offsetParent = element.getOffsetParent();
+ var position = element.getPosition(offsetParent);
+ var scroll = {
+ w: window.getScroll(),
+ offsetParent: offsetParent.getScroll()
+ };
+ position.x += scroll.offsetParent.x;
+ position.y += scroll.offsetParent.y;
+
+ if (offsetParent.getStyle('position') == 'fixed'){
+ position.x -= scroll.w.x;
+ position.y -= scroll.w.y;
+ }
+
+ return position;
+ },
+
+ getClone: function(event, element){
+ if (!this.options.clone) return new Element(element.tagName).inject(document.body);
+ if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
+ var clone = element.clone(true).setStyles({
+ margin: 0,
+ position: 'absolute',
+ visibility: 'hidden',
+ width: element.getStyle('width')
+ }).addEvent('mousedown', function(event){
+ element.fireEvent('mousedown', event);
+ });
+ //prevent the duplicated radio inputs from unchecking the real one
+ if (clone.get('html').test('radio')){
+ clone.getElements('input[type=radio]').each(function(input, i){
+ input.set('name', 'clone_' + i);
+ if (input.get('checked')) element.getElements('input[type=radio]')[i].set('checked', true);
+ });
+ }
+
+ return clone.inject(this.list).setPosition(this.getDroppableCoordinates(this.element));
+ },
+
+ getDroppables: function(){
+ var droppables = this.list.getChildren().erase(this.clone).erase(this.element);
+ if (!this.options.constrain) droppables.append(this.lists).erase(this.list);
+ return droppables;
+ },
+
+ insert: function(dragging, element){
+ var where = 'inside';
+ if (this.lists.contains(element)){
+ this.list = element;
+ this.drag.droppables = this.getDroppables();
+ } else {
+ where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
+ }
+ this.element.inject(element, where);
+ this.fireEvent('sort', [this.element, this.clone]);
+ },
+
+ start: function(event, element){
+ if (
+ !this.idle ||
+ event.rightClick ||
+ (!this.options.handle && this.options.unDraggableTags.contains(event.target.get('tag')))
+ ) return;
+
+ this.idle = false;
+ this.element = element;
+ this.opacity = element.getStyle('opacity');
+ this.list = element.getParent();
+ this.clone = this.getClone(event, element);
+
+ this.drag = new Drag.Move(this.clone, Object.merge({
+
+ droppables: this.getDroppables()
+ }, this.options.dragOptions)).addEvents({
+ onSnap: function(){
+ event.stop();
+ this.clone.setStyle('visibility', 'visible');
+ this.element.setStyle('opacity', this.options.opacity || 0);
+ this.fireEvent('start', [this.element, this.clone]);
+ }.bind(this),
+ onEnter: this.insert.bind(this),
+ onCancel: this.end.bind(this),
+ onComplete: this.end.bind(this)
+ });
+
+ this.clone.inject(this.element, 'before');
+ this.drag.start(event);
+ },
+
+ end: function(){
+ this.drag.detach();
+ this.element.setStyle('opacity', this.opacity);
+ var self = this;
+ if (this.effect){
+ var dim = this.element.getStyles('width', 'height'),
+ clone = this.clone,
+ pos = clone.computePosition(this.getDroppableCoordinates(clone));
+
+ var destroy = function(){
+ this.removeEvent('cancel', destroy);
+ clone.destroy();
+ self.reset();
+ };
+
+ this.effect.element = clone;
+ this.effect.start({
+ top: pos.top,
+ left: pos.left,
+ width: dim.width,
+ height: dim.height,
+ opacity: 0.25
+ }).addEvent('cancel', destroy).chain(destroy);
+ } else {
+ this.clone.destroy();
+ self.reset();
+ }
+
+ },
+
+ reset: function(){
+ this.idle = true;
+ this.fireEvent('complete', this.element);
+ },
+
+ serialize: function(){
+ var params = Array.link(arguments, {
+ modifier: Type.isFunction,
+ index: function(obj){
+ return obj != null;
+ }
+ });
+ var serial = this.lists.map(function(list){
+ return list.getChildren().map(params.modifier || function(element){
+ return element.get('id');
+ }, this);
+ }, this);
+
+ var index = params.index;
+ if (this.lists.length == 1) index = 0;
+ return (index || index === 0) && index >= 0 && index < this.lists.length ? serial[index] : serial;
+ }
+
+});
+
+
+/*
+---
+
+script: Request.Periodical.js
+
+name: Request.Periodical
+
+description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load
+
+license: MIT-style license
+
+authors:
+ - Christoph Pojer
+
+requires:
+ - Core/Request
+ - MooTools.More
+
+provides: [Request.Periodical]
+
+...
+*/
+
+Request.implement({
+
+ options: {
+ initialDelay: 5000,
+ delay: 5000,
+ limit: 60000
+ },
+
+ startTimer: function(data){
+ var fn = function(){
+ if (!this.running) this.send({data: data});
+ };
+ this.lastDelay = this.options.initialDelay;
+ this.timer = fn.delay(this.lastDelay, this);
+ this.completeCheck = function(response){
+ clearTimeout(this.timer);
+ this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit);
+ this.timer = fn.delay(this.lastDelay, this);
+ };
+ return this.addEvent('complete', this.completeCheck);
+ },
+
+ stopTimer: function(){
+ clearTimeout(this.timer);
+ return this.removeEvent('complete', this.completeCheck);
+ }
+
+});
+
+
+/*
+---
+
+script: Color.js
+
+name: Color
+
+description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - Core/Array
+ - Core/String
+ - Core/Number
+ - Core/Hash
+ - Core/Function
+ - MooTools.More
+
+provides: [Color]
+
+...
+*/
+
+(function(){
+
+var Color = this.Color = new Type('Color', function(color, type){
+ if (arguments.length >= 3){
+ type = 'rgb'; color = Array.slice(arguments, 0, 3);
+ } else if (typeof color == 'string'){
+ if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
+ else if (color.match(/hsb/)) color = color.hsbToRgb();
+ else color = color.hexToRgb(true);
+ }
+ type = type || 'rgb';
+ switch (type){
+ case 'hsb':
+ var old = color;
+ color = color.hsbToRgb();
+ color.hsb = old;
+ break;
+ case 'hex': color = color.hexToRgb(true); break;
+ }
+ color.rgb = color.slice(0, 3);
+ color.hsb = color.hsb || color.rgbToHsb();
+ color.hex = color.rgbToHex();
+ return Object.append(color, this);
+});
+
+Color.implement({
+
+ mix: function(){
+ var colors = Array.slice(arguments);
+ var alpha = (typeOf(colors.getLast()) == 'number') ? colors.pop() : 50;
+ var rgb = this.slice();
+ colors.each(function(color){
+ color = new Color(color);
+ for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
+ });
+ return new Color(rgb, 'rgb');
+ },
+
+ invert: function(){
+ return new Color(this.map(function(value){
+ return 255 - value;
+ }));
+ },
+
+ setHue: function(value){
+ return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
+ },
+
+ setSaturation: function(percent){
+ return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
+ },
+
+ setBrightness: function(percent){
+ return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
+ }
+
+});
+
+this.$RGB = function(r, g, b){
+ return new Color([r, g, b], 'rgb');
+};
+
+this.$HSB = function(h, s, b){
+ return new Color([h, s, b], 'hsb');
+};
+
+this.$HEX = function(hex){
+ return new Color(hex, 'hex');
+};
+
+Array.implement({
+
+ rgbToHsb: function(){
+ var red = this[0],
+ green = this[1],
+ blue = this[2],
+ hue = 0;
+ var max = Math.max(red, green, blue),
+ min = Math.min(red, green, blue);
+ var delta = max - min;
+ var brightness = max / 255,
+ saturation = (max != 0) ? delta / max : 0;
+ if (saturation != 0){
+ var rr = (max - red) / delta;
+ var gr = (max - green) / delta;
+ var br = (max - blue) / delta;
+ if (red == max) hue = br - gr;
+ else if (green == max) hue = 2 + rr - br;
+ else hue = 4 + gr - rr;
+ hue /= 6;
+ if (hue < 0) hue++;
+ }
+ return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
+ },
+
+ hsbToRgb: function(){
+ var br = Math.round(this[2] / 100 * 255);
+ if (this[1] == 0){
+ return [br, br, br];
+ } else {
+ var hue = this[0] % 360;
+ var f = hue % 60;
+ var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
+ var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
+ var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
+ switch (Math.floor(hue / 60)){
+ case 0: return [br, t, p];
+ case 1: return [q, br, p];
+ case 2: return [p, br, t];
+ case 3: return [p, q, br];
+ case 4: return [t, p, br];
+ case 5: return [br, p, q];
+ }
+ }
+ return false;
+ }
+
+});
+
+String.implement({
+
+ rgbToHsb: function(){
+ var rgb = this.match(/\d{1,3}/g);
+ return (rgb) ? rgb.rgbToHsb() : null;
+ },
+
+ hsbToRgb: function(){
+ var hsb = this.match(/\d{1,3}/g);
+ return (hsb) ? hsb.hsbToRgb() : null;
+ }
+
+});
+
+})();
+
+
diff --git a/module/web/media/js/mootools-more-1.4.0.1.js b/module/webui/themes/Dark/js/static/mootools-more.min.js
index f3f8e4ee1..ce03a60fd 100644
--- a/module/web/media/js/mootools-more-1.4.0.1.js
+++ b/module/webui/themes/Dark/js/static/mootools-more.min.js
@@ -1,8 +1,13 @@
-// MooTools: the javascript framework.
-// Load this file's selection again by visiting: http://mootools.net/more/c1cc18c2fff04bcc58921b4dff80a6f1
-// Or build this file again with packager using: packager build More/Form.Request More/Fx.Reveal More/Sortables More/Request.Periodical More/Color
/*
---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/more/c1cc18c2fff04bcc58921b4dff80a6f1
+
+packager build:
+ - packager build More/Form.Request More/Fx.Reveal More/Sortables More/Request.Periodical More/Color
+
copyrights:
- [MooTools](http://mootools.net)
@@ -10,7 +15,8 @@ licenses:
- [MIT License](http://mootools.net/license.txt)
...
*/
-MooTools.More={version:"1.4.0.1",build:"a4244edf2aa97ac8a196fc96082dd35af1abab87"};Class.Mutators.Binds=function(a){if(!this.prototype.initialize){this.implement("initialize",function(){});
+
+MooTools.More={version:"1.5.0",build:"73db5e24e6e9c5c87b3a27aebef2248053f7db37"};Class.Mutators.Binds=function(a){if(!this.prototype.initialize){this.implement("initialize",function(){});
}return Array.from(a).concat(this.prototype.Binds||[]);};Class.Mutators.initialize=function(a){return function(){Array.from(this.Binds).each(function(b){var c=this[b];
if(c){this[b]=c.bind(this);}},this);return a.apply(this,arguments);};};Class.Occlude=new Class({occlude:function(c,b){b=document.id(b||this.element);var a=b.retrieve(c||this.property);
if(a&&!this.occluded){return(this.occluded=a);}this.occluded=false;b.store(c||this.property,this);return this.occluded;}});Class.refactor=function(b,a){Object.each(a,function(e,d){var c=b.prototype[d];
@@ -28,13 +34,13 @@ if(d.mode=="vertical"){delete e.width;delete d.planes.width;}else{if(d.mode=="ho
e["total"+k]=j;i.each(function(m){var l=c(m,g);e["computed"+m.capitalize()]=l;e["total"+k]+=l;});},this);return Object.append(e,g);}});})();(function(b){var a=Element.Position={options:{relativeTo:document.body,position:{x:"center",y:"center"},offset:{x:0,y:0}},getOptions:function(d,c){c=Object.merge({},a.options,c);
a.setPositionOption(c);a.setEdgeOption(c);a.setOffsetOption(d,c);a.setDimensionsOption(d,c);return c;},setPositionOption:function(c){c.position=a.getCoordinateFromValue(c.position);
},setEdgeOption:function(d){var c=a.getCoordinateFromValue(d.edge);d.edge=c?c:(d.position.x=="center"&&d.position.y=="center")?{x:"center",y:"center"}:{x:"left",y:"top"};
-},setOffsetOption:function(f,d){var c={x:0,y:0},g=f.measure(function(){return document.id(this.getOffsetParent());}),e=g.getScroll();if(!g||g==f.getDocument().body){return;
-}c=g.measure(function(){var i=this.getPosition();if(this.getStyle("position")=="fixed"){var h=window.getScroll();i.x+=h.x;i.y+=h.y;}return i;});d.offset={parentPositioned:g!=document.id(d.relativeTo),x:d.offset.x-c.x+e.x,y:d.offset.y-c.y+e.y};
-},setDimensionsOption:function(d,c){c.dimensions=d.getDimensions({computeSize:true,styles:["padding","border","margin"]});},getPosition:function(e,d){var c={};
-d=a.getOptions(e,d);var f=document.id(d.relativeTo)||document.body;a.setPositionCoordinates(d,c,f);if(d.edge){a.toEdge(c,d);}var g=d.offset;c.left=((c.x>=0||g.parentPositioned||d.allowNegative)?c.x:0).toInt();
-c.top=((c.y>=0||g.parentPositioned||d.allowNegative)?c.y:0).toInt();a.toMinMax(c,d);if(d.relFixedPosition||f.getStyle("position")=="fixed"){a.toRelFixedPosition(f,c);
-}if(d.ignoreScroll){a.toIgnoreScroll(f,c);}if(d.ignoreMargins){a.toIgnoreMargins(c,d);}c.left=Math.ceil(c.left);c.top=Math.ceil(c.top);delete c.x;delete c.y;
-return c;},setPositionCoordinates:function(k,g,d){var f=k.offset.y,h=k.offset.x,e=(d==document.body)?window.getScroll():d.getPosition(),j=e.y,c=e.x,i=window.getSize();
+},setOffsetOption:function(f,d){var c={x:0,y:0};var e={x:0,y:0};var g=f.measure(function(){return document.id(this.getOffsetParent());});if(!g||g==f.getDocument().body){return;
+}e=g.getScroll();c=g.measure(function(){var i=this.getPosition();if(this.getStyle("position")=="fixed"){var h=window.getScroll();i.x+=h.x;i.y+=h.y;}return i;
+});d.offset={parentPositioned:g!=document.id(d.relativeTo),x:d.offset.x-c.x+e.x,y:d.offset.y-c.y+e.y};},setDimensionsOption:function(d,c){c.dimensions=d.getDimensions({computeSize:true,styles:["padding","border","margin"]});
+},getPosition:function(e,d){var c={};d=a.getOptions(e,d);var f=document.id(d.relativeTo)||document.body;a.setPositionCoordinates(d,c,f);if(d.edge){a.toEdge(c,d);
+}var g=d.offset;c.left=((c.x>=0||g.parentPositioned||d.allowNegative)?c.x:0).toInt();c.top=((c.y>=0||g.parentPositioned||d.allowNegative)?c.y:0).toInt();
+a.toMinMax(c,d);if(d.relFixedPosition||f.getStyle("position")=="fixed"){a.toRelFixedPosition(f,c);}if(d.ignoreScroll){a.toIgnoreScroll(f,c);}if(d.ignoreMargins){a.toIgnoreMargins(c,d);
+}c.left=Math.ceil(c.left);c.top=Math.ceil(c.top);delete c.x;delete c.y;return c;},setPositionCoordinates:function(k,g,d){var f=k.offset.y,h=k.offset.x,e=(d==document.body)?window.getScroll():d.getPosition(),j=e.y,c=e.x,i=window.getSize();
switch(k.position.x){case"left":g.x=c+h;break;case"right":g.x=c+h+d.offsetWidth;break;default:g.x=c+((d==document.body?i.x:d.offsetWidth)/2)+h;break;}switch(k.position.y){case"top":g.y=j+f;
break;case"bottom":g.y=j+f+d.offsetHeight;break;default:g.y=j+((d==document.body?i.y:d.offsetHeight)/2)+f;break;}},toMinMax:function(c,d){var f={left:"x",top:"y"},e;
["minimum","maximum"].each(function(g){["left","top"].each(function(h){e=d[g]?d[g][f[h]]:null;if(e!=null&&((g=="minimum")?c[h]<e:c[h]>e)){c[h]=e;}});});
@@ -45,19 +51,20 @@ c.top+=d.edge.y=="bottom"?d.dimensions["margin-bottom"]:(d.edge.y!="center"?-d.d
break;}switch(f.y){case"top":e.y=0;break;case"bottom":e.y=-g.y-g.computedTop-g.computedBottom;break;default:e.y=-(Math.round(g.totalHeight/2));break;}c.x+=e.x;
c.y+=e.y;},getCoordinateFromValue:function(c){if(typeOf(c)!="string"){return c;}c=c.toLowerCase();return{x:c.test("left")?"left":(c.test("right")?"right":"center"),y:c.test(/upper|top/)?"top":(c.test("bottom")?"bottom":"center")};
}};Element.implement({position:function(d){if(d&&(d.x!=null||d.y!=null)){return(b?b.apply(this,arguments):this);}var c=this.setStyle("position","absolute").calculatePosition(d);
-return(d&&d.returnPos)?c:this.setStyles(c);},calculatePosition:function(c){return a.getPosition(this,c);}});})(Element.prototype.position);var IframeShim=new Class({Implements:[Options,Events,Class.Occlude],options:{className:"iframeShim",src:'javascript:false;document.write("");',display:false,zIndex:null,margin:0,offset:{x:0,y:0},browsers:(Browser.ie6||(Browser.firefox&&Browser.version<3&&Browser.Platform.mac))},property:"IframeShim",initialize:function(b,a){this.element=document.id(b);
-if(this.occlude()){return this.occluded;}this.setOptions(a);this.makeShim();return this;},makeShim:function(){if(this.options.browsers){var c=this.element.getStyle("zIndex").toInt();
-if(!c){c=1;var b=this.element.getStyle("position");if(b=="static"||!b){this.element.setStyle("position","relative");}this.element.setStyle("zIndex",c);
-}c=((this.options.zIndex!=null||this.options.zIndex===0)&&c>this.options.zIndex)?this.options.zIndex:c-1;if(c<0){c=1;}this.shim=new Element("iframe",{src:this.options.src,scrolling:"no",frameborder:0,styles:{zIndex:c,position:"absolute",border:"none",filter:"progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)"},"class":this.options.className}).store("IframeShim",this);
-var a=(function(){this.shim.inject(this.element,"after");this[this.options.display?"show":"hide"]();this.fireEvent("inject");}).bind(this);if(!IframeShim.ready){window.addEvent("load",a);
-}else{a();}}else{this.position=this.hide=this.show=this.dispose=Function.from(this);}},position:function(){if(!IframeShim.ready||!this.shim){return this;
-}var a=this.element.measure(function(){return this.getSize();});if(this.options.margin!=undefined){a.x=a.x-(this.options.margin*2);a.y=a.y-(this.options.margin*2);
-this.options.offset.x+=this.options.margin;this.options.offset.y+=this.options.margin;}this.shim.set({width:a.x,height:a.y}).position({relativeTo:this.element,offset:this.options.offset});
+return(d&&d.returnPos)?c:this.setStyles(c);},calculatePosition:function(c){return a.getPosition(this,c);}});})(Element.prototype.position);(function(){var a=false;
+this.IframeShim=new Class({Implements:[Options,Events,Class.Occlude],options:{className:"iframeShim",src:'javascript:false;document.write("");',display:false,zIndex:null,margin:0,offset:{x:0,y:0},browsers:a},property:"IframeShim",initialize:function(c,b){this.element=document.id(c);
+if(this.occlude()){return this.occluded;}this.setOptions(b);this.makeShim();return this;},makeShim:function(){if(this.options.browsers){var d=this.element.getStyle("zIndex").toInt();
+if(!d){d=1;var c=this.element.getStyle("position");if(c=="static"||!c){this.element.setStyle("position","relative");}this.element.setStyle("zIndex",d);
+}d=((this.options.zIndex!=null||this.options.zIndex===0)&&d>this.options.zIndex)?this.options.zIndex:d-1;if(d<0){d=1;}this.shim=new Element("iframe",{src:this.options.src,scrolling:"no",frameborder:0,styles:{zIndex:d,position:"absolute",border:"none",filter:"progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)"},"class":this.options.className}).store("IframeShim",this);
+var b=(function(){this.shim.inject(this.element,"after");this[this.options.display?"show":"hide"]();this.fireEvent("inject");}).bind(this);if(!IframeShim.ready){window.addEvent("load",b);
+}else{b();}}else{this.position=this.hide=this.show=this.dispose=Function.from(this);}},position:function(){if(!IframeShim.ready||!this.shim){return this;
+}var b=this.element.measure(function(){return this.getSize();});if(this.options.margin!=undefined){b.x=b.x-(this.options.margin*2);b.y=b.y-(this.options.margin*2);
+this.options.offset.x+=this.options.margin;this.options.offset.y+=this.options.margin;}this.shim.set({width:b.x,height:b.y}).position({relativeTo:this.element,offset:this.options.offset});
return this;},hide:function(){if(this.shim){this.shim.setStyle("display","none");}return this;},show:function(){if(this.shim){this.shim.setStyle("display","block");
}return this.position();},dispose:function(){if(this.shim){this.shim.dispose();}return this;},destroy:function(){if(this.shim){this.shim.destroy();}return this;
-}});window.addEvent("load",function(){IframeShim.ready=true;});var Mask=new Class({Implements:[Options,Events],Binds:["position"],options:{style:{},"class":"mask",maskMargins:false,useIframeShim:true,iframeShimOptions:{}},initialize:function(b,a){this.target=document.id(b)||document.id(document.body);
+}});})();window.addEvent("load",function(){IframeShim.ready=true;});var Mask=new Class({Implements:[Options,Events],Binds:["position"],options:{style:{},"class":"mask",maskMargins:false,useIframeShim:true,iframeShimOptions:{}},initialize:function(b,a){this.target=document.id(b)||document.id(document.body);
this.target.store("mask",this);this.setOptions(a);this.render();this.inject();},render:function(){this.element=new Element("div",{"class":this.options["class"],id:this.options.id||"mask-"+String.uniqueID(),styles:Object.merge({},this.options.style,{display:"none"}),events:{click:function(a){this.fireEvent("click",a);
-if(this.options.hideOnClick){this.hide();}}.bind(this)}});this.hidden=true;},toElement:function(){return this.element;},inject:function(b,a){a=a||(this.options.inject?this.options.inject.where:"")||this.target==document.body?"inside":"after";
+if(this.options.hideOnClick){this.hide();}}.bind(this)}});this.hidden=true;},toElement:function(){return this.element;},inject:function(b,a){a=a||(this.options.inject?this.options.inject.where:"")||(this.target==document.body?"inside":"after");
b=b||(this.options.inject&&this.options.inject.target)||this.target;this.element.inject(b,a);if(this.options.useIframeShim){this.shim=new IframeShim(this.element,this.options.iframeShimOptions);
this.addEvents({show:this.shim.show.bind(this.shim),hide:this.shim.hide.bind(this.shim),destroy:this.shim.destroy.bind(this.shim)});}},position:function(){this.resize(this.options.width,this.options.height);
this.element.position({relativeTo:this.target,position:"topLeft",ignoreMargins:!this.options.maskMargins,ignoreScroll:this.target==document.body});return this;
@@ -75,10 +82,10 @@ this.target.store("spinner",this);this.setOptions(a);this.render();this.inject()
this.content.inject(this.element);if(this.options.message){this.msg=document.id(this.options.message)||new Element("p",this.options.messageContainer).appendText(this.options.message);
this.msg.inject(this.content);}if(this.options.img){this.img=document.id(this.options.img)||new Element("div",this.options.img);this.img.inject(this.content);
}this.element.set("tween",this.options.fxOptions);},show:function(a){if(this.active){return this.chain(this.show.bind(this));}if(!this.hidden){this.callChain.delay(20,this);
-return this;}this.active=true;return this.parent(a);},showMask:function(a){var b=function(){this.content.position(Object.merge({relativeTo:this.element},this.options.containerPosition));
+return this;}this.target.set("aria-busy","true");this.active=true;return this.parent(a);},showMask:function(a){var b=function(){this.content.position(Object.merge({relativeTo:this.element},this.options.containerPosition));
}.bind(this);if(a){this.parent();b();}else{if(!this.options.style.opacity){this.options.style.opacity=this.element.getStyle("opacity").toFloat();}this.element.setStyles({display:"block",opacity:0}).tween("opacity",this.options.style.opacity);
b();this.hidden=false;this.fireEvent("show");this.callChain();}},hide:function(a){if(this.active){return this.chain(this.hide.bind(this));}if(this.hidden){this.callChain.delay(20,this);
-return this;}this.active=true;return this.parent(a);},hideMask:function(a){if(a){return this.parent();}this.element.tween("opacity",0).get("tween").chain(function(){this.element.setStyle("display","none");
+return this;}this.target.set("aria-busy","false");this.active=true;return this.parent(a);},hideMask:function(a){if(a){return this.parent();}this.element.tween("opacity",0).get("tween").chain(function(){this.element.setStyle("display","none");
this.hidden=true;this.fireEvent("hide");this.callChain();}.bind(this));},destroy:function(){this.content.destroy();this.parent();this.target.eliminate("spinner");
}});Request=Class.refactor(Request,{options:{useSpinner:false,spinnerOptions:{},spinnerTarget:false},initialize:function(a){this._send=this.send;this.send=function(b){var c=this.getSpinner();
if(c){c.chain(this._send.pass(b,this)).show();}else{this._send(b);}return this;};this.previous(a);},getSpinner:function(){if(!this.spinner){var b=document.id(this.options.spinnerTarget)||document.id(this.options.update);
@@ -111,7 +118,7 @@ if(this.occlude()){return this.occluded;}this.setOptions(a).setTarget(c).attach(
var b=this.element.retrieve("validator");if(b){b[c]("onFormValidate",this.onFormValidate);}else{this.element[c]("submit",this.onSubmit);}return this;},detach:function(){return this.attach(false);
},enable:function(){return this.attach();},disable:function(){return this.detach();},onFormValidate:function(c,b,a){if(!a){return;}var d=this.element.retrieve("validator");
if(c||(d&&!d.options.stopOnFailure)){a.stop();this.send();}},onSubmit:function(a){var b=this.element.retrieve("validator");if(b){this.element.removeEvent("submit",this.onSubmit);
-b.addEvent("onFormValidate",this.onFormValidate);this.element.validate();return;}if(a){a.stop();}this.send();},saveClickedButton:function(b,c){var a=c.get("name");
+b.addEvent("onFormValidate",this.onFormValidate);b.validate(a);return;}if(a){a.stop();}this.send();},saveClickedButton:function(b,c){var a=c.get("name");
if(!a||!this.options.sendButtonClicked){return;}this.options.extraData[a]=c.get("value")||true;this.clickedCleaner=function(){delete this.options.extraData[a];
this.clickedCleaner=function(){};}.bind(this);},clickedCleaner:function(){},send:function(){var b=this.element.toQueryString().trim(),a=Object.toQueryString(this.options.extraData);
if(b){b+="&"+a;}else{b=a;}this.fireEvent("send",[this.element,b.parseQueryString()]);this.request.send({data:b,url:this.options.requestOptions.url||this.element.get("action")});
@@ -123,8 +130,8 @@ this.clickedCleaner();return this;}});Element.implement("formUpdate",function(c,
},swapClass:function(a,b){return this.removeClass(a).addClass(b);}});Document.implement({clearSelection:function(){if(window.getSelection){var a=window.getSelection();
if(a&&a.removeAllRanges){a.removeAllRanges();}}else{if(document.selection&&document.selection.empty){try{document.selection.empty();}catch(b){}}}}});(function(){var a=function(d){var b=d.options.hideInputs;
if(window.OverText){var c=[null];OverText.each(function(e){c.include("."+e.options.labelClass);});if(c){b+=c.join(", ");}}return(b)?d.element.getElements(b):null;
-};Fx.Reveal=new Class({Extends:Fx.Morph,options:{link:"cancel",styles:["padding","border","margin"],transitionOpacity:!Browser.ie6,mode:"vertical",display:function(){return this.element.get("tag")!="tr"?"block":"table-row";
-},opacity:1,hideInputs:Browser.ie?"select, input, textarea, object, embed":null},dissolve:function(){if(!this.hiding&&!this.showing){if(this.element.getStyle("display")!="none"){this.hiding=true;
+};Fx.Reveal=new Class({Extends:Fx.Morph,options:{link:"cancel",styles:["padding","border","margin"],transitionOpacity:"opacity" in document.documentElement,mode:"vertical",display:function(){return this.element.get("tag")!="tr"?"block":"table-row";
+},opacity:1,hideInputs:!("opacity" in document.documentElement)?"select, input, textarea, object, embed":null},dissolve:function(){if(!this.hiding&&!this.showing){if(this.element.getStyle("display")!="none"){this.hiding=true;
this.showing=false;this.hidden=true;this.cssText=this.element.style.cssText;var d=this.element.getComputedSize({styles:this.options.styles,mode:this.options.mode});
if(this.options.transitionOpacity){d.opacity=this.options.opacity;}var c={};Object.each(d,function(f,e){c[e]=[f,0];});this.element.setStyles({display:Function.from(this.options.display).call(this),overflow:"hidden"});
var b=a(this);if(b){b.setStyle("visibility","hidden");}this.$chain.unshift(function(){if(this.hidden){this.hiding=false;this.element.style.cssText=this.cssText;
@@ -146,7 +153,7 @@ return this;},nix:function(b){var c=Array.link(arguments,{destroy:Type.isBoolean
}.bind(this));return this;},wink:function(){var c=Array.link(arguments,{duration:Type.isNumber,options:Type.isObject});var b=this.get("reveal").setOptions(c.options);
b.reveal().chain(function(){(function(){b.dissolve();}).delay(c.duration||2000);});}});})();var Drag=new Class({Implements:[Events,Options],options:{snap:6,unit:"px",grid:false,style:true,limit:false,handle:false,invert:false,preventDefault:false,stopPropagation:false,modifiers:{x:"left",y:"top"}},initialize:function(){var b=Array.link(arguments,{options:Type.isObject,element:function(c){return c!=null;
}});this.element=document.id(b.element);this.document=this.element.getDocument();this.setOptions(b.options||{});var a=typeOf(this.options.handle);this.handles=((a=="array"||a=="collection")?$$(this.options.handle):document.id(this.options.handle))||this.element;
-this.mouse={now:{},pos:{}};this.value={start:{},now:{}};this.selection=(Browser.ie)?"selectstart":"mousedown";if(Browser.ie&&!Drag.ondragstartFixed){document.ondragstart=Function.from(false);
+this.mouse={now:{},pos:{}};this.value={start:{},now:{}};this.selection="selectstart" in document?"selectstart":"mousedown";if("ondragstart" in document&&!("FileReader" in window)&&!Drag.ondragstartFixed){document.ondragstart=Function.from(false);
Drag.ondragstartFixed=true;}this.bound={start:this.start.bind(this),check:this.check.bind(this),drag:this.drag.bind(this),stop:this.stop.bind(this),cancel:this.cancel.bind(this),eventStop:Function.from(false)};
this.attach();},attach:function(){this.handles.addEvent("mousedown",this.bound.start);return this;},detach:function(){this.handles.removeEvent("mousedown",this.bound.start);
return this;},start:function(a){var j=this.options;if(a.rightClick){return;}if(j.preventDefault){a.preventDefault();}if(j.stopPropagation){a.stopPropagation();
@@ -164,39 +171,42 @@ this.fireEvent("start",[this.element,a]).fireEvent("snap",this.element);}},drag:
if(a){this.document.removeEvent(this.selection,this.bound.eventStop);this.fireEvent("cancel",this.element);}},stop:function(b){var a={mousemove:this.bound.drag,mouseup:this.bound.stop};
a[this.selection]=this.bound.eventStop;this.document.removeEvents(a);if(b){this.fireEvent("complete",[this.element,b]);}}});Element.implement({makeResizable:function(a){var b=new Drag(this,Object.merge({modifiers:{x:"width",y:"height"}},a));
this.store("resizer",b);return b.addEvent("drag",function(){this.fireEvent("resize",b);}.bind(this));}});Drag.Move=new Class({Extends:Drag,options:{droppables:[],container:false,precalculate:false,includeMargins:true,checkDroppables:true},initialize:function(b,a){this.parent(b,a);
-b=this.element;this.droppables=$$(this.options.droppables);this.container=document.id(this.options.container);if(this.container&&typeOf(this.container)!="element"){this.container=document.id(this.container.getDocument().body);
-}if(this.options.style){if(this.options.modifiers.x=="left"&&this.options.modifiers.y=="top"){var c=b.getOffsetParent(),d=b.getStyles("left","top");if(c&&(d.left=="auto"||d.top=="auto")){b.setPosition(b.getPosition(c));
-}}if(b.getStyle("position")=="static"){b.setStyle("position","absolute");}}this.addEvent("start",this.checkDroppables,true);this.overed=null;},start:function(a){if(this.container){this.options.limit=this.calculateLimit();
-}if(this.options.precalculate){this.positions=this.droppables.map(function(b){return b.getCoordinates();});}this.parent(a);},calculateLimit:function(){var j=this.element,e=this.container,d=document.id(j.getOffsetParent())||document.body,h=e.getCoordinates(d),c={},b={},k={},g={},m={};
+b=this.element;this.droppables=$$(this.options.droppables);this.setContainer(this.options.container);if(this.options.style){if(this.options.modifiers.x=="left"&&this.options.modifiers.y=="top"){var c=b.getOffsetParent(),d=b.getStyles("left","top");
+if(c&&(d.left=="auto"||d.top=="auto")){b.setPosition(b.getPosition(c));}}if(b.getStyle("position")=="static"){b.setStyle("position","absolute");}}this.addEvent("start",this.checkDroppables,true);
+this.overed=null;},setContainer:function(a){this.container=document.id(a);if(this.container&&typeOf(this.container)!="element"){this.container=document.id(this.container.getDocument().body);
+}},start:function(a){if(this.container){this.options.limit=this.calculateLimit();}if(this.options.precalculate){this.positions=this.droppables.map(function(b){return b.getCoordinates();
+});}this.parent(a);},calculateLimit:function(){var j=this.element,e=this.container,d=document.id(j.getOffsetParent())||document.body,h=e.getCoordinates(d),c={},b={},k={},g={},m={};
["top","right","bottom","left"].each(function(q){c[q]=j.getStyle("margin-"+q).toInt();b[q]=j.getStyle("border-"+q).toInt();k[q]=e.getStyle("margin-"+q).toInt();
g[q]=e.getStyle("border-"+q).toInt();m[q]=d.getStyle("padding-"+q).toInt();},this);var f=j.offsetWidth+c.left+c.right,p=j.offsetHeight+c.top+c.bottom,i=0,l=0,o=h.right-g.right-f,a=h.bottom-g.bottom-p;
if(this.options.includeMargins){i+=c.left;l+=c.top;}else{o+=c.right;a+=c.bottom;}if(j.getStyle("position")=="relative"){var n=j.getCoordinates(d);n.left-=j.getStyle("left").toInt();
n.top-=j.getStyle("top").toInt();i-=n.left;l-=n.top;if(e.getStyle("position")!="relative"){i+=g.left;l+=g.top;}o+=c.left-n.left;a+=c.top-n.top;if(e!=d){i+=k.left+m.left;
-l+=((Browser.ie6||Browser.ie7)?0:k.top)+m.top;}}else{i-=c.left;l-=c.top;if(e!=d){i+=h.left+g.left;l+=h.top+g.top;}}return{x:[i,o],y:[l,a]};},getDroppableCoordinates:function(c){var b=c.getCoordinates();
-if(c.getStyle("position")=="fixed"){var a=window.getScroll();b.left+=a.x;b.right+=a.x;b.top+=a.y;b.bottom+=a.y;}return b;},checkDroppables:function(){var a=this.droppables.filter(function(d,c){d=this.positions?this.positions[c]:this.getDroppableCoordinates(d);
+if(!m.left&&i<0){i=0;}l+=d==document.body?0:k.top+m.top;if(!m.top&&l<0){l=0;}}}else{i-=c.left;l-=c.top;if(e!=d){i+=h.left+g.left;l+=h.top+g.top;}}return{x:[i,o],y:[l,a]};
+},getDroppableCoordinates:function(c){var b=c.getCoordinates();if(c.getStyle("position")=="fixed"){var a=window.getScroll();b.left+=a.x;b.right+=a.x;b.top+=a.y;
+b.bottom+=a.y;}return b;},checkDroppables:function(){var a=this.droppables.filter(function(d,c){d=this.positions?this.positions[c]:this.getDroppableCoordinates(d);
var b=this.mouse.now;return(b.x>d.left&&b.x<d.right&&b.y<d.bottom&&b.y>d.top);},this).getLast();if(this.overed!=a){if(this.overed){this.fireEvent("leave",[this.element,this.overed]);
}if(a){this.fireEvent("enter",[this.element,a]);}this.overed=a;}},drag:function(a){this.parent(a);if(this.options.checkDroppables&&this.droppables.length){this.checkDroppables();
}},stop:function(a){this.checkDroppables();this.fireEvent("drop",[this.element,this.overed,a]);this.overed=null;return this.parent(a);}});Element.implement({makeDraggable:function(a){var b=new Drag.Move(this,a);
-this.store("dragger",b);return b;}});var Sortables=new Class({Implements:[Events,Options],options:{opacity:1,clone:false,revert:false,handle:false,dragOptions:{}},initialize:function(a,b){this.setOptions(b);
+this.store("dragger",b);return b;}});var Sortables=new Class({Implements:[Events,Options],options:{opacity:1,clone:false,revert:false,handle:false,dragOptions:{},unDraggableTags:["button","input","a","textarea","select","option"]},initialize:function(a,b){this.setOptions(b);
this.elements=[];this.lists=[];this.idle=true;this.addLists($$(document.id(a)||a));if(!this.options.clone){this.options.revert=false;}if(this.options.revert){this.effect=new Fx.Morph(null,Object.merge({duration:250,link:"cancel"},this.options.revert));
}},attach:function(){this.addLists(this.lists);return this;},detach:function(){this.lists=this.removeLists(this.lists);return this;},addItems:function(){Array.flatten(arguments).each(function(a){this.elements.push(a);
var b=a.retrieve("sortables:start",function(c){this.start.call(this,c,a);}.bind(this));(this.options.handle?a.getElement(this.options.handle)||a:a).addEvent("mousedown",b);
},this);return this;},addLists:function(){Array.flatten(arguments).each(function(a){this.lists.include(a);this.addItems(a.getChildren());},this);return this;
},removeItems:function(){return $$(Array.flatten(arguments).map(function(a){this.elements.erase(a);var b=a.retrieve("sortables:start");(this.options.handle?a.getElement(this.options.handle)||a:a).removeEvent("mousedown",b);
return a;},this));},removeLists:function(){return $$(Array.flatten(arguments).map(function(a){this.lists.erase(a);this.removeItems(a.getChildren());return a;
-},this));},getClone:function(b,a){if(!this.options.clone){return new Element(a.tagName).inject(document.body);}if(typeOf(this.options.clone)=="function"){return this.options.clone.call(this,b,a,this.list);
-}var c=a.clone(true).setStyles({margin:0,position:"absolute",visibility:"hidden",width:a.getStyle("width")}).addEvent("mousedown",function(d){a.fireEvent("mousedown",d);
+},this));},getDroppableCoordinates:function(c){var d=c.getOffsetParent();var b=c.getPosition(d);var a={w:window.getScroll(),offsetParent:d.getScroll()};
+b.x+=a.offsetParent.x;b.y+=a.offsetParent.y;if(d.getStyle("position")=="fixed"){b.x-=a.w.x;b.y-=a.w.y;}return b;},getClone:function(b,a){if(!this.options.clone){return new Element(a.tagName).inject(document.body);
+}if(typeOf(this.options.clone)=="function"){return this.options.clone.call(this,b,a,this.list);}var c=a.clone(true).setStyles({margin:0,position:"absolute",visibility:"hidden",width:a.getStyle("width")}).addEvent("mousedown",function(d){a.fireEvent("mousedown",d);
});if(c.get("html").test("radio")){c.getElements("input[type=radio]").each(function(d,e){d.set("name","clone_"+e);if(d.get("checked")){a.getElements("input[type=radio]")[e].set("checked",true);
-}});}return c.inject(this.list).setPosition(a.getPosition(a.getOffsetParent()));},getDroppables:function(){var a=this.list.getChildren().erase(this.clone).erase(this.element);
+}});}return c.inject(this.list).setPosition(this.getDroppableCoordinates(this.element));},getDroppables:function(){var a=this.list.getChildren().erase(this.clone).erase(this.element);
if(!this.options.constrain){a.append(this.lists).erase(this.list);}return a;},insert:function(c,b){var a="inside";if(this.lists.contains(b)){this.list=b;
this.drag.droppables=this.getDroppables();}else{a=this.element.getAllPrevious().contains(b)?"before":"after";}this.element.inject(b,a);this.fireEvent("sort",[this.element,this.clone]);
-},start:function(b,a){if(!this.idle||b.rightClick||["button","input","a","textarea"].contains(b.target.get("tag"))){return;}this.idle=false;this.element=a;
-this.opacity=a.getStyle("opacity");this.list=a.getParent();this.clone=this.getClone(b,a);this.drag=new Drag.Move(this.clone,Object.merge({droppables:this.getDroppables()},this.options.dragOptions)).addEvents({onSnap:function(){b.stop();
+},start:function(b,a){if(!this.idle||b.rightClick||(!this.options.handle&&this.options.unDraggableTags.contains(b.target.get("tag")))){return;}this.idle=false;
+this.element=a;this.opacity=a.getStyle("opacity");this.list=a.getParent();this.clone=this.getClone(b,a);this.drag=new Drag.Move(this.clone,Object.merge({droppables:this.getDroppables()},this.options.dragOptions)).addEvents({onSnap:function(){b.stop();
this.clone.setStyle("visibility","visible");this.element.setStyle("opacity",this.options.opacity||0);this.fireEvent("start",[this.element,this.clone]);
}.bind(this),onEnter:this.insert.bind(this),onCancel:this.end.bind(this),onComplete:this.end.bind(this)});this.clone.inject(this.element,"before");this.drag.start(b);
-},end:function(){this.drag.detach();this.element.setStyle("opacity",this.opacity);if(this.effect){var b=this.element.getStyles("width","height"),d=this.clone,c=d.computePosition(this.element.getPosition(this.clone.getOffsetParent()));
-var a=function(){this.removeEvent("cancel",a);d.destroy();};this.effect.element=d;this.effect.start({top:c.top,left:c.left,width:b.width,height:b.height,opacity:0.25}).addEvent("cancel",a).chain(a);
-}else{this.clone.destroy();}this.reset();},reset:function(){this.idle=true;this.fireEvent("complete",this.element);},serialize:function(){var c=Array.link(arguments,{modifier:Type.isFunction,index:function(d){return d!=null;
+},end:function(){this.drag.detach();this.element.setStyle("opacity",this.opacity);var a=this;if(this.effect){var c=this.element.getStyles("width","height"),e=this.clone,d=e.computePosition(this.getDroppableCoordinates(e));
+var b=function(){this.removeEvent("cancel",b);e.destroy();a.reset();};this.effect.element=e;this.effect.start({top:d.top,left:d.left,width:c.width,height:c.height,opacity:0.25}).addEvent("cancel",b).chain(b);
+}else{this.clone.destroy();a.reset();}},reset:function(){this.idle=true;this.fireEvent("complete",this.element);},serialize:function(){var c=Array.link(arguments,{modifier:Type.isFunction,index:function(d){return d!=null;
}});var b=this.lists.map(function(d){return d.getChildren().map(c.modifier||function(e){return e.get("id");},this);},this);var a=c.index;if(this.lists.length==1){a=0;
}return(a||a===0)&&a>=0&&a<this.lists.length?b[a]:b;}});Request.implement({options:{initialDelay:5000,delay:5000,limit:60000},startTimer:function(b){var a=function(){if(!this.running){this.send({data:b});
}};this.lastDelay=this.options.initialDelay;this.timer=a.delay(this.lastDelay,this);this.completeCheck=function(c){clearTimeout(this.timer);this.lastDelay=(c)?this.options.delay:(this.lastDelay+this.options.delay).min(this.options.limit);
diff --git a/module/web/media/js/purr_static.js b/module/webui/themes/Dark/js/static/purr.js
index 7e0aee949..9cbc503d9 100644
--- a/module/web/media/js/purr_static.js
+++ b/module/webui/themes/Dark/js/static/purr.js
@@ -57,8 +57,9 @@ var Purr = new Class({
'fx': {
'duration': 500
},
+ 'highlight': false,
'highlightRepeat': false,
- 'highlight': { // false to disable highlighting
+ 'highlight': {
'start': '#FF0',
'end': false
}
@@ -161,15 +162,15 @@ var Purr = new Class({
var buttonWrapper = new Element(this.options.elements.buttonWrapper, this.options.elementOptions.buttonWrapper);
alert.grab(buttonWrapper);
options.buttons.each(function(button){
- if(button.text !== undefined)
+ if(button.text != undefined)
{
var callbackButton = new Element(this.options.elements.button, this.options.elementOptions.button);
callbackButton.set('html', button.text);
- if(button.callback !== undefined)
+ if(button.callback != undefined)
{
callbackButton.addEvent('click', button.callback.pass(alert));
}
- if(button.dismiss !== undefined && button.dismiss)
+ if(button.dismiss != undefined && button.dismiss)
{
callbackButton.addEvent('click', this.dismiss.pass(alert, this));
}
@@ -177,7 +178,7 @@ var Purr = new Class({
}
}, this);
}
- if(options.className !== undefined)
+ if(options.className != undefined)
{
alert.addClass(options.className);
}
@@ -233,7 +234,7 @@ var Purr = new Class({
'opacity': 0
});
alertFx.start({
- 'opacity': [this.options.elementOptions.alert.styles.opacity, '.9'].pick()
+ 'opacity': [this.options.elementOptions.alert.styles.opacity, .9].pick(),
});
},
@@ -253,7 +254,7 @@ var Purr = new Class({
'fadeOut': function(alert){
if(this.holdUp)
{
- this.dismiss.delay(100, this, [alert, true]);
+ this.dismiss.delay(100, this, [alert, true])
return null;
}
var alertFx = alert.retrieve('fx');
@@ -263,7 +264,7 @@ var Purr = new Class({
}
var to = {
'opacity': 0
- };
+ }
if(this.options.mode == 'top')
{
to['margin-top'] = '-'+alert.offsetHeight+'px';
@@ -288,7 +289,7 @@ Element.implement({
options = options || {
'mode':'top'
};
- alert = new Purr(options);
+ alert = new Purr(options)
this.store('alert', alert);
}
diff --git a/module/webui/themes/Dark/js/static/purr.min.js b/module/webui/themes/Dark/js/static/purr.min.js
new file mode 100644
index 000000000..bf70e357d
--- /dev/null
+++ b/module/webui/themes/Dark/js/static/purr.min.js
@@ -0,0 +1 @@
+var Purr=new Class({options:{mode:"top",position:"left",elementAlertClass:"purr-element-alert",elements:{wrapper:"div",alert:"div",buttonWrapper:"div",button:"button"},elementOptions:{wrapper:{styles:{position:"fixed","z-index":"9999"},"class":"purr-wrapper"},alert:{"class":"purr-alert",styles:{opacity:".85"}},buttonWrapper:{"class":"purr-button-wrapper"},button:{"class":"purr-button"}},alert:{buttons:[],clickDismiss:!0,hoverWait:!0,hideAfter:5e3,fx:{duration:500},highlight:!1,highlightRepeat:!1,highlight:{start:"#FF0",end:!1}}},Implements:[Options,Events,Chain],initialize:function(t){return this.setOptions(t),this.createWrapper(),this},bindAlert:function(){return this.alert.bind(this)},createWrapper:function(){this.wrapper=new Element(this.options.elements.wrapper,this.options.elementOptions.wrapper),"top"==this.options.mode?this.wrapper.setStyle("top",0):this.wrapper.setStyle("bottom",0),document.id(document.body).grab(this.wrapper),this.positionWrapper(this.options.position)},positionWrapper:function(t){if("object"==typeOf(t)){var e=this.getWrapperCoords();this.wrapper.setStyles({bottom:"",left:t.x,top:t.y-e.height,position:"absolute"})}else"left"==t?this.wrapper.setStyle("left",0):"right"==t?this.wrapper.setStyle("right",0):this.wrapper.setStyle("left",window.innerWidth/2-this.getWrapperCoords().width/2);return this},getWrapperCoords:function(){this.wrapper.setStyle("visibility","hidden");var t=this.alert("need something in here to measure"),e=this.wrapper.getCoordinates();return t.destroy(),this.wrapper.setStyle("visibility",""),e},alert:function(t,e){e=Object.merge({},this.options.alert,e||{});var i=new Element(this.options.elements.alert,this.options.elementOptions.alert);if("string"==typeOf(t))i.set("html",t);else if("element"==typeOf(t))i.grab(t);else if("array"==typeOf(t)){var s=[];return t.each(function(t){s.push(this.alert(t,e))},this),s}if(i.store("options",e),e.buttons.length>0){e.clickDismiss=!1,e.hideAfter=!1,e.hoverWait=!1;var r=new Element(this.options.elements.buttonWrapper,this.options.elementOptions.buttonWrapper);i.grab(r),e.buttons.each(function(t){if(void 0!=t.text){var e=new Element(this.options.elements.button,this.options.elementOptions.button);e.set("html",t.text),void 0!=t.callback&&e.addEvent("click",t.callback.pass(i)),void 0!=t.dismiss&&t.dismiss&&e.addEvent("click",this.dismiss.pass(i,this)),r.grab(e)}},this)}void 0!=e.className&&i.addClass(e.className),this.wrapper.grab(i,"top"==this.options.mode?"bottom":"top");var o=Object.merge(this.options.alert.fx,e.fx),n=new Fx.Morph(i,o);return i.store("fx",n),this.fadeIn(i),e.highlight&&n.addEvent("complete",function(){i.highlight(e.highlight.start,e.highlight.end),e.highlightRepeat&&i.highlight.periodical(e.highlightRepeat,i,[e.highlight.start,e.highlight.end])}),e.hideAfter&&this.dismiss(i),e.clickDismiss&&i.addEvent("click",function(){this.holdUp=!1,this.dismiss(i,!0)}.bind(this)),e.hoverWait&&i.addEvents({mouseenter:function(){this.holdUp=!0}.bind(this),mouseleave:function(){this.holdUp=!1}.bind(this)}),i},fadeIn:function(t){var e=t.retrieve("fx");e.set({opacity:0}),e.start({opacity:[this.options.elementOptions.alert.styles.opacity,.9].pick()})},dismiss:function(t,e){e=e||!1;var i=t.retrieve("options");e?this.fadeOut(t):this.fadeOut.delay(i.hideAfter,this,t)},fadeOut:function(t){if(this.holdUp)return this.dismiss.delay(100,this,[t,!0]),null;var e=t.retrieve("fx");if(!e)return null;var i={opacity:0};"top"==this.options.mode?i["margin-top"]="-"+t.offsetHeight+"px":i["margin-bottom"]="-"+t.offsetHeight+"px",e.start(i),e.addEvent("complete",function(){t.destroy()})}});Element.implement({alert:function(t,e){var i=this.retrieve("alert");i||(e=e||{mode:"top"},i=new Purr(e),this.store("alert",i));var s=this.getCoordinates();i.alert(t,e),i.wrapper.setStyles({bottom:"",left:s.left-i.wrapper.getWidth()/2+this.getWidth()/2,top:s.top-i.wrapper.getHeight(),position:"absolute"})}}); \ No newline at end of file
diff --git a/module/web/media/js/tinytab_static.js b/module/webui/themes/Dark/js/static/tinytab.js
index 6c38292f5..de50279fc 100644
--- a/module/web/media/js/tinytab_static.js
+++ b/module/webui/themes/Dark/js/static/tinytab.js
@@ -20,11 +20,6 @@ provides: TinyTab
initialize: function(tabs, contents, opt) {
this.tabs = tabs;
this.contents = contents;
- this.header = $("tabsback");
- this.headers = [];
- for(var i =0; i < this.tabs.length; i++){
- this.headers.push("");
- }
if(!opt) opt = {};
this.css = opt.selectedClass || 'selected';
this.select(this.tabs[0]);
@@ -40,9 +35,7 @@ provides: TinyTab
this.tabs.removeClass(this.css);
el.addClass(this.css);
this.contents.setStyle('display','none');
- var index = this.tabs.indexOf(el);
- this.header.set("text", this.headers[index]);
- var content = this.contents[index];
+ var content = this.contents[this.tabs.indexOf(el)];
content.setStyle('display','block');
this.fireEvent('change',[content,el]);
}
diff --git a/module/webui/themes/Dark/js/static/tinytab.min.js b/module/webui/themes/Dark/js/static/tinytab.min.js
new file mode 100644
index 000000000..2f4fa0436
--- /dev/null
+++ b/module/webui/themes/Dark/js/static/tinytab.min.js
@@ -0,0 +1 @@
+!function(){this.TinyTab=new Class({Implements:Events,initialize:function(s,t,e){this.tabs=s,this.contents=t,e||(e={}),this.css=e.selectedClass||"selected",this.select(this.tabs[0]),s.each(function(s){s.addEvent("click",function(t){this.select(s),t.stop()}.bind(this))}.bind(this))},select:function(s){this.tabs.removeClass(this.css),s.addClass(this.css),this.contents.setStyle("display","none");var t=this.contents[this.tabs.indexOf(s)];t.setStyle("display","block"),this.fireEvent("change",[t,s])}})}(document.id); \ No newline at end of file
diff --git a/module/web/templates/default/admin.html b/module/webui/themes/Dark/tml/admin.html
index b049411fd..3a88ffc7a 100644
--- a/module/web/templates/default/admin.html
+++ b/module/webui/themes/Dark/tml/admin.html
@@ -1,7 +1,7 @@
-{% extends 'default/base.html' %}
+{% extends '/Dark/tml/base.html' %}
{% block head %}
- <script type="text/javascript" src="media/js/admin.js"></script>
+ <script type="text/javascript" src="/Dark/js/render/admin.min.js"></script>
{% endblock %}
@@ -9,13 +9,13 @@
{% block subtitle %}{{ _("Administrate") }}{% endblock %}
{% block content %}
-
+
<a href="#" id="quit-pyload" style="font-size: large; font-weight: bold;">{{_("Quit pyLoad")}}</a> |
<a href="#" id="restart-pyload" style="font-size: large; font-weight: bold;">{{_("Restart pyLoad")}}</a>
<br>
<br>
- {{ _("To add user or change passwords use:") }} <b>python pyLoadCore.py -u</b><br>
+ {{ _("To add user or change passwords use:") }} <b>python pyload.py -u</b><br>
{{ _("Important: Admin user have always all permissions!") }}
<form action="" method="POST">
diff --git a/module/webui/themes/Dark/tml/base.html b/module/webui/themes/Dark/tml/base.html
new file mode 100644
index 000000000..bebb0600d
--- /dev/null
+++ b/module/webui/themes/Dark/tml/base.html
@@ -0,0 +1,177 @@
+<?xml version="1.0" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<link rel="stylesheet" type="text/css" href="/Dark/css/base.min.css"/>
+<link rel="stylesheet" type="text/css" href="/Dark/css/window.min.css"/>
+<link rel="stylesheet" type="text/css" href="/Dark/css/MooDialog.min.css"/>
+
+<script type="text/javascript" src="/Dark/js/static/mootools-core.min.js"></script>
+<script type="text/javascript" src="/Dark/js/static/mootools-more.min.js"></script>
+<script type="text/javascript" src="/Dark/js/static/MooDialog.min.js"></script>
+<script type="text/javascript" src="/Dark/js/static/purr.min.js"></script>
+
+
+<script type="text/javascript" src="/Dark/js/render/base.min.js"></script>
+
+<title>{% block title %}pyLoad {{_("Webinterface")}}{% endblock %}</title>
+
+{% block head %}
+{% endblock %}
+</head>
+<body>
+<a class="anchor" name="top" id="top"></a>
+
+<div id="head-panel">
+
+
+ <div id="head-search-and-login">
+ {% block headpanel %}
+
+ {% if user.is_authenticated %}
+
+
+{% if update %}
+<span>
+<span style="font-weight: bold; margin: 0 2px 0 2px;">{{_("pyLoad Update available!")}}</span>
+</span>
+{% endif %}
+
+
+{% if plugins %}
+<span>
+<span style="font-weight: bold; margin: 0 2px 0 2px;">{{_("Plugins updated, please restart!")}}</span>
+</span>
+{% endif %}
+
+<span id="cap_info" style="display: {% if captcha %}inline{%else%}none{% endif %}">
+<img src="/Dark/img/default/images.png" alt="Captcha:" style="vertical-align:middle; margin:2px" />
+<span style="font-weight: bold; cursor: pointer; margin-right: 2px;">{{_("Captcha waiting")}}</span>
+</span>
+
+ <img src="/Dark/img/default/head-login.png" alt="User:" style="vertical-align:middle; margin:2px" /><span style="padding-right: 2px;">{{user.name}}</span>
+ <ul id="user-actions">
+ <li><a href="/logout" class="action logout" rel="nofollow">{{_("Logout")}}</a></li>
+ {% if user.is_admin %}
+ <li><a href="/admin" class="action profile" rel="nofollow">{{_("Administrate")}}</a></li>
+ {% endif %}
+ <li><a href="/info" class="action info" rel="nofollow">{{_("Info")}}</a></li>
+
+ </ul>
+{% else %}
+ <span style="padding-right: 2px;">{{_("Please Login!")}}</span>
+{% endif %}
+
+ {% endblock %}
+ </div>
+
+ <a href="/"><img id="head-logo" src="/Dark/img/pyload-logo.png" alt="pyLoad" /></a>
+{% if user.is_authenticated %}
+ <div id="head-menu">
+ <ul>
+
+ {% macro selected(name, right=False) -%}
+ {% if name in url -%}class="{% if right -%}right {% endif %}selected"{%- endif %}
+ {% if not name in url and right -%}class="right"{%- endif %}
+ {%- endmacro %}
+
+
+ {% block menu %}
+ <li>
+ <a href="/" title=""><img src="/Dark/img/default/head-menu-home.png" alt="" /> {{_("Home")}}</a>
+ </li>
+ <li {{ selected('queue') }}>
+ <a href="/queue/" title=""><img src="/Dark/img/default/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
+ </li>
+ <li {{ selected('collector') }}>
+ <a href="/collector/" title=""><img src="/Dark/img/default/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
+ </li>
+ <li {{ selected('downloads') }}>
+ <a href="/downloads/" title=""><img src="/Dark/img/default/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
+ </li>
+ <li {{ selected('logs', True) }}>
+ <a href="/logs/" title=""><img src="/Dark/img/default/head-menu-index.png" alt="" />{{_("Logs")}}</a>
+ </li>
+ <li {{ selected('settings', True) }}>
+ <a href="/settings/" title=""><img src="/Dark/img/default/head-menu-config.png" alt="" />{{_("Config")}}</a>
+ </li>
+ {% endblock %}
+
+ </ul>
+ </div>{% endif %}
+
+ <div style="clear:both;"></div>
+</div>
+
+{% if perms.STATUS %}
+<ul id="page-actions2">
+ <li id="action_play"><a href="#" class="action play" accesskey="o" rel="nofollow">{{_("Start")}}</a></li>
+ <li id="action_stop"><a href="#" class="action stop" accesskey="o" rel="nofollow">{{_("Stop")}}</a></li>
+ <li id="action_cancel"><a href="#" class="action cancel" accesskey="o" rel="nofollow">{{_("Cancel")}}</a></li>
+ <li id="action_add"><a href="#" class="action add" accesskey="o" rel="nofollow" >{{_("Add")}}</a></li>
+</ul>
+{% endif %}
+
+{% if perms.LIST %}
+<ul id="page-actions">
+ <li><span class="time">{{_("Download:")}}</span><a id="time" style=" background-color: {% if status.download %}#8ffc25{% else %} #fc6e26{% endif %}; padding-left: 0cm; padding-right: 0.1cm;color:black; "> {% if status.download %}{{_("on")}}{% else %}{{_("off")}}{% endif %}</a></li>
+ <li><span class="reconnect">{{_("Reconnect:")}}</span><a id="reconnect" style=" background-color: {% if status.reconnect %}#8ffc25{% else %} #fc6e26{% endif %}; padding-left: 0cm; padding-right: 0.1cm;color:black; "> {% if status.reconnect %}{{_("on")}}{% else %}{{_("off")}}{% endif %}</a></li>
+ <li><a class="action backlink">{{_("Speed:")}} <b id="speed">{{ status.speed }}</b></a></li>
+ <li><a class="action cog">{{_("Active:")}} <b id="aktiv" title="{{_("Active")}}">{{ status.active }}</b> / <b id="aktiv_from" title="{{_("Queued")}}">{{ status.queue }}</b> / <b id="aktiv_total" title="{{_("Total")}}">{{ status.total }}</b></a></li>
+ <li><a href="" class="action revisions" accesskey="o" rel="nofollow">{{_("Reload page")}}</a></li>
+</ul>
+{% endif %}
+
+{% block pageactions %}
+{% endblock %}
+<br/>
+
+<div id="body-wrapper" class="dokuwiki">
+
+<div id="content" lang="en" dir="ltr">
+
+<h1>{% block subtitle %}pyLoad - {{_("Webinterface")}}{% endblock %}</h1>
+
+{% block statusbar %}
+{% endblock %}
+
+
+<br/>
+
+<div class="level1" style="clear:both">
+</div>
+<noscript><h1>Enable JavaScript to use the webinterface.</h1></noscript>
+
+{% for message in messages %}
+ <b><p>{{message}}</p></b>
+{% endfor %}
+
+<div id="load-indicator" style="opacity: 0; float: right; margin-top: -10px;">
+ <img src="/Dark/img/default/ajax-loader.gif" alt="" style="padding-right: 5px"/>
+ {{_("loading")}}
+</div>
+
+{% block content %}
+{% endblock content %}
+
+ <hr style="clear: both;" />
+
+<div id="foot">&copy; 2008-2015 pyLoad Team
+<a href="#top" class="action top" accesskey="x"><span>{{_("Back to top")}}</span></a><br />
+<!--<div class="breadcrumbs"></div>-->
+
+</div>
+</div>
+</div>
+
+<div id="window_popup" style="display: none;">
+ {% include '/Dark/tml/window.html' %}
+ {% include '/Dark/tml/captcha.html' %}
+ {% block hidden %}
+ {% endblock %}
+</div>
+</body>
+</html>
diff --git a/module/webui/themes/Dark/tml/captcha.html b/module/webui/themes/Dark/tml/captcha.html
new file mode 100644
index 000000000..731d97d11
--- /dev/null
+++ b/module/webui/themes/Dark/tml/captcha.html
@@ -0,0 +1,42 @@
+<!-- Captcha box -->
+<div id="cap_box" class="window_box">
+
+ <form id="cap_form" action="/json/set_captcha" method="POST" enctype="multipart/form-data" onsubmit="return false;">
+
+ <h1>{{_("Captcha reading")}}</h1>
+ <p id="cap_title">{{_("Please read the text on the captcha.")}}</p>
+
+ <div id="cap_textual">
+
+ <input id="cap_id" name="cap_id" type="hidden" value="" />
+
+ <label>{{_("Captcha")}}
+ <span class="small">{{_("The captcha.")}}</span>
+ </label>
+ <span class="cont">
+ <img id="cap_textual_img" src="">
+ </span>
+
+ <label>{{_("Text")}}
+ <span class="small">{{_("Input the text on the captcha.")}}</span>
+ </label>
+ <input id="cap_result" name="cap_result" type="text" size="20" />
+
+ </div>
+
+ <div id="cap_positional" style="text-align: center">
+ <img id="cap_positional_img" src="" style="margin: 10px; cursor:pointer">
+ </div>
+
+ <div id="button_bar" style="text-align: center">
+ <span>
+ <button id="cap_submit" type="submit" style="margin-left: 0">{{_("Submit")}}</button>
+ <button id="cap_reset" type="reset" style="margin-left: 0">{{_("Close")}}</button>
+ </span>
+ </div>
+
+ <div class="spacer"></div>
+
+ </form>
+
+</div> \ No newline at end of file
diff --git a/module/web/templates/default/downloads.html b/module/webui/themes/Dark/tml/downloads.html
index 450b8a102..c6ff7a243 100644
--- a/module/web/templates/default/downloads.html
+++ b/module/webui/themes/Dark/tml/downloads.html
@@ -1,4 +1,4 @@
-{% extends 'default/base.html' %}
+{% extends '/Dark/tml/base.html' %}
{% block title %}Downloads - {{super()}} {% endblock %}
@@ -19,7 +19,7 @@
</ul>
</li>
{% endfor %}
-
+
{% for file in files.files %}
<li> <a href='get/{{ file|escape }}'>{{ file }}</a></li>
{% endfor %}
diff --git a/module/webui/themes/Dark/tml/folder.html b/module/webui/themes/Dark/tml/folder.html
new file mode 100644
index 000000000..b57338d5e
--- /dev/null
+++ b/module/webui/themes/Dark/tml/folder.html
@@ -0,0 +1,15 @@
+<li class="folder">
+ <input type="hidden" name="path" class="path" value="{{ path }}" />
+ <input type="hidden" name="name" class="name" value="{{ name }}" />
+ <span>
+ <b>{{ name }}</b>
+ <span class="buttons" style="opacity:0">
+ <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/Dark/img/default/pencil.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Dark/img/default/delete.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Add subdirectory")}}" class="mkdir" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Dark/img/default/add_folder.png" />
+ </span>
+ </span>
+ <div style="display:none">{{ _("Folder is empty") }}</div>
+</li> \ No newline at end of file
diff --git a/module/web/templates/default/home.html b/module/webui/themes/Dark/tml/home.html
index 7359e326c..9131c640e 100644
--- a/module/web/templates/default/home.html
+++ b/module/webui/themes/Dark/tml/home.html
@@ -1,4 +1,4 @@
-{% extends 'default/base.html' %}
+{% extends '/Dark/tml/base.html' %}
{% block head %}
<script type="text/javascript">
@@ -7,21 +7,21 @@ var em;
var operafix = (navigator.userAgent.toLowerCase().search("opera") >= 0);
document.addEvent("domready", function(){
- em = new EntryManager();
+ em = new EntryManager();
});
var EntryManager = new Class({
initialize: function(){
this.json = new Request.JSON({
- url: "json/links",
+ url: "json/links",
secure: false,
async: true,
- onSuccess: this.update.bind(this),
- initialDelay: 0,
- delay: 2500,
- limit: 30000
- });
-
+ onSuccess: this.update.bind(this),
+ initialDelay: 0,
+ delay: 2500,
+ limit: 30000
+ });
+
this.ids = [{% for link in content %}
{% if forloop.last %}
{{ link.id }}
@@ -29,12 +29,12 @@ var EntryManager = new Class({
{{ link.id }},
{% endif %}
{% endfor %}];
-
+
this.entries = [];
this.container = $('LinksAktiv');
-
+
this.parseFromContent();
-
+
this.json.startTimer();
},
parseFromContent: function(){
@@ -45,12 +45,12 @@ var EntryManager = new Class({
}, this);
},
update: function(data){
-
+
try{
this.ids = this.entries.map(function(item){
return item.fid
});
-
+
this.ids.filter(function(id){
return !this.ids.contains(id)
},data).each(function(id){
@@ -59,13 +59,13 @@ var EntryManager = new Class({
this.entries = this.entries.filter(function(item){return item.fid != this},id);
this.ids = this.ids.erase(id)
}, this);
-
+
data.links.each(function(link, i){
if (this.ids.contains(link.fid)){
-
+
var index = this.ids.indexOf(link.fid);
this.entries[index].update(link)
-
+
}else{
var entry = new LinkEntry(link.fid);
entry.insert(link);
@@ -74,7 +74,7 @@ var EntryManager = new Class({
this.container.adopt(entry.elements.tr,entry.elements.pgbTr);
entry.fade.start('opacity', 1);
entry.fadeBar.start('opacity', 1);
-
+
}
}, this)
}catch(e){
@@ -129,7 +129,7 @@ var LinkEntry = new Class({
'html': item.percent+ '% / '+ humanFileSize(item.size-item.bleft)
}),
remove: new Element('img',{
- 'src': 'media/default/img/control_cancel.png',
+ 'src': '/Dark/img/default/control_cancel.png',
'styles':{
'vertical-align': 'middle',
'margin-right': '-20px',
@@ -150,7 +150,7 @@ var LinkEntry = new Class({
}
})
};
-
+
this.elements.tr.adopt(this.elements.name,this.elements.status,this.elements.info,this.elements.bleft,new Element('td').adopt(this.elements.percent,this.elements.remove));
this.elements.pgbTr.adopt(new Element('td',{'colspan':5}).adopt(this.elements.pgb));
this.initEffects();
@@ -163,11 +163,11 @@ var LinkEntry = new Class({
this.bar = new Fx.Morph(this.elements.pgb, {unit: '%', duration: 5000, link: 'link', fps:30});
this.fade = new Fx.Tween(this.elements.tr);
this.fadeBar = new Fx.Tween(this.elements.pgbTr);
-
+
this.elements.remove.addEvent('click', function(){
new Request({method: 'get', url: '/json/abort_link/'+this.id}).send();
}.bind(this));
-
+
},
update: function(item){
this.elements.name.set('text', item.name);
@@ -208,25 +208,22 @@ var LinkEntry = new Class({
{% block menu %}
<li class="selected">
- <a href="/" title=""><img src="/media/default/img/head-menu-home.png" alt="" /> {{_("Home")}}</a>
+ <a href="/" title=""><img src="/Dark/img/default/head-menu-home.png" alt="" /> {{_("Home")}}</a>
</li>
<li>
- <a href="/queue/" title=""><img src="/media/default/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
+ <a href="/queue/" title=""><img src="/Dark/img/default/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
</li>
<li>
- <a href="/collector/" title=""><img src="/media/default/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
+ <a href="/collector/" title=""><img src="/Dark/img/default/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
</li>
<li>
- <a href="/downloads/" title=""><img src="/media/default/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
+ <a href="/downloads/" title=""><img src="/Dark/img/default/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
</li>
-{#<li>#}
-{# <a href="/filemanager/" title=""><img src="/media/default/img/head-menu-download.png" alt="" /> {{_("FileManager")}}</a>#}
-{#</li>#}
<li class="right">
- <a href="/logs/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-index.png" alt="" />{{_("Logs")}}</a>
+ <a href="/logs/" title=""><img src="/Dark/img/default/head-menu-index.png" alt="" />{{_("Logs")}}</a>
</li>
<li class="right">
- <a href="/settings/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-config.png" alt="" />{{_("Config")}}</a>
+ <a href="/settings/" title=""><img src="/Dark/img/default/head-menu-config.png" alt="" />{{_("Config")}}</a>
</li>
{% endblock %}
@@ -242,7 +239,7 @@ var LinkEntry = new Class({
</tr>
</thead>
<tbody id="LinksAktiv">
-
+
{% for link in content %}
<tr id="link_{{ link.id }}">
<td id="link_{{ link.id }}_name">{{ link.name }}</td>
@@ -251,7 +248,7 @@ var LinkEntry = new Class({
<td id="link_{{ link.id }}_bleft">{{ link.format_size }}</td>
<td>
<span id="link_{{ link.id }}_percent">{{ link.percent }}% /{{ link.bleft }}</span>
- <img id="link_{{ link.id }}_remove" style="vertical-align: middle; margin-right: -20px; margin-left: 5px; margin-top: -2px; cursor:pointer;" src="media/default/img/control_cancel.png"/>
+ <img id="link_{{ link.id }}_remove" style="vertical-align: middle; margin-right: -20px; margin-left: 5px; margin-top: -2px; cursor:pointer;" src="/Dark/img/default/control_cancel.png"/>
</td>
</tr>
<tr id="link_{{ link.id }}_pgb_tr">
@@ -260,7 +257,7 @@ var LinkEntry = new Class({
</td>
</tr>
{% endfor %}
-
+
</tbody>
</table>
{% endblock %} \ No newline at end of file
diff --git a/module/webui/themes/Dark/tml/info.html b/module/webui/themes/Dark/tml/info.html
new file mode 100644
index 000000000..1c0a35e9f
--- /dev/null
+++ b/module/webui/themes/Dark/tml/info.html
@@ -0,0 +1,76 @@
+{% extends '/Dark/tml/base.html' %}
+
+{% block head %}
+{% endblock %}
+
+{% block title %}{{ _("Information") }} - {{ super() }} {% endblock %}
+{% block subtitle %}{{ _("Information") }}{% endblock %}
+
+{% block content %}
+ <h3>{{ _("News") }}</h3>
+
+ <ul id="twitter_update_list"></ul>
+ <script type="text/javascript" src="http://twitter.com/javascripts/blogger.min.js"></script>
+ <script type="text/javascript" src="http://api.twitter.com/1/statuses/user_timeline.json?screen_name=pyLoad&include_rts=true&count=5&callback=twitterCallback2"></script>
+
+ <h3>{{ _("Support") }}</h3>
+
+ <ul>
+ <li style="font-weight:bold;">
+ <a href="http://pyload.org/wiki" target="_blank">Wiki</a>
+ &nbsp;&nbsp;|&nbsp;&nbsp;
+ <a href="http://forum.pyload.org/" target="_blank">Forum</a>
+ &nbsp;&nbsp;|&nbsp;&nbsp;
+ <a href="http://pyload.org/irc/" target="_blank">Chat</a>
+ </li>
+ <li style="font-weight:bold;"><a href="http://docs.pyload.org" target="_blank">Documentation</a></li>
+ <li style="font-weight:bold;"><a href="https://bitbucket.org/spoob/pyload/overview" target="_blank">Development</a></li>
+ <li style="font-weight:bold;"><a href="https://bitbucket.org/spoob/pyload/issues?status=new&status=open" target="_blank">Issue Tracker</a></li>
+
+ </ul>
+
+ <h3>{{ _("System") }}</h3>
+ <table class="system">
+ <tr>
+ <td>{{ _("Python:") }}</td>
+ <td>{{ python }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("OS:") }}</td>
+ <td>{{ os }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("pyLoad version:") }}</td>
+ <td>{{ version }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Installation Folder:") }}</td>
+ <td>{{ folder }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Config Folder:") }}</td>
+ <td>{{ config }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Download Folder:") }}</td>
+ <td>{{ download }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Free Space:") }}</td>
+ <td>{{ freespace }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Language:") }}</td>
+ <td>{{ language }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Webinterface Port:") }}</td>
+ <td>{{ webif }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Remote Interface Port:") }}</td>
+ <td>{{ remote }}</td>
+ </tr>
+ </table>
+
+{% endblock %} \ No newline at end of file
diff --git a/module/webui/themes/Dark/tml/login.html b/module/webui/themes/Dark/tml/login.html
new file mode 100644
index 000000000..dcb3a18b8
--- /dev/null
+++ b/module/webui/themes/Dark/tml/login.html
@@ -0,0 +1,37 @@
+{% extends '/Dark/tml/base.html' %}
+
+{% block title %}{{_("Login")}} - {{super()}} {% endblock %}
+
+{% block content %}
+
+<div class="centeralign">
+<form action="" method="post" accept-charset="utf-8" id="login">
+ <div class="no">
+ <input type="hidden" name="do" value="login" />
+ <fieldset>
+ <legend>Login</legend>
+{% if errors %}
+<p style="color:red;">{{_("Your username and password didn't match. Please try again.")}}</p>
+{% endif %}
+ <table id="login_table">
+ <tr>
+ <td>{{_("Username")}}</td>
+ <td><input type="text" size="20" name="username" /></td>
+ </tr>
+ <tr>
+ <td>{{_("Password")}}</td>
+ <td><input type="password" size="20" name="password" /></td>
+ </tr>
+<tr>
+<td>&nbsp;</td>
+ <td><input type="submit" value="Login" class="button" /></td>
+</tr>
+</table>
+ </fieldset>
+ </div>
+</form>
+
+</div>
+<br>
+
+{% endblock %}
diff --git a/module/web/templates/default/logout.html b/module/webui/themes/Dark/tml/logout.html
index d3f07472b..08adfcf6c 100644
--- a/module/web/templates/default/logout.html
+++ b/module/webui/themes/Dark/tml/logout.html
@@ -1,4 +1,4 @@
-{% extends 'default/base.html' %}
+{% extends '/Dark/tml/base.html' %}
{% block head %}
<meta http-equiv="refresh" content="3; url=/">
diff --git a/module/web/templates/default/logs.html b/module/webui/themes/Dark/tml/logs.html
index d6288df0e..e7fead2df 100644
--- a/module/web/templates/default/logs.html
+++ b/module/webui/themes/Dark/tml/logs.html
@@ -1,9 +1,9 @@
-{% extends 'default/base.html' %}
+{% extends '/Dark/tml/base.html' %}
{% block title %}{{_("Logs")}} - {{super()}} {% endblock %}
{% block subtitle %}{{_("Logs")}}{% endblock %}
{% block head %}
-<link rel="stylesheet" type="text/css" href="/media/default/css/log.css"/>
+<link rel="stylesheet" type="text/css" href="/Dark/css/log.min.css"/>
{% endblock %}
{% block content %}
diff --git a/module/web/templates/default/pathchooser.html b/module/webui/themes/Dark/tml/pathchooser.html
index d00637055..288af8bfa 100644
--- a/module/web/templates/default/pathchooser.html
+++ b/module/webui/themes/Dark/tml/pathchooser.html
@@ -1,6 +1,6 @@
<html>
<head>
- <script class="javascript">
+ <script class="javascript">
function chosen()
{
opener.ifield.value = document.forms[0].p.value;
@@ -22,23 +22,23 @@
{
document.forms[0].p.value = file;
setValid();
-
+
}
- </script>
- <link rel="stylesheet" type="text/css" href="/media/default/css/pathchooser.css"/>
+ </script>
+ <link rel="stylesheet" type="text/css" href="/Dark/css/pathchooser.min.css"/>
</head>
<body{% if type == 'file' %}{% if not oldfile %} onload="setInvalid();"{% endif %}{% endif %}>
<center>
<div id="paths">
- <form method="get" action="?" onSubmit="chosen();" onReset="exit();">
- <input type="text" name="p" value="{{ oldfile|default(cwd) }}" size="60" onfocus="setValid();">
- <input type="submit" value="Ok" name="send">
- </form>
-
+ <form method="get" action="?" onSubmit="chosen();" onReset="exit();">
+ <input type="text" name="p" value="{{ oldfile|default(cwd) }}" size="60" onfocus="setValid();">
+ <input type="submit" value="Ok" name="send">
+ </form>
+
{% if type == 'folder' %}
- <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/pathchooser" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/pathchooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span>
+ <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/pathchooser" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/pathchooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span>
{% else %}
- <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/filechooser/" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/filechooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span>
+ <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/filechooser/" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/filechooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span>
{% endif %}
</div>
<table border="0" cellspacing="0" cellpadding="3">
@@ -48,13 +48,13 @@
<th>{{_("type")}}</th>
<th>{{_("last modified")}}</th>
</tr>
- {% if parentdir %}
+ {% if parentdir %}
<tr>
<td colspan="4">
<a href="{% if type == 'folder' %}{{ "/pathchooser/" + parentdir|quotepath }}{% else %}{{ "/filechooser/" + parentdir|quotepath }}{% endif %}"><span class="parentdir">{{_("parent directory")}}</span></a>
</td>
</tr>
- {% endif %}
+ {% endif %}
{% for file in files %}
<tr>
{% if type == 'folder' %}
diff --git a/module/webui/themes/Dark/tml/queue.html b/module/webui/themes/Dark/tml/queue.html
new file mode 100644
index 000000000..1a61e2638
--- /dev/null
+++ b/module/webui/themes/Dark/tml/queue.html
@@ -0,0 +1,104 @@
+{% extends '/Dark/tml/base.html' %}
+{% block head %}
+
+<script type="text/javascript" src="/Dark/js/render/package.min.js"></script>
+
+<script type="text/javascript">
+
+document.addEvent("domready", function(){
+ var pUI = new PackageUI("url", {{ target }});
+});
+</script>
+{% endblock %}
+
+{% if target %}
+ {% set name = _("Queue") %}
+{% else %}
+ {% set name = _("Collector") %}
+{% endif %}
+
+{% block title %}{{name}} - {{super()}} {% endblock %}
+{% block subtitle %}{{name}}{% endblock %}
+
+{% block pageactions %}
+<ul id="page-actions-more">
+ <li id="del_finished"><a style="padding: 0; font-weight: bold;" href="#">{{_("Delete Finished")}}</a></li>
+ <li id="restart_failed"><a style="padding: 0; font-weight: bold;" href="#">{{_("Restart Failed")}}</a></li>
+</ul>
+{% endblock %}
+
+{% block content %}
+{% autoescape true %}
+
+<ul id="package-list" style="list-style: none; padding-left: 0; margin-top: -10px;">
+{% for package in content %}
+ <li>
+<div id="package_{{package.pid}}" class="package">
+ <div class="order" style="display: none;">{{ package.order }}</div>
+
+ <div class="packagename" style="cursor: pointer">
+ <img class="package_drag" src="/Dark/img/default/folder.png" style="cursor: move; margin-bottom: -2px">
+ <span class="name">{{package.name}}</span>
+ &nbsp;&nbsp;
+ <span class="buttons" style="opacity:0">
+ <img title="{{_("Delete Package")}}" style="cursor: pointer" width="12px" height="12px" src="/Dark/img/default/delete.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Restart Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/Dark/img/default/arrow_refresh.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Edit Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/Dark/img/default/pencil.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Move Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/Dark/img/default/package_go.png" />
+ </span>
+ </div>
+ {% set progress = (package.linksdone * 100) / package.linkstotal %}
+
+ <div id="progress" style="border-radius: 4px; border: 1px solid grey; width: 50%; height: 1em">
+ <div style="width: {{ progress }}%; height: 100%; background-color: #525252;"></div>
+ <label style="font-size: 0.8em; font-weight: bold; padding-left: 5px; position: relative; top: -17px">
+ {{ package.sizedone|formatsize }} / {{ package.sizetotal|formatsize }}</label>
+ <label style="font-size: 0.8em; font-weight: bold; padding-right: 5px ;float: right; position: relative; top: -17px">
+ {{ package.linksdone }} / {{ package.linkstotal }}</label>
+ </div>
+ <div style="clear: both; margin-bottom: -10px"></div>
+
+ <div id="children_{{package.pid}}" style="display: none;" class="children">
+ <span class="child_secrow">{{_("Folder:")}} <span class="folder">{{package.folder}}</span> | {{_("Password:")}} <span class="password">{{package.password}}</span></span>
+ <ul id="sort_children_{{package.pid}}" style="list-style: none; padding-left: 0">
+ </ul>
+ </div>
+</div>
+ </li>
+{% endfor %}
+</ul>
+{% endautoescape %}
+{% endblock %}
+
+{% block hidden %}
+<div id="pack_box" class="window_box" style="z-index: 2">
+ <form id="pack_form" action="/json/edit_package" method="POST" enctype="multipart/form-data">
+ <h1>{{_("Edit Package")}}</h1>
+ <p>{{_("Edit the package detais below.")}}</p>
+ <input name="pack_id" id="pack_id" type="hidden" value=""/>
+ <label for="pack_name">{{_("Name")}}
+ <span class="small">{{_("The name of the package.")}}</span>
+ </label>
+ <input id="pack_name" name="pack_name" type="text" size="20" />
+
+ <label for="pack_folder">{{_("Folder")}}
+ <span class="small">{{_("Name of subfolder for these downloads.")}}</span>
+ </label>
+ <input id="pack_folder" name="pack_folder" type="text" size="20" />
+
+ <label for="pack_pws">{{_("Password")}}
+ <span class="small">{{_("List of passwords used for unrar.")}}</span>
+ </label>
+ <textarea rows="3" name="pack_pws" id="pack_pws"></textarea>
+
+ <button type="submit">{{_("Submit")}}</button>
+ <button id="pack_reset" style="margin-left: 0" type="reset" >{{_("Reset")}}</button>
+ <div class="spacer"></div>
+
+ </form>
+
+</div>
+{% endblock %} \ No newline at end of file
diff --git a/module/webui/themes/Dark/tml/settings.html b/module/webui/themes/Dark/tml/settings.html
new file mode 100644
index 000000000..cf0fe1745
--- /dev/null
+++ b/module/webui/themes/Dark/tml/settings.html
@@ -0,0 +1,204 @@
+{% extends '/Dark/tml/base.html' %}
+
+{% block title %}{{ _("Config") }} - {{ super() }} {% endblock %}
+{% block subtitle %}{{ _("Config") }}{% endblock %}
+
+{% block head %}
+ <script type="text/javascript" src="/Dark/js/static/tinytab.min.js"></script>
+ <script type="text/javascript" src="/Dark/js/static/MooDropMenu.min.js"></script>
+ <script type="text/javascript" src="/Dark/js/render/settings.min.js"></script>
+
+{% endblock %}
+
+{% block content %}
+
+ <ul id="toptabs" class="tabs">
+ <li><a class="selected" href="#">{{ _("General") }}</a></li>
+ <li><a href="#">{{ _("Plugins") }}</a></li>
+ <li><a href="#">{{ _("Accounts") }}</a></li>
+ </ul>
+
+ <div id="tabsback" style="height: 20px; padding-left: 150px; color: white; font-weight: bold;">
+
+ </div>
+
+ <span id="tabs-body">
+ <!-- General -->
+ <span id="general" class="active tabContent">
+ <ul class="nav tabs">
+ <li class>
+ <a>Menu</a>
+ <ul id="general-menu">
+ {% for entry,name in conf.general %}
+ <nobr>
+ <li style="color:white;" id="general|{{ entry }}">{{ name }}</li>
+ </nobr>
+ <br>
+ {% endfor %}
+ </ul>
+ </li>
+ </ul>
+
+ <form id="general_form" action="" method="POST" autocomplete="off">
+ <span id="general_form_content">
+ <br>
+ <h3>&nbsp;&nbsp; {{ _("Choose a section from the menu") }}</h3>
+ <br>
+ </span>
+
+ <input id="general|submit" class="styled_button" type="submit" value="{{_("Submit")}}"/>
+ </form>
+ </span>
+
+ <!-- Plugins -->
+ <span id="plugins" class="tabContent">
+ <ul class="nav tabs">
+ <li class>
+ <a>Menu</a>
+ <ul id="plugin-menu">
+ {% for entry,name in conf.plugin %}
+ <nobr>
+ <li id="plugin|{{ entry }}">{{ name }}</li>
+ </nobr>
+ <br>
+ {% endfor %}
+ </ul>
+ </li>
+ </ul>
+
+
+ <form id="plugin_form" action="" method="POST" autocomplete="off">
+
+ <span id="plugin_form_content">
+ <br>
+ <h3>&nbsp;&nbsp; {{ _("Choose a section from the menu") }}</h3>
+ <br>
+ </span>
+ <input id="plugin|submit" class="styled_button" type="submit" value="{{_("Submit")}}"/>
+ </form>
+
+ </span>
+
+ <!-- Accounts -->
+ <span id="accounts" class="tabContent">
+ <form id="account_form" action="/json/update_accounts" method="POST">
+
+ <table class="settable wide">
+
+ <thead>
+ <tr>
+ <th>{{ _("Plugin") }}</th>
+ <th>{{ _("Name") }}</th>
+ <th>{{ _("Password") }}</th>
+ <th>{{ _("Status") }}</th>
+ <th>{{ _("Premium") }}</th>
+ <th>{{ _("Valid until") }}</th>
+ <th>{{ _("Traffic left") }}</th>
+ <th>{{ _("Time") }}</th>
+ <th>{{ _("Max Parallel") }}</th>
+ <th>{{ _("Delete?") }}</th>
+ </tr>
+ </thead>
+
+
+ {% for account in conf.accs %}
+ {% set plugin = account.type %}
+ <tr>
+ <td>
+ <span style="padding:5px">{{ plugin }}</span>
+ </td>
+
+ <td><label for="{{plugin}}|password;{{account.login}}"
+ style="color:grey;">{{ account.login }}</label></td>
+ <td>
+ <input id="{{plugin}}|password;{{account.login}}"
+ name="{{plugin}}|password;{{account.login}}"
+ type="password" value="{{account.password}}" size="12"/>
+ </td>
+ <td>
+ {% if account.valid %}
+ <span style="font-weight: bold; color: #006400;">
+ {{ _("valid") }}
+ {% else %}
+ <span style="font-weight: bold; color: #8b0000;">
+ {{ _("not valid") }}
+ {% endif %}
+ </span>
+ </td>
+ <td>
+ {% if account.premium %}
+ <span style="font-weight: bold; color: #006400;">
+ {{ _("yes") }}
+ {% else %}
+ <span style="font-weight: bold; color: #8b0000;">
+ {{ _("no") }}
+ {% endif %}
+ </span>
+ </td>
+ <td>
+ <span style="font-weight: bold;">
+ {{ account.validuntil }}
+ </span>
+ </td>
+ <td>
+ <span style="font-weight: bold;">
+ {{ account.trafficleft }}
+ </span>
+ </td>
+ <td>
+ <input id="{{plugin}}|time;{{account.login}}"
+ name="{{plugin}}|time;{{account.login}}" type="text"
+ size="7" value="{{account.time}}"/>
+ </td>
+ <td>
+ <input id="{{plugin}}|limitdl;{{account.login}}"
+ name="{{plugin}}|limitdl;{{account.login}}" type="text"
+ size="2" value="{{account.limitdl}}"/>
+ </td>
+ <td>
+ <input id="{{plugin}}|delete;{{account.login}}"
+ name="{{plugin}}|delete;{{account.login}}" type="checkbox"
+ value="True"/>
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
+
+ <button id="account_submit" type="submit" class="styled_button">{{_("Submit")}}</button>
+ <button id="account_add" style="margin-left: 0" type="submit" class="styled_button">{{_("Add")}}</button>
+ </form>
+ </span>
+ </span>
+{% endblock %}
+{% block hidden %}
+<div id="account_box" class="window_box" style="z-index: 2">
+<form id="add_account_form" action="/json/add_account" method="POST" enctype="multipart/form-data">
+<h1>{{_("Add Account")}}</h1>
+<p>{{_("Enter your account data to use premium features.")}}</p>
+<label for="account_login">{{_("Login")}}
+<span class="small">{{_("Your username.")}}</span>
+</label>
+<input id="account_login" name="account_login" type="text" size="20" />
+
+<label for="account_password">{{_("Password")}}
+<span class="small">{{_("The password for this account.")}}</span>
+</label>
+<input id="account_password" name="account_password" type="password" size="20" />
+
+<label for="account_type">{{_("Type")}}
+<span class="small">{{_("Choose the hoster for your account.")}}</span>
+</label>
+ <select name=account_type id="account_type">
+ {% for type in types|sort %}
+ <option value="{{ type }}">{{ type }}</option>
+ {% endfor %}
+ </select>
+
+<button id="account_add_button" type="submit">{{_("Add")}}</button>
+<button id="account_reset" style="margin-left: 0" type="reset">{{_("Reset")}}</button>
+<div class="spacer"></div>
+
+</form>
+
+</div>
+{% endblock %} \ No newline at end of file
diff --git a/module/webui/themes/Dark/tml/settings_item.html b/module/webui/themes/Dark/tml/settings_item.html
new file mode 100644
index 000000000..e417e564c
--- /dev/null
+++ b/module/webui/themes/Dark/tml/settings_item.html
@@ -0,0 +1,48 @@
+<table class="settable">
+ {% if section.outline %}
+ <tr><th colspan="2">{{ section.outline }}</th></tr>
+ {% endif %}
+ {% for okey, option in section.iteritems() %}
+ {% if okey not in ("desc","outline") %}
+ <tr>
+ <td><label for="{{skey}}|{{okey}}"
+ style="color:white;">{{ option.desc }}:</label></td>
+ <td>
+ {% if option.type == "bool" %}
+ <select id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}">
+ <option {% if option.value %} selected="selected"
+ {% endif %}value="True">{{ _("on") }}</option>
+ <option {% if not option.value %} selected="selected"
+ {% endif %}value="False">{{ _("off") }}</option>
+ </select>
+ {% elif ";" in option.type %}
+ <select id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}">
+ {% for entry in option.list %}
+ <option {% if option.value == entry %}
+ selected="selected" {% endif %}>{{ entry }}</option>
+ {% endfor %}
+ </select>
+ {% elif option.type == "folder" %}
+ <input name="{{skey}}|{{okey}}" type="text"
+ id="{{skey}}|{{okey}}" value="{{option.value}}"/>
+ <input name="browsebutton" type="button"
+ onclick="ifield = document.getElementById('{{skey}}|{{okey}}'); pathchooser = window.open('{% if option.value %}{{ "/pathchooser/" + option.value|quotepath }}{% else %}{{ pathroot }}{% endif %}', 'pathchooser', 'scrollbars=yes,toolbar=no,menubar=no,statusbar=no,width=650,height=300'); pathchooser.ifield = ifield; window.ifield = ifield;"
+ value="{{_("Browse")}}"/>
+ {% elif option.type == "file" %}
+ <input name="{{skey}}|{{okey}}" type="text"
+ id="{{skey}}|{{okey}}" value="{{option.value}}"/>
+ <input name="browsebutton" type="button"
+ onclick="ifield = document.getElementById('{{skey}}|{{okey}}'); filechooser = window.open('{% if option.value %}{{ "/filechooser/" + option.value|quotepath }}{% else %}{{ fileroot }}{% endif %}', 'filechooser', 'scrollbars=yes,toolbar=no,menubar=no,statusbar=no,width=650,height=300'); filechooser.ifield = ifield; window.ifield = ifield;"
+ value="{{_("Browse")}}"/>
+ {% elif option.type == "password" %}
+ <input id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}"
+ type="password" value="{{option.value}}"/>
+ {% else %}
+ <input id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}"
+ type="text" value="{{option.value}}"/>
+ {% endif %}
+ </td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+</table> \ No newline at end of file
diff --git a/module/webui/themes/Dark/tml/window.html b/module/webui/themes/Dark/tml/window.html
new file mode 100644
index 000000000..a518a7b95
--- /dev/null
+++ b/module/webui/themes/Dark/tml/window.html
@@ -0,0 +1,52 @@
+<iframe id="upload_target" name="upload_target" src="" style="display: none; width:0;height:0"></iframe>
+
+<div id="add_box" class="window_box">
+<form id="add_form" action="/json/add_package" method="POST" enctype="multipart/form-data">
+<h1>{{_("Add Package")}}</h1>
+<p>{{_("Paste your links or upload a container.")}}</p>
+<label for="add_name">{{_("Name")}}
+<span class="small">{{_("The name of the new package.")}}</span>
+</label>
+<input id="add_name" name="add_name" type="text" size="20" />
+
+<label for="add_links">{{_("Links")}}
+<span class="small">{{_("Paste your links here or any text and press the filter button.")}}</span>
+<span class="small"> {{ _("Filter urls") }}
+<img alt="URIParsing" Title="Parse Uri" src="/Dark/img/default/parseUri.png" style="cursor:pointer; vertical-align: text-bottom;" onclick="parseUri()"/>
+</span>
+
+</label>
+<textarea rows="5" name="add_links" id="add_links"></textarea>
+
+<label for="add_password">{{_("Password")}}
+ <span class="small">{{_("Password for RAR-Archive")}}</span>
+</label>
+<input id="add_password" name="add_password" type="text" size="20">
+
+<label>{{_("File")}}
+<span class="small">{{_("Upload a container.")}}</span>
+</label>
+<input type="file" name="add_file" id="add_file"/>
+
+<label for="add_dest">{{_("Destination")}}
+</label>
+<span class="cont">
+<table class="window_table">
+<tr>
+ <td>{{_("Queue")}}</td>
+ <td><input type="radio" name="add_dest" id="add_dest" value="1" checked="checked" /></td>
+</tr>
+<tr>
+ <td>{{_("Collector")}}</td>
+ <td><input type="radio" name="add_dest" id="add_dest2" value="0" /></td>
+</tr>
+</table>
+</span>
+
+<button type="submit">{{_("Add Package")}}</button>
+<button id="add_reset" style="margin-left:0;" type="reset">{{_("Reset")}}</button>
+<div class="spacer"></div>
+
+</form>
+
+</div> \ No newline at end of file
diff --git a/module/webui/themes/Default/css/MooDialog.css b/module/webui/themes/Default/css/MooDialog.css
new file mode 100644
index 000000000..1e2ae68aa
--- /dev/null
+++ b/module/webui/themes/Default/css/MooDialog.css
@@ -0,0 +1,91 @@
+/* Created by Arian Stolwijk <http://www.aryweb.nl> */
+
+.MooDialog {
+/* position: fixed;*/
+ margin: 0 auto 0 -350px;
+ width:600px;
+ padding:14px;
+ left:50%;
+ top: 100px;
+
+ position: absolute;
+ left: 50%;
+ z-index: 50000;
+
+ background: #eef5f8;
+ color: black;
+ border-radius: 7px;
+ -moz-border-radius: 7px;
+ -webkit-border-radius: 7px;
+ border-radius: 7px;
+ -moz-box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
+ -webkit-box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
+ box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
+}
+
+.MooDialogTitle {
+ padding-top: 30px;
+}
+
+.MooDialog .title {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ padding: 3px 20px;
+ background: #b7c4dc;
+ border-bottom: 1px solid #a1aec5;
+ font-weight: bold;
+ text-shadow: 1px 1px 0 #fff;
+ color: black;
+ border-radius: 7px;
+ -moz-border-radius: 7px;
+ -webkit-border-radius: 7px;
+}
+
+.MooDialog .close {
+ background: url(../img/MooDialog/dialog-close.png) no-repeat;
+ width: 16px;
+ height: 16px;
+ display: block;
+ cursor: pointer;
+ top: -5px;
+ left: -5px;
+ position: absolute;
+}
+
+.MooDialog .buttons {
+ text-align: right;
+ margin: 0;
+ padding: 0;
+ border: 0;
+ background: none;
+}
+
+.MooDialog .iframe {
+ width: 100%;
+ height: 100%;
+}
+
+.MooDialog .textInput {
+ width: 200px;
+ float: left;
+}
+
+.MooDialog .MooDialogAlert,
+.MooDialog .MooDialogConfirm,
+.MooDialog .MooDialogPrompt,
+.MooDialog .MooDialogError {
+ background: url(../img/MooDialog/dialog-warning.png) no-repeat;
+ padding-left: 40px;
+ min-height: 40px;
+}
+
+.MooDialog .MooDialogConfirm,
+.MooDialog .MooDialogPromt {
+ background: url(../img/MooDialog/dialog-question.png) no-repeat;
+}
+
+.MooDialog .MooDialogError {
+ background: url(../img/MooDialog/dialog-error.png) no-repeat;
+}
diff --git a/module/webui/themes/Default/css/base.css b/module/webui/themes/Default/css/base.css
new file mode 100644
index 000000000..f5078bfbb
--- /dev/null
+++ b/module/webui/themes/Default/css/base.css
@@ -0,0 +1,902 @@
+.hidden {
+ display:none;
+}
+.leftalign {
+ text-align:left;
+}
+.centeralign {
+ text-align:center;
+}
+.rightalign {
+ text-align:right;
+}
+
+
+.dokuwiki div.plugin_translation ul li a.wikilink1:link, .dokuwiki div.plugin_translation ul li a.wikilink1:hover, .dokuwiki div.plugin_translation ul li a.wikilink1:active, .dokuwiki div.plugin_translation ul li a.wikilink1:visited {
+ background-color:#000080;
+ color:#fff !important;
+ text-decoration:none;
+ padding:0 0.2em;
+ margin:0.1em 0.2em;
+ border:none !important;
+}
+.dokuwiki div.plugin_translation ul li a.wikilink2:link, .dokuwiki div.plugin_translation ul li a.wikilink2:hover, .dokuwiki div.plugin_translation ul li a.wikilink2:active, .dokuwiki div.plugin_translation ul li a.wikilink2:visited {
+ background-color:#808080;
+ color:#fff !important;
+ text-decoration:none;
+ padding:0 0.2em;
+ margin:0.1em 0.2em;
+ border:none !important;
+}
+
+.dokuwiki div.plugin_translation ul li a:hover img {
+ opacity:1.0;
+ height:15px;
+}
+
+body {
+ margin:0;
+ padding:0;
+ background-color:white;
+ color:black;
+ font-size:12px;
+ font-family:Verdana, Helvetica, "Lucida Grande", Lucida, Arial, sans-serif;
+ font-family:sans-serif;
+ font-size:99, 96%;
+ font-size-adjust:none;
+ font-style:normal;
+ font-variant:normal;
+ font-weight:normal;
+ line-height:normal;
+}
+hr {
+ border-width:0;
+ border-bottom:1px #aaa dotted;
+}
+img {
+ border:none;
+}
+form {
+ margin:0px;
+ padding:0px;
+ border:none;
+ display:inline;
+ background:transparent;
+}
+ul li {
+ margin:5px;
+}
+textarea {
+ font-family:monospace;
+}
+table {
+ margin:0.5em 0;
+ border-collapse:collapse;
+}
+td {
+ padding:0.25em;
+ border:1pt solid #ADB9CC;
+}
+a {
+ color:#3465a4;
+ text-decoration:none;
+}
+a:hover {
+ text-decoration:underline;
+}
+
+option {
+ border:0 none #fff;
+}
+strong.highlight {
+ background-color:#fc9;
+ padding:1pt;
+}
+#pagebottom {
+ clear:both;
+}
+hr {
+ height:1px;
+ color:#c0c0c0;
+ background-color:#c0c0c0;
+ border:none;
+ margin:.2em 0 .2em 0;
+}
+
+.invisible {
+ margin:0px;
+ border:0px;
+ padding:0px;
+ height:0px;
+ visibility:hidden;
+}
+.left {
+ float:left !important;
+}
+.right {
+ float:right !important;
+}
+.center {
+ text-align:center;
+}
+div#body-wrapper {
+ padding:40px 40px 10px 40px;
+ font-size:127%;
+}
+div#content {
+ margin-top:-20px;
+ padding:0;
+ font-size:14px;
+ color:black;
+ line-height:1.5em;
+}
+h1, h2, h3, h4, h5, h6 {
+ background:transparent none repeat scroll 0 0;
+ border-bottom:1px solid #aaa;
+ color:black;
+ font-weight:normal;
+ margin:0;
+ padding:0;
+ padding-bottom:0.17em;
+ padding-top:0.5em;
+}
+h1 {
+ font-size:188%;
+ line-height:1.2em;
+ margin-bottom:0.1em;
+ padding-bottom:0;
+}
+h2 {
+ font-size:150%;
+}
+h3, h4, h5, h6 {
+ border-bottom:none;
+ font-weight:bold;
+}
+h3 {
+ font-size:132%;
+}
+h4 {
+ font-size:116%;
+}
+h5 {
+ font-size:100%;
+}
+h6 {
+ font-size:80%;
+}
+ul#page-actions, ul#page-actions-more {
+ float:right;
+ margin:10px 10px 0 10px;
+ padding:6px;
+ color:black;
+ background-color:#ececec;
+ list-style-type:none;
+ white-space: nowrap;
+ border-radius:5px;
+ -moz-border-radius:5px;
+}
+ul#user-actions {
+ padding:5px;
+ margin:0;
+ display:inline;
+ color:black;
+ background-color:#ececec;
+ list-style-type:none;
+ -moz-border-radius:3px;
+ border-radius:3px;
+}
+ul#page-actions li, ul#user-actions li, ul#page-actions-more li {
+ display:inline;
+}
+ul#page-actions a, ul#user-actions a, ul#page-actions-more a {
+ text-decoration:none;
+ color:black;
+ display:inline;
+ margin:0 3px;
+ padding:2px 0px 2px 18px;
+}
+ul#page-actions a:hover, ul#page-actions a:focus, ul#user-actions a:hover, ul#user-actions a:focus {
+ /*text-decoration:underline;*/
+}
+/***************************/
+ul#page-actions2 {
+ float:left;
+ margin:10px 10px 0 10px;
+ padding:6px;
+ color:black;
+ background-color:#ececec;
+ list-style-type:none;
+ border-radius:5px;
+ -moz-border-radius:5px;
+}
+ul#user-actions2 {
+ padding:5px;
+ margin:0;
+ display:inline;
+ color:black;
+ background-color:#ececec;
+ list-style-type:none;
+ border-radius:3px;
+ -moz-border-radius:3px;
+}
+ul#page-actions2 li, ul#user-actions2 li {
+ display:inline;
+}
+ul#page-actions2 a, ul#user-actions2 a {
+ text-decoration:none;
+ color:black;
+ display:inline;
+ margin:0 3px;
+ padding:2px 0px 2px 18px;
+}
+ul#page-actions2 a:hover, ul#page-actions2 a:focus, ul#user-actions2 a:hover, ul#user-actions2 a:focus,
+ul#page-actions-more a:hover, ul#page-actions-more a:focus{
+ color: #4e7bb4;
+}
+/****************************/
+.hidden {
+ display:none;
+}
+
+a.logout {
+ background:transparent url(../img/user-actions-logout.png) 0px 1px no-repeat;
+}
+
+a.info {
+ background:transparent url(../img/user-info.png) 0px 1px no-repeat;
+}
+
+a.admin {
+ background:transparent url(../img/user-actions-admin.png) 0px 1px no-repeat;
+}
+a.profile {
+ background:transparent url(../img/user-actions-profile.png) 0px 1px no-repeat;
+}
+a.create, a.edit {
+ background:transparent url(../img/page-tools-edit.png) 0px 1px no-repeat;
+}
+a.source, a.show {
+ background:transparent url(../img/page-tools-source.png) 0px 1px no-repeat;
+}
+a.revisions {
+ background:transparent url(../img/page-tools-revisions.png) 0px 1px no-repeat;
+}
+a.subscribe, a.unsubscribe {
+ background:transparent url(../img/page-tools-subscribe.png) 0px 1px no-repeat;
+}
+a.backlink {
+ background:transparent url(../img/page-tools-backlinks.png) 0px 1px no-repeat;
+}
+a.play {
+ background:transparent url(../img/control_play.png) 0px 1px no-repeat;
+}
+.time {
+ background:transparent url(../img/status_None.png) 0px 1px no-repeat;
+ padding: 2px 0px 2px 18px;
+ margin: 0px 3px;
+}
+.reconnect {
+ background:transparent url(../img/reconnect.png) 0px 1px no-repeat;
+ padding: 2px 0px 2px 18px;
+ margin: 0px 3px;
+}
+a.play:hover {
+ background:transparent url(../img/control_play_blue.png) 0px 1px no-repeat;
+}
+a.cancel {
+ background:transparent url(../img/control_cancel.png) 0px 1px no-repeat;
+}
+a.cancel:hover {
+ background:transparent url(../img/control_cancel_blue.png) 0px 1px no-repeat;
+}
+a.pause {
+ background:transparent url(../img/control_pause.png) 0px 1px no-repeat;
+}
+a.pause:hover {
+ background:transparent url(../img/control_pause_blue.png) 0px 1px no-repeat;
+ font-weight: bold;
+}
+a.stop {
+ background:transparent url(../img/control_stop.png) 0px 1px no-repeat;
+}
+a.stop:hover {
+ background:transparent url(../img/control_stop_blue.png) 0px 1px no-repeat;
+}
+a.add {
+ background:transparent url(../img/control_add.png) 0px 1px no-repeat;
+}
+a.add:hover {
+ background:transparent url(../img/control_add_blue.png) 0px 1px no-repeat;
+}
+a.cog {
+ background:transparent url(../img/cog.png) 0px 1px no-repeat;
+}
+#head-panel {
+ background:#525252 url(../img/head_bg1.png) bottom left repeat-x;
+}
+#head-panel h1 {
+ display:none;
+ margin:0;
+ text-decoration:none;
+ padding-top:0.8em;
+ padding-left:3.3em;
+ font-size:2.6em;
+ color:#eeeeec;
+}
+#head-panel #head-logo {
+ float:left;
+ margin:5px 0 -15px 5px;
+ padding:0;
+ overflow:visible;
+}
+#head-menu {
+ background:transparent url(../img/tabs-border-bottom.png) 0 100% repeat-x;
+ width:100%;
+ float:left;
+ margin:0;
+ padding:0;
+ padding-top:0.8em;
+}
+#head-menu ul {
+ list-style:none;
+ margin:0 1em 0 2em;
+}
+#head-menu ul li {
+ float:left;
+ margin:0;
+ margin-left:0.3em;
+ font-size:14px;
+ margin-bottom:4px;
+}
+#head-menu ul li.selected, #head-menu ul li:hover {
+ margin-bottom:0px;
+}
+#head-menu ul li a img {
+ height:22px;
+ width:22px;
+ vertical-align:middle;
+}
+#head-menu ul li a, #head-menu ul li a:link {
+ float:left;
+ text-decoration:none;
+ color:#555;
+ background:#eaeaea url(../img/tab-background.png) 0 100% repeat-x;
+ padding:3px 7px 3px 7px;
+ border:2px solid #ccc;
+ border-bottom:0px solid transparent;
+ padding-bottom:3px;
+ -moz-border-radius:5px;
+ border-radius:5px;
+}
+#head-menu ul li a:hover, #head-menu ul li a:focus {
+ color:#111;
+ padding-bottom:7px;
+ border-bottom:0px none transparent;
+ outline:none;
+ border-bottom-left-radius: 0px;
+ border-bottom-right-radius: 0px;
+ -moz-border-radius-bottomright:0px;
+ -moz-border-radius-bottomleft:0px;
+}
+#head-menu ul li a:focus {
+ margin-bottom:-4px;
+}
+#head-menu ul li.selected a {
+ color:#3566A5;
+ background:#fff;
+ padding-bottom:7px;
+ border-bottom:0px none transparent;
+ border-bottom-left-radius: 0px;
+ border-bottom-right-radius: 0px;
+ -moz-border-radius-bottomright:0px;
+ -moz-border-radius-bottomleft:0px;
+}
+#head-menu ul li.selected a:hover, #head-menu ul li.selected a:focus {
+ color:#111;
+}
+div#head-search-and-login {
+ float:right;
+ margin:0 1em 0 0;
+ background-color:#222;
+ padding:7px 7px 5px 5px;
+ color:white;
+ white-space: nowrap;
+ border-bottom-left-radius: 6px;
+ border-bottom-right-radius: 6px;
+ -moz-border-radius-bottomright:6px;
+ -moz-border-radius-bottomleft:6px;
+}
+div#head-search-and-login form {
+ display:inline;
+ padding:0 3px;
+}
+div#head-search-and-login form input {
+ border:2px solid #888;
+ background:#eee;
+ font-size:14px;
+ padding:2px;
+ border-radius:3px;
+ -moz-border-radius:3px;
+}
+div#head-search-and-login form input:focus {
+ background:#fff;
+}
+#head-search {
+ font-size:14px;
+}
+#head-username, #head-password {
+ width:80px;
+ font-size:14px;
+}
+#pageinfo {
+ clear:both;
+ color:#888;
+ padding:0.6em 0;
+ margin:0;
+}
+#foot {
+ font-style:normal;
+ color:#888;
+ text-align:center;
+}
+#foot a {
+ color:#aaf;
+}
+#foot img {
+ vertical-align:middle;
+}
+div.toc {
+ border:1px dotted #888;
+ background:#f0f0f0;
+ margin:1em 0 1em 1em;
+ float:right;
+ font-size:95%;
+}
+div.toc .tocheader {
+ font-weight:bold;
+ margin:0.5em 1em;
+}
+div.toc ol {
+ margin:1em 0.5em 1em 1em;
+ padding:0;
+}
+div.toc ol li {
+ margin:0;
+ padding:0;
+ margin-left:1em;
+}
+div.toc ol ol {
+ margin:0.5em 0.5em 0.5em 1em;
+ padding:0;
+}
+div.recentchanges table {
+ clear:both;
+}
+div#editor-help {
+ font-size:90%;
+ border:1px dotted #888;
+ padding:0ex 1ex 1ex 1ex;
+ background:#f7f6f2;
+}
+div#preview {
+ margin-top:1em;
+}
+label.block {
+ display:block;
+ text-align:right;
+ font-weight:bold;
+}
+label.simple {
+ display:block;
+ text-align:left;
+ font-weight:normal;
+}
+label.block input.edit {
+ width:50%;
+}
+/*fieldset {
+ width:300px;
+ text-align:center;
+ padding:0.5em;
+ margin:auto;
+}
+*/
+div.editor {
+ margin:0 0 0 0;
+}
+table {
+ margin:0.5em 0;
+ border-collapse:collapse;
+}
+td {
+ padding:0.25em;
+ border:1pt solid #ADB9CC;
+}
+td p {
+ margin:0;
+ padding:0;
+}
+.u {
+ text-decoration:underline;
+}
+.footnotes ul {
+ padding:0 2em;
+ margin:0 0 1em;
+}
+.footnotes li {
+ list-style:none;
+}
+.userpref table, .userpref td {
+ border:none;
+}
+#message {
+ clear:both;
+ padding:5px 10px;
+ background-color:#eee;
+ border-bottom:2px solid #ccc;
+}
+#message p {
+ margin:5px 0;
+ padding:0;
+ font-weight:bold;
+}
+#message div.buttons {
+ font-weight:normal;
+}
+.diff {
+ width:99%;
+}
+.diff-title {
+ background-color:#C0C0C0;
+}
+.searchresult dd span {
+ font-weight:bold;
+}
+.boxtext {
+ font-family:tahoma, arial, sans-serif;
+ font-size:11px;
+ color:#000;
+ float:none;
+ padding:3px 0 0 10px;
+}
+.statusbutton {
+ width:32px;
+ height:32px;
+ float:left;
+ margin-left:-32px;
+ margin-right:5px;
+ opacity:0;
+ cursor:pointer
+}
+.dlsize {
+ float:left;
+ padding-right: 8px;
+}
+.dlspeed {
+ float:left;
+ padding-right: 8px;
+}
+.package {
+ margin-bottom: 10px;
+}
+.packagename {
+ font-weight: bold;
+}
+
+.child {
+ margin-left: 20px;
+}
+.child_status {
+ margin-right: 10px;
+}
+.child_secrow {
+ font-size: 10px;
+}
+
+.header, .header th {
+ text-align: left;
+ font-weight: normal;
+ background-color:#ececec;
+ -moz-border-radius:5px;
+ border-radius:5px;
+}
+.progress_bar {
+ background: #0C0;
+ height: 5px;
+
+}
+
+.queue {
+ border: none
+}
+
+.queue tr td {
+ border: none
+}
+
+.header, .header th{
+ text-align: left;
+ font-weight: normal;
+}
+
+
+.clearer
+{
+ clear: both;
+ height: 1px;
+}
+
+.left
+{
+ float: left;
+}
+
+.right
+{
+ float: right;
+}
+
+
+.setfield
+{
+ display: table-cell;
+}
+
+ul.tabs li a
+{
+ padding: 5px 16px 4px 15px;
+ border: none;
+ font-weight: bold;
+
+ border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+
+}
+
+
+#tabs span
+{
+ display: none;
+}
+
+#tabs span.selected
+{
+ display: inline;
+}
+
+#tabsback
+{
+ background-color: #525252;
+ margin: 2px 0 0;
+ padding: 6px 4px 1px 4px;
+
+ border-top-right-radius: 30px;
+ border-top-left-radius: 3px;
+ -moz-border-radius-topright: 30px;
+ -moz-border-radius-topleft: 3px;
+}
+ul.tabs
+{
+ list-style-type: none;
+ margin:0;
+ padding: 0 40px 0 0;
+}
+
+ul.tabs li
+{
+ display: inline;
+ margin-left: 8px;
+}
+
+
+ul.tabs li a
+{
+ color: #42454a;
+ background-color: #eaeaea;
+ border: 1px none #c9c3ba;
+ margin: 0;
+ text-decoration: none;
+
+ outline: 0;
+
+ padding: 5px 16px 4px 15px;
+ font-weight: bold;
+
+ border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+
+}
+
+ul.tabs li a.selected, ul.tabs li a:hover
+{
+ color: #000;
+ background-color: white;
+
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-border-radius-bottomright: 0;
+ -moz-border-radius-bottomleft: 0;
+}
+
+ul.tabs li a:hover
+{
+ background-color: #f1f4ee;
+}
+
+ul.tabs li a.selected
+{
+ font-weight: bold;
+ background-color: #525252;
+ padding-bottom: 5px;
+ color: white;
+}
+
+
+#tabs-body {
+ position: relative;
+ overflow: hidden;
+}
+
+
+span.tabContent
+{
+ border: 2px solid #525252;
+ margin: 0;
+ padding: 0;
+ padding-bottom: 10px;
+}
+
+#tabs-body > span {
+ display: none;
+}
+
+#tabs-body > span.active {
+ display: block;
+}
+
+.hide
+{
+ display: none;
+}
+
+.settable
+{
+ margin: 20px;
+ border: none;
+}
+.settable td
+{
+ border: none;
+ margin: 0;
+ padding: 5px;
+}
+
+.settable th{
+ padding-bottom: 8px;
+}
+
+.settable.wide td , .settable.wide th {
+ padding-left: 15px;
+ padding-right: 15px;
+}
+
+
+/*settings navbar*/
+ul.nav {
+ margin: -30px 0 0;
+ padding: 0;
+ list-style: none;
+ position: absolute;
+}
+
+
+ul.nav li {
+ position: relative;
+ float: left;
+ padding: 5px;
+}
+
+ul.nav > li a {
+ background: white;
+ -moz-border-radius: 4px 4px 4px 4px;
+ border: 1px solid #C9C3BA;
+ border-bottom: medium none;
+ color: black;
+}
+
+ul.nav ul {
+ position: absolute;
+ top: 26px;
+ left: 10px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ border: 1px solid #AAA;
+ background: #f1f1f1;
+ -webkit-box-shadow: 1px 1px 5px #AAA;
+ -moz-box-shadow: 1px 1px 5px #AAA;
+ box-shadow: 1px 1px 5px #AAA;
+ cursor: pointer;
+}
+
+ul.nav .open {
+ display: block;
+}
+
+ul.nav .close {
+ display: none;
+}
+
+ul.nav ul li {
+ float: none;
+ padding: 0;
+}
+
+ul.nav ul li a {
+ width: 130px;
+ background: #f1f1f1;
+ padding: 3px;
+ display: block;
+ font-weight: normal;
+}
+
+ul.nav ul li a:hover {
+ background: #CDCDCD;
+}
+
+ul.nav ul ul {
+ left: 137px;
+ top: 0;
+}
+
+.purr-wrapper{
+ margin:10px;
+}
+
+/*Purr alert styles*/
+
+.purr-alert{
+ margin-bottom:10px;
+ padding:10px;
+ background:#000;
+ font-size:13px;
+ font-weight:bold;
+ color:#FFF;
+ -moz-border-radius:5px;
+ -webkit-border-radius:5px;
+ /*-moz-box-shadow: 0 0 10px rgba(255,255,0,.25);*/
+ width:300px;
+}
+.purr-alert.error{
+ color:#F55;
+ padding-left:30px;
+ background:url(../img/error.png) no-repeat #000 7px 10px;
+ width:280px;
+}
+.purr-alert.success{
+ color:#5F5;
+ padding-left:30px;
+ background:url(../img/success.png) no-repeat #000 7px 10px;
+ width:280px;
+}
+.purr-alert.notice{
+ color:#99F;
+ padding-left:30px;
+ background:url(../img/notice.png) no-repeat #000 7px 10px;
+ width:280px;
+}
+
+table.system {
+ border: none;
+ margin-left: 10px;
+}
+
+table.system td {
+ border: none
+}
+
+table.system tr > td:first-child {
+ font-weight: bold;
+ padding-right: 10px;
+}
diff --git a/module/webui/themes/Default/css/log.css b/module/webui/themes/Default/css/log.css
new file mode 100644
index 000000000..33e67397d
--- /dev/null
+++ b/module/webui/themes/Default/css/log.css
@@ -0,0 +1,71 @@
+
+html, body, #content
+{
+ height: 100%;
+}
+#body-wrapper
+{
+ height: 70%;
+}
+.logdiv
+{
+ height: 90%;
+ width: 100%;
+ overflow: auto;
+ border: 2px solid #CCC;
+ outline: 1px solid #666;
+ background-color: #FFE;
+ margin-right: auto;
+ margin-left: auto;
+}
+.logform
+{
+ display: table;
+ margin: 0 auto 0 auto;
+ padding-top: 5px;
+}
+.logtable
+{
+
+ margin: 0px;
+}
+.logtable td
+{
+ border: none;
+ white-space: nowrap;
+
+ font-family: monospace;
+ font-size: 16px;
+ margin: 0px;
+ padding: 0px 10px 0px 10px;
+ line-height: 110%;
+}
+td.logline
+{
+ background-color: #EEE;
+ text-align:right;
+ padding: 0px 5px 0px 5px;
+}
+td.loglevel
+{
+ text-align:right;
+}
+.logperpage
+{
+ float: right;
+ padding-bottom: 8px;
+}
+.logpaginator
+{
+ float: left;
+ padding-top: 5px;
+}
+.logpaginator a
+{
+ padding: 0px 8px 0px 8px;
+}
+.logwarn
+{
+ text-align: center;
+ color: red;
+}
diff --git a/module/webui/themes/Default/css/pathchooser.css b/module/webui/themes/Default/css/pathchooser.css
new file mode 100644
index 000000000..204812aa1
--- /dev/null
+++ b/module/webui/themes/Default/css/pathchooser.css
@@ -0,0 +1,68 @@
+table {
+ width: 90%;
+ border: 1px dotted #888888;
+ font-family: sans-serif;
+ font-size: 10pt;
+}
+
+th {
+ background-color: #525252;
+ color: #E0E0E0;
+}
+
+table, tr, td {
+ background-color: #F0F0F0;
+}
+
+a, a:visited {
+ text-decoration: none;
+ font-weight: bold;
+}
+
+#paths {
+ width: 90%;
+ text-align: left;
+}
+
+.file_directory {
+ color: #c0c0c0;
+}
+.path_directory {
+ color: #3c3c3c;
+}
+.file_file {
+ color: #3c3c3c;
+}
+.path_file {
+ color: #c0c0c0;
+}
+
+.parentdir {
+ color: #000000;
+ font-size: 10pt;
+}
+.name {
+ text-align: left;
+}
+.size {
+ text-align: right;
+}
+.type {
+ text-align: left;
+}
+.mtime {
+ text-align: center;
+}
+
+.path_abs_rel {
+ color: #3c3c3c;
+ text-decoration: none;
+ font-weight: bold;
+ font-family: sans-serif;
+ font-size: 10pt;
+}
+
+.path_abs_rel a {
+ color: #3c3c3c;
+ font-style: italic;
+}
diff --git a/module/webui/themes/Default/css/window.css b/module/webui/themes/Default/css/window.css
new file mode 100644
index 000000000..b469b4259
--- /dev/null
+++ b/module/webui/themes/Default/css/window.css
@@ -0,0 +1,73 @@
+/* ----------- stylized ----------- */
+.window_box h1{
+ font-size:14px;
+ font-weight:bold;
+ margin-bottom:8px;
+}
+.window_box p{
+ font-size:11px;
+ color:#666666;
+ margin-bottom:20px;
+ border-bottom:solid 1px #b7ddf2;
+ padding-bottom:10px;
+}
+.window_box label{
+ display:block;
+ font-weight:bold;
+ text-align:right;
+ width:240px;
+ float:left;
+}
+.window_box .small{
+ color:#666666;
+ display:block;
+ font-size:11px;
+ font-weight:normal;
+ text-align:right;
+ width:240px;
+}
+.window_box select, .window_box input{
+ float:left;
+ font-size:12px;
+ padding:4px 2px;
+ border:solid 1px #aacfe4;
+ width:300px;
+ margin:2px 0 20px 10px;
+}
+.window_box .cont{
+ float:left;
+ font-size:12px;
+ padding: 0px 10px 15px 0px;
+ width:300px;
+ margin:0px 0px 0px 10px;
+}
+.window_box .cont input{
+ float: none;
+ margin: 0px 15px 0px 1px;
+}
+.window_box textarea{
+ float:left;
+ font-size:12px;
+ padding:4px 2px;
+ border:solid 1px #aacfe4;
+ width:300px;
+ margin:2px 0 20px 10px;
+}
+.window_box button, .styled_button{
+ clear:both;
+ margin-left:150px;
+ width:125px;
+ height:31px;
+ background:#666666 url(../img/button.png) no-repeat;
+ text-align:center;
+ line-height:31px;
+ color:#FFFFFF;
+ font-size:11px;
+ font-weight:bold;
+ border: 0px;
+}
+
+.styled_button {
+ margin-left: 15px;
+ cursor: pointer;
+}
diff --git a/module/webui/themes/Default/img/MooDialog/dialog-close.png b/module/webui/themes/Default/img/MooDialog/dialog-close.png
new file mode 100644
index 000000000..81ebb88b2
--- /dev/null
+++ b/module/webui/themes/Default/img/MooDialog/dialog-close.png
Binary files differ
diff --git a/module/webui/themes/Default/img/MooDialog/dialog-error.png b/module/webui/themes/Default/img/MooDialog/dialog-error.png
new file mode 100644
index 000000000..d70328403
--- /dev/null
+++ b/module/webui/themes/Default/img/MooDialog/dialog-error.png
Binary files differ
diff --git a/module/webui/themes/Default/img/MooDialog/dialog-question.png b/module/webui/themes/Default/img/MooDialog/dialog-question.png
new file mode 100644
index 000000000..b0af3db5b
--- /dev/null
+++ b/module/webui/themes/Default/img/MooDialog/dialog-question.png
Binary files differ
diff --git a/module/webui/themes/Default/img/MooDialog/dialog-warning.png b/module/webui/themes/Default/img/MooDialog/dialog-warning.png
new file mode 100644
index 000000000..aad64d4be
--- /dev/null
+++ b/module/webui/themes/Default/img/MooDialog/dialog-warning.png
Binary files differ
diff --git a/module/webui/themes/Default/img/add_folder.png b/module/webui/themes/Default/img/add_folder.png
new file mode 100644
index 000000000..8acbc411b
--- /dev/null
+++ b/module/webui/themes/Default/img/add_folder.png
Binary files differ
diff --git a/module/webui/themes/Default/img/ajax-loader.gif b/module/webui/themes/Default/img/ajax-loader.gif
new file mode 100644
index 000000000..2fd8e0737
--- /dev/null
+++ b/module/webui/themes/Default/img/ajax-loader.gif
Binary files differ
diff --git a/module/webui/themes/Default/img/arrow_refresh.png b/module/webui/themes/Default/img/arrow_refresh.png
new file mode 100644
index 000000000..0de26566d
--- /dev/null
+++ b/module/webui/themes/Default/img/arrow_refresh.png
Binary files differ
diff --git a/module/webui/themes/Default/img/arrow_right.png b/module/webui/themes/Default/img/arrow_right.png
new file mode 100644
index 000000000..b1a181923
--- /dev/null
+++ b/module/webui/themes/Default/img/arrow_right.png
Binary files differ
diff --git a/module/webui/themes/Default/img/big_button.gif b/module/webui/themes/Default/img/big_button.gif
new file mode 100644
index 000000000..7680490ea
--- /dev/null
+++ b/module/webui/themes/Default/img/big_button.gif
Binary files differ
diff --git a/module/webui/themes/Default/img/big_button_over.gif b/module/webui/themes/Default/img/big_button_over.gif
new file mode 100644
index 000000000..2e3ee10d2
--- /dev/null
+++ b/module/webui/themes/Default/img/big_button_over.gif
Binary files differ
diff --git a/module/webui/themes/Default/img/body.png b/module/webui/themes/Default/img/body.png
new file mode 100644
index 000000000..7ff1043e0
--- /dev/null
+++ b/module/webui/themes/Default/img/body.png
Binary files differ
diff --git a/module/web/media/default/img/button.png b/module/webui/themes/Default/img/button.png
index 890160614..890160614 100644
--- a/module/web/media/default/img/button.png
+++ b/module/webui/themes/Default/img/button.png
Binary files differ
diff --git a/module/webui/themes/Default/img/closebtn.gif b/module/webui/themes/Default/img/closebtn.gif
new file mode 100644
index 000000000..3e27e6030
--- /dev/null
+++ b/module/webui/themes/Default/img/closebtn.gif
Binary files differ
diff --git a/module/webui/themes/Default/img/cog.png b/module/webui/themes/Default/img/cog.png
new file mode 100644
index 000000000..67de2c6cc
--- /dev/null
+++ b/module/webui/themes/Default/img/cog.png
Binary files differ
diff --git a/module/webui/themes/Default/img/control_add.png b/module/webui/themes/Default/img/control_add.png
new file mode 100644
index 000000000..d39886893
--- /dev/null
+++ b/module/webui/themes/Default/img/control_add.png
Binary files differ
diff --git a/module/webui/themes/Default/img/control_add_blue.png b/module/webui/themes/Default/img/control_add_blue.png
new file mode 100644
index 000000000..d11b7f41d
--- /dev/null
+++ b/module/webui/themes/Default/img/control_add_blue.png
Binary files differ
diff --git a/module/webui/themes/Default/img/control_cancel.png b/module/webui/themes/Default/img/control_cancel.png
new file mode 100644
index 000000000..7b9bc3fba
--- /dev/null
+++ b/module/webui/themes/Default/img/control_cancel.png
Binary files differ
diff --git a/module/webui/themes/Default/img/control_cancel_blue.png b/module/webui/themes/Default/img/control_cancel_blue.png
new file mode 100644
index 000000000..0c5c96ce3
--- /dev/null
+++ b/module/webui/themes/Default/img/control_cancel_blue.png
Binary files differ
diff --git a/module/webui/themes/Default/img/control_pause.png b/module/webui/themes/Default/img/control_pause.png
new file mode 100644
index 000000000..2d9ce9c4e
--- /dev/null
+++ b/module/webui/themes/Default/img/control_pause.png
Binary files differ
diff --git a/module/webui/themes/Default/img/control_pause_blue.png b/module/webui/themes/Default/img/control_pause_blue.png
new file mode 100644
index 000000000..ec61099b0
--- /dev/null
+++ b/module/webui/themes/Default/img/control_pause_blue.png
Binary files differ
diff --git a/module/webui/themes/Default/img/control_play.png b/module/webui/themes/Default/img/control_play.png
new file mode 100644
index 000000000..0846555d0
--- /dev/null
+++ b/module/webui/themes/Default/img/control_play.png
Binary files differ
diff --git a/module/webui/themes/Default/img/control_play_blue.png b/module/webui/themes/Default/img/control_play_blue.png
new file mode 100644
index 000000000..f8c8ec683
--- /dev/null
+++ b/module/webui/themes/Default/img/control_play_blue.png
Binary files differ
diff --git a/module/webui/themes/Default/img/control_stop.png b/module/webui/themes/Default/img/control_stop.png
new file mode 100644
index 000000000..893bb60e5
--- /dev/null
+++ b/module/webui/themes/Default/img/control_stop.png
Binary files differ
diff --git a/module/webui/themes/Default/img/control_stop_blue.png b/module/webui/themes/Default/img/control_stop_blue.png
new file mode 100644
index 000000000..e6f75d232
--- /dev/null
+++ b/module/webui/themes/Default/img/control_stop_blue.png
Binary files differ
diff --git a/module/webui/themes/Default/img/delete.png b/module/webui/themes/Default/img/delete.png
new file mode 100644
index 000000000..08f249365
--- /dev/null
+++ b/module/webui/themes/Default/img/delete.png
Binary files differ
diff --git a/module/webui/themes/Default/img/drag_corner.gif b/module/webui/themes/Default/img/drag_corner.gif
new file mode 100644
index 000000000..befb1adf1
--- /dev/null
+++ b/module/webui/themes/Default/img/drag_corner.gif
Binary files differ
diff --git a/module/webui/themes/Default/img/error.png b/module/webui/themes/Default/img/error.png
new file mode 100644
index 000000000..c37bd062e
--- /dev/null
+++ b/module/webui/themes/Default/img/error.png
Binary files differ
diff --git a/module/webui/themes/Default/img/folder.png b/module/webui/themes/Default/img/folder.png
new file mode 100644
index 000000000..784e8fa48
--- /dev/null
+++ b/module/webui/themes/Default/img/folder.png
Binary files differ
diff --git a/module/webui/themes/Default/img/full.png b/module/webui/themes/Default/img/full.png
new file mode 100644
index 000000000..fea52af76
--- /dev/null
+++ b/module/webui/themes/Default/img/full.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-login.png b/module/webui/themes/Default/img/head-login.png
new file mode 100644
index 000000000..b59b7cbbf
--- /dev/null
+++ b/module/webui/themes/Default/img/head-login.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-menu-collector.png b/module/webui/themes/Default/img/head-menu-collector.png
new file mode 100644
index 000000000..861be40bc
--- /dev/null
+++ b/module/webui/themes/Default/img/head-menu-collector.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-menu-config.png b/module/webui/themes/Default/img/head-menu-config.png
new file mode 100644
index 000000000..bbf43d4f3
--- /dev/null
+++ b/module/webui/themes/Default/img/head-menu-config.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-menu-development.png b/module/webui/themes/Default/img/head-menu-development.png
new file mode 100644
index 000000000..fad150fe1
--- /dev/null
+++ b/module/webui/themes/Default/img/head-menu-development.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-menu-download.png b/module/webui/themes/Default/img/head-menu-download.png
new file mode 100644
index 000000000..98c5da9db
--- /dev/null
+++ b/module/webui/themes/Default/img/head-menu-download.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-menu-home.png b/module/webui/themes/Default/img/head-menu-home.png
new file mode 100644
index 000000000..9d62109aa
--- /dev/null
+++ b/module/webui/themes/Default/img/head-menu-home.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-menu-index.png b/module/webui/themes/Default/img/head-menu-index.png
new file mode 100644
index 000000000..44d631064
--- /dev/null
+++ b/module/webui/themes/Default/img/head-menu-index.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-menu-news.png b/module/webui/themes/Default/img/head-menu-news.png
new file mode 100644
index 000000000..43950ebc9
--- /dev/null
+++ b/module/webui/themes/Default/img/head-menu-news.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-menu-queue.png b/module/webui/themes/Default/img/head-menu-queue.png
new file mode 100644
index 000000000..be98793ce
--- /dev/null
+++ b/module/webui/themes/Default/img/head-menu-queue.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-menu-recent.png b/module/webui/themes/Default/img/head-menu-recent.png
new file mode 100644
index 000000000..fc9b0497f
--- /dev/null
+++ b/module/webui/themes/Default/img/head-menu-recent.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-menu-wiki.png b/module/webui/themes/Default/img/head-menu-wiki.png
new file mode 100644
index 000000000..07cf0102d
--- /dev/null
+++ b/module/webui/themes/Default/img/head-menu-wiki.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head-search-noshadow.png b/module/webui/themes/Default/img/head-search-noshadow.png
new file mode 100644
index 000000000..aafdae015
--- /dev/null
+++ b/module/webui/themes/Default/img/head-search-noshadow.png
Binary files differ
diff --git a/module/webui/themes/Default/img/head_bg1.png b/module/webui/themes/Default/img/head_bg1.png
new file mode 100644
index 000000000..f2848c3cc
--- /dev/null
+++ b/module/webui/themes/Default/img/head_bg1.png
Binary files differ
diff --git a/module/webui/themes/Default/img/images.png b/module/webui/themes/Default/img/images.png
new file mode 100644
index 000000000..184860d1e
--- /dev/null
+++ b/module/webui/themes/Default/img/images.png
Binary files differ
diff --git a/module/webui/themes/Default/img/notice.png b/module/webui/themes/Default/img/notice.png
new file mode 100644
index 000000000..12cd1aef9
--- /dev/null
+++ b/module/webui/themes/Default/img/notice.png
Binary files differ
diff --git a/module/webui/themes/Default/img/package_go.png b/module/webui/themes/Default/img/package_go.png
new file mode 100644
index 000000000..aace63ad6
--- /dev/null
+++ b/module/webui/themes/Default/img/package_go.png
Binary files differ
diff --git a/module/webui/themes/Default/img/page-tools-backlinks.png b/module/webui/themes/Default/img/page-tools-backlinks.png
new file mode 100644
index 000000000..3eb6a9ce3
--- /dev/null
+++ b/module/webui/themes/Default/img/page-tools-backlinks.png
Binary files differ
diff --git a/module/webui/themes/Default/img/page-tools-edit.png b/module/webui/themes/Default/img/page-tools-edit.png
new file mode 100644
index 000000000..188e1c12b
--- /dev/null
+++ b/module/webui/themes/Default/img/page-tools-edit.png
Binary files differ
diff --git a/module/webui/themes/Default/img/page-tools-revisions.png b/module/webui/themes/Default/img/page-tools-revisions.png
new file mode 100644
index 000000000..5c3b8587f
--- /dev/null
+++ b/module/webui/themes/Default/img/page-tools-revisions.png
Binary files differ
diff --git a/module/webui/themes/Default/img/parseUri.png b/module/webui/themes/Default/img/parseUri.png
new file mode 100644
index 000000000..937bded9d
--- /dev/null
+++ b/module/webui/themes/Default/img/parseUri.png
Binary files differ
diff --git a/module/webui/themes/Default/img/pencil.png b/module/webui/themes/Default/img/pencil.png
new file mode 100644
index 000000000..0bfecd50e
--- /dev/null
+++ b/module/webui/themes/Default/img/pencil.png
Binary files differ
diff --git a/module/web/media/default/img/pyload-logo-edited3.5-new-font-small.png b/module/webui/themes/Default/img/pyload-logo.png
index 2443cd8b1..2443cd8b1 100644
--- a/module/web/media/default/img/pyload-logo-edited3.5-new-font-small.png
+++ b/module/webui/themes/Default/img/pyload-logo.png
Binary files differ
diff --git a/module/webui/themes/Default/img/reconnect.png b/module/webui/themes/Default/img/reconnect.png
new file mode 100644
index 000000000..49b269145
--- /dev/null
+++ b/module/webui/themes/Default/img/reconnect.png
Binary files differ
diff --git a/module/webui/themes/Default/img/status_None.png b/module/webui/themes/Default/img/status_None.png
new file mode 100644
index 000000000..293b13f77
--- /dev/null
+++ b/module/webui/themes/Default/img/status_None.png
Binary files differ
diff --git a/module/webui/themes/Default/img/status_downloading.png b/module/webui/themes/Default/img/status_downloading.png
new file mode 100644
index 000000000..fb4ebc850
--- /dev/null
+++ b/module/webui/themes/Default/img/status_downloading.png
Binary files differ
diff --git a/module/webui/themes/Default/img/status_failed.png b/module/webui/themes/Default/img/status_failed.png
new file mode 100644
index 000000000..c37bd062e
--- /dev/null
+++ b/module/webui/themes/Default/img/status_failed.png
Binary files differ
diff --git a/module/webui/themes/Default/img/status_finished.png b/module/webui/themes/Default/img/status_finished.png
new file mode 100644
index 000000000..89c8129a4
--- /dev/null
+++ b/module/webui/themes/Default/img/status_finished.png
Binary files differ
diff --git a/module/webui/themes/Default/img/status_offline.png b/module/webui/themes/Default/img/status_offline.png
new file mode 100644
index 000000000..0cfd58596
--- /dev/null
+++ b/module/webui/themes/Default/img/status_offline.png
Binary files differ
diff --git a/module/webui/themes/Default/img/status_proc.png b/module/webui/themes/Default/img/status_proc.png
new file mode 100644
index 000000000..67de2c6cc
--- /dev/null
+++ b/module/webui/themes/Default/img/status_proc.png
Binary files differ
diff --git a/module/webui/themes/Default/img/status_queue.png b/module/webui/themes/Default/img/status_queue.png
new file mode 100644
index 000000000..293b13f77
--- /dev/null
+++ b/module/webui/themes/Default/img/status_queue.png
Binary files differ
diff --git a/module/webui/themes/Default/img/status_waiting.png b/module/webui/themes/Default/img/status_waiting.png
new file mode 100644
index 000000000..2842cc338
--- /dev/null
+++ b/module/webui/themes/Default/img/status_waiting.png
Binary files differ
diff --git a/module/webui/themes/Default/img/success.png b/module/webui/themes/Default/img/success.png
new file mode 100644
index 000000000..89c8129a4
--- /dev/null
+++ b/module/webui/themes/Default/img/success.png
Binary files differ
diff --git a/module/web/media/default/img/tab-background.png b/module/webui/themes/Default/img/tab-background.png
index 29a5d1991..29a5d1991 100644
--- a/module/web/media/default/img/tab-background.png
+++ b/module/webui/themes/Default/img/tab-background.png
Binary files differ
diff --git a/module/webui/themes/Default/img/tabs-border-bottom.png b/module/webui/themes/Default/img/tabs-border-bottom.png
new file mode 100644
index 000000000..02440f428
--- /dev/null
+++ b/module/webui/themes/Default/img/tabs-border-bottom.png
Binary files differ
diff --git a/module/webui/themes/Default/img/user-actions-logout.png b/module/webui/themes/Default/img/user-actions-logout.png
new file mode 100644
index 000000000..0010931e2
--- /dev/null
+++ b/module/webui/themes/Default/img/user-actions-logout.png
Binary files differ
diff --git a/module/webui/themes/Default/img/user-actions-profile.png b/module/webui/themes/Default/img/user-actions-profile.png
new file mode 100644
index 000000000..46573fff6
--- /dev/null
+++ b/module/webui/themes/Default/img/user-actions-profile.png
Binary files differ
diff --git a/module/webui/themes/Default/img/user-info.png b/module/webui/themes/Default/img/user-info.png
new file mode 100644
index 000000000..6e643100f
--- /dev/null
+++ b/module/webui/themes/Default/img/user-info.png
Binary files differ
diff --git a/module/webui/themes/Default/js/render/admin.coffee b/module/webui/themes/Default/js/render/admin.coffee
new file mode 100644
index 000000000..5afbcbb66
--- /dev/null
+++ b/module/webui/themes/Default/js/render/admin.coffee
@@ -0,0 +1,58 @@
+root = this
+
+window.addEvent "domready", ->
+
+ root.passwordDialog = new MooDialog {destroyOnHide: false}
+ root.passwordDialog.setContent $ 'password_box'
+
+ $("login_password_reset").addEvent "click", (e) -> root.passwordDialog.close()
+ $("login_password_button").addEvent "click", (e) ->
+
+ newpw = $("login_new_password").get("value")
+ newpw2 = $("login_new_password2").get("value")
+
+ if newpw is newpw2
+ form = $("password_form")
+ form.set "send", {
+ onSuccess: (data) ->
+ root.notify.alert "Success", {
+ 'className': 'success'
+ }
+ onFailure: (data) ->
+ root.notify.alert "Error", {
+ 'className': 'error'
+ }
+ }
+
+ form.send()
+
+ root.passwordDialog.close()
+ else
+ alert '{{_("Passwords did not match.")}}'
+
+ e.stop()
+
+ for item in $$(".change_password")
+ id = item.get("id")
+ user = id.split("|")[1]
+ $("user_login").set("value", user)
+ item.addEvent "click", (e) -> root.passwordDialog.open()
+
+ $('quit-pyload').addEvent "click", (e) ->
+ new MooDialog.Confirm "{{_('You are really sure you want to quit pyLoad?')}}", ->
+ new Request.JSON({
+ url: '/api/kill'
+ method: 'get'
+ }).send()
+ , ->
+ e.stop()
+
+ $('restart-pyload').addEvent "click", (e) ->
+ new MooDialog.Confirm "{{_('Are you sure you want to restart pyLoad?')}}", ->
+ new Request.JSON({
+ url: '/api/restart'
+ method: 'get'
+ onSuccess: (data) -> alert "{{_('pyLoad restarted')}}"
+ }).send()
+ , ->
+ e.stop()
diff --git a/module/webui/themes/Default/js/render/admin.min.js b/module/webui/themes/Default/js/render/admin.min.js
new file mode 100644
index 000000000..94a5e494d
--- /dev/null
+++ b/module/webui/themes/Default/js/render/admin.min.js
@@ -0,0 +1,3 @@
+{% autoescape true %}
+var root;root=this;window.addEvent("domready",function(){var f,c,b,e,a,d;root.passwordDialog=new MooDialog({destroyOnHide:false});root.passwordDialog.setContent($("password_box"));$("login_password_reset").addEvent("click",function(g){return root.passwordDialog.close()});$("login_password_button").addEvent("click",function(j){var h,i,g;i=$("login_new_password").get("value");g=$("login_new_password2").get("value");if(i===g){h=$("password_form");h.set("send",{onSuccess:function(k){return root.notify.alert("Success",{className:"success"})},onFailure:function(k){return root.notify.alert("Error",{className:"error"})}});h.send();root.passwordDialog.close()}else{alert('{{_("Passwords did not match.")}}')}return j.stop()});d=$$(".change_password");for(e=0,a=d.length;e<a;e++){c=d[e];f=c.get("id");b=f.split("|")[1];$("user_login").set("value",b);c.addEvent("click",function(g){return root.passwordDialog.open()})}$("quit-pyload").addEvent("click",function(g){new MooDialog.Confirm("{{_('You are really sure you want to quit pyLoad?')}}",function(){return new Request.JSON({url:"/api/kill",method:"get"}).send()},function(){});return g.stop()});return $("restart-pyload").addEvent("click",function(g){new MooDialog.Confirm("{{_('Are you sure you want to restart pyLoad?')}}",function(){return new Request.JSON({url:"/api/restart",method:"get",onSuccess:function(h){return alert("{{_('pyLoad restarted')}}")}}).send()},function(){});return g.stop()})});
+{% endautoescape %}
diff --git a/module/webui/themes/Default/js/render/base.coffee b/module/webui/themes/Default/js/render/base.coffee
new file mode 100644
index 000000000..07b8bfb6f
--- /dev/null
+++ b/module/webui/themes/Default/js/render/base.coffee
@@ -0,0 +1,177 @@
+# External scope
+root = this
+
+# helper functions
+humanFileSize = (size) ->
+ filesizename = new Array("B", "KiB", "MiB", "GiB", "TiB", "PiB")
+ loga = Math.log(size) / Math.log(1024)
+ i = Math.floor(loga)
+ a = Math.pow(1024, i)
+ if size is 0 then "0 B" else (Math.round(size * 100 / a) / 100 + " " + filesizename[i])
+
+
+parseUri = () ->
+ oldString = $("add_links").value
+ regxp = new RegExp('(ht|f)tp(s?):\/\/[a-zA-Z0-9\-\.\/\?=_&%#]+[<| |\"|\'|\r|\n|\t]{1}', 'g')
+ resu = oldString.match regxp
+ return if resu == null
+ res = ""
+
+ for part in resu
+ if part.indexOf(" ") != -1
+ res = res + part.replace(" ", " \n")
+ else if part.indexOf("\t") != -1
+ res = res + part.replace("\t", " \n")
+ else if part.indexOf("\r") != -1
+ res = res + part.replace("\r", " \n")
+ else if part.indexOf("\"") != -1
+ res = res + part.replace("\"", " \n")
+ else if part.indexOf("<") != -1
+ res = res + part.replace("<", " \n")
+ else if part.indexOf("'") != -1
+ res = res + part.replace("'", " \n")
+ else
+ res = res + part.replace("\n", " \n")
+
+ $("add_links").value = res
+
+
+Array::remove = (from, to) ->
+ rest = this.slice((to || from) + 1 || this.length)
+ this.length = from < 0 ? this.length + from : from
+ return [] if this.length == 0
+ return this.push.apply(this, rest)
+
+
+document.addEvent "domready", ->
+
+ # global notification
+ root.notify = new Purr {
+ 'mode': 'top'
+ 'position': 'center'
+ }
+
+ root.captchaBox = new MooDialog {destroyOnHide: false}
+ root.captchaBox.setContent $ 'cap_box'
+
+ root.addBox = new MooDialog {destroyOnHide: false}
+ root.addBox.setContent $ 'add_box'
+
+ $('add_form').onsubmit = ->
+ $('add_form').target = 'upload_target'
+ if $('add_name').value is "" and $('add_file').value is ""
+ alert '{{_("Please Enter a packagename.")}}'
+ return false
+ else
+ root.addBox.close()
+ return true
+
+ $('add_reset').addEvent 'click', -> root.addBox.close()
+
+ $('action_add').addEvent 'click', -> $("add_form").reset(); root.addBox.open()
+ $('action_play').addEvent 'click', -> new Request({method: 'get', url: '/api/unpauseServer'}).send()
+ $('action_cancel').addEvent 'click', -> new Request({method: 'get', url: '/api/stopAllDownloads'}).send()
+ $('action_stop').addEvent 'click', -> new Request({method: 'get', url: '/api/pauseServer'}).send()
+
+
+ # captcha events
+
+ $('cap_info').addEvent 'click', ->
+ load_captcha "get", ""
+ root.captchaBox.open()
+ $('cap_reset').addEvent 'click', -> root.captchaBox.close()
+ $('cap_form').addEvent 'submit', (e) ->
+ submit_captcha()
+ e.stop()
+
+ $('cap_positional').addEvent 'click', on_captcha_click
+
+ new Request.JSON({
+ url: "/json/status"
+ onSuccess: LoadJsonToContent
+ secure: false
+ async: true
+ initialDelay: 0
+ delay: 4000
+ limit: 3000
+ }).startTimer()
+
+
+LoadJsonToContent = (data) ->
+ $("speed").set 'text', humanFileSize(data.speed)+"/s"
+ $("aktiv").set 'text', data.active
+ $("aktiv_from").set 'text', data.queue
+ $("aktiv_total").set 'text', data.total
+
+ if data.captcha
+ if $("cap_info").getStyle("display") != "inline"
+ $("cap_info").setStyle 'display', 'inline'
+ root.notify.alert '{{_("New Captcha Request")}}', {
+ 'className': 'notify'
+ }
+ else
+ $("cap_info").setStyle 'display', 'none'
+
+
+ if data.download
+ $("time").set 'text', ' {{_("on")}}'
+ $("time").setStyle 'background-color', "#8ffc25"
+ else
+ $("time").set 'text', ' {{_("off")}}'
+ $("time").setStyle 'background-color', "#fc6e26"
+
+ if data.reconnect
+ $("reconnect").set 'text', ' {{_("on")}}'
+ $("reconnect").setStyle 'background-color', "#8ffc25"
+ else
+ $("reconnect").set 'text', ' {{_("off")}}'
+ $("reconnect").setStyle 'background-color', "#fc6e26"
+
+ return null
+
+
+set_captcha = (data) ->
+ $('cap_id').set 'value', data.id
+ if (data.result_type is 'textual')
+ $('cap_textual_img').set 'src', data.src
+ $('cap_title').set 'text', '{{_("Please read the text on the captcha.")}}'
+ $('cap_submit').setStyle 'display', 'inline'
+ $('cap_textual').setStyle 'display', 'block'
+ $('cap_positional').setStyle 'display', 'none'
+
+ else if (data.result_type == 'positional')
+ $('cap_positional_img').set('src', data.src)
+ $('cap_title').set('text', '{{_("Please click on the right captcha position.")}}')
+ $('cap_submit').setStyle('display', 'none')
+ $('cap_textual').setStyle('display', 'none')
+
+
+load_captcha = (method, post) ->
+ new Request.JSON({
+ url: "/json/set_captcha"
+ onSuccess: (data) -> set_captcha(data) if data.captcha else clear_captcha()
+ secure: false
+ async: true
+ method: method
+ }).send(post)
+
+
+clear_captcha = ->
+ $('cap_textual').setStyle 'display', 'none'
+ $('cap_textual_img').set 'src', ''
+ $('cap_positional').setStyle 'display', 'none'
+ $('cap_positional_img').set 'src', ''
+ $('cap_title').set 'text', '{{_("No Captchas to read.")}}'
+
+
+submit_captcha = ->
+ load_captcha("post", "cap_id=" + $('cap_id').get('value') + "&cap_result=" + $('cap_result').get('value') )
+ $('cap_result').set('value', '')
+
+
+on_captcha_click = (e) ->
+ position = e.target.getPosition()
+ x = e.page.x - position.x
+ y = e.page.y - position.y
+ $('cap_result').value = x + "," + y
+ submit_captcha()
diff --git a/module/webui/themes/Default/js/render/base.min.js b/module/webui/themes/Default/js/render/base.min.js
new file mode 100644
index 000000000..1ba1d73f9
--- /dev/null
+++ b/module/webui/themes/Default/js/render/base.min.js
@@ -0,0 +1,3 @@
+{% autoescape true %}
+var LoadJsonToContent,clear_captcha,humanFileSize,load_captcha,on_captcha_click,parseUri,root,set_captcha,submit_captcha;root=this;humanFileSize=function(f){var c,d,e,b;d=new Array("B","KiB","MiB","GiB","TiB","PiB");b=Math.log(f)/Math.log(1024);e=Math.floor(b);c=Math.pow(1024,e);if(f===0){return"0 B"}else{return Math.round(f*100/c)/100+" "+d[e]}};parseUri=function(){var b,c,g,e,d,f,a;b=$("add_links").value;g=new RegExp("(ht|f)tp(s?)://[a-zA-Z0-9-./?=_&%#]+[<| |\"|'|\r|\n|\t]{1}","g");d=b.match(g);if(d===null){return}e="";for(f=0,a=d.length;f<a;f++){c=d[f];if(c.indexOf(" ")!==-1){e=e+c.replace(" "," \n")}else{if(c.indexOf("\t")!==-1){e=e+c.replace("\t"," \n")}else{if(c.indexOf("\r")!==-1){e=e+c.replace("\r"," \n")}else{if(c.indexOf('"')!==-1){e=e+c.replace('"'," \n")}else{if(c.indexOf("<")!==-1){e=e+c.replace("<"," \n")}else{if(c.indexOf("'")!==-1){e=e+c.replace("'"," \n")}else{e=e+c.replace("\n"," \n")}}}}}}}return $("add_links").value=e};Array.prototype.remove=function(d,c){var a,b;a=this.slice((c||d)+1||this.length);this.length=(b=d<0)!=null?b:this.length+{from:d};if(this.length===0){return[]}return this.push.apply(this,a)};document.addEvent("domready",function(){root.notify=new Purr({mode:"top",position:"center"});root.captchaBox=new MooDialog({destroyOnHide:false});root.captchaBox.setContent($("cap_box"));root.addBox=new MooDialog({destroyOnHide:false});root.addBox.setContent($("add_box"));$("add_form").onsubmit=function(){$("add_form").target="upload_target";if($("add_name").value===""&&$("add_file").value===""){alert('{{_("Please Enter a packagename.")}}');return false}else{root.addBox.close();return true}};$("add_reset").addEvent("click",function(){return root.addBox.close()});$("action_add").addEvent("click",function(){$("add_form").reset();return root.addBox.open()});$("action_play").addEvent("click",function(){return new Request({method:"get",url:"/api/unpauseServer"}).send()});$("action_cancel").addEvent("click",function(){return new Request({method:"get",url:"/api/stopAllDownloads"}).send()});$("action_stop").addEvent("click",function(){return new Request({method:"get",url:"/api/pauseServer"}).send()});$("cap_info").addEvent("click",function(){load_captcha("get","");return root.captchaBox.open()});$("cap_reset").addEvent("click",function(){return root.captchaBox.close()});$("cap_form").addEvent("submit",function(a){submit_captcha();return a.stop()});$("cap_positional").addEvent("click",on_captcha_click);return new Request.JSON({url:"/json/status",onSuccess:LoadJsonToContent,secure:false,async:true,initialDelay:0,delay:4000,limit:3000}).startTimer()});LoadJsonToContent=function(a){$("speed").set("text",humanFileSize(a.speed)+"/s");$("aktiv").set("text",a.active);$("aktiv_from").set("text",a.queue);$("aktiv_total").set("text",a.total);if(a.captcha){if($("cap_info").getStyle("display")!=="inline"){$("cap_info").setStyle("display","inline");root.notify.alert('{{_("New Captcha Request")}}',{className:"notify"})}}else{$("cap_info").setStyle("display","none")}if(a.download){$("time").set("text",' {{_("on")}}');$("time").setStyle("background-color","#8ffc25")}else{$("time").set("text",' {{_("off")}}');$("time").setStyle("background-color","#fc6e26")}if(a.reconnect){$("reconnect").set("text",' {{_("on")}}');$("reconnect").setStyle("background-color","#8ffc25")}else{$("reconnect").set("text",' {{_("off")}}');$("reconnect").setStyle("background-color","#fc6e26")}return null};set_captcha=function(a){$("cap_id").set("value",a.id);if(a.result_type==="textual"){$("cap_textual_img").set("src",a.src);$("cap_title").set("text",'{{_("Please read the text on the captcha.")}}');$("cap_submit").setStyle("display","inline");$("cap_textual").setStyle("display","block");return $("cap_positional").setStyle("display","none")}else{if(a.result_type==="positional"){$("cap_positional_img").set("src",a.src);$("cap_title").set("text",'{{_("Please click on the right captcha position.")}}');$("cap_submit").setStyle("display","none");return $("cap_textual").setStyle("display","none")}}};load_captcha=function(b,a){return new Request.JSON({url:"/json/set_captcha",onSuccess:function(c){return set_captcha(c)(c.captcha?void 0:clear_captcha())},secure:false,async:true,method:b}).send(a)};clear_captcha=function(){$("cap_textual").setStyle("display","none");$("cap_textual_img").set("src","");$("cap_positional").setStyle("display","none");$("cap_positional_img").set("src","");return $("cap_title").set("text",'{{_("No Captchas to read.")}}')};submit_captcha=function(){load_captcha("post","cap_id="+$("cap_id").get("value")+"&cap_result="+$("cap_result").get("value"));$("cap_result").set("value","");return false};on_captcha_click=function(c){var b,a,d;b=c.target.getPosition();a=c.page.x-b.x;d=c.page.y-b.y;$("cap_result").value=a+","+d;return submit_captcha()};
+{% endautoescape %}
diff --git a/module/webui/themes/Default/js/render/package.js b/module/webui/themes/Default/js/render/package.js
new file mode 100644
index 000000000..659a8e6fc
--- /dev/null
+++ b/module/webui/themes/Default/js/render/package.js
@@ -0,0 +1,376 @@
+var root = this;
+
+document.addEvent("domready", function() {
+ root.load = new Fx.Tween($("load-indicator"), {link: "cancel"});
+ root.load.set("opacity", 0);
+
+
+ root.packageBox = new MooDialog({destroyOnHide: false});
+ root.packageBox.setContent($('pack_box'));
+
+ $('pack_reset').addEvent('click', function() {
+ $('pack_form').reset();
+ root.packageBox.close();
+ });
+});
+
+function indicateLoad() {
+ //$("load-indicator").reveal();
+ root.load.start("opacity", 1)
+}
+
+function indicateFinish() {
+ root.load.start("opacity", 0)
+}
+
+function indicateSuccess() {
+ indicateFinish();
+ root.notify.alert('{{_("Success")}}.', {
+ 'className': 'success'
+ });
+}
+
+function indicateFail() {
+ indicateFinish();
+ root.notify.alert('{{_("Failed")}}.', {
+ 'className': 'error'
+ });
+}
+
+var PackageUI = new Class({
+ initialize: function(url, type) {
+ this.url = url;
+ this.type = type;
+ this.packages = [];
+ this.parsePackages();
+
+ this.sorts = new Sortables($("package-list"), {
+ constrain: false,
+ clone: true,
+ revert: true,
+ opacity: 0.4,
+ handle: ".package_drag",
+ onComplete: this.saveSort.bind(this)
+ });
+
+ $("del_finished").addEvent("click", this.deleteFinished.bind(this));
+ $("restart_failed").addEvent("click", this.restartFailed.bind(this));
+
+ },
+
+ parsePackages: function() {
+ $("package-list").getChildren("li").each(function(ele) {
+ var id = ele.getFirst().get("id").match(/[0-9]+/);
+ this.packages.push(new Package(this, id, ele))
+ }.bind(this))
+ },
+
+ loadPackages: function() {
+ },
+
+ deleteFinished: function() {
+ indicateLoad();
+ new Request.JSON({
+ method: 'get',
+ url: '/api/deleteFinished',
+ onSuccess: function(data) {
+ if (data.length > 0) {
+ window.location.reload()
+ } else {
+ this.packages.each(function(pack) {
+ pack.close();
+ });
+ indicateSuccess();
+ }
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ },
+
+ restartFailed: function() {
+ indicateLoad();
+ new Request.JSON({
+ method: 'get',
+ url: '/api/restartFailed',
+ onSuccess: function(data) {
+ this.packages.each(function(pack) {
+ pack.close();
+ });
+ indicateSuccess();
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ },
+
+ startSort: function(ele, copy) {
+ },
+
+ saveSort: function(ele, copy) {
+ var order = [];
+ this.sorts.serialize(function(li, pos) {
+ if (li == ele && ele.retrieve("order") != pos) {
+ order.push(ele.retrieve("pid") + "|" + pos)
+ }
+ li.store("order", pos)
+ });
+ if (order.length > 0) {
+ indicateLoad();
+ new Request.JSON({
+ method: 'get',
+ url: '/json/package_order/' + order[0],
+ onSuccess: indicateFinish,
+ onFailure: indicateFail
+ }).send();
+ }
+ }
+
+});
+
+var Package = new Class({
+ initialize: function(ui, id, ele, data) {
+ this.ui = ui;
+ this.id = id;
+ this.linksLoaded = false;
+
+ if (!ele) {
+ this.createElement(data);
+ } else {
+ this.ele = ele;
+ this.order = ele.getElements("div.order")[0].get("html");
+ this.ele.store("order", this.order);
+ this.ele.store("pid", this.id);
+ this.parseElement();
+ }
+
+ var pname = this.ele.getElements(".packagename")[0];
+ this.buttons = new Fx.Tween(this.ele.getElements(".buttons")[0], {link: "cancel"});
+ this.buttons.set("opacity", 0);
+
+ pname.addEvent("mouseenter", function(e) {
+ this.buttons.start("opacity", 1)
+ }.bind(this));
+
+ pname.addEvent("mouseleave", function(e) {
+ this.buttons.start("opacity", 0)
+ }.bind(this));
+
+
+ },
+
+ createElement: function() {
+ alert("create")
+ },
+
+ parseElement: function() {
+ var imgs = this.ele.getElements('img');
+
+ this.name = this.ele.getElements('.name')[0];
+ this.folder = this.ele.getElements('.folder')[0];
+ this.password = this.ele.getElements('.password')[0];
+
+ imgs[1].addEvent('click', this.deletePackage.bind(this));
+ imgs[2].addEvent('click', this.restartPackage.bind(this));
+ imgs[3].addEvent('click', this.editPackage.bind(this));
+ imgs[4].addEvent('click', this.movePackage.bind(this));
+
+ this.ele.getElement('.packagename').addEvent('click', this.toggle.bind(this));
+
+ },
+
+ loadLinks: function() {
+ indicateLoad();
+ new Request.JSON({
+ method: 'get',
+ url: '/json/package/' + this.id,
+ onSuccess: this.createLinks.bind(this),
+ onFailure: indicateFail
+ }).send();
+ },
+
+ createLinks: function(data) {
+ var ul = $("sort_children_{id}".substitute({'id': this.id}));
+ ul.set("html", "");
+ data.links.each(function(link) {
+ link.id = link.fid;
+ var li = new Element("li", {
+ 'style': {
+ 'margin-left': 0
+ }
+ });
+
+ var html = "<span style='cursor: move' class='child_status sorthandle'><img src='../img/{icon}' style='width: 12px; height:12px;'/></span>\n".substitute({'icon': link.icon});
+ html += "<span style='font-size: 15px'><a href=\"{url}\" target=\"_blank\">{name}</a></span><br /><div class='child_secrow'>".substitute({'url': link.url, 'name': link.name});
+ html += "<span class='child_status'>{statusmsg}</span>{error}&nbsp;".substitute({'statusmsg': link.statusmsg, 'error':link.error});
+ html += "<span class='child_status'>{format_size}</span>".substitute({'format_size': link.format_size});
+ html += "<span class='child_status'>{plugin}</span>&nbsp;&nbsp;".substitute({'plugin': link.plugin});
+ html += "<img title='{{_(\"Delete Link\")}}' style='cursor: pointer;' width='10px' height='10px' src='../img/delete.png' />&nbsp;&nbsp;";
+ html += "<img title='{{_(\"Restart Link\")}}' style='cursor: pointer;margin-left: -4px' width='10px' height='10px' src='../img/arrow_refresh.png' /></div>";
+
+ var div = new Element("div", {
+ 'id': "file_" + link.id,
+ 'class': "child",
+ 'html': html
+ });
+
+ li.store("order", link.order);
+ li.store("lid", link.id);
+
+ li.adopt(div);
+ ul.adopt(li);
+ });
+ this.sorts = new Sortables(ul, {
+ constrain: false,
+ clone: true,
+ revert: true,
+ opacity: 0.4,
+ handle: ".sorthandle",
+ onComplete: this.saveSort.bind(this)
+ });
+ this.registerLinkEvents();
+ this.linksLoaded = true;
+ indicateFinish();
+ this.toggle();
+ },
+
+ registerLinkEvents: function() {
+ this.ele.getElements('.child').each(function(child) {
+ var lid = child.get('id').match(/[0-9]+/);
+ var imgs = child.getElements('.child_secrow img');
+ imgs[0].addEvent('click', function(e) {
+ new Request({
+ method: 'get',
+ url: '/api/deleteFiles/[' + this + "]",
+ onSuccess: function() {
+ $('file_' + this).nix()
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ }.bind(lid));
+
+ imgs[1].addEvent('click', function(e) {
+ new Request({
+ method: 'get',
+ url: '/api/restartFile/' + this,
+ onSuccess: function() {
+ var ele = $('file_' + this);
+ var imgs = ele.getElements("img");
+ imgs[0].set("src", "../img/status_queue.png");
+ var spans = ele.getElements(".child_status");
+ spans[1].set("html", "queued");
+ indicateSuccess();
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ }.bind(lid));
+ });
+ },
+
+ toggle: function() {
+ var child = this.ele.getElement('.children');
+ if (child.getStyle('display') == "block") {
+ child.dissolve();
+ } else {
+ if (!this.linksLoaded) {
+ this.loadLinks();
+ } else {
+ child.reveal();
+ }
+ }
+ },
+
+
+ deletePackage: function(event) {
+ indicateLoad();
+ new Request({
+ method: 'get',
+ url: '/api/deletePackages/[' + this.id + "]",
+ onSuccess: function() {
+ this.ele.nix();
+ indicateFinish();
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ //hide_pack();
+ event.stop();
+ },
+
+ restartPackage: function(event) {
+ indicateLoad();
+ new Request({
+ method: 'get',
+ url: '/api/restartPackage/' + this.id,
+ onSuccess: function() {
+ this.close();
+ indicateSuccess();
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ event.stop();
+ },
+
+ close: function() {
+ var child = this.ele.getElement('.children');
+ if (child.getStyle('display') == "block") {
+ child.dissolve();
+ }
+ var ul = $("sort_children_{id}".substitute({'id': this.id}));
+ ul.erase("html");
+ this.linksLoaded = false;
+ },
+
+ movePackage: function(event) {
+ indicateLoad();
+ new Request({
+ method: 'get',
+ url: '/json/move_package/' + ((this.ui.type + 1) % 2) + "/" + this.id,
+ onSuccess: function() {
+ this.ele.nix();
+ indicateFinish();
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ event.stop();
+ },
+
+ editPackage: function(event) {
+ $("pack_form").removeEvents("submit");
+ $("pack_form").addEvent("submit", this.savePackage.bind(this));
+
+ $("pack_id").set("value", this.id);
+ $("pack_name").set("value", this.name.get("text"));
+ $("pack_folder").set("value", this.folder.get("text"));
+ $("pack_pws").set("value", this.password.get("text"));
+
+ root.packageBox.open();
+ event.stop();
+ },
+
+ savePackage: function(event) {
+ $("pack_form").send();
+ this.name.set("text", $("pack_name").get("value"));
+ this.folder.set("text", $("pack_folder").get("value"));
+ this.password.set("text", $("pack_pws").get("value"));
+ root.packageBox.close();
+ event.stop();
+ },
+
+ saveSort: function(ele, copy) {
+ var order = [];
+ this.sorts.serialize(function(li, pos) {
+ if (li == ele && ele.retrieve("order") != pos) {
+ order.push(ele.retrieve("lid") + "|" + pos)
+ }
+ li.store("order", pos)
+ });
+ if (order.length > 0) {
+ indicateLoad();
+ new Request.JSON({
+ method: 'get',
+ url: '/json/link_order/' + order[0],
+ onSuccess: indicateFinish,
+ onFailure: indicateFail
+ }).send();
+ }
+ }
+
+});
diff --git a/module/webui/themes/Default/js/render/settings.coffee b/module/webui/themes/Default/js/render/settings.coffee
new file mode 100644
index 000000000..d522741b9
--- /dev/null
+++ b/module/webui/themes/Default/js/render/settings.coffee
@@ -0,0 +1,107 @@
+root = this
+
+window.addEvent 'domready', ->
+ root.accountDialog = new MooDialog {destroyOnHide: false}
+ root.accountDialog.setContent $ 'account_box'
+
+ new TinyTab $$('#toptabs li a'), $$('#tabs-body > span')
+
+ $$('ul.nav').each (nav) ->
+ new MooDropMenu nav, {
+ onOpen: (el) -> el.fade 'in'
+ onClose: (el) -> el.fade 'out'
+ onInitialize: (el) -> el.fade('hide').set 'tween', {duration:500}
+ }
+
+ new SettingsUI()
+
+
+class SettingsUI
+ constructor: ->
+ @menu = $$ "#general-menu li"
+ @menu.append $$ "#plugin-menu li"
+
+ @name = $ "tabsback"
+ @general = $ "general_form_content"
+ @plugin = $ "plugin_form_content"
+
+ el.addEvent 'click', @menuClick.bind(this) for el in @menu
+
+ $("general|submit").addEvent "click", @configSubmit.bind(this)
+ $("plugin|submit").addEvent "click", @configSubmit.bind(this)
+
+ $("account_add").addEvent "click", (e) ->
+ root.accountDialog.open()
+ e.stop()
+
+ $("account_reset").addEvent "click", (e) ->
+ root.accountDialog.close()
+
+ $("account_add_button").addEvent "click", @addAccount.bind(this)
+ $("account_submit").addEvent "click", @submitAccounts.bind(this)
+
+
+ menuClick: (e) ->
+ [category, section] = e.target.get("id").split("|")
+ name = e.target.get "text"
+
+
+ target = if category is "general" then @general else @plugin
+ target.dissolve()
+
+ new Request({
+ "method" : "get"
+ "url" : "/json/load_config/#{category}/#{section}"
+ 'onSuccess': (data) =>
+ target.set "html", data
+ target.reveal()
+ this.name.set "text", name
+ }).send()
+
+
+ configSubmit: (e) ->
+ category = e.target.get("id").split("|")[0]
+ form = $("#{category}_form")
+
+ form.set "send", {
+ 'method': "post"
+ 'url': "/json/save_config/#{category}"
+ "onSuccess" : ->
+ root.notify.alert '{{ _("Settings saved.")}}', {
+ 'className': 'success'
+ }
+ 'onFailure': ->
+ root.notify.alert '{{ _("Error occured.")}}', {
+ 'className': 'error'
+ }
+ }
+ form.send()
+ e.stop()
+
+ addAccount: (e) ->
+ form = $ "add_account_form"
+ form.set "send", {
+ 'method': "post"
+ "onSuccess" : -> window.location.reload()
+ 'onFailure': ->
+ root.notify.alert '{{_("Error occured.")}}', {
+ 'className': 'error'
+ }
+ }
+
+ form.send()
+ e.stop()
+
+ submitAccounts: (e) ->
+ form = $ "account_form"
+ form.set "send", {
+ 'method': "post",
+ "onSuccess" : -> window.location.reload()
+ 'onFailure': ->
+ root.notify.alert('{{ _("Error occured.") }}', {
+ 'className': 'error'
+ })
+ }
+
+ form.send()
+ e.stop()
diff --git a/module/webui/themes/Default/js/render/settings.min.js b/module/webui/themes/Default/js/render/settings.min.js
new file mode 100644
index 000000000..41d1cb25a
--- /dev/null
+++ b/module/webui/themes/Default/js/render/settings.min.js
@@ -0,0 +1,3 @@
+{% autoescape true %}
+var SettingsUI,root;var __bind=function(a,b){return function(){return a.apply(b,arguments)}};root=this;window.addEvent("domready",function(){root.accountDialog=new MooDialog({destroyOnHide:false});root.accountDialog.setContent($("account_box"));new TinyTab($$("#toptabs li a"),$$("#tabs-body > span"));$$("ul.nav").each(function(a){return new MooDropMenu(a,{onOpen:function(b){return b.fade("in")},onClose:function(b){return b.fade("out")},onInitialize:function(b){return b.fade("hide").set("tween",{duration:500})}})});return new SettingsUI()});SettingsUI=(function(){function a(){var c,e,b,d;this.menu=$$("#general-menu li");this.menu.append($$("#plugin-menu li"));this.name=$("tabsback");this.general=$("general_form_content");this.plugin=$("plugin_form_content");d=this.menu;for(e=0,b=d.length;e<b;e++){c=d[e];c.addEvent("click",this.menuClick.bind(this))}$("general|submit").addEvent("click",this.configSubmit.bind(this));$("plugin|submit").addEvent("click",this.configSubmit.bind(this));$("account_add").addEvent("click",function(f){root.accountDialog.open();return f.stop()});$("account_reset").addEvent("click",function(f){return root.accountDialog.close()});$("account_add_button").addEvent("click",this.addAccount.bind(this));$("account_submit").addEvent("click",this.submitAccounts.bind(this))}a.prototype.menuClick=function(h){var c,b,g,f,d;d=h.target.get("id").split("|"),c=d[0],g=d[1];b=h.target.get("text");f=c==="general"?this.general:this.plugin;f.dissolve();return new Request({method:"get",url:"/json/load_config/"+c+"/"+g,onSuccess:__bind(function(e){f.set("html",e);f.reveal();return this.name.set("text",b)},this)}).send()};a.prototype.configSubmit=function(d){var c,b;c=d.target.get("id").split("|")[0];b=$(""+c+"_form");b.set("send",{method:"post",url:"/json/save_config/"+c,onSuccess:function(){return root.notify.alert('{{ _("Settings saved.")}}',{className:"success"})},onFailure:function(){return root.notify.alert('{{ _("Error occured.")}}',{className:"error"})}});b.send();return d.stop()};a.prototype.addAccount=function(c){var b;b=$("add_account_form");b.set("send",{method:"post",onSuccess:function(){return window.location.reload()},onFailure:function(){return root.notify.alert('{{_("Error occured.")}}',{className:"error"})}});b.send();return c.stop()};a.prototype.submitAccounts=function(c){var b;b=$("account_form");b.set("send",{method:"post",onSuccess:function(){return window.location.reload()},onFailure:function(){return root.notify.alert('{{ _("Error occured.") }}',{className:"error"})}});b.send();return c.stop()};return a})();
+{% endautoescape %}
diff --git a/module/webui/themes/Default/js/static/MooDialog.js b/module/webui/themes/Default/js/static/MooDialog.js
new file mode 100644
index 000000000..45a52496f
--- /dev/null
+++ b/module/webui/themes/Default/js/static/MooDialog.js
@@ -0,0 +1,140 @@
+/*
+---
+name: MooDialog
+description: The base class of MooDialog
+authors: Arian Stolwijk
+license: MIT-style license
+requires: [Core/Class, Core/Element, Core/Element.Style, Core/Element.Event]
+provides: [MooDialog, Element.MooDialog]
+...
+*/
+
+
+var MooDialog = new Class({
+
+ Implements: [Options, Events],
+
+ options: {
+ 'class': 'MooDialog',
+ title: null,
+ scroll: true, // IE
+ forceScroll: false,
+ useEscKey: true,
+ destroyOnHide: true,
+ autoOpen: true,
+ closeButton: true,
+ onInitialize: function(){
+ this.wrapper.setStyle('display', 'none');
+ },
+ onBeforeOpen: function(){
+ this.wrapper.setStyle('display', 'block');
+ this.fireEvent('show');
+ },
+ onBeforeClose: function(){
+ this.wrapper.setStyle('display', 'none');
+ this.fireEvent('hide');
+ }/*,
+ onOpen: function(){},
+ onClose: function(){},
+ onShow: function(){},
+ onHide: function(){},
+ onInitialize: function(wrapper){},
+ onContentChange: function(content){}*/
+ },
+
+ initialize: function(options){
+ this.setOptions(options);
+ this.options.inject = this.options.inject || document.body;
+ options = this.options;
+
+ var wrapper = this.wrapper = new Element('div.' + options['class'].replace(' ', '.')).inject(options.inject);
+ this.content = new Element('div.content').inject(wrapper);
+
+ if (options.title){
+ this.title = new Element('div.title').set('text', options.title).inject(wrapper);
+ wrapper.addClass('MooDialogTitle');
+ }
+
+ if (options.closeButton){
+ this.closeButton = new Element('a.close', {
+ events: {click: this.close.bind(this)}
+ }).inject(wrapper);
+ }
+
+
+ /*<ie6>*/// IE 6 scroll
+ if ((options.scroll && Browser.ie6) || options.forceScroll){
+ wrapper.setStyle('position', 'absolute');
+ var position = wrapper.getPosition(options.inject);
+ window.addEvent('scroll', function(){
+ var scroll = document.getScroll();
+ wrapper.setPosition({
+ x: position.x + scroll.x,
+ y: position.y + scroll.y
+ });
+ });
+ }
+ /*</ie6>*/
+
+ if (options.useEscKey){
+ // Add event for the esc key
+ document.addEvent('keydown', function(e){
+ if (e.key == 'esc') this.close();
+ }.bind(this));
+ }
+
+ this.addEvent('hide', function(){
+ if (options.destroyOnHide) this.destroy();
+ }.bind(this));
+
+ this.fireEvent('initialize', wrapper);
+ },
+
+ setContent: function(){
+ var content = Array.from(arguments);
+ if (content.length == 1) content = content[0];
+
+ this.content.empty();
+
+ var type = typeOf(content);
+ if (['string', 'number'].contains(type)) this.content.set('text', content);
+ else this.content.adopt(content);
+
+ this.fireEvent('contentChange', this.content);
+
+ return this;
+ },
+
+ open: function(){
+ this.fireEvent('beforeOpen', this.wrapper).fireEvent('open');
+ this.opened = true;
+ return this;
+ },
+
+ close: function(){
+ this.fireEvent('beforeClose', this.wrapper).fireEvent('close');
+ this.opened = false;
+ return this;
+ },
+
+ destroy: function(){
+ this.wrapper.destroy();
+ },
+
+ toElement: function(){
+ return this.wrapper;
+ }
+
+});
+
+
+Element.implement({
+
+ MooDialog: function(options){
+ this.store('MooDialog',
+ new MooDialog(options).setContent(this).open()
+ );
+ return this;
+ }
+
+});
diff --git a/module/webui/themes/Default/js/static/MooDialog.min.js b/module/webui/themes/Default/js/static/MooDialog.min.js
new file mode 100644
index 000000000..90b3ae100
--- /dev/null
+++ b/module/webui/themes/Default/js/static/MooDialog.min.js
@@ -0,0 +1 @@
+var MooDialog=new Class({Implements:[Options,Events],options:{"class":"MooDialog",title:null,scroll:!0,forceScroll:!1,useEscKey:!0,destroyOnHide:!0,autoOpen:!0,closeButton:!0,onInitialize:function(){this.wrapper.setStyle("display","none")},onBeforeOpen:function(){this.wrapper.setStyle("display","block"),this.fireEvent("show")},onBeforeClose:function(){this.wrapper.setStyle("display","none"),this.fireEvent("hide")}},initialize:function(t){this.setOptions(t),this.options.inject=this.options.inject||document.body,t=this.options;var e=this.wrapper=new Element("div."+t["class"].replace(" ",".")).inject(t.inject);if(this.content=new Element("div.content").inject(e),t.title&&(this.title=new Element("div.title").set("text",t.title).inject(e),e.addClass("MooDialogTitle")),t.closeButton&&(this.closeButton=new Element("a.close",{events:{click:this.close.bind(this)}}).inject(e)),t.scroll&&Browser.ie6||t.forceScroll){e.setStyle("position","absolute");var n=e.getPosition(t.inject);window.addEvent("scroll",function(){var t=document.getScroll();e.setPosition({x:n.x+t.x,y:n.y+t.y})})}t.useEscKey&&document.addEvent("keydown",function(t){"esc"==t.key&&this.close()}.bind(this)),this.addEvent("hide",function(){t.destroyOnHide&&this.destroy()}.bind(this)),this.fireEvent("initialize",e)},setContent:function(){var t=Array.from(arguments);1==t.length&&(t=t[0]),this.content.empty();var e=typeOf(t);return["string","number"].contains(e)?this.content.set("text",t):this.content.adopt(t),this.fireEvent("contentChange",this.content),this},open:function(){return this.fireEvent("beforeOpen",this.wrapper).fireEvent("open"),this.opened=!0,this},close:function(){return this.fireEvent("beforeClose",this.wrapper).fireEvent("close"),this.opened=!1,this},destroy:function(){this.wrapper.destroy()},toElement:function(){return this.wrapper}});Element.implement({MooDialog:function(t){return this.store("MooDialog",new MooDialog(t).setContent(this).open()),this}}); \ No newline at end of file
diff --git a/module/webui/themes/Default/js/static/MooDropMenu.js b/module/webui/themes/Default/js/static/MooDropMenu.js
new file mode 100644
index 000000000..ac0fa1874
--- /dev/null
+++ b/module/webui/themes/Default/js/static/MooDropMenu.js
@@ -0,0 +1,86 @@
+/*
+---
+description: This provides a simple Drop Down menu with infinit levels
+
+license: MIT-style
+
+authors:
+- Arian Stolwijk
+
+requires:
+ - Core/Class.Extras
+ - Core/Element.Event
+ - Core/Selectors
+
+provides: [MooDropMenu, Element.MooDropMenu]
+
+...
+*/
+
+var MooDropMenu = new Class({
+
+ Implements: [Options, Events],
+
+ options: {
+ onOpen: function(el){
+ el.removeClass('close').addClass('open');
+ },
+ onClose: function(el){
+ el.removeClass('open').addClass('close');
+ },
+ onInitialize: function(el){
+ el.removeClass('open').addClass('close');
+ },
+ mouseoutDelay: 200,
+ mouseoverDelay: 0,
+ listSelector: 'ul',
+ itemSelector: 'li',
+ openEvent: 'mouseenter',
+ closeEvent: 'mouseleave'
+ },
+
+ initialize: function(menu, options, level){
+ this.setOptions(options);
+ options = this.options;
+
+ var menu = this.menu = document.id(menu);
+
+ menu.getElements(options.itemSelector + ' > ' + options.listSelector).each(function(el){
+
+ this.fireEvent('initialize', el);
+
+ var parent = el.getParent(options.itemSelector),
+ timer;
+
+ parent.addEvent(options.openEvent, function(){
+ parent.store('DropDownOpen', true);
+
+ clearTimeout(timer);
+ if (options.mouseoverDelay) timer = this.fireEvent.delay(options.mouseoverDelay, this, ['open', el]);
+ else this.fireEvent('open', el);
+
+ }.bind(this)).addEvent(options.closeEvent, function(){
+ parent.store('DropDownOpen', false);
+
+ clearTimeout(timer);
+ timer = (function(){
+ if (!parent.retrieve('DropDownOpen')) this.fireEvent('close', el);
+ }).delay(options.mouseoutDelay, this);
+
+ }.bind(this));
+
+ }, this);
+ },
+
+ toElement: function(){
+ return this.menu
+ }
+
+});
+
+/* So you can do like this $('nav').MooDropMenu(); or even $('nav').MooDropMenu().setStyle('border',1); */
+Element.implement({
+ MooDropMenu: function(options){
+ return this.store('MooDropMenu', new MooDropMenu(this, options));
+ }
+});
diff --git a/module/webui/themes/Default/js/static/MooDropMenu.min.js b/module/webui/themes/Default/js/static/MooDropMenu.min.js
new file mode 100644
index 000000000..552ae247a
--- /dev/null
+++ b/module/webui/themes/Default/js/static/MooDropMenu.min.js
@@ -0,0 +1 @@
+var MooDropMenu=new Class({Implements:[Options,Events],options:{onOpen:function(e){e.removeClass("close").addClass("open")},onClose:function(e){e.removeClass("open").addClass("close")},onInitialize:function(e){e.removeClass("open").addClass("close")},mouseoutDelay:200,mouseoverDelay:0,listSelector:"ul",itemSelector:"li",openEvent:"mouseenter",closeEvent:"mouseleave"},initialize:function(e,o){this.setOptions(o),o=this.options;var e=this.menu=document.id(e);e.getElements(o.itemSelector+" > "+o.listSelector).each(function(e){this.fireEvent("initialize",e);var n,t=e.getParent(o.itemSelector);t.addEvent(o.openEvent,function(){t.store("DropDownOpen",!0),clearTimeout(n),o.mouseoverDelay?n=this.fireEvent.delay(o.mouseoverDelay,this,["open",e]):this.fireEvent("open",e)}.bind(this)).addEvent(o.closeEvent,function(){t.store("DropDownOpen",!1),clearTimeout(n),n=function(){t.retrieve("DropDownOpen")||this.fireEvent("close",e)}.delay(o.mouseoutDelay,this)}.bind(this))},this)},toElement:function(){return this.menu}});Element.implement({MooDropMenu:function(e){return this.store("MooDropMenu",new MooDropMenu(this,e))}}); \ No newline at end of file
diff --git a/module/webui/themes/Default/js/static/mootools-core.js b/module/webui/themes/Default/js/static/mootools-core.js
new file mode 100644
index 000000000..db83850fd
--- /dev/null
+++ b/module/webui/themes/Default/js/static/mootools-core.js
@@ -0,0 +1,5977 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/core/8423c12ffd6a6bfcde9ea22554aec795
+
+packager build:
+ - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady
+
+...
+*/
+
+/*
+---
+
+name: Core
+
+description: The heart of MooTools.
+
+license: MIT-style license.
+
+copyright: Copyright (c) 2006-2014 [Valerio Proietti](http://mad4milk.net/).
+
+authors: The MooTools production team (http://mootools.net/developers/)
+
+inspiration:
+ - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
+ - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
+
+provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
+
+...
+*/
+
+(function(){
+
+this.MooTools = {
+ version: '1.5.0',
+ build: '0f7b690afee9349b15909f33016a25d2e4d9f4e3'
+};
+
+// typeOf, instanceOf
+
+var typeOf = this.typeOf = function(item){
+ if (item == null) return 'null';
+ if (item.$family != null) return item.$family();
+
+ if (item.nodeName){
+ if (item.nodeType == 1) return 'element';
+ if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
+ } else if (typeof item.length == 'number'){
+ if ('callee' in item) return 'arguments';
+ if ('item' in item) return 'collection';
+ }
+
+ return typeof item;
+};
+
+var instanceOf = this.instanceOf = function(item, object){
+ if (item == null) return false;
+ var constructor = item.$constructor || item.constructor;
+ while (constructor){
+ if (constructor === object) return true;
+ constructor = constructor.parent;
+ }
+ /*<ltIE8>*/
+ if (!item.hasOwnProperty) return false;
+ /*</ltIE8>*/
+ return item instanceof object;
+};
+
+// Function overloading
+
+var Function = this.Function;
+
+var enumerables = true;
+for (var i in {toString: 1}) enumerables = null;
+if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
+
+Function.prototype.overloadSetter = function(usePlural){
+ var self = this;
+ return function(a, b){
+ if (a == null) return this;
+ if (usePlural || typeof a != 'string'){
+ for (var k in a) self.call(this, k, a[k]);
+ if (enumerables) for (var i = enumerables.length; i--;){
+ k = enumerables[i];
+ if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
+ }
+ } else {
+ self.call(this, a, b);
+ }
+ return this;
+ };
+};
+
+Function.prototype.overloadGetter = function(usePlural){
+ var self = this;
+ return function(a){
+ var args, result;
+ if (typeof a != 'string') args = a;
+ else if (arguments.length > 1) args = arguments;
+ else if (usePlural) args = [a];
+ if (args){
+ result = {};
+ for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
+ } else {
+ result = self.call(this, a);
+ }
+ return result;
+ };
+};
+
+Function.prototype.extend = function(key, value){
+ this[key] = value;
+}.overloadSetter();
+
+Function.prototype.implement = function(key, value){
+ this.prototype[key] = value;
+}.overloadSetter();
+
+// From
+
+var slice = Array.prototype.slice;
+
+Function.from = function(item){
+ return (typeOf(item) == 'function') ? item : function(){
+ return item;
+ };
+};
+
+Array.from = function(item){
+ if (item == null) return [];
+ return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
+};
+
+Number.from = function(item){
+ var number = parseFloat(item);
+ return isFinite(number) ? number : null;
+};
+
+String.from = function(item){
+ return item + '';
+};
+
+// hide, protect
+
+Function.implement({
+
+ hide: function(){
+ this.$hidden = true;
+ return this;
+ },
+
+ protect: function(){
+ this.$protected = true;
+ return this;
+ }
+
+});
+
+// Type
+
+var Type = this.Type = function(name, object){
+ if (name){
+ var lower = name.toLowerCase();
+ var typeCheck = function(item){
+ return (typeOf(item) == lower);
+ };
+
+ Type['is' + name] = typeCheck;
+ if (object != null){
+ object.prototype.$family = (function(){
+ return lower;
+ }).hide();
+
+ }
+ }
+
+ if (object == null) return null;
+
+ object.extend(this);
+ object.$constructor = Type;
+ object.prototype.$constructor = object;
+
+ return object;
+};
+
+var toString = Object.prototype.toString;
+
+Type.isEnumerable = function(item){
+ return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
+};
+
+var hooks = {};
+
+var hooksOf = function(object){
+ var type = typeOf(object.prototype);
+ return hooks[type] || (hooks[type] = []);
+};
+
+var implement = function(name, method){
+ if (method && method.$hidden) return;
+
+ var hooks = hooksOf(this);
+
+ for (var i = 0; i < hooks.length; i++){
+ var hook = hooks[i];
+ if (typeOf(hook) == 'type') implement.call(hook, name, method);
+ else hook.call(this, name, method);
+ }
+
+ var previous = this.prototype[name];
+ if (previous == null || !previous.$protected) this.prototype[name] = method;
+
+ if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
+ return method.apply(item, slice.call(arguments, 1));
+ });
+};
+
+var extend = function(name, method){
+ if (method && method.$hidden) return;
+ var previous = this[name];
+ if (previous == null || !previous.$protected) this[name] = method;
+};
+
+Type.implement({
+
+ implement: implement.overloadSetter(),
+
+ extend: extend.overloadSetter(),
+
+ alias: function(name, existing){
+ implement.call(this, name, this.prototype[existing]);
+ }.overloadSetter(),
+
+ mirror: function(hook){
+ hooksOf(this).push(hook);
+ return this;
+ }
+
+});
+
+new Type('Type', Type);
+
+// Default Types
+
+var force = function(name, object, methods){
+ var isType = (object != Object),
+ prototype = object.prototype;
+
+ if (isType) object = new Type(name, object);
+
+ for (var i = 0, l = methods.length; i < l; i++){
+ var key = methods[i],
+ generic = object[key],
+ proto = prototype[key];
+
+ if (generic) generic.protect();
+ if (isType && proto) object.implement(key, proto.protect());
+ }
+
+ if (isType){
+ var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
+ object.forEachMethod = function(fn){
+ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
+ fn.call(prototype, prototype[methods[i]], methods[i]);
+ }
+ for (var key in prototype) fn.call(prototype, prototype[key], key);
+ };
+ }
+
+ return force;
+};
+
+force('String', String, [
+ 'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
+ 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
+])('Array', Array, [
+ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
+ 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
+])('Number', Number, [
+ 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
+])('Function', Function, [
+ 'apply', 'call', 'bind'
+])('RegExp', RegExp, [
+ 'exec', 'test'
+])('Object', Object, [
+ 'create', 'defineProperty', 'defineProperties', 'keys',
+ 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
+ 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
+])('Date', Date, ['now']);
+
+Object.extend = extend.overloadSetter();
+
+Date.extend('now', function(){
+ return +(new Date);
+});
+
+new Type('Boolean', Boolean);
+
+// fixes NaN returning as Number
+
+Number.prototype.$family = function(){
+ return isFinite(this) ? 'number' : 'null';
+}.hide();
+
+// Number.random
+
+Number.extend('random', function(min, max){
+ return Math.floor(Math.random() * (max - min + 1) + min);
+});
+
+// forEach, each
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+Object.extend('forEach', function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
+ }
+});
+
+Object.each = Object.forEach;
+
+Array.implement({
+
+ /*<!ES5>*/
+ forEach: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (i in this) fn.call(bind, this[i], i, this);
+ }
+ },
+ /*</!ES5>*/
+
+ each: function(fn, bind){
+ Array.forEach(this, fn, bind);
+ return this;
+ }
+
+});
+
+// Array & Object cloning, Object merging and appending
+
+var cloneOf = function(item){
+ switch (typeOf(item)){
+ case 'array': return item.clone();
+ case 'object': return Object.clone(item);
+ default: return item;
+ }
+};
+
+Array.implement('clone', function(){
+ var i = this.length, clone = new Array(i);
+ while (i--) clone[i] = cloneOf(this[i]);
+ return clone;
+});
+
+var mergeOne = function(source, key, current){
+ switch (typeOf(current)){
+ case 'object':
+ if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
+ else source[key] = Object.clone(current);
+ break;
+ case 'array': source[key] = current.clone(); break;
+ default: source[key] = current;
+ }
+ return source;
+};
+
+Object.extend({
+
+ merge: function(source, k, v){
+ if (typeOf(k) == 'string') return mergeOne(source, k, v);
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var object = arguments[i];
+ for (var key in object) mergeOne(source, key, object[key]);
+ }
+ return source;
+ },
+
+ clone: function(object){
+ var clone = {};
+ for (var key in object) clone[key] = cloneOf(object[key]);
+ return clone;
+ },
+
+ append: function(original){
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var extended = arguments[i] || {};
+ for (var key in extended) original[key] = extended[key];
+ }
+ return original;
+ }
+
+});
+
+// Object-less types
+
+['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
+ new Type(name);
+});
+
+// Unique ID
+
+var UID = Date.now();
+
+String.extend('uniqueID', function(){
+ return (UID++).toString(36);
+});
+
+
+
+})();
+
+
+/*
+---
+
+name: Array
+
+description: Contains Array Prototypes like each, contains, and erase.
+
+license: MIT-style license.
+
+requires: [Type]
+
+provides: Array
+
+...
+*/
+
+Array.implement({
+
+ /*<!ES5>*/
+ every: function(fn, bind){
+ for (var i = 0, l = this.length >>> 0; i < l; i++){
+ if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
+ }
+ return true;
+ },
+
+ filter: function(fn, bind){
+ var results = [];
+ for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this){
+ value = this[i];
+ if (fn.call(bind, value, i, this)) results.push(value);
+ }
+ return results;
+ },
+
+ indexOf: function(item, from){
+ var length = this.length >>> 0;
+ for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){
+ if (this[i] === item) return i;
+ }
+ return -1;
+ },
+
+ map: function(fn, bind){
+ var length = this.length >>> 0, results = Array(length);
+ for (var i = 0; i < length; i++){
+ if (i in this) results[i] = fn.call(bind, this[i], i, this);
+ }
+ return results;
+ },
+
+ some: function(fn, bind){
+ for (var i = 0, l = this.length >>> 0; i < l; i++){
+ if ((i in this) && fn.call(bind, this[i], i, this)) return true;
+ }
+ return false;
+ },
+ /*</!ES5>*/
+
+ clean: function(){
+ return this.filter(function(item){
+ return item != null;
+ });
+ },
+
+ invoke: function(methodName){
+ var args = Array.slice(arguments, 1);
+ return this.map(function(item){
+ return item[methodName].apply(item, args);
+ });
+ },
+
+ associate: function(keys){
+ var obj = {}, length = Math.min(this.length, keys.length);
+ for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
+ return obj;
+ },
+
+ link: function(object){
+ var result = {};
+ for (var i = 0, l = this.length; i < l; i++){
+ for (var key in object){
+ if (object[key](this[i])){
+ result[key] = this[i];
+ delete object[key];
+ break;
+ }
+ }
+ }
+ return result;
+ },
+
+ contains: function(item, from){
+ return this.indexOf(item, from) != -1;
+ },
+
+ append: function(array){
+ this.push.apply(this, array);
+ return this;
+ },
+
+ getLast: function(){
+ return (this.length) ? this[this.length - 1] : null;
+ },
+
+ getRandom: function(){
+ return (this.length) ? this[Number.random(0, this.length - 1)] : null;
+ },
+
+ include: function(item){
+ if (!this.contains(item)) this.push(item);
+ return this;
+ },
+
+ combine: function(array){
+ for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
+ return this;
+ },
+
+ erase: function(item){
+ for (var i = this.length; i--;){
+ if (this[i] === item) this.splice(i, 1);
+ }
+ return this;
+ },
+
+ empty: function(){
+ this.length = 0;
+ return this;
+ },
+
+ flatten: function(){
+ var array = [];
+ for (var i = 0, l = this.length; i < l; i++){
+ var type = typeOf(this[i]);
+ if (type == 'null') continue;
+ array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
+ }
+ return array;
+ },
+
+ pick: function(){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (this[i] != null) return this[i];
+ }
+ return null;
+ },
+
+ hexToRgb: function(array){
+ if (this.length != 3) return null;
+ var rgb = this.map(function(value){
+ if (value.length == 1) value += value;
+ return parseInt(value, 16);
+ });
+ return (array) ? rgb : 'rgb(' + rgb + ')';
+ },
+
+ rgbToHex: function(array){
+ if (this.length < 3) return null;
+ if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
+ var hex = [];
+ for (var i = 0; i < 3; i++){
+ var bit = (this[i] - 0).toString(16);
+ hex.push((bit.length == 1) ? '0' + bit : bit);
+ }
+ return (array) ? hex : '#' + hex.join('');
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: String
+
+description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
+
+license: MIT-style license.
+
+requires: [Type, Array]
+
+provides: String
+
+...
+*/
+
+String.implement({
+
+ //<!ES6>
+ contains: function(string, index){
+ return (index ? String(this).slice(index) : String(this)).indexOf(string) > -1;
+ },
+ //</!ES6>
+
+ test: function(regex, params){
+ return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
+ },
+
+ trim: function(){
+ return String(this).replace(/^\s+|\s+$/g, '');
+ },
+
+ clean: function(){
+ return String(this).replace(/\s+/g, ' ').trim();
+ },
+
+ camelCase: function(){
+ return String(this).replace(/-\D/g, function(match){
+ return match.charAt(1).toUpperCase();
+ });
+ },
+
+ hyphenate: function(){
+ return String(this).replace(/[A-Z]/g, function(match){
+ return ('-' + match.charAt(0).toLowerCase());
+ });
+ },
+
+ capitalize: function(){
+ return String(this).replace(/\b[a-z]/g, function(match){
+ return match.toUpperCase();
+ });
+ },
+
+ escapeRegExp: function(){
+ return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ hexToRgb: function(array){
+ var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
+ return (hex) ? hex.slice(1).hexToRgb(array) : null;
+ },
+
+ rgbToHex: function(array){
+ var rgb = String(this).match(/\d{1,3}/g);
+ return (rgb) ? rgb.rgbToHex(array) : null;
+ },
+
+ substitute: function(object, regexp){
+ return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
+ if (match.charAt(0) == '\\') return match.slice(1);
+ return (object[name] != null) ? object[name] : '';
+ });
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: Number
+
+description: Contains Number Prototypes like limit, round, times, and ceil.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Number
+
+...
+*/
+
+Number.implement({
+
+ limit: function(min, max){
+ return Math.min(max, Math.max(min, this));
+ },
+
+ round: function(precision){
+ precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
+ return Math.round(this * precision) / precision;
+ },
+
+ times: function(fn, bind){
+ for (var i = 0; i < this; i++) fn.call(bind, i, this);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ }
+
+});
+
+Number.alias('each', 'times');
+
+(function(math){
+ var methods = {};
+ math.each(function(name){
+ if (!Number[name]) methods[name] = function(){
+ return Math[name].apply(null, [this].concat(Array.from(arguments)));
+ };
+ });
+ Number.implement(methods);
+})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
+
+
+/*
+---
+
+name: Function
+
+description: Contains Function Prototypes like create, bind, pass, and delay.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Function
+
+...
+*/
+
+Function.extend({
+
+ attempt: function(){
+ for (var i = 0, l = arguments.length; i < l; i++){
+ try {
+ return arguments[i]();
+ } catch (e){}
+ }
+ return null;
+ }
+
+});
+
+Function.implement({
+
+ attempt: function(args, bind){
+ try {
+ return this.apply(bind, Array.from(args));
+ } catch (e){}
+
+ return null;
+ },
+
+ /*<!ES5-bind>*/
+ bind: function(that){
+ var self = this,
+ args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
+ F = function(){};
+
+ var bound = function(){
+ var context = that, length = arguments.length;
+ if (this instanceof bound){
+ F.prototype = self.prototype;
+ context = new F;
+ }
+ var result = (!args && !length)
+ ? self.call(context)
+ : self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
+ return context == that ? result : context;
+ };
+ return bound;
+ },
+ /*</!ES5-bind>*/
+
+ pass: function(args, bind){
+ var self = this;
+ if (args != null) args = Array.from(args);
+ return function(){
+ return self.apply(bind, args || arguments);
+ };
+ },
+
+ delay: function(delay, bind, args){
+ return setTimeout(this.pass((args == null ? [] : args), bind), delay);
+ },
+
+ periodical: function(periodical, bind, args){
+ return setInterval(this.pass((args == null ? [] : args), bind), periodical);
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: Object
+
+description: Object generic methods
+
+license: MIT-style license.
+
+requires: Type
+
+provides: [Object, Hash]
+
+...
+*/
+
+(function(){
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+Object.extend({
+
+ subset: function(object, keys){
+ var results = {};
+ for (var i = 0, l = keys.length; i < l; i++){
+ var k = keys[i];
+ if (k in object) results[k] = object[k];
+ }
+ return results;
+ },
+
+ map: function(object, fn, bind){
+ var results = {};
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
+ }
+ return results;
+ },
+
+ filter: function(object, fn, bind){
+ var results = {};
+ for (var key in object){
+ var value = object[key];
+ if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
+ }
+ return results;
+ },
+
+ every: function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
+ }
+ return true;
+ },
+
+ some: function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
+ }
+ return false;
+ },
+
+ keys: function(object){
+ var keys = [];
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) keys.push(key);
+ }
+ return keys;
+ },
+
+ values: function(object){
+ var values = [];
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) values.push(object[key]);
+ }
+ return values;
+ },
+
+ getLength: function(object){
+ return Object.keys(object).length;
+ },
+
+ keyOf: function(object, value){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && object[key] === value) return key;
+ }
+ return null;
+ },
+
+ contains: function(object, value){
+ return Object.keyOf(object, value) != null;
+ },
+
+ toQueryString: function(object, base){
+ var queryString = [];
+
+ Object.each(object, function(value, key){
+ if (base) key = base + '[' + key + ']';
+ var result;
+ switch (typeOf(value)){
+ case 'object': result = Object.toQueryString(value, key); break;
+ case 'array':
+ var qs = {};
+ value.each(function(val, i){
+ qs[i] = val;
+ });
+ result = Object.toQueryString(qs, key);
+ break;
+ default: result = key + '=' + encodeURIComponent(value);
+ }
+ if (value != null) queryString.push(result);
+ });
+
+ return queryString.join('&');
+ }
+
+});
+
+})();
+
+
+
+
+/*
+---
+
+name: Browser
+
+description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
+
+license: MIT-style license.
+
+requires: [Array, Function, Number, String]
+
+provides: [Browser, Window, Document]
+
+...
+*/
+
+(function(){
+
+var document = this.document;
+var window = document.window = this;
+
+var parse = function(ua, platform){
+ ua = ua.toLowerCase();
+ platform = (platform ? platform.toLowerCase() : '');
+
+ var UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/) || [null, 'unknown', 0];
+
+ if (UA[1] == 'trident'){
+ UA[1] = 'ie';
+ if (UA[4]) UA[2] = UA[4];
+ } else if (UA[1] == 'crios') {
+ UA[1] = 'chrome';
+ }
+
+ var platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0];
+ if (platform == 'win') platform = 'windows';
+
+ return {
+ extend: Function.prototype.extend,
+ name: (UA[1] == 'version') ? UA[3] : UA[1],
+ version: parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
+ platform: platform
+ };
+};
+
+var Browser = this.Browser = parse(navigator.userAgent, navigator.platform);
+
+if (Browser.ie){
+ Browser.version = document.documentMode;
+}
+
+Browser.extend({
+ Features: {
+ xpath: !!(document.evaluate),
+ air: !!(window.runtime),
+ query: !!(document.querySelector),
+ json: !!(window.JSON)
+ },
+ parseUA: parse
+});
+
+
+
+// Request
+
+Browser.Request = (function(){
+
+ var XMLHTTP = function(){
+ return new XMLHttpRequest();
+ };
+
+ var MSXML2 = function(){
+ return new ActiveXObject('MSXML2.XMLHTTP');
+ };
+
+ var MSXML = function(){
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ };
+
+ return Function.attempt(function(){
+ XMLHTTP();
+ return XMLHTTP;
+ }, function(){
+ MSXML2();
+ return MSXML2;
+ }, function(){
+ MSXML();
+ return MSXML;
+ });
+
+})();
+
+Browser.Features.xhr = !!(Browser.Request);
+
+
+
+// String scripts
+
+Browser.exec = function(text){
+ if (!text) return text;
+ if (window.execScript){
+ window.execScript(text);
+ } else {
+ var script = document.createElement('script');
+ script.setAttribute('type', 'text/javascript');
+ script.text = text;
+ document.head.appendChild(script);
+ document.head.removeChild(script);
+ }
+ return text;
+};
+
+String.implement('stripScripts', function(exec){
+ var scripts = '';
+ var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
+ scripts += code + '\n';
+ return '';
+ });
+ if (exec === true) Browser.exec(scripts);
+ else if (typeOf(exec) == 'function') exec(scripts, text);
+ return text;
+});
+
+// Window, Document
+
+Browser.extend({
+ Document: this.Document,
+ Window: this.Window,
+ Element: this.Element,
+ Event: this.Event
+});
+
+this.Window = this.$constructor = new Type('Window', function(){});
+
+this.$family = Function.from('window').hide();
+
+Window.mirror(function(name, method){
+ window[name] = method;
+});
+
+this.Document = document.$constructor = new Type('Document', function(){});
+
+document.$family = Function.from('document').hide();
+
+Document.mirror(function(name, method){
+ document[name] = method;
+});
+
+document.html = document.documentElement;
+if (!document.head) document.head = document.getElementsByTagName('head')[0];
+
+if (document.execCommand) try {
+ document.execCommand("BackgroundImageCache", false, true);
+} catch (e){}
+
+/*<ltIE9>*/
+if (this.attachEvent && !this.addEventListener){
+ var unloadEvent = function(){
+ this.detachEvent('onunload', unloadEvent);
+ document.head = document.html = document.window = null;
+ };
+ this.attachEvent('onunload', unloadEvent);
+}
+
+// IE fails on collections and <select>.options (refers to <select>)
+var arrayFrom = Array.from;
+try {
+ arrayFrom(document.html.childNodes);
+} catch(e){
+ Array.from = function(item){
+ if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
+ var i = item.length, array = new Array(i);
+ while (i--) array[i] = item[i];
+ return array;
+ }
+ return arrayFrom(item);
+ };
+
+ var prototype = Array.prototype,
+ slice = prototype.slice;
+ ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
+ var method = prototype[name];
+ Array[name] = function(item){
+ return method.apply(Array.from(item), slice.call(arguments, 1));
+ };
+ });
+}
+/*</ltIE9>*/
+
+
+
+})();
+
+
+/*
+---
+
+name: Event
+
+description: Contains the Event Type, to make the event object cross-browser.
+
+license: MIT-style license.
+
+requires: [Window, Document, Array, Function, String, Object]
+
+provides: Event
+
+...
+*/
+
+(function() {
+
+var _keys = {};
+
+var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){
+ if (!win) win = window;
+ event = event || win.event;
+ if (event.$extended) return event;
+ this.event = event;
+ this.$extended = true;
+ this.shift = event.shiftKey;
+ this.control = event.ctrlKey;
+ this.alt = event.altKey;
+ this.meta = event.metaKey;
+ var type = this.type = event.type;
+ var target = event.target || event.srcElement;
+ while (target && target.nodeType == 3) target = target.parentNode;
+ this.target = document.id(target);
+
+ if (type.indexOf('key') == 0){
+ var code = this.code = (event.which || event.keyCode);
+ this.key = _keys[code];
+ if (type == 'keydown' || type == 'keyup'){
+ if (code > 111 && code < 124) this.key = 'f' + (code - 111);
+ else if (code > 95 && code < 106) this.key = code - 96;
+ }
+ if (this.key == null) this.key = String.fromCharCode(code).toLowerCase();
+ } else if (type == 'click' || type == 'dblclick' || type == 'contextmenu' || type == 'DOMMouseScroll' || type.indexOf('mouse') == 0){
+ var doc = win.document;
+ doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
+ this.page = {
+ x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
+ y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
+ };
+ this.client = {
+ x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
+ y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
+ };
+ if (type == 'DOMMouseScroll' || type == 'mousewheel')
+ this.wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
+
+ this.rightClick = (event.which == 3 || event.button == 2);
+ if (type == 'mouseover' || type == 'mouseout'){
+ var related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
+ while (related && related.nodeType == 3) related = related.parentNode;
+ this.relatedTarget = document.id(related);
+ }
+ } else if (type.indexOf('touch') == 0 || type.indexOf('gesture') == 0){
+ this.rotation = event.rotation;
+ this.scale = event.scale;
+ this.targetTouches = event.targetTouches;
+ this.changedTouches = event.changedTouches;
+ var touches = this.touches = event.touches;
+ if (touches && touches[0]){
+ var touch = touches[0];
+ this.page = {x: touch.pageX, y: touch.pageY};
+ this.client = {x: touch.clientX, y: touch.clientY};
+ }
+ }
+
+ if (!this.client) this.client = {};
+ if (!this.page) this.page = {};
+});
+
+DOMEvent.implement({
+
+ stop: function(){
+ return this.preventDefault().stopPropagation();
+ },
+
+ stopPropagation: function(){
+ if (this.event.stopPropagation) this.event.stopPropagation();
+ else this.event.cancelBubble = true;
+ return this;
+ },
+
+ preventDefault: function(){
+ if (this.event.preventDefault) this.event.preventDefault();
+ else this.event.returnValue = false;
+ return this;
+ }
+
+});
+
+DOMEvent.defineKey = function(code, key){
+ _keys[code] = key;
+ return this;
+};
+
+DOMEvent.defineKeys = DOMEvent.defineKey.overloadSetter(true);
+
+DOMEvent.defineKeys({
+ '38': 'up', '40': 'down', '37': 'left', '39': 'right',
+ '27': 'esc', '32': 'space', '8': 'backspace', '9': 'tab',
+ '46': 'delete', '13': 'enter'
+});
+
+})();
+
+
+
+
+
+
+/*
+---
+
+name: Class
+
+description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
+
+license: MIT-style license.
+
+requires: [Array, String, Function, Number]
+
+provides: Class
+
+...
+*/
+
+(function(){
+
+var Class = this.Class = new Type('Class', function(params){
+ if (instanceOf(params, Function)) params = {initialize: params};
+
+ var newClass = function(){
+ reset(this);
+ if (newClass.$prototyping) return this;
+ this.$caller = null;
+ var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
+ this.$caller = this.caller = null;
+ return value;
+ }.extend(this).implement(params);
+
+ newClass.$constructor = Class;
+ newClass.prototype.$constructor = newClass;
+ newClass.prototype.parent = parent;
+
+ return newClass;
+});
+
+var parent = function(){
+ if (!this.$caller) throw new Error('The method "parent" cannot be called.');
+ var name = this.$caller.$name,
+ parent = this.$caller.$owner.parent,
+ previous = (parent) ? parent.prototype[name] : null;
+ if (!previous) throw new Error('The method "' + name + '" has no parent.');
+ return previous.apply(this, arguments);
+};
+
+var reset = function(object){
+ for (var key in object){
+ var value = object[key];
+ switch (typeOf(value)){
+ case 'object':
+ var F = function(){};
+ F.prototype = value;
+ object[key] = reset(new F);
+ break;
+ case 'array': object[key] = value.clone(); break;
+ }
+ }
+ return object;
+};
+
+var wrap = function(self, key, method){
+ if (method.$origin) method = method.$origin;
+ var wrapper = function(){
+ if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
+ var caller = this.caller, current = this.$caller;
+ this.caller = current; this.$caller = wrapper;
+ var result = method.apply(this, arguments);
+ this.$caller = current; this.caller = caller;
+ return result;
+ }.extend({$owner: self, $origin: method, $name: key});
+ return wrapper;
+};
+
+var implement = function(key, value, retain){
+ if (Class.Mutators.hasOwnProperty(key)){
+ value = Class.Mutators[key].call(this, value);
+ if (value == null) return this;
+ }
+
+ if (typeOf(value) == 'function'){
+ if (value.$hidden) return this;
+ this.prototype[key] = (retain) ? value : wrap(this, key, value);
+ } else {
+ Object.merge(this.prototype, key, value);
+ }
+
+ return this;
+};
+
+var getInstance = function(klass){
+ klass.$prototyping = true;
+ var proto = new klass;
+ delete klass.$prototyping;
+ return proto;
+};
+
+Class.implement('implement', implement.overloadSetter());
+
+Class.Mutators = {
+
+ Extends: function(parent){
+ this.parent = parent;
+ this.prototype = getInstance(parent);
+ },
+
+ Implements: function(items){
+ Array.from(items).each(function(item){
+ var instance = new item;
+ for (var key in instance) implement.call(this, key, instance[key], true);
+ }, this);
+ }
+};
+
+})();
+
+
+/*
+---
+
+name: Class.Extras
+
+description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
+
+license: MIT-style license.
+
+requires: Class
+
+provides: [Class.Extras, Chain, Events, Options]
+
+...
+*/
+
+(function(){
+
+this.Chain = new Class({
+
+ $chain: [],
+
+ chain: function(){
+ this.$chain.append(Array.flatten(arguments));
+ return this;
+ },
+
+ callChain: function(){
+ return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
+ },
+
+ clearChain: function(){
+ this.$chain.empty();
+ return this;
+ }
+
+});
+
+var removeOn = function(string){
+ return string.replace(/^on([A-Z])/, function(full, first){
+ return first.toLowerCase();
+ });
+};
+
+this.Events = new Class({
+
+ $events: {},
+
+ addEvent: function(type, fn, internal){
+ type = removeOn(type);
+
+
+
+ this.$events[type] = (this.$events[type] || []).include(fn);
+ if (internal) fn.internal = true;
+ return this;
+ },
+
+ addEvents: function(events){
+ for (var type in events) this.addEvent(type, events[type]);
+ return this;
+ },
+
+ fireEvent: function(type, args, delay){
+ type = removeOn(type);
+ var events = this.$events[type];
+ if (!events) return this;
+ args = Array.from(args);
+ events.each(function(fn){
+ if (delay) fn.delay(delay, this, args);
+ else fn.apply(this, args);
+ }, this);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ type = removeOn(type);
+ var events = this.$events[type];
+ if (events && !fn.internal){
+ var index = events.indexOf(fn);
+ if (index != -1) delete events[index];
+ }
+ return this;
+ },
+
+ removeEvents: function(events){
+ var type;
+ if (typeOf(events) == 'object'){
+ for (type in events) this.removeEvent(type, events[type]);
+ return this;
+ }
+ if (events) events = removeOn(events);
+ for (type in this.$events){
+ if (events && events != type) continue;
+ var fns = this.$events[type];
+ for (var i = fns.length; i--;) if (i in fns){
+ this.removeEvent(type, fns[i]);
+ }
+ }
+ return this;
+ }
+
+});
+
+this.Options = new Class({
+
+ setOptions: function(){
+ var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
+ if (this.addEvent) for (var option in options){
+ if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
+ this.addEvent(option, options[option]);
+ delete options[option];
+ }
+ return this;
+ }
+
+});
+
+})();
+
+
+/*
+---
+name: Slick.Parser
+description: Standalone CSS3 Selector parser
+provides: Slick.Parser
+...
+*/
+
+;(function(){
+
+var parsed,
+ separatorIndex,
+ combinatorIndex,
+ reversed,
+ cache = {},
+ reverseCache = {},
+ reUnescape = /\\/g;
+
+var parse = function(expression, isReversed){
+ if (expression == null) return null;
+ if (expression.Slick === true) return expression;
+ expression = ('' + expression).replace(/^\s+|\s+$/g, '');
+ reversed = !!isReversed;
+ var currentCache = (reversed) ? reverseCache : cache;
+ if (currentCache[expression]) return currentCache[expression];
+ parsed = {
+ Slick: true,
+ expressions: [],
+ raw: expression,
+ reverse: function(){
+ return parse(this.raw, true);
+ }
+ };
+ separatorIndex = -1;
+ while (expression != (expression = expression.replace(regexp, parser)));
+ parsed.length = parsed.expressions.length;
+ return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
+};
+
+var reverseCombinator = function(combinator){
+ if (combinator === '!') return ' ';
+ else if (combinator === ' ') return '!';
+ else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
+ else return '!' + combinator;
+};
+
+var reverse = function(expression){
+ var expressions = expression.expressions;
+ for (var i = 0; i < expressions.length; i++){
+ var exp = expressions[i];
+ var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
+
+ for (var j = 0; j < exp.length; j++){
+ var cexp = exp[j];
+ if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
+ cexp.combinator = cexp.reverseCombinator;
+ delete cexp.reverseCombinator;
+ }
+
+ exp.reverse().push(last);
+ }
+ return expression;
+};
+
+var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
+ return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
+ return '\\' + match;
+ });
+};
+
+var regexp = new RegExp(
+/*
+#!/usr/bin/env ruby
+puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
+__END__
+ "(?x)^(?:\
+ \\s* ( , ) \\s* # Separator \n\
+ | \\s* ( <combinator>+ ) \\s* # Combinator \n\
+ | ( \\s+ ) # CombinatorChildren \n\
+ | ( <unicode>+ | \\* ) # Tag \n\
+ | \\# ( <unicode>+ ) # ID \n\
+ | \\. ( <unicode>+ ) # ClassName \n\
+ | # Attribute \n\
+ \\[ \
+ \\s* (<unicode1>+) (?: \
+ \\s* ([*^$!~|]?=) (?: \
+ \\s* (?:\
+ ([\"']?)(.*?)\\9 \
+ )\
+ ) \
+ )? \\s* \
+ \\](?!\\]) \n\
+ | :+ ( <unicode>+ )(?:\
+ \\( (?:\
+ (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
+ ) \\)\
+ )?\
+ )"
+*/
+ "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
+ .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
+ .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+ .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+);
+
+function parser(
+ rawMatch,
+
+ separator,
+ combinator,
+ combinatorChildren,
+
+ tagName,
+ id,
+ className,
+
+ attributeKey,
+ attributeOperator,
+ attributeQuote,
+ attributeValue,
+
+ pseudoMarker,
+ pseudoClass,
+ pseudoQuote,
+ pseudoClassQuotedValue,
+ pseudoClassValue
+){
+ if (separator || separatorIndex === -1){
+ parsed.expressions[++separatorIndex] = [];
+ combinatorIndex = -1;
+ if (separator) return '';
+ }
+
+ if (combinator || combinatorChildren || combinatorIndex === -1){
+ combinator = combinator || ' ';
+ var currentSeparator = parsed.expressions[separatorIndex];
+ if (reversed && currentSeparator[combinatorIndex])
+ currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
+ currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
+ }
+
+ var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
+
+ if (tagName){
+ currentParsed.tag = tagName.replace(reUnescape, '');
+
+ } else if (id){
+ currentParsed.id = id.replace(reUnescape, '');
+
+ } else if (className){
+ className = className.replace(reUnescape, '');
+
+ if (!currentParsed.classList) currentParsed.classList = [];
+ if (!currentParsed.classes) currentParsed.classes = [];
+ currentParsed.classList.push(className);
+ currentParsed.classes.push({
+ value: className,
+ regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
+ });
+
+ } else if (pseudoClass){
+ pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
+ pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
+
+ if (!currentParsed.pseudos) currentParsed.pseudos = [];
+ currentParsed.pseudos.push({
+ key: pseudoClass.replace(reUnescape, ''),
+ value: pseudoClassValue,
+ type: pseudoMarker.length == 1 ? 'class' : 'element'
+ });
+
+ } else if (attributeKey){
+ attributeKey = attributeKey.replace(reUnescape, '');
+ attributeValue = (attributeValue || '').replace(reUnescape, '');
+
+ var test, regexp;
+
+ switch (attributeOperator){
+ case '^=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) ); break;
+ case '$=' : regexp = new RegExp( escapeRegExp(attributeValue) +'$' ); break;
+ case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
+ case '|=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) +'(-|$)' ); break;
+ case '=' : test = function(value){
+ return attributeValue == value;
+ }; break;
+ case '*=' : test = function(value){
+ return value && value.indexOf(attributeValue) > -1;
+ }; break;
+ case '!=' : test = function(value){
+ return attributeValue != value;
+ }; break;
+ default : test = function(value){
+ return !!value;
+ };
+ }
+
+ if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
+ return false;
+ };
+
+ if (!test) test = function(value){
+ return value && regexp.test(value);
+ };
+
+ if (!currentParsed.attributes) currentParsed.attributes = [];
+ currentParsed.attributes.push({
+ key: attributeKey,
+ operator: attributeOperator,
+ value: attributeValue,
+ test: test
+ });
+
+ }
+
+ return '';
+};
+
+// Slick NS
+
+var Slick = (this.Slick || {});
+
+Slick.parse = function(expression){
+ return parse(expression);
+};
+
+Slick.escapeRegExp = escapeRegExp;
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+
+/*
+---
+name: Slick.Finder
+description: The new, superfast css selector engine.
+provides: Slick.Finder
+requires: Slick.Parser
+...
+*/
+
+;(function(){
+
+var local = {},
+ featuresCache = {},
+ toString = Object.prototype.toString;
+
+// Feature / Bug detection
+
+local.isNativeCode = function(fn){
+ return (/\{\s*\[native code\]\s*\}/).test('' + fn);
+};
+
+local.isXML = function(document){
+ return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
+ (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
+};
+
+local.setDocument = function(document){
+
+ // convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
+ var nodeType = document.nodeType;
+ if (nodeType == 9); // document
+ else if (nodeType) document = document.ownerDocument; // node
+ else if (document.navigator) document = document.document; // window
+ else return;
+
+ // check if it's the old document
+
+ if (this.document === document) return;
+ this.document = document;
+
+ // check if we have done feature detection on this document before
+
+ var root = document.documentElement,
+ rootUid = this.getUIDXML(root),
+ features = featuresCache[rootUid],
+ feature;
+
+ if (features){
+ for (feature in features){
+ this[feature] = features[feature];
+ }
+ return;
+ }
+
+ features = featuresCache[rootUid] = {};
+
+ features.root = root;
+ features.isXMLDocument = this.isXML(document);
+
+ features.brokenStarGEBTN
+ = features.starSelectsClosedQSA
+ = features.idGetsName
+ = features.brokenMixedCaseQSA
+ = features.brokenGEBCN
+ = features.brokenCheckedQSA
+ = features.brokenEmptyAttributeQSA
+ = features.isHTMLDocument
+ = features.nativeMatchesSelector
+ = false;
+
+ var starSelectsClosed, starSelectsComments,
+ brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
+ brokenFormAttributeGetter;
+
+ var selected, id = 'slick_uniqueid';
+ var testNode = document.createElement('div');
+
+ var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
+ testRoot.appendChild(testNode);
+
+ // on non-HTML documents innerHTML and getElementsById doesnt work properly
+ try {
+ testNode.innerHTML = '<a id="'+id+'"></a>';
+ features.isHTMLDocument = !!document.getElementById(id);
+ } catch(e){};
+
+ if (features.isHTMLDocument){
+
+ testNode.style.display = 'none';
+
+ // IE returns comment nodes for getElementsByTagName('*') for some documents
+ testNode.appendChild(document.createComment(''));
+ starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
+
+ // IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
+ try {
+ testNode.innerHTML = 'foo</foo>';
+ selected = testNode.getElementsByTagName('*');
+ starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+ } catch(e){};
+
+ features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
+
+ // IE returns elements with the name instead of just id for getElementsById for some documents
+ try {
+ testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
+ features.idGetsName = document.getElementById(id) === testNode.firstChild;
+ } catch(e){};
+
+ if (testNode.getElementsByClassName){
+
+ // Safari 3.2 getElementsByClassName caches results
+ try {
+ testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
+ testNode.getElementsByClassName('b').length;
+ testNode.firstChild.className = 'b';
+ cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
+ } catch(e){};
+
+ // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
+ try {
+ testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
+ brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
+ } catch(e){};
+
+ features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
+ }
+
+ if (testNode.querySelectorAll){
+ // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
+ try {
+ testNode.innerHTML = 'foo</foo>';
+ selected = testNode.querySelectorAll('*');
+ features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+ } catch(e){};
+
+ // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
+ try {
+ testNode.innerHTML = '<a class="MiX"></a>';
+ features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
+ } catch(e){};
+
+ // Webkit and Opera dont return selected options on querySelectorAll
+ try {
+ testNode.innerHTML = '<select><option selected="selected">a</option></select>';
+ features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
+ } catch(e){};
+
+ // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
+ try {
+ testNode.innerHTML = '<a class=""></a>';
+ features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
+ } catch(e){};
+
+ }
+
+ // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
+ try {
+ testNode.innerHTML = '<form action="s"><input id="action"/></form>';
+ brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
+ } catch(e){};
+
+ // native matchesSelector function
+
+ features.nativeMatchesSelector = root.matches || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
+ if (features.nativeMatchesSelector) try {
+ // if matchesSelector trows errors on incorrect sintaxes we can use it
+ features.nativeMatchesSelector.call(root, ':slick');
+ features.nativeMatchesSelector = null;
+ } catch(e){};
+
+ }
+
+ try {
+ root.slick_expando = 1;
+ delete root.slick_expando;
+ features.getUID = this.getUIDHTML;
+ } catch(e) {
+ features.getUID = this.getUIDXML;
+ }
+
+ testRoot.removeChild(testNode);
+ testNode = selected = testRoot = null;
+
+ // getAttribute
+
+ features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
+ var method = this.attributeGetters[name];
+ if (method) return method.call(node);
+ var attributeNode = node.getAttributeNode(name);
+ return (attributeNode) ? attributeNode.nodeValue : null;
+ } : function(node, name){
+ var method = this.attributeGetters[name];
+ return (method) ? method.call(node) : node.getAttribute(name);
+ };
+
+ // hasAttribute
+
+ features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) {
+ return node.hasAttribute(attribute);
+ } : function(node, attribute) {
+ node = node.getAttributeNode(attribute);
+ return !!(node && (node.specified || node.nodeValue));
+ };
+
+ // contains
+ // FIXME: Add specs: local.contains should be different for xml and html documents?
+ var nativeRootContains = root && this.isNativeCode(root.contains),
+ nativeDocumentContains = document && this.isNativeCode(document.contains);
+
+ features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){
+ return context.contains(node);
+ } : (nativeRootContains && !nativeDocumentContains) ? function(context, node){
+ // IE8 does not have .contains on document.
+ return context === node || ((context === document) ? document.documentElement : context).contains(node);
+ } : (root && root.compareDocumentPosition) ? function(context, node){
+ return context === node || !!(context.compareDocumentPosition(node) & 16);
+ } : function(context, node){
+ if (node) do {
+ if (node === context) return true;
+ } while ((node = node.parentNode));
+ return false;
+ };
+
+ // document order sorting
+ // credits to Sizzle (http://sizzlejs.com/)
+
+ features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
+ if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
+ return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+ } : ('sourceIndex' in root) ? function(a, b){
+ if (!a.sourceIndex || !b.sourceIndex) return 0;
+ return a.sourceIndex - b.sourceIndex;
+ } : (document.createRange) ? function(a, b){
+ if (!a.ownerDocument || !b.ownerDocument) return 0;
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+ aRange.setStart(a, 0);
+ aRange.setEnd(a, 0);
+ bRange.setStart(b, 0);
+ bRange.setEnd(b, 0);
+ return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+ } : null ;
+
+ root = null;
+
+ for (feature in features){
+ this[feature] = features[feature];
+ }
+};
+
+// Main Method
+
+var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
+ reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
+ qsaFailExpCache = {};
+
+local.search = function(context, expression, append, first){
+
+ var found = this.found = (first) ? null : (append || []);
+
+ if (!context) return found;
+ else if (context.navigator) context = context.document; // Convert the node from a window to a document
+ else if (!context.nodeType) return found;
+
+ // setup
+
+ var parsed, i,
+ uniques = this.uniques = {},
+ hasOthers = !!(append && append.length),
+ contextIsDocument = (context.nodeType == 9);
+
+ if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
+
+ // avoid duplicating items already in the append array
+ if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
+
+ // expression checks
+
+ if (typeof expression == 'string'){ // expression is a string
+
+ /*<simple-selectors-override>*/
+ var simpleSelector = expression.match(reSimpleSelector);
+ simpleSelectors: if (simpleSelector) {
+
+ var symbol = simpleSelector[1],
+ name = simpleSelector[2],
+ node, nodes;
+
+ if (!symbol){
+
+ if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
+ nodes = context.getElementsByTagName(name);
+ if (first) return nodes[0] || null;
+ for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+
+ } else if (symbol == '#'){
+
+ if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
+ node = context.getElementById(name);
+ if (!node) return found;
+ if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
+ if (first) return node || null;
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+
+ } else if (symbol == '.'){
+
+ if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
+ if (context.getElementsByClassName && !this.brokenGEBCN){
+ nodes = context.getElementsByClassName(name);
+ if (first) return nodes[0] || null;
+ for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+ } else {
+ var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
+ nodes = context.getElementsByTagName('*');
+ for (i = 0; node = nodes[i++];){
+ className = node.className;
+ if (!(className && matchClass.test(className))) continue;
+ if (first) return node;
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+ }
+
+ }
+
+ if (hasOthers) this.sort(found);
+ return (first) ? null : found;
+
+ }
+ /*</simple-selectors-override>*/
+
+ /*<query-selector-override>*/
+ querySelector: if (context.querySelectorAll) {
+
+ if (!this.isHTMLDocument
+ || qsaFailExpCache[expression]
+ //TODO: only skip when expression is actually mixed case
+ || this.brokenMixedCaseQSA
+ || (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
+ || (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
+ || (!contextIsDocument //Abort when !contextIsDocument and...
+ // there are multiple expressions in the selector
+ // since we currently only fix non-document rooted QSA for single expression selectors
+ && expression.indexOf(',') > -1
+ )
+ || Slick.disableQSA
+ ) break querySelector;
+
+ var _expression = expression, _context = context;
+ if (!contextIsDocument){
+ // non-document rooted QSA
+ // credits to Andrew Dupont
+ var currentId = _context.getAttribute('id'), slickid = 'slickid__';
+ _context.setAttribute('id', slickid);
+ _expression = '#' + slickid + ' ' + _expression;
+ context = _context.parentNode;
+ }
+
+ try {
+ if (first) return context.querySelector(_expression) || null;
+ else nodes = context.querySelectorAll(_expression);
+ } catch(e) {
+ qsaFailExpCache[expression] = 1;
+ break querySelector;
+ } finally {
+ if (!contextIsDocument){
+ if (currentId) _context.setAttribute('id', currentId);
+ else _context.removeAttribute('id');
+ context = _context;
+ }
+ }
+
+ if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
+ if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ } else for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+
+ if (hasOthers) this.sort(found);
+ return found;
+
+ }
+ /*</query-selector-override>*/
+
+ parsed = this.Slick.parse(expression);
+ if (!parsed.length) return found;
+ } else if (expression == null){ // there is no expression
+ return found;
+ } else if (expression.Slick){ // expression is a parsed Slick object
+ parsed = expression;
+ } else if (this.contains(context.documentElement || context, expression)){ // expression is a node
+ (found) ? found.push(expression) : found = expression;
+ return found;
+ } else { // other junk
+ return found;
+ }
+
+ /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+ // cache elements for the nth selectors
+
+ this.posNTH = {};
+ this.posNTHLast = {};
+ this.posNTHType = {};
+ this.posNTHTypeLast = {};
+
+ /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+ // if append is null and there is only a single selector with one expression use pushArray, else use pushUID
+ this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
+
+ if (found == null) found = [];
+
+ // default engine
+
+ var j, m, n;
+ var combinator, tag, id, classList, classes, attributes, pseudos;
+ var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
+
+ search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
+
+ combinator = 'combinator:' + currentBit.combinator;
+ if (!this[combinator]) continue search;
+
+ tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
+ id = currentBit.id;
+ classList = currentBit.classList;
+ classes = currentBit.classes;
+ attributes = currentBit.attributes;
+ pseudos = currentBit.pseudos;
+ lastBit = (j === (currentExpression.length - 1));
+
+ this.bitUniques = {};
+
+ if (lastBit){
+ this.uniques = uniques;
+ this.found = found;
+ } else {
+ this.uniques = {};
+ this.found = [];
+ }
+
+ if (j === 0){
+ this[combinator](context, tag, id, classes, attributes, pseudos, classList);
+ if (first && lastBit && found.length) break search;
+ } else {
+ if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
+ this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+ if (found.length) break search;
+ } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+ }
+
+ currentItems = this.found;
+ }
+
+ // should sort if there are nodes in append and if you pass multiple expressions.
+ if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
+
+ return (first) ? (found[0] || null) : found;
+};
+
+// Utils
+
+local.uidx = 1;
+local.uidk = 'slick-uniqueid';
+
+local.getUIDXML = function(node){
+ var uid = node.getAttribute(this.uidk);
+ if (!uid){
+ uid = this.uidx++;
+ node.setAttribute(this.uidk, uid);
+ }
+ return uid;
+};
+
+local.getUIDHTML = function(node){
+ return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
+};
+
+// sort based on the setDocument documentSorter method.
+
+local.sort = function(results){
+ if (!this.documentSorter) return results;
+ results.sort(this.documentSorter);
+ return results;
+};
+
+/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+local.cacheNTH = {};
+
+local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
+
+local.parseNTHArgument = function(argument){
+ var parsed = argument.match(this.matchNTH);
+ if (!parsed) return false;
+ var special = parsed[2] || false;
+ var a = parsed[1] || 1;
+ if (a == '-') a = -1;
+ var b = +parsed[3] || 0;
+ parsed =
+ (special == 'n') ? {a: a, b: b} :
+ (special == 'odd') ? {a: 2, b: 1} :
+ (special == 'even') ? {a: 2, b: 0} : {a: 0, b: a};
+
+ return (this.cacheNTH[argument] = parsed);
+};
+
+local.createNTHPseudo = function(child, sibling, positions, ofType){
+ return function(node, argument){
+ var uid = this.getUID(node);
+ if (!this[positions][uid]){
+ var parent = node.parentNode;
+ if (!parent) return false;
+ var el = parent[child], count = 1;
+ if (ofType){
+ var nodeName = node.nodeName;
+ do {
+ if (el.nodeName != nodeName) continue;
+ this[positions][this.getUID(el)] = count++;
+ } while ((el = el[sibling]));
+ } else {
+ do {
+ if (el.nodeType != 1) continue;
+ this[positions][this.getUID(el)] = count++;
+ } while ((el = el[sibling]));
+ }
+ }
+ argument = argument || 'n';
+ var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
+ if (!parsed) return false;
+ var a = parsed.a, b = parsed.b, pos = this[positions][uid];
+ if (a == 0) return b == pos;
+ if (a > 0){
+ if (pos < b) return false;
+ } else {
+ if (b < pos) return false;
+ }
+ return ((pos - b) % a) == 0;
+ };
+};
+
+/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+local.pushArray = function(node, tag, id, classes, attributes, pseudos){
+ if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
+};
+
+local.pushUID = function(node, tag, id, classes, attributes, pseudos){
+ var uid = this.getUID(node);
+ if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
+ this.uniques[uid] = true;
+ this.found.push(node);
+ }
+};
+
+local.matchNode = function(node, selector){
+ if (this.isHTMLDocument && this.nativeMatchesSelector){
+ try {
+ return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
+ } catch(matchError) {}
+ }
+
+ var parsed = this.Slick.parse(selector);
+ if (!parsed) return true;
+
+ // simple (single) selectors
+ var expressions = parsed.expressions, simpleExpCounter = 0, i;
+ for (i = 0; (currentExpression = expressions[i]); i++){
+ if (currentExpression.length == 1){
+ var exp = currentExpression[0];
+ if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
+ simpleExpCounter++;
+ }
+ }
+
+ if (simpleExpCounter == parsed.length) return false;
+
+ var nodes = this.search(this.document, parsed), item;
+ for (i = 0; item = nodes[i++];){
+ if (item === node) return true;
+ }
+ return false;
+};
+
+local.matchPseudo = function(node, name, argument){
+ var pseudoName = 'pseudo:' + name;
+ if (this[pseudoName]) return this[pseudoName](node, argument);
+ var attribute = this.getAttribute(node, name);
+ return (argument) ? argument == attribute : !!attribute;
+};
+
+local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
+ if (tag){
+ var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
+ if (tag == '*'){
+ if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
+ } else {
+ if (nodeName != tag) return false;
+ }
+ }
+
+ if (id && node.getAttribute('id') != id) return false;
+
+ var i, part, cls;
+ if (classes) for (i = classes.length; i--;){
+ cls = this.getAttribute(node, 'class');
+ if (!(cls && classes[i].regexp.test(cls))) return false;
+ }
+ if (attributes) for (i = attributes.length; i--;){
+ part = attributes[i];
+ if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
+ }
+ if (pseudos) for (i = pseudos.length; i--;){
+ part = pseudos[i];
+ if (!this.matchPseudo(node, part.key, part.value)) return false;
+ }
+ return true;
+};
+
+var combinators = {
+
+ ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
+
+ var i, item, children;
+
+ if (this.isHTMLDocument){
+ getById: if (id){
+ item = this.document.getElementById(id);
+ if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
+ // all[id] returns all the elements with that name or id inside node
+ // if theres just one it will return the element, else it will be a collection
+ children = node.all[id];
+ if (!children) return;
+ if (!children[0]) children = [children];
+ for (i = 0; item = children[i++];){
+ var idNode = item.getAttributeNode('id');
+ if (idNode && idNode.nodeValue == id){
+ this.push(item, tag, null, classes, attributes, pseudos);
+ break;
+ }
+ }
+ return;
+ }
+ if (!item){
+ // if the context is in the dom we return, else we will try GEBTN, breaking the getById label
+ if (this.contains(this.root, node)) return;
+ else break getById;
+ } else if (this.document !== node && !this.contains(node, item)) return;
+ this.push(item, tag, null, classes, attributes, pseudos);
+ return;
+ }
+ getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
+ children = node.getElementsByClassName(classList.join(' '));
+ if (!(children && children.length)) break getByClass;
+ for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
+ return;
+ }
+ }
+ getByTag: {
+ children = node.getElementsByTagName(tag);
+ if (!(children && children.length)) break getByTag;
+ if (!this.brokenStarGEBTN) tag = null;
+ for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '>': function(node, tag, id, classes, attributes, pseudos){ // direct children
+ if ((node = node.firstChild)) do {
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ } while ((node = node.nextSibling));
+ },
+
+ '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
+ while ((node = node.nextSibling)) if (node.nodeType == 1){
+ this.push(node, tag, id, classes, attributes, pseudos);
+ break;
+ }
+ },
+
+ '^': function(node, tag, id, classes, attributes, pseudos){ // first child
+ node = node.firstChild;
+ if (node){
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
+ while ((node = node.nextSibling)){
+ if (node.nodeType != 1) continue;
+ var uid = this.getUID(node);
+ if (this.bitUniques[uid]) break;
+ this.bitUniques[uid] = true;
+ this.push(node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
+ this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+ this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+ },
+
+ '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
+ this['combinator:~'](node, tag, id, classes, attributes, pseudos);
+ this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
+ while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
+ node = node.parentNode;
+ if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
+ while ((node = node.previousSibling)) if (node.nodeType == 1){
+ this.push(node, tag, id, classes, attributes, pseudos);
+ break;
+ }
+ },
+
+ '!^': function(node, tag, id, classes, attributes, pseudos){ // last child
+ node = node.lastChild;
+ if (node){
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
+ while ((node = node.previousSibling)){
+ if (node.nodeType != 1) continue;
+ var uid = this.getUID(node);
+ if (this.bitUniques[uid]) break;
+ this.bitUniques[uid] = true;
+ this.push(node, tag, id, classes, attributes, pseudos);
+ }
+ }
+
+};
+
+for (var c in combinators) local['combinator:' + c] = combinators[c];
+
+var pseudos = {
+
+ /*<pseudo-selectors>*/
+
+ 'empty': function(node){
+ var child = node.firstChild;
+ return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
+ },
+
+ 'not': function(node, expression){
+ return !this.matchNode(node, expression);
+ },
+
+ 'contains': function(node, text){
+ return (node.innerText || node.textContent || '').indexOf(text) > -1;
+ },
+
+ 'first-child': function(node){
+ while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
+ return true;
+ },
+
+ 'last-child': function(node){
+ while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
+ return true;
+ },
+
+ 'only-child': function(node){
+ var prev = node;
+ while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
+ var next = node;
+ while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
+ return true;
+ },
+
+ /*<nth-pseudo-selectors>*/
+
+ 'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
+
+ 'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
+
+ 'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
+
+ 'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
+
+ 'index': function(node, index){
+ return this['pseudo:nth-child'](node, '' + (index + 1));
+ },
+
+ 'even': function(node){
+ return this['pseudo:nth-child'](node, '2n');
+ },
+
+ 'odd': function(node){
+ return this['pseudo:nth-child'](node, '2n+1');
+ },
+
+ /*</nth-pseudo-selectors>*/
+
+ /*<of-type-pseudo-selectors>*/
+
+ 'first-of-type': function(node){
+ var nodeName = node.nodeName;
+ while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
+ return true;
+ },
+
+ 'last-of-type': function(node){
+ var nodeName = node.nodeName;
+ while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
+ return true;
+ },
+
+ 'only-of-type': function(node){
+ var prev = node, nodeName = node.nodeName;
+ while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
+ var next = node;
+ while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
+ return true;
+ },
+
+ /*</of-type-pseudo-selectors>*/
+
+ // custom pseudos
+
+ 'enabled': function(node){
+ return !node.disabled;
+ },
+
+ 'disabled': function(node){
+ return node.disabled;
+ },
+
+ 'checked': function(node){
+ return node.checked || node.selected;
+ },
+
+ 'focus': function(node){
+ return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
+ },
+
+ 'root': function(node){
+ return (node === this.root);
+ },
+
+ 'selected': function(node){
+ return node.selected;
+ }
+
+ /*</pseudo-selectors>*/
+};
+
+for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
+
+// attributes methods
+
+var attributeGetters = local.attributeGetters = {
+
+ 'for': function(){
+ return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
+ },
+
+ 'href': function(){
+ return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
+ },
+
+ 'style': function(){
+ return (this.style) ? this.style.cssText : this.getAttribute('style');
+ },
+
+ 'tabindex': function(){
+ var attributeNode = this.getAttributeNode('tabindex');
+ return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+ },
+
+ 'type': function(){
+ return this.getAttribute('type');
+ },
+
+ 'maxlength': function(){
+ var attributeNode = this.getAttributeNode('maxLength');
+ return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+ }
+
+};
+
+attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
+
+// Slick
+
+var Slick = local.Slick = (this.Slick || {});
+
+Slick.version = '1.1.7';
+
+// Slick finder
+
+Slick.search = function(context, expression, append){
+ return local.search(context, expression, append);
+};
+
+Slick.find = function(context, expression){
+ return local.search(context, expression, null, true);
+};
+
+// Slick containment checker
+
+Slick.contains = function(container, node){
+ local.setDocument(container);
+ return local.contains(container, node);
+};
+
+// Slick attribute getter
+
+Slick.getAttribute = function(node, name){
+ local.setDocument(node);
+ return local.getAttribute(node, name);
+};
+
+Slick.hasAttribute = function(node, name){
+ local.setDocument(node);
+ return local.hasAttribute(node, name);
+};
+
+// Slick matcher
+
+Slick.match = function(node, selector){
+ if (!(node && selector)) return false;
+ if (!selector || selector === node) return true;
+ local.setDocument(node);
+ return local.matchNode(node, selector);
+};
+
+// Slick attribute accessor
+
+Slick.defineAttributeGetter = function(name, fn){
+ local.attributeGetters[name] = fn;
+ return this;
+};
+
+Slick.lookupAttributeGetter = function(name){
+ return local.attributeGetters[name];
+};
+
+// Slick pseudo accessor
+
+Slick.definePseudo = function(name, fn){
+ local['pseudo:' + name] = function(node, argument){
+ return fn.call(node, argument);
+ };
+ return this;
+};
+
+Slick.lookupPseudo = function(name){
+ var pseudo = local['pseudo:' + name];
+ if (pseudo) return function(argument){
+ return pseudo.call(this, argument);
+ };
+ return null;
+};
+
+// Slick overrides accessor
+
+Slick.override = function(regexp, fn){
+ local.override(regexp, fn);
+ return this;
+};
+
+Slick.isXML = local.isXML;
+
+Slick.uidOf = function(node){
+ return local.getUIDHTML(node);
+};
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+
+/*
+---
+
+name: Element
+
+description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements.
+
+license: MIT-style license.
+
+requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder]
+
+provides: [Element, Elements, $, $$, IFrame, Selectors]
+
+...
+*/
+
+var Element = this.Element = function(tag, props){
+ var konstructor = Element.Constructors[tag];
+ if (konstructor) return konstructor(props);
+ if (typeof tag != 'string') return document.id(tag).set(props);
+
+ if (!props) props = {};
+
+ if (!(/^[\w-]+$/).test(tag)){
+ var parsed = Slick.parse(tag).expressions[0][0];
+ tag = (parsed.tag == '*') ? 'div' : parsed.tag;
+ if (parsed.id && props.id == null) props.id = parsed.id;
+
+ var attributes = parsed.attributes;
+ if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){
+ attr = attributes[i];
+ if (props[attr.key] != null) continue;
+
+ if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value;
+ else if (!attr.value && !attr.operator) props[attr.key] = true;
+ }
+
+ if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
+ }
+
+ return document.newElement(tag, props);
+};
+
+
+if (Browser.Element){
+ Element.prototype = Browser.Element.prototype;
+ // IE8 and IE9 require the wrapping.
+ Element.prototype._fireEvent = (function(fireEvent){
+ return function(type, event){
+ return fireEvent.call(this, type, event);
+ };
+ })(Element.prototype.fireEvent);
+}
+
+new Type('Element', Element).mirror(function(name){
+ if (Array.prototype[name]) return;
+
+ var obj = {};
+ obj[name] = function(){
+ var results = [], args = arguments, elements = true;
+ for (var i = 0, l = this.length; i < l; i++){
+ var element = this[i], result = results[i] = element[name].apply(element, args);
+ elements = (elements && typeOf(result) == 'element');
+ }
+ return (elements) ? new Elements(results) : results;
+ };
+
+ Elements.implement(obj);
+});
+
+if (!Browser.Element){
+ Element.parent = Object;
+
+ Element.Prototype = {
+ '$constructor': Element,
+ '$family': Function.from('element').hide()
+ };
+
+ Element.mirror(function(name, method){
+ Element.Prototype[name] = method;
+ });
+}
+
+Element.Constructors = {};
+
+
+
+var IFrame = new Type('IFrame', function(){
+ var params = Array.link(arguments, {
+ properties: Type.isObject,
+ iframe: function(obj){
+ return (obj != null);
+ }
+ });
+
+ var props = params.properties || {}, iframe;
+ if (params.iframe) iframe = document.id(params.iframe);
+ var onload = props.onload || function(){};
+ delete props.onload;
+ props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
+ iframe = new Element(iframe || 'iframe', props);
+
+ var onLoad = function(){
+ onload.call(iframe.contentWindow);
+ };
+
+ if (window.frames[props.id]) onLoad();
+ else iframe.addListener('load', onLoad);
+ return iframe;
+});
+
+var Elements = this.Elements = function(nodes){
+ if (nodes && nodes.length){
+ var uniques = {}, node;
+ for (var i = 0; node = nodes[i++];){
+ var uid = Slick.uidOf(node);
+ if (!uniques[uid]){
+ uniques[uid] = true;
+ this.push(node);
+ }
+ }
+ }
+};
+
+Elements.prototype = {length: 0};
+Elements.parent = Array;
+
+new Type('Elements', Elements).implement({
+
+ filter: function(filter, bind){
+ if (!filter) return this;
+ return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){
+ return item.match(filter);
+ } : filter, bind));
+ }.protect(),
+
+ push: function(){
+ var length = this.length;
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = document.id(arguments[i]);
+ if (item) this[length++] = item;
+ }
+ return (this.length = length);
+ }.protect(),
+
+ unshift: function(){
+ var items = [];
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = document.id(arguments[i]);
+ if (item) items.push(item);
+ }
+ return Array.prototype.unshift.apply(this, items);
+ }.protect(),
+
+ concat: function(){
+ var newElements = new Elements(this);
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = arguments[i];
+ if (Type.isEnumerable(item)) newElements.append(item);
+ else newElements.push(item);
+ }
+ return newElements;
+ }.protect(),
+
+ append: function(collection){
+ for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
+ return this;
+ }.protect(),
+
+ empty: function(){
+ while (this.length) delete this[--this.length];
+ return this;
+ }.protect()
+
+});
+
+
+
+(function(){
+
+// FF, IE
+var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
+
+splice.call(object, 1, 1);
+if (object[1] == 1) Elements.implement('splice', function(){
+ var length = this.length;
+ var result = splice.apply(this, arguments);
+ while (length >= this.length) delete this[length--];
+ return result;
+}.protect());
+
+Array.forEachMethod(function(method, name){
+ Elements.implement(name, method);
+});
+
+Array.mirror(Elements);
+
+/*<ltIE8>*/
+var createElementAcceptsHTML;
+try {
+ createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x');
+} catch (e){}
+
+var escapeQuotes = function(html){
+ return ('' + html).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
+};
+/*</ltIE8>*/
+
+Document.implement({
+
+ newElement: function(tag, props){
+ if (props && props.checked != null) props.defaultChecked = props.checked;
+ /*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
+ if (createElementAcceptsHTML && props){
+ tag = '<' + tag;
+ if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
+ if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
+ tag += '>';
+ delete props.name;
+ delete props.type;
+ }
+ /*</ltIE8>*/
+ return this.id(this.createElement(tag)).set(props);
+ }
+
+});
+
+})();
+
+(function(){
+
+Slick.uidOf(window);
+Slick.uidOf(document);
+
+Document.implement({
+
+ newTextNode: function(text){
+ return this.createTextNode(text);
+ },
+
+ getDocument: function(){
+ return this;
+ },
+
+ getWindow: function(){
+ return this.window;
+ },
+
+ id: (function(){
+
+ var types = {
+
+ string: function(id, nocash, doc){
+ id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
+ return (id) ? types.element(id, nocash) : null;
+ },
+
+ element: function(el, nocash){
+ Slick.uidOf(el);
+ if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){
+ var fireEvent = el.fireEvent;
+ // wrapping needed in IE7, or else crash
+ el._fireEvent = function(type, event){
+ return fireEvent(type, event);
+ };
+ Object.append(el, Element.Prototype);
+ }
+ return el;
+ },
+
+ object: function(obj, nocash, doc){
+ if (obj.toElement) return types.element(obj.toElement(doc), nocash);
+ return null;
+ }
+
+ };
+
+ types.textnode = types.whitespace = types.window = types.document = function(zero){
+ return zero;
+ };
+
+ return function(el, nocash, doc){
+ if (el && el.$family && el.uniqueNumber) return el;
+ var type = typeOf(el);
+ return (types[type]) ? types[type](el, nocash, doc || document) : null;
+ };
+
+ })()
+
+});
+
+if (window.$ == null) Window.implement('$', function(el, nc){
+ return document.id(el, nc, this.document);
+});
+
+Window.implement({
+
+ getDocument: function(){
+ return this.document;
+ },
+
+ getWindow: function(){
+ return this;
+ }
+
+});
+
+[Document, Element].invoke('implement', {
+
+ getElements: function(expression){
+ return Slick.search(this, expression, new Elements);
+ },
+
+ getElement: function(expression){
+ return document.id(Slick.find(this, expression));
+ }
+
+});
+
+var contains = {contains: function(element){
+ return Slick.contains(this, element);
+}};
+
+if (!document.contains) Document.implement(contains);
+if (!document.createElement('div').contains) Element.implement(contains);
+
+
+
+// tree walking
+
+var injectCombinator = function(expression, combinator){
+ if (!expression) return combinator;
+
+ expression = Object.clone(Slick.parse(expression));
+
+ var expressions = expression.expressions;
+ for (var i = expressions.length; i--;)
+ expressions[i][0].combinator = combinator;
+
+ return expression;
+};
+
+Object.forEach({
+ getNext: '~',
+ getPrevious: '!~',
+ getParent: '!'
+}, function(combinator, method){
+ Element.implement(method, function(expression){
+ return this.getElement(injectCombinator(expression, combinator));
+ });
+});
+
+Object.forEach({
+ getAllNext: '~',
+ getAllPrevious: '!~',
+ getSiblings: '~~',
+ getChildren: '>',
+ getParents: '!'
+}, function(combinator, method){
+ Element.implement(method, function(expression){
+ return this.getElements(injectCombinator(expression, combinator));
+ });
+});
+
+Element.implement({
+
+ getFirst: function(expression){
+ return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
+ },
+
+ getLast: function(expression){
+ return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
+ },
+
+ getWindow: function(){
+ return this.ownerDocument.window;
+ },
+
+ getDocument: function(){
+ return this.ownerDocument;
+ },
+
+ getElementById: function(id){
+ return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
+ },
+
+ match: function(expression){
+ return !expression || Slick.match(this, expression);
+ }
+
+});
+
+
+
+if (window.$$ == null) Window.implement('$$', function(selector){
+ if (arguments.length == 1){
+ if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
+ else if (Type.isEnumerable(selector)) return new Elements(selector);
+ }
+ return new Elements(arguments);
+});
+
+// Inserters
+
+var inserters = {
+
+ before: function(context, element){
+ var parent = element.parentNode;
+ if (parent) parent.insertBefore(context, element);
+ },
+
+ after: function(context, element){
+ var parent = element.parentNode;
+ if (parent) parent.insertBefore(context, element.nextSibling);
+ },
+
+ bottom: function(context, element){
+ element.appendChild(context);
+ },
+
+ top: function(context, element){
+ element.insertBefore(context, element.firstChild);
+ }
+
+};
+
+inserters.inside = inserters.bottom;
+
+
+
+// getProperty / setProperty
+
+var propertyGetters = {}, propertySetters = {};
+
+// properties
+
+var properties = {};
+Array.forEach([
+ 'type', 'value', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan',
+ 'frameBorder', 'rowSpan', 'tabIndex', 'useMap'
+], function(property){
+ properties[property.toLowerCase()] = property;
+});
+
+properties.html = 'innerHTML';
+properties.text = (document.createElement('div').textContent == null) ? 'innerText': 'textContent';
+
+Object.forEach(properties, function(real, key){
+ propertySetters[key] = function(node, value){
+ node[real] = value;
+ };
+ propertyGetters[key] = function(node){
+ return node[real];
+ };
+});
+
+// Booleans
+
+var bools = [
+ 'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked',
+ 'disabled', 'readOnly', 'multiple', 'selected', 'noresize',
+ 'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay',
+ 'loop'
+];
+
+var booleans = {};
+Array.forEach(bools, function(bool){
+ var lower = bool.toLowerCase();
+ booleans[lower] = bool;
+ propertySetters[lower] = function(node, value){
+ node[bool] = !!value;
+ };
+ propertyGetters[lower] = function(node){
+ return !!node[bool];
+ };
+});
+
+// Special cases
+
+Object.append(propertySetters, {
+
+ 'class': function(node, value){
+ ('className' in node) ? node.className = (value || '') : node.setAttribute('class', value);
+ },
+
+ 'for': function(node, value){
+ ('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value);
+ },
+
+ 'style': function(node, value){
+ (node.style) ? node.style.cssText = value : node.setAttribute('style', value);
+ },
+
+ 'value': function(node, value){
+ node.value = (value != null) ? value : '';
+ }
+
+});
+
+propertyGetters['class'] = function(node){
+ return ('className' in node) ? node.className || null : node.getAttribute('class');
+};
+
+/* <webkit> */
+var el = document.createElement('button');
+// IE sets type as readonly and throws
+try { el.type = 'button'; } catch(e){}
+if (el.type != 'button') propertySetters.type = function(node, value){
+ node.setAttribute('type', value);
+};
+el = null;
+/* </webkit> */
+
+/*<IE>*/
+var input = document.createElement('input');
+input.value = 't';
+input.type = 'submit';
+if (input.value != 't') propertySetters.type = function(node, type){
+ var value = node.value;
+ node.type = type;
+ node.value = value;
+};
+input = null;
+/*</IE>*/
+
+/* getProperty, setProperty */
+
+/* <ltIE9> */
+var pollutesGetAttribute = (function(div){
+ div.random = 'attribute';
+ return (div.getAttribute('random') == 'attribute');
+})(document.createElement('div'));
+
+var hasCloneBug = (function(test){
+ test.innerHTML = '<object><param name="should_fix" value="the unknown"></object>';
+ return test.cloneNode(true).firstChild.childNodes.length != 1;
+})(document.createElement('div'));
+/* </ltIE9> */
+
+var hasClassList = !!document.createElement('div').classList;
+
+var classes = function(className){
+ var classNames = (className || '').clean().split(" "), uniques = {};
+ return classNames.filter(function(className){
+ if (className !== "" && !uniques[className]) return uniques[className] = className;
+ });
+};
+
+var addToClassList = function(name){
+ this.classList.add(name);
+};
+
+var removeFromClassList = function(name){
+ this.classList.remove(name);
+};
+
+Element.implement({
+
+ setProperty: function(name, value){
+ var setter = propertySetters[name.toLowerCase()];
+ if (setter){
+ setter(this, value);
+ } else {
+ /* <ltIE9> */
+ var attributeWhiteList;
+ if (pollutesGetAttribute) attributeWhiteList = this.retrieve('$attributeWhiteList', {});
+ /* </ltIE9> */
+
+ if (value == null){
+ this.removeAttribute(name);
+ /* <ltIE9> */
+ if (pollutesGetAttribute) delete attributeWhiteList[name];
+ /* </ltIE9> */
+ } else {
+ this.setAttribute(name, '' + value);
+ /* <ltIE9> */
+ if (pollutesGetAttribute) attributeWhiteList[name] = true;
+ /* </ltIE9> */
+ }
+ }
+ return this;
+ },
+
+ setProperties: function(attributes){
+ for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
+ return this;
+ },
+
+ getProperty: function(name){
+ var getter = propertyGetters[name.toLowerCase()];
+ if (getter) return getter(this);
+ /* <ltIE9> */
+ if (pollutesGetAttribute){
+ var attr = this.getAttributeNode(name), attributeWhiteList = this.retrieve('$attributeWhiteList', {});
+ if (!attr) return null;
+ if (attr.expando && !attributeWhiteList[name]){
+ var outer = this.outerHTML;
+ // segment by the opening tag and find mention of attribute name
+ if (outer.substr(0, outer.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(name) < 0) return null;
+ attributeWhiteList[name] = true;
+ }
+ }
+ /* </ltIE9> */
+ var result = Slick.getAttribute(this, name);
+ return (!result && !Slick.hasAttribute(this, name)) ? null : result;
+ },
+
+ getProperties: function(){
+ var args = Array.from(arguments);
+ return args.map(this.getProperty, this).associate(args);
+ },
+
+ removeProperty: function(name){
+ return this.setProperty(name, null);
+ },
+
+ removeProperties: function(){
+ Array.each(arguments, this.removeProperty, this);
+ return this;
+ },
+
+ set: function(prop, value){
+ var property = Element.Properties[prop];
+ (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
+ }.overloadSetter(),
+
+ get: function(prop){
+ var property = Element.Properties[prop];
+ return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
+ }.overloadGetter(),
+
+ erase: function(prop){
+ var property = Element.Properties[prop];
+ (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
+ return this;
+ },
+
+ hasClass: hasClassList ? function(className){
+ return this.classList.contains(className);
+ } : function(className){
+ return this.className.clean().contains(className, ' ');
+ },
+
+ addClass: hasClassList ? function(className){
+ classes(className).forEach(addToClassList, this);
+ return this;
+ } : function(className){
+ this.className = classes(className + ' ' + this.className).join(' ');
+ return this;
+ },
+
+ removeClass: hasClassList ? function(className){
+ classes(className).forEach(removeFromClassList, this);
+ return this;
+ } : function(className){
+ var classNames = classes(this.className);
+ classes(className).forEach(classNames.erase, classNames);
+ this.className = classNames.join(' ');
+ return this;
+ },
+
+ toggleClass: function(className, force){
+ if (force == null) force = !this.hasClass(className);
+ return (force) ? this.addClass(className) : this.removeClass(className);
+ },
+
+ adopt: function(){
+ var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
+ if (length > 1) parent = fragment = document.createDocumentFragment();
+
+ for (var i = 0; i < length; i++){
+ var element = document.id(elements[i], true);
+ if (element) parent.appendChild(element);
+ }
+
+ if (fragment) this.appendChild(fragment);
+
+ return this;
+ },
+
+ appendText: function(text, where){
+ return this.grab(this.getDocument().newTextNode(text), where);
+ },
+
+ grab: function(el, where){
+ inserters[where || 'bottom'](document.id(el, true), this);
+ return this;
+ },
+
+ inject: function(el, where){
+ inserters[where || 'bottom'](this, document.id(el, true));
+ return this;
+ },
+
+ replaces: function(el){
+ el = document.id(el, true);
+ el.parentNode.replaceChild(this, el);
+ return this;
+ },
+
+ wraps: function(el, where){
+ el = document.id(el, true);
+ return this.replaces(el).grab(el, where);
+ },
+
+ getSelected: function(){
+ this.selectedIndex; // Safari 3.2.1
+ return new Elements(Array.from(this.options).filter(function(option){
+ return option.selected;
+ }));
+ },
+
+ toQueryString: function(){
+ var queryString = [];
+ this.getElements('input, select, textarea').each(function(el){
+ var type = el.type;
+ if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
+
+ var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
+ // IE
+ return document.id(opt).get('value');
+ }) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
+
+ Array.from(value).each(function(val){
+ if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
+ });
+ });
+ return queryString.join('&');
+ }
+
+});
+
+
+// appendHTML
+
+var appendInserters = {
+ before: 'beforeBegin',
+ after: 'afterEnd',
+ bottom: 'beforeEnd',
+ top: 'afterBegin',
+ inside: 'beforeEnd'
+};
+
+Element.implement('appendHTML', ('insertAdjacentHTML' in document.createElement('div')) ? function(html, where){
+ this.insertAdjacentHTML(appendInserters[where || 'bottom'], html);
+ return this;
+} : function(html, where){
+ var temp = new Element('div', {html: html}),
+ children = temp.childNodes,
+ fragment = temp.firstChild;
+
+ if (!fragment) return this;
+ if (children.length > 1){
+ fragment = document.createDocumentFragment();
+ for (var i = 0, l = children.length; i < l; i++){
+ fragment.appendChild(children[i]);
+ }
+ }
+
+ inserters[where || 'bottom'](fragment, this);
+ return this;
+});
+
+var collected = {}, storage = {};
+
+var get = function(uid){
+ return (storage[uid] || (storage[uid] = {}));
+};
+
+var clean = function(item){
+ var uid = item.uniqueNumber;
+ if (item.removeEvents) item.removeEvents();
+ if (item.clearAttributes) item.clearAttributes();
+ if (uid != null){
+ delete collected[uid];
+ delete storage[uid];
+ }
+ return item;
+};
+
+var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
+
+Element.implement({
+
+ destroy: function(){
+ var children = clean(this).getElementsByTagName('*');
+ Array.each(children, clean);
+ Element.dispose(this);
+ return null;
+ },
+
+ empty: function(){
+ Array.from(this.childNodes).each(Element.dispose);
+ return this;
+ },
+
+ dispose: function(){
+ return (this.parentNode) ? this.parentNode.removeChild(this) : this;
+ },
+
+ clone: function(contents, keepid){
+ contents = contents !== false;
+ var clone = this.cloneNode(contents), ce = [clone], te = [this], i;
+
+ if (contents){
+ ce.append(Array.from(clone.getElementsByTagName('*')));
+ te.append(Array.from(this.getElementsByTagName('*')));
+ }
+
+ for (i = ce.length; i--;){
+ var node = ce[i], element = te[i];
+ if (!keepid) node.removeAttribute('id');
+ /*<ltIE9>*/
+ if (node.clearAttributes){
+ node.clearAttributes();
+ node.mergeAttributes(element);
+ node.removeAttribute('uniqueNumber');
+ if (node.options){
+ var no = node.options, eo = element.options;
+ for (var j = no.length; j--;) no[j].selected = eo[j].selected;
+ }
+ }
+ /*</ltIE9>*/
+ var prop = formProps[element.tagName.toLowerCase()];
+ if (prop && element[prop]) node[prop] = element[prop];
+ }
+
+ /*<ltIE9>*/
+ if (hasCloneBug){
+ var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
+ for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
+ }
+ /*</ltIE9>*/
+ return document.id(clone);
+ }
+
+});
+
+[Element, Window, Document].invoke('implement', {
+
+ addListener: function(type, fn){
+ if (window.attachEvent && !window.addEventListener){
+ collected[Slick.uidOf(this)] = this;
+ }
+ if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
+ else this.attachEvent('on' + type, fn);
+ return this;
+ },
+
+ removeListener: function(type, fn){
+ if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
+ else this.detachEvent('on' + type, fn);
+ return this;
+ },
+
+ retrieve: function(property, dflt){
+ var storage = get(Slick.uidOf(this)), prop = storage[property];
+ if (dflt != null && prop == null) prop = storage[property] = dflt;
+ return prop != null ? prop : null;
+ },
+
+ store: function(property, value){
+ var storage = get(Slick.uidOf(this));
+ storage[property] = value;
+ return this;
+ },
+
+ eliminate: function(property){
+ var storage = get(Slick.uidOf(this));
+ delete storage[property];
+ return this;
+ }
+
+});
+
+/*<ltIE9>*/
+if (window.attachEvent && !window.addEventListener){
+ var gc = function(){
+ Object.each(collected, clean);
+ if (window.CollectGarbage) CollectGarbage();
+ window.removeListener('unload', gc);
+ }
+ window.addListener('unload', gc);
+}
+/*</ltIE9>*/
+
+Element.Properties = {};
+
+
+
+Element.Properties.style = {
+
+ set: function(style){
+ this.style.cssText = style;
+ },
+
+ get: function(){
+ return this.style.cssText;
+ },
+
+ erase: function(){
+ this.style.cssText = '';
+ }
+
+};
+
+Element.Properties.tag = {
+
+ get: function(){
+ return this.tagName.toLowerCase();
+ }
+
+};
+
+Element.Properties.html = {
+
+ set: function(html){
+ if (html == null) html = '';
+ else if (typeOf(html) == 'array') html = html.join('');
+ this.innerHTML = html;
+ },
+
+ erase: function(){
+ this.innerHTML = '';
+ }
+
+};
+
+var supportsHTML5Elements = true, supportsTableInnerHTML = true, supportsTRInnerHTML = true;
+
+/*<ltIE9>*/
+// technique by jdbarlett - http://jdbartlett.com/innershiv/
+var div = document.createElement('div');
+div.innerHTML = '<nav></nav>';
+supportsHTML5Elements = (div.childNodes.length == 1);
+if (!supportsHTML5Elements){
+ var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '),
+ fragment = document.createDocumentFragment(), l = tags.length;
+ while (l--) fragment.createElement(tags[l]);
+}
+div = null;
+/*</ltIE9>*/
+
+/*<IE>*/
+supportsTableInnerHTML = Function.attempt(function(){
+ var table = document.createElement('table');
+ table.innerHTML = '<tr><td></td></tr>';
+ return true;
+});
+
+/*<ltFF4>*/
+var tr = document.createElement('tr'), html = '<td></td>';
+tr.innerHTML = html;
+supportsTRInnerHTML = (tr.innerHTML == html);
+tr = null;
+/*</ltFF4>*/
+
+if (!supportsTableInnerHTML || !supportsTRInnerHTML || !supportsHTML5Elements){
+
+ Element.Properties.html.set = (function(set){
+
+ var translations = {
+ table: [1, '<table>', '</table>'],
+ select: [1, '<select>', '</select>'],
+ tbody: [2, '<table><tbody>', '</tbody></table>'],
+ tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
+ };
+
+ translations.thead = translations.tfoot = translations.tbody;
+
+ return function(html){
+ var wrap = translations[this.get('tag')];
+ if (!wrap && !supportsHTML5Elements) wrap = [0, '', ''];
+ if (!wrap) return set.call(this, html);
+
+ var level = wrap[0], wrapper = document.createElement('div'), target = wrapper;
+ if (!supportsHTML5Elements) fragment.appendChild(wrapper);
+ wrapper.innerHTML = [wrap[1], html, wrap[2]].flatten().join('');
+ while (level--) target = target.firstChild;
+ this.empty().adopt(target.childNodes);
+ if (!supportsHTML5Elements) fragment.removeChild(wrapper);
+ wrapper = null;
+ };
+
+ })(Element.Properties.html.set);
+}
+/*</IE>*/
+
+/*<ltIE9>*/
+var testForm = document.createElement('form');
+testForm.innerHTML = '<select><option>s</option></select>';
+
+if (testForm.firstChild.value != 's') Element.Properties.value = {
+
+ set: function(value){
+ var tag = this.get('tag');
+ if (tag != 'select') return this.setProperty('value', value);
+ var options = this.getElements('option');
+ value = String(value);
+ for (var i = 0; i < options.length; i++){
+ var option = options[i],
+ attr = option.getAttributeNode('value'),
+ optionValue = (attr && attr.specified) ? option.value : option.get('text');
+ if (optionValue === value) return option.selected = true;
+ }
+ },
+
+ get: function(){
+ var option = this, tag = option.get('tag');
+
+ if (tag != 'select' && tag != 'option') return this.getProperty('value');
+
+ if (tag == 'select' && !(option = option.getSelected()[0])) return '';
+
+ var attr = option.getAttributeNode('value');
+ return (attr && attr.specified) ? option.value : option.get('text');
+ }
+
+};
+testForm = null;
+/*</ltIE9>*/
+
+/*<IE>*/
+if (document.createElement('div').getAttributeNode('id')) Element.Properties.id = {
+ set: function(id){
+ this.id = this.getAttributeNode('id').value = id;
+ },
+ get: function(){
+ return this.id || null;
+ },
+ erase: function(){
+ this.id = this.getAttributeNode('id').value = '';
+ }
+};
+/*</IE>*/
+
+})();
+
+
+/*
+---
+
+name: Element.Style
+
+description: Contains methods for interacting with the styles of Elements in a fashionable way.
+
+license: MIT-style license.
+
+requires: Element
+
+provides: Element.Style
+
+...
+*/
+
+(function(){
+
+var html = document.html, el;
+
+//<ltIE9>
+// Check for oldIE, which does not remove styles when they're set to null
+el = document.createElement('div');
+el.style.color = 'red';
+el.style.color = null;
+var doesNotRemoveStyles = el.style.color == 'red';
+
+// check for oldIE, which returns border* shorthand styles in the wrong order (color-width-style instead of width-style-color)
+var border = '1px solid #123abc';
+el.style.border = border;
+var returnsBordersInWrongOrder = el.style.border != border;
+el = null;
+//</ltIE9>
+
+var hasGetComputedStyle = !!window.getComputedStyle;
+
+Element.Properties.styles = {set: function(styles){
+ this.setStyles(styles);
+}};
+
+var hasOpacity = (html.style.opacity != null),
+ hasFilter = (html.style.filter != null),
+ reAlpha = /alpha\(opacity=([\d.]+)\)/i;
+
+var setVisibility = function(element, opacity){
+ element.store('$opacity', opacity);
+ element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden';
+};
+
+//<ltIE9>
+var setFilter = function(element, regexp, value){
+ var style = element.style,
+ filter = style.filter || element.getComputedStyle('filter') || '';
+ style.filter = (regexp.test(filter) ? filter.replace(regexp, value) : filter + ' ' + value).trim();
+ if (!style.filter) style.removeAttribute('filter');
+};
+//</ltIE9>
+
+var setOpacity = (hasOpacity ? function(element, opacity){
+ element.style.opacity = opacity;
+} : (hasFilter ? function(element, opacity){
+ if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
+ if (opacity == null || opacity == 1){
+ setFilter(element, reAlpha, '');
+ if (opacity == 1 && getOpacity(element) != 1) setFilter(element, reAlpha, 'alpha(opacity=100)');
+ } else {
+ setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')');
+ }
+} : setVisibility));
+
+var getOpacity = (hasOpacity ? function(element){
+ var opacity = element.style.opacity || element.getComputedStyle('opacity');
+ return (opacity == '') ? 1 : opacity.toFloat();
+} : (hasFilter ? function(element){
+ var filter = (element.style.filter || element.getComputedStyle('filter')),
+ opacity;
+ if (filter) opacity = filter.match(reAlpha);
+ return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
+} : function(element){
+ var opacity = element.retrieve('$opacity');
+ if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1);
+ return opacity;
+}));
+
+var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat',
+ namedPositions = {left: '0%', top: '0%', center: '50%', right: '100%', bottom: '100%'},
+ hasBackgroundPositionXY = (html.style.backgroundPositionX != null);
+
+//<ltIE9>
+var removeStyle = function(style, property){
+ if (property == 'backgroundPosition'){
+ style.removeAttribute(property + 'X');
+ property += 'Y';
+ }
+ style.removeAttribute(property);
+};
+//</ltIE9>
+
+Element.implement({
+
+ getComputedStyle: function(property){
+ if (!hasGetComputedStyle && this.currentStyle) return this.currentStyle[property.camelCase()];
+ var defaultView = Element.getDocument(this).defaultView,
+ computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
+ return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : '';
+ },
+
+ setStyle: function(property, value){
+ if (property == 'opacity'){
+ if (value != null) value = parseFloat(value);
+ setOpacity(this, value);
+ return this;
+ }
+ property = (property == 'float' ? floatName : property).camelCase();
+ if (typeOf(value) != 'string'){
+ var map = (Element.Styles[property] || '@').split(' ');
+ value = Array.from(value).map(function(val, i){
+ if (!map[i]) return '';
+ return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
+ }).join(' ');
+ } else if (value == String(Number(value))){
+ value = Math.round(value);
+ }
+ this.style[property] = value;
+ //<ltIE9>
+ if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
+ removeStyle(this.style, property);
+ }
+ //</ltIE9>
+ return this;
+ },
+
+ getStyle: function(property){
+ if (property == 'opacity') return getOpacity(this);
+ property = (property == 'float' ? floatName : property).camelCase();
+ var result = this.style[property];
+ if (!result || property == 'zIndex'){
+ if (Element.ShortStyles.hasOwnProperty(property)){
+ result = [];
+ for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s));
+ return result.join(' ');
+ }
+ result = this.getComputedStyle(property);
+ }
+ if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){
+ return result.replace(/(top|right|bottom|left)/g, function(position){
+ return namedPositions[position];
+ }) || '0px';
+ }
+ if (!result && property == 'backgroundPosition') return '0px 0px';
+ if (result){
+ result = String(result);
+ var color = result.match(/rgba?\([\d\s,]+\)/);
+ if (color) result = result.replace(color[0], color[0].rgbToHex());
+ }
+ if (!hasGetComputedStyle && !this.style[property]){
+ if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){
+ var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
+ values.each(function(value){
+ size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
+ }, this);
+ return this['offset' + property.capitalize()] - size + 'px';
+ }
+ if ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
+ return '0px';
+ }
+ }
+ //<ltIE9>
+ if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){
+ return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1');
+ }
+ //</ltIE9>
+ return result;
+ },
+
+ setStyles: function(styles){
+ for (var style in styles) this.setStyle(style, styles[style]);
+ return this;
+ },
+
+ getStyles: function(){
+ var result = {};
+ Array.flatten(arguments).each(function(key){
+ result[key] = this.getStyle(key);
+ }, this);
+ return result;
+ }
+
+});
+
+Element.Styles = {
+ left: '@px', top: '@px', bottom: '@px', right: '@px',
+ width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
+ backgroundColor: 'rgb(@, @, @)', backgroundSize: '@px', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
+ fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
+ margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
+ borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
+ zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
+};
+
+
+
+
+
+Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
+
+['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
+ var Short = Element.ShortStyles;
+ var All = Element.Styles;
+ ['margin', 'padding'].each(function(style){
+ var sd = style + direction;
+ Short[style][sd] = All[sd] = '@px';
+ });
+ var bd = 'border' + direction;
+ Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
+ var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
+ Short[bd] = {};
+ Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
+ Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
+ Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
+});
+
+if (hasBackgroundPositionXY) Element.ShortStyles.backgroundPosition = {backgroundPositionX: '@', backgroundPositionY: '@'};
+})();
+
+
+/*
+---
+
+name: Element.Event
+
+description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events, if necessary.
+
+license: MIT-style license.
+
+requires: [Element, Event]
+
+provides: Element.Event
+
+...
+*/
+
+(function(){
+
+Element.Properties.events = {set: function(events){
+ this.addEvents(events);
+}};
+
+[Element, Window, Document].invoke('implement', {
+
+ addEvent: function(type, fn){
+ var events = this.retrieve('events', {});
+ if (!events[type]) events[type] = {keys: [], values: []};
+ if (events[type].keys.contains(fn)) return this;
+ events[type].keys.push(fn);
+ var realType = type,
+ custom = Element.Events[type],
+ condition = fn,
+ self = this;
+ if (custom){
+ if (custom.onAdd) custom.onAdd.call(this, fn, type);
+ if (custom.condition){
+ condition = function(event){
+ if (custom.condition.call(this, event, type)) return fn.call(this, event);
+ return true;
+ };
+ }
+ if (custom.base) realType = Function.from(custom.base).call(this, type);
+ }
+ var defn = function(){
+ return fn.call(self);
+ };
+ var nativeEvent = Element.NativeEvents[realType];
+ if (nativeEvent){
+ if (nativeEvent == 2){
+ defn = function(event){
+ event = new DOMEvent(event, self.getWindow());
+ if (condition.call(self, event) === false) event.stop();
+ };
+ }
+ this.addListener(realType, defn, arguments[2]);
+ }
+ events[type].values.push(defn);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ var list = events[type];
+ var index = list.keys.indexOf(fn);
+ if (index == -1) return this;
+ var value = list.values[index];
+ delete list.keys[index];
+ delete list.values[index];
+ var custom = Element.Events[type];
+ if (custom){
+ if (custom.onRemove) custom.onRemove.call(this, fn, type);
+ if (custom.base) type = Function.from(custom.base).call(this, type);
+ }
+ return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
+ },
+
+ addEvents: function(events){
+ for (var event in events) this.addEvent(event, events[event]);
+ return this;
+ },
+
+ removeEvents: function(events){
+ var type;
+ if (typeOf(events) == 'object'){
+ for (type in events) this.removeEvent(type, events[type]);
+ return this;
+ }
+ var attached = this.retrieve('events');
+ if (!attached) return this;
+ if (!events){
+ for (type in attached) this.removeEvents(type);
+ this.eliminate('events');
+ } else if (attached[events]){
+ attached[events].keys.each(function(fn){
+ this.removeEvent(events, fn);
+ }, this);
+ delete attached[events];
+ }
+ return this;
+ },
+
+ fireEvent: function(type, args, delay){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ args = Array.from(args);
+
+ events[type].keys.each(function(fn){
+ if (delay) fn.delay(delay, this, args);
+ else fn.apply(this, args);
+ }, this);
+ return this;
+ },
+
+ cloneEvents: function(from, type){
+ from = document.id(from);
+ var events = from.retrieve('events');
+ if (!events) return this;
+ if (!type){
+ for (var eventType in events) this.cloneEvents(from, eventType);
+ } else if (events[type]){
+ events[type].keys.each(function(fn){
+ this.addEvent(type, fn);
+ }, this);
+ }
+ return this;
+ }
+
+});
+
+Element.NativeEvents = {
+ click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
+ mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
+ mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
+ keydown: 2, keypress: 2, keyup: 2, //keyboard
+ orientationchange: 2, // mobile
+ touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
+ gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
+ focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements
+ load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
+ hashchange: 1, popstate: 2, // history
+ error: 1, abort: 1, scroll: 1 //misc
+};
+
+Element.Events = {
+ mousewheel: {
+ base: 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll'
+ }
+};
+
+var check = function(event){
+ var related = event.relatedTarget;
+ if (related == null) return true;
+ if (!related) return false;
+ return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
+};
+
+if ('onmouseenter' in document.documentElement){
+ Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2;
+ Element.MouseenterCheck = check;
+} else {
+ Element.Events.mouseenter = {
+ base: 'mouseover',
+ condition: check
+ };
+
+ Element.Events.mouseleave = {
+ base: 'mouseout',
+ condition: check
+ };
+}
+
+/*<ltIE9>*/
+if (!window.addEventListener){
+ Element.NativeEvents.propertychange = 2;
+ Element.Events.change = {
+ base: function(){
+ var type = this.type;
+ return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change';
+ },
+ condition: function(event){
+ return event.type != 'propertychange' || event.event.propertyName == 'checked';
+ }
+ };
+}
+/*</ltIE9>*/
+
+
+
+})();
+
+
+/*
+---
+
+name: Element.Delegation
+
+description: Extends the Element native object to include the delegate method for more efficient event management.
+
+license: MIT-style license.
+
+requires: [Element.Event]
+
+provides: [Element.Delegation]
+
+...
+*/
+
+(function(){
+
+var eventListenerSupport = !!window.addEventListener;
+
+Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2;
+
+var bubbleUp = function(self, match, fn, event, target){
+ while (target && target != self){
+ if (match(target, event)) return fn.call(target, event, target);
+ target = document.id(target.parentNode);
+ }
+};
+
+var map = {
+ mouseenter: {
+ base: 'mouseover',
+ condition: Element.MouseenterCheck
+ },
+ mouseleave: {
+ base: 'mouseout',
+ condition: Element.MouseenterCheck
+ },
+ focus: {
+ base: 'focus' + (eventListenerSupport ? '' : 'in'),
+ capture: true
+ },
+ blur: {
+ base: eventListenerSupport ? 'blur' : 'focusout',
+ capture: true
+ }
+};
+
+/*<ltIE9>*/
+var _key = '$delegation:';
+var formObserver = function(type){
+
+ return {
+
+ base: 'focusin',
+
+ remove: function(self, uid){
+ var list = self.retrieve(_key + type + 'listeners', {})[uid];
+ if (list && list.forms) for (var i = list.forms.length; i--;){
+ list.forms[i].removeEvent(type, list.fns[i]);
+ }
+ },
+
+ listen: function(self, match, fn, event, target, uid){
+ var form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
+ if (!form) return;
+
+ var listeners = self.retrieve(_key + type + 'listeners', {}),
+ listener = listeners[uid] || {forms: [], fns: []},
+ forms = listener.forms, fns = listener.fns;
+
+ if (forms.indexOf(form) != -1) return;
+ forms.push(form);
+
+ var _fn = function(event){
+ bubbleUp(self, match, fn, event, target);
+ };
+ form.addEvent(type, _fn);
+ fns.push(_fn);
+
+ listeners[uid] = listener;
+ self.store(_key + type + 'listeners', listeners);
+ }
+ };
+};
+
+var inputObserver = function(type){
+ return {
+ base: 'focusin',
+ listen: function(self, match, fn, event, target){
+ var events = {blur: function(){
+ this.removeEvents(events);
+ }};
+ events[type] = function(event){
+ bubbleUp(self, match, fn, event, target);
+ };
+ event.target.addEvents(events);
+ }
+ };
+};
+
+if (!eventListenerSupport) Object.append(map, {
+ submit: formObserver('submit'),
+ reset: formObserver('reset'),
+ change: inputObserver('change'),
+ select: inputObserver('select')
+});
+/*</ltIE9>*/
+
+var proto = Element.prototype,
+ addEvent = proto.addEvent,
+ removeEvent = proto.removeEvent;
+
+var relay = function(old, method){
+ return function(type, fn, useCapture){
+ if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture);
+ var parsed = Slick.parse(type).expressions[0][0];
+ if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture);
+ var newType = parsed.tag;
+ parsed.pseudos.slice(1).each(function(pseudo){
+ newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : '');
+ });
+ old.call(this, type, fn);
+ return method.call(this, newType, parsed.pseudos[0].value, fn);
+ };
+};
+
+var delegation = {
+
+ addEvent: function(type, match, fn){
+ var storage = this.retrieve('$delegates', {}), stored = storage[type];
+ if (stored) for (var _uid in stored){
+ if (stored[_uid].fn == fn && stored[_uid].match == match) return this;
+ }
+
+ var _type = type, _match = match, _fn = fn, _map = map[type] || {};
+ type = _map.base || _type;
+
+ match = function(target){
+ return Slick.match(target, _match);
+ };
+
+ var elementEvent = Element.Events[_type];
+ if (_map.condition || elementEvent && elementEvent.condition){
+ var __match = match, condition = _map.condition || elementEvent.condition;
+ match = function(target, event){
+ return __match(target, event) && condition.call(target, event, type);
+ };
+ }
+
+ var self = this, uid = String.uniqueID();
+ var delegator = _map.listen ? function(event, target){
+ if (!target && event && event.target) target = event.target;
+ if (target) _map.listen(self, match, fn, event, target, uid);
+ } : function(event, target){
+ if (!target && event && event.target) target = event.target;
+ if (target) bubbleUp(self, match, fn, event, target);
+ };
+
+ if (!stored) stored = {};
+ stored[uid] = {
+ match: _match,
+ fn: _fn,
+ delegator: delegator
+ };
+ storage[_type] = stored;
+ return addEvent.call(this, type, delegator, _map.capture);
+ },
+
+ removeEvent: function(type, match, fn, _uid){
+ var storage = this.retrieve('$delegates', {}), stored = storage[type];
+ if (!stored) return this;
+
+ if (_uid){
+ var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {};
+ type = _map.base || _type;
+ if (_map.remove) _map.remove(this, _uid);
+ delete stored[_uid];
+ storage[_type] = stored;
+ return removeEvent.call(this, type, delegator, _map.capture);
+ }
+
+ var __uid, s;
+ if (fn) for (__uid in stored){
+ s = stored[__uid];
+ if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid);
+ } else for (__uid in stored){
+ s = stored[__uid];
+ if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid);
+ }
+ return this;
+ }
+
+};
+
+[Element, Window, Document].invoke('implement', {
+ addEvent: relay(addEvent, delegation.addEvent),
+ removeEvent: relay(removeEvent, delegation.removeEvent)
+});
+
+})();
+
+
+/*
+---
+
+name: Element.Dimensions
+
+description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
+
+license: MIT-style license.
+
+credits:
+ - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
+ - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
+
+requires: [Element, Element.Style]
+
+provides: [Element.Dimensions]
+
+...
+*/
+
+(function(){
+
+var element = document.createElement('div'),
+ child = document.createElement('div');
+element.style.height = '0';
+element.appendChild(child);
+var brokenOffsetParent = (child.offsetParent === element);
+element = child = null;
+
+var isOffset = function(el){
+ return styleString(el, 'position') != 'static' || isBody(el);
+};
+
+var isOffsetStatic = function(el){
+ return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
+};
+
+Element.implement({
+
+ scrollTo: function(x, y){
+ if (isBody(this)){
+ this.getWindow().scrollTo(x, y);
+ } else {
+ this.scrollLeft = x;
+ this.scrollTop = y;
+ }
+ return this;
+ },
+
+ getSize: function(){
+ if (isBody(this)) return this.getWindow().getSize();
+ return {x: this.offsetWidth, y: this.offsetHeight};
+ },
+
+ getScrollSize: function(){
+ if (isBody(this)) return this.getWindow().getScrollSize();
+ return {x: this.scrollWidth, y: this.scrollHeight};
+ },
+
+ getScroll: function(){
+ if (isBody(this)) return this.getWindow().getScroll();
+ return {x: this.scrollLeft, y: this.scrollTop};
+ },
+
+ getScrolls: function(){
+ var element = this.parentNode, position = {x: 0, y: 0};
+ while (element && !isBody(element)){
+ position.x += element.scrollLeft;
+ position.y += element.scrollTop;
+ element = element.parentNode;
+ }
+ return position;
+ },
+
+ getOffsetParent: brokenOffsetParent ? function(){
+ var element = this;
+ if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+ var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
+ while ((element = element.parentNode)){
+ if (isOffsetCheck(element)) return element;
+ }
+ return null;
+ } : function(){
+ var element = this;
+ if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+ try {
+ return element.offsetParent;
+ } catch(e) {}
+ return null;
+ },
+
+ getOffsets: function(){
+ var hasGetBoundingClientRect = this.getBoundingClientRect;
+
+ if (hasGetBoundingClientRect){
+ var bound = this.getBoundingClientRect(),
+ html = document.id(this.getDocument().documentElement),
+ htmlScroll = html.getScroll(),
+ elemScrolls = this.getScrolls(),
+ isFixed = (styleString(this, 'position') == 'fixed');
+
+ return {
+ x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
+ y: bound.top.toInt() + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
+ };
+ }
+
+ var element = this, position = {x: 0, y: 0};
+ if (isBody(this)) return position;
+
+ while (element && !isBody(element)){
+ position.x += element.offsetLeft;
+ position.y += element.offsetTop;
+
+ element = element.offsetParent;
+ }
+
+ return position;
+ },
+
+ getPosition: function(relative){
+ var offset = this.getOffsets(),
+ scroll = this.getScrolls();
+ var position = {
+ x: offset.x - scroll.x,
+ y: offset.y - scroll.y
+ };
+
+ if (relative && (relative = document.id(relative))){
+ var relativePosition = relative.getPosition();
+ return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
+ }
+ return position;
+ },
+
+ getCoordinates: function(element){
+ if (isBody(this)) return this.getWindow().getCoordinates();
+ var position = this.getPosition(element),
+ size = this.getSize();
+ var obj = {
+ left: position.x,
+ top: position.y,
+ width: size.x,
+ height: size.y
+ };
+ obj.right = obj.left + obj.width;
+ obj.bottom = obj.top + obj.height;
+ return obj;
+ },
+
+ computePosition: function(obj){
+ return {
+ left: obj.x - styleNumber(this, 'margin-left'),
+ top: obj.y - styleNumber(this, 'margin-top')
+ };
+ },
+
+ setPosition: function(obj){
+ return this.setStyles(this.computePosition(obj));
+ }
+
+});
+
+
+[Document, Window].invoke('implement', {
+
+ getSize: function(){
+ var doc = getCompatElement(this);
+ return {x: doc.clientWidth, y: doc.clientHeight};
+ },
+
+ getScroll: function(){
+ var win = this.getWindow(), doc = getCompatElement(this);
+ return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
+ },
+
+ getScrollSize: function(){
+ var doc = getCompatElement(this),
+ min = this.getSize(),
+ body = this.getDocument().body;
+
+ return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
+ },
+
+ getPosition: function(){
+ return {x: 0, y: 0};
+ },
+
+ getCoordinates: function(){
+ var size = this.getSize();
+ return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
+ }
+
+});
+
+// private methods
+
+var styleString = Element.getComputedStyle;
+
+function styleNumber(element, style){
+ return styleString(element, style).toInt() || 0;
+}
+
+function borderBox(element){
+ return styleString(element, '-moz-box-sizing') == 'border-box';
+}
+
+function topBorder(element){
+ return styleNumber(element, 'border-top-width');
+}
+
+function leftBorder(element){
+ return styleNumber(element, 'border-left-width');
+}
+
+function isBody(element){
+ return (/^(?:body|html)$/i).test(element.tagName);
+}
+
+function getCompatElement(element){
+ var doc = element.getDocument();
+ return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
+}
+
+})();
+
+//aliases
+Element.alias({position: 'setPosition'}); //compatability
+
+[Window, Document, Element].invoke('implement', {
+
+ getHeight: function(){
+ return this.getSize().y;
+ },
+
+ getWidth: function(){
+ return this.getSize().x;
+ },
+
+ getScrollTop: function(){
+ return this.getScroll().y;
+ },
+
+ getScrollLeft: function(){
+ return this.getScroll().x;
+ },
+
+ getScrollHeight: function(){
+ return this.getScrollSize().y;
+ },
+
+ getScrollWidth: function(){
+ return this.getScrollSize().x;
+ },
+
+ getTop: function(){
+ return this.getPosition().y;
+ },
+
+ getLeft: function(){
+ return this.getPosition().x;
+ }
+
+});
+
+
+/*
+---
+
+name: Fx
+
+description: Contains the basic animation logic to be extended by all other Fx Classes.
+
+license: MIT-style license.
+
+requires: [Chain, Events, Options]
+
+provides: Fx
+
+...
+*/
+
+(function(){
+
+var Fx = this.Fx = new Class({
+
+ Implements: [Chain, Events, Options],
+
+ options: {
+ /*
+ onStart: nil,
+ onCancel: nil,
+ onComplete: nil,
+ */
+ fps: 60,
+ unit: false,
+ duration: 500,
+ frames: null,
+ frameSkip: true,
+ link: 'ignore'
+ },
+
+ initialize: function(options){
+ this.subject = this.subject || this;
+ this.setOptions(options);
+ },
+
+ getTransition: function(){
+ return function(p){
+ return -(Math.cos(Math.PI * p) - 1) / 2;
+ };
+ },
+
+ step: function(now){
+ if (this.options.frameSkip){
+ var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
+ this.time = now;
+ this.frame += frames;
+ } else {
+ this.frame++;
+ }
+
+ if (this.frame < this.frames){
+ var delta = this.transition(this.frame / this.frames);
+ this.set(this.compute(this.from, this.to, delta));
+ } else {
+ this.frame = this.frames;
+ this.set(this.compute(this.from, this.to, 1));
+ this.stop();
+ }
+ },
+
+ set: function(now){
+ return now;
+ },
+
+ compute: function(from, to, delta){
+ return Fx.compute(from, to, delta);
+ },
+
+ check: function(){
+ if (!this.isRunning()) return true;
+ switch (this.options.link){
+ case 'cancel': this.cancel(); return true;
+ case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
+ }
+ return false;
+ },
+
+ start: function(from, to){
+ if (!this.check(from, to)) return this;
+ this.from = from;
+ this.to = to;
+ this.frame = (this.options.frameSkip) ? 0 : -1;
+ this.time = null;
+ this.transition = this.getTransition();
+ var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration;
+ this.duration = Fx.Durations[duration] || duration.toInt();
+ this.frameInterval = 1000 / fps;
+ this.frames = frames || Math.round(this.duration / this.frameInterval);
+ this.fireEvent('start', this.subject);
+ pushInstance.call(this, fps);
+ return this;
+ },
+
+ stop: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ if (this.frames == this.frame){
+ this.fireEvent('complete', this.subject);
+ if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
+ } else {
+ this.fireEvent('stop', this.subject);
+ }
+ }
+ return this;
+ },
+
+ cancel: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ this.frame = this.frames;
+ this.fireEvent('cancel', this.subject).clearChain();
+ }
+ return this;
+ },
+
+ pause: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ }
+ return this;
+ },
+
+ resume: function(){
+ if (this.isPaused()) pushInstance.call(this, this.options.fps);
+ return this;
+ },
+
+ isRunning: function(){
+ var list = instances[this.options.fps];
+ return list && list.contains(this);
+ },
+
+ isPaused: function(){
+ return (this.frame < this.frames) && !this.isRunning();
+ }
+
+});
+
+Fx.compute = function(from, to, delta){
+ return (to - from) * delta + from;
+};
+
+Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
+
+// global timers
+
+var instances = {}, timers = {};
+
+var loop = function(){
+ var now = Date.now();
+ for (var i = this.length; i--;){
+ var instance = this[i];
+ if (instance) instance.step(now);
+ }
+};
+
+var pushInstance = function(fps){
+ var list = instances[fps] || (instances[fps] = []);
+ list.push(this);
+ if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
+};
+
+var pullInstance = function(fps){
+ var list = instances[fps];
+ if (list){
+ list.erase(this);
+ if (!list.length && timers[fps]){
+ delete instances[fps];
+ timers[fps] = clearInterval(timers[fps]);
+ }
+ }
+};
+
+})();
+
+
+/*
+---
+
+name: Fx.CSS
+
+description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
+
+license: MIT-style license.
+
+requires: [Fx, Element.Style]
+
+provides: Fx.CSS
+
+...
+*/
+
+Fx.CSS = new Class({
+
+ Extends: Fx,
+
+ //prepares the base from/to object
+
+ prepare: function(element, property, values){
+ values = Array.from(values);
+ var from = values[0], to = values[1];
+ if (to == null){
+ to = from;
+ from = element.getStyle(property);
+ var unit = this.options.unit;
+ // adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299
+ if (unit && from && typeof from == 'string' && from.slice(-unit.length) != unit && parseFloat(from) != 0){
+ element.setStyle(property, to + unit);
+ var value = element.getComputedStyle(property);
+ // IE and Opera support pixelLeft or pixelWidth
+ if (!(/px$/.test(value))){
+ value = element.style[('pixel-' + property).camelCase()];
+ if (value == null){
+ // adapted from Dean Edwards' http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+ var left = element.style.left;
+ element.style.left = to + unit;
+ value = element.style.pixelLeft;
+ element.style.left = left;
+ }
+ }
+ from = (to || 1) / (parseFloat(value) || 1) * (parseFloat(from) || 0);
+ element.setStyle(property, from + unit);
+ }
+ }
+ return {from: this.parse(from), to: this.parse(to)};
+ },
+
+ //parses a value into an array
+
+ parse: function(value){
+ value = Function.from(value)();
+ value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
+ return value.map(function(val){
+ val = String(val);
+ var found = false;
+ Object.each(Fx.CSS.Parsers, function(parser, key){
+ if (found) return;
+ var parsed = parser.parse(val);
+ if (parsed || parsed === 0) found = {value: parsed, parser: parser};
+ });
+ found = found || {value: val, parser: Fx.CSS.Parsers.String};
+ return found;
+ });
+ },
+
+ //computes by a from and to prepared objects, using their parsers.
+
+ compute: function(from, to, delta){
+ var computed = [];
+ (Math.min(from.length, to.length)).times(function(i){
+ computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
+ });
+ computed.$family = Function.from('fx:css:value');
+ return computed;
+ },
+
+ //serves the value as settable
+
+ serve: function(value, unit){
+ if (typeOf(value) != 'fx:css:value') value = this.parse(value);
+ var returned = [];
+ value.each(function(bit){
+ returned = returned.concat(bit.parser.serve(bit.value, unit));
+ });
+ return returned;
+ },
+
+ //renders the change to an element
+
+ render: function(element, property, value, unit){
+ element.setStyle(property, this.serve(value, unit));
+ },
+
+ //searches inside the page css to find the values for a selector
+
+ search: function(selector){
+ if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
+ var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
+
+ var searchStyles = function(rules){
+ Array.each(rules, function(rule, i){
+ if (rule.media){
+ searchStyles(rule.rules || rule.cssRules);
+ return;
+ }
+ if (!rule.style) return;
+ var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
+ return m.toLowerCase();
+ }) : null;
+ if (!selectorText || !selectorTest.test(selectorText)) return;
+ Object.each(Element.Styles, function(value, style){
+ if (!rule.style[style] || Element.ShortStyles[style]) return;
+ value = String(rule.style[style]);
+ to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
+ });
+ });
+ };
+
+ Array.each(document.styleSheets, function(sheet, j){
+ var href = sheet.href;
+ if (href && href.indexOf('://') > -1 && href.indexOf(document.domain) == -1) return;
+ var rules = sheet.rules || sheet.cssRules;
+ searchStyles(rules);
+ });
+ return Fx.CSS.Cache[selector] = to;
+ }
+
+});
+
+Fx.CSS.Cache = {};
+
+Fx.CSS.Parsers = {
+
+ Color: {
+ parse: function(value){
+ if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
+ return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
+ },
+ compute: function(from, to, delta){
+ return from.map(function(value, i){
+ return Math.round(Fx.compute(from[i], to[i], delta));
+ });
+ },
+ serve: function(value){
+ return value.map(Number);
+ }
+ },
+
+ Number: {
+ parse: parseFloat,
+ compute: Fx.compute,
+ serve: function(value, unit){
+ return (unit) ? value + unit : value;
+ }
+ },
+
+ String: {
+ parse: Function.from(false),
+ compute: function(zero, one){
+ return one;
+ },
+ serve: function(zero){
+ return zero;
+ }
+ }
+
+};
+
+
+
+
+/*
+---
+
+name: Fx.Tween
+
+description: Formerly Fx.Style, effect to transition any CSS property for an element.
+
+license: MIT-style license.
+
+requires: Fx.CSS
+
+provides: [Fx.Tween, Element.fade, Element.highlight]
+
+...
+*/
+
+Fx.Tween = new Class({
+
+ Extends: Fx.CSS,
+
+ initialize: function(element, options){
+ this.element = this.subject = document.id(element);
+ this.parent(options);
+ },
+
+ set: function(property, now){
+ if (arguments.length == 1){
+ now = property;
+ property = this.property || this.options.property;
+ }
+ this.render(this.element, property, now, this.options.unit);
+ return this;
+ },
+
+ start: function(property, from, to){
+ if (!this.check(property, from, to)) return this;
+ var args = Array.flatten(arguments);
+ this.property = this.options.property || args.shift();
+ var parsed = this.prepare(this.element, this.property, args);
+ return this.parent(parsed.from, parsed.to);
+ }
+
+});
+
+Element.Properties.tween = {
+
+ set: function(options){
+ this.get('tween').cancel().setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var tween = this.retrieve('tween');
+ if (!tween){
+ tween = new Fx.Tween(this, {link: 'cancel'});
+ this.store('tween', tween);
+ }
+ return tween;
+ }
+
+};
+
+Element.implement({
+
+ tween: function(property, from, to){
+ this.get('tween').start(property, from, to);
+ return this;
+ },
+
+ fade: function(how){
+ var fade = this.get('tween'), method, args = ['opacity'].append(arguments), toggle;
+ if (args[1] == null) args[1] = 'toggle';
+ switch (args[1]){
+ case 'in': method = 'start'; args[1] = 1; break;
+ case 'out': method = 'start'; args[1] = 0; break;
+ case 'show': method = 'set'; args[1] = 1; break;
+ case 'hide': method = 'set'; args[1] = 0; break;
+ case 'toggle':
+ var flag = this.retrieve('fade:flag', this.getStyle('opacity') == 1);
+ method = 'start';
+ args[1] = flag ? 0 : 1;
+ this.store('fade:flag', !flag);
+ toggle = true;
+ break;
+ default: method = 'start';
+ }
+ if (!toggle) this.eliminate('fade:flag');
+ fade[method].apply(fade, args);
+ var to = args[args.length - 1];
+ if (method == 'set' || to != 0) this.setStyle('visibility', to == 0 ? 'hidden' : 'visible');
+ else fade.chain(function(){
+ this.element.setStyle('visibility', 'hidden');
+ this.callChain();
+ });
+ return this;
+ },
+
+ highlight: function(start, end){
+ if (!end){
+ end = this.retrieve('highlight:original', this.getStyle('background-color'));
+ end = (end == 'transparent') ? '#fff' : end;
+ }
+ var tween = this.get('tween');
+ tween.start('background-color', start || '#ffff88', end).chain(function(){
+ this.setStyle('background-color', this.retrieve('highlight:original'));
+ tween.callChain();
+ }.bind(this));
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+name: Fx.Morph
+
+description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
+
+license: MIT-style license.
+
+requires: Fx.CSS
+
+provides: Fx.Morph
+
+...
+*/
+
+Fx.Morph = new Class({
+
+ Extends: Fx.CSS,
+
+ initialize: function(element, options){
+ this.element = this.subject = document.id(element);
+ this.parent(options);
+ },
+
+ set: function(now){
+ if (typeof now == 'string') now = this.search(now);
+ for (var p in now) this.render(this.element, p, now[p], this.options.unit);
+ return this;
+ },
+
+ compute: function(from, to, delta){
+ var now = {};
+ for (var p in from) now[p] = this.parent(from[p], to[p], delta);
+ return now;
+ },
+
+ start: function(properties){
+ if (!this.check(properties)) return this;
+ if (typeof properties == 'string') properties = this.search(properties);
+ var from = {}, to = {};
+ for (var p in properties){
+ var parsed = this.prepare(this.element, p, properties[p]);
+ from[p] = parsed.from;
+ to[p] = parsed.to;
+ }
+ return this.parent(from, to);
+ }
+
+});
+
+Element.Properties.morph = {
+
+ set: function(options){
+ this.get('morph').cancel().setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var morph = this.retrieve('morph');
+ if (!morph){
+ morph = new Fx.Morph(this, {link: 'cancel'});
+ this.store('morph', morph);
+ }
+ return morph;
+ }
+
+};
+
+Element.implement({
+
+ morph: function(props){
+ this.get('morph').start(props);
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+name: Fx.Transitions
+
+description: Contains a set of advanced transitions to be used with any of the Fx Classes.
+
+license: MIT-style license.
+
+credits:
+ - Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
+
+requires: Fx
+
+provides: Fx.Transitions
+
+...
+*/
+
+Fx.implement({
+
+ getTransition: function(){
+ var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
+ if (typeof trans == 'string'){
+ var data = trans.split(':');
+ trans = Fx.Transitions;
+ trans = trans[data[0]] || trans[data[0].capitalize()];
+ if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
+ }
+ return trans;
+ }
+
+});
+
+Fx.Transition = function(transition, params){
+ params = Array.from(params);
+ var easeIn = function(pos){
+ return transition(pos, params);
+ };
+ return Object.append(easeIn, {
+ easeIn: easeIn,
+ easeOut: function(pos){
+ return 1 - transition(1 - pos, params);
+ },
+ easeInOut: function(pos){
+ return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2;
+ }
+ });
+};
+
+Fx.Transitions = {
+
+ linear: function(zero){
+ return zero;
+ }
+
+};
+
+
+
+Fx.Transitions.extend = function(transitions){
+ for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
+};
+
+Fx.Transitions.extend({
+
+ Pow: function(p, x){
+ return Math.pow(p, x && x[0] || 6);
+ },
+
+ Expo: function(p){
+ return Math.pow(2, 8 * (p - 1));
+ },
+
+ Circ: function(p){
+ return 1 - Math.sin(Math.acos(p));
+ },
+
+ Sine: function(p){
+ return 1 - Math.cos(p * Math.PI / 2);
+ },
+
+ Back: function(p, x){
+ x = x && x[0] || 1.618;
+ return Math.pow(p, 2) * ((x + 1) * p - x);
+ },
+
+ Bounce: function(p){
+ var value;
+ for (var a = 0, b = 1; 1; a += b, b /= 2){
+ if (p >= (7 - 4 * a) / 11){
+ value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
+ break;
+ }
+ }
+ return value;
+ },
+
+ Elastic: function(p, x){
+ return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3);
+ }
+
+});
+
+['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
+ Fx.Transitions[transition] = new Fx.Transition(function(p){
+ return Math.pow(p, i + 2);
+ });
+});
+
+
+/*
+---
+
+name: Request
+
+description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
+
+license: MIT-style license.
+
+requires: [Object, Element, Chain, Events, Options, Browser]
+
+provides: Request
+
+...
+*/
+
+(function(){
+
+var empty = function(){},
+ progressSupport = ('onprogress' in new Browser.Request);
+
+var Request = this.Request = new Class({
+
+ Implements: [Chain, Events, Options],
+
+ options: {/*
+ onRequest: function(){},
+ onLoadstart: function(event, xhr){},
+ onProgress: function(event, xhr){},
+ onComplete: function(){},
+ onCancel: function(){},
+ onSuccess: function(responseText, responseXML){},
+ onFailure: function(xhr){},
+ onException: function(headerName, value){},
+ onTimeout: function(){},
+ user: '',
+ password: '',*/
+ url: '',
+ data: '',
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ },
+ async: true,
+ format: false,
+ method: 'post',
+ link: 'ignore',
+ isSuccess: null,
+ emulation: true,
+ urlEncoded: true,
+ encoding: 'utf-8',
+ evalScripts: false,
+ evalResponse: false,
+ timeout: 0,
+ noCache: false
+ },
+
+ initialize: function(options){
+ this.xhr = new Browser.Request();
+ this.setOptions(options);
+ this.headers = this.options.headers;
+ },
+
+ onStateChange: function(){
+ var xhr = this.xhr;
+ if (xhr.readyState != 4 || !this.running) return;
+ this.running = false;
+ this.status = 0;
+ Function.attempt(function(){
+ var status = xhr.status;
+ this.status = (status == 1223) ? 204 : status;
+ }.bind(this));
+ xhr.onreadystatechange = empty;
+ if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
+ clearTimeout(this.timer);
+
+ this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
+ if (this.options.isSuccess.call(this, this.status))
+ this.success(this.response.text, this.response.xml);
+ else
+ this.failure();
+ },
+
+ isSuccess: function(){
+ var status = this.status;
+ return (status >= 200 && status < 300);
+ },
+
+ isRunning: function(){
+ return !!this.running;
+ },
+
+ processScripts: function(text){
+ if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text);
+ return text.stripScripts(this.options.evalScripts);
+ },
+
+ success: function(text, xml){
+ this.onSuccess(this.processScripts(text), xml);
+ },
+
+ onSuccess: function(){
+ this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
+ },
+
+ failure: function(){
+ this.onFailure();
+ },
+
+ onFailure: function(){
+ this.fireEvent('complete').fireEvent('failure', this.xhr);
+ },
+
+ loadstart: function(event){
+ this.fireEvent('loadstart', [event, this.xhr]);
+ },
+
+ progress: function(event){
+ this.fireEvent('progress', [event, this.xhr]);
+ },
+
+ timeout: function(){
+ this.fireEvent('timeout', this.xhr);
+ },
+
+ setHeader: function(name, value){
+ this.headers[name] = value;
+ return this;
+ },
+
+ getHeader: function(name){
+ return Function.attempt(function(){
+ return this.xhr.getResponseHeader(name);
+ }.bind(this));
+ },
+
+ check: function(){
+ if (!this.running) return true;
+ switch (this.options.link){
+ case 'cancel': this.cancel(); return true;
+ case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
+ }
+ return false;
+ },
+
+ send: function(options){
+ if (!this.check(options)) return this;
+
+ this.options.isSuccess = this.options.isSuccess || this.isSuccess;
+ this.running = true;
+
+ var type = typeOf(options);
+ if (type == 'string' || type == 'element') options = {data: options};
+
+ var old = this.options;
+ options = Object.append({data: old.data, url: old.url, method: old.method}, options);
+ var data = options.data, url = String(options.url), method = options.method.toLowerCase();
+
+ switch (typeOf(data)){
+ case 'element': data = document.id(data).toQueryString(); break;
+ case 'object': case 'hash': data = Object.toQueryString(data);
+ }
+
+ if (this.options.format){
+ var format = 'format=' + this.options.format;
+ data = (data) ? format + '&' + data : format;
+ }
+
+ if (this.options.emulation && !['get', 'post'].contains(method)){
+ var _method = '_method=' + method;
+ data = (data) ? _method + '&' + data : _method;
+ method = 'post';
+ }
+
+ if (this.options.urlEncoded && ['post', 'put'].contains(method)){
+ var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
+ this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
+ }
+
+ if (!url) url = document.location.pathname;
+
+ var trimPosition = url.lastIndexOf('/');
+ if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
+
+ if (this.options.noCache)
+ url += (url.indexOf('?') > -1 ? '&' : '?') + String.uniqueID();
+
+ if (data && (method == 'get' || method == 'delete')){
+ url += (url.indexOf('?') > -1 ? '&' : '?') + data;
+ data = null;
+ }
+
+ var xhr = this.xhr;
+ if (progressSupport){
+ xhr.onloadstart = this.loadstart.bind(this);
+ xhr.onprogress = this.progress.bind(this);
+ }
+
+ xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
+ if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true;
+
+ xhr.onreadystatechange = this.onStateChange.bind(this);
+
+ Object.each(this.headers, function(value, key){
+ try {
+ xhr.setRequestHeader(key, value);
+ } catch (e){
+ this.fireEvent('exception', [key, value]);
+ }
+ }, this);
+
+ this.fireEvent('request');
+ xhr.send(data);
+ if (!this.options.async) this.onStateChange();
+ else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
+ return this;
+ },
+
+ cancel: function(){
+ if (!this.running) return this;
+ this.running = false;
+ var xhr = this.xhr;
+ xhr.abort();
+ clearTimeout(this.timer);
+ xhr.onreadystatechange = empty;
+ if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
+ this.xhr = new Browser.Request();
+ this.fireEvent('cancel');
+ return this;
+ }
+
+});
+
+var methods = {};
+['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
+ methods[method] = function(data){
+ var object = {
+ method: method
+ };
+ if (data != null) object.data = data;
+ return this.send(object);
+ };
+});
+
+Request.implement(methods);
+
+Element.Properties.send = {
+
+ set: function(options){
+ var send = this.get('send').cancel();
+ send.setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var send = this.retrieve('send');
+ if (!send){
+ send = new Request({
+ data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
+ });
+ this.store('send', send);
+ }
+ return send;
+ }
+
+};
+
+Element.implement({
+
+ send: function(url){
+ var sender = this.get('send');
+ sender.send({data: this, url: url || sender.options.url});
+ return this;
+ }
+
+});
+
+})();
+
+
+/*
+---
+
+name: Request.HTML
+
+description: Extends the basic Request Class with additional methods for interacting with HTML responses.
+
+license: MIT-style license.
+
+requires: [Element, Request]
+
+provides: Request.HTML
+
+...
+*/
+
+Request.HTML = new Class({
+
+ Extends: Request,
+
+ options: {
+ update: false,
+ append: false,
+ evalScripts: true,
+ filter: false,
+ headers: {
+ Accept: 'text/html, application/xml, text/xml, */*'
+ }
+ },
+
+ success: function(text){
+ var options = this.options, response = this.response;
+
+ response.html = text.stripScripts(function(script){
+ response.javascript = script;
+ });
+
+ var match = response.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
+ if (match) response.html = match[1];
+ var temp = new Element('div').set('html', response.html);
+
+ response.tree = temp.childNodes;
+ response.elements = temp.getElements(options.filter || '*');
+
+ if (options.filter) response.tree = response.elements;
+ if (options.update){
+ var update = document.id(options.update).empty();
+ if (options.filter) update.adopt(response.elements);
+ else update.set('html', response.html);
+ } else if (options.append){
+ var append = document.id(options.append);
+ if (options.filter) response.elements.reverse().inject(append);
+ else append.adopt(temp.getChildren());
+ }
+ if (options.evalScripts) Browser.exec(response.javascript);
+
+ this.onSuccess(response.tree, response.elements, response.html, response.javascript);
+ }
+
+});
+
+Element.Properties.load = {
+
+ set: function(options){
+ var load = this.get('load').cancel();
+ load.setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var load = this.retrieve('load');
+ if (!load){
+ load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'});
+ this.store('load', load);
+ }
+ return load;
+ }
+
+};
+
+Element.implement({
+
+ load: function(){
+ this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString}));
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+name: JSON
+
+description: JSON encoder and decoder.
+
+license: MIT-style license.
+
+SeeAlso: <http://www.json.org/>
+
+requires: [Array, String, Number, Function]
+
+provides: JSON
+
+...
+*/
+
+if (typeof JSON == 'undefined') this.JSON = {};
+
+
+
+(function(){
+
+var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
+
+var escape = function(chr){
+ return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
+};
+
+JSON.validate = function(string){
+ string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+ replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+
+ return (/^[\],:{}\s]*$/).test(string);
+};
+
+JSON.encode = JSON.stringify ? function(obj){
+ return JSON.stringify(obj);
+} : function(obj){
+ if (obj && obj.toJSON) obj = obj.toJSON();
+
+ switch (typeOf(obj)){
+ case 'string':
+ return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
+ case 'array':
+ return '[' + obj.map(JSON.encode).clean() + ']';
+ case 'object': case 'hash':
+ var string = [];
+ Object.each(obj, function(value, key){
+ var json = JSON.encode(value);
+ if (json) string.push(JSON.encode(key) + ':' + json);
+ });
+ return '{' + string + '}';
+ case 'number': case 'boolean': return '' + obj;
+ case 'null': return 'null';
+ }
+
+ return null;
+};
+
+JSON.secure = true;
+
+
+JSON.decode = function(string, secure){
+ if (!string || typeOf(string) != 'string') return null;
+
+ if (secure == null) secure = JSON.secure;
+ if (secure){
+ if (JSON.parse) return JSON.parse(string);
+ if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
+ }
+
+ return eval('(' + string + ')');
+};
+
+})();
+
+
+/*
+---
+
+name: Request.JSON
+
+description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
+
+license: MIT-style license.
+
+requires: [Request, JSON]
+
+provides: Request.JSON
+
+...
+*/
+
+Request.JSON = new Class({
+
+ Extends: Request,
+
+ options: {
+ /*onError: function(text, error){},*/
+ secure: true
+ },
+
+ initialize: function(options){
+ this.parent(options);
+ Object.append(this.headers, {
+ 'Accept': 'application/json',
+ 'X-Request': 'JSON'
+ });
+ },
+
+ success: function(text){
+ var json;
+ try {
+ json = this.response.json = JSON.decode(text, this.options.secure);
+ } catch (error){
+ this.fireEvent('error', [text, error]);
+ return;
+ }
+ if (json == null) this.onFailure();
+ else this.onSuccess(json, text);
+ }
+
+});
+
+
+/*
+---
+
+name: Cookie
+
+description: Class for creating, reading, and deleting browser Cookies.
+
+license: MIT-style license.
+
+credits:
+ - Based on the functions by Peter-Paul Koch (http://quirksmode.org).
+
+requires: [Options, Browser]
+
+provides: Cookie
+
+...
+*/
+
+var Cookie = new Class({
+
+ Implements: Options,
+
+ options: {
+ path: '/',
+ domain: false,
+ duration: false,
+ secure: false,
+ document: document,
+ encode: true
+ },
+
+ initialize: function(key, options){
+ this.key = key;
+ this.setOptions(options);
+ },
+
+ write: function(value){
+ if (this.options.encode) value = encodeURIComponent(value);
+ if (this.options.domain) value += '; domain=' + this.options.domain;
+ if (this.options.path) value += '; path=' + this.options.path;
+ if (this.options.duration){
+ var date = new Date();
+ date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
+ value += '; expires=' + date.toGMTString();
+ }
+ if (this.options.secure) value += '; secure';
+ this.options.document.cookie = this.key + '=' + value;
+ return this;
+ },
+
+ read: function(){
+ var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
+ return (value) ? decodeURIComponent(value[1]) : null;
+ },
+
+ dispose: function(){
+ new Cookie(this.key, Object.merge({}, this.options, {duration: -1})).write('');
+ return this;
+ }
+
+});
+
+Cookie.write = function(key, value, options){
+ return new Cookie(key, options).write(value);
+};
+
+Cookie.read = function(key){
+ return new Cookie(key).read();
+};
+
+Cookie.dispose = function(key, options){
+ return new Cookie(key, options).dispose();
+};
+
+
+/*
+---
+
+name: DOMReady
+
+description: Contains the custom event domready.
+
+license: MIT-style license.
+
+requires: [Browser, Element, Element.Event]
+
+provides: [DOMReady, DomReady]
+
+...
+*/
+
+(function(window, document){
+
+var ready,
+ loaded,
+ checks = [],
+ shouldPoll,
+ timer,
+ testElement = document.createElement('div');
+
+var domready = function(){
+ clearTimeout(timer);
+ if (ready) return;
+ Browser.loaded = ready = true;
+ document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
+
+ document.fireEvent('domready');
+ window.fireEvent('domready');
+};
+
+var check = function(){
+ for (var i = checks.length; i--;) if (checks[i]()){
+ domready();
+ return true;
+ }
+ return false;
+};
+
+var poll = function(){
+ clearTimeout(timer);
+ if (!check()) timer = setTimeout(poll, 10);
+};
+
+document.addListener('DOMContentLoaded', domready);
+
+/*<ltIE8>*/
+// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
+// testElement.doScroll() throws when the DOM is not ready, only in the top window
+var doScrollWorks = function(){
+ try {
+ testElement.doScroll();
+ return true;
+ } catch (e){}
+ return false;
+};
+// If doScroll works already, it can't be used to determine domready
+// e.g. in an iframe
+if (testElement.doScroll && !doScrollWorks()){
+ checks.push(doScrollWorks);
+ shouldPoll = true;
+}
+/*</ltIE8>*/
+
+if (document.readyState) checks.push(function(){
+ var state = document.readyState;
+ return (state == 'loaded' || state == 'complete');
+});
+
+if ('onreadystatechange' in document) document.addListener('readystatechange', check);
+else shouldPoll = true;
+
+if (shouldPoll) poll();
+
+Element.Events.domready = {
+ onAdd: function(fn){
+ if (ready) fn.call(this);
+ }
+};
+
+// Make sure that domready fires before load
+Element.Events.load = {
+ base: 'load',
+ onAdd: function(fn){
+ if (loaded && this == window) fn.call(this);
+ },
+ condition: function(){
+ if (this == window){
+ domready();
+ delete Element.Events.load;
+ }
+ return true;
+ }
+};
+
+// This is based on the custom load event
+window.addEvent('load', function(){
+ loaded = true;
+});
+
+})(window, document);
+
diff --git a/module/webui/themes/Default/js/static/mootools-core.min.js b/module/webui/themes/Default/js/static/mootools-core.min.js
new file mode 100644
index 000000000..354f94196
--- /dev/null
+++ b/module/webui/themes/Default/js/static/mootools-core.min.js
@@ -0,0 +1,491 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/core/8423c12ffd6a6bfcde9ea22554aec795
+
+packager build:
+ - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady
+
+copyrights:
+ - [MooTools](http://mootools.net)
+
+licenses:
+ - [MIT License](http://mootools.net/license.txt)
+...
+*/
+
+(function(){this.MooTools={version:"1.5.0",build:"0f7b690afee9349b15909f33016a25d2e4d9f4e3"};var o=this.typeOf=function(i){if(i==null){return"null";}if(i.$family!=null){return i.$family();
+}if(i.nodeName){if(i.nodeType==1){return"element";}if(i.nodeType==3){return(/\S/).test(i.nodeValue)?"textnode":"whitespace";}}else{if(typeof i.length=="number"){if("callee" in i){return"arguments";
+}if("item" in i){return"collection";}}}return typeof i;};var j=this.instanceOf=function(t,i){if(t==null){return false;}var s=t.$constructor||t.constructor;
+while(s){if(s===i){return true;}s=s.parent;}if(!t.hasOwnProperty){return false;}return t instanceof i;};var f=this.Function;var p=true;for(var k in {toString:1}){p=null;
+}if(p){p=["hasOwnProperty","valueOf","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","constructor"];}f.prototype.overloadSetter=function(s){var i=this;
+return function(u,t){if(u==null){return this;}if(s||typeof u!="string"){for(var v in u){i.call(this,v,u[v]);}if(p){for(var w=p.length;w--;){v=p[w];if(u.hasOwnProperty(v)){i.call(this,v,u[v]);
+}}}}else{i.call(this,u,t);}return this;};};f.prototype.overloadGetter=function(s){var i=this;return function(u){var v,t;if(typeof u!="string"){v=u;}else{if(arguments.length>1){v=arguments;
+}else{if(s){v=[u];}}}if(v){t={};for(var w=0;w<v.length;w++){t[v[w]]=i.call(this,v[w]);}}else{t=i.call(this,u);}return t;};};f.prototype.extend=function(i,s){this[i]=s;
+}.overloadSetter();f.prototype.implement=function(i,s){this.prototype[i]=s;}.overloadSetter();var n=Array.prototype.slice;f.from=function(i){return(o(i)=="function")?i:function(){return i;
+};};Array.from=function(i){if(i==null){return[];}return(a.isEnumerable(i)&&typeof i!="string")?(o(i)=="array")?i:n.call(i):[i];};Number.from=function(s){var i=parseFloat(s);
+return isFinite(i)?i:null;};String.from=function(i){return i+"";};f.implement({hide:function(){this.$hidden=true;return this;},protect:function(){this.$protected=true;
+return this;}});var a=this.Type=function(u,t){if(u){var s=u.toLowerCase();var i=function(v){return(o(v)==s);};a["is"+u]=i;if(t!=null){t.prototype.$family=(function(){return s;
+}).hide();}}if(t==null){return null;}t.extend(this);t.$constructor=a;t.prototype.$constructor=t;return t;};var e=Object.prototype.toString;a.isEnumerable=function(i){return(i!=null&&typeof i.length=="number"&&e.call(i)!="[object Function]");
+};var q={};var r=function(i){var s=o(i.prototype);return q[s]||(q[s]=[]);};var b=function(t,x){if(x&&x.$hidden){return;}var s=r(this);for(var u=0;u<s.length;
+u++){var w=s[u];if(o(w)=="type"){b.call(w,t,x);}else{w.call(this,t,x);}}var v=this.prototype[t];if(v==null||!v.$protected){this.prototype[t]=x;}if(this[t]==null&&o(x)=="function"){m.call(this,t,function(i){return x.apply(i,n.call(arguments,1));
+});}};var m=function(i,t){if(t&&t.$hidden){return;}var s=this[i];if(s==null||!s.$protected){this[i]=t;}};a.implement({implement:b.overloadSetter(),extend:m.overloadSetter(),alias:function(i,s){b.call(this,i,this.prototype[s]);
+}.overloadSetter(),mirror:function(i){r(this).push(i);return this;}});new a("Type",a);var d=function(s,x,v){var u=(x!=Object),B=x.prototype;if(u){x=new a(s,x);
+}for(var y=0,w=v.length;y<w;y++){var C=v[y],A=x[C],z=B[C];if(A){A.protect();}if(u&&z){x.implement(C,z.protect());}}if(u){var t=B.propertyIsEnumerable(v[0]);
+x.forEachMethod=function(G){if(!t){for(var F=0,D=v.length;F<D;F++){G.call(B,B[v[F]],v[F]);}}for(var E in B){G.call(B,B[E],E);}};}return d;};d("String",String,["charAt","charCodeAt","concat","contains","indexOf","lastIndexOf","match","quote","replace","search","slice","split","substr","substring","trim","toLowerCase","toUpperCase"])("Array",Array,["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice","indexOf","lastIndexOf","filter","forEach","every","map","some","reduce","reduceRight"])("Number",Number,["toExponential","toFixed","toLocaleString","toPrecision"])("Function",f,["apply","call","bind"])("RegExp",RegExp,["exec","test"])("Object",Object,["create","defineProperty","defineProperties","keys","getPrototypeOf","getOwnPropertyDescriptor","getOwnPropertyNames","preventExtensions","isExtensible","seal","isSealed","freeze","isFrozen"])("Date",Date,["now"]);
+Object.extend=m.overloadSetter();Date.extend("now",function(){return +(new Date);});new a("Boolean",Boolean);Number.prototype.$family=function(){return isFinite(this)?"number":"null";
+}.hide();Number.extend("random",function(s,i){return Math.floor(Math.random()*(i-s+1)+s);});var g=Object.prototype.hasOwnProperty;Object.extend("forEach",function(i,t,u){for(var s in i){if(g.call(i,s)){t.call(u,i[s],s,i);
+}}});Object.each=Object.forEach;Array.implement({forEach:function(u,v){for(var t=0,s=this.length;t<s;t++){if(t in this){u.call(v,this[t],t,this);}}},each:function(i,s){Array.forEach(this,i,s);
+return this;}});var l=function(i){switch(o(i)){case"array":return i.clone();case"object":return Object.clone(i);default:return i;}};Array.implement("clone",function(){var s=this.length,t=new Array(s);
+while(s--){t[s]=l(this[s]);}return t;});var h=function(s,i,t){switch(o(t)){case"object":if(o(s[i])=="object"){Object.merge(s[i],t);}else{s[i]=Object.clone(t);
+}break;case"array":s[i]=t.clone();break;default:s[i]=t;}return s;};Object.extend({merge:function(z,u,t){if(o(u)=="string"){return h(z,u,t);}for(var y=1,s=arguments.length;
+y<s;y++){var w=arguments[y];for(var x in w){h(z,x,w[x]);}}return z;},clone:function(i){var t={};for(var s in i){t[s]=l(i[s]);}return t;},append:function(w){for(var v=1,t=arguments.length;
+v<t;v++){var s=arguments[v]||{};for(var u in s){w[u]=s[u];}}return w;}});["Object","WhiteSpace","TextNode","Collection","Arguments"].each(function(i){new a(i);
+});var c=Date.now();String.extend("uniqueID",function(){return(c++).toString(36);});})();Array.implement({every:function(c,d){for(var b=0,a=this.length>>>0;
+b<a;b++){if((b in this)&&!c.call(d,this[b],b,this)){return false;}}return true;},filter:function(d,f){var c=[];for(var e,b=0,a=this.length>>>0;b<a;b++){if(b in this){e=this[b];
+if(d.call(f,e,b,this)){c.push(e);}}}return c;},indexOf:function(c,d){var b=this.length>>>0;for(var a=(d<0)?Math.max(0,b+d):d||0;a<b;a++){if(this[a]===c){return a;
+}}return -1;},map:function(c,e){var d=this.length>>>0,b=Array(d);for(var a=0;a<d;a++){if(a in this){b[a]=c.call(e,this[a],a,this);}}return b;},some:function(c,d){for(var b=0,a=this.length>>>0;
+b<a;b++){if((b in this)&&c.call(d,this[b],b,this)){return true;}}return false;},clean:function(){return this.filter(function(a){return a!=null;});},invoke:function(a){var b=Array.slice(arguments,1);
+return this.map(function(c){return c[a].apply(c,b);});},associate:function(c){var d={},b=Math.min(this.length,c.length);for(var a=0;a<b;a++){d[c[a]]=this[a];
+}return d;},link:function(c){var a={};for(var e=0,b=this.length;e<b;e++){for(var d in c){if(c[d](this[e])){a[d]=this[e];delete c[d];break;}}}return a;},contains:function(a,b){return this.indexOf(a,b)!=-1;
+},append:function(a){this.push.apply(this,a);return this;},getLast:function(){return(this.length)?this[this.length-1]:null;},getRandom:function(){return(this.length)?this[Number.random(0,this.length-1)]:null;
+},include:function(a){if(!this.contains(a)){this.push(a);}return this;},combine:function(c){for(var b=0,a=c.length;b<a;b++){this.include(c[b]);}return this;
+},erase:function(b){for(var a=this.length;a--;){if(this[a]===b){this.splice(a,1);}}return this;},empty:function(){this.length=0;return this;},flatten:function(){var d=[];
+for(var b=0,a=this.length;b<a;b++){var c=typeOf(this[b]);if(c=="null"){continue;}d=d.concat((c=="array"||c=="collection"||c=="arguments"||instanceOf(this[b],Array))?Array.flatten(this[b]):this[b]);
+}return d;},pick:function(){for(var b=0,a=this.length;b<a;b++){if(this[b]!=null){return this[b];}}return null;},hexToRgb:function(b){if(this.length!=3){return null;
+}var a=this.map(function(c){if(c.length==1){c+=c;}return parseInt(c,16);});return(b)?a:"rgb("+a+")";},rgbToHex:function(d){if(this.length<3){return null;
+}if(this.length==4&&this[3]==0&&!d){return"transparent";}var b=[];for(var a=0;a<3;a++){var c=(this[a]-0).toString(16);b.push((c.length==1)?"0"+c:c);}return(d)?b:"#"+b.join("");
+}});String.implement({contains:function(b,a){return(a?String(this).slice(a):String(this)).indexOf(b)>-1;},test:function(a,b){return((typeOf(a)=="regexp")?a:new RegExp(""+a,b)).test(this);
+},trim:function(){return String(this).replace(/^\s+|\s+$/g,"");},clean:function(){return String(this).replace(/\s+/g," ").trim();},camelCase:function(){return String(this).replace(/-\D/g,function(a){return a.charAt(1).toUpperCase();
+});},hyphenate:function(){return String(this).replace(/[A-Z]/g,function(a){return("-"+a.charAt(0).toLowerCase());});},capitalize:function(){return String(this).replace(/\b[a-z]/g,function(a){return a.toUpperCase();
+});},escapeRegExp:function(){return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1");},toInt:function(a){return parseInt(this,a||10);},toFloat:function(){return parseFloat(this);
+},hexToRgb:function(b){var a=String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);return(a)?a.slice(1).hexToRgb(b):null;},rgbToHex:function(b){var a=String(this).match(/\d{1,3}/g);
+return(a)?a.rgbToHex(b):null;},substitute:function(a,b){return String(this).replace(b||(/\\?\{([^{}]+)\}/g),function(d,c){if(d.charAt(0)=="\\"){return d.slice(1);
+}return(a[c]!=null)?a[c]:"";});}});Number.implement({limit:function(b,a){return Math.min(a,Math.max(b,this));},round:function(a){a=Math.pow(10,a||0).toFixed(a<0?-a:0);
+return Math.round(this*a)/a;},times:function(b,c){for(var a=0;a<this;a++){b.call(c,a,this);}},toFloat:function(){return parseFloat(this);},toInt:function(a){return parseInt(this,a||10);
+}});Number.alias("each","times");(function(b){var a={};b.each(function(c){if(!Number[c]){a[c]=function(){return Math[c].apply(null,[this].concat(Array.from(arguments)));
+};}});Number.implement(a);})(["abs","acos","asin","atan","atan2","ceil","cos","exp","floor","log","max","min","pow","sin","sqrt","tan"]);Function.extend({attempt:function(){for(var b=0,a=arguments.length;
+b<a;b++){try{return arguments[b]();}catch(c){}}return null;}});Function.implement({attempt:function(a,c){try{return this.apply(c,Array.from(a));}catch(b){}return null;
+},bind:function(e){var a=this,b=arguments.length>1?Array.slice(arguments,1):null,d=function(){};var c=function(){var g=e,h=arguments.length;if(this instanceof c){d.prototype=a.prototype;
+g=new d;}var f=(!b&&!h)?a.call(g):a.apply(g,b&&h?b.concat(Array.slice(arguments)):b||arguments);return g==e?f:g;};return c;},pass:function(b,c){var a=this;
+if(b!=null){b=Array.from(b);}return function(){return a.apply(c,b||arguments);};},delay:function(b,c,a){return setTimeout(this.pass((a==null?[]:a),c),b);
+},periodical:function(c,b,a){return setInterval(this.pass((a==null?[]:a),b),c);}});(function(){var a=Object.prototype.hasOwnProperty;Object.extend({subset:function(d,g){var f={};
+for(var e=0,b=g.length;e<b;e++){var c=g[e];if(c in d){f[c]=d[c];}}return f;},map:function(b,e,f){var d={};for(var c in b){if(a.call(b,c)){d[c]=e.call(f,b[c],c,b);
+}}return d;},filter:function(b,e,g){var d={};for(var c in b){var f=b[c];if(a.call(b,c)&&e.call(g,f,c,b)){d[c]=f;}}return d;},every:function(b,d,e){for(var c in b){if(a.call(b,c)&&!d.call(e,b[c],c)){return false;
+}}return true;},some:function(b,d,e){for(var c in b){if(a.call(b,c)&&d.call(e,b[c],c)){return true;}}return false;},keys:function(b){var d=[];for(var c in b){if(a.call(b,c)){d.push(c);
+}}return d;},values:function(c){var b=[];for(var d in c){if(a.call(c,d)){b.push(c[d]);}}return b;},getLength:function(b){return Object.keys(b).length;},keyOf:function(b,d){for(var c in b){if(a.call(b,c)&&b[c]===d){return c;
+}}return null;},contains:function(b,c){return Object.keyOf(b,c)!=null;},toQueryString:function(b,c){var d=[];Object.each(b,function(h,g){if(c){g=c+"["+g+"]";
+}var f;switch(typeOf(h)){case"object":f=Object.toQueryString(h,g);break;case"array":var e={};h.each(function(k,j){e[j]=k;});f=Object.toQueryString(e,g);
+break;default:f=g+"="+encodeURIComponent(h);}if(h!=null){d.push(f);}});return d.join("&");}});})();(function(){var f=this.document;var d=f.window=this;
+var a=function(k,e){k=k.toLowerCase();e=(e?e.toLowerCase():"");var l=k.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/)||[null,"unknown",0];
+if(l[1]=="trident"){l[1]="ie";if(l[4]){l[2]=l[4];}}else{if(l[1]=="crios"){l[1]="chrome";}}var e=k.match(/ip(?:ad|od|hone)/)?"ios":(k.match(/(?:webos|android)/)||e.match(/mac|win|linux/)||["other"])[0];
+if(e=="win"){e="windows";}return{extend:Function.prototype.extend,name:(l[1]=="version")?l[3]:l[1],version:parseFloat((l[1]=="opera"&&l[4])?l[4]:l[2]),platform:e};
+};var j=this.Browser=a(navigator.userAgent,navigator.platform);if(j.ie){j.version=f.documentMode;}j.extend({Features:{xpath:!!(f.evaluate),air:!!(d.runtime),query:!!(f.querySelector),json:!!(d.JSON)},parseUA:a});
+j.Request=(function(){var l=function(){return new XMLHttpRequest();};var k=function(){return new ActiveXObject("MSXML2.XMLHTTP");};var e=function(){return new ActiveXObject("Microsoft.XMLHTTP");
+};return Function.attempt(function(){l();return l;},function(){k();return k;},function(){e();return e;});})();j.Features.xhr=!!(j.Request);j.exec=function(k){if(!k){return k;
+}if(d.execScript){d.execScript(k);}else{var e=f.createElement("script");e.setAttribute("type","text/javascript");e.text=k;f.head.appendChild(e);f.head.removeChild(e);
+}return k;};String.implement("stripScripts",function(k){var e="";var l=this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi,function(m,n){e+=n+"\n";return"";
+});if(k===true){j.exec(e);}else{if(typeOf(k)=="function"){k(e,l);}}return l;});j.extend({Document:this.Document,Window:this.Window,Element:this.Element,Event:this.Event});
+this.Window=this.$constructor=new Type("Window",function(){});this.$family=Function.from("window").hide();Window.mirror(function(e,k){d[e]=k;});this.Document=f.$constructor=new Type("Document",function(){});
+f.$family=Function.from("document").hide();Document.mirror(function(e,k){f[e]=k;});f.html=f.documentElement;if(!f.head){f.head=f.getElementsByTagName("head")[0];
+}if(f.execCommand){try{f.execCommand("BackgroundImageCache",false,true);}catch(c){}}if(this.attachEvent&&!this.addEventListener){var b=function(){this.detachEvent("onunload",b);
+f.head=f.html=f.window=null;};this.attachEvent("onunload",b);}var g=Array.from;try{g(f.html.childNodes);}catch(c){Array.from=function(k){if(typeof k!="string"&&Type.isEnumerable(k)&&typeOf(k)!="array"){var e=k.length,l=new Array(e);
+while(e--){l[e]=k[e];}return l;}return g(k);};var h=Array.prototype,i=h.slice;["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice"].each(function(e){var k=h[e];
+Array[e]=function(l){return k.apply(Array.from(l),i.call(arguments,1));};});}})();(function(){var b={};var a=this.DOMEvent=new Type("DOMEvent",function(c,g){if(!g){g=window;
+}c=c||g.event;if(c.$extended){return c;}this.event=c;this.$extended=true;this.shift=c.shiftKey;this.control=c.ctrlKey;this.alt=c.altKey;this.meta=c.metaKey;
+var i=this.type=c.type;var h=c.target||c.srcElement;while(h&&h.nodeType==3){h=h.parentNode;}this.target=document.id(h);if(i.indexOf("key")==0){var d=this.code=(c.which||c.keyCode);
+this.key=b[d];if(i=="keydown"||i=="keyup"){if(d>111&&d<124){this.key="f"+(d-111);}else{if(d>95&&d<106){this.key=d-96;}}}if(this.key==null){this.key=String.fromCharCode(d).toLowerCase();
+}}else{if(i=="click"||i=="dblclick"||i=="contextmenu"||i=="DOMMouseScroll"||i.indexOf("mouse")==0){var j=g.document;j=(!j.compatMode||j.compatMode=="CSS1Compat")?j.html:j.body;
+this.page={x:(c.pageX!=null)?c.pageX:c.clientX+j.scrollLeft,y:(c.pageY!=null)?c.pageY:c.clientY+j.scrollTop};this.client={x:(c.pageX!=null)?c.pageX-g.pageXOffset:c.clientX,y:(c.pageY!=null)?c.pageY-g.pageYOffset:c.clientY};
+if(i=="DOMMouseScroll"||i=="mousewheel"){this.wheel=(c.wheelDelta)?c.wheelDelta/120:-(c.detail||0)/3;}this.rightClick=(c.which==3||c.button==2);if(i=="mouseover"||i=="mouseout"){var k=c.relatedTarget||c[(i=="mouseover"?"from":"to")+"Element"];
+while(k&&k.nodeType==3){k=k.parentNode;}this.relatedTarget=document.id(k);}}else{if(i.indexOf("touch")==0||i.indexOf("gesture")==0){this.rotation=c.rotation;
+this.scale=c.scale;this.targetTouches=c.targetTouches;this.changedTouches=c.changedTouches;var f=this.touches=c.touches;if(f&&f[0]){var e=f[0];this.page={x:e.pageX,y:e.pageY};
+this.client={x:e.clientX,y:e.clientY};}}}}if(!this.client){this.client={};}if(!this.page){this.page={};}});a.implement({stop:function(){return this.preventDefault().stopPropagation();
+},stopPropagation:function(){if(this.event.stopPropagation){this.event.stopPropagation();}else{this.event.cancelBubble=true;}return this;},preventDefault:function(){if(this.event.preventDefault){this.event.preventDefault();
+}else{this.event.returnValue=false;}return this;}});a.defineKey=function(d,c){b[d]=c;return this;};a.defineKeys=a.defineKey.overloadSetter(true);a.defineKeys({"38":"up","40":"down","37":"left","39":"right","27":"esc","32":"space","8":"backspace","9":"tab","46":"delete","13":"enter"});
+})();(function(){var a=this.Class=new Type("Class",function(h){if(instanceOf(h,Function)){h={initialize:h};}var g=function(){e(this);if(g.$prototyping){return this;
+}this.$caller=null;var i=(this.initialize)?this.initialize.apply(this,arguments):this;this.$caller=this.caller=null;return i;}.extend(this).implement(h);
+g.$constructor=a;g.prototype.$constructor=g;g.prototype.parent=c;return g;});var c=function(){if(!this.$caller){throw new Error('The method "parent" cannot be called.');
+}var g=this.$caller.$name,h=this.$caller.$owner.parent,i=(h)?h.prototype[g]:null;if(!i){throw new Error('The method "'+g+'" has no parent.');}return i.apply(this,arguments);
+};var e=function(g){for(var h in g){var j=g[h];switch(typeOf(j)){case"object":var i=function(){};i.prototype=j;g[h]=e(new i);break;case"array":g[h]=j.clone();
+break;}}return g;};var b=function(g,h,j){if(j.$origin){j=j.$origin;}var i=function(){if(j.$protected&&this.$caller==null){throw new Error('The method "'+h+'" cannot be called.');
+}var l=this.caller,m=this.$caller;this.caller=m;this.$caller=i;var k=j.apply(this,arguments);this.$caller=m;this.caller=l;return k;}.extend({$owner:g,$origin:j,$name:h});
+return i;};var f=function(h,i,g){if(a.Mutators.hasOwnProperty(h)){i=a.Mutators[h].call(this,i);if(i==null){return this;}}if(typeOf(i)=="function"){if(i.$hidden){return this;
+}this.prototype[h]=(g)?i:b(this,h,i);}else{Object.merge(this.prototype,h,i);}return this;};var d=function(g){g.$prototyping=true;var h=new g;delete g.$prototyping;
+return h;};a.implement("implement",f.overloadSetter());a.Mutators={Extends:function(g){this.parent=g;this.prototype=d(g);},Implements:function(g){Array.from(g).each(function(j){var h=new j;
+for(var i in h){f.call(this,i,h[i],true);}},this);}};})();(function(){this.Chain=new Class({$chain:[],chain:function(){this.$chain.append(Array.flatten(arguments));
+return this;},callChain:function(){return(this.$chain.length)?this.$chain.shift().apply(this,arguments):false;},clearChain:function(){this.$chain.empty();
+return this;}});var a=function(b){return b.replace(/^on([A-Z])/,function(c,d){return d.toLowerCase();});};this.Events=new Class({$events:{},addEvent:function(d,c,b){d=a(d);
+this.$events[d]=(this.$events[d]||[]).include(c);if(b){c.internal=true;}return this;},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this;
+},fireEvent:function(e,c,b){e=a(e);var d=this.$events[e];if(!d){return this;}c=Array.from(c);d.each(function(f){if(b){f.delay(b,this,c);}else{f.apply(this,c);
+}},this);return this;},removeEvent:function(e,d){e=a(e);var c=this.$events[e];if(c&&!d.internal){var b=c.indexOf(d);if(b!=-1){delete c[b];}}return this;
+},removeEvents:function(d){var e;if(typeOf(d)=="object"){for(e in d){this.removeEvent(e,d[e]);}return this;}if(d){d=a(d);}for(e in this.$events){if(d&&d!=e){continue;
+}var c=this.$events[e];for(var b=c.length;b--;){if(b in c){this.removeEvent(e,c[b]);}}}return this;}});this.Options=new Class({setOptions:function(){var b=this.options=Object.merge.apply(null,[{},this.options].append(arguments));
+if(this.addEvent){for(var c in b){if(typeOf(b[c])!="function"||!(/^on[A-Z]/).test(c)){continue;}this.addEvent(c,b[c]);delete b[c];}}return this;}});})();
+(function(){var k,n,l,g,a={},c={},m=/\\/g;var e=function(q,p){if(q==null){return null;}if(q.Slick===true){return q;}q=(""+q).replace(/^\s+|\s+$/g,"");g=!!p;
+var o=(g)?c:a;if(o[q]){return o[q];}k={Slick:true,expressions:[],raw:q,reverse:function(){return e(this.raw,true);}};n=-1;while(q!=(q=q.replace(j,b))){}k.length=k.expressions.length;
+return o[k.raw]=(g)?h(k):k;};var i=function(o){if(o==="!"){return" ";}else{if(o===" "){return"!";}else{if((/^!/).test(o)){return o.replace(/^!/,"");}else{return"!"+o;
+}}}};var h=function(u){var r=u.expressions;for(var p=0;p<r.length;p++){var t=r[p];var q={parts:[],tag:"*",combinator:i(t[0].combinator)};for(var o=0;o<t.length;
+o++){var s=t[o];if(!s.reverseCombinator){s.reverseCombinator=" ";}s.combinator=s.reverseCombinator;delete s.reverseCombinator;}t.reverse().push(q);}return u;
+};var f=function(o){return o.replace(/[-[\]{}()*+?.\\^$|,#\s]/g,function(p){return"\\"+p;});};var j=new RegExp("^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)".replace(/<combinator>/,"["+f(">+~`!@$%^&={}\\;</")+"]").replace(/<unicode>/g,"(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])").replace(/<unicode1>/g,"(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])"));
+function b(x,s,D,z,r,C,q,B,A,y,u,F,G,v,p,w){if(s||n===-1){k.expressions[++n]=[];l=-1;if(s){return"";}}if(D||z||l===-1){D=D||" ";var t=k.expressions[n];
+if(g&&t[l]){t[l].reverseCombinator=i(D);}t[++l]={combinator:D,tag:"*"};}var o=k.expressions[n][l];if(r){o.tag=r.replace(m,"");}else{if(C){o.id=C.replace(m,"");
+}else{if(q){q=q.replace(m,"");if(!o.classList){o.classList=[];}if(!o.classes){o.classes=[];}o.classList.push(q);o.classes.push({value:q,regexp:new RegExp("(^|\\s)"+f(q)+"(\\s|$)")});
+}else{if(G){w=w||p;w=w?w.replace(m,""):null;if(!o.pseudos){o.pseudos=[];}o.pseudos.push({key:G.replace(m,""),value:w,type:F.length==1?"class":"element"});
+}else{if(B){B=B.replace(m,"");u=(u||"").replace(m,"");var E,H;switch(A){case"^=":H=new RegExp("^"+f(u));break;case"$=":H=new RegExp(f(u)+"$");break;case"~=":H=new RegExp("(^|\\s)"+f(u)+"(\\s|$)");
+break;case"|=":H=new RegExp("^"+f(u)+"(-|$)");break;case"=":E=function(I){return u==I;};break;case"*=":E=function(I){return I&&I.indexOf(u)>-1;};break;
+case"!=":E=function(I){return u!=I;};break;default:E=function(I){return !!I;};}if(u==""&&(/^[*$^]=$/).test(A)){E=function(){return false;};}if(!E){E=function(I){return I&&H.test(I);
+};}if(!o.attributes){o.attributes=[];}o.attributes.push({key:B,operator:A,value:u,test:E});}}}}}return"";}var d=(this.Slick||{});d.parse=function(o){return e(o);
+};d.escapeRegExp=f;if(!this.Slick){this.Slick=d;}}).apply((typeof exports!="undefined")?exports:this);(function(){var k={},m={},d=Object.prototype.toString;
+k.isNativeCode=function(c){return(/\{\s*\[native code\]\s*\}/).test(""+c);};k.isXML=function(c){return(!!c.xmlVersion)||(!!c.xml)||(d.call(c)=="[object XMLDocument]")||(c.nodeType==9&&c.documentElement.nodeName!="HTML");
+};k.setDocument=function(w){var p=w.nodeType;if(p==9){}else{if(p){w=w.ownerDocument;}else{if(w.navigator){w=w.document;}else{return;}}}if(this.document===w){return;
+}this.document=w;var A=w.documentElement,o=this.getUIDXML(A),s=m[o],r;if(s){for(r in s){this[r]=s[r];}return;}s=m[o]={};s.root=A;s.isXMLDocument=this.isXML(w);
+s.brokenStarGEBTN=s.starSelectsClosedQSA=s.idGetsName=s.brokenMixedCaseQSA=s.brokenGEBCN=s.brokenCheckedQSA=s.brokenEmptyAttributeQSA=s.isHTMLDocument=s.nativeMatchesSelector=false;
+var q,u,y,z,t;var x,v="slick_uniqueid";var c=w.createElement("div");var n=w.body||w.getElementsByTagName("body")[0]||A;n.appendChild(c);try{c.innerHTML='<a id="'+v+'"></a>';
+s.isHTMLDocument=!!w.getElementById(v);}catch(C){}if(s.isHTMLDocument){c.style.display="none";c.appendChild(w.createComment(""));u=(c.getElementsByTagName("*").length>1);
+try{c.innerHTML="foo</foo>";x=c.getElementsByTagName("*");q=(x&&!!x.length&&x[0].nodeName.charAt(0)=="/");}catch(C){}s.brokenStarGEBTN=u||q;try{c.innerHTML='<a name="'+v+'"></a><b id="'+v+'"></b>';
+s.idGetsName=w.getElementById(v)===c.firstChild;}catch(C){}if(c.getElementsByClassName){try{c.innerHTML='<a class="f"></a><a class="b"></a>';c.getElementsByClassName("b").length;
+c.firstChild.className="b";z=(c.getElementsByClassName("b").length!=2);}catch(C){}try{c.innerHTML='<a class="a"></a><a class="f b a"></a>';y=(c.getElementsByClassName("a").length!=2);
+}catch(C){}s.brokenGEBCN=z||y;}if(c.querySelectorAll){try{c.innerHTML="foo</foo>";x=c.querySelectorAll("*");s.starSelectsClosedQSA=(x&&!!x.length&&x[0].nodeName.charAt(0)=="/");
+}catch(C){}try{c.innerHTML='<a class="MiX"></a>';s.brokenMixedCaseQSA=!c.querySelectorAll(".MiX").length;}catch(C){}try{c.innerHTML='<select><option selected="selected">a</option></select>';
+s.brokenCheckedQSA=(c.querySelectorAll(":checked").length==0);}catch(C){}try{c.innerHTML='<a class=""></a>';s.brokenEmptyAttributeQSA=(c.querySelectorAll('[class*=""]').length!=0);
+}catch(C){}}try{c.innerHTML='<form action="s"><input id="action"/></form>';t=(c.firstChild.getAttribute("action")!="s");}catch(C){}s.nativeMatchesSelector=A.matches||A.mozMatchesSelector||A.webkitMatchesSelector;
+if(s.nativeMatchesSelector){try{s.nativeMatchesSelector.call(A,":slick");s.nativeMatchesSelector=null;}catch(C){}}}try{A.slick_expando=1;delete A.slick_expando;
+s.getUID=this.getUIDHTML;}catch(C){s.getUID=this.getUIDXML;}n.removeChild(c);c=x=n=null;s.getAttribute=(s.isHTMLDocument&&t)?function(G,E){var H=this.attributeGetters[E];
+if(H){return H.call(G);}var F=G.getAttributeNode(E);return(F)?F.nodeValue:null;}:function(F,E){var G=this.attributeGetters[E];return(G)?G.call(F):F.getAttribute(E);
+};s.hasAttribute=(A&&this.isNativeCode(A.hasAttribute))?function(F,E){return F.hasAttribute(E);}:function(F,E){F=F.getAttributeNode(E);return !!(F&&(F.specified||F.nodeValue));
+};var D=A&&this.isNativeCode(A.contains),B=w&&this.isNativeCode(w.contains);s.contains=(D&&B)?function(E,F){return E.contains(F);}:(D&&!B)?function(E,F){return E===F||((E===w)?w.documentElement:E).contains(F);
+}:(A&&A.compareDocumentPosition)?function(E,F){return E===F||!!(E.compareDocumentPosition(F)&16);}:function(E,F){if(F){do{if(F===E){return true;}}while((F=F.parentNode));
+}return false;};s.documentSorter=(A.compareDocumentPosition)?function(F,E){if(!F.compareDocumentPosition||!E.compareDocumentPosition){return 0;}return F.compareDocumentPosition(E)&4?-1:F===E?0:1;
+}:("sourceIndex" in A)?function(F,E){if(!F.sourceIndex||!E.sourceIndex){return 0;}return F.sourceIndex-E.sourceIndex;}:(w.createRange)?function(H,F){if(!H.ownerDocument||!F.ownerDocument){return 0;
+}var G=H.ownerDocument.createRange(),E=F.ownerDocument.createRange();G.setStart(H,0);G.setEnd(H,0);E.setStart(F,0);E.setEnd(F,0);return G.compareBoundaryPoints(Range.START_TO_END,E);
+}:null;A=null;for(r in s){this[r]=s[r];}};var f=/^([#.]?)((?:[\w-]+|\*))$/,h=/\[.+[*$^]=(?:""|'')?\]/,g={};k.search=function(U,z,H,s){var p=this.found=(s)?null:(H||[]);
+if(!U){return p;}else{if(U.navigator){U=U.document;}else{if(!U.nodeType){return p;}}}var F,O,V=this.uniques={},I=!!(H&&H.length),y=(U.nodeType==9);if(this.document!==(y?U:U.ownerDocument)){this.setDocument(U);
+}if(I){for(O=p.length;O--;){V[this.getUID(p[O])]=true;}}if(typeof z=="string"){var r=z.match(f);simpleSelectors:if(r){var u=r[1],v=r[2],A,E;if(!u){if(v=="*"&&this.brokenStarGEBTN){break simpleSelectors;
+}E=U.getElementsByTagName(v);if(s){return E[0]||null;}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{if(u=="#"){if(!this.isHTMLDocument||!y){break simpleSelectors;
+}A=U.getElementById(v);if(!A){return p;}if(this.idGetsName&&A.getAttributeNode("id").nodeValue!=v){break simpleSelectors;}if(s){return A||null;}if(!(I&&V[this.getUID(A)])){p.push(A);
+}}else{if(u=="."){if(!this.isHTMLDocument||((!U.getElementsByClassName||this.brokenGEBCN)&&U.querySelectorAll)){break simpleSelectors;}if(U.getElementsByClassName&&!this.brokenGEBCN){E=U.getElementsByClassName(v);
+if(s){return E[0]||null;}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{var T=new RegExp("(^|\\s)"+e.escapeRegExp(v)+"(\\s|$)");E=U.getElementsByTagName("*");
+for(O=0;A=E[O++];){className=A.className;if(!(className&&T.test(className))){continue;}if(s){return A;}if(!(I&&V[this.getUID(A)])){p.push(A);}}}}}}if(I){this.sort(p);
+}return(s)?null:p;}querySelector:if(U.querySelectorAll){if(!this.isHTMLDocument||g[z]||this.brokenMixedCaseQSA||(this.brokenCheckedQSA&&z.indexOf(":checked")>-1)||(this.brokenEmptyAttributeQSA&&h.test(z))||(!y&&z.indexOf(",")>-1)||e.disableQSA){break querySelector;
+}var S=z,x=U;if(!y){var C=x.getAttribute("id"),t="slickid__";x.setAttribute("id",t);S="#"+t+" "+S;U=x.parentNode;}try{if(s){return U.querySelector(S)||null;
+}else{E=U.querySelectorAll(S);}}catch(Q){g[z]=1;break querySelector;}finally{if(!y){if(C){x.setAttribute("id",C);}else{x.removeAttribute("id");}U=x;}}if(this.starSelectsClosedQSA){for(O=0;
+A=E[O++];){if(A.nodeName>"@"&&!(I&&V[this.getUID(A)])){p.push(A);}}}else{for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}if(I){this.sort(p);
+}return p;}F=this.Slick.parse(z);if(!F.length){return p;}}else{if(z==null){return p;}else{if(z.Slick){F=z;}else{if(this.contains(U.documentElement||U,z)){(p)?p.push(z):p=z;
+return p;}else{return p;}}}}this.posNTH={};this.posNTHLast={};this.posNTHType={};this.posNTHTypeLast={};this.push=(!I&&(s||(F.length==1&&F.expressions[0].length==1)))?this.pushArray:this.pushUID;
+if(p==null){p=[];}var M,L,K;var B,J,D,c,q,G,W;var N,P,o,w,R=F.expressions;search:for(O=0;(P=R[O]);O++){for(M=0;(o=P[M]);M++){B="combinator:"+o.combinator;
+if(!this[B]){continue search;}J=(this.isXMLDocument)?o.tag:o.tag.toUpperCase();D=o.id;c=o.classList;q=o.classes;G=o.attributes;W=o.pseudos;w=(M===(P.length-1));
+this.bitUniques={};if(w){this.uniques=V;this.found=p;}else{this.uniques={};this.found=[];}if(M===0){this[B](U,J,D,q,G,W,c);if(s&&w&&p.length){break search;
+}}else{if(s&&w){for(L=0,K=N.length;L<K;L++){this[B](N[L],J,D,q,G,W,c);if(p.length){break search;}}}else{for(L=0,K=N.length;L<K;L++){this[B](N[L],J,D,q,G,W,c);
+}}}N=this.found;}}if(I||(F.expressions.length>1)){this.sort(p);}return(s)?(p[0]||null):p;};k.uidx=1;k.uidk="slick-uniqueid";k.getUIDXML=function(n){var c=n.getAttribute(this.uidk);
+if(!c){c=this.uidx++;n.setAttribute(this.uidk,c);}return c;};k.getUIDHTML=function(c){return c.uniqueNumber||(c.uniqueNumber=this.uidx++);};k.sort=function(c){if(!this.documentSorter){return c;
+}c.sort(this.documentSorter);return c;};k.cacheNTH={};k.matchNTH=/^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;k.parseNTHArgument=function(q){var o=q.match(this.matchNTH);
+if(!o){return false;}var p=o[2]||false;var n=o[1]||1;if(n=="-"){n=-1;}var c=+o[3]||0;o=(p=="n")?{a:n,b:c}:(p=="odd")?{a:2,b:1}:(p=="even")?{a:2,b:0}:{a:0,b:n};
+return(this.cacheNTH[q]=o);};k.createNTHPseudo=function(p,n,c,o){return function(s,q){var u=this.getUID(s);if(!this[c][u]){var A=s.parentNode;if(!A){return false;
+}var r=A[p],t=1;if(o){var z=s.nodeName;do{if(r.nodeName!=z){continue;}this[c][this.getUID(r)]=t++;}while((r=r[n]));}else{do{if(r.nodeType!=1){continue;
+}this[c][this.getUID(r)]=t++;}while((r=r[n]));}}q=q||"n";var v=this.cacheNTH[q]||this.parseNTHArgument(q);if(!v){return false;}var y=v.a,x=v.b,w=this[c][u];
+if(y==0){return x==w;}if(y>0){if(w<x){return false;}}else{if(x<w){return false;}}return((w-x)%y)==0;};};k.pushArray=function(p,c,r,o,n,q){if(this.matchSelector(p,c,r,o,n,q)){this.found.push(p);
+}};k.pushUID=function(q,c,s,p,n,r){var o=this.getUID(q);if(!this.uniques[o]&&this.matchSelector(q,c,s,p,n,r)){this.uniques[o]=true;this.found.push(q);}};
+k.matchNode=function(n,o){if(this.isHTMLDocument&&this.nativeMatchesSelector){try{return this.nativeMatchesSelector.call(n,o.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g,'[$1="$2"]'));
+}catch(u){}}var t=this.Slick.parse(o);if(!t){return true;}var r=t.expressions,s=0,q;for(q=0;(currentExpression=r[q]);q++){if(currentExpression.length==1){var p=currentExpression[0];
+if(this.matchSelector(n,(this.isXMLDocument)?p.tag:p.tag.toUpperCase(),p.id,p.classes,p.attributes,p.pseudos)){return true;}s++;}}if(s==t.length){return false;
+}var c=this.search(this.document,t),v;for(q=0;v=c[q++];){if(v===n){return true;}}return false;};k.matchPseudo=function(q,c,p){var n="pseudo:"+c;if(this[n]){return this[n](q,p);
+}var o=this.getAttribute(q,c);return(p)?p==o:!!o;};k.matchSelector=function(o,v,c,p,q,s){if(v){var t=(this.isXMLDocument)?o.nodeName:o.nodeName.toUpperCase();
+if(v=="*"){if(t<"@"){return false;}}else{if(t!=v){return false;}}}if(c&&o.getAttribute("id")!=c){return false;}var r,n,u;if(p){for(r=p.length;r--;){u=this.getAttribute(o,"class");
+if(!(u&&p[r].regexp.test(u))){return false;}}}if(q){for(r=q.length;r--;){n=q[r];if(n.operator?!n.test(this.getAttribute(o,n.key)):!this.hasAttribute(o,n.key)){return false;
+}}}if(s){for(r=s.length;r--;){n=s[r];if(!this.matchPseudo(o,n.key,n.value)){return false;}}}return true;};var j={" ":function(q,w,n,r,s,u,p){var t,v,o;
+if(this.isHTMLDocument){getById:if(n){v=this.document.getElementById(n);if((!v&&q.all)||(this.idGetsName&&v&&v.getAttributeNode("id").nodeValue!=n)){o=q.all[n];
+if(!o){return;}if(!o[0]){o=[o];}for(t=0;v=o[t++];){var c=v.getAttributeNode("id");if(c&&c.nodeValue==n){this.push(v,w,null,r,s,u);break;}}return;}if(!v){if(this.contains(this.root,q)){return;
+}else{break getById;}}else{if(this.document!==q&&!this.contains(q,v)){return;}}this.push(v,w,null,r,s,u);return;}getByClass:if(r&&q.getElementsByClassName&&!this.brokenGEBCN){o=q.getElementsByClassName(p.join(" "));
+if(!(o&&o.length)){break getByClass;}for(t=0;v=o[t++];){this.push(v,w,n,null,s,u);}return;}}getByTag:{o=q.getElementsByTagName(w);if(!(o&&o.length)){break getByTag;
+}if(!this.brokenStarGEBTN){w=null;}for(t=0;v=o[t++];){this.push(v,w,n,r,s,u);}}},">":function(p,c,r,o,n,q){if((p=p.firstChild)){do{if(p.nodeType==1){this.push(p,c,r,o,n,q);
+}}while((p=p.nextSibling));}},"+":function(p,c,r,o,n,q){while((p=p.nextSibling)){if(p.nodeType==1){this.push(p,c,r,o,n,q);break;}}},"^":function(p,c,r,o,n,q){p=p.firstChild;
+if(p){if(p.nodeType==1){this.push(p,c,r,o,n,q);}else{this["combinator:+"](p,c,r,o,n,q);}}},"~":function(q,c,s,p,n,r){while((q=q.nextSibling)){if(q.nodeType!=1){continue;
+}var o=this.getUID(q);if(this.bitUniques[o]){break;}this.bitUniques[o]=true;this.push(q,c,s,p,n,r);}},"++":function(p,c,r,o,n,q){this["combinator:+"](p,c,r,o,n,q);
+this["combinator:!+"](p,c,r,o,n,q);},"~~":function(p,c,r,o,n,q){this["combinator:~"](p,c,r,o,n,q);this["combinator:!~"](p,c,r,o,n,q);},"!":function(p,c,r,o,n,q){while((p=p.parentNode)){if(p!==this.document){this.push(p,c,r,o,n,q);
+}}},"!>":function(p,c,r,o,n,q){p=p.parentNode;if(p!==this.document){this.push(p,c,r,o,n,q);}},"!+":function(p,c,r,o,n,q){while((p=p.previousSibling)){if(p.nodeType==1){this.push(p,c,r,o,n,q);
+break;}}},"!^":function(p,c,r,o,n,q){p=p.lastChild;if(p){if(p.nodeType==1){this.push(p,c,r,o,n,q);}else{this["combinator:!+"](p,c,r,o,n,q);}}},"!~":function(q,c,s,p,n,r){while((q=q.previousSibling)){if(q.nodeType!=1){continue;
+}var o=this.getUID(q);if(this.bitUniques[o]){break;}this.bitUniques[o]=true;this.push(q,c,s,p,n,r);}}};for(var i in j){k["combinator:"+i]=j[i];}var l={empty:function(c){var n=c.firstChild;
+return !(n&&n.nodeType==1)&&!(c.innerText||c.textContent||"").length;},not:function(c,n){return !this.matchNode(c,n);},contains:function(c,n){return(c.innerText||c.textContent||"").indexOf(n)>-1;
+},"first-child":function(c){while((c=c.previousSibling)){if(c.nodeType==1){return false;}}return true;},"last-child":function(c){while((c=c.nextSibling)){if(c.nodeType==1){return false;
+}}return true;},"only-child":function(o){var n=o;while((n=n.previousSibling)){if(n.nodeType==1){return false;}}var c=o;while((c=c.nextSibling)){if(c.nodeType==1){return false;
+}}return true;},"nth-child":k.createNTHPseudo("firstChild","nextSibling","posNTH"),"nth-last-child":k.createNTHPseudo("lastChild","previousSibling","posNTHLast"),"nth-of-type":k.createNTHPseudo("firstChild","nextSibling","posNTHType",true),"nth-last-of-type":k.createNTHPseudo("lastChild","previousSibling","posNTHTypeLast",true),index:function(n,c){return this["pseudo:nth-child"](n,""+(c+1));
+},even:function(c){return this["pseudo:nth-child"](c,"2n");},odd:function(c){return this["pseudo:nth-child"](c,"2n+1");},"first-of-type":function(c){var n=c.nodeName;
+while((c=c.previousSibling)){if(c.nodeName==n){return false;}}return true;},"last-of-type":function(c){var n=c.nodeName;while((c=c.nextSibling)){if(c.nodeName==n){return false;
+}}return true;},"only-of-type":function(o){var n=o,p=o.nodeName;while((n=n.previousSibling)){if(n.nodeName==p){return false;}}var c=o;while((c=c.nextSibling)){if(c.nodeName==p){return false;
+}}return true;},enabled:function(c){return !c.disabled;},disabled:function(c){return c.disabled;},checked:function(c){return c.checked||c.selected;},focus:function(c){return this.isHTMLDocument&&this.document.activeElement===c&&(c.href||c.type||this.hasAttribute(c,"tabindex"));
+},root:function(c){return(c===this.root);},selected:function(c){return c.selected;}};for(var b in l){k["pseudo:"+b]=l[b];}var a=k.attributeGetters={"for":function(){return("htmlFor" in this)?this.htmlFor:this.getAttribute("for");
+},href:function(){return("href" in this)?this.getAttribute("href",2):this.getAttribute("href");},style:function(){return(this.style)?this.style.cssText:this.getAttribute("style");
+},tabindex:function(){var c=this.getAttributeNode("tabindex");return(c&&c.specified)?c.nodeValue:null;},type:function(){return this.getAttribute("type");
+},maxlength:function(){var c=this.getAttributeNode("maxLength");return(c&&c.specified)?c.nodeValue:null;}};a.MAXLENGTH=a.maxLength=a.maxlength;var e=k.Slick=(this.Slick||{});
+e.version="1.1.7";e.search=function(n,o,c){return k.search(n,o,c);};e.find=function(c,n){return k.search(c,n,null,true);};e.contains=function(c,n){k.setDocument(c);
+return k.contains(c,n);};e.getAttribute=function(n,c){k.setDocument(n);return k.getAttribute(n,c);};e.hasAttribute=function(n,c){k.setDocument(n);return k.hasAttribute(n,c);
+};e.match=function(n,c){if(!(n&&c)){return false;}if(!c||c===n){return true;}k.setDocument(n);return k.matchNode(n,c);};e.defineAttributeGetter=function(c,n){k.attributeGetters[c]=n;
+return this;};e.lookupAttributeGetter=function(c){return k.attributeGetters[c];};e.definePseudo=function(c,n){k["pseudo:"+c]=function(p,o){return n.call(p,o);
+};return this;};e.lookupPseudo=function(c){var n=k["pseudo:"+c];if(n){return function(o){return n.call(this,o);};}return null;};e.override=function(n,c){k.override(n,c);
+return this;};e.isXML=k.isXML;e.uidOf=function(c){return k.getUIDHTML(c);};if(!this.Slick){this.Slick=e;}}).apply((typeof exports!="undefined")?exports:this);
+var Element=this.Element=function(b,g){var h=Element.Constructors[b];if(h){return h(g);}if(typeof b!="string"){return document.id(b).set(g);}if(!g){g={};
+}if(!(/^[\w-]+$/).test(b)){var e=Slick.parse(b).expressions[0][0];b=(e.tag=="*")?"div":e.tag;if(e.id&&g.id==null){g.id=e.id;}var d=e.attributes;if(d){for(var a,f=0,c=d.length;
+f<c;f++){a=d[f];if(g[a.key]!=null){continue;}if(a.value!=null&&a.operator=="="){g[a.key]=a.value;}else{if(!a.value&&!a.operator){g[a.key]=true;}}}}if(e.classList&&g["class"]==null){g["class"]=e.classList.join(" ");
+}}return document.newElement(b,g);};if(Browser.Element){Element.prototype=Browser.Element.prototype;Element.prototype._fireEvent=(function(a){return function(b,c){return a.call(this,b,c);
+};})(Element.prototype.fireEvent);}new Type("Element",Element).mirror(function(a){if(Array.prototype[a]){return;}var b={};b[a]=function(){var h=[],e=arguments,j=true;
+for(var g=0,d=this.length;g<d;g++){var f=this[g],c=h[g]=f[a].apply(f,e);j=(j&&typeOf(c)=="element");}return(j)?new Elements(h):h;};Elements.implement(b);
+});if(!Browser.Element){Element.parent=Object;Element.Prototype={"$constructor":Element,"$family":Function.from("element").hide()};Element.mirror(function(a,b){Element.Prototype[a]=b;
+});}Element.Constructors={};var IFrame=new Type("IFrame",function(){var e=Array.link(arguments,{properties:Type.isObject,iframe:function(f){return(f!=null);
+}});var c=e.properties||{},b;if(e.iframe){b=document.id(e.iframe);}var d=c.onload||function(){};delete c.onload;c.id=c.name=[c.id,c.name,b?(b.id||b.name):"IFrame_"+String.uniqueID()].pick();
+b=new Element(b||"iframe",c);var a=function(){d.call(b.contentWindow);};if(window.frames[c.id]){a();}else{b.addListener("load",a);}return b;});var Elements=this.Elements=function(a){if(a&&a.length){var e={},d;
+for(var c=0;d=a[c++];){var b=Slick.uidOf(d);if(!e[b]){e[b]=true;this.push(d);}}}};Elements.prototype={length:0};Elements.parent=Array;new Type("Elements",Elements).implement({filter:function(a,b){if(!a){return this;
+}return new Elements(Array.filter(this,(typeOf(a)=="string")?function(c){return c.match(a);}:a,b));}.protect(),push:function(){var d=this.length;for(var b=0,a=arguments.length;
+b<a;b++){var c=document.id(arguments[b]);if(c){this[d++]=c;}}return(this.length=d);}.protect(),unshift:function(){var b=[];for(var c=0,a=arguments.length;
+c<a;c++){var d=document.id(arguments[c]);if(d){b.push(d);}}return Array.prototype.unshift.apply(this,b);}.protect(),concat:function(){var b=new Elements(this);
+for(var c=0,a=arguments.length;c<a;c++){var d=arguments[c];if(Type.isEnumerable(d)){b.append(d);}else{b.push(d);}}return b;}.protect(),append:function(c){for(var b=0,a=c.length;
+b<a;b++){this.push(c[b]);}return this;}.protect(),empty:function(){while(this.length){delete this[--this.length];}return this;}.protect()});(function(){var f=Array.prototype.splice,a={"0":0,"1":1,length:2};
+f.call(a,1,1);if(a[1]==1){Elements.implement("splice",function(){var g=this.length;var e=f.apply(this,arguments);while(g>=this.length){delete this[g--];
+}return e;}.protect());}Array.forEachMethod(function(g,e){Elements.implement(e,g);});Array.mirror(Elements);var d;try{d=(document.createElement("<input name=x>").name=="x");
+}catch(b){}var c=function(e){return(""+e).replace(/&/g,"&amp;").replace(/"/g,"&quot;");};Document.implement({newElement:function(e,g){if(g&&g.checked!=null){g.defaultChecked=g.checked;
+}if(d&&g){e="<"+e;if(g.name){e+=' name="'+c(g.name)+'"';}if(g.type){e+=' type="'+c(g.type)+'"';}e+=">";delete g.name;delete g.type;}return this.id(this.createElement(e)).set(g);
+}});})();(function(){Slick.uidOf(window);Slick.uidOf(document);Document.implement({newTextNode:function(e){return this.createTextNode(e);},getDocument:function(){return this;
+},getWindow:function(){return this.window;},id:(function(){var e={string:function(L,K,l){L=Slick.find(l,"#"+L.replace(/(\W)/g,"\\$1"));return(L)?e.element(L,K):null;
+},element:function(K,L){Slick.uidOf(K);if(!L&&!K.$family&&!(/^(?:object|embed)$/i).test(K.tagName)){var l=K.fireEvent;K._fireEvent=function(M,N){return l(M,N);
+};Object.append(K,Element.Prototype);}return K;},object:function(K,L,l){if(K.toElement){return e.element(K.toElement(l),L);}return null;}};e.textnode=e.whitespace=e.window=e.document=function(l){return l;
+};return function(K,M,L){if(K&&K.$family&&K.uniqueNumber){return K;}var l=typeOf(K);return(e[l])?e[l](K,M,L||document):null;};})()});if(window.$==null){Window.implement("$",function(e,l){return document.id(e,l,this.document);
+});}Window.implement({getDocument:function(){return this.document;},getWindow:function(){return this;}});[Document,Element].invoke("implement",{getElements:function(e){return Slick.search(this,e,new Elements);
+},getElement:function(e){return document.id(Slick.find(this,e));}});var p={contains:function(e){return Slick.contains(this,e);}};if(!document.contains){Document.implement(p);
+}if(!document.createElement("div").contains){Element.implement(p);}var v=function(L,K){if(!L){return K;}L=Object.clone(Slick.parse(L));var l=L.expressions;
+for(var e=l.length;e--;){l[e][0].combinator=K;}return L;};Object.forEach({getNext:"~",getPrevious:"!~",getParent:"!"},function(e,l){Element.implement(l,function(K){return this.getElement(v(K,e));
+});});Object.forEach({getAllNext:"~",getAllPrevious:"!~",getSiblings:"~~",getChildren:">",getParents:"!"},function(e,l){Element.implement(l,function(K){return this.getElements(v(K,e));
+});});Element.implement({getFirst:function(e){return document.id(Slick.search(this,v(e,">"))[0]);},getLast:function(e){return document.id(Slick.search(this,v(e,">")).getLast());
+},getWindow:function(){return this.ownerDocument.window;},getDocument:function(){return this.ownerDocument;},getElementById:function(e){return document.id(Slick.find(this,"#"+(""+e).replace(/(\W)/g,"\\$1")));
+},match:function(e){return !e||Slick.match(this,e);}});if(window.$$==null){Window.implement("$$",function(e){if(arguments.length==1){if(typeof e=="string"){return Slick.search(this.document,e,new Elements);
+}else{if(Type.isEnumerable(e)){return new Elements(e);}}}return new Elements(arguments);});}var A={before:function(l,e){var K=e.parentNode;if(K){K.insertBefore(l,e);
+}},after:function(l,e){var K=e.parentNode;if(K){K.insertBefore(l,e.nextSibling);}},bottom:function(l,e){e.appendChild(l);},top:function(l,e){e.insertBefore(l,e.firstChild);
+}};A.inside=A.bottom;var n={},d={};var o={};Array.forEach(["type","value","defaultValue","accessKey","cellPadding","cellSpacing","colSpan","frameBorder","rowSpan","tabIndex","useMap"],function(e){o[e.toLowerCase()]=e;
+});o.html="innerHTML";o.text=(document.createElement("div").textContent==null)?"innerText":"textContent";Object.forEach(o,function(l,e){d[e]=function(K,L){K[l]=L;
+};n[e]=function(K){return K[l];};});var B=["compact","nowrap","ismap","declare","noshade","checked","disabled","readOnly","multiple","selected","noresize","defer","defaultChecked","autofocus","controls","autoplay","loop"];
+var k={};Array.forEach(B,function(e){var l=e.toLowerCase();k[l]=e;d[l]=function(K,L){K[e]=!!L;};n[l]=function(K){return !!K[e];};});Object.append(d,{"class":function(e,l){("className" in e)?e.className=(l||""):e.setAttribute("class",l);
+},"for":function(e,l){("htmlFor" in e)?e.htmlFor=l:e.setAttribute("for",l);},style:function(e,l){(e.style)?e.style.cssText=l:e.setAttribute("style",l);
+},value:function(e,l){e.value=(l!=null)?l:"";}});n["class"]=function(e){return("className" in e)?e.className||null:e.getAttribute("class");};var f=document.createElement("button");
+try{f.type="button";}catch(E){}if(f.type!="button"){d.type=function(e,l){e.setAttribute("type",l);};}f=null;var s=document.createElement("input");s.value="t";
+s.type="submit";if(s.value!="t"){d.type=function(l,e){var K=l.value;l.type=e;l.value=K;};}s=null;var u=(function(e){e.random="attribute";return(e.getAttribute("random")=="attribute");
+})(document.createElement("div"));var i=(function(e){e.innerHTML='<object><param name="should_fix" value="the unknown"></object>';return e.cloneNode(true).firstChild.childNodes.length!=1;
+})(document.createElement("div"));var j=!!document.createElement("div").classList;var F=function(e){var l=(e||"").clean().split(" "),K={};return l.filter(function(L){if(L!==""&&!K[L]){return K[L]=L;
+}});};var t=function(e){this.classList.add(e);};var g=function(e){this.classList.remove(e);};Element.implement({setProperty:function(l,K){var L=d[l.toLowerCase()];
+if(L){L(this,K);}else{var e;if(u){e=this.retrieve("$attributeWhiteList",{});}if(K==null){this.removeAttribute(l);if(u){delete e[l];}}else{this.setAttribute(l,""+K);
+if(u){e[l]=true;}}}return this;},setProperties:function(e){for(var l in e){this.setProperty(l,e[l]);}return this;},getProperty:function(M){var K=n[M.toLowerCase()];
+if(K){return K(this);}if(u){var l=this.getAttributeNode(M),L=this.retrieve("$attributeWhiteList",{});if(!l){return null;}if(l.expando&&!L[M]){var N=this.outerHTML;
+if(N.substr(0,N.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(M)<0){return null;}L[M]=true;}}var e=Slick.getAttribute(this,M);return(!e&&!Slick.hasAttribute(this,M))?null:e;
+},getProperties:function(){var e=Array.from(arguments);return e.map(this.getProperty,this).associate(e);},removeProperty:function(e){return this.setProperty(e,null);
+},removeProperties:function(){Array.each(arguments,this.removeProperty,this);return this;},set:function(K,l){var e=Element.Properties[K];(e&&e.set)?e.set.call(this,l):this.setProperty(K,l);
+}.overloadSetter(),get:function(l){var e=Element.Properties[l];return(e&&e.get)?e.get.apply(this):this.getProperty(l);}.overloadGetter(),erase:function(l){var e=Element.Properties[l];
+(e&&e.erase)?e.erase.apply(this):this.removeProperty(l);return this;},hasClass:j?function(e){return this.classList.contains(e);}:function(e){return this.className.clean().contains(e," ");
+},addClass:j?function(e){F(e).forEach(t,this);return this;}:function(e){this.className=F(e+" "+this.className).join(" ");return this;},removeClass:j?function(e){F(e).forEach(g,this);
+return this;}:function(e){var l=F(this.className);F(e).forEach(l.erase,l);this.className=l.join(" ");return this;},toggleClass:function(e,l){if(l==null){l=!this.hasClass(e);
+}return(l)?this.addClass(e):this.removeClass(e);},adopt:function(){var L=this,e,N=Array.flatten(arguments),M=N.length;if(M>1){L=e=document.createDocumentFragment();
+}for(var K=0;K<M;K++){var l=document.id(N[K],true);if(l){L.appendChild(l);}}if(e){this.appendChild(e);}return this;},appendText:function(l,e){return this.grab(this.getDocument().newTextNode(l),e);
+},grab:function(l,e){A[e||"bottom"](document.id(l,true),this);return this;},inject:function(l,e){A[e||"bottom"](this,document.id(l,true));return this;},replaces:function(e){e=document.id(e,true);
+e.parentNode.replaceChild(this,e);return this;},wraps:function(l,e){l=document.id(l,true);return this.replaces(l).grab(l,e);},getSelected:function(){this.selectedIndex;
+return new Elements(Array.from(this.options).filter(function(e){return e.selected;}));},toQueryString:function(){var e=[];this.getElements("input, select, textarea").each(function(K){var l=K.type;
+if(!K.name||K.disabled||l=="submit"||l=="reset"||l=="file"||l=="image"){return;}var L=(K.get("tag")=="select")?K.getSelected().map(function(M){return document.id(M).get("value");
+}):((l=="radio"||l=="checkbox")&&!K.checked)?null:K.get("value");Array.from(L).each(function(M){if(typeof M!="undefined"){e.push(encodeURIComponent(K.name)+"="+encodeURIComponent(M));
+}});});return e.join("&");}});var I={before:"beforeBegin",after:"afterEnd",bottom:"beforeEnd",top:"afterBegin",inside:"beforeEnd"};Element.implement("appendHTML",("insertAdjacentHTML" in document.createElement("div"))?function(l,e){this.insertAdjacentHTML(I[e||"bottom"],l);
+return this;}:function(P,M){var K=new Element("div",{html:P}),O=K.childNodes,L=K.firstChild;if(!L){return this;}if(O.length>1){L=document.createDocumentFragment();
+for(var N=0,e=O.length;N<e;N++){L.appendChild(O[N]);}}A[M||"bottom"](L,this);return this;});var m={},D={};var G=function(e){return(D[e]||(D[e]={}));};var z=function(l){var e=l.uniqueNumber;
+if(l.removeEvents){l.removeEvents();}if(l.clearAttributes){l.clearAttributes();}if(e!=null){delete m[e];delete D[e];}return l;};var H={input:"checked",option:"selected",textarea:"value"};
+Element.implement({destroy:function(){var e=z(this).getElementsByTagName("*");Array.each(e,z);Element.dispose(this);return null;},empty:function(){Array.from(this.childNodes).each(Element.dispose);
+return this;},dispose:function(){return(this.parentNode)?this.parentNode.removeChild(this):this;},clone:function(N,L){N=N!==false;var S=this.cloneNode(N),K=[S],M=[this],Q;
+if(N){K.append(Array.from(S.getElementsByTagName("*")));M.append(Array.from(this.getElementsByTagName("*")));}for(Q=K.length;Q--;){var O=K[Q],R=M[Q];if(!L){O.removeAttribute("id");
+}if(O.clearAttributes){O.clearAttributes();O.mergeAttributes(R);O.removeAttribute("uniqueNumber");if(O.options){var V=O.options,e=R.options;for(var P=V.length;
+P--;){V[P].selected=e[P].selected;}}}var l=H[R.tagName.toLowerCase()];if(l&&R[l]){O[l]=R[l];}}if(i){var T=S.getElementsByTagName("object"),U=this.getElementsByTagName("object");
+for(Q=T.length;Q--;){T[Q].outerHTML=U[Q].outerHTML;}}return document.id(S);}});[Element,Window,Document].invoke("implement",{addListener:function(l,e){if(window.attachEvent&&!window.addEventListener){m[Slick.uidOf(this)]=this;
+}if(this.addEventListener){this.addEventListener(l,e,!!arguments[2]);}else{this.attachEvent("on"+l,e);}return this;},removeListener:function(l,e){if(this.removeEventListener){this.removeEventListener(l,e,!!arguments[2]);
+}else{this.detachEvent("on"+l,e);}return this;},retrieve:function(l,e){var L=G(Slick.uidOf(this)),K=L[l];if(e!=null&&K==null){K=L[l]=e;}return K!=null?K:null;
+},store:function(l,e){var K=G(Slick.uidOf(this));K[l]=e;return this;},eliminate:function(e){var l=G(Slick.uidOf(this));delete l[e];return this;}});if(window.attachEvent&&!window.addEventListener){var J=function(){Object.each(m,z);
+if(window.CollectGarbage){CollectGarbage();}window.removeListener("unload",J);};window.addListener("unload",J);}Element.Properties={};Element.Properties.style={set:function(e){this.style.cssText=e;
+},get:function(){return this.style.cssText;},erase:function(){this.style.cssText="";}};Element.Properties.tag={get:function(){return this.tagName.toLowerCase();
+}};Element.Properties.html={set:function(e){if(e==null){e="";}else{if(typeOf(e)=="array"){e=e.join("");}}this.innerHTML=e;},erase:function(){this.innerHTML="";
+}};var a=true,h=true,C=true;var x=document.createElement("div");x.innerHTML="<nav></nav>";a=(x.childNodes.length==1);if(!a){var w="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video".split(" "),b=document.createDocumentFragment(),y=w.length;
+while(y--){b.createElement(w[y]);}}x=null;h=Function.attempt(function(){var e=document.createElement("table");e.innerHTML="<tr><td></td></tr>";return true;
+});var c=document.createElement("tr"),r="<td></td>";c.innerHTML=r;C=(c.innerHTML==r);c=null;if(!h||!C||!a){Element.Properties.html.set=(function(l){var e={table:[1,"<table>","</table>"],select:[1,"<select>","</select>"],tbody:[2,"<table><tbody>","</tbody></table>"],tr:[3,"<table><tbody><tr>","</tr></tbody></table>"]};
+e.thead=e.tfoot=e.tbody;return function(K){var L=e[this.get("tag")];if(!L&&!a){L=[0,"",""];}if(!L){return l.call(this,K);}var O=L[0],N=document.createElement("div"),M=N;
+if(!a){b.appendChild(N);}N.innerHTML=[L[1],K,L[2]].flatten().join("");while(O--){M=M.firstChild;}this.empty().adopt(M.childNodes);if(!a){b.removeChild(N);
+}N=null;};})(Element.Properties.html.set);}var q=document.createElement("form");q.innerHTML="<select><option>s</option></select>";if(q.firstChild.value!="s"){Element.Properties.value={set:function(N){var l=this.get("tag");
+if(l!="select"){return this.setProperty("value",N);}var K=this.getElements("option");N=String(N);for(var L=0;L<K.length;L++){var M=K[L],e=M.getAttributeNode("value"),O=(e&&e.specified)?M.value:M.get("text");
+if(O===N){return M.selected=true;}}},get:function(){var K=this,l=K.get("tag");if(l!="select"&&l!="option"){return this.getProperty("value");}if(l=="select"&&!(K=K.getSelected()[0])){return"";
+}var e=K.getAttributeNode("value");return(e&&e.specified)?K.value:K.get("text");}};}q=null;if(document.createElement("div").getAttributeNode("id")){Element.Properties.id={set:function(e){this.id=this.getAttributeNode("id").value=e;
+},get:function(){return this.id||null;},erase:function(){this.id=this.getAttributeNode("id").value="";}};}})();(function(){var l=document.html,f;f=document.createElement("div");
+f.style.color="red";f.style.color=null;var e=f.style.color=="red";var k="1px solid #123abc";f.style.border=k;var o=f.style.border!=k;f=null;var n=!!window.getComputedStyle;
+Element.Properties.styles={set:function(r){this.setStyles(r);}};var j=(l.style.opacity!=null),g=(l.style.filter!=null),q=/alpha\(opacity=([\d.]+)\)/i;var b=function(s,r){s.store("$opacity",r);
+s.style.visibility=r>0||r==null?"visible":"hidden";};var p=function(r,v,u){var t=r.style,s=t.filter||r.getComputedStyle("filter")||"";t.filter=(v.test(s)?s.replace(v,u):s+" "+u).trim();
+if(!t.filter){t.removeAttribute("filter");}};var h=(j?function(s,r){s.style.opacity=r;}:(g?function(s,r){if(!s.currentStyle||!s.currentStyle.hasLayout){s.style.zoom=1;
+}if(r==null||r==1){p(s,q,"");if(r==1&&i(s)!=1){p(s,q,"alpha(opacity=100)");}}else{p(s,q,"alpha(opacity="+(r*100).limit(0,100).round()+")");}}:b));var i=(j?function(s){var r=s.style.opacity||s.getComputedStyle("opacity");
+return(r=="")?1:r.toFloat();}:(g?function(s){var t=(s.style.filter||s.getComputedStyle("filter")),r;if(t){r=t.match(q);}return(r==null||t==null)?1:(r[1]/100);
+}:function(s){var r=s.retrieve("$opacity");if(r==null){r=(s.style.visibility=="hidden"?0:1);}return r;}));var d=(l.style.cssFloat==null)?"styleFloat":"cssFloat",a={left:"0%",top:"0%",center:"50%",right:"100%",bottom:"100%"},c=(l.style.backgroundPositionX!=null);
+var m=function(r,s){if(s=="backgroundPosition"){r.removeAttribute(s+"X");s+="Y";}r.removeAttribute(s);};Element.implement({getComputedStyle:function(t){if(!n&&this.currentStyle){return this.currentStyle[t.camelCase()];
+}var s=Element.getDocument(this).defaultView,r=s?s.getComputedStyle(this,null):null;return(r)?r.getPropertyValue((t==d)?"float":t.hyphenate()):"";},setStyle:function(s,r){if(s=="opacity"){if(r!=null){r=parseFloat(r);
+}h(this,r);return this;}s=(s=="float"?d:s).camelCase();if(typeOf(r)!="string"){var t=(Element.Styles[s]||"@").split(" ");r=Array.from(r).map(function(v,u){if(!t[u]){return"";
+}return(typeOf(v)=="number")?t[u].replace("@",Math.round(v)):v;}).join(" ");}else{if(r==String(Number(r))){r=Math.round(r);}}this.style[s]=r;if((r==""||r==null)&&e&&this.style.removeAttribute){m(this.style,s);
+}return this;},getStyle:function(x){if(x=="opacity"){return i(this);}x=(x=="float"?d:x).camelCase();var r=this.style[x];if(!r||x=="zIndex"){if(Element.ShortStyles.hasOwnProperty(x)){r=[];
+for(var w in Element.ShortStyles[x]){r.push(this.getStyle(w));}return r.join(" ");}r=this.getComputedStyle(x);}if(c&&/^backgroundPosition[XY]?$/.test(x)){return r.replace(/(top|right|bottom|left)/g,function(s){return a[s];
+})||"0px";}if(!r&&x=="backgroundPosition"){return"0px 0px";}if(r){r=String(r);var u=r.match(/rgba?\([\d\s,]+\)/);if(u){r=r.replace(u[0],u[0].rgbToHex());
+}}if(!n&&!this.style[x]){if((/^(height|width)$/).test(x)&&!(/px$/.test(r))){var t=(x=="width")?["left","right"]:["top","bottom"],v=0;t.each(function(s){v+=this.getStyle("border-"+s+"-width").toInt()+this.getStyle("padding-"+s).toInt();
+},this);return this["offset"+x.capitalize()]-v+"px";}if((/^border(.+)Width|margin|padding/).test(x)&&isNaN(parseFloat(r))){return"0px";}}if(o&&/^border(Top|Right|Bottom|Left)?$/.test(x)&&/^#/.test(r)){return r.replace(/^(.+)\s(.+)\s(.+)$/,"$2 $3 $1");
+}return r;},setStyles:function(s){for(var r in s){this.setStyle(r,s[r]);}return this;},getStyles:function(){var r={};Array.flatten(arguments).each(function(s){r[s]=this.getStyle(s);
+},this);return r;}});Element.Styles={left:"@px",top:"@px",bottom:"@px",right:"@px",width:"@px",height:"@px",maxWidth:"@px",maxHeight:"@px",minWidth:"@px",minHeight:"@px",backgroundColor:"rgb(@, @, @)",backgroundSize:"@px",backgroundPosition:"@px @px",color:"rgb(@, @, @)",fontSize:"@px",letterSpacing:"@px",lineHeight:"@px",clip:"rect(@px @px @px @px)",margin:"@px @px @px @px",padding:"@px @px @px @px",border:"@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)",borderWidth:"@px @px @px @px",borderStyle:"@ @ @ @",borderColor:"rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)",zIndex:"@",zoom:"@",fontWeight:"@",textIndent:"@px",opacity:"@"};
+Element.ShortStyles={margin:{},padding:{},border:{},borderWidth:{},borderStyle:{},borderColor:{}};["Top","Right","Bottom","Left"].each(function(x){var w=Element.ShortStyles;
+var s=Element.Styles;["margin","padding"].each(function(y){var z=y+x;w[y][z]=s[z]="@px";});var v="border"+x;w.border[v]=s[v]="@px @ rgb(@, @, @)";var u=v+"Width",r=v+"Style",t=v+"Color";
+w[v]={};w.borderWidth[u]=w[v][u]=s[u]="@px";w.borderStyle[r]=w[v][r]=s[r]="@";w.borderColor[t]=w[v][t]=s[t]="rgb(@, @, @)";});if(c){Element.ShortStyles.backgroundPosition={backgroundPositionX:"@",backgroundPositionY:"@"};
+}})();(function(){Element.Properties.events={set:function(b){this.addEvents(b);}};[Element,Window,Document].invoke("implement",{addEvent:function(f,h){var i=this.retrieve("events",{});
+if(!i[f]){i[f]={keys:[],values:[]};}if(i[f].keys.contains(h)){return this;}i[f].keys.push(h);var g=f,b=Element.Events[f],d=h,j=this;if(b){if(b.onAdd){b.onAdd.call(this,h,f);
+}if(b.condition){d=function(k){if(b.condition.call(this,k,f)){return h.call(this,k);}return true;};}if(b.base){g=Function.from(b.base).call(this,f);}}var e=function(){return h.call(j);
+};var c=Element.NativeEvents[g];if(c){if(c==2){e=function(k){k=new DOMEvent(k,j.getWindow());if(d.call(j,k)===false){k.stop();}};}this.addListener(g,e,arguments[2]);
+}i[f].values.push(e);return this;},removeEvent:function(e,d){var c=this.retrieve("events");if(!c||!c[e]){return this;}var h=c[e];var b=h.keys.indexOf(d);
+if(b==-1){return this;}var g=h.values[b];delete h.keys[b];delete h.values[b];var f=Element.Events[e];if(f){if(f.onRemove){f.onRemove.call(this,d,e);}if(f.base){e=Function.from(f.base).call(this,e);
+}}return(Element.NativeEvents[e])?this.removeListener(e,g,arguments[2]):this;},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this;
+},removeEvents:function(b){var d;if(typeOf(b)=="object"){for(d in b){this.removeEvent(d,b[d]);}return this;}var c=this.retrieve("events");if(!c){return this;
+}if(!b){for(d in c){this.removeEvents(d);}this.eliminate("events");}else{if(c[b]){c[b].keys.each(function(e){this.removeEvent(b,e);},this);delete c[b];
+}}return this;},fireEvent:function(e,c,b){var d=this.retrieve("events");if(!d||!d[e]){return this;}c=Array.from(c);d[e].keys.each(function(f){if(b){f.delay(b,this,c);
+}else{f.apply(this,c);}},this);return this;},cloneEvents:function(e,d){e=document.id(e);var c=e.retrieve("events");if(!c){return this;}if(!d){for(var b in c){this.cloneEvents(e,b);
+}}else{if(c[d]){c[d].keys.each(function(f){this.addEvent(d,f);},this);}}return this;}});Element.NativeEvents={click:2,dblclick:2,mouseup:2,mousedown:2,contextmenu:2,mousewheel:2,DOMMouseScroll:2,mouseover:2,mouseout:2,mousemove:2,selectstart:2,selectend:2,keydown:2,keypress:2,keyup:2,orientationchange:2,touchstart:2,touchmove:2,touchend:2,touchcancel:2,gesturestart:2,gesturechange:2,gestureend:2,focus:2,blur:2,change:2,reset:2,select:2,submit:2,paste:2,input:2,load:2,unload:1,beforeunload:2,resize:1,move:1,DOMContentLoaded:1,readystatechange:1,hashchange:1,popstate:2,error:1,abort:1,scroll:1};
+Element.Events={mousewheel:{base:"onwheel" in document?"wheel":"onmousewheel" in document?"mousewheel":"DOMMouseScroll"}};var a=function(b){var c=b.relatedTarget;
+if(c==null){return true;}if(!c){return false;}return(c!=this&&c.prefix!="xul"&&typeOf(this)!="document"&&!this.contains(c));};if("onmouseenter" in document.documentElement){Element.NativeEvents.mouseenter=Element.NativeEvents.mouseleave=2;
+Element.MouseenterCheck=a;}else{Element.Events.mouseenter={base:"mouseover",condition:a};Element.Events.mouseleave={base:"mouseout",condition:a};}if(!window.addEventListener){Element.NativeEvents.propertychange=2;
+Element.Events.change={base:function(){var b=this.type;return(this.get("tag")=="input"&&(b=="radio"||b=="checkbox"))?"propertychange":"change";},condition:function(b){return b.type!="propertychange"||b.event.propertyName=="checked";
+}};}})();(function(){var c=!!window.addEventListener;Element.NativeEvents.focusin=Element.NativeEvents.focusout=2;var k=function(l,m,n,o,p){while(p&&p!=l){if(m(p,o)){return n.call(p,o,p);
+}p=document.id(p.parentNode);}};var a={mouseenter:{base:"mouseover",condition:Element.MouseenterCheck},mouseleave:{base:"mouseout",condition:Element.MouseenterCheck},focus:{base:"focus"+(c?"":"in"),capture:true},blur:{base:c?"blur":"focusout",capture:true}};
+var b="$delegation:";var i=function(l){return{base:"focusin",remove:function(m,o){var p=m.retrieve(b+l+"listeners",{})[o];if(p&&p.forms){for(var n=p.forms.length;
+n--;){p.forms[n].removeEvent(l,p.fns[n]);}}},listen:function(x,r,v,n,t,s){var o=(t.get("tag")=="form")?t:n.target.getParent("form");if(!o){return;}var u=x.retrieve(b+l+"listeners",{}),p=u[s]||{forms:[],fns:[]},m=p.forms,w=p.fns;
+if(m.indexOf(o)!=-1){return;}m.push(o);var q=function(y){k(x,r,v,y,t);};o.addEvent(l,q);w.push(q);u[s]=p;x.store(b+l+"listeners",u);}};};var d=function(l){return{base:"focusin",listen:function(m,n,p,q,r){var o={blur:function(){this.removeEvents(o);
+}};o[l]=function(s){k(m,n,p,s,r);};q.target.addEvents(o);}};};if(!c){Object.append(a,{submit:i("submit"),reset:i("reset"),change:d("change"),select:d("select")});
+}var h=Element.prototype,f=h.addEvent,j=h.removeEvent;var e=function(l,m){return function(r,q,n){if(r.indexOf(":relay")==-1){return l.call(this,r,q,n);
+}var o=Slick.parse(r).expressions[0][0];if(o.pseudos[0].key!="relay"){return l.call(this,r,q,n);}var p=o.tag;o.pseudos.slice(1).each(function(s){p+=":"+s.key+(s.value?"("+s.value+")":"");
+});l.call(this,r,q);return m.call(this,p,o.pseudos[0].value,q);};};var g={addEvent:function(v,q,x){var t=this.retrieve("$delegates",{}),r=t[v];if(r){for(var y in r){if(r[y].fn==x&&r[y].match==q){return this;
+}}}var p=v,u=q,o=x,n=a[v]||{};v=n.base||p;q=function(B){return Slick.match(B,u);};var w=Element.Events[p];if(n.condition||w&&w.condition){var l=q,m=n.condition||w.condition;
+q=function(C,B){return l(C,B)&&m.call(C,B,v);};}var z=this,s=String.uniqueID();var A=n.listen?function(B,C){if(!C&&B&&B.target){C=B.target;}if(C){n.listen(z,q,x,B,C,s);
+}}:function(B,C){if(!C&&B&&B.target){C=B.target;}if(C){k(z,q,x,B,C);}};if(!r){r={};}r[s]={match:u,fn:o,delegator:A};t[p]=r;return f.call(this,v,A,n.capture);
+},removeEvent:function(r,n,t,u){var q=this.retrieve("$delegates",{}),p=q[r];if(!p){return this;}if(u){var m=r,w=p[u].delegator,l=a[r]||{};r=l.base||m;if(l.remove){l.remove(this,u);
+}delete p[u];q[m]=p;return j.call(this,r,w,l.capture);}var o,v;if(t){for(o in p){v=p[o];if(v.match==n&&v.fn==t){return g.removeEvent.call(this,r,n,t,o);
+}}}else{for(o in p){v=p[o];if(v.match==n){g.removeEvent.call(this,r,n,v.fn,o);}}}return this;}};[Element,Window,Document].invoke("implement",{addEvent:e(f,g.addEvent),removeEvent:e(j,g.removeEvent)});
+})();(function(){var h=document.createElement("div"),e=document.createElement("div");h.style.height="0";h.appendChild(e);var d=(e.offsetParent===h);h=e=null;
+var l=function(m){return k(m,"position")!="static"||a(m);};var i=function(m){return l(m)||(/^(?:table|td|th)$/i).test(m.tagName);};Element.implement({scrollTo:function(m,n){if(a(this)){this.getWindow().scrollTo(m,n);
+}else{this.scrollLeft=m;this.scrollTop=n;}return this;},getSize:function(){if(a(this)){return this.getWindow().getSize();}return{x:this.offsetWidth,y:this.offsetHeight};
+},getScrollSize:function(){if(a(this)){return this.getWindow().getScrollSize();}return{x:this.scrollWidth,y:this.scrollHeight};},getScroll:function(){if(a(this)){return this.getWindow().getScroll();
+}return{x:this.scrollLeft,y:this.scrollTop};},getScrolls:function(){var n=this.parentNode,m={x:0,y:0};while(n&&!a(n)){m.x+=n.scrollLeft;m.y+=n.scrollTop;
+n=n.parentNode;}return m;},getOffsetParent:d?function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;}var n=(k(m,"position")=="static")?i:l;
+while((m=m.parentNode)){if(n(m)){return m;}}return null;}:function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;}try{return m.offsetParent;
+}catch(n){}return null;},getOffsets:function(){var n=this.getBoundingClientRect;if(n){var r=this.getBoundingClientRect(),p=document.id(this.getDocument().documentElement),q=p.getScroll(),t=this.getScrolls(),s=(k(this,"position")=="fixed");
+return{x:r.left.toInt()+t.x+((s)?0:q.x)-p.clientLeft,y:r.top.toInt()+t.y+((s)?0:q.y)-p.clientTop};}var o=this,m={x:0,y:0};if(a(this)){return m;}while(o&&!a(o)){m.x+=o.offsetLeft;
+m.y+=o.offsetTop;o=o.offsetParent;}return m;},getPosition:function(p){var q=this.getOffsets(),n=this.getScrolls();var m={x:q.x-n.x,y:q.y-n.y};if(p&&(p=document.id(p))){var o=p.getPosition();
+return{x:m.x-o.x-b(p),y:m.y-o.y-g(p)};}return m;},getCoordinates:function(o){if(a(this)){return this.getWindow().getCoordinates();}var m=this.getPosition(o),n=this.getSize();
+var p={left:m.x,top:m.y,width:n.x,height:n.y};p.right=p.left+p.width;p.bottom=p.top+p.height;return p;},computePosition:function(m){return{left:m.x-j(this,"margin-left"),top:m.y-j(this,"margin-top")};
+},setPosition:function(m){return this.setStyles(this.computePosition(m));}});[Document,Window].invoke("implement",{getSize:function(){var m=f(this);return{x:m.clientWidth,y:m.clientHeight};
+},getScroll:function(){var n=this.getWindow(),m=f(this);return{x:n.pageXOffset||m.scrollLeft,y:n.pageYOffset||m.scrollTop};},getScrollSize:function(){var o=f(this),n=this.getSize(),m=this.getDocument().body;
+return{x:Math.max(o.scrollWidth,m.scrollWidth,n.x),y:Math.max(o.scrollHeight,m.scrollHeight,n.y)};},getPosition:function(){return{x:0,y:0};},getCoordinates:function(){var m=this.getSize();
+return{top:0,left:0,bottom:m.y,right:m.x,height:m.y,width:m.x};}});var k=Element.getComputedStyle;function j(m,n){return k(m,n).toInt()||0;}function c(m){return k(m,"-moz-box-sizing")=="border-box";
+}function g(m){return j(m,"border-top-width");}function b(m){return j(m,"border-left-width");}function a(m){return(/^(?:body|html)$/i).test(m.tagName);
+}function f(m){var n=m.getDocument();return(!n.compatMode||n.compatMode=="CSS1Compat")?n.html:n.body;}})();Element.alias({position:"setPosition"});[Window,Document,Element].invoke("implement",{getHeight:function(){return this.getSize().y;
+},getWidth:function(){return this.getSize().x;},getScrollTop:function(){return this.getScroll().y;},getScrollLeft:function(){return this.getScroll().x;
+},getScrollHeight:function(){return this.getScrollSize().y;},getScrollWidth:function(){return this.getScrollSize().x;},getTop:function(){return this.getPosition().y;
+},getLeft:function(){return this.getPosition().x;}});(function(){var f=this.Fx=new Class({Implements:[Chain,Events,Options],options:{fps:60,unit:false,duration:500,frames:null,frameSkip:true,link:"ignore"},initialize:function(g){this.subject=this.subject||this;
+this.setOptions(g);},getTransition:function(){return function(g){return -(Math.cos(Math.PI*g)-1)/2;};},step:function(g){if(this.options.frameSkip){var h=(this.time!=null)?(g-this.time):0,i=h/this.frameInterval;
+this.time=g;this.frame+=i;}else{this.frame++;}if(this.frame<this.frames){var j=this.transition(this.frame/this.frames);this.set(this.compute(this.from,this.to,j));
+}else{this.frame=this.frames;this.set(this.compute(this.from,this.to,1));this.stop();}},set:function(g){return g;},compute:function(i,h,g){return f.compute(i,h,g);
+},check:function(){if(!this.isRunning()){return true;}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.pass(arguments,this));
+return false;}return false;},start:function(k,j){if(!this.check(k,j)){return this;}this.from=k;this.to=j;this.frame=(this.options.frameSkip)?0:-1;this.time=null;
+this.transition=this.getTransition();var i=this.options.frames,h=this.options.fps,g=this.options.duration;this.duration=f.Durations[g]||g.toInt();this.frameInterval=1000/h;
+this.frames=i||Math.round(this.duration/this.frameInterval);this.fireEvent("start",this.subject);b.call(this,h);return this;},stop:function(){if(this.isRunning()){this.time=null;
+d.call(this,this.options.fps);if(this.frames==this.frame){this.fireEvent("complete",this.subject);if(!this.callChain()){this.fireEvent("chainComplete",this.subject);
+}}else{this.fireEvent("stop",this.subject);}}return this;},cancel:function(){if(this.isRunning()){this.time=null;d.call(this,this.options.fps);this.frame=this.frames;
+this.fireEvent("cancel",this.subject).clearChain();}return this;},pause:function(){if(this.isRunning()){this.time=null;d.call(this,this.options.fps);}return this;
+},resume:function(){if(this.isPaused()){b.call(this,this.options.fps);}return this;},isRunning:function(){var g=e[this.options.fps];return g&&g.contains(this);
+},isPaused:function(){return(this.frame<this.frames)&&!this.isRunning();}});f.compute=function(i,h,g){return(h-i)*g+i;};f.Durations={"short":250,normal:500,"long":1000};
+var e={},c={};var a=function(){var h=Date.now();for(var j=this.length;j--;){var g=this[j];if(g){g.step(h);}}};var b=function(h){var g=e[h]||(e[h]=[]);g.push(this);
+if(!c[h]){c[h]=a.periodical(Math.round(1000/h),g);}};var d=function(h){var g=e[h];if(g){g.erase(this);if(!g.length&&c[h]){delete e[h];c[h]=clearInterval(c[h]);
+}}};})();Fx.CSS=new Class({Extends:Fx,prepare:function(b,e,a){a=Array.from(a);var h=a[0],g=a[1];if(g==null){g=h;h=b.getStyle(e);var c=this.options.unit;
+if(c&&h&&typeof h=="string"&&h.slice(-c.length)!=c&&parseFloat(h)!=0){b.setStyle(e,g+c);var d=b.getComputedStyle(e);if(!(/px$/.test(d))){d=b.style[("pixel-"+e).camelCase()];
+if(d==null){var f=b.style.left;b.style.left=g+c;d=b.style.pixelLeft;b.style.left=f;}}h=(g||1)/(parseFloat(d)||1)*(parseFloat(h)||0);b.setStyle(e,h+c);}}return{from:this.parse(h),to:this.parse(g)};
+},parse:function(a){a=Function.from(a)();a=(typeof a=="string")?a.split(" "):Array.from(a);return a.map(function(c){c=String(c);var b=false;Object.each(Fx.CSS.Parsers,function(f,e){if(b){return;
+}var d=f.parse(c);if(d||d===0){b={value:d,parser:f};}});b=b||{value:c,parser:Fx.CSS.Parsers.String};return b;});},compute:function(d,c,b){var a=[];(Math.min(d.length,c.length)).times(function(e){a.push({value:d[e].parser.compute(d[e].value,c[e].value,b),parser:d[e].parser});
+});a.$family=Function.from("fx:css:value");return a;},serve:function(c,b){if(typeOf(c)!="fx:css:value"){c=this.parse(c);}var a=[];c.each(function(d){a=a.concat(d.parser.serve(d.value,b));
+});return a;},render:function(a,d,c,b){a.setStyle(d,this.serve(c,b));},search:function(a){if(Fx.CSS.Cache[a]){return Fx.CSS.Cache[a];}var d={},c=new RegExp("^"+a.escapeRegExp()+"$");
+var b=function(e){Array.each(e,function(h,f){if(h.media){b(h.rules||h.cssRules);return;}if(!h.style){return;}var g=(h.selectorText)?h.selectorText.replace(/^\w+/,function(i){return i.toLowerCase();
+}):null;if(!g||!c.test(g)){return;}Object.each(Element.Styles,function(j,i){if(!h.style[i]||Element.ShortStyles[i]){return;}j=String(h.style[i]);d[i]=((/^rgb/).test(j))?j.rgbToHex():j;
+});});};Array.each(document.styleSheets,function(g,f){var e=g.href;if(e&&e.indexOf("://")>-1&&e.indexOf(document.domain)==-1){return;}var h=g.rules||g.cssRules;
+b(h);});return Fx.CSS.Cache[a]=d;}});Fx.CSS.Cache={};Fx.CSS.Parsers={Color:{parse:function(a){if(a.match(/^#[0-9a-f]{3,6}$/i)){return a.hexToRgb(true);
+}return((a=a.match(/(\d+),\s*(\d+),\s*(\d+)/)))?[a[1],a[2],a[3]]:false;},compute:function(c,b,a){return c.map(function(e,d){return Math.round(Fx.compute(c[d],b[d],a));
+});},serve:function(a){return a.map(Number);}},Number:{parse:parseFloat,compute:Fx.compute,serve:function(b,a){return(a)?b+a:b;}},String:{parse:Function.from(false),compute:function(b,a){return a;
+},serve:function(a){return a;}}};Fx.Tween=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=document.id(b);this.parent(a);},set:function(b,a){if(arguments.length==1){a=b;
+b=this.property||this.options.property;}this.render(this.element,b,a,this.options.unit);return this;},start:function(c,e,d){if(!this.check(c,e,d)){return this;
+}var b=Array.flatten(arguments);this.property=this.options.property||b.shift();var a=this.prepare(this.element,this.property,b);return this.parent(a.from,a.to);
+}});Element.Properties.tween={set:function(a){this.get("tween").cancel().setOptions(a);return this;},get:function(){var a=this.retrieve("tween");if(!a){a=new Fx.Tween(this,{link:"cancel"});
+this.store("tween",a);}return a;}};Element.implement({tween:function(a,c,b){this.get("tween").start(a,c,b);return this;},fade:function(d){var e=this.get("tween"),g,c=["opacity"].append(arguments),a;
+if(c[1]==null){c[1]="toggle";}switch(c[1]){case"in":g="start";c[1]=1;break;case"out":g="start";c[1]=0;break;case"show":g="set";c[1]=1;break;case"hide":g="set";
+c[1]=0;break;case"toggle":var b=this.retrieve("fade:flag",this.getStyle("opacity")==1);g="start";c[1]=b?0:1;this.store("fade:flag",!b);a=true;break;default:g="start";
+}if(!a){this.eliminate("fade:flag");}e[g].apply(e,c);var f=c[c.length-1];if(g=="set"||f!=0){this.setStyle("visibility",f==0?"hidden":"visible");}else{e.chain(function(){this.element.setStyle("visibility","hidden");
+this.callChain();});}return this;},highlight:function(c,a){if(!a){a=this.retrieve("highlight:original",this.getStyle("background-color"));a=(a=="transparent")?"#fff":a;
+}var b=this.get("tween");b.start("background-color",c||"#ffff88",a).chain(function(){this.setStyle("background-color",this.retrieve("highlight:original"));
+b.callChain();}.bind(this));return this;}});Fx.Morph=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=document.id(b);this.parent(a);
+},set:function(a){if(typeof a=="string"){a=this.search(a);}for(var b in a){this.render(this.element,b,a[b],this.options.unit);}return this;},compute:function(e,d,c){var a={};
+for(var b in e){a[b]=this.parent(e[b],d[b],c);}return a;},start:function(b){if(!this.check(b)){return this;}if(typeof b=="string"){b=this.search(b);}var e={},d={};
+for(var c in b){var a=this.prepare(this.element,c,b[c]);e[c]=a.from;d[c]=a.to;}return this.parent(e,d);}});Element.Properties.morph={set:function(a){this.get("morph").cancel().setOptions(a);
+return this;},get:function(){var a=this.retrieve("morph");if(!a){a=new Fx.Morph(this,{link:"cancel"});this.store("morph",a);}return a;}};Element.implement({morph:function(a){this.get("morph").start(a);
+return this;}});Fx.implement({getTransition:function(){var a=this.options.transition||Fx.Transitions.Sine.easeInOut;if(typeof a=="string"){var b=a.split(":");
+a=Fx.Transitions;a=a[b[0]]||a[b[0].capitalize()];if(b[1]){a=a["ease"+b[1].capitalize()+(b[2]?b[2].capitalize():"")];}}return a;}});Fx.Transition=function(c,b){b=Array.from(b);
+var a=function(d){return c(d,b);};return Object.append(a,{easeIn:a,easeOut:function(d){return 1-c(1-d,b);},easeInOut:function(d){return(d<=0.5?c(2*d,b):(2-c(2*(1-d),b)))/2;
+}});};Fx.Transitions={linear:function(a){return a;}};Fx.Transitions.extend=function(a){for(var b in a){Fx.Transitions[b]=new Fx.Transition(a[b]);}};Fx.Transitions.extend({Pow:function(b,a){return Math.pow(b,a&&a[0]||6);
+},Expo:function(a){return Math.pow(2,8*(a-1));},Circ:function(a){return 1-Math.sin(Math.acos(a));},Sine:function(a){return 1-Math.cos(a*Math.PI/2);},Back:function(b,a){a=a&&a[0]||1.618;
+return Math.pow(b,2)*((a+1)*b-a);},Bounce:function(f){var e;for(var d=0,c=1;1;d+=c,c/=2){if(f>=(7-4*d)/11){e=c*c-Math.pow((11-6*d-11*f)/4,2);break;}}return e;
+},Elastic:function(b,a){return Math.pow(2,10*--b)*Math.cos(20*b*Math.PI*(a&&a[0]||1)/3);}});["Quad","Cubic","Quart","Quint"].each(function(b,a){Fx.Transitions[b]=new Fx.Transition(function(c){return Math.pow(c,a+2);
+});});(function(){var d=function(){},a=("onprogress" in new Browser.Request);var c=this.Request=new Class({Implements:[Chain,Events,Options],options:{url:"",data:"",headers:{"X-Requested-With":"XMLHttpRequest",Accept:"text/javascript, text/html, application/xml, text/xml, */*"},async:true,format:false,method:"post",link:"ignore",isSuccess:null,emulation:true,urlEncoded:true,encoding:"utf-8",evalScripts:false,evalResponse:false,timeout:0,noCache:false},initialize:function(e){this.xhr=new Browser.Request();
+this.setOptions(e);this.headers=this.options.headers;},onStateChange:function(){var e=this.xhr;if(e.readyState!=4||!this.running){return;}this.running=false;
+this.status=0;Function.attempt(function(){var f=e.status;this.status=(f==1223)?204:f;}.bind(this));e.onreadystatechange=d;if(a){e.onprogress=e.onloadstart=d;
+}clearTimeout(this.timer);this.response={text:this.xhr.responseText||"",xml:this.xhr.responseXML};if(this.options.isSuccess.call(this,this.status)){this.success(this.response.text,this.response.xml);
+}else{this.failure();}},isSuccess:function(){var e=this.status;return(e>=200&&e<300);},isRunning:function(){return !!this.running;},processScripts:function(e){if(this.options.evalResponse||(/(ecma|java)script/).test(this.getHeader("Content-type"))){return Browser.exec(e);
+}return e.stripScripts(this.options.evalScripts);},success:function(f,e){this.onSuccess(this.processScripts(f),e);},onSuccess:function(){this.fireEvent("complete",arguments).fireEvent("success",arguments).callChain();
+},failure:function(){this.onFailure();},onFailure:function(){this.fireEvent("complete").fireEvent("failure",this.xhr);},loadstart:function(e){this.fireEvent("loadstart",[e,this.xhr]);
+},progress:function(e){this.fireEvent("progress",[e,this.xhr]);},timeout:function(){this.fireEvent("timeout",this.xhr);},setHeader:function(e,f){this.headers[e]=f;
+return this;},getHeader:function(e){return Function.attempt(function(){return this.xhr.getResponseHeader(e);}.bind(this));},check:function(){if(!this.running){return true;
+}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.pass(arguments,this));return false;}return false;},send:function(o){if(!this.check(o)){return this;
+}this.options.isSuccess=this.options.isSuccess||this.isSuccess;this.running=true;var l=typeOf(o);if(l=="string"||l=="element"){o={data:o};}var h=this.options;
+o=Object.append({data:h.data,url:h.url,method:h.method},o);var j=o.data,f=String(o.url),e=o.method.toLowerCase();switch(typeOf(j)){case"element":j=document.id(j).toQueryString();
+break;case"object":case"hash":j=Object.toQueryString(j);}if(this.options.format){var m="format="+this.options.format;j=(j)?m+"&"+j:m;}if(this.options.emulation&&!["get","post"].contains(e)){var k="_method="+e;
+j=(j)?k+"&"+j:k;e="post";}if(this.options.urlEncoded&&["post","put"].contains(e)){var g=(this.options.encoding)?"; charset="+this.options.encoding:"";this.headers["Content-type"]="application/x-www-form-urlencoded"+g;
+}if(!f){f=document.location.pathname;}var i=f.lastIndexOf("/");if(i>-1&&(i=f.indexOf("#"))>-1){f=f.substr(0,i);}if(this.options.noCache){f+=(f.indexOf("?")>-1?"&":"?")+String.uniqueID();
+}if(j&&(e=="get"||e=="delete")){f+=(f.indexOf("?")>-1?"&":"?")+j;j=null;}var n=this.xhr;if(a){n.onloadstart=this.loadstart.bind(this);n.onprogress=this.progress.bind(this);
+}n.open(e.toUpperCase(),f,this.options.async,this.options.user,this.options.password);if(this.options.user&&"withCredentials" in n){n.withCredentials=true;
+}n.onreadystatechange=this.onStateChange.bind(this);Object.each(this.headers,function(q,p){try{n.setRequestHeader(p,q);}catch(r){this.fireEvent("exception",[p,q]);
+}},this);this.fireEvent("request");n.send(j);if(!this.options.async){this.onStateChange();}else{if(this.options.timeout){this.timer=this.timeout.delay(this.options.timeout,this);
+}}return this;},cancel:function(){if(!this.running){return this;}this.running=false;var e=this.xhr;e.abort();clearTimeout(this.timer);e.onreadystatechange=d;
+if(a){e.onprogress=e.onloadstart=d;}this.xhr=new Browser.Request();this.fireEvent("cancel");return this;}});var b={};["get","post","put","delete","GET","POST","PUT","DELETE"].each(function(e){b[e]=function(g){var f={method:e};
+if(g!=null){f.data=g;}return this.send(f);};});c.implement(b);Element.Properties.send={set:function(e){var f=this.get("send").cancel();f.setOptions(e);
+return this;},get:function(){var e=this.retrieve("send");if(!e){e=new c({data:this,link:"cancel",method:this.get("method")||"post",url:this.get("action")});
+this.store("send",e);}return e;}};Element.implement({send:function(e){var f=this.get("send");f.send({data:this,url:e||f.options.url});return this;}});})();
+Request.HTML=new Class({Extends:Request,options:{update:false,append:false,evalScripts:true,filter:false,headers:{Accept:"text/html, application/xml, text/xml, */*"}},success:function(f){var e=this.options,c=this.response;
+c.html=f.stripScripts(function(h){c.javascript=h;});var d=c.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);if(d){c.html=d[1];}var b=new Element("div").set("html",c.html);
+c.tree=b.childNodes;c.elements=b.getElements(e.filter||"*");if(e.filter){c.tree=c.elements;}if(e.update){var g=document.id(e.update).empty();if(e.filter){g.adopt(c.elements);
+}else{g.set("html",c.html);}}else{if(e.append){var a=document.id(e.append);if(e.filter){c.elements.reverse().inject(a);}else{a.adopt(b.getChildren());}}}if(e.evalScripts){Browser.exec(c.javascript);
+}this.onSuccess(c.tree,c.elements,c.html,c.javascript);}});Element.Properties.load={set:function(a){var b=this.get("load").cancel();b.setOptions(a);return this;
+},get:function(){var a=this.retrieve("load");if(!a){a=new Request.HTML({data:this,link:"cancel",update:this,method:"get"});this.store("load",a);}return a;
+}};Element.implement({load:function(){this.get("load").send(Array.link(arguments,{data:Type.isObject,url:Type.isString}));return this;}});if(typeof JSON=="undefined"){this.JSON={};
+}(function(){var special={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};var escape=function(chr){return special[chr]||"\\u"+("0000"+chr.charCodeAt(0).toString(16)).slice(-4);
+};JSON.validate=function(string){string=string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"");
+return(/^[\],:{}\s]*$/).test(string);};JSON.encode=JSON.stringify?function(obj){return JSON.stringify(obj);}:function(obj){if(obj&&obj.toJSON){obj=obj.toJSON();
+}switch(typeOf(obj)){case"string":return'"'+obj.replace(/[\x00-\x1f\\"]/g,escape)+'"';case"array":return"["+obj.map(JSON.encode).clean()+"]";case"object":case"hash":var string=[];
+Object.each(obj,function(value,key){var json=JSON.encode(value);if(json){string.push(JSON.encode(key)+":"+json);}});return"{"+string+"}";case"number":case"boolean":return""+obj;
+case"null":return"null";}return null;};JSON.secure=true;JSON.decode=function(string,secure){if(!string||typeOf(string)!="string"){return null;}if(secure==null){secure=JSON.secure;
+}if(secure){if(JSON.parse){return JSON.parse(string);}if(!JSON.validate(string)){throw new Error("JSON could not decode the input; security is enabled and the value is not secure.");
+}}return eval("("+string+")");};})();Request.JSON=new Class({Extends:Request,options:{secure:true},initialize:function(a){this.parent(a);Object.append(this.headers,{Accept:"application/json","X-Request":"JSON"});
+},success:function(c){var b;try{b=this.response.json=JSON.decode(c,this.options.secure);}catch(a){this.fireEvent("error",[c,a]);return;}if(b==null){this.onFailure();
+}else{this.onSuccess(b,c);}}});var Cookie=new Class({Implements:Options,options:{path:"/",domain:false,duration:false,secure:false,document:document,encode:true},initialize:function(b,a){this.key=b;
+this.setOptions(a);},write:function(b){if(this.options.encode){b=encodeURIComponent(b);}if(this.options.domain){b+="; domain="+this.options.domain;}if(this.options.path){b+="; path="+this.options.path;
+}if(this.options.duration){var a=new Date();a.setTime(a.getTime()+this.options.duration*24*60*60*1000);b+="; expires="+a.toGMTString();}if(this.options.secure){b+="; secure";
+}this.options.document.cookie=this.key+"="+b;return this;},read:function(){var a=this.options.document.cookie.match("(?:^|;)\\s*"+this.key.escapeRegExp()+"=([^;]*)");
+return(a)?decodeURIComponent(a[1]):null;},dispose:function(){new Cookie(this.key,Object.merge({},this.options,{duration:-1})).write("");return this;}});
+Cookie.write=function(b,c,a){return new Cookie(b,a).write(c);};Cookie.read=function(a){return new Cookie(a).read();};Cookie.dispose=function(b,a){return new Cookie(b,a).dispose();
+};(function(i,k){var l,f,e=[],c,b,d=k.createElement("div");var g=function(){clearTimeout(b);if(l){return;}Browser.loaded=l=true;k.removeListener("DOMContentLoaded",g).removeListener("readystatechange",a);
+k.fireEvent("domready");i.fireEvent("domready");};var a=function(){for(var m=e.length;m--;){if(e[m]()){g();return true;}}return false;};var j=function(){clearTimeout(b);
+if(!a()){b=setTimeout(j,10);}};k.addListener("DOMContentLoaded",g);var h=function(){try{d.doScroll();return true;}catch(m){}return false;};if(d.doScroll&&!h()){e.push(h);
+c=true;}if(k.readyState){e.push(function(){var m=k.readyState;return(m=="loaded"||m=="complete");});}if("onreadystatechange" in k){k.addListener("readystatechange",a);
+}else{c=true;}if(c){j();}Element.Events.domready={onAdd:function(m){if(l){m.call(this);}}};Element.Events.load={base:"load",onAdd:function(m){if(f&&this==i){m.call(this);
+}},condition:function(){if(this==i){g();delete Element.Events.load;}return true;}};i.addEvent("load",function(){f=true;});})(window,document); \ No newline at end of file
diff --git a/module/webui/themes/Default/js/static/mootools-more.js b/module/webui/themes/Default/js/static/mootools-more.js
new file mode 100644
index 000000000..c7f4a1a0e
--- /dev/null
+++ b/module/webui/themes/Default/js/static/mootools-more.js
@@ -0,0 +1,2856 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/more/c1cc18c2fff04bcc58921b4dff80a6f1
+
+packager build:
+ - packager build More/Form.Request More/Fx.Reveal More/Sortables More/Request.Periodical More/Color
+
+...
+*/
+
+/*
+---
+
+script: More.js
+
+name: More
+
+description: MooTools More
+
+license: MIT-style license
+
+authors:
+ - Guillermo Rauch
+ - Thomas Aylott
+ - Scott Kyle
+ - Arian Stolwijk
+ - Tim Wienk
+ - Christoph Pojer
+ - Aaron Newton
+ - Jacob Thornton
+
+requires:
+ - Core/MooTools
+
+provides: [MooTools.More]
+
+...
+*/
+
+MooTools.More = {
+ version: '1.5.0',
+ build: '73db5e24e6e9c5c87b3a27aebef2248053f7db37'
+};
+
+
+/*
+---
+
+script: Class.Binds.js
+
+name: Class.Binds
+
+description: Automagically binds specified methods in a class to the instance of the class.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Class
+ - MooTools.More
+
+provides: [Class.Binds]
+
+...
+*/
+
+Class.Mutators.Binds = function(binds){
+ if (!this.prototype.initialize) this.implement('initialize', function(){});
+ return Array.from(binds).concat(this.prototype.Binds || []);
+};
+
+Class.Mutators.initialize = function(initialize){
+ return function(){
+ Array.from(this.Binds).each(function(name){
+ var original = this[name];
+ if (original) this[name] = original.bind(this);
+ }, this);
+ return initialize.apply(this, arguments);
+ };
+};
+
+
+/*
+---
+
+script: Class.Occlude.js
+
+name: Class.Occlude
+
+description: Prevents a class from being applied to a DOM element twice.
+
+license: MIT-style license.
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Class
+ - Core/Element
+ - MooTools.More
+
+provides: [Class.Occlude]
+
+...
+*/
+
+Class.Occlude = new Class({
+
+ occlude: function(property, element){
+ element = document.id(element || this.element);
+ var instance = element.retrieve(property || this.property);
+ if (instance && !this.occluded)
+ return (this.occluded = instance);
+
+ this.occluded = false;
+ element.store(property || this.property, this);
+ return this.occluded;
+ }
+
+});
+
+
+/*
+---
+
+script: Class.Refactor.js
+
+name: Class.Refactor
+
+description: Extends a class onto itself with new property, preserving any items attached to the class's namespace.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Class
+ - MooTools.More
+
+# Some modules declare themselves dependent on Class.Refactor
+provides: [Class.refactor, Class.Refactor]
+
+...
+*/
+
+Class.refactor = function(original, refactors){
+
+ Object.each(refactors, function(item, name){
+ var origin = original.prototype[name];
+ origin = (origin && origin.$origin) || origin || function(){};
+ original.implement(name, (typeof item == 'function') ? function(){
+ var old = this.previous;
+ this.previous = origin;
+ var value = item.apply(this, arguments);
+ this.previous = old;
+ return value;
+ } : item);
+ });
+
+ return original;
+
+};
+
+
+/*
+---
+
+script: Element.Measure.js
+
+name: Element.Measure
+
+description: Extends the Element native object to include methods useful in measuring dimensions.
+
+credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Element.Style
+ - Core/Element.Dimensions
+ - MooTools.More
+
+provides: [Element.Measure]
+
+...
+*/
+
+(function(){
+
+var getStylesList = function(styles, planes){
+ var list = [];
+ Object.each(planes, function(directions){
+ Object.each(directions, function(edge){
+ styles.each(function(style){
+ list.push(style + '-' + edge + (style == 'border' ? '-width' : ''));
+ });
+ });
+ });
+ return list;
+};
+
+var calculateEdgeSize = function(edge, styles){
+ var total = 0;
+ Object.each(styles, function(value, style){
+ if (style.test(edge)) total = total + value.toInt();
+ });
+ return total;
+};
+
+var isVisible = function(el){
+ return !!(!el || el.offsetHeight || el.offsetWidth);
+};
+
+
+Element.implement({
+
+ measure: function(fn){
+ if (isVisible(this)) return fn.call(this);
+ var parent = this.getParent(),
+ toMeasure = [];
+ while (!isVisible(parent) && parent != document.body){
+ toMeasure.push(parent.expose());
+ parent = parent.getParent();
+ }
+ var restore = this.expose(),
+ result = fn.call(this);
+ restore();
+ toMeasure.each(function(restore){
+ restore();
+ });
+ return result;
+ },
+
+ expose: function(){
+ if (this.getStyle('display') != 'none') return function(){};
+ var before = this.style.cssText;
+ this.setStyles({
+ display: 'block',
+ position: 'absolute',
+ visibility: 'hidden'
+ });
+ return function(){
+ this.style.cssText = before;
+ }.bind(this);
+ },
+
+ getDimensions: function(options){
+ options = Object.merge({computeSize: false}, options);
+ var dim = {x: 0, y: 0};
+
+ var getSize = function(el, options){
+ return (options.computeSize) ? el.getComputedSize(options) : el.getSize();
+ };
+
+ var parent = this.getParent('body');
+
+ if (parent && this.getStyle('display') == 'none'){
+ dim = this.measure(function(){
+ return getSize(this, options);
+ });
+ } else if (parent){
+ try { //safari sometimes crashes here, so catch it
+ dim = getSize(this, options);
+ }catch(e){}
+ }
+
+ return Object.append(dim, (dim.x || dim.x === 0) ? {
+ width: dim.x,
+ height: dim.y
+ } : {
+ x: dim.width,
+ y: dim.height
+ }
+ );
+ },
+
+ getComputedSize: function(options){
+
+
+ options = Object.merge({
+ styles: ['padding','border'],
+ planes: {
+ height: ['top','bottom'],
+ width: ['left','right']
+ },
+ mode: 'both'
+ }, options);
+
+ var styles = {},
+ size = {width: 0, height: 0},
+ dimensions;
+
+ if (options.mode == 'vertical'){
+ delete size.width;
+ delete options.planes.width;
+ } else if (options.mode == 'horizontal'){
+ delete size.height;
+ delete options.planes.height;
+ }
+
+ getStylesList(options.styles, options.planes).each(function(style){
+ styles[style] = this.getStyle(style).toInt();
+ }, this);
+
+ Object.each(options.planes, function(edges, plane){
+
+ var capitalized = plane.capitalize(),
+ style = this.getStyle(plane);
+
+ if (style == 'auto' && !dimensions) dimensions = this.getDimensions();
+
+ style = styles[plane] = (style == 'auto') ? dimensions[plane] : style.toInt();
+ size['total' + capitalized] = style;
+
+ edges.each(function(edge){
+ var edgesize = calculateEdgeSize(edge, styles);
+ size['computed' + edge.capitalize()] = edgesize;
+ size['total' + capitalized] += edgesize;
+ });
+
+ }, this);
+
+ return Object.append(size, styles);
+ }
+
+});
+
+})();
+
+
+/*
+---
+
+script: Element.Position.js
+
+name: Element.Position
+
+description: Extends the Element native object to include methods useful positioning elements relative to others.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+ - Jacob Thornton
+
+requires:
+ - Core/Options
+ - Core/Element.Dimensions
+ - Element.Measure
+
+provides: [Element.Position]
+
+...
+*/
+
+(function(original){
+
+var local = Element.Position = {
+
+ options: {/*
+ edge: false,
+ returnPos: false,
+ minimum: {x: 0, y: 0},
+ maximum: {x: 0, y: 0},
+ relFixedPosition: false,
+ ignoreMargins: false,
+ ignoreScroll: false,
+ allowNegative: false,*/
+ relativeTo: document.body,
+ position: {
+ x: 'center', //left, center, right
+ y: 'center' //top, center, bottom
+ },
+ offset: {x: 0, y: 0}
+ },
+
+ getOptions: function(element, options){
+ options = Object.merge({}, local.options, options);
+ local.setPositionOption(options);
+ local.setEdgeOption(options);
+ local.setOffsetOption(element, options);
+ local.setDimensionsOption(element, options);
+ return options;
+ },
+
+ setPositionOption: function(options){
+ options.position = local.getCoordinateFromValue(options.position);
+ },
+
+ setEdgeOption: function(options){
+ var edgeOption = local.getCoordinateFromValue(options.edge);
+ options.edge = edgeOption ? edgeOption :
+ (options.position.x == 'center' && options.position.y == 'center') ? {x: 'center', y: 'center'} :
+ {x: 'left', y: 'top'};
+ },
+
+ setOffsetOption: function(element, options){
+ var parentOffset = {x: 0, y: 0};
+ var parentScroll = {x: 0, y: 0};
+ var offsetParent = element.measure(function(){
+ return document.id(this.getOffsetParent());
+ });
+
+ if (!offsetParent || offsetParent == element.getDocument().body) return;
+
+ parentScroll = offsetParent.getScroll();
+ parentOffset = offsetParent.measure(function(){
+ var position = this.getPosition();
+ if (this.getStyle('position') == 'fixed'){
+ var scroll = window.getScroll();
+ position.x += scroll.x;
+ position.y += scroll.y;
+ }
+ return position;
+ });
+
+ options.offset = {
+ parentPositioned: offsetParent != document.id(options.relativeTo),
+ x: options.offset.x - parentOffset.x + parentScroll.x,
+ y: options.offset.y - parentOffset.y + parentScroll.y
+ };
+ },
+
+ setDimensionsOption: function(element, options){
+ options.dimensions = element.getDimensions({
+ computeSize: true,
+ styles: ['padding', 'border', 'margin']
+ });
+ },
+
+ getPosition: function(element, options){
+ var position = {};
+ options = local.getOptions(element, options);
+ var relativeTo = document.id(options.relativeTo) || document.body;
+
+ local.setPositionCoordinates(options, position, relativeTo);
+ if (options.edge) local.toEdge(position, options);
+
+ var offset = options.offset;
+ position.left = ((position.x >= 0 || offset.parentPositioned || options.allowNegative) ? position.x : 0).toInt();
+ position.top = ((position.y >= 0 || offset.parentPositioned || options.allowNegative) ? position.y : 0).toInt();
+
+ local.toMinMax(position, options);
+
+ if (options.relFixedPosition || relativeTo.getStyle('position') == 'fixed') local.toRelFixedPosition(relativeTo, position);
+ if (options.ignoreScroll) local.toIgnoreScroll(relativeTo, position);
+ if (options.ignoreMargins) local.toIgnoreMargins(position, options);
+
+ position.left = Math.ceil(position.left);
+ position.top = Math.ceil(position.top);
+ delete position.x;
+ delete position.y;
+
+ return position;
+ },
+
+ setPositionCoordinates: function(options, position, relativeTo){
+ var offsetY = options.offset.y,
+ offsetX = options.offset.x,
+ calc = (relativeTo == document.body) ? window.getScroll() : relativeTo.getPosition(),
+ top = calc.y,
+ left = calc.x,
+ winSize = window.getSize();
+
+ switch(options.position.x){
+ case 'left': position.x = left + offsetX; break;
+ case 'right': position.x = left + offsetX + relativeTo.offsetWidth; break;
+ default: position.x = left + ((relativeTo == document.body ? winSize.x : relativeTo.offsetWidth) / 2) + offsetX; break;
+ }
+
+ switch(options.position.y){
+ case 'top': position.y = top + offsetY; break;
+ case 'bottom': position.y = top + offsetY + relativeTo.offsetHeight; break;
+ default: position.y = top + ((relativeTo == document.body ? winSize.y : relativeTo.offsetHeight) / 2) + offsetY; break;
+ }
+ },
+
+ toMinMax: function(position, options){
+ var xy = {left: 'x', top: 'y'}, value;
+ ['minimum', 'maximum'].each(function(minmax){
+ ['left', 'top'].each(function(lr){
+ value = options[minmax] ? options[minmax][xy[lr]] : null;
+ if (value != null && ((minmax == 'minimum') ? position[lr] < value : position[lr] > value)) position[lr] = value;
+ });
+ });
+ },
+
+ toRelFixedPosition: function(relativeTo, position){
+ var winScroll = window.getScroll();
+ position.top += winScroll.y;
+ position.left += winScroll.x;
+ },
+
+ toIgnoreScroll: function(relativeTo, position){
+ var relScroll = relativeTo.getScroll();
+ position.top -= relScroll.y;
+ position.left -= relScroll.x;
+ },
+
+ toIgnoreMargins: function(position, options){
+ position.left += options.edge.x == 'right'
+ ? options.dimensions['margin-right']
+ : (options.edge.x != 'center'
+ ? -options.dimensions['margin-left']
+ : -options.dimensions['margin-left'] + ((options.dimensions['margin-right'] + options.dimensions['margin-left']) / 2));
+
+ position.top += options.edge.y == 'bottom'
+ ? options.dimensions['margin-bottom']
+ : (options.edge.y != 'center'
+ ? -options.dimensions['margin-top']
+ : -options.dimensions['margin-top'] + ((options.dimensions['margin-bottom'] + options.dimensions['margin-top']) / 2));
+ },
+
+ toEdge: function(position, options){
+ var edgeOffset = {},
+ dimensions = options.dimensions,
+ edge = options.edge;
+
+ switch(edge.x){
+ case 'left': edgeOffset.x = 0; break;
+ case 'right': edgeOffset.x = -dimensions.x - dimensions.computedRight - dimensions.computedLeft; break;
+ // center
+ default: edgeOffset.x = -(Math.round(dimensions.totalWidth / 2)); break;
+ }
+
+ switch(edge.y){
+ case 'top': edgeOffset.y = 0; break;
+ case 'bottom': edgeOffset.y = -dimensions.y - dimensions.computedTop - dimensions.computedBottom; break;
+ // center
+ default: edgeOffset.y = -(Math.round(dimensions.totalHeight / 2)); break;
+ }
+
+ position.x += edgeOffset.x;
+ position.y += edgeOffset.y;
+ },
+
+ getCoordinateFromValue: function(option){
+ if (typeOf(option) != 'string') return option;
+ option = option.toLowerCase();
+
+ return {
+ x: option.test('left') ? 'left'
+ : (option.test('right') ? 'right' : 'center'),
+ y: option.test(/upper|top/) ? 'top'
+ : (option.test('bottom') ? 'bottom' : 'center')
+ };
+ }
+
+};
+
+Element.implement({
+
+ position: function(options){
+ if (options && (options.x != null || options.y != null)){
+ return (original ? original.apply(this, arguments) : this);
+ }
+ var position = this.setStyle('position', 'absolute').calculatePosition(options);
+ return (options && options.returnPos) ? position : this.setStyles(position);
+ },
+
+ calculatePosition: function(options){
+ return local.getPosition(this, options);
+ }
+
+});
+
+})(Element.prototype.position);
+
+
+/*
+---
+
+script: IframeShim.js
+
+name: IframeShim
+
+description: Defines IframeShim, a class for obscuring select lists and flash objects in IE.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Element.Event
+ - Core/Element.Style
+ - Core/Options
+ - Core/Events
+ - Element.Position
+ - Class.Occlude
+
+provides: [IframeShim]
+
+...
+*/
+
+(function(){
+
+var browsers = false;
+
+
+this.IframeShim = new Class({
+
+ Implements: [Options, Events, Class.Occlude],
+
+ options: {
+ className: 'iframeShim',
+ src: 'javascript:false;document.write("");',
+ display: false,
+ zIndex: null,
+ margin: 0,
+ offset: {x: 0, y: 0},
+ browsers: browsers
+ },
+
+ property: 'IframeShim',
+
+ initialize: function(element, options){
+ this.element = document.id(element);
+ if (this.occlude()) return this.occluded;
+ this.setOptions(options);
+ this.makeShim();
+ return this;
+ },
+
+ makeShim: function(){
+ if (this.options.browsers){
+ var zIndex = this.element.getStyle('zIndex').toInt();
+
+ if (!zIndex){
+ zIndex = 1;
+ var pos = this.element.getStyle('position');
+ if (pos == 'static' || !pos) this.element.setStyle('position', 'relative');
+ this.element.setStyle('zIndex', zIndex);
+ }
+ zIndex = ((this.options.zIndex != null || this.options.zIndex === 0) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1;
+ if (zIndex < 0) zIndex = 1;
+ this.shim = new Element('iframe', {
+ src: this.options.src,
+ scrolling: 'no',
+ frameborder: 0,
+ styles: {
+ zIndex: zIndex,
+ position: 'absolute',
+ border: 'none',
+ filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
+ },
+ 'class': this.options.className
+ }).store('IframeShim', this);
+ var inject = (function(){
+ this.shim.inject(this.element, 'after');
+ this[this.options.display ? 'show' : 'hide']();
+ this.fireEvent('inject');
+ }).bind(this);
+ if (!IframeShim.ready) window.addEvent('load', inject);
+ else inject();
+ } else {
+ this.position = this.hide = this.show = this.dispose = Function.from(this);
+ }
+ },
+
+ position: function(){
+ if (!IframeShim.ready || !this.shim) return this;
+ var size = this.element.measure(function(){
+ return this.getSize();
+ });
+ if (this.options.margin != undefined){
+ size.x = size.x - (this.options.margin * 2);
+ size.y = size.y - (this.options.margin * 2);
+ this.options.offset.x += this.options.margin;
+ this.options.offset.y += this.options.margin;
+ }
+ this.shim.set({width: size.x, height: size.y}).position({
+ relativeTo: this.element,
+ offset: this.options.offset
+ });
+ return this;
+ },
+
+ hide: function(){
+ if (this.shim) this.shim.setStyle('display', 'none');
+ return this;
+ },
+
+ show: function(){
+ if (this.shim) this.shim.setStyle('display', 'block');
+ return this.position();
+ },
+
+ dispose: function(){
+ if (this.shim) this.shim.dispose();
+ return this;
+ },
+
+ destroy: function(){
+ if (this.shim) this.shim.destroy();
+ return this;
+ }
+
+});
+
+})();
+
+window.addEvent('load', function(){
+ IframeShim.ready = true;
+});
+
+
+/*
+---
+
+script: Mask.js
+
+name: Mask
+
+description: Creates a mask element to cover another.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Options
+ - Core/Events
+ - Core/Element.Event
+ - Class.Binds
+ - Element.Position
+ - IframeShim
+
+provides: [Mask]
+
+...
+*/
+
+var Mask = new Class({
+
+ Implements: [Options, Events],
+
+ Binds: ['position'],
+
+ options: {/*
+ onShow: function(){},
+ onHide: function(){},
+ onDestroy: function(){},
+ onClick: function(event){},
+ inject: {
+ where: 'after',
+ target: null,
+ },
+ hideOnClick: false,
+ id: null,
+ destroyOnHide: false,*/
+ style: {},
+ 'class': 'mask',
+ maskMargins: false,
+ useIframeShim: true,
+ iframeShimOptions: {}
+ },
+
+ initialize: function(target, options){
+ this.target = document.id(target) || document.id(document.body);
+ this.target.store('mask', this);
+ this.setOptions(options);
+ this.render();
+ this.inject();
+ },
+
+ render: function(){
+ this.element = new Element('div', {
+ 'class': this.options['class'],
+ id: this.options.id || 'mask-' + String.uniqueID(),
+ styles: Object.merge({}, this.options.style, {
+ display: 'none'
+ }),
+ events: {
+ click: function(event){
+ this.fireEvent('click', event);
+ if (this.options.hideOnClick) this.hide();
+ }.bind(this)
+ }
+ });
+
+ this.hidden = true;
+ },
+
+ toElement: function(){
+ return this.element;
+ },
+
+ inject: function(target, where){
+ where = where || (this.options.inject ? this.options.inject.where : '') || (this.target == document.body ? 'inside' : 'after');
+ target = target || (this.options.inject && this.options.inject.target) || this.target;
+
+ this.element.inject(target, where);
+
+ if (this.options.useIframeShim){
+ this.shim = new IframeShim(this.element, this.options.iframeShimOptions);
+
+ this.addEvents({
+ show: this.shim.show.bind(this.shim),
+ hide: this.shim.hide.bind(this.shim),
+ destroy: this.shim.destroy.bind(this.shim)
+ });
+ }
+ },
+
+ position: function(){
+ this.resize(this.options.width, this.options.height);
+
+ this.element.position({
+ relativeTo: this.target,
+ position: 'topLeft',
+ ignoreMargins: !this.options.maskMargins,
+ ignoreScroll: this.target == document.body
+ });
+
+ return this;
+ },
+
+ resize: function(x, y){
+ var opt = {
+ styles: ['padding', 'border']
+ };
+ if (this.options.maskMargins) opt.styles.push('margin');
+
+ var dim = this.target.getComputedSize(opt);
+ if (this.target == document.body){
+ this.element.setStyles({width: 0, height: 0});
+ var win = window.getScrollSize();
+ if (dim.totalHeight < win.y) dim.totalHeight = win.y;
+ if (dim.totalWidth < win.x) dim.totalWidth = win.x;
+ }
+ this.element.setStyles({
+ width: Array.pick([x, dim.totalWidth, dim.x]),
+ height: Array.pick([y, dim.totalHeight, dim.y])
+ });
+
+ return this;
+ },
+
+ show: function(){
+ if (!this.hidden) return this;
+
+ window.addEvent('resize', this.position);
+ this.position();
+ this.showMask.apply(this, arguments);
+
+ return this;
+ },
+
+ showMask: function(){
+ this.element.setStyle('display', 'block');
+ this.hidden = false;
+ this.fireEvent('show');
+ },
+
+ hide: function(){
+ if (this.hidden) return this;
+
+ window.removeEvent('resize', this.position);
+ this.hideMask.apply(this, arguments);
+ if (this.options.destroyOnHide) return this.destroy();
+
+ return this;
+ },
+
+ hideMask: function(){
+ this.element.setStyle('display', 'none');
+ this.hidden = true;
+ this.fireEvent('hide');
+ },
+
+ toggle: function(){
+ this[this.hidden ? 'show' : 'hide']();
+ },
+
+ destroy: function(){
+ this.hide();
+ this.element.destroy();
+ this.fireEvent('destroy');
+ this.target.eliminate('mask');
+ }
+
+});
+
+Element.Properties.mask = {
+
+ set: function(options){
+ var mask = this.retrieve('mask');
+ if (mask) mask.destroy();
+ return this.eliminate('mask').store('mask:options', options);
+ },
+
+ get: function(){
+ var mask = this.retrieve('mask');
+ if (!mask){
+ mask = new Mask(this, this.retrieve('mask:options'));
+ this.store('mask', mask);
+ }
+ return mask;
+ }
+
+};
+
+Element.implement({
+
+ mask: function(options){
+ if (options) this.set('mask', options);
+ this.get('mask').show();
+ return this;
+ },
+
+ unmask: function(){
+ this.get('mask').hide();
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+script: Spinner.js
+
+name: Spinner
+
+description: Adds a semi-transparent overlay over a dom element with a spinnin ajax icon.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Fx.Tween
+ - Core/Request
+ - Class.refactor
+ - Mask
+
+provides: [Spinner]
+
+...
+*/
+
+var Spinner = new Class({
+
+ Extends: Mask,
+
+ Implements: Chain,
+
+ options: {/*
+ message: false,*/
+ 'class': 'spinner',
+ containerPosition: {},
+ content: {
+ 'class': 'spinner-content'
+ },
+ messageContainer: {
+ 'class': 'spinner-msg'
+ },
+ img: {
+ 'class': 'spinner-img'
+ },
+ fxOptions: {
+ link: 'chain'
+ }
+ },
+
+ initialize: function(target, options){
+ this.target = document.id(target) || document.id(document.body);
+ this.target.store('spinner', this);
+ this.setOptions(options);
+ this.render();
+ this.inject();
+
+ // Add this to events for when noFx is true; parent methods handle hide/show.
+ var deactivate = function(){ this.active = false; }.bind(this);
+ this.addEvents({
+ hide: deactivate,
+ show: deactivate
+ });
+ },
+
+ render: function(){
+ this.parent();
+
+ this.element.set('id', this.options.id || 'spinner-' + String.uniqueID());
+
+ this.content = document.id(this.options.content) || new Element('div', this.options.content);
+ this.content.inject(this.element);
+
+ if (this.options.message){
+ this.msg = document.id(this.options.message) || new Element('p', this.options.messageContainer).appendText(this.options.message);
+ this.msg.inject(this.content);
+ }
+
+ if (this.options.img){
+ this.img = document.id(this.options.img) || new Element('div', this.options.img);
+ this.img.inject(this.content);
+ }
+
+ this.element.set('tween', this.options.fxOptions);
+ },
+
+ show: function(noFx){
+ if (this.active) return this.chain(this.show.bind(this));
+ if (!this.hidden){
+ this.callChain.delay(20, this);
+ return this;
+ }
+
+ this.target.set('aria-busy', 'true');
+ this.active = true;
+
+ return this.parent(noFx);
+ },
+
+ showMask: function(noFx){
+ var pos = function(){
+ this.content.position(Object.merge({
+ relativeTo: this.element
+ }, this.options.containerPosition));
+ }.bind(this);
+
+ if (noFx){
+ this.parent();
+ pos();
+ } else {
+ if (!this.options.style.opacity) this.options.style.opacity = this.element.getStyle('opacity').toFloat();
+ this.element.setStyles({
+ display: 'block',
+ opacity: 0
+ }).tween('opacity', this.options.style.opacity);
+ pos();
+ this.hidden = false;
+ this.fireEvent('show');
+ this.callChain();
+ }
+ },
+
+ hide: function(noFx){
+ if (this.active) return this.chain(this.hide.bind(this));
+ if (this.hidden){
+ this.callChain.delay(20, this);
+ return this;
+ }
+
+ this.target.set('aria-busy', 'false');
+ this.active = true;
+
+ return this.parent(noFx);
+ },
+
+ hideMask: function(noFx){
+ if (noFx) return this.parent();
+ this.element.tween('opacity', 0).get('tween').chain(function(){
+ this.element.setStyle('display', 'none');
+ this.hidden = true;
+ this.fireEvent('hide');
+ this.callChain();
+ }.bind(this));
+ },
+
+ destroy: function(){
+ this.content.destroy();
+ this.parent();
+ this.target.eliminate('spinner');
+ }
+
+});
+
+Request = Class.refactor(Request, {
+
+ options: {
+ useSpinner: false,
+ spinnerOptions: {},
+ spinnerTarget: false
+ },
+
+ initialize: function(options){
+ this._send = this.send;
+ this.send = function(options){
+ var spinner = this.getSpinner();
+ if (spinner) spinner.chain(this._send.pass(options, this)).show();
+ else this._send(options);
+ return this;
+ };
+ this.previous(options);
+ },
+
+ getSpinner: function(){
+ if (!this.spinner){
+ var update = document.id(this.options.spinnerTarget) || document.id(this.options.update);
+ if (this.options.useSpinner && update){
+ update.set('spinner', this.options.spinnerOptions);
+ var spinner = this.spinner = update.get('spinner');
+ ['complete', 'exception', 'cancel'].each(function(event){
+ this.addEvent(event, spinner.hide.bind(spinner));
+ }, this);
+ }
+ }
+ return this.spinner;
+ }
+
+});
+
+Element.Properties.spinner = {
+
+ set: function(options){
+ var spinner = this.retrieve('spinner');
+ if (spinner) spinner.destroy();
+ return this.eliminate('spinner').store('spinner:options', options);
+ },
+
+ get: function(){
+ var spinner = this.retrieve('spinner');
+ if (!spinner){
+ spinner = new Spinner(this, this.retrieve('spinner:options'));
+ this.store('spinner', spinner);
+ }
+ return spinner;
+ }
+
+};
+
+Element.implement({
+
+ spin: function(options){
+ if (options) this.set('spinner', options);
+ this.get('spinner').show();
+ return this;
+ },
+
+ unspin: function(){
+ this.get('spinner').hide();
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+script: String.QueryString.js
+
+name: String.QueryString
+
+description: Methods for dealing with URI query strings.
+
+license: MIT-style license
+
+authors:
+ - Sebastian Markbåge
+ - Aaron Newton
+ - Lennart Pilon
+ - Valerio Proietti
+
+requires:
+ - Core/Array
+ - Core/String
+ - MooTools.More
+
+provides: [String.QueryString]
+
+...
+*/
+
+String.implement({
+
+ parseQueryString: function(decodeKeys, decodeValues){
+ if (decodeKeys == null) decodeKeys = true;
+ if (decodeValues == null) decodeValues = true;
+
+ var vars = this.split(/[&;]/),
+ object = {};
+ if (!vars.length) return object;
+
+ vars.each(function(val){
+ var index = val.indexOf('=') + 1,
+ value = index ? val.substr(index) : '',
+ keys = index ? val.substr(0, index - 1).match(/([^\]\[]+|(\B)(?=\]))/g) : [val],
+ obj = object;
+ if (!keys) return;
+ if (decodeValues) value = decodeURIComponent(value);
+ keys.each(function(key, i){
+ if (decodeKeys) key = decodeURIComponent(key);
+ var current = obj[key];
+
+ if (i < keys.length - 1) obj = obj[key] = current || {};
+ else if (typeOf(current) == 'array') current.push(value);
+ else obj[key] = current != null ? [current, value] : value;
+ });
+ });
+
+ return object;
+ },
+
+ cleanQueryString: function(method){
+ return this.split('&').filter(function(val){
+ var index = val.indexOf('='),
+ key = index < 0 ? '' : val.substr(0, index),
+ value = val.substr(index + 1);
+
+ return method ? method.call(null, key, value) : (value || value === 0);
+ }).join('&');
+ }
+
+});
+
+
+/*
+---
+
+name: Events.Pseudos
+
+description: Adds the functionality to add pseudo events
+
+license: MIT-style license
+
+authors:
+ - Arian Stolwijk
+
+requires: [Core/Class.Extras, Core/Slick.Parser, MooTools.More]
+
+provides: [Events.Pseudos]
+
+...
+*/
+
+(function(){
+
+Events.Pseudos = function(pseudos, addEvent, removeEvent){
+
+ var storeKey = '_monitorEvents:';
+
+ var storageOf = function(object){
+ return {
+ store: object.store ? function(key, value){
+ object.store(storeKey + key, value);
+ } : function(key, value){
+ (object._monitorEvents || (object._monitorEvents = {}))[key] = value;
+ },
+ retrieve: object.retrieve ? function(key, dflt){
+ return object.retrieve(storeKey + key, dflt);
+ } : function(key, dflt){
+ if (!object._monitorEvents) return dflt;
+ return object._monitorEvents[key] || dflt;
+ }
+ };
+ };
+
+ var splitType = function(type){
+ if (type.indexOf(':') == -1 || !pseudos) return null;
+
+ var parsed = Slick.parse(type).expressions[0][0],
+ parsedPseudos = parsed.pseudos,
+ l = parsedPseudos.length,
+ splits = [];
+
+ while (l--){
+ var pseudo = parsedPseudos[l].key,
+ listener = pseudos[pseudo];
+ if (listener != null) splits.push({
+ event: parsed.tag,
+ value: parsedPseudos[l].value,
+ pseudo: pseudo,
+ original: type,
+ listener: listener
+ });
+ }
+ return splits.length ? splits : null;
+ };
+
+ return {
+
+ addEvent: function(type, fn, internal){
+ var split = splitType(type);
+ if (!split) return addEvent.call(this, type, fn, internal);
+
+ var storage = storageOf(this),
+ events = storage.retrieve(type, []),
+ eventType = split[0].event,
+ args = Array.slice(arguments, 2),
+ stack = fn,
+ self = this;
+
+ split.each(function(item){
+ var listener = item.listener,
+ stackFn = stack;
+ if (listener == false) eventType += ':' + item.pseudo + '(' + item.value + ')';
+ else stack = function(){
+ listener.call(self, item, stackFn, arguments, stack);
+ };
+ });
+
+ events.include({type: eventType, event: fn, monitor: stack});
+ storage.store(type, events);
+
+ if (type != eventType) addEvent.apply(this, [type, fn].concat(args));
+ return addEvent.apply(this, [eventType, stack].concat(args));
+ },
+
+ removeEvent: function(type, fn){
+ var split = splitType(type);
+ if (!split) return removeEvent.call(this, type, fn);
+
+ var storage = storageOf(this),
+ events = storage.retrieve(type);
+ if (!events) return this;
+
+ var args = Array.slice(arguments, 2);
+
+ removeEvent.apply(this, [type, fn].concat(args));
+ events.each(function(monitor, i){
+ if (!fn || monitor.event == fn) removeEvent.apply(this, [monitor.type, monitor.monitor].concat(args));
+ delete events[i];
+ }, this);
+
+ storage.store(type, events);
+ return this;
+ }
+
+ };
+
+};
+
+var pseudos = {
+
+ once: function(split, fn, args, monitor){
+ fn.apply(this, args);
+ this.removeEvent(split.event, monitor)
+ .removeEvent(split.original, fn);
+ },
+
+ throttle: function(split, fn, args){
+ if (!fn._throttled){
+ fn.apply(this, args);
+ fn._throttled = setTimeout(function(){
+ fn._throttled = false;
+ }, split.value || 250);
+ }
+ },
+
+ pause: function(split, fn, args){
+ clearTimeout(fn._pause);
+ fn._pause = fn.delay(split.value || 250, this, args);
+ }
+
+};
+
+Events.definePseudo = function(key, listener){
+ pseudos[key] = listener;
+ return this;
+};
+
+Events.lookupPseudo = function(key){
+ return pseudos[key];
+};
+
+var proto = Events.prototype;
+Events.implement(Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
+
+['Request', 'Fx'].each(function(klass){
+ if (this[klass]) this[klass].implement(Events.prototype);
+});
+
+})();
+
+
+/*
+---
+
+name: Element.Event.Pseudos
+
+description: Adds the functionality to add pseudo events for Elements
+
+license: MIT-style license
+
+authors:
+ - Arian Stolwijk
+
+requires: [Core/Element.Event, Core/Element.Delegation, Events.Pseudos]
+
+provides: [Element.Event.Pseudos, Element.Delegation.Pseudo]
+
+...
+*/
+
+(function(){
+
+var pseudos = {relay: false},
+ copyFromEvents = ['once', 'throttle', 'pause'],
+ count = copyFromEvents.length;
+
+while (count--) pseudos[copyFromEvents[count]] = Events.lookupPseudo(copyFromEvents[count]);
+
+DOMEvent.definePseudo = function(key, listener){
+ pseudos[key] = listener;
+ return this;
+};
+
+var proto = Element.prototype;
+[Element, Window, Document].invoke('implement', Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
+
+})();
+
+
+/*
+---
+
+script: Form.Request.js
+
+name: Form.Request
+
+description: Handles the basic functionality of submitting a form and updating a dom element with the result.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Request.HTML
+ - Class.Binds
+ - Class.Occlude
+ - Spinner
+ - String.QueryString
+ - Element.Delegation.Pseudo
+
+provides: [Form.Request]
+
+...
+*/
+
+if (!window.Form) window.Form = {};
+
+(function(){
+
+ Form.Request = new Class({
+
+ Binds: ['onSubmit', 'onFormValidate'],
+
+ Implements: [Options, Events, Class.Occlude],
+
+ options: {/*
+ onFailure: function(){},
+ onSuccess: function(){}, // aliased to onComplete,
+ onSend: function(){}*/
+ requestOptions: {
+ evalScripts: true,
+ useSpinner: true,
+ emulation: false,
+ link: 'ignore'
+ },
+ sendButtonClicked: true,
+ extraData: {},
+ resetForm: true
+ },
+
+ property: 'form.request',
+
+ initialize: function(form, target, options){
+ this.element = document.id(form);
+ if (this.occlude()) return this.occluded;
+ this.setOptions(options)
+ .setTarget(target)
+ .attach();
+ },
+
+ setTarget: function(target){
+ this.target = document.id(target);
+ if (!this.request){
+ this.makeRequest();
+ } else {
+ this.request.setOptions({
+ update: this.target
+ });
+ }
+ return this;
+ },
+
+ toElement: function(){
+ return this.element;
+ },
+
+ makeRequest: function(){
+ var self = this;
+ this.request = new Request.HTML(Object.merge({
+ update: this.target,
+ emulation: false,
+ spinnerTarget: this.element,
+ method: this.element.get('method') || 'post'
+ }, this.options.requestOptions)).addEvents({
+ success: function(tree, elements, html, javascript){
+ ['complete', 'success'].each(function(evt){
+ self.fireEvent(evt, [self.target, tree, elements, html, javascript]);
+ });
+ },
+ failure: function(){
+ self.fireEvent('complete', arguments).fireEvent('failure', arguments);
+ },
+ exception: function(){
+ self.fireEvent('failure', arguments);
+ }
+ });
+ return this.attachReset();
+ },
+
+ attachReset: function(){
+ if (!this.options.resetForm) return this;
+ this.request.addEvent('success', function(){
+ Function.attempt(function(){
+ this.element.reset();
+ }.bind(this));
+ if (window.OverText) OverText.update();
+ }.bind(this));
+ return this;
+ },
+
+ attach: function(attach){
+ var method = (attach != false) ? 'addEvent' : 'removeEvent';
+ this.element[method]('click:relay(button, input[type=submit])', this.saveClickedButton.bind(this));
+
+ var fv = this.element.retrieve('validator');
+ if (fv) fv[method]('onFormValidate', this.onFormValidate);
+ else this.element[method]('submit', this.onSubmit);
+
+ return this;
+ },
+
+ detach: function(){
+ return this.attach(false);
+ },
+
+ //public method
+ enable: function(){
+ return this.attach();
+ },
+
+ //public method
+ disable: function(){
+ return this.detach();
+ },
+
+ onFormValidate: function(valid, form, event){
+ //if there's no event, then this wasn't a submit event
+ if (!event) return;
+ var fv = this.element.retrieve('validator');
+ if (valid || (fv && !fv.options.stopOnFailure)){
+ event.stop();
+ this.send();
+ }
+ },
+
+ onSubmit: function(event){
+ var fv = this.element.retrieve('validator');
+ if (fv){
+ //form validator was created after Form.Request
+ this.element.removeEvent('submit', this.onSubmit);
+ fv.addEvent('onFormValidate', this.onFormValidate);
+ fv.validate(event);
+ return;
+ }
+ if (event) event.stop();
+ this.send();
+ },
+
+ saveClickedButton: function(event, target){
+ var targetName = target.get('name');
+ if (!targetName || !this.options.sendButtonClicked) return;
+ this.options.extraData[targetName] = target.get('value') || true;
+ this.clickedCleaner = function(){
+ delete this.options.extraData[targetName];
+ this.clickedCleaner = function(){};
+ }.bind(this);
+ },
+
+ clickedCleaner: function(){},
+
+ send: function(){
+ var str = this.element.toQueryString().trim(),
+ data = Object.toQueryString(this.options.extraData);
+
+ if (str) str += "&" + data;
+ else str = data;
+
+ this.fireEvent('send', [this.element, str.parseQueryString()]);
+ this.request.send({
+ data: str,
+ url: this.options.requestOptions.url || this.element.get('action')
+ });
+ this.clickedCleaner();
+ return this;
+ }
+
+ });
+
+ Element.implement('formUpdate', function(update, options){
+ var fq = this.retrieve('form.request');
+ if (!fq){
+ fq = new Form.Request(this, update, options);
+ } else {
+ if (update) fq.setTarget(update);
+ if (options) fq.setOptions(options).makeRequest();
+ }
+ fq.send();
+ return this;
+ });
+
+})();
+
+
+/*
+---
+
+script: Element.Shortcuts.js
+
+name: Element.Shortcuts
+
+description: Extends the Element native object to include some shortcut methods.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Element.Style
+ - MooTools.More
+
+provides: [Element.Shortcuts]
+
+...
+*/
+
+Element.implement({
+
+ isDisplayed: function(){
+ return this.getStyle('display') != 'none';
+ },
+
+ isVisible: function(){
+ var w = this.offsetWidth,
+ h = this.offsetHeight;
+ return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.style.display != 'none';
+ },
+
+ toggle: function(){
+ return this[this.isDisplayed() ? 'hide' : 'show']();
+ },
+
+ hide: function(){
+ var d;
+ try {
+ //IE fails here if the element is not in the dom
+ d = this.getStyle('display');
+ } catch(e){}
+ if (d == 'none') return this;
+ return this.store('element:_originalDisplay', d || '').setStyle('display', 'none');
+ },
+
+ show: function(display){
+ if (!display && this.isDisplayed()) return this;
+ display = display || this.retrieve('element:_originalDisplay') || 'block';
+ return this.setStyle('display', (display == 'none') ? 'block' : display);
+ },
+
+ swapClass: function(remove, add){
+ return this.removeClass(remove).addClass(add);
+ }
+
+});
+
+Document.implement({
+
+ clearSelection: function(){
+ if (window.getSelection){
+ var selection = window.getSelection();
+ if (selection && selection.removeAllRanges) selection.removeAllRanges();
+ } else if (document.selection && document.selection.empty){
+ try {
+ //IE fails here if selected element is not in dom
+ document.selection.empty();
+ } catch(e){}
+ }
+ }
+
+});
+
+
+/*
+---
+
+script: Fx.Reveal.js
+
+name: Fx.Reveal
+
+description: Defines Fx.Reveal, a class that shows and hides elements with a transition.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Fx.Morph
+ - Element.Shortcuts
+ - Element.Measure
+
+provides: [Fx.Reveal]
+
+...
+*/
+
+(function(){
+
+
+var hideTheseOf = function(object){
+ var hideThese = object.options.hideInputs;
+ if (window.OverText){
+ var otClasses = [null];
+ OverText.each(function(ot){
+ otClasses.include('.' + ot.options.labelClass);
+ });
+ if (otClasses) hideThese += otClasses.join(', ');
+ }
+ return (hideThese) ? object.element.getElements(hideThese) : null;
+};
+
+
+Fx.Reveal = new Class({
+
+ Extends: Fx.Morph,
+
+ options: {/*
+ onShow: function(thisElement){},
+ onHide: function(thisElement){},
+ onComplete: function(thisElement){},
+ heightOverride: null,
+ widthOverride: null,*/
+ link: 'cancel',
+ styles: ['padding', 'border', 'margin'],
+ transitionOpacity: 'opacity' in document.documentElement,
+ mode: 'vertical',
+ display: function(){
+ return this.element.get('tag') != 'tr' ? 'block' : 'table-row';
+ },
+ opacity: 1,
+ hideInputs: !('opacity' in document.documentElement) ? 'select, input, textarea, object, embed' : null
+ },
+
+ dissolve: function(){
+ if (!this.hiding && !this.showing){
+ if (this.element.getStyle('display') != 'none'){
+ this.hiding = true;
+ this.showing = false;
+ this.hidden = true;
+ this.cssText = this.element.style.cssText;
+
+ var startStyles = this.element.getComputedSize({
+ styles: this.options.styles,
+ mode: this.options.mode
+ });
+ if (this.options.transitionOpacity) startStyles.opacity = this.options.opacity;
+
+ var zero = {};
+ Object.each(startStyles, function(style, name){
+ zero[name] = [style, 0];
+ });
+
+ this.element.setStyles({
+ display: Function.from(this.options.display).call(this),
+ overflow: 'hidden'
+ });
+
+ var hideThese = hideTheseOf(this);
+ if (hideThese) hideThese.setStyle('visibility', 'hidden');
+
+ this.$chain.unshift(function(){
+ if (this.hidden){
+ this.hiding = false;
+ this.element.style.cssText = this.cssText;
+ this.element.setStyle('display', 'none');
+ if (hideThese) hideThese.setStyle('visibility', 'visible');
+ }
+ this.fireEvent('hide', this.element);
+ this.callChain();
+ }.bind(this));
+
+ this.start(zero);
+ } else {
+ this.callChain.delay(10, this);
+ this.fireEvent('complete', this.element);
+ this.fireEvent('hide', this.element);
+ }
+ } else if (this.options.link == 'chain'){
+ this.chain(this.dissolve.bind(this));
+ } else if (this.options.link == 'cancel' && !this.hiding){
+ this.cancel();
+ this.dissolve();
+ }
+ return this;
+ },
+
+ reveal: function(){
+ if (!this.showing && !this.hiding){
+ if (this.element.getStyle('display') == 'none'){
+ this.hiding = false;
+ this.showing = true;
+ this.hidden = false;
+ this.cssText = this.element.style.cssText;
+
+ var startStyles;
+ this.element.measure(function(){
+ startStyles = this.element.getComputedSize({
+ styles: this.options.styles,
+ mode: this.options.mode
+ });
+ }.bind(this));
+ if (this.options.heightOverride != null) startStyles.height = this.options.heightOverride.toInt();
+ if (this.options.widthOverride != null) startStyles.width = this.options.widthOverride.toInt();
+ if (this.options.transitionOpacity){
+ this.element.setStyle('opacity', 0);
+ startStyles.opacity = this.options.opacity;
+ }
+
+ var zero = {
+ height: 0,
+ display: Function.from(this.options.display).call(this)
+ };
+ Object.each(startStyles, function(style, name){
+ zero[name] = 0;
+ });
+ zero.overflow = 'hidden';
+
+ this.element.setStyles(zero);
+
+ var hideThese = hideTheseOf(this);
+ if (hideThese) hideThese.setStyle('visibility', 'hidden');
+
+ this.$chain.unshift(function(){
+ this.element.style.cssText = this.cssText;
+ this.element.setStyle('display', Function.from(this.options.display).call(this));
+ if (!this.hidden) this.showing = false;
+ if (hideThese) hideThese.setStyle('visibility', 'visible');
+ this.callChain();
+ this.fireEvent('show', this.element);
+ }.bind(this));
+
+ this.start(startStyles);
+ } else {
+ this.callChain();
+ this.fireEvent('complete', this.element);
+ this.fireEvent('show', this.element);
+ }
+ } else if (this.options.link == 'chain'){
+ this.chain(this.reveal.bind(this));
+ } else if (this.options.link == 'cancel' && !this.showing){
+ this.cancel();
+ this.reveal();
+ }
+ return this;
+ },
+
+ toggle: function(){
+ if (this.element.getStyle('display') == 'none'){
+ this.reveal();
+ } else {
+ this.dissolve();
+ }
+ return this;
+ },
+
+ cancel: function(){
+ this.parent.apply(this, arguments);
+ if (this.cssText != null) this.element.style.cssText = this.cssText;
+ this.hiding = false;
+ this.showing = false;
+ return this;
+ }
+
+});
+
+Element.Properties.reveal = {
+
+ set: function(options){
+ this.get('reveal').cancel().setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var reveal = this.retrieve('reveal');
+ if (!reveal){
+ reveal = new Fx.Reveal(this);
+ this.store('reveal', reveal);
+ }
+ return reveal;
+ }
+
+};
+
+Element.Properties.dissolve = Element.Properties.reveal;
+
+Element.implement({
+
+ reveal: function(options){
+ this.get('reveal').setOptions(options).reveal();
+ return this;
+ },
+
+ dissolve: function(options){
+ this.get('reveal').setOptions(options).dissolve();
+ return this;
+ },
+
+ nix: function(options){
+ var params = Array.link(arguments, {destroy: Type.isBoolean, options: Type.isObject});
+ this.get('reveal').setOptions(options).dissolve().chain(function(){
+ this[params.destroy ? 'destroy' : 'dispose']();
+ }.bind(this));
+ return this;
+ },
+
+ wink: function(){
+ var params = Array.link(arguments, {duration: Type.isNumber, options: Type.isObject});
+ var reveal = this.get('reveal').setOptions(params.options);
+ reveal.reveal().chain(function(){
+ (function(){
+ reveal.dissolve();
+ }).delay(params.duration || 2000);
+ });
+ }
+
+});
+
+})();
+
+
+/*
+---
+
+script: Drag.js
+
+name: Drag
+
+description: The base Drag Class. Can be used to drag and resize Elements using mouse events.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+ - Tom Occhinno
+ - Jan Kassens
+
+requires:
+ - Core/Events
+ - Core/Options
+ - Core/Element.Event
+ - Core/Element.Style
+ - Core/Element.Dimensions
+ - MooTools.More
+
+provides: [Drag]
+...
+
+*/
+
+var Drag = new Class({
+
+ Implements: [Events, Options],
+
+ options: {/*
+ onBeforeStart: function(thisElement){},
+ onStart: function(thisElement, event){},
+ onSnap: function(thisElement){},
+ onDrag: function(thisElement, event){},
+ onCancel: function(thisElement){},
+ onComplete: function(thisElement, event){},*/
+ snap: 6,
+ unit: 'px',
+ grid: false,
+ style: true,
+ limit: false,
+ handle: false,
+ invert: false,
+ preventDefault: false,
+ stopPropagation: false,
+ modifiers: {x: 'left', y: 'top'}
+ },
+
+ initialize: function(){
+ var params = Array.link(arguments, {
+ 'options': Type.isObject,
+ 'element': function(obj){
+ return obj != null;
+ }
+ });
+
+ this.element = document.id(params.element);
+ this.document = this.element.getDocument();
+ this.setOptions(params.options || {});
+ var htype = typeOf(this.options.handle);
+ this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
+ this.mouse = {'now': {}, 'pos': {}};
+ this.value = {'start': {}, 'now': {}};
+
+ this.selection = 'selectstart' in document ? 'selectstart' : 'mousedown';
+
+
+ if ('ondragstart' in document && !('FileReader' in window) && !Drag.ondragstartFixed){
+ document.ondragstart = Function.from(false);
+ Drag.ondragstartFixed = true;
+ }
+
+ this.bound = {
+ start: this.start.bind(this),
+ check: this.check.bind(this),
+ drag: this.drag.bind(this),
+ stop: this.stop.bind(this),
+ cancel: this.cancel.bind(this),
+ eventStop: Function.from(false)
+ };
+ this.attach();
+ },
+
+ attach: function(){
+ this.handles.addEvent('mousedown', this.bound.start);
+ return this;
+ },
+
+ detach: function(){
+ this.handles.removeEvent('mousedown', this.bound.start);
+ return this;
+ },
+
+ start: function(event){
+ var options = this.options;
+
+ if (event.rightClick) return;
+
+ if (options.preventDefault) event.preventDefault();
+ if (options.stopPropagation) event.stopPropagation();
+ this.mouse.start = event.page;
+
+ this.fireEvent('beforeStart', this.element);
+
+ var limit = options.limit;
+ this.limit = {x: [], y: []};
+
+ var z, coordinates;
+ for (z in options.modifiers){
+ if (!options.modifiers[z]) continue;
+
+ var style = this.element.getStyle(options.modifiers[z]);
+
+ // Some browsers (IE and Opera) don't always return pixels.
+ if (style && !style.match(/px$/)){
+ if (!coordinates) coordinates = this.element.getCoordinates(this.element.getOffsetParent());
+ style = coordinates[options.modifiers[z]];
+ }
+
+ if (options.style) this.value.now[z] = (style || 0).toInt();
+ else this.value.now[z] = this.element[options.modifiers[z]];
+
+ if (options.invert) this.value.now[z] *= -1;
+
+ this.mouse.pos[z] = event.page[z] - this.value.now[z];
+
+ if (limit && limit[z]){
+ var i = 2;
+ while (i--){
+ var limitZI = limit[z][i];
+ if (limitZI || limitZI === 0) this.limit[z][i] = (typeof limitZI == 'function') ? limitZI() : limitZI;
+ }
+ }
+ }
+
+ if (typeOf(this.options.grid) == 'number') this.options.grid = {
+ x: this.options.grid,
+ y: this.options.grid
+ };
+
+ var events = {
+ mousemove: this.bound.check,
+ mouseup: this.bound.cancel
+ };
+ events[this.selection] = this.bound.eventStop;
+ this.document.addEvents(events);
+ },
+
+ check: function(event){
+ if (this.options.preventDefault) event.preventDefault();
+ var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
+ if (distance > this.options.snap){
+ this.cancel();
+ this.document.addEvents({
+ mousemove: this.bound.drag,
+ mouseup: this.bound.stop
+ });
+ this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
+ }
+ },
+
+ drag: function(event){
+ var options = this.options;
+
+ if (options.preventDefault) event.preventDefault();
+ this.mouse.now = event.page;
+
+ for (var z in options.modifiers){
+ if (!options.modifiers[z]) continue;
+ this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
+
+ if (options.invert) this.value.now[z] *= -1;
+
+ if (options.limit && this.limit[z]){
+ if ((this.limit[z][1] || this.limit[z][1] === 0) && (this.value.now[z] > this.limit[z][1])){
+ this.value.now[z] = this.limit[z][1];
+ } else if ((this.limit[z][0] || this.limit[z][0] === 0) && (this.value.now[z] < this.limit[z][0])){
+ this.value.now[z] = this.limit[z][0];
+ }
+ }
+
+ if (options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % options.grid[z]);
+
+ if (options.style) this.element.setStyle(options.modifiers[z], this.value.now[z] + options.unit);
+ else this.element[options.modifiers[z]] = this.value.now[z];
+ }
+
+ this.fireEvent('drag', [this.element, event]);
+ },
+
+ cancel: function(event){
+ this.document.removeEvents({
+ mousemove: this.bound.check,
+ mouseup: this.bound.cancel
+ });
+ if (event){
+ this.document.removeEvent(this.selection, this.bound.eventStop);
+ this.fireEvent('cancel', this.element);
+ }
+ },
+
+ stop: function(event){
+ var events = {
+ mousemove: this.bound.drag,
+ mouseup: this.bound.stop
+ };
+ events[this.selection] = this.bound.eventStop;
+ this.document.removeEvents(events);
+ if (event) this.fireEvent('complete', [this.element, event]);
+ }
+
+});
+
+Element.implement({
+
+ makeResizable: function(options){
+ var drag = new Drag(this, Object.merge({
+ modifiers: {
+ x: 'width',
+ y: 'height'
+ }
+ }, options));
+
+ this.store('resizer', drag);
+ return drag.addEvent('drag', function(){
+ this.fireEvent('resize', drag);
+ }.bind(this));
+ }
+
+});
+
+
+/*
+---
+
+script: Drag.Move.js
+
+name: Drag.Move
+
+description: A Drag extension that provides support for the constraining of draggables to containers and droppables.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+ - Tom Occhinno
+ - Jan Kassens
+ - Aaron Newton
+ - Scott Kyle
+
+requires:
+ - Core/Element.Dimensions
+ - Drag
+
+provides: [Drag.Move]
+
+...
+*/
+
+Drag.Move = new Class({
+
+ Extends: Drag,
+
+ options: {/*
+ onEnter: function(thisElement, overed){},
+ onLeave: function(thisElement, overed){},
+ onDrop: function(thisElement, overed, event){},*/
+ droppables: [],
+ container: false,
+ precalculate: false,
+ includeMargins: true,
+ checkDroppables: true
+ },
+
+ initialize: function(element, options){
+ this.parent(element, options);
+ element = this.element;
+
+ this.droppables = $$(this.options.droppables);
+ this.setContainer(this.options.container);
+
+ if (this.options.style){
+ if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){
+ var parent = element.getOffsetParent(),
+ styles = element.getStyles('left', 'top');
+ if (parent && (styles.left == 'auto' || styles.top == 'auto')){
+ element.setPosition(element.getPosition(parent));
+ }
+ }
+
+ if (element.getStyle('position') == 'static') element.setStyle('position', 'absolute');
+ }
+
+ this.addEvent('start', this.checkDroppables, true);
+ this.overed = null;
+ },
+
+ setContainer: function(container) {
+ this.container = document.id(container);
+ if (this.container && typeOf(this.container) != 'element'){
+ this.container = document.id(this.container.getDocument().body);
+ }
+ },
+
+ start: function(event){
+ if (this.container) this.options.limit = this.calculateLimit();
+
+ if (this.options.precalculate){
+ this.positions = this.droppables.map(function(el){
+ return el.getCoordinates();
+ });
+ }
+
+ this.parent(event);
+ },
+
+ calculateLimit: function(){
+ var element = this.element,
+ container = this.container,
+
+ offsetParent = document.id(element.getOffsetParent()) || document.body,
+ containerCoordinates = container.getCoordinates(offsetParent),
+ elementMargin = {},
+ elementBorder = {},
+ containerMargin = {},
+ containerBorder = {},
+ offsetParentPadding = {};
+
+ ['top', 'right', 'bottom', 'left'].each(function(pad){
+ elementMargin[pad] = element.getStyle('margin-' + pad).toInt();
+ elementBorder[pad] = element.getStyle('border-' + pad).toInt();
+ containerMargin[pad] = container.getStyle('margin-' + pad).toInt();
+ containerBorder[pad] = container.getStyle('border-' + pad).toInt();
+ offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
+ }, this);
+
+ var width = element.offsetWidth + elementMargin.left + elementMargin.right,
+ height = element.offsetHeight + elementMargin.top + elementMargin.bottom,
+ left = 0,
+ top = 0,
+ right = containerCoordinates.right - containerBorder.right - width,
+ bottom = containerCoordinates.bottom - containerBorder.bottom - height;
+
+ if (this.options.includeMargins){
+ left += elementMargin.left;
+ top += elementMargin.top;
+ } else {
+ right += elementMargin.right;
+ bottom += elementMargin.bottom;
+ }
+
+ if (element.getStyle('position') == 'relative'){
+ var coords = element.getCoordinates(offsetParent);
+ coords.left -= element.getStyle('left').toInt();
+ coords.top -= element.getStyle('top').toInt();
+
+ left -= coords.left;
+ top -= coords.top;
+ if (container.getStyle('position') != 'relative'){
+ left += containerBorder.left;
+ top += containerBorder.top;
+ }
+ right += elementMargin.left - coords.left;
+ bottom += elementMargin.top - coords.top;
+
+ if (container != offsetParent){
+ left += containerMargin.left + offsetParentPadding.left;
+ if (!offsetParentPadding.left && left < 0) left = 0;
+ top += offsetParent == document.body ? 0 : containerMargin.top + offsetParentPadding.top;
+ if (!offsetParentPadding.top && top < 0) top = 0;
+ }
+ } else {
+ left -= elementMargin.left;
+ top -= elementMargin.top;
+ if (container != offsetParent){
+ left += containerCoordinates.left + containerBorder.left;
+ top += containerCoordinates.top + containerBorder.top;
+ }
+ }
+
+ return {
+ x: [left, right],
+ y: [top, bottom]
+ };
+ },
+
+ getDroppableCoordinates: function(element){
+ var position = element.getCoordinates();
+ if (element.getStyle('position') == 'fixed'){
+ var scroll = window.getScroll();
+ position.left += scroll.x;
+ position.right += scroll.x;
+ position.top += scroll.y;
+ position.bottom += scroll.y;
+ }
+ return position;
+ },
+
+ checkDroppables: function(){
+ var overed = this.droppables.filter(function(el, i){
+ el = this.positions ? this.positions[i] : this.getDroppableCoordinates(el);
+ var now = this.mouse.now;
+ return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
+ }, this).getLast();
+
+ if (this.overed != overed){
+ if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
+ if (overed) this.fireEvent('enter', [this.element, overed]);
+ this.overed = overed;
+ }
+ },
+
+ drag: function(event){
+ this.parent(event);
+ if (this.options.checkDroppables && this.droppables.length) this.checkDroppables();
+ },
+
+ stop: function(event){
+ this.checkDroppables();
+ this.fireEvent('drop', [this.element, this.overed, event]);
+ this.overed = null;
+ return this.parent(event);
+ }
+
+});
+
+Element.implement({
+
+ makeDraggable: function(options){
+ var drag = new Drag.Move(this, options);
+ this.store('dragger', drag);
+ return drag;
+ }
+
+});
+
+
+/*
+---
+
+script: Sortables.js
+
+name: Sortables
+
+description: Class for creating a drag and drop sorting interface for lists of items.
+
+license: MIT-style license
+
+authors:
+ - Tom Occhino
+
+requires:
+ - Core/Fx.Morph
+ - Drag.Move
+
+provides: [Sortables]
+
+...
+*/
+
+var Sortables = new Class({
+
+ Implements: [Events, Options],
+
+ options: {/*
+ onSort: function(element, clone){},
+ onStart: function(element, clone){},
+ onComplete: function(element){},*/
+ opacity: 1,
+ clone: false,
+ revert: false,
+ handle: false,
+ dragOptions: {},
+ unDraggableTags: ['button', 'input', 'a', 'textarea', 'select', 'option']
+ },
+
+ initialize: function(lists, options){
+ this.setOptions(options);
+
+ this.elements = [];
+ this.lists = [];
+ this.idle = true;
+
+ this.addLists($$(document.id(lists) || lists));
+
+ if (!this.options.clone) this.options.revert = false;
+ if (this.options.revert) this.effect = new Fx.Morph(null, Object.merge({
+ duration: 250,
+ link: 'cancel'
+ }, this.options.revert));
+ },
+
+ attach: function(){
+ this.addLists(this.lists);
+ return this;
+ },
+
+ detach: function(){
+ this.lists = this.removeLists(this.lists);
+ return this;
+ },
+
+ addItems: function(){
+ Array.flatten(arguments).each(function(element){
+ this.elements.push(element);
+ var start = element.retrieve('sortables:start', function(event){
+ this.start.call(this, event, element);
+ }.bind(this));
+ (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
+ }, this);
+ return this;
+ },
+
+ addLists: function(){
+ Array.flatten(arguments).each(function(list){
+ this.lists.include(list);
+ this.addItems(list.getChildren());
+ }, this);
+ return this;
+ },
+
+ removeItems: function(){
+ return $$(Array.flatten(arguments).map(function(element){
+ this.elements.erase(element);
+ var start = element.retrieve('sortables:start');
+ (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
+
+ return element;
+ }, this));
+ },
+
+ removeLists: function(){
+ return $$(Array.flatten(arguments).map(function(list){
+ this.lists.erase(list);
+ this.removeItems(list.getChildren());
+
+ return list;
+ }, this));
+ },
+
+ getDroppableCoordinates: function (element){
+ var offsetParent = element.getOffsetParent();
+ var position = element.getPosition(offsetParent);
+ var scroll = {
+ w: window.getScroll(),
+ offsetParent: offsetParent.getScroll()
+ };
+ position.x += scroll.offsetParent.x;
+ position.y += scroll.offsetParent.y;
+
+ if (offsetParent.getStyle('position') == 'fixed'){
+ position.x -= scroll.w.x;
+ position.y -= scroll.w.y;
+ }
+
+ return position;
+ },
+
+ getClone: function(event, element){
+ if (!this.options.clone) return new Element(element.tagName).inject(document.body);
+ if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
+ var clone = element.clone(true).setStyles({
+ margin: 0,
+ position: 'absolute',
+ visibility: 'hidden',
+ width: element.getStyle('width')
+ }).addEvent('mousedown', function(event){
+ element.fireEvent('mousedown', event);
+ });
+ //prevent the duplicated radio inputs from unchecking the real one
+ if (clone.get('html').test('radio')){
+ clone.getElements('input[type=radio]').each(function(input, i){
+ input.set('name', 'clone_' + i);
+ if (input.get('checked')) element.getElements('input[type=radio]')[i].set('checked', true);
+ });
+ }
+
+ return clone.inject(this.list).setPosition(this.getDroppableCoordinates(this.element));
+ },
+
+ getDroppables: function(){
+ var droppables = this.list.getChildren().erase(this.clone).erase(this.element);
+ if (!this.options.constrain) droppables.append(this.lists).erase(this.list);
+ return droppables;
+ },
+
+ insert: function(dragging, element){
+ var where = 'inside';
+ if (this.lists.contains(element)){
+ this.list = element;
+ this.drag.droppables = this.getDroppables();
+ } else {
+ where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
+ }
+ this.element.inject(element, where);
+ this.fireEvent('sort', [this.element, this.clone]);
+ },
+
+ start: function(event, element){
+ if (
+ !this.idle ||
+ event.rightClick ||
+ (!this.options.handle && this.options.unDraggableTags.contains(event.target.get('tag')))
+ ) return;
+
+ this.idle = false;
+ this.element = element;
+ this.opacity = element.getStyle('opacity');
+ this.list = element.getParent();
+ this.clone = this.getClone(event, element);
+
+ this.drag = new Drag.Move(this.clone, Object.merge({
+
+ droppables: this.getDroppables()
+ }, this.options.dragOptions)).addEvents({
+ onSnap: function(){
+ event.stop();
+ this.clone.setStyle('visibility', 'visible');
+ this.element.setStyle('opacity', this.options.opacity || 0);
+ this.fireEvent('start', [this.element, this.clone]);
+ }.bind(this),
+ onEnter: this.insert.bind(this),
+ onCancel: this.end.bind(this),
+ onComplete: this.end.bind(this)
+ });
+
+ this.clone.inject(this.element, 'before');
+ this.drag.start(event);
+ },
+
+ end: function(){
+ this.drag.detach();
+ this.element.setStyle('opacity', this.opacity);
+ var self = this;
+ if (this.effect){
+ var dim = this.element.getStyles('width', 'height'),
+ clone = this.clone,
+ pos = clone.computePosition(this.getDroppableCoordinates(clone));
+
+ var destroy = function(){
+ this.removeEvent('cancel', destroy);
+ clone.destroy();
+ self.reset();
+ };
+
+ this.effect.element = clone;
+ this.effect.start({
+ top: pos.top,
+ left: pos.left,
+ width: dim.width,
+ height: dim.height,
+ opacity: 0.25
+ }).addEvent('cancel', destroy).chain(destroy);
+ } else {
+ this.clone.destroy();
+ self.reset();
+ }
+
+ },
+
+ reset: function(){
+ this.idle = true;
+ this.fireEvent('complete', this.element);
+ },
+
+ serialize: function(){
+ var params = Array.link(arguments, {
+ modifier: Type.isFunction,
+ index: function(obj){
+ return obj != null;
+ }
+ });
+ var serial = this.lists.map(function(list){
+ return list.getChildren().map(params.modifier || function(element){
+ return element.get('id');
+ }, this);
+ }, this);
+
+ var index = params.index;
+ if (this.lists.length == 1) index = 0;
+ return (index || index === 0) && index >= 0 && index < this.lists.length ? serial[index] : serial;
+ }
+
+});
+
+
+/*
+---
+
+script: Request.Periodical.js
+
+name: Request.Periodical
+
+description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load
+
+license: MIT-style license
+
+authors:
+ - Christoph Pojer
+
+requires:
+ - Core/Request
+ - MooTools.More
+
+provides: [Request.Periodical]
+
+...
+*/
+
+Request.implement({
+
+ options: {
+ initialDelay: 5000,
+ delay: 5000,
+ limit: 60000
+ },
+
+ startTimer: function(data){
+ var fn = function(){
+ if (!this.running) this.send({data: data});
+ };
+ this.lastDelay = this.options.initialDelay;
+ this.timer = fn.delay(this.lastDelay, this);
+ this.completeCheck = function(response){
+ clearTimeout(this.timer);
+ this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit);
+ this.timer = fn.delay(this.lastDelay, this);
+ };
+ return this.addEvent('complete', this.completeCheck);
+ },
+
+ stopTimer: function(){
+ clearTimeout(this.timer);
+ return this.removeEvent('complete', this.completeCheck);
+ }
+
+});
+
+
+/*
+---
+
+script: Color.js
+
+name: Color
+
+description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - Core/Array
+ - Core/String
+ - Core/Number
+ - Core/Hash
+ - Core/Function
+ - MooTools.More
+
+provides: [Color]
+
+...
+*/
+
+(function(){
+
+var Color = this.Color = new Type('Color', function(color, type){
+ if (arguments.length >= 3){
+ type = 'rgb'; color = Array.slice(arguments, 0, 3);
+ } else if (typeof color == 'string'){
+ if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
+ else if (color.match(/hsb/)) color = color.hsbToRgb();
+ else color = color.hexToRgb(true);
+ }
+ type = type || 'rgb';
+ switch (type){
+ case 'hsb':
+ var old = color;
+ color = color.hsbToRgb();
+ color.hsb = old;
+ break;
+ case 'hex': color = color.hexToRgb(true); break;
+ }
+ color.rgb = color.slice(0, 3);
+ color.hsb = color.hsb || color.rgbToHsb();
+ color.hex = color.rgbToHex();
+ return Object.append(color, this);
+});
+
+Color.implement({
+
+ mix: function(){
+ var colors = Array.slice(arguments);
+ var alpha = (typeOf(colors.getLast()) == 'number') ? colors.pop() : 50;
+ var rgb = this.slice();
+ colors.each(function(color){
+ color = new Color(color);
+ for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
+ });
+ return new Color(rgb, 'rgb');
+ },
+
+ invert: function(){
+ return new Color(this.map(function(value){
+ return 255 - value;
+ }));
+ },
+
+ setHue: function(value){
+ return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
+ },
+
+ setSaturation: function(percent){
+ return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
+ },
+
+ setBrightness: function(percent){
+ return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
+ }
+
+});
+
+this.$RGB = function(r, g, b){
+ return new Color([r, g, b], 'rgb');
+};
+
+this.$HSB = function(h, s, b){
+ return new Color([h, s, b], 'hsb');
+};
+
+this.$HEX = function(hex){
+ return new Color(hex, 'hex');
+};
+
+Array.implement({
+
+ rgbToHsb: function(){
+ var red = this[0],
+ green = this[1],
+ blue = this[2],
+ hue = 0;
+ var max = Math.max(red, green, blue),
+ min = Math.min(red, green, blue);
+ var delta = max - min;
+ var brightness = max / 255,
+ saturation = (max != 0) ? delta / max : 0;
+ if (saturation != 0){
+ var rr = (max - red) / delta;
+ var gr = (max - green) / delta;
+ var br = (max - blue) / delta;
+ if (red == max) hue = br - gr;
+ else if (green == max) hue = 2 + rr - br;
+ else hue = 4 + gr - rr;
+ hue /= 6;
+ if (hue < 0) hue++;
+ }
+ return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
+ },
+
+ hsbToRgb: function(){
+ var br = Math.round(this[2] / 100 * 255);
+ if (this[1] == 0){
+ return [br, br, br];
+ } else {
+ var hue = this[0] % 360;
+ var f = hue % 60;
+ var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
+ var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
+ var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
+ switch (Math.floor(hue / 60)){
+ case 0: return [br, t, p];
+ case 1: return [q, br, p];
+ case 2: return [p, br, t];
+ case 3: return [p, q, br];
+ case 4: return [t, p, br];
+ case 5: return [br, p, q];
+ }
+ }
+ return false;
+ }
+
+});
+
+String.implement({
+
+ rgbToHsb: function(){
+ var rgb = this.match(/\d{1,3}/g);
+ return (rgb) ? rgb.rgbToHsb() : null;
+ },
+
+ hsbToRgb: function(){
+ var hsb = this.match(/\d{1,3}/g);
+ return (hsb) ? hsb.hsbToRgb() : null;
+ }
+
+});
+
+})();
+
+
diff --git a/module/webui/themes/Default/js/static/mootools-more.min.js b/module/webui/themes/Default/js/static/mootools-more.min.js
new file mode 100644
index 000000000..ce03a60fd
--- /dev/null
+++ b/module/webui/themes/Default/js/static/mootools-more.min.js
@@ -0,0 +1,226 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/more/c1cc18c2fff04bcc58921b4dff80a6f1
+
+packager build:
+ - packager build More/Form.Request More/Fx.Reveal More/Sortables More/Request.Periodical More/Color
+
+copyrights:
+ - [MooTools](http://mootools.net)
+
+licenses:
+ - [MIT License](http://mootools.net/license.txt)
+...
+*/
+
+MooTools.More={version:"1.5.0",build:"73db5e24e6e9c5c87b3a27aebef2248053f7db37"};Class.Mutators.Binds=function(a){if(!this.prototype.initialize){this.implement("initialize",function(){});
+}return Array.from(a).concat(this.prototype.Binds||[]);};Class.Mutators.initialize=function(a){return function(){Array.from(this.Binds).each(function(b){var c=this[b];
+if(c){this[b]=c.bind(this);}},this);return a.apply(this,arguments);};};Class.Occlude=new Class({occlude:function(c,b){b=document.id(b||this.element);var a=b.retrieve(c||this.property);
+if(a&&!this.occluded){return(this.occluded=a);}this.occluded=false;b.store(c||this.property,this);return this.occluded;}});Class.refactor=function(b,a){Object.each(a,function(e,d){var c=b.prototype[d];
+c=(c&&c.$origin)||c||function(){};b.implement(d,(typeof e=="function")?function(){var f=this.previous;this.previous=c;var g=e.apply(this,arguments);this.previous=f;
+return g;}:e);});return b;};(function(){var b=function(e,d){var f=[];Object.each(d,function(g){Object.each(g,function(h){e.each(function(i){f.push(i+"-"+h+(i=="border"?"-width":""));
+});});});return f;};var c=function(f,e){var d=0;Object.each(e,function(h,g){if(g.test(f)){d=d+h.toInt();}});return d;};var a=function(d){return !!(!d||d.offsetHeight||d.offsetWidth);
+};Element.implement({measure:function(h){if(a(this)){return h.call(this);}var g=this.getParent(),e=[];while(!a(g)&&g!=document.body){e.push(g.expose());
+g=g.getParent();}var f=this.expose(),d=h.call(this);f();e.each(function(i){i();});return d;},expose:function(){if(this.getStyle("display")!="none"){return function(){};
+}var d=this.style.cssText;this.setStyles({display:"block",position:"absolute",visibility:"hidden"});return function(){this.style.cssText=d;}.bind(this);
+},getDimensions:function(d){d=Object.merge({computeSize:false},d);var i={x:0,y:0};var h=function(j,e){return(e.computeSize)?j.getComputedSize(e):j.getSize();
+};var f=this.getParent("body");if(f&&this.getStyle("display")=="none"){i=this.measure(function(){return h(this,d);});}else{if(f){try{i=h(this,d);}catch(g){}}}return Object.append(i,(i.x||i.x===0)?{width:i.x,height:i.y}:{x:i.width,y:i.height});
+},getComputedSize:function(d){d=Object.merge({styles:["padding","border"],planes:{height:["top","bottom"],width:["left","right"]},mode:"both"},d);var g={},e={width:0,height:0},f;
+if(d.mode=="vertical"){delete e.width;delete d.planes.width;}else{if(d.mode=="horizontal"){delete e.height;delete d.planes.height;}}b(d.styles,d.planes).each(function(h){g[h]=this.getStyle(h).toInt();
+},this);Object.each(d.planes,function(i,h){var k=h.capitalize(),j=this.getStyle(h);if(j=="auto"&&!f){f=this.getDimensions();}j=g[h]=(j=="auto")?f[h]:j.toInt();
+e["total"+k]=j;i.each(function(m){var l=c(m,g);e["computed"+m.capitalize()]=l;e["total"+k]+=l;});},this);return Object.append(e,g);}});})();(function(b){var a=Element.Position={options:{relativeTo:document.body,position:{x:"center",y:"center"},offset:{x:0,y:0}},getOptions:function(d,c){c=Object.merge({},a.options,c);
+a.setPositionOption(c);a.setEdgeOption(c);a.setOffsetOption(d,c);a.setDimensionsOption(d,c);return c;},setPositionOption:function(c){c.position=a.getCoordinateFromValue(c.position);
+},setEdgeOption:function(d){var c=a.getCoordinateFromValue(d.edge);d.edge=c?c:(d.position.x=="center"&&d.position.y=="center")?{x:"center",y:"center"}:{x:"left",y:"top"};
+},setOffsetOption:function(f,d){var c={x:0,y:0};var e={x:0,y:0};var g=f.measure(function(){return document.id(this.getOffsetParent());});if(!g||g==f.getDocument().body){return;
+}e=g.getScroll();c=g.measure(function(){var i=this.getPosition();if(this.getStyle("position")=="fixed"){var h=window.getScroll();i.x+=h.x;i.y+=h.y;}return i;
+});d.offset={parentPositioned:g!=document.id(d.relativeTo),x:d.offset.x-c.x+e.x,y:d.offset.y-c.y+e.y};},setDimensionsOption:function(d,c){c.dimensions=d.getDimensions({computeSize:true,styles:["padding","border","margin"]});
+},getPosition:function(e,d){var c={};d=a.getOptions(e,d);var f=document.id(d.relativeTo)||document.body;a.setPositionCoordinates(d,c,f);if(d.edge){a.toEdge(c,d);
+}var g=d.offset;c.left=((c.x>=0||g.parentPositioned||d.allowNegative)?c.x:0).toInt();c.top=((c.y>=0||g.parentPositioned||d.allowNegative)?c.y:0).toInt();
+a.toMinMax(c,d);if(d.relFixedPosition||f.getStyle("position")=="fixed"){a.toRelFixedPosition(f,c);}if(d.ignoreScroll){a.toIgnoreScroll(f,c);}if(d.ignoreMargins){a.toIgnoreMargins(c,d);
+}c.left=Math.ceil(c.left);c.top=Math.ceil(c.top);delete c.x;delete c.y;return c;},setPositionCoordinates:function(k,g,d){var f=k.offset.y,h=k.offset.x,e=(d==document.body)?window.getScroll():d.getPosition(),j=e.y,c=e.x,i=window.getSize();
+switch(k.position.x){case"left":g.x=c+h;break;case"right":g.x=c+h+d.offsetWidth;break;default:g.x=c+((d==document.body?i.x:d.offsetWidth)/2)+h;break;}switch(k.position.y){case"top":g.y=j+f;
+break;case"bottom":g.y=j+f+d.offsetHeight;break;default:g.y=j+((d==document.body?i.y:d.offsetHeight)/2)+f;break;}},toMinMax:function(c,d){var f={left:"x",top:"y"},e;
+["minimum","maximum"].each(function(g){["left","top"].each(function(h){e=d[g]?d[g][f[h]]:null;if(e!=null&&((g=="minimum")?c[h]<e:c[h]>e)){c[h]=e;}});});
+},toRelFixedPosition:function(e,c){var d=window.getScroll();c.top+=d.y;c.left+=d.x;},toIgnoreScroll:function(e,d){var c=e.getScroll();d.top-=c.y;d.left-=c.x;
+},toIgnoreMargins:function(c,d){c.left+=d.edge.x=="right"?d.dimensions["margin-right"]:(d.edge.x!="center"?-d.dimensions["margin-left"]:-d.dimensions["margin-left"]+((d.dimensions["margin-right"]+d.dimensions["margin-left"])/2));
+c.top+=d.edge.y=="bottom"?d.dimensions["margin-bottom"]:(d.edge.y!="center"?-d.dimensions["margin-top"]:-d.dimensions["margin-top"]+((d.dimensions["margin-bottom"]+d.dimensions["margin-top"])/2));
+},toEdge:function(c,d){var e={},g=d.dimensions,f=d.edge;switch(f.x){case"left":e.x=0;break;case"right":e.x=-g.x-g.computedRight-g.computedLeft;break;default:e.x=-(Math.round(g.totalWidth/2));
+break;}switch(f.y){case"top":e.y=0;break;case"bottom":e.y=-g.y-g.computedTop-g.computedBottom;break;default:e.y=-(Math.round(g.totalHeight/2));break;}c.x+=e.x;
+c.y+=e.y;},getCoordinateFromValue:function(c){if(typeOf(c)!="string"){return c;}c=c.toLowerCase();return{x:c.test("left")?"left":(c.test("right")?"right":"center"),y:c.test(/upper|top/)?"top":(c.test("bottom")?"bottom":"center")};
+}};Element.implement({position:function(d){if(d&&(d.x!=null||d.y!=null)){return(b?b.apply(this,arguments):this);}var c=this.setStyle("position","absolute").calculatePosition(d);
+return(d&&d.returnPos)?c:this.setStyles(c);},calculatePosition:function(c){return a.getPosition(this,c);}});})(Element.prototype.position);(function(){var a=false;
+this.IframeShim=new Class({Implements:[Options,Events,Class.Occlude],options:{className:"iframeShim",src:'javascript:false;document.write("");',display:false,zIndex:null,margin:0,offset:{x:0,y:0},browsers:a},property:"IframeShim",initialize:function(c,b){this.element=document.id(c);
+if(this.occlude()){return this.occluded;}this.setOptions(b);this.makeShim();return this;},makeShim:function(){if(this.options.browsers){var d=this.element.getStyle("zIndex").toInt();
+if(!d){d=1;var c=this.element.getStyle("position");if(c=="static"||!c){this.element.setStyle("position","relative");}this.element.setStyle("zIndex",d);
+}d=((this.options.zIndex!=null||this.options.zIndex===0)&&d>this.options.zIndex)?this.options.zIndex:d-1;if(d<0){d=1;}this.shim=new Element("iframe",{src:this.options.src,scrolling:"no",frameborder:0,styles:{zIndex:d,position:"absolute",border:"none",filter:"progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)"},"class":this.options.className}).store("IframeShim",this);
+var b=(function(){this.shim.inject(this.element,"after");this[this.options.display?"show":"hide"]();this.fireEvent("inject");}).bind(this);if(!IframeShim.ready){window.addEvent("load",b);
+}else{b();}}else{this.position=this.hide=this.show=this.dispose=Function.from(this);}},position:function(){if(!IframeShim.ready||!this.shim){return this;
+}var b=this.element.measure(function(){return this.getSize();});if(this.options.margin!=undefined){b.x=b.x-(this.options.margin*2);b.y=b.y-(this.options.margin*2);
+this.options.offset.x+=this.options.margin;this.options.offset.y+=this.options.margin;}this.shim.set({width:b.x,height:b.y}).position({relativeTo:this.element,offset:this.options.offset});
+return this;},hide:function(){if(this.shim){this.shim.setStyle("display","none");}return this;},show:function(){if(this.shim){this.shim.setStyle("display","block");
+}return this.position();},dispose:function(){if(this.shim){this.shim.dispose();}return this;},destroy:function(){if(this.shim){this.shim.destroy();}return this;
+}});})();window.addEvent("load",function(){IframeShim.ready=true;});var Mask=new Class({Implements:[Options,Events],Binds:["position"],options:{style:{},"class":"mask",maskMargins:false,useIframeShim:true,iframeShimOptions:{}},initialize:function(b,a){this.target=document.id(b)||document.id(document.body);
+this.target.store("mask",this);this.setOptions(a);this.render();this.inject();},render:function(){this.element=new Element("div",{"class":this.options["class"],id:this.options.id||"mask-"+String.uniqueID(),styles:Object.merge({},this.options.style,{display:"none"}),events:{click:function(a){this.fireEvent("click",a);
+if(this.options.hideOnClick){this.hide();}}.bind(this)}});this.hidden=true;},toElement:function(){return this.element;},inject:function(b,a){a=a||(this.options.inject?this.options.inject.where:"")||(this.target==document.body?"inside":"after");
+b=b||(this.options.inject&&this.options.inject.target)||this.target;this.element.inject(b,a);if(this.options.useIframeShim){this.shim=new IframeShim(this.element,this.options.iframeShimOptions);
+this.addEvents({show:this.shim.show.bind(this.shim),hide:this.shim.hide.bind(this.shim),destroy:this.shim.destroy.bind(this.shim)});}},position:function(){this.resize(this.options.width,this.options.height);
+this.element.position({relativeTo:this.target,position:"topLeft",ignoreMargins:!this.options.maskMargins,ignoreScroll:this.target==document.body});return this;
+},resize:function(a,e){var b={styles:["padding","border"]};if(this.options.maskMargins){b.styles.push("margin");}var d=this.target.getComputedSize(b);if(this.target==document.body){this.element.setStyles({width:0,height:0});
+var c=window.getScrollSize();if(d.totalHeight<c.y){d.totalHeight=c.y;}if(d.totalWidth<c.x){d.totalWidth=c.x;}}this.element.setStyles({width:Array.pick([a,d.totalWidth,d.x]),height:Array.pick([e,d.totalHeight,d.y])});
+return this;},show:function(){if(!this.hidden){return this;}window.addEvent("resize",this.position);this.position();this.showMask.apply(this,arguments);
+return this;},showMask:function(){this.element.setStyle("display","block");this.hidden=false;this.fireEvent("show");},hide:function(){if(this.hidden){return this;
+}window.removeEvent("resize",this.position);this.hideMask.apply(this,arguments);if(this.options.destroyOnHide){return this.destroy();}return this;},hideMask:function(){this.element.setStyle("display","none");
+this.hidden=true;this.fireEvent("hide");},toggle:function(){this[this.hidden?"show":"hide"]();},destroy:function(){this.hide();this.element.destroy();this.fireEvent("destroy");
+this.target.eliminate("mask");}});Element.Properties.mask={set:function(b){var a=this.retrieve("mask");if(a){a.destroy();}return this.eliminate("mask").store("mask:options",b);
+},get:function(){var a=this.retrieve("mask");if(!a){a=new Mask(this,this.retrieve("mask:options"));this.store("mask",a);}return a;}};Element.implement({mask:function(a){if(a){this.set("mask",a);
+}this.get("mask").show();return this;},unmask:function(){this.get("mask").hide();return this;}});var Spinner=new Class({Extends:Mask,Implements:Chain,options:{"class":"spinner",containerPosition:{},content:{"class":"spinner-content"},messageContainer:{"class":"spinner-msg"},img:{"class":"spinner-img"},fxOptions:{link:"chain"}},initialize:function(c,a){this.target=document.id(c)||document.id(document.body);
+this.target.store("spinner",this);this.setOptions(a);this.render();this.inject();var b=function(){this.active=false;}.bind(this);this.addEvents({hide:b,show:b});
+},render:function(){this.parent();this.element.set("id",this.options.id||"spinner-"+String.uniqueID());this.content=document.id(this.options.content)||new Element("div",this.options.content);
+this.content.inject(this.element);if(this.options.message){this.msg=document.id(this.options.message)||new Element("p",this.options.messageContainer).appendText(this.options.message);
+this.msg.inject(this.content);}if(this.options.img){this.img=document.id(this.options.img)||new Element("div",this.options.img);this.img.inject(this.content);
+}this.element.set("tween",this.options.fxOptions);},show:function(a){if(this.active){return this.chain(this.show.bind(this));}if(!this.hidden){this.callChain.delay(20,this);
+return this;}this.target.set("aria-busy","true");this.active=true;return this.parent(a);},showMask:function(a){var b=function(){this.content.position(Object.merge({relativeTo:this.element},this.options.containerPosition));
+}.bind(this);if(a){this.parent();b();}else{if(!this.options.style.opacity){this.options.style.opacity=this.element.getStyle("opacity").toFloat();}this.element.setStyles({display:"block",opacity:0}).tween("opacity",this.options.style.opacity);
+b();this.hidden=false;this.fireEvent("show");this.callChain();}},hide:function(a){if(this.active){return this.chain(this.hide.bind(this));}if(this.hidden){this.callChain.delay(20,this);
+return this;}this.target.set("aria-busy","false");this.active=true;return this.parent(a);},hideMask:function(a){if(a){return this.parent();}this.element.tween("opacity",0).get("tween").chain(function(){this.element.setStyle("display","none");
+this.hidden=true;this.fireEvent("hide");this.callChain();}.bind(this));},destroy:function(){this.content.destroy();this.parent();this.target.eliminate("spinner");
+}});Request=Class.refactor(Request,{options:{useSpinner:false,spinnerOptions:{},spinnerTarget:false},initialize:function(a){this._send=this.send;this.send=function(b){var c=this.getSpinner();
+if(c){c.chain(this._send.pass(b,this)).show();}else{this._send(b);}return this;};this.previous(a);},getSpinner:function(){if(!this.spinner){var b=document.id(this.options.spinnerTarget)||document.id(this.options.update);
+if(this.options.useSpinner&&b){b.set("spinner",this.options.spinnerOptions);var a=this.spinner=b.get("spinner");["complete","exception","cancel"].each(function(c){this.addEvent(c,a.hide.bind(a));
+},this);}}return this.spinner;}});Element.Properties.spinner={set:function(a){var b=this.retrieve("spinner");if(b){b.destroy();}return this.eliminate("spinner").store("spinner:options",a);
+},get:function(){var a=this.retrieve("spinner");if(!a){a=new Spinner(this,this.retrieve("spinner:options"));this.store("spinner",a);}return a;}};Element.implement({spin:function(a){if(a){this.set("spinner",a);
+}this.get("spinner").show();return this;},unspin:function(){this.get("spinner").hide();return this;}});String.implement({parseQueryString:function(d,a){if(d==null){d=true;
+}if(a==null){a=true;}var c=this.split(/[&;]/),b={};if(!c.length){return b;}c.each(function(i){var e=i.indexOf("=")+1,g=e?i.substr(e):"",f=e?i.substr(0,e-1).match(/([^\]\[]+|(\B)(?=\]))/g):[i],h=b;
+if(!f){return;}if(a){g=decodeURIComponent(g);}f.each(function(k,j){if(d){k=decodeURIComponent(k);}var l=h[k];if(j<f.length-1){h=h[k]=l||{};}else{if(typeOf(l)=="array"){l.push(g);
+}else{h[k]=l!=null?[l,g]:g;}}});});return b;},cleanQueryString:function(a){return this.split("&").filter(function(e){var b=e.indexOf("="),c=b<0?"":e.substr(0,b),d=e.substr(b+1);
+return a?a.call(null,c,d):(d||d===0);}).join("&");}});(function(){Events.Pseudos=function(h,e,f){var d="_monitorEvents:";var c=function(i){return{store:i.store?function(j,k){i.store(d+j,k);
+}:function(j,k){(i._monitorEvents||(i._monitorEvents={}))[j]=k;},retrieve:i.retrieve?function(j,k){return i.retrieve(d+j,k);}:function(j,k){if(!i._monitorEvents){return k;
+}return i._monitorEvents[j]||k;}};};var g=function(k){if(k.indexOf(":")==-1||!h){return null;}var j=Slick.parse(k).expressions[0][0],p=j.pseudos,i=p.length,o=[];
+while(i--){var n=p[i].key,m=h[n];if(m!=null){o.push({event:j.tag,value:p[i].value,pseudo:n,original:k,listener:m});}}return o.length?o:null;};return{addEvent:function(m,p,j){var n=g(m);
+if(!n){return e.call(this,m,p,j);}var k=c(this),r=k.retrieve(m,[]),i=n[0].event,l=Array.slice(arguments,2),o=p,q=this;n.each(function(s){var t=s.listener,u=o;
+if(t==false){i+=":"+s.pseudo+"("+s.value+")";}else{o=function(){t.call(q,s,u,arguments,o);};}});r.include({type:i,event:p,monitor:o});k.store(m,r);if(m!=i){e.apply(this,[m,p].concat(l));
+}return e.apply(this,[i,o].concat(l));},removeEvent:function(m,l){var k=g(m);if(!k){return f.call(this,m,l);}var n=c(this),j=n.retrieve(m);if(!j){return this;
+}var i=Array.slice(arguments,2);f.apply(this,[m,l].concat(i));j.each(function(o,p){if(!l||o.event==l){f.apply(this,[o.type,o.monitor].concat(i));}delete j[p];
+},this);n.store(m,j);return this;}};};var b={once:function(e,f,d,c){f.apply(this,d);this.removeEvent(e.event,c).removeEvent(e.original,f);},throttle:function(d,e,c){if(!e._throttled){e.apply(this,c);
+e._throttled=setTimeout(function(){e._throttled=false;},d.value||250);}},pause:function(d,e,c){clearTimeout(e._pause);e._pause=e.delay(d.value||250,this,c);
+}};Events.definePseudo=function(c,d){b[c]=d;return this;};Events.lookupPseudo=function(c){return b[c];};var a=Events.prototype;Events.implement(Events.Pseudos(b,a.addEvent,a.removeEvent));
+["Request","Fx"].each(function(c){if(this[c]){this[c].implement(Events.prototype);}});})();(function(){var d={relay:false},c=["once","throttle","pause"],b=c.length;
+while(b--){d[c[b]]=Events.lookupPseudo(c[b]);}DOMEvent.definePseudo=function(e,f){d[e]=f;return this;};var a=Element.prototype;[Element,Window,Document].invoke("implement",Events.Pseudos(d,a.addEvent,a.removeEvent));
+})();if(!window.Form){window.Form={};}(function(){Form.Request=new Class({Binds:["onSubmit","onFormValidate"],Implements:[Options,Events,Class.Occlude],options:{requestOptions:{evalScripts:true,useSpinner:true,emulation:false,link:"ignore"},sendButtonClicked:true,extraData:{},resetForm:true},property:"form.request",initialize:function(b,c,a){this.element=document.id(b);
+if(this.occlude()){return this.occluded;}this.setOptions(a).setTarget(c).attach();},setTarget:function(a){this.target=document.id(a);if(!this.request){this.makeRequest();
+}else{this.request.setOptions({update:this.target});}return this;},toElement:function(){return this.element;},makeRequest:function(){var a=this;this.request=new Request.HTML(Object.merge({update:this.target,emulation:false,spinnerTarget:this.element,method:this.element.get("method")||"post"},this.options.requestOptions)).addEvents({success:function(c,e,d,b){["complete","success"].each(function(f){a.fireEvent(f,[a.target,c,e,d,b]);
+});},failure:function(){a.fireEvent("complete",arguments).fireEvent("failure",arguments);},exception:function(){a.fireEvent("failure",arguments);}});return this.attachReset();
+},attachReset:function(){if(!this.options.resetForm){return this;}this.request.addEvent("success",function(){Function.attempt(function(){this.element.reset();
+}.bind(this));if(window.OverText){OverText.update();}}.bind(this));return this;},attach:function(a){var c=(a!=false)?"addEvent":"removeEvent";this.element[c]("click:relay(button, input[type=submit])",this.saveClickedButton.bind(this));
+var b=this.element.retrieve("validator");if(b){b[c]("onFormValidate",this.onFormValidate);}else{this.element[c]("submit",this.onSubmit);}return this;},detach:function(){return this.attach(false);
+},enable:function(){return this.attach();},disable:function(){return this.detach();},onFormValidate:function(c,b,a){if(!a){return;}var d=this.element.retrieve("validator");
+if(c||(d&&!d.options.stopOnFailure)){a.stop();this.send();}},onSubmit:function(a){var b=this.element.retrieve("validator");if(b){this.element.removeEvent("submit",this.onSubmit);
+b.addEvent("onFormValidate",this.onFormValidate);b.validate(a);return;}if(a){a.stop();}this.send();},saveClickedButton:function(b,c){var a=c.get("name");
+if(!a||!this.options.sendButtonClicked){return;}this.options.extraData[a]=c.get("value")||true;this.clickedCleaner=function(){delete this.options.extraData[a];
+this.clickedCleaner=function(){};}.bind(this);},clickedCleaner:function(){},send:function(){var b=this.element.toQueryString().trim(),a=Object.toQueryString(this.options.extraData);
+if(b){b+="&"+a;}else{b=a;}this.fireEvent("send",[this.element,b.parseQueryString()]);this.request.send({data:b,url:this.options.requestOptions.url||this.element.get("action")});
+this.clickedCleaner();return this;}});Element.implement("formUpdate",function(c,b){var a=this.retrieve("form.request");if(!a){a=new Form.Request(this,c,b);
+}else{if(c){a.setTarget(c);}if(b){a.setOptions(b).makeRequest();}}a.send();return this;});})();Element.implement({isDisplayed:function(){return this.getStyle("display")!="none";
+},isVisible:function(){var a=this.offsetWidth,b=this.offsetHeight;return(a==0&&b==0)?false:(a>0&&b>0)?true:this.style.display!="none";},toggle:function(){return this[this.isDisplayed()?"hide":"show"]();
+},hide:function(){var b;try{b=this.getStyle("display");}catch(a){}if(b=="none"){return this;}return this.store("element:_originalDisplay",b||"").setStyle("display","none");
+},show:function(a){if(!a&&this.isDisplayed()){return this;}a=a||this.retrieve("element:_originalDisplay")||"block";return this.setStyle("display",(a=="none")?"block":a);
+},swapClass:function(a,b){return this.removeClass(a).addClass(b);}});Document.implement({clearSelection:function(){if(window.getSelection){var a=window.getSelection();
+if(a&&a.removeAllRanges){a.removeAllRanges();}}else{if(document.selection&&document.selection.empty){try{document.selection.empty();}catch(b){}}}}});(function(){var a=function(d){var b=d.options.hideInputs;
+if(window.OverText){var c=[null];OverText.each(function(e){c.include("."+e.options.labelClass);});if(c){b+=c.join(", ");}}return(b)?d.element.getElements(b):null;
+};Fx.Reveal=new Class({Extends:Fx.Morph,options:{link:"cancel",styles:["padding","border","margin"],transitionOpacity:"opacity" in document.documentElement,mode:"vertical",display:function(){return this.element.get("tag")!="tr"?"block":"table-row";
+},opacity:1,hideInputs:!("opacity" in document.documentElement)?"select, input, textarea, object, embed":null},dissolve:function(){if(!this.hiding&&!this.showing){if(this.element.getStyle("display")!="none"){this.hiding=true;
+this.showing=false;this.hidden=true;this.cssText=this.element.style.cssText;var d=this.element.getComputedSize({styles:this.options.styles,mode:this.options.mode});
+if(this.options.transitionOpacity){d.opacity=this.options.opacity;}var c={};Object.each(d,function(f,e){c[e]=[f,0];});this.element.setStyles({display:Function.from(this.options.display).call(this),overflow:"hidden"});
+var b=a(this);if(b){b.setStyle("visibility","hidden");}this.$chain.unshift(function(){if(this.hidden){this.hiding=false;this.element.style.cssText=this.cssText;
+this.element.setStyle("display","none");if(b){b.setStyle("visibility","visible");}}this.fireEvent("hide",this.element);this.callChain();}.bind(this));this.start(c);
+}else{this.callChain.delay(10,this);this.fireEvent("complete",this.element);this.fireEvent("hide",this.element);}}else{if(this.options.link=="chain"){this.chain(this.dissolve.bind(this));
+}else{if(this.options.link=="cancel"&&!this.hiding){this.cancel();this.dissolve();}}}return this;},reveal:function(){if(!this.showing&&!this.hiding){if(this.element.getStyle("display")=="none"){this.hiding=false;
+this.showing=true;this.hidden=false;this.cssText=this.element.style.cssText;var d;this.element.measure(function(){d=this.element.getComputedSize({styles:this.options.styles,mode:this.options.mode});
+}.bind(this));if(this.options.heightOverride!=null){d.height=this.options.heightOverride.toInt();}if(this.options.widthOverride!=null){d.width=this.options.widthOverride.toInt();
+}if(this.options.transitionOpacity){this.element.setStyle("opacity",0);d.opacity=this.options.opacity;}var c={height:0,display:Function.from(this.options.display).call(this)};
+Object.each(d,function(f,e){c[e]=0;});c.overflow="hidden";this.element.setStyles(c);var b=a(this);if(b){b.setStyle("visibility","hidden");}this.$chain.unshift(function(){this.element.style.cssText=this.cssText;
+this.element.setStyle("display",Function.from(this.options.display).call(this));if(!this.hidden){this.showing=false;}if(b){b.setStyle("visibility","visible");
+}this.callChain();this.fireEvent("show",this.element);}.bind(this));this.start(d);}else{this.callChain();this.fireEvent("complete",this.element);this.fireEvent("show",this.element);
+}}else{if(this.options.link=="chain"){this.chain(this.reveal.bind(this));}else{if(this.options.link=="cancel"&&!this.showing){this.cancel();this.reveal();
+}}}return this;},toggle:function(){if(this.element.getStyle("display")=="none"){this.reveal();}else{this.dissolve();}return this;},cancel:function(){this.parent.apply(this,arguments);
+if(this.cssText!=null){this.element.style.cssText=this.cssText;}this.hiding=false;this.showing=false;return this;}});Element.Properties.reveal={set:function(b){this.get("reveal").cancel().setOptions(b);
+return this;},get:function(){var b=this.retrieve("reveal");if(!b){b=new Fx.Reveal(this);this.store("reveal",b);}return b;}};Element.Properties.dissolve=Element.Properties.reveal;
+Element.implement({reveal:function(b){this.get("reveal").setOptions(b).reveal();return this;},dissolve:function(b){this.get("reveal").setOptions(b).dissolve();
+return this;},nix:function(b){var c=Array.link(arguments,{destroy:Type.isBoolean,options:Type.isObject});this.get("reveal").setOptions(b).dissolve().chain(function(){this[c.destroy?"destroy":"dispose"]();
+}.bind(this));return this;},wink:function(){var c=Array.link(arguments,{duration:Type.isNumber,options:Type.isObject});var b=this.get("reveal").setOptions(c.options);
+b.reveal().chain(function(){(function(){b.dissolve();}).delay(c.duration||2000);});}});})();var Drag=new Class({Implements:[Events,Options],options:{snap:6,unit:"px",grid:false,style:true,limit:false,handle:false,invert:false,preventDefault:false,stopPropagation:false,modifiers:{x:"left",y:"top"}},initialize:function(){var b=Array.link(arguments,{options:Type.isObject,element:function(c){return c!=null;
+}});this.element=document.id(b.element);this.document=this.element.getDocument();this.setOptions(b.options||{});var a=typeOf(this.options.handle);this.handles=((a=="array"||a=="collection")?$$(this.options.handle):document.id(this.options.handle))||this.element;
+this.mouse={now:{},pos:{}};this.value={start:{},now:{}};this.selection="selectstart" in document?"selectstart":"mousedown";if("ondragstart" in document&&!("FileReader" in window)&&!Drag.ondragstartFixed){document.ondragstart=Function.from(false);
+Drag.ondragstartFixed=true;}this.bound={start:this.start.bind(this),check:this.check.bind(this),drag:this.drag.bind(this),stop:this.stop.bind(this),cancel:this.cancel.bind(this),eventStop:Function.from(false)};
+this.attach();},attach:function(){this.handles.addEvent("mousedown",this.bound.start);return this;},detach:function(){this.handles.removeEvent("mousedown",this.bound.start);
+return this;},start:function(a){var j=this.options;if(a.rightClick){return;}if(j.preventDefault){a.preventDefault();}if(j.stopPropagation){a.stopPropagation();
+}this.mouse.start=a.page;this.fireEvent("beforeStart",this.element);var c=j.limit;this.limit={x:[],y:[]};var e,g;for(e in j.modifiers){if(!j.modifiers[e]){continue;
+}var b=this.element.getStyle(j.modifiers[e]);if(b&&!b.match(/px$/)){if(!g){g=this.element.getCoordinates(this.element.getOffsetParent());}b=g[j.modifiers[e]];
+}if(j.style){this.value.now[e]=(b||0).toInt();}else{this.value.now[e]=this.element[j.modifiers[e]];}if(j.invert){this.value.now[e]*=-1;}this.mouse.pos[e]=a.page[e]-this.value.now[e];
+if(c&&c[e]){var d=2;while(d--){var f=c[e][d];if(f||f===0){this.limit[e][d]=(typeof f=="function")?f():f;}}}}if(typeOf(this.options.grid)=="number"){this.options.grid={x:this.options.grid,y:this.options.grid};
+}var h={mousemove:this.bound.check,mouseup:this.bound.cancel};h[this.selection]=this.bound.eventStop;this.document.addEvents(h);},check:function(a){if(this.options.preventDefault){a.preventDefault();
+}var b=Math.round(Math.sqrt(Math.pow(a.page.x-this.mouse.start.x,2)+Math.pow(a.page.y-this.mouse.start.y,2)));if(b>this.options.snap){this.cancel();this.document.addEvents({mousemove:this.bound.drag,mouseup:this.bound.stop});
+this.fireEvent("start",[this.element,a]).fireEvent("snap",this.element);}},drag:function(b){var a=this.options;if(a.preventDefault){b.preventDefault();
+}this.mouse.now=b.page;for(var c in a.modifiers){if(!a.modifiers[c]){continue;}this.value.now[c]=this.mouse.now[c]-this.mouse.pos[c];if(a.invert){this.value.now[c]*=-1;
+}if(a.limit&&this.limit[c]){if((this.limit[c][1]||this.limit[c][1]===0)&&(this.value.now[c]>this.limit[c][1])){this.value.now[c]=this.limit[c][1];}else{if((this.limit[c][0]||this.limit[c][0]===0)&&(this.value.now[c]<this.limit[c][0])){this.value.now[c]=this.limit[c][0];
+}}}if(a.grid[c]){this.value.now[c]-=((this.value.now[c]-(this.limit[c][0]||0))%a.grid[c]);}if(a.style){this.element.setStyle(a.modifiers[c],this.value.now[c]+a.unit);
+}else{this.element[a.modifiers[c]]=this.value.now[c];}}this.fireEvent("drag",[this.element,b]);},cancel:function(a){this.document.removeEvents({mousemove:this.bound.check,mouseup:this.bound.cancel});
+if(a){this.document.removeEvent(this.selection,this.bound.eventStop);this.fireEvent("cancel",this.element);}},stop:function(b){var a={mousemove:this.bound.drag,mouseup:this.bound.stop};
+a[this.selection]=this.bound.eventStop;this.document.removeEvents(a);if(b){this.fireEvent("complete",[this.element,b]);}}});Element.implement({makeResizable:function(a){var b=new Drag(this,Object.merge({modifiers:{x:"width",y:"height"}},a));
+this.store("resizer",b);return b.addEvent("drag",function(){this.fireEvent("resize",b);}.bind(this));}});Drag.Move=new Class({Extends:Drag,options:{droppables:[],container:false,precalculate:false,includeMargins:true,checkDroppables:true},initialize:function(b,a){this.parent(b,a);
+b=this.element;this.droppables=$$(this.options.droppables);this.setContainer(this.options.container);if(this.options.style){if(this.options.modifiers.x=="left"&&this.options.modifiers.y=="top"){var c=b.getOffsetParent(),d=b.getStyles("left","top");
+if(c&&(d.left=="auto"||d.top=="auto")){b.setPosition(b.getPosition(c));}}if(b.getStyle("position")=="static"){b.setStyle("position","absolute");}}this.addEvent("start",this.checkDroppables,true);
+this.overed=null;},setContainer:function(a){this.container=document.id(a);if(this.container&&typeOf(this.container)!="element"){this.container=document.id(this.container.getDocument().body);
+}},start:function(a){if(this.container){this.options.limit=this.calculateLimit();}if(this.options.precalculate){this.positions=this.droppables.map(function(b){return b.getCoordinates();
+});}this.parent(a);},calculateLimit:function(){var j=this.element,e=this.container,d=document.id(j.getOffsetParent())||document.body,h=e.getCoordinates(d),c={},b={},k={},g={},m={};
+["top","right","bottom","left"].each(function(q){c[q]=j.getStyle("margin-"+q).toInt();b[q]=j.getStyle("border-"+q).toInt();k[q]=e.getStyle("margin-"+q).toInt();
+g[q]=e.getStyle("border-"+q).toInt();m[q]=d.getStyle("padding-"+q).toInt();},this);var f=j.offsetWidth+c.left+c.right,p=j.offsetHeight+c.top+c.bottom,i=0,l=0,o=h.right-g.right-f,a=h.bottom-g.bottom-p;
+if(this.options.includeMargins){i+=c.left;l+=c.top;}else{o+=c.right;a+=c.bottom;}if(j.getStyle("position")=="relative"){var n=j.getCoordinates(d);n.left-=j.getStyle("left").toInt();
+n.top-=j.getStyle("top").toInt();i-=n.left;l-=n.top;if(e.getStyle("position")!="relative"){i+=g.left;l+=g.top;}o+=c.left-n.left;a+=c.top-n.top;if(e!=d){i+=k.left+m.left;
+if(!m.left&&i<0){i=0;}l+=d==document.body?0:k.top+m.top;if(!m.top&&l<0){l=0;}}}else{i-=c.left;l-=c.top;if(e!=d){i+=h.left+g.left;l+=h.top+g.top;}}return{x:[i,o],y:[l,a]};
+},getDroppableCoordinates:function(c){var b=c.getCoordinates();if(c.getStyle("position")=="fixed"){var a=window.getScroll();b.left+=a.x;b.right+=a.x;b.top+=a.y;
+b.bottom+=a.y;}return b;},checkDroppables:function(){var a=this.droppables.filter(function(d,c){d=this.positions?this.positions[c]:this.getDroppableCoordinates(d);
+var b=this.mouse.now;return(b.x>d.left&&b.x<d.right&&b.y<d.bottom&&b.y>d.top);},this).getLast();if(this.overed!=a){if(this.overed){this.fireEvent("leave",[this.element,this.overed]);
+}if(a){this.fireEvent("enter",[this.element,a]);}this.overed=a;}},drag:function(a){this.parent(a);if(this.options.checkDroppables&&this.droppables.length){this.checkDroppables();
+}},stop:function(a){this.checkDroppables();this.fireEvent("drop",[this.element,this.overed,a]);this.overed=null;return this.parent(a);}});Element.implement({makeDraggable:function(a){var b=new Drag.Move(this,a);
+this.store("dragger",b);return b;}});var Sortables=new Class({Implements:[Events,Options],options:{opacity:1,clone:false,revert:false,handle:false,dragOptions:{},unDraggableTags:["button","input","a","textarea","select","option"]},initialize:function(a,b){this.setOptions(b);
+this.elements=[];this.lists=[];this.idle=true;this.addLists($$(document.id(a)||a));if(!this.options.clone){this.options.revert=false;}if(this.options.revert){this.effect=new Fx.Morph(null,Object.merge({duration:250,link:"cancel"},this.options.revert));
+}},attach:function(){this.addLists(this.lists);return this;},detach:function(){this.lists=this.removeLists(this.lists);return this;},addItems:function(){Array.flatten(arguments).each(function(a){this.elements.push(a);
+var b=a.retrieve("sortables:start",function(c){this.start.call(this,c,a);}.bind(this));(this.options.handle?a.getElement(this.options.handle)||a:a).addEvent("mousedown",b);
+},this);return this;},addLists:function(){Array.flatten(arguments).each(function(a){this.lists.include(a);this.addItems(a.getChildren());},this);return this;
+},removeItems:function(){return $$(Array.flatten(arguments).map(function(a){this.elements.erase(a);var b=a.retrieve("sortables:start");(this.options.handle?a.getElement(this.options.handle)||a:a).removeEvent("mousedown",b);
+return a;},this));},removeLists:function(){return $$(Array.flatten(arguments).map(function(a){this.lists.erase(a);this.removeItems(a.getChildren());return a;
+},this));},getDroppableCoordinates:function(c){var d=c.getOffsetParent();var b=c.getPosition(d);var a={w:window.getScroll(),offsetParent:d.getScroll()};
+b.x+=a.offsetParent.x;b.y+=a.offsetParent.y;if(d.getStyle("position")=="fixed"){b.x-=a.w.x;b.y-=a.w.y;}return b;},getClone:function(b,a){if(!this.options.clone){return new Element(a.tagName).inject(document.body);
+}if(typeOf(this.options.clone)=="function"){return this.options.clone.call(this,b,a,this.list);}var c=a.clone(true).setStyles({margin:0,position:"absolute",visibility:"hidden",width:a.getStyle("width")}).addEvent("mousedown",function(d){a.fireEvent("mousedown",d);
+});if(c.get("html").test("radio")){c.getElements("input[type=radio]").each(function(d,e){d.set("name","clone_"+e);if(d.get("checked")){a.getElements("input[type=radio]")[e].set("checked",true);
+}});}return c.inject(this.list).setPosition(this.getDroppableCoordinates(this.element));},getDroppables:function(){var a=this.list.getChildren().erase(this.clone).erase(this.element);
+if(!this.options.constrain){a.append(this.lists).erase(this.list);}return a;},insert:function(c,b){var a="inside";if(this.lists.contains(b)){this.list=b;
+this.drag.droppables=this.getDroppables();}else{a=this.element.getAllPrevious().contains(b)?"before":"after";}this.element.inject(b,a);this.fireEvent("sort",[this.element,this.clone]);
+},start:function(b,a){if(!this.idle||b.rightClick||(!this.options.handle&&this.options.unDraggableTags.contains(b.target.get("tag")))){return;}this.idle=false;
+this.element=a;this.opacity=a.getStyle("opacity");this.list=a.getParent();this.clone=this.getClone(b,a);this.drag=new Drag.Move(this.clone,Object.merge({droppables:this.getDroppables()},this.options.dragOptions)).addEvents({onSnap:function(){b.stop();
+this.clone.setStyle("visibility","visible");this.element.setStyle("opacity",this.options.opacity||0);this.fireEvent("start",[this.element,this.clone]);
+}.bind(this),onEnter:this.insert.bind(this),onCancel:this.end.bind(this),onComplete:this.end.bind(this)});this.clone.inject(this.element,"before");this.drag.start(b);
+},end:function(){this.drag.detach();this.element.setStyle("opacity",this.opacity);var a=this;if(this.effect){var c=this.element.getStyles("width","height"),e=this.clone,d=e.computePosition(this.getDroppableCoordinates(e));
+var b=function(){this.removeEvent("cancel",b);e.destroy();a.reset();};this.effect.element=e;this.effect.start({top:d.top,left:d.left,width:c.width,height:c.height,opacity:0.25}).addEvent("cancel",b).chain(b);
+}else{this.clone.destroy();a.reset();}},reset:function(){this.idle=true;this.fireEvent("complete",this.element);},serialize:function(){var c=Array.link(arguments,{modifier:Type.isFunction,index:function(d){return d!=null;
+}});var b=this.lists.map(function(d){return d.getChildren().map(c.modifier||function(e){return e.get("id");},this);},this);var a=c.index;if(this.lists.length==1){a=0;
+}return(a||a===0)&&a>=0&&a<this.lists.length?b[a]:b;}});Request.implement({options:{initialDelay:5000,delay:5000,limit:60000},startTimer:function(b){var a=function(){if(!this.running){this.send({data:b});
+}};this.lastDelay=this.options.initialDelay;this.timer=a.delay(this.lastDelay,this);this.completeCheck=function(c){clearTimeout(this.timer);this.lastDelay=(c)?this.options.delay:(this.lastDelay+this.options.delay).min(this.options.limit);
+this.timer=a.delay(this.lastDelay,this);};return this.addEvent("complete",this.completeCheck);},stopTimer:function(){clearTimeout(this.timer);return this.removeEvent("complete",this.completeCheck);
+}});(function(){var a=this.Color=new Type("Color",function(c,d){if(arguments.length>=3){d="rgb";c=Array.slice(arguments,0,3);}else{if(typeof c=="string"){if(c.match(/rgb/)){c=c.rgbToHex().hexToRgb(true);
+}else{if(c.match(/hsb/)){c=c.hsbToRgb();}else{c=c.hexToRgb(true);}}}}d=d||"rgb";switch(d){case"hsb":var b=c;c=c.hsbToRgb();c.hsb=b;break;case"hex":c=c.hexToRgb(true);
+break;}c.rgb=c.slice(0,3);c.hsb=c.hsb||c.rgbToHsb();c.hex=c.rgbToHex();return Object.append(c,this);});a.implement({mix:function(){var b=Array.slice(arguments);
+var d=(typeOf(b.getLast())=="number")?b.pop():50;var c=this.slice();b.each(function(e){e=new a(e);for(var f=0;f<3;f++){c[f]=Math.round((c[f]/100*(100-d))+(e[f]/100*d));
+}});return new a(c,"rgb");},invert:function(){return new a(this.map(function(b){return 255-b;}));},setHue:function(b){return new a([b,this.hsb[1],this.hsb[2]],"hsb");
+},setSaturation:function(b){return new a([this.hsb[0],b,this.hsb[2]],"hsb");},setBrightness:function(b){return new a([this.hsb[0],this.hsb[1],b],"hsb");
+}});this.$RGB=function(e,d,c){return new a([e,d,c],"rgb");};this.$HSB=function(e,d,c){return new a([e,d,c],"hsb");};this.$HEX=function(b){return new a(b,"hex");
+};Array.implement({rgbToHsb:function(){var c=this[0],d=this[1],k=this[2],h=0;var j=Math.max(c,d,k),f=Math.min(c,d,k);var l=j-f;var i=j/255,g=(j!=0)?l/j:0;
+if(g!=0){var e=(j-c)/l;var b=(j-d)/l;var m=(j-k)/l;if(c==j){h=m-b;}else{if(d==j){h=2+e-m;}else{h=4+b-e;}}h/=6;if(h<0){h++;}}return[Math.round(h*360),Math.round(g*100),Math.round(i*100)];
+},hsbToRgb:function(){var d=Math.round(this[2]/100*255);if(this[1]==0){return[d,d,d];}else{var b=this[0]%360;var g=b%60;var h=Math.round((this[2]*(100-this[1]))/10000*255);
+var e=Math.round((this[2]*(6000-this[1]*g))/600000*255);var c=Math.round((this[2]*(6000-this[1]*(60-g)))/600000*255);switch(Math.floor(b/60)){case 0:return[d,c,h];
+case 1:return[e,d,h];case 2:return[h,d,c];case 3:return[h,e,d];case 4:return[c,h,d];case 5:return[d,h,e];}}return false;}});String.implement({rgbToHsb:function(){var b=this.match(/\d{1,3}/g);
+return(b)?b.rgbToHsb():null;},hsbToRgb:function(){var b=this.match(/\d{1,3}/g);return(b)?b.hsbToRgb():null;}});})(); \ No newline at end of file
diff --git a/module/webui/themes/Default/js/static/purr.js b/module/webui/themes/Default/js/static/purr.js
new file mode 100644
index 000000000..9cbc503d9
--- /dev/null
+++ b/module/webui/themes/Default/js/static/purr.js
@@ -0,0 +1,309 @@
+/*
+---
+script: purr.js
+
+description: Class to create growl-style popup notifications.
+
+license: MIT-style
+
+authors: [atom smith]
+
+requires:
+- core/1.3: [Core, Browser, Array, Function, Number, String, Hash, Event, Class.Extras, Element.Event, Element.Style, Element.Dimensions, Fx.CSS, FX.Tween, Fx.Morph]
+
+provides: [Purr, Element.alert]
+...
+*/
+
+
+var Purr = new Class({
+
+ 'options': {
+ 'mode': 'top',
+ 'position': 'left',
+ 'elementAlertClass': 'purr-element-alert',
+ 'elements': {
+ 'wrapper': 'div',
+ 'alert': 'div',
+ 'buttonWrapper': 'div',
+ 'button': 'button'
+ },
+ 'elementOptions': {
+ 'wrapper': {
+ 'styles': {
+ 'position': 'fixed',
+ 'z-index': '9999'
+ },
+ 'class': 'purr-wrapper'
+ },
+ 'alert': {
+ 'class': 'purr-alert',
+ 'styles': {
+ 'opacity': '.85'
+ }
+ },
+ 'buttonWrapper': {
+ 'class': 'purr-button-wrapper'
+ },
+ 'button': {
+ 'class': 'purr-button'
+ }
+ },
+ 'alert': {
+ 'buttons': [],
+ 'clickDismiss': true,
+ 'hoverWait': true,
+ 'hideAfter': 5000,
+ 'fx': {
+ 'duration': 500
+ },
+ 'highlight': false,
+ 'highlightRepeat': false,
+ 'highlight': {
+ 'start': '#FF0',
+ 'end': false
+ }
+ }
+ },
+
+ 'Implements': [Options, Events, Chain],
+
+ 'initialize': function(options){
+ this.setOptions(options);
+ this.createWrapper();
+ return this;
+ },
+
+ 'bindAlert': function(){
+ return this.alert.bind(this);
+ },
+
+ 'createWrapper': function(){
+ this.wrapper = new Element(this.options.elements.wrapper, this.options.elementOptions.wrapper);
+ if(this.options.mode == 'top')
+ {
+ this.wrapper.setStyle('top', 0);
+ }
+ else
+ {
+ this.wrapper.setStyle('bottom', 0);
+ }
+ document.id(document.body).grab(this.wrapper);
+ this.positionWrapper(this.options.position);
+ },
+
+ 'positionWrapper': function(position){
+ if(typeOf(position) == 'object')
+ {
+
+ var wrapperCoords = this.getWrapperCoords();
+
+ this.wrapper.setStyles({
+ 'bottom': '',
+ 'left': position.x,
+ 'top': position.y - wrapperCoords.height,
+ 'position': 'absolute'
+ });
+ }
+ else if(position == 'left')
+ {
+ this.wrapper.setStyle('left', 0);
+ }
+ else if(position == 'right')
+ {
+ this.wrapper.setStyle('right', 0);
+ }
+ else
+ {
+ this.wrapper.setStyle('left', (window.innerWidth / 2) - (this.getWrapperCoords().width / 2));
+ }
+ return this;
+ },
+
+ 'getWrapperCoords': function(){
+ this.wrapper.setStyle('visibility', 'hidden');
+ var measurer = this.alert('need something in here to measure');
+ var coords = this.wrapper.getCoordinates();
+ measurer.destroy();
+ this.wrapper.setStyle('visibility','');
+ return coords;
+ },
+
+ 'alert': function(msg, options){
+
+ options = Object.merge({}, this.options.alert, options || {});
+
+ var alert = new Element(this.options.elements.alert, this.options.elementOptions.alert);
+
+ if(typeOf(msg) == 'string')
+ {
+ alert.set('html', msg);
+ }
+ else if(typeOf(msg) == 'element')
+ {
+ alert.grab(msg);
+ }
+ else if(typeOf(msg) == 'array')
+ {
+ var alerts = [];
+ msg.each(function(m){
+ alerts.push(this.alert(m, options));
+ }, this);
+ return alerts;
+ }
+
+ alert.store('options', options);
+
+ if(options.buttons.length > 0)
+ {
+ options.clickDismiss = false;
+ options.hideAfter = false;
+ options.hoverWait = false;
+ var buttonWrapper = new Element(this.options.elements.buttonWrapper, this.options.elementOptions.buttonWrapper);
+ alert.grab(buttonWrapper);
+ options.buttons.each(function(button){
+ if(button.text != undefined)
+ {
+ var callbackButton = new Element(this.options.elements.button, this.options.elementOptions.button);
+ callbackButton.set('html', button.text);
+ if(button.callback != undefined)
+ {
+ callbackButton.addEvent('click', button.callback.pass(alert));
+ }
+ if(button.dismiss != undefined && button.dismiss)
+ {
+ callbackButton.addEvent('click', this.dismiss.pass(alert, this));
+ }
+ buttonWrapper.grab(callbackButton);
+ }
+ }, this);
+ }
+ if(options.className != undefined)
+ {
+ alert.addClass(options.className);
+ }
+
+ this.wrapper.grab(alert, (this.options.mode == 'top') ? 'bottom' : 'top');
+
+ var fx = Object.merge(this.options.alert.fx, options.fx);
+ var alertFx = new Fx.Morph(alert, fx);
+ alert.store('fx', alertFx);
+ this.fadeIn(alert);
+
+ if(options.highlight)
+ {
+ alertFx.addEvent('complete', function(){
+ alert.highlight(options.highlight.start, options.highlight.end);
+ if(options.highlightRepeat)
+ {
+ alert.highlight.periodical(options.highlightRepeat, alert, [options.highlight.start, options.highlight.end]);
+ }
+ });
+ }
+ if(options.hideAfter)
+ {
+ this.dismiss(alert);
+ }
+
+ if(options.clickDismiss)
+ {
+ alert.addEvent('click', function(){
+ this.holdUp = false;
+ this.dismiss(alert, true);
+ }.bind(this));
+ }
+
+ if(options.hoverWait)
+ {
+ alert.addEvents({
+ 'mouseenter': function(){
+ this.holdUp = true;
+ }.bind(this),
+ 'mouseleave': function(){
+ this.holdUp = false;
+ }.bind(this)
+ });
+ }
+
+ return alert;
+ },
+
+ 'fadeIn': function(alert){
+ var alertFx = alert.retrieve('fx');
+ alertFx.set({
+ 'opacity': 0
+ });
+ alertFx.start({
+ 'opacity': [this.options.elementOptions.alert.styles.opacity, .9].pick(),
+ });
+ },
+
+ 'dismiss': function(alert, now){
+ now = now || false;
+ var options = alert.retrieve('options');
+ if(now)
+ {
+ this.fadeOut(alert);
+ }
+ else
+ {
+ this.fadeOut.delay(options.hideAfter, this, alert);
+ }
+ },
+
+ 'fadeOut': function(alert){
+ if(this.holdUp)
+ {
+ this.dismiss.delay(100, this, [alert, true])
+ return null;
+ }
+ var alertFx = alert.retrieve('fx');
+ if(!alertFx)
+ {
+ return null;
+ }
+ var to = {
+ 'opacity': 0
+ }
+ if(this.options.mode == 'top')
+ {
+ to['margin-top'] = '-'+alert.offsetHeight+'px';
+ }
+ else
+ {
+ to['margin-bottom'] = '-'+alert.offsetHeight+'px';
+ }
+ alertFx.start(to);
+ alertFx.addEvent('complete', function(){
+ alert.destroy();
+ });
+ }
+});
+
+Element.implement({
+
+ 'alert': function(msg, options){
+ var alert = this.retrieve('alert');
+ if(!alert)
+ {
+ options = options || {
+ 'mode':'top'
+ };
+ alert = new Purr(options)
+ this.store('alert', alert);
+ }
+
+ var coords = this.getCoordinates();
+
+ alert.alert(msg, options);
+
+ alert.wrapper.setStyles({
+ 'bottom': '',
+ 'left': (coords.left - (alert.wrapper.getWidth() / 2)) + (this.getWidth() / 2),
+ 'top': coords.top - (alert.wrapper.getHeight()),
+ 'position': 'absolute'
+ });
+
+ }
+
+}); \ No newline at end of file
diff --git a/module/webui/themes/Default/js/static/purr.min.js b/module/webui/themes/Default/js/static/purr.min.js
new file mode 100644
index 000000000..bf70e357d
--- /dev/null
+++ b/module/webui/themes/Default/js/static/purr.min.js
@@ -0,0 +1 @@
+var Purr=new Class({options:{mode:"top",position:"left",elementAlertClass:"purr-element-alert",elements:{wrapper:"div",alert:"div",buttonWrapper:"div",button:"button"},elementOptions:{wrapper:{styles:{position:"fixed","z-index":"9999"},"class":"purr-wrapper"},alert:{"class":"purr-alert",styles:{opacity:".85"}},buttonWrapper:{"class":"purr-button-wrapper"},button:{"class":"purr-button"}},alert:{buttons:[],clickDismiss:!0,hoverWait:!0,hideAfter:5e3,fx:{duration:500},highlight:!1,highlightRepeat:!1,highlight:{start:"#FF0",end:!1}}},Implements:[Options,Events,Chain],initialize:function(t){return this.setOptions(t),this.createWrapper(),this},bindAlert:function(){return this.alert.bind(this)},createWrapper:function(){this.wrapper=new Element(this.options.elements.wrapper,this.options.elementOptions.wrapper),"top"==this.options.mode?this.wrapper.setStyle("top",0):this.wrapper.setStyle("bottom",0),document.id(document.body).grab(this.wrapper),this.positionWrapper(this.options.position)},positionWrapper:function(t){if("object"==typeOf(t)){var e=this.getWrapperCoords();this.wrapper.setStyles({bottom:"",left:t.x,top:t.y-e.height,position:"absolute"})}else"left"==t?this.wrapper.setStyle("left",0):"right"==t?this.wrapper.setStyle("right",0):this.wrapper.setStyle("left",window.innerWidth/2-this.getWrapperCoords().width/2);return this},getWrapperCoords:function(){this.wrapper.setStyle("visibility","hidden");var t=this.alert("need something in here to measure"),e=this.wrapper.getCoordinates();return t.destroy(),this.wrapper.setStyle("visibility",""),e},alert:function(t,e){e=Object.merge({},this.options.alert,e||{});var i=new Element(this.options.elements.alert,this.options.elementOptions.alert);if("string"==typeOf(t))i.set("html",t);else if("element"==typeOf(t))i.grab(t);else if("array"==typeOf(t)){var s=[];return t.each(function(t){s.push(this.alert(t,e))},this),s}if(i.store("options",e),e.buttons.length>0){e.clickDismiss=!1,e.hideAfter=!1,e.hoverWait=!1;var r=new Element(this.options.elements.buttonWrapper,this.options.elementOptions.buttonWrapper);i.grab(r),e.buttons.each(function(t){if(void 0!=t.text){var e=new Element(this.options.elements.button,this.options.elementOptions.button);e.set("html",t.text),void 0!=t.callback&&e.addEvent("click",t.callback.pass(i)),void 0!=t.dismiss&&t.dismiss&&e.addEvent("click",this.dismiss.pass(i,this)),r.grab(e)}},this)}void 0!=e.className&&i.addClass(e.className),this.wrapper.grab(i,"top"==this.options.mode?"bottom":"top");var o=Object.merge(this.options.alert.fx,e.fx),n=new Fx.Morph(i,o);return i.store("fx",n),this.fadeIn(i),e.highlight&&n.addEvent("complete",function(){i.highlight(e.highlight.start,e.highlight.end),e.highlightRepeat&&i.highlight.periodical(e.highlightRepeat,i,[e.highlight.start,e.highlight.end])}),e.hideAfter&&this.dismiss(i),e.clickDismiss&&i.addEvent("click",function(){this.holdUp=!1,this.dismiss(i,!0)}.bind(this)),e.hoverWait&&i.addEvents({mouseenter:function(){this.holdUp=!0}.bind(this),mouseleave:function(){this.holdUp=!1}.bind(this)}),i},fadeIn:function(t){var e=t.retrieve("fx");e.set({opacity:0}),e.start({opacity:[this.options.elementOptions.alert.styles.opacity,.9].pick()})},dismiss:function(t,e){e=e||!1;var i=t.retrieve("options");e?this.fadeOut(t):this.fadeOut.delay(i.hideAfter,this,t)},fadeOut:function(t){if(this.holdUp)return this.dismiss.delay(100,this,[t,!0]),null;var e=t.retrieve("fx");if(!e)return null;var i={opacity:0};"top"==this.options.mode?i["margin-top"]="-"+t.offsetHeight+"px":i["margin-bottom"]="-"+t.offsetHeight+"px",e.start(i),e.addEvent("complete",function(){t.destroy()})}});Element.implement({alert:function(t,e){var i=this.retrieve("alert");i||(e=e||{mode:"top"},i=new Purr(e),this.store("alert",i));var s=this.getCoordinates();i.alert(t,e),i.wrapper.setStyles({bottom:"",left:s.left-i.wrapper.getWidth()/2+this.getWidth()/2,top:s.top-i.wrapper.getHeight(),position:"absolute"})}}); \ No newline at end of file
diff --git a/module/webui/themes/Default/js/static/tinytab.js b/module/webui/themes/Default/js/static/tinytab.js
new file mode 100644
index 000000000..de50279fc
--- /dev/null
+++ b/module/webui/themes/Default/js/static/tinytab.js
@@ -0,0 +1,43 @@
+/*
+---
+description: TinyTab - Tiny and simple tab handler for Mootools.
+
+license: MIT-style
+
+authors:
+- Danillo César de O. Melo
+
+requires:
+- core/1.2.4: '*'
+
+provides: TinyTab
+
+...
+*/
+(function($) {
+ this.TinyTab = new Class({
+ Implements: Events,
+ initialize: function(tabs, contents, opt) {
+ this.tabs = tabs;
+ this.contents = contents;
+ if(!opt) opt = {};
+ this.css = opt.selectedClass || 'selected';
+ this.select(this.tabs[0]);
+ tabs.each(function(el){
+ el.addEvent('click',function(e){
+ this.select(el);
+ e.stop();
+ }.bind(this));
+ }.bind(this));
+ },
+
+ select: function(el) {
+ this.tabs.removeClass(this.css);
+ el.addClass(this.css);
+ this.contents.setStyle('display','none');
+ var content = this.contents[this.tabs.indexOf(el)];
+ content.setStyle('display','block');
+ this.fireEvent('change',[content,el]);
+ }
+ });
+})(document.id); \ No newline at end of file
diff --git a/module/webui/themes/Default/js/static/tinytab.min.js b/module/webui/themes/Default/js/static/tinytab.min.js
new file mode 100644
index 000000000..2f4fa0436
--- /dev/null
+++ b/module/webui/themes/Default/js/static/tinytab.min.js
@@ -0,0 +1 @@
+!function(){this.TinyTab=new Class({Implements:Events,initialize:function(s,t,e){this.tabs=s,this.contents=t,e||(e={}),this.css=e.selectedClass||"selected",this.select(this.tabs[0]),s.each(function(s){s.addEvent("click",function(t){this.select(s),t.stop()}.bind(this))}.bind(this))},select:function(s){this.tabs.removeClass(this.css),s.addClass(this.css),this.contents.setStyle("display","none");var t=this.contents[this.tabs.indexOf(s)];t.setStyle("display","block"),this.fireEvent("change",[t,s])}})}(document.id); \ No newline at end of file
diff --git a/module/webui/themes/Default/tml/admin.html b/module/webui/themes/Default/tml/admin.html
new file mode 100644
index 000000000..0b7301fc8
--- /dev/null
+++ b/module/webui/themes/Default/tml/admin.html
@@ -0,0 +1,98 @@
+{% extends '/Default/tml/base.html' %}
+
+{% block head %}
+ <script type="text/javascript" src="/Default/js/render/admin.min.js"></script>
+{% endblock %}
+
+
+{% block title %}{{ _("Administrate") }} - {{ super() }} {% endblock %}
+{% block subtitle %}{{ _("Administrate") }}{% endblock %}
+
+{% block content %}
+
+ <a href="#" id="quit-pyload" style="font-size: large; font-weight: bold;">{{_("Quit pyLoad")}}</a> |
+ <a href="#" id="restart-pyload" style="font-size: large; font-weight: bold;">{{_("Restart pyLoad")}}</a>
+ <br>
+ <br>
+
+ {{ _("To add user or change passwords use:") }} <b>python pyload.py -u</b><br>
+ {{ _("Important: Admin user have always all permissions!") }}
+
+ <form action="" method="POST">
+ <table class="settable wide">
+ <thead style="font-size: 11px">
+ <th>
+ {{ _("Name") }}
+ </th>
+ <th>
+ {{ _("Change Password") }}
+ </th>
+ <th>
+ {{ _("Admin") }}
+ </th>
+ <th>
+ {{ _("Permissions") }}
+ </th>
+ </thead>
+
+ {% for name, data in users.iteritems() %}
+ <tr>
+ <td>{{ name }}</td>
+ <td><a class="change_password" href="#" id="change_pw|{{name}}">{{ _("change") }}</a></td>
+ <td><input name="{{ name }}|admin" type="checkbox" {% if data.perms.admin %}
+ checked="True" {% endif %}"></td>
+ <td>
+ <select multiple="multiple" size="{{ permlist|length }}" name="{{ name }}|perms">
+ {% for perm in permlist %}
+ {% if data.perms|getitem(perm) %}
+ <option selected="selected">{{ perm }}</option>
+ {% else %}
+ <option>{{ perm }}</option>
+ {% endif %}
+ {% endfor %}
+ </select>
+ </td>
+ </tr>
+ {% endfor %}
+
+
+ </table>
+
+ <button class="styled_button" type="submit">{{ _("Submit") }}</button>
+ </form>
+{% endblock %}
+{% block hidden %}
+ <div id="password_box" class="window_box" style="z-index: 2">
+ <form id="password_form" action="/json/change_password" method="POST" enctype="multipart/form-data">
+ <h1>{{ _("Change Password") }}</h1>
+
+ <p>{{ _("Enter your current and desired Password.") }}</p>
+ <label for="user_login">{{ _("User") }}
+ <span class="small">{{ _("Your username.") }}</span>
+ </label>
+ <input id="user_login" name="user_login" type="text" size="20"/>
+
+ <label for="login_current_password">{{ _("Current password") }}
+ <span class="small">{{ _("The password for this account.") }}</span>
+ </label>
+ <input id="login_current_password" name="login_current_password" type="password" size="20"/>
+
+ <label for="login_new_password">{{ _("New password") }}
+ <span class="small">{{ _("The new password.") }}</span>
+ </label>
+ <input id="login_new_password" name="login_new_password" type="password" size="20"/>
+
+ <label for="login_new_password2">{{ _("New password (repeat)") }}
+ <span class="small">{{ _("Please repeat the new password.") }}</span>
+ </label>
+ <input id="login_new_password2" name="login_new_password2" type="password" size="20"/>
+
+
+ <button id="login_password_button" type="submit">{{ _("Submit") }}</button>
+ <button id="login_password_reset" style="margin-left: 0" type="reset">{{ _("Reset") }}</button>
+ <div class="spacer"></div>
+
+ </form>
+
+ </div>
+{% endblock %}
diff --git a/module/web/templates/default/base.html b/module/webui/themes/Default/tml/base.html
index 147c08a37..aac70a394 100644
--- a/module/web/templates/default/base.html
+++ b/module/webui/themes/Default/tml/base.html
@@ -5,17 +5,17 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-<link rel="stylesheet" type="text/css" href="/media/default/css/default.css"/>
-<link rel="stylesheet" type="text/css" href="/media/default/css/window.css"/>
-<link rel="stylesheet" type="text/css" href="/media/default/css/MooDialog.css"/>
+<link rel="stylesheet" type="text/css" href="/Default/css/base.min.css"/>
+<link rel="stylesheet" type="text/css" href="/Default/css/window.min.css"/>
+<link rel="stylesheet" type="text/css" href="/Default/css/MooDialog.min.css"/>
-<script type="text/javascript" src="/media/js/mootools-core-1.4.1.js"></script>
-<script type="text/javascript" src="/media/js/mootools-more-1.4.0.1.js"></script>
-<script type="text/javascript" src="/media/js/MooDialog_static.js"></script>
-<script type="text/javascript" src="/media/js/purr_static.js"></script>
+<script type="text/javascript" src="/Default/js/static/mootools-core.min.js"></script>
+<script type="text/javascript" src="/Default/js/static/mootools-more.min.js"></script>
+<script type="text/javascript" src="/Default/js/static/MooDialog.min.js"></script>
+<script type="text/javascript" src="/Default/js/static/purr.min.js"></script>
-<script type="text/javascript" src="/media/js/base.js"></script>
+<script type="text/javascript" src="/Default/js/render/base.min.js"></script>
<title>{% block title %}pyLoad {{_("Webinterface")}}{% endblock %}</title>
@@ -36,7 +36,7 @@
{% if update %}
<span>
-<span style="font-weight: bold; margin: 0 2px 0 2px;">{{_("pyLoad Update available!")}}</span>
+<span style="font-weight: bold; margin: 0 2px 0 2px;">{{_("New pyLoad version %s available!") % update}}</span>
</span>
{% endif %}
@@ -48,30 +48,30 @@
{% endif %}
<span id="cap_info" style="display: {% if captcha %}inline{%else%}none{% endif %}">
-<img src="/media/default/img/images.png" alt="Captcha:" style="vertical-align:middle; margin:2px" />
+<img src="/Default/img/images.png" alt="Captcha:" style="vertical-align:middle; margin:2px" />
<span style="font-weight: bold; cursor: pointer; margin-right: 2px;">{{_("Captcha waiting")}}</span>
</span>
- <img src="/media/default/img/head-login.png" alt="User:" style="vertical-align:middle; margin:2px" /><span style="padding-right: 2px;">{{user.name}}</span>
- <ul id="user-actions">
- <li><a href="/logout" class="action logout" rel="nofollow">{{_("Logout")}}</a></li>
- {% if user.is_admin %}
- <li><a href="/admin" class="action profile" rel="nofollow">{{_("Administrate")}}</a></li>
- {% endif %}
+ <img src="/Default/img/head-login.png" alt="User:" style="vertical-align:middle; margin:2px" /><span style="padding-right: 2px;">{{user.name}}</span>
+ <ul id="user-actions">
+ <li><a href="/logout" class="action logout" rel="nofollow">{{_("Logout")}}</a></li>
+ {% if user.is_admin %}
+ <li><a href="/admin" class="action profile" rel="nofollow">{{_("Administrate")}}</a></li>
+ {% endif %}
<li><a href="/info" class="action info" rel="nofollow">{{_("Info")}}</a></li>
- </ul>
+ </ul>
{% else %}
<span style="padding-right: 2px;">{{_("Please Login!")}}</span>
{% endif %}
{% endblock %}
- </div>
+ </div>
- <a href="/"><img id="head-logo" src="/media/default/img/pyload-logo-edited3.5-new-font-small.png" alt="pyLoad" /></a>
+ <a href="/"><img id="head-logo" src="/Default/img/pyload-logo.png" alt="pyLoad" /></a>
- <div id="head-menu">
- <ul>
+ <div id="head-menu">
+ <ul>
{% macro selected(name, right=False) -%}
{% if name in url -%}class="{% if right -%}right {% endif %}selected"{%- endif %}
@@ -79,39 +79,36 @@
{%- endmacro %}
- {% block menu %}
- <li>
- <a href="/" title=""><img src="/media/default/img/head-menu-home.png" alt="" /> {{_("Home")}}</a>
- </li>
- <li {{ selected('queue') }}>
- <a href="/queue/" title=""><img src="/media/default/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
- </li>
- <li {{ selected('collector') }}>
- <a href="/collector/" title=""><img src="/media/default/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
- </li>
- <li {{ selected('downloads') }}>
- <a href="/downloads/" title=""><img src="/media/default/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
- </li>
-{# <li {{ selected('filemanager') }}>#}
-{# <a href="/filemanager/" title=""><img src="/media/default/img/head-menu-download.png" alt="" /> {{_("FileManager")}}</a>#}
-{# </li>#}
- <li {{ selected('logs', True) }}>
- <a href="/logs/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-index.png" alt="" />{{_("Logs")}}</a>
- </li>
- <li {{ selected('settings', True) }}>
- <a href="/settings/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-config.png" alt="" />{{_("Config")}}</a>
- </li>
- {% endblock %}
-
- </ul>
- </div>
-
- <div style="clear:both;"></div>
+ {% block menu %}
+ <li>
+ <a href="/" title=""><img src="/Default/img/head-menu-home.png" alt="" /> {{_("Home")}}</a>
+ </li>
+ <li {{ selected('queue') }}>
+ <a href="/queue/" title=""><img src="/Default/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
+ </li>
+ <li {{ selected('collector') }}>
+ <a href="/collector/" title=""><img src="/Default/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
+ </li>
+ <li {{ selected('downloads') }}>
+ <a href="/downloads/" title=""><img src="/Default/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
+ </li>
+ <li {{ selected('logs', True) }}>
+ <a href="/logs/" title=""><img src="/Default/img/head-menu-index.png" alt="" />{{_("Logs")}}</a>
+ </li>
+ <li {{ selected('settings', True) }}>
+ <a href="/settings/" title=""><img src="/Default/img/head-menu-config.png" alt="" />{{_("Config")}}</a>
+ </li>
+ {% endblock %}
+
+ </ul>
+ </div>
+
+ <div style="clear:both;"></div>
</div>
{% if perms.STATUS %}
<ul id="page-actions2">
- <li id="action_play"><a href="#" class="action play" accesskey="o" rel="nofollow">{{_("Start")}}</a></li>
+ <li id="action_play"><a href="#" class="action play" accesskey="o" rel="nofollow">{{_("Start")}}</a></li>
<li id="action_stop"><a href="#" class="action stop" accesskey="o" rel="nofollow">{{_("Stop")}}</a></li>
<li id="action_cancel"><a href="#" class="action cancel" accesskey="o" rel="nofollow">{{_("Cancel")}}</a></li>
<li id="action_add"><a href="#" class="action add" accesskey="o" rel="nofollow" >{{_("Add")}}</a></li>
@@ -149,20 +146,20 @@
<noscript><h1>Enable JavaScript to use the webinterface.</h1></noscript>
{% for message in messages %}
- <b><p>{{message}}</p></b>
+ <b><p>{{message}}</p></b>
{% endfor %}
<div id="load-indicator" style="opacity: 0; float: right; margin-top: -10px;">
- <img src="/media/default/img/ajax-loader.gif" alt="" style="padding-right: 5px"/>
+ <img src="/Default/img/ajax-loader.gif" alt="" style="padding-right: 5px"/>
{{_("loading")}}
</div>
{% block content %}
{% endblock content %}
- <hr style="clear: both;" />
+ <hr style="clear: both;" />
-<div id="foot">&copy; 2008-2011 pyLoad Team
+<div id="foot">&copy; 2008-2015 pyLoad Team
<a href="#top" class="action top" accesskey="x"><span>{{_("Back to top")}}</span></a><br />
<!--<div class="breadcrumbs"></div>-->
@@ -171,8 +168,8 @@
</div>
<div style="display: none;">
- {% include "default/window.html" %}
- {% include "default/captcha.html" %}
+ {% include '/Default/tml/window.html' %}
+ {% include '/Default/tml/captcha.html' %}
{% block hidden %}
{% endblock %}
</div>
diff --git a/module/webui/themes/Default/tml/captcha.html b/module/webui/themes/Default/tml/captcha.html
new file mode 100644
index 000000000..3bfa6cbcf
--- /dev/null
+++ b/module/webui/themes/Default/tml/captcha.html
@@ -0,0 +1,42 @@
+<!-- Captcha box -->
+<div id="cap_box" class="window_box">
+
+ <form id="cap_form" action="/json/set_captcha" method="POST" enctype="multipart/form-data" onsubmit="return false;">
+
+ <h1>{{_("Captcha reading")}}</h1>
+ <p id="cap_title">{{_("Please read the text on the captcha.")}}</p>
+
+ <div id="cap_textual">
+
+ <input id="cap_id" name="cap_id" type="hidden" value="" />
+
+ <label>{{_("Captcha")}}
+ <span class="small">{{_("The captcha.")}}</span>
+ </label>
+ <span class="cont">
+ <img id="cap_textual_img" src="">
+ </span>
+
+ <label>{{_("Text")}}
+ <span class="small">{{_("Input the text on the captcha.")}}</span>
+ </label>
+ <input id="cap_result" name="cap_result" type="text" size="20" />
+
+ </div>
+
+ <div id="cap_positional" style="text-align: center">
+ <img id="cap_positional_img" src="" style="margin: 10px; cursor:pointer">
+ </div>
+
+ <div id="button_bar" style="text-align: center">
+ <span>
+ <button id="cap_submit" type="submit" style="margin-left: 0">{{_("Submit")}}</button>
+ <button id="cap_reset" type="reset" style="margin-left: 0">{{_("Close")}}</button>
+ </span>
+ </div>
+
+ <div class="spacer"></div>
+
+ </form>
+
+</div>
diff --git a/module/webui/themes/Default/tml/downloads.html b/module/webui/themes/Default/tml/downloads.html
new file mode 100644
index 000000000..fe0f4f1bc
--- /dev/null
+++ b/module/webui/themes/Default/tml/downloads.html
@@ -0,0 +1,29 @@
+{% extends '/Default/tml/base.html' %}
+
+{% block title %}Downloads - {{super()}} {% endblock %}
+
+{% block subtitle %}
+{{_("Downloads")}}
+{% endblock %}
+
+{% block content %}
+
+<ul>
+ {% for folder in files.folder %}
+ <li>
+ {{ folder.name }}
+ <ul>
+ {% for file in folder.files %}
+ <li><a href='get/{{ folder.path|escape }}/{{ file|escape }}'>{{file}}</a></li>
+ {% endfor %}
+ </ul>
+ </li>
+ {% endfor %}
+
+ {% for file in files.files %}
+ <li> <a href='get/{{ file|escape }}'>{{ file }}</a></li>
+ {% endfor %}
+
+</ul>
+
+{% endblock %}
diff --git a/module/webui/themes/Default/tml/filemanager.html b/module/webui/themes/Default/tml/filemanager.html
new file mode 100644
index 000000000..c2ac5df9d
--- /dev/null
+++ b/module/webui/themes/Default/tml/filemanager.html
@@ -0,0 +1,76 @@
+{% extends '/Default/tml/base.html' %}
+
+{% block head %}
+
+<script type="text/javascript">
+
+document.addEvent("domready", function(){
+ var fmUI = new FilemanagerUI("url",1);
+});
+</script>
+{% endblock %}
+
+{% block title %}Downloads - {{super()}} {% endblock %}
+
+
+{% block subtitle %}
+{{_("FileManager")}}
+{% endblock %}
+
+{% macro display_file(file) %}
+ <li class="file">
+ <input type="hidden" name="path" class="path" value="{{ file.path }}" />
+ <input type="hidden" name="name" class="name" value="{{ file.name }}" />
+ <span>
+ <b>{{ file.name }}</b>
+ <span class="buttons" style="opacity:0">
+ <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/Default/img/pencil.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Default/img/delete.png" />
+ </span>
+ </span>
+ </li>
+{%- endmacro %}
+
+{% macro display_folder(fld, open = false) -%}
+ <li class="folder">
+ <input type="hidden" name="path" class="path" value="{{ fld.path }}" />
+ <input type="hidden" name="name" class="name" value="{{ fld.name }}" />
+ <span>
+ <b>{{ fld.name }}</b>
+ <span class="buttons" style="opacity:0">
+ <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/Default/img/pencil.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Default/img/delete.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Add subdirectory")}}" class="mkdir" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Default/img/add_folder.png" />
+ </span>
+ </span>
+ {% if (fld.folders|length + fld.files|length) > 0 %}
+ {% if open %}
+ <ul>
+ {% else %}
+ <ul style="display:none">
+ {% endif %}
+ {% for child in fld.folders %}
+ {{ display_folder(child) }}
+ {% endfor %}
+ {% for child in fld.files %}
+ {{ display_file(child) }}
+ {% endfor %}
+ </ul>
+ {% else %}
+ <div style="display:none">{{ _("Folder is empty") }}</div>
+ {% endif %}
+ </li>
+{%- endmacro %}
+
+{% block content %}
+
+<div style="clear:both"><!-- --></div>
+
+<ul id="directories-list">
+{{ display_folder(root, true) }}
+</ul>
+
+{% endblock %}
diff --git a/module/webui/themes/Default/tml/folder.html b/module/webui/themes/Default/tml/folder.html
new file mode 100644
index 000000000..54dd72bb9
--- /dev/null
+++ b/module/webui/themes/Default/tml/folder.html
@@ -0,0 +1,15 @@
+<li class="folder">
+ <input type="hidden" name="path" class="path" value="{{ path }}" />
+ <input type="hidden" name="name" class="name" value="{{ name }}" />
+ <span>
+ <b>{{ name }}</b>
+ <span class="buttons" style="opacity:0">
+ <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/Default/img/pencil.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Default/img/delete.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Add subdirectory")}}" class="mkdir" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Default/img/add_folder.png" />
+ </span>
+ </span>
+ <div style="display:none">{{ _("Folder is empty") }}</div>
+</li>
diff --git a/module/webui/themes/Default/tml/home.html b/module/webui/themes/Default/tml/home.html
new file mode 100644
index 000000000..09daae596
--- /dev/null
+++ b/module/webui/themes/Default/tml/home.html
@@ -0,0 +1,263 @@
+{% extends '/Default/tml/base.html' %}
+{% block head %}
+
+<script type="text/javascript">
+
+var em;
+var operafix = (navigator.userAgent.toLowerCase().search("opera") >= 0);
+
+document.addEvent("domready", function(){
+ em = new EntryManager();
+});
+
+var EntryManager = new Class({
+ initialize: function(){
+ this.json = new Request.JSON({
+ url: "json/links",
+ secure: false,
+ async: true,
+ onSuccess: this.update.bind(this),
+ initialDelay: 0,
+ delay: 2500,
+ limit: 30000
+ });
+
+ this.ids = [{% for link in content %}
+ {% if forloop.last %}
+ {{ link.id }}
+ {% else %}
+ {{ link.id }},
+ {% endif %}
+ {% endfor %}];
+
+ this.entries = [];
+ this.container = $('LinksAktiv');
+
+ this.parseFromContent();
+
+ this.json.startTimer();
+ },
+ parseFromContent: function(){
+ this.ids.each(function(id,index){
+ var entry = new LinkEntry(id);
+ entry.parse();
+ this.entries.push(entry)
+ }, this);
+ },
+ update: function(data){
+
+ try{
+ this.ids = this.entries.map(function(item){
+ return item.fid
+ });
+
+ this.ids.filter(function(id){
+ return !this.ids.contains(id)
+ },data).each(function(id){
+ var index = this.ids.indexOf(id);
+ this.entries[index].remove();
+ this.entries = this.entries.filter(function(item){return item.fid != this},id);
+ this.ids = this.ids.erase(id)
+ }, this);
+
+ data.links.each(function(link, i){
+ if (this.ids.contains(link.fid)){
+
+ var index = this.ids.indexOf(link.fid);
+ this.entries[index].update(link)
+
+ }else{
+ var entry = new LinkEntry(link.fid);
+ entry.insert(link);
+ this.entries.push(entry);
+ this.ids.push(link.fid);
+ this.container.adopt(entry.elements.tr,entry.elements.pgbTr);
+ entry.fade.start('opacity', 1);
+ entry.fadeBar.start('opacity', 1);
+
+ }
+ }, this)
+ }catch(e){
+ //alert(e)
+ }
+ }
+});
+
+
+var LinkEntry = new Class({
+ initialize: function(id){
+ this.fid = id;
+ this.id = id;
+ },
+ parse: function(){
+ this.elements = {
+ tr: $("link_{id}".substitute({id: this.id})),
+ name: $("link_{id}_name".substitute({id: this.id})),
+ status: $("link_{id}_status".substitute({id: this.id})),
+ info: $("link_{id}_info".substitute({id: this.id})),
+ bleft: $("link_{id}_bleft".substitute({id: this.id})),
+ percent: $("link_{id}_percent".substitute({id: this.id})),
+ remove: $("link_{id}_remove".substitute({id: this.id})),
+ pgbTr: $("link_{id}_pgb_tr".substitute({id: this.id})),
+ pgb: $("link_{id}_pgb".substitute({id: this.id}))
+ };
+ this.initEffects();
+ },
+ insert: function(item){
+ try{
+
+ this.elements = {
+ tr: new Element('tr', {
+ 'html': '',
+ 'styles':{
+ 'opacity': 0
+ }
+ }),
+ name: new Element('td', {
+ 'html': item.name
+ }),
+ status: new Element('td', {
+ 'html': item.statusmsg
+ }),
+ info: new Element('td', {
+ 'html': item.info
+ }),
+ bleft: new Element('td', {
+ 'html': humanFileSize(item.size)
+ }),
+ percent: new Element('span', {
+ 'html': item.percent+ '% / '+ humanFileSize(item.size-item.bleft)
+ }),
+ remove: new Element('img',{
+ 'src': '/Default/img/control_cancel.png',
+ 'styles':{
+ 'vertical-align': 'middle',
+ 'margin-right': '-20px',
+ 'margin-left': '5px',
+ 'margin-top': '-2px',
+ 'cursor': 'pointer'
+ }
+ }),
+ pgbTr: new Element('tr', {
+ 'html':''
+ }),
+ pgb: new Element('div', {
+ 'html': '&nbsp;',
+ 'styles':{
+ 'height': '4px',
+ 'width': item.percent+'%',
+ 'background-color': '#ddd'
+ }
+ })
+ };
+
+ this.elements.tr.adopt(this.elements.name,this.elements.status,this.elements.info,this.elements.bleft,new Element('td').adopt(this.elements.percent,this.elements.remove));
+ this.elements.pgbTr.adopt(new Element('td',{'colspan':5}).adopt(this.elements.pgb));
+ this.initEffects();
+ }catch(e){
+ alert(e)
+ }
+ },
+ initEffects: function(){
+ if(!operafix)
+ this.bar = new Fx.Morph(this.elements.pgb, {unit: '%', duration: 5000, link: 'link', fps:30});
+ this.fade = new Fx.Tween(this.elements.tr);
+ this.fadeBar = new Fx.Tween(this.elements.pgbTr);
+
+ this.elements.remove.addEvent('click', function(){
+ new Request({method: 'get', url: '/json/abort_link/'+this.id}).send();
+ }.bind(this));
+
+ },
+ update: function(item){
+ this.elements.name.set('text', item.name);
+ this.elements.status.set('text', item.statusmsg);
+ this.elements.info.set('text', item.info);
+ this.elements.bleft.set('text', item.format_size);
+ this.elements.percent.set('text', item.percent+ '% / '+ humanFileSize(item.size-item.bleft));
+ if(!operafix)
+ {
+ this.bar.start({
+ 'width': item.percent,
+ 'background-color': [Math.round(120/100*item.percent),100,100].hsbToRgb().rgbToHex()
+ });
+ }
+ else
+ {
+ this.elements.pgb.set(
+ 'styles', {
+ 'height': '4px',
+ 'width': item.percent+'%',
+ 'background-color': [Math.round(120/100*item.percent),100,100].hsbToRgb().rgbToHex(),
+ });
+ }
+ },
+ remove: function(){
+ this.fade.start('opacity',0).chain(function(){this.elements.tr.dispose();}.bind(this));
+ this.fadeBar.start('opacity',0).chain(function(){this.elements.pgbTr.dispose();}.bind(this));
+
+ }
+ });
+</script>
+
+{% endblock %}
+
+{% block subtitle %}
+{{_("Active Downloads")}}
+{% endblock %}
+
+{% block menu %}
+<li class="selected">
+ <a href="/" title=""><img src="/Default/img/head-menu-home.png" alt="" /> {{_("Home")}}</a>
+</li>
+<li>
+ <a href="/queue/" title=""><img src="/Default/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
+</li>
+<li>
+ <a href="/collector/" title=""><img src="/Default/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
+</li>
+<li>
+ <a href="/downloads/" title=""><img src="/Default/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
+</li>
+<li class="right">
+ <a href="/logs/" title=""><img src="/Default/img/head-menu-index.png" alt="" />{{_("Logs")}}</a>
+</li>
+<li class="right">
+ <a href="/settings/" title=""><img src="/Default/img/head-menu-config.png" alt="" />{{_("Config")}}</a>
+</li>
+{% endblock %}
+
+{% block content %}
+<table width="100%" class="queue">
+ <thead>
+ <tr class="header">
+ <th>{{_("Name")}}</th>
+ <th>{{_("Status")}}</th>
+ <th>{{_("Information")}}</th>
+ <th>{{_("Size")}}</th>
+ <th>{{_("Progress")}}</th>
+ </tr>
+ </thead>
+ <tbody id="LinksAktiv">
+
+ {% for link in content %}
+ <tr id="link_{{ link.id }}">
+ <td id="link_{{ link.id }}_name">{{ link.name }}</td>
+ <td id="link_{{ link.id }}_status">{{ link.status }}</td>
+ <td id="link_{{ link.id }}_info">{{ link.info }}</td>
+ <td id="link_{{ link.id }}_bleft">{{ link.format_size }}</td>
+ <td>
+ <span id="link_{{ link.id }}_percent">{{ link.percent }}% /{{ link.bleft }}</span>
+ <img id="link_{{ link.id }}_remove" style="vertical-align: middle; margin-right: -20px; margin-left: 5px; margin-top: -2px; cursor:pointer;" src="/Default/img/control_cancel.png"/>
+ </td>
+ </tr>
+ <tr id="link_{{ link.id }}_pgb_tr">
+ <td colspan="5">
+ <div id="link_{{ link.id }}_pgb" class="progressBar" style="background-color: green; height:4px; width: {{ link.percent }}%;">&nbsp;</div>
+ </td>
+ </tr>
+ {% endfor %}
+
+ </tbody>
+</table>
+{% endblock %}
diff --git a/module/web/templates/default/info.html b/module/webui/themes/Default/tml/info.html
index 77ae57376..a5fc3c3f7 100644
--- a/module/web/templates/default/info.html
+++ b/module/webui/themes/Default/tml/info.html
@@ -1,10 +1,10 @@
-{% extends 'default/base.html' %}
+{% extends '/Default/tml/base.html' %}
{% block head %}
<script type="text/javascript">
window.addEvent("domready", function() {
var ul = new Element('ul#twitter_update_list');
- var script1 = new Element('script[src=http://twitter.com/javascripts/blogger.js][type=text/javascript]');
+ var script1 = new Element('script[src=http://twitter.com/javascripts/blogger.min.js][type=text/javascript]');
var script2 = new Element('script[src=http://twitter.com/statuses/user_timeline/pyLoad.json?callback=twitterCallback2&count=6][type=text/javascript]');
$("twitter").adopt(ul, script1, script2);
});
@@ -78,4 +78,4 @@
</tr>
</table>
-{% endblock %} \ No newline at end of file
+{% endblock %}
diff --git a/module/web/templates/default/login.html b/module/webui/themes/Default/tml/login.html
index 9e91ad309..538ce792d 100644
--- a/module/web/templates/default/login.html
+++ b/module/webui/themes/Default/tml/login.html
@@ -1,4 +1,4 @@
-{% extends 'default/base.html' %}
+{% extends '/Default/tml/base.html' %}
{% block title %}{{_("Login")}} - {{super()}} {% endblock %}
@@ -27,10 +27,10 @@
{% if errors %}
<p>{{_("Your username and password didn't match. Please try again.")}}</p>
- {{ _("To reset your login data or add an user run:") }} <b> python pyLoadCore.py -u</b>
+ {{ _("To reset your login data or add an user run:") }} <b> python pyload.py -u</b>
{% endif %}
</div>
<br>
-
+
{% endblock %}
diff --git a/module/webui/themes/Default/tml/logout.html b/module/webui/themes/Default/tml/logout.html
new file mode 100644
index 000000000..19d673e7c
--- /dev/null
+++ b/module/webui/themes/Default/tml/logout.html
@@ -0,0 +1,9 @@
+{% extends '/Default/tml/base.html' %}
+
+{% block head %}
+<meta http-equiv="refresh" content="3; url=/">
+{% endblock %}
+
+{% block content %}
+<p><b>{{_("You were successfully logged out.")}}</b></p>
+{% endblock %}
diff --git a/module/webui/themes/Default/tml/logs.html b/module/webui/themes/Default/tml/logs.html
new file mode 100644
index 000000000..ccf992d04
--- /dev/null
+++ b/module/webui/themes/Default/tml/logs.html
@@ -0,0 +1,41 @@
+{% extends '/Default/tml/base.html' %}
+
+{% block title %}{{_("Logs")}} - {{super()}} {% endblock %}
+{% block subtitle %}{{_("Logs")}}{% endblock %}
+{% block head %}
+<link rel="stylesheet" type="text/css" href="/Default/css/log.min.css"/>
+{% endblock %}
+
+{% block content %}
+<div style="clear: both;"></div>
+
+<div class="logpaginator"><a href="{{ "/logs/1" }}">&lt;&lt; {{_("Start")}}</a> <a href="{{ "/logs/" + iprev|string }}">&lt; {{_("prev")}}</a> <a href="{{ "/logs/" + inext|string }}">{{_("next")}} &gt;</a> <a href="/logs/">{{_("End")}} &gt;&gt;</a></div>
+<div class="logperpage">
+ <form id="logform1" action="" method="POST">
+ <label for="reversed">Reversed:</label>
+ <input type="checkbox" name="reversed" onchange="this.form.submit();" {% if reversed %} checked="checked" {% endif %} />&nbsp;
+ <label for="perpage">Lines per page:</label>
+ <select name="perpage" onchange="this.form.submit();">
+ {% for value in perpage_p %}
+ <option value="{{value.0}}"{% if value.0 == perpage %} selected="selected" {% endif %}>{{value.1}}</option>
+ {% endfor %}
+ </select>
+ </form>
+</div>
+<div class="logwarn">{{warning}}</div>
+<div style="clear: both;"></div>
+<div class="logdiv">
+ <table class="logtable" cellpadding="0" cellspacing="0">
+ {% for line in log %}
+ <tr><td class="logline">{{line.line}}</td><td>{{line.date}}</td><td class="loglevel">{{line.level}}</td><td>{{line.message}}</td></tr>
+ {% endfor %}
+ </table>
+</div>
+<div class="logform">
+<form id="logform2" action="" method="POST">
+ <label for="from">Jump to time:</label><input type="text" name="from" size="15" value="{{from}}"/>
+ <input type="submit" value="ok" />
+</form>
+</div>
+<div style="clear: both; height: 10px;">&nbsp; </div>
+{% endblock %}
diff --git a/module/webui/themes/Default/tml/pathchooser.html b/module/webui/themes/Default/tml/pathchooser.html
new file mode 100644
index 000000000..5cf08a6e2
--- /dev/null
+++ b/module/webui/themes/Default/tml/pathchooser.html
@@ -0,0 +1,76 @@
+<html>
+<head>
+ <script class="javascript">
+ function chosen()
+ {
+ opener.ifield.value = document.forms[0].p.value;
+ close();
+ }
+ function exit()
+ {
+ close();
+ }
+ function setInvalid() {
+ document.forms[0].send.disabled = 'disabled';
+ document.forms[0].p.style.color = '#FF0000';
+ }
+ function setValid() {
+ document.forms[0].send.disabled = '';
+ document.forms[0].p.style.color = '#000000';
+ }
+ function setFile(file)
+ {
+ document.forms[0].p.value = file;
+ setValid();
+
+ }
+ </script>
+ <link rel="stylesheet" type="text/css" href="/Default/css/pathchooser.min.css"/>
+</head>
+<body{% if type == 'file' %}{% if not oldfile %} onload="setInvalid();"{% endif %}{% endif %}>
+<center>
+ <div id="paths">
+ <form method="get" action="?" onSubmit="chosen();" onReset="exit();">
+ <input type="text" name="p" value="{{ oldfile|default(cwd) }}" size="60" onfocus="setValid();">
+ <input type="submit" value="Ok" name="send">
+ </form>
+
+ {% if type == 'folder' %}
+ <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/pathchooser" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/pathchooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span>
+ {% else %}
+ <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/filechooser/" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/filechooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span>
+ {% endif %}
+ </div>
+ <table border="0" cellspacing="0" cellpadding="3">
+ <tr>
+ <th>{{_("name")}}</th>
+ <th>{{_("size")}}</th>
+ <th>{{_("type")}}</th>
+ <th>{{_("last modified")}}</th>
+ </tr>
+ {% if parentdir %}
+ <tr>
+ <td colspan="4">
+ <a href="{% if type == 'folder' %}{{ "/pathchooser/" + parentdir|quotepath }}{% else %}{{ "/filechooser/" + parentdir|quotepath }}{% endif %}"><span class="parentdir">{{_("parent directory")}}</span></a>
+ </td>
+ </tr>
+ {% endif %}
+{% for file in files %}
+ <tr>
+ {% if type == 'folder' %}
+ <td class="name">{% if file.type == 'dir' %}<a href="{{ "/pathchooser/" + file.fullpath|quotepath }}" title="{{ file.fullpath }}"><span class="path_directory">{{ file.name|truncate(25) }}</span></a>{% else %}<span class="path_file" title="{{ file.fullpath }}">{{ file.name|truncate(25) }}{% endif %}</span></td>
+ {% else %}
+ <td class="name">{% if file.type == 'dir' %}<a href="{{ "/filechooser/" + file.fullpath|quotepath }}" title="{{ file.fullpath }}"><span class="file_directory">{{ file.name|truncate(25) }}</span></a>{% else %}<a href="#" onclick="setFile('{{ file.fullpath }}');" title="{{ file.fullpath }}"><span class="file_file">{{ file.name|truncate(25) }}{% endif %}</span></a></td>
+ {% endif %}
+ <td class="size">{{ file.size|float|filesizeformat }}</td>
+ <td class="type">{% if file.type == 'dir' %}directory{% else %}{{ file.ext|default("file") }}{% endif %}</td>
+ <td class="mtime">{{ file.modified|date("d.m.Y - H:i:s") }}</td>
+ <tr>
+<!-- <tr>
+ <td colspan="4">{{_("no content")}}</td>
+ </tr> -->
+{% endfor %}
+ </table>
+ </center>
+</body>
+</html>
diff --git a/module/web/templates/default/queue.html b/module/webui/themes/Default/tml/queue.html
index 046abbe49..0fa9580e0 100644
--- a/module/web/templates/default/queue.html
+++ b/module/webui/themes/Default/tml/queue.html
@@ -1,7 +1,7 @@
-{% extends 'default/base.html' %}
+{% extends '/Default/tml/base.html' %}
{% block head %}
-<script type="text/javascript" src="/media/js/package_ui.js"></script>
+<script type="text/javascript" src="/Default/js/render/package.min.js"></script>
<script type="text/javascript">
@@ -35,19 +35,19 @@ document.addEvent("domready", function(){
<li>
<div id="package_{{package.pid}}" class="package">
<div class="order" style="display: none;">{{ package.order }}</div>
-
+
<div class="packagename" style="cursor: pointer">
- <img class="package_drag" src="/media/default/img/folder.png" style="cursor: move; margin-bottom: -2px">
+ <img class="package_drag" src="/Default/img/folder.png" style="cursor: move; margin-bottom: -2px">
<span class="name">{{package.name}}</span>
&nbsp;&nbsp;
<span class="buttons" style="opacity:0">
- <img title="{{_("Delete Package")}}" style="cursor: pointer" width="12px" height="12px" src="/media/default/img/delete.png" />
+ <img title="{{_("Delete Package")}}" style="cursor: pointer" width="12px" height="12px" src="/Default/img/delete.png" />
&nbsp;&nbsp;
- <img title="{{_("Restart Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/arrow_refresh.png" />
+ <img title="{{_("Restart Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/Default/img/arrow_refresh.png" />
&nbsp;&nbsp;
- <img title="{{_("Edit Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/pencil.png" />
+ <img title="{{_("Edit Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/Default/img/pencil.png" />
&nbsp;&nbsp;
- <img title="{{_("Move Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/package_go.png" />
+ <img title="{{_("Move Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/Default/img/package_go.png" />
</span>
</div>
{% set progress = (package.linksdone * 100) / package.linkstotal %}
@@ -101,4 +101,4 @@ document.addEvent("domready", function(){
</form>
</div>
-{% endblock %} \ No newline at end of file
+{% endblock %}
diff --git a/module/web/templates/default/settings.html b/module/webui/themes/Default/tml/settings.html
index a4443025a..9205f011b 100644
--- a/module/web/templates/default/settings.html
+++ b/module/webui/themes/Default/tml/settings.html
@@ -1,12 +1,12 @@
-{% extends 'default/base.html' %}
+{% extends '/Default/tml/base.html' %}
{% block title %}{{ _("Config") }} - {{ super() }} {% endblock %}
{% block subtitle %}{{ _("Config") }}{% endblock %}
{% block head %}
- <script type="text/javascript" src="/media/js/tinytab_static.js"></script>
- <script type="text/javascript" src="/media/js/MooDropMenu_static.js"></script>
- <script type="text/javascript" src="/media/js/settings.js"></script>
+ <script type="text/javascript" src="/Default/js/static/tinytab.min.js"></script>
+ <script type="text/javascript" src="/Default/js/static/MooDropMenu.min.js"></script>
+ <script type="text/javascript" src="/Default/js/render/settings.min.js"></script>
{% endblock %}
@@ -107,13 +107,13 @@
<td>
<span style="padding:5px">{{ plugin }}</span>
</td>
-
+
<td><label for="{{plugin}}|password;{{account.login}}"
style="color:#424242;">{{ account.login }}</label></td>
<td>
<input id="{{plugin}}|password;{{account.login}}"
name="{{plugin}}|password;{{account.login}}"
- type="password" value="{{account.password}}" size="12"/>
+ type="password" size="12"/>
</td>
<td>
{% if account.valid %}
@@ -148,12 +148,12 @@
<td>
<input id="{{plugin}}|time;{{account.login}}"
name="{{plugin}}|time;{{account.login}}" type="text"
- size="7" value="{{account.time}}"/>
+ size="7" value="{{account.options['time']}}"/>
</td>
<td>
<input id="{{plugin}}|limitdl;{{account.login}}"
name="{{plugin}}|limitdl;{{account.login}}" type="text"
- size="2" value="{{account.limitdl}}"/>
+ size="2" value="{{account.options['limitdl']}}"/>
</td>
<td>
<input id="{{plugin}}|delete;{{account.login}}"
@@ -201,4 +201,4 @@
</form>
</div>
-{% endblock %} \ No newline at end of file
+{% endblock %}
diff --git a/module/webui/themes/Default/tml/settings_item.html b/module/webui/themes/Default/tml/settings_item.html
new file mode 100644
index 000000000..6642d34b4
--- /dev/null
+++ b/module/webui/themes/Default/tml/settings_item.html
@@ -0,0 +1,48 @@
+<table class="settable">
+ {% if section.outline %}
+ <tr><th colspan="2">{{ section.outline }}</th></tr>
+ {% endif %}
+ {% for okey, option in section.iteritems() %}
+ {% if okey not in ("desc", "outline") %}
+ <tr>
+ <td><label for="{{skey}}|{{okey}}"
+ style="color:#424242;">{{ option.desc }}:</label></td>
+ <td>
+ {% if option.type == "bool" %}
+ <select id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}">
+ <option {% if option.value %} selected="selected"
+ {% endif %}value="True">{{ _("on") }}</option>
+ <option {% if not option.value %} selected="selected"
+ {% endif %}value="False">{{ _("off") }}</option>
+ </select>
+ {% elif ";" in option.type %}
+ <select id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}">
+ {% for entry in option.list %}
+ <option {% if option.value == entry %}
+ selected="selected" {% endif %}>{{ entry }}</option>
+ {% endfor %}
+ </select>
+ {% elif option.type == "folder" %}
+ <input name="{{skey}}|{{okey}}" type="text"
+ id="{{skey}}|{{okey}}" value="{{option.value}}"/>
+ <input name="browsebutton" type="button"
+ onclick="ifield = document.getElementById('{{skey}}|{{okey}}'); pathchooser = window.open('{% if option.value %}{{ "/pathchooser/" + option.value|quotepath }}{% else %}{{ pathroot }}{% endif %}', 'pathchooser', 'scrollbars=yes,toolbar=no,menubar=no,statusbar=no,width=650,height=300'); pathchooser.ifield = ifield; window.ifield = ifield;"
+ value="{{_("Browse")}}"/>
+ {% elif option.type == "file" %}
+ <input name="{{skey}}|{{okey}}" type="text"
+ id="{{skey}}|{{okey}}" value="{{option.value}}"/>
+ <input name="browsebutton" type="button"
+ onclick="ifield = document.getElementById('{{skey}}|{{okey}}'); filechooser = window.open('{% if option.value %}{{ "/filechooser/" + option.value|quotepath }}{% else %}{{ fileroot }}{% endif %}', 'filechooser', 'scrollbars=yes,toolbar=no,menubar=no,statusbar=no,width=650,height=300'); filechooser.ifield = ifield; window.ifield = ifield;"
+ value="{{_("Browse")}}"/>
+ {% elif option.type == "password" %}
+ <input id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}"
+ type="password" value="{{option.value}}"/>
+ {% else %}
+ <input id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}"
+ type="text" value="{{option.value}}"/>
+ {% endif %}
+ </td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+</table>
diff --git a/module/web/templates/default/window.html b/module/webui/themes/Default/tml/window.html
index a11323fe0..800874af0 100644
--- a/module/web/templates/default/window.html
+++ b/module/webui/themes/Default/tml/window.html
@@ -12,7 +12,7 @@
<label for="add_links">{{_("Links")}}
<span class="small">{{_("Paste your links here or any text and press the filter button.")}}</span>
<span class="small"> {{ _("Filter urls") }}
-<img alt="URIParsing" Title="Parse Uri" src="/media/default/img/parseUri.png" style="cursor:pointer; vertical-align: text-bottom;" onclick="parseUri()"/>
+<img alt="URIParsing" Title="Parse Uri" src="/Default/img/parseUri.png" style="cursor:pointer; vertical-align: text-bottom;" onclick="parseUri()"/>
</span>
</label>
@@ -43,4 +43,4 @@
</form>
-</div> \ No newline at end of file
+</div>
diff --git a/module/webui/themes/Flat/css/MooDialog.css b/module/webui/themes/Flat/css/MooDialog.css
new file mode 100644
index 000000000..7d6e38ece
--- /dev/null
+++ b/module/webui/themes/Flat/css/MooDialog.css
@@ -0,0 +1,85 @@
+/* Created by Arian Stolwijk <http://www.aryweb.nl> */
+
+.MooDialog {
+/* position: fixed;*/
+ margin: 0 auto 0 -350px;
+ width:600px;
+ padding:14px;
+ left:50%;
+ top: 100px;
+
+ position: absolute;
+ left: 50%;
+ z-index: 50000;
+
+ background-color: #E44424;
+ color: black;
+ opacity:0.9;
+}
+
+.MooDialogTitle {
+ padding-top: 30px;
+}
+
+.MooDialog .title {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ padding: 3px 20px;
+ background: #b7c4dc;
+ border-bottom: 1px solid #a1aec5;
+ font-weight: bold;
+ text-shadow: 1px 1px 0 #fff;
+ color: black;
+ border-radius: 7px;
+ -moz-border-radius: 7px;
+ -webkit-border-radius: 7px;
+}
+
+.MooDialog .close {
+ background: url(../img/control_cancel.png) no-repeat;
+ width: 16px;
+ height: 16px;
+ display: block;
+ cursor: pointer;
+ top: -5px;
+ left: -5px;
+ position: absolute;
+}
+
+.MooDialog .buttons {
+ text-align: right;
+ margin: 0;
+ padding: 0;
+ border: 0;
+ background: none;
+}
+
+.MooDialog .iframe {
+ width: 100%;
+ height: 100%;
+}
+
+.MooDialog .textInput {
+ width: 200px;
+ float: left;
+}
+
+.MooDialog .MooDialogAlert,
+.MooDialog .MooDialogConfirm,
+.MooDialog .MooDialogPrompt,
+.MooDialog .MooDialogError {
+ background: url(../img/MooDialog/dialog-warning.png) no-repeat;
+ padding-left: 40px;
+ min-height: 40px;
+}
+
+.MooDialog .MooDialogConfirm,
+.MooDialog .MooDialogPromt {
+ background: url(../img/MooDialog/dialog-question.png) no-repeat;
+}
+
+.MooDialog .MooDialogError {
+ background: url(../img/MooDialog/dialog-error.png) no-repeat;
+}
diff --git a/module/webui/themes/Flat/css/base.css b/module/webui/themes/Flat/css/base.css
new file mode 100644
index 000000000..fd73d0a19
--- /dev/null
+++ b/module/webui/themes/Flat/css/base.css
@@ -0,0 +1,866 @@
+.hidden {
+ display:none;
+}
+.leftalign {
+ text-align:left;
+}
+.centeralign {
+ text-align:center;
+}
+.rightalign {
+ text-align:right;
+}
+.dokuwiki div.plugin_translation ul li a.wikilink1:link, .dokuwiki div.plugin_translation ul li a.wikilink1:hover, .dokuwiki div.plugin_translation ul li a.wikilink1:active, .dokuwiki div.plugin_translation ul li a.wikilink1:visited {
+ background-color:#000080;
+ border:none !important;
+ color:#FFFFFF !important;
+ margin:0.1em 0.2em;
+ padding:0 0.2em;
+ text-decoration:none;
+}
+.dokuwiki div.plugin_translation ul li a.wikilink2:link, .dokuwiki div.plugin_translation ul li a.wikilink2:hover, .dokuwiki div.plugin_translation ul li a.wikilink2:active, .dokuwiki div.plugin_translation ul li a.wikilink2:visited {
+ background-color:#808080;
+ border:none !important;
+ color:#FFFFFF !important;
+ margin:0.1em 0.2em;
+ padding:0 0.2em;
+ text-decoration:none;
+}
+.dokuwiki div.plugin_translation ul li a:hover img {
+ height:15px;
+ opacity:1;
+}
+body {
+ background-color:white;
+ color:black;
+ font-family:'Open Sans', sans-serif;
+ font-size:12px;
+ font-style:normal;
+ font-variant:normal;
+ font-weight:300;
+ line-height:normal;
+ margin:0;
+ padding:0;
+}
+hr {
+ border-bottom-color:#AAAAAA;
+ border-bottom-style:dotted;
+}
+img {
+ border:none;
+}
+form {
+ background-color:transparent;
+ border:none;
+ display:inline;
+ margin:0;
+ padding:0;
+}
+ul li {
+ margin:5px;
+}
+textarea {
+ font-family:monospace;
+}
+table {
+ border-collapse:collapse;
+ margin:0.5em 0;
+}
+td {
+ border:1pt solid #ADB9CC;
+ padding:0.25em;
+}
+a {
+ color:#3465A4;
+ text-decoration:none;
+}
+a:hover {
+ text-decoration:underline;
+}
+option {
+ border:0 none #FFFFFF;
+}
+strong.highlight {
+ background-color:#FFCC99;
+ padding:1pt;
+}
+#pagebottom {
+ clear:both;
+}
+hr {
+ background-color:#C0C0C0;
+ border:none;
+ color:#C0C0C0;
+ margin:0.2em 0;
+}
+.invisible {
+ border:0;
+ height:0;
+ margin:0;
+ padding:0;
+ visibility:hidden;
+}
+.left {
+ float:left !important;
+}
+.right {
+ float:right !important;
+}
+.center {
+ text-align:center;
+}
+div#body-wrapper {
+ font-size:127%;
+ padding:40px 40px 10px;
+}
+div#content {
+ color:black;
+ font-size:14px;
+ line-height:1.5em;
+ margin-top:-20px;
+ padding:0;
+}
+h1, h2, h3, h4, h5, h6 {
+ background-attachment:scroll;
+ background-color:transparent;
+ background-image:none;
+ background-position:0 0;
+ background-repeat:repeat repeat;
+ color:black;
+ font-family:'Open Sans', sans-serif;
+ font-weight:normal;
+ margin:0;
+ padding:0.5em 0 0.17em;
+}
+h1 {
+ font-family:'Open Sans', sans-serif;
+ font-weight:300;
+ line-height:1.2em;
+ margin-bottom:0.1em;
+ padding-bottom:0;
+ margin-left:-25px;
+}
+h2 {
+ font-size:150%;
+}
+h3, h4, h5, h6 {
+ border-bottom-style:none;
+ font-weight:bold;
+}
+h3 {
+ font-size:132%;
+}
+h4 {
+ font-size:116%;
+}
+h5 {
+ font-size:100%;
+}
+h6 {
+ font-size:80%;
+}
+ul#page-actions, ul#page-actions-more {
+ background-color:#ECECEC;
+ color:black;
+ float:right;
+ list-style-type:none;
+ margin:10px 10px 0;
+ padding:6px;
+ white-space:nowrap;
+}
+ul#user-actions {
+ background-color:#67BCDB;
+ color:black;
+ display:inline;
+ list-style-type:none;
+ margin:0;
+ padding:10px;
+}
+ul#page-actions li, ul#user-actions li, ul#page-actions-more li {
+ display:inline;
+}
+ul#page-actions a, ul#user-actions a, ul#page-actions-more a {
+ color:black;
+ display:inline;
+ margin:0 3px;
+ padding:2px 0 2px 18px;
+ text-decoration:none;
+}
+ul#page-actions a:hover, ul#page-actions a:focus, ul#user-actions a:hover, ul#user-actions a:focus {
+}
+ul#page-actions2 {
+ background-color:#ECECEC;
+ color:black;
+ float:left;
+ list-style-type:none;
+ margin:10px 10px 0;
+ padding:6px;
+}
+ul#user-actions2 {
+ background-color:#ECECEC;
+ border-bottom-left-radius:3px;
+ border-bottom-right-radius:3px;
+ border-top-left-radius:3px;
+ border-top-right-radius:3px;
+ color:black;
+ display:inline;
+ list-style-type:none;
+ margin:0;
+ padding:5px;
+}
+ul#page-actions2 li, ul#user-actions2 li {
+ display:inline;
+}
+ul#page-actions2 a, ul#user-actions2 a {
+ color:black;
+ display:inline;
+ margin:0 3px;
+ padding:2px 0 2px 18px;
+ text-decoration:none;
+}
+ul#page-actions2 a:hover, ul#page-actions2 a:focus, ul#user-actions2 a:hover, ul#user-actions2 a:focus, ul#page-actions-more a:hover, ul#page-actions-more a:focus {
+ color:#4E7BB4;
+}
+.hidden {
+ display:none;
+}
+a.logout {
+ background-color:transparent;
+ background-image:url(../img/user-actions-logout.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.info {
+ background-color:transparent;
+ background-image:url(../img/user-info.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.admin {
+ background-color:transparent;
+ background-image:url(../img/user-actions-admin.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.profile {
+ background-color:transparent;
+ background-image:url(../img/user-actions-profile.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.create, a.edit {
+ background-color:transparent;
+ background-image:url(../img/page-tools-edit.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.source, a.show {
+ background-color:transparent;
+ background-image:url(../img/page-tools-source.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.revisions {
+ background-color:transparent;
+ background-image:url(../img/page-tools-revisions.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.subscribe, a.unsubscribe {
+ background-color:transparent;
+ background-image:url(../img/page-tools-subscribe.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.backlink {
+ background-color:transparent;
+ background-image:url(../img/page-tools-backlinks.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.play {
+ background-color:transparent;
+ background-image:url(../img/control_play.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+.time {
+ background-color:transparent;
+ background-image:url(../img/status_None.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+ margin:0 3px;
+ padding:2px 0 2px 18px;
+}
+.reconnect {
+ background-color:transparent;
+ background-image:url(../img/reconnect.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+ margin:0 3px;
+ padding:2px 0 2px 18px;
+}
+a.play:hover {
+ background-color:transparent;
+ background-image:url(../img/control_play_blue.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.cancel {
+ background-color:transparent;
+ background-image:url(../img/control_cancel.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.cancel:hover {
+ background-color:transparent;
+ background-image:url(../img/control_cancel_blue.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.pause {
+ background-color:transparent;
+ background-image:url(../img/control_pause.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.pause:hover {
+ background-color:transparent;
+ background-image:url(../img/control_pause_blue.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+ font-weight:bold;
+}
+a.stop {
+ background-color:transparent;
+ background-image:url(../img/control_stop.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.stop:hover {
+ background-color:transparent;
+ background-image:url(../img/control_stop_blue.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.add {
+ background-color:transparent;
+ background-image:url(../img/control_add.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.add:hover {
+ background-color:transparent;
+ background-image:url(../img/control_add_blue.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+a.cog {
+ background-color:transparent;
+ background-image:url(../img/cog.png);
+ background-position:0 1px;
+ background-repeat:no-repeat no-repeat;
+}
+#head-panel {
+ background-color:#67BCDB;
+ background-position:0 100%;
+ background-repeat:repeat no-repeat;
+}
+#head-panel h1 {
+ color:#EEEEEC;
+ display:none;
+ font-size:2.6em;
+ margin:0;
+ padding-left:3.3em;
+ padding-top:0.8em;
+ text-decoration:none;
+}
+#head-panel #head-logo {
+ float:left;
+ overflow:visible;
+ padding:11px 8px 0;
+}
+#head-menu {
+ float:left;
+ margin:0;
+ padding:1em 0 0;
+ width:100%;
+}
+#head-menu ul {
+ list-style:none;
+ margin:0 1em 0 2em;
+}
+#head-menu ul li {
+ float:left;
+ font-size:14px;
+ margin:0 0 4px 0.3em;
+}
+#head-menu ul li.selected, #head-menu ul li:hover {
+ margin-bottom:0;
+}
+#head-menu ul li a img {
+ height:22px;
+ padding-right:4px;
+ vertical-align:middle;
+ width:22px;
+}
+#head-menu ul li a, #head-menu ul li a:link {
+ background-color:#FFFFFF;
+ background-position:0 100%;
+ background-repeat:repeat no-repeat;
+ border-color:#CCCCCC #CCCCCC transparent;
+ color:#555555;
+ padding:7px 15px 8px;
+ text-decoration:none;
+ opacity:0.7;
+}
+#head-menu ul li a:hover, #head-menu ul li a:focus {
+ border-bottom-color:transparent;
+ border-bottom-left-radius:0;
+ border-bottom-right-radius:0;
+ border-bottom-style:none;
+ border-bottom-width:0;
+ outline:none;
+ padding-bottom:7px;
+ opacity:1;
+ background-color: #FF9009;
+ transition: background 0.2s ease-out, opacity 0.5s ease-in;
+}
+#head-menu ul li a:focus {
+ margin-bottom:-4px;
+}
+#head-menu ul li.selected a {
+ border-bottom-color:transparent;
+ color:#3566A5;
+ padding:7px 15px 8px;
+ opacity:1;
+}
+#head-menu ul li.selected a:hover, #head-menu ul li.selected a:focus {
+ color:#111111;
+}
+div#head-search-and-login {
+ float:right;
+ margin:0 12px 0 0;
+ padding:7px 0px 7px 7px;
+ white-space:nowrap;
+ background-color:#FFF056;
+ opacity:0.9;
+}
+div#head-search-and-login form {
+ display:inline;
+ padding:0 3px;
+}
+div#head-search-and-login form input {
+ background-color:#EEEEEE;
+ border:2px solid #888888;
+ border-bottom-left-radius:3px;
+ border-bottom-right-radius:3px;
+ border-top-left-radius:3px;
+ border-top-right-radius:3px;
+ font-size:14px;
+ padding:2px;
+}
+div#head-search-and-login form input:focus {
+ background-color:#FFFFFF;
+}
+#head-search {
+ font-size:14px;
+}
+#head-username, #head-password {
+ font-size:14px;
+ width:80px;
+}
+#pageinfo {
+ clear:both;
+ color:#888888;
+ margin:0;
+ padding:0.6em 0;
+}
+#foot {
+ color:#888888;
+ font-style:normal;
+ text-align:center;
+}
+#foot a {
+ color:#AAAAFF;
+}
+#foot img {
+ vertical-align:middle;
+}
+div.toc {
+ background-color:#F0F0F0;
+ border:1px dotted #888888;
+ float:right;
+ font-size:95%;
+ margin:1em 0 1em 1em;
+}
+div.toc .tocheader {
+ font-weight:bold;
+ margin:0.5em 1em;
+}
+div.toc ol {
+ margin:1em 0.5em 1em 1em;
+ padding:0;
+}
+div.toc ol li {
+ margin:0 0 0 1em;
+ padding:0;
+}
+div.toc ol ol {
+ margin:0.5em 0.5em 0.5em 1em;
+ padding:0;
+}
+div.recentchanges table {
+ clear:both;
+}
+div#editor-help {
+ background-color:#F7F6F2;
+ border:1px dotted #888888;
+ font-size:90%;
+ padding:0 1ex 1ex;
+}
+div#preview {
+ margin-top:1em;
+}
+label.block {
+ display:block;
+ font-weight:bold;
+ text-align:right;
+}
+label.simple {
+ display:block;
+ font-weight:normal;
+ text-align:left;
+}
+label.block input.edit {
+ width:50%;
+}
+div.editor {
+ margin:0;
+}
+table {
+ border-collapse:collapse;
+ margin:0.5em 0;
+}
+td {
+ border:1pt solid #ADB9CC;
+ padding:0.25em;
+}
+td p {
+ margin:0;
+ padding:0;
+}
+.u {
+ text-decoration:underline;
+}
+.footnotes ul {
+ margin:0 0 1em;
+ padding:0 2em;
+}
+.footnotes li {
+ list-style:none;
+}
+.userpref table, .userpref td {
+ border:none;
+}
+#message {
+ background-color:#EEEEEE;
+ border-bottom-color:#CCCCCC;
+ border-bottom-style:solid;
+ border-bottom-width:2px;
+ clear:both;
+ padding:5px 10px;
+}
+#message p {
+ font-weight:bold;
+ margin:5px 0;
+ padding:0;
+}
+#message div.buttons {
+ font-weight:normal;
+}
+.diff {
+ width:99%;
+}
+.diff-title {
+ background-color:#C0C0C0;
+}
+.searchresult dd span {
+ font-weight:400;
+}
+.boxtext {
+ color:#000000;
+ float:none;
+ font-family:tahoma, arial, sans-serif;
+ font-size:11px;
+ padding:3px 0 0 10px;
+}
+.statusbutton {
+ cursor:pointer;
+ float:left;
+ height:32px;
+ margin-left:-32px;
+ margin-right:5px;
+ opacity:0;
+ width:32px;
+}
+.dlsize {
+ float:left;
+ padding-right:8px;
+}
+.dlspeed {
+ float:left;
+ padding-right:8px;
+}
+.package {
+ margin-bottom:10px;
+}
+.packagename {
+ font-weight:300;
+}
+.child {
+ margin-left:20px;
+}
+.child_status {
+ margin-right:10px;
+}
+.child_secrow {
+ font-size:10px;
+}
+.header, .header th {
+ background-color:#ECECEC;
+ font-weight:300;
+ text-align:left;
+}
+.progress_bar {
+ background-color:#00CC00;
+ height:5px;
+}
+.queue {
+ border:none;
+}
+.queue tr td {
+ border:none;
+}
+.header, .header th {
+ font-weight:normal;
+ text-align:left;
+}
+.clearer {
+ clear:both;
+ height:1px;
+}
+.left {
+ float:left;
+}
+.right {
+ float:right;
+}
+.setfield {
+ display:table-cell;
+}
+ul.tabs li a {
+ border:none;
+ border-bottom-left-radius:0;
+ border-bottom-right-radius:0;
+ border-top-left-radius:5px;
+ border-top-right-radius:5px;
+ font-weight:bold;
+ padding:5px 16px 4px 15px;
+}
+#tabs span {
+ display:none;
+}
+#tabs span.selected {
+ display:inline;
+}
+#tabsback {
+ background-color:#525252;
+ border-top-left-radius:3px;
+ border-top-right-radius:30px;
+ margin:2px 0 0;
+ padding:6px 4px 1px;
+}
+ul.tabs {
+ list-style-type:none;
+ margin:0;
+ padding:0 40px 0 0;
+}
+ul.tabs li {
+ display:inline;
+ margin-left:8px;
+}
+ul.tabs li a {
+ background-color:#EAEAEA;
+ border:1px none #C9C3BA;
+ border-bottom-left-radius:0;
+ border-bottom-right-radius:0;
+ border-top-left-radius:5px;
+ border-top-right-radius:5px;
+ color:#42454A;
+ font-weight:bold;
+ margin:0;
+ outline:0;
+ padding:5px 16px 4px 15px;
+ text-decoration:none;
+}
+ul.tabs li a.selected, ul.tabs li a:hover {
+ background-color:white;
+ border-bottom-left-radius:0;
+ border-bottom-right-radius:0;
+ color:#000000;
+}
+ul.tabs li a:hover {
+ background-color:#F1F4EE;
+}
+ul.tabs li a.selected {
+ background-color:#525252;
+ color:white;
+ font-weight:bold;
+ padding-bottom:5px;
+}
+#tabs-body {
+ overflow:hidden;
+ position:relative;
+}
+span.tabContent {
+ border:2px solid #525252;
+ margin:0;
+ padding:0 0 10px;
+}
+#tabs-body > span {
+ display:none;
+}
+#tabs-body > span.active {
+ display:block;
+}
+.hide {
+ display:none;
+}
+.settable {
+ border:none;
+ margin:20px;
+}
+.settable td {
+ border:none;
+ margin:0;
+ padding:5px;
+}
+.settable th {
+ padding-bottom:8px;
+}
+.settable.wide td, .settable.wide th {
+ padding-left:15px;
+ padding-right:15px;
+}
+ul.nav {
+ list-style:none;
+ margin:-30px 0 0;
+ padding:0;
+ position:absolute;
+}
+ul.nav li {
+ float:left;
+ padding:5px;
+ position:relative;
+}
+ul.nav > li a {
+ background-color:white;
+ border-left-color:#C9C3BA;
+ border-right-color:#C9C3BA;
+ border-style:solid solid none;
+ border-top-color:#C9C3BA;
+ border-width:1px 1px medium;
+ color:black;
+}
+ul.nav ul {
+ -webkit-box-shadow:#AAAAAA 1px 1px 5px;
+ background-color:#F1F1F1;
+ border:1px solid #AAAAAA;
+ box-shadow:#AAAAAA 1px 1px 5px;
+ cursor:pointer;
+ left:10px;
+ list-style:none;
+ margin:0;
+ padding:0;
+ position:absolute;
+ top:26px;
+}
+ul.nav .open {
+ display:block;
+}
+ul.nav .close {
+ display:none;
+}
+ul.nav ul li {
+ float:none;
+ padding:0;
+}
+ul.nav ul li a {
+ background-color:#F1F1F1;
+ display:block;
+ font-weight:normal;
+ padding:3px;
+ width:130px;
+}
+ul.nav ul li a:hover {
+ background-color:#CDCDCD;
+}
+ul.nav ul ul {
+ left:137px;
+ top:0;
+}
+.purr-wrapper {
+ margin:10px;
+}
+.purr-alert {
+ background-color:#000000;
+ border-bottom-left-radius:5px;
+ border-bottom-right-radius:5px;
+ border-top-left-radius:5px;
+ border-top-right-radius:5px;
+ color:#FFFFFF;
+ font-size:13px;
+ font-weight:bold;
+ margin-bottom:10px;
+ padding:10px;
+ width:300px;
+}
+.purr-alert.error {
+ background-color:#000000;
+ background-image:url(../img/error.png);
+ background-position:7px 10px;
+ background-repeat:no-repeat no-repeat;
+ color:#FF5555;
+ padding-left:30px;
+ width:280px;
+}
+.purr-alert.success {
+ background-color:#000000;
+ background-image:url(../img/success.png);
+ background-position:7px 10px;
+ background-repeat:no-repeat no-repeat;
+ color:#55FF55;
+ padding-left:30px;
+ width:280px;
+}
+.purr-alert.notice {
+ background-color:#000000;
+ background-image:url(../img/notice.png);
+ background-position:7px 10px;
+ background-repeat:no-repeat no-repeat;
+ color:#9999FF;
+ padding-left:30px;
+ width:280px;
+}
+table.system {
+ border:none;
+ margin-left:10px;
+}
+table.system td {
+ border:none;
+}
+table.system tr > td:first-child {
+ font-weight:bold;
+ padding-right:10px;
+} \ No newline at end of file
diff --git a/module/webui/themes/Flat/css/log.css b/module/webui/themes/Flat/css/log.css
new file mode 100644
index 000000000..c2f21b506
--- /dev/null
+++ b/module/webui/themes/Flat/css/log.css
@@ -0,0 +1,72 @@
+
+html, body, #content
+{
+ height: 100%;
+}
+#body-wrapper
+{
+ height: 70%;
+}
+.logdiv
+{
+ height: 90%;
+ width: 100%;
+ overflow: auto;
+ border: 2px solid #CCC;
+ outline: 1px solid #666;
+ background-color: #FFE;
+ margin-right: auto;
+ margin-left: auto;
+}
+.logform
+{
+ display: table;
+ margin: 0 auto 0 auto;
+ padding-top: 5px;
+}
+.logtable
+{
+
+ margin: 0px;
+}
+.logtable td
+{
+ border: none;
+ white-space: nowrap;
+
+
+ font-family: monospace;
+ font-size: 16px;
+ margin: 0px;
+ padding: 0px 10px 0px 10px;
+ line-height: 110%;
+}
+td.logline
+{
+ background-color: #EEE;
+ text-align:right;
+ padding: 0px 5px 0px 5px;
+}
+td.loglevel
+{
+ text-align:right;
+}
+.logperpage
+{
+ float: right;
+ padding-bottom: 8px;
+}
+.logpaginator
+{
+ float: left;
+ padding-top: 5px;
+}
+.logpaginator a
+{
+ padding: 0px 8px 0px 8px;
+}
+.logwarn
+{
+ text-align: center;
+ color: red;
+} \ No newline at end of file
diff --git a/module/webui/themes/Flat/css/pathchooser.css b/module/webui/themes/Flat/css/pathchooser.css
new file mode 100644
index 000000000..204812aa1
--- /dev/null
+++ b/module/webui/themes/Flat/css/pathchooser.css
@@ -0,0 +1,68 @@
+table {
+ width: 90%;
+ border: 1px dotted #888888;
+ font-family: sans-serif;
+ font-size: 10pt;
+}
+
+th {
+ background-color: #525252;
+ color: #E0E0E0;
+}
+
+table, tr, td {
+ background-color: #F0F0F0;
+}
+
+a, a:visited {
+ text-decoration: none;
+ font-weight: bold;
+}
+
+#paths {
+ width: 90%;
+ text-align: left;
+}
+
+.file_directory {
+ color: #c0c0c0;
+}
+.path_directory {
+ color: #3c3c3c;
+}
+.file_file {
+ color: #3c3c3c;
+}
+.path_file {
+ color: #c0c0c0;
+}
+
+.parentdir {
+ color: #000000;
+ font-size: 10pt;
+}
+.name {
+ text-align: left;
+}
+.size {
+ text-align: right;
+}
+.type {
+ text-align: left;
+}
+.mtime {
+ text-align: center;
+}
+
+.path_abs_rel {
+ color: #3c3c3c;
+ text-decoration: none;
+ font-weight: bold;
+ font-family: sans-serif;
+ font-size: 10pt;
+}
+
+.path_abs_rel a {
+ color: #3c3c3c;
+ font-style: italic;
+}
diff --git a/module/webui/themes/Flat/css/window.css b/module/webui/themes/Flat/css/window.css
new file mode 100644
index 000000000..40c1aadca
--- /dev/null
+++ b/module/webui/themes/Flat/css/window.css
@@ -0,0 +1,70 @@
+/* ----------- stylized ----------- */
+.window_box h1{
+ margin-bottom:8px;
+}
+.window_box p{
+ font-size:11px;
+ color:#FFFFFF;
+ margin-bottom:20px;
+ border-bottom:solid 1px #b7ddf2;
+ padding-bottom:10px;
+}
+.window_box label{
+ display:block;
+ font-weight:bold;
+ text-align:right;
+ width:240px;
+ float:left;
+}
+.window_box .small{
+ color:#FFFFFF;
+ display:block;
+ font-size:11px;
+ font-weight:normal;
+ text-align:right;
+ width:240px;
+}
+.window_box select, .window_box input{
+ float:left;
+ font-size:12px;
+ padding:4px 2px;
+ width:300px;
+ margin:2px 0 20px 10px;
+}
+.window_box .cont{
+ float:left;
+ font-size:12px;
+ padding: 0px 10px 15px 0px;
+ width:300px;
+ margin:0px 0px 0px 10px;
+ color:#FFFFFF;
+}
+.window_box .cont input{
+ float: none;
+ margin: 0px 15px 0px 1px;
+}
+.window_box textarea{
+ float:left;
+ font-size:12px;
+ padding:4px 2px;
+ width:300px;
+ margin:2px 0 20px 10px;
+}
+.window_box button, .styled_button{
+ clear:both;
+ margin-left:150px;
+ width:125px;
+ height:31px;
+ background:url(../img/button.png) no-repeat;
+ text-align:center;
+ line-height:31px;
+ color:#FFFFFF;
+ font-size:11px;
+ font-weight:bold;
+ border: 0px;
+}
+
+.styled_button {
+ margin-left: 15px;
+ cursor: pointer;
+}
diff --git a/module/webui/themes/Flat/img/MooDialog/dialog-close.png b/module/webui/themes/Flat/img/MooDialog/dialog-close.png
new file mode 100644
index 000000000..81ebb88b2
--- /dev/null
+++ b/module/webui/themes/Flat/img/MooDialog/dialog-close.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/MooDialog/dialog-error.png b/module/webui/themes/Flat/img/MooDialog/dialog-error.png
new file mode 100644
index 000000000..d70328403
--- /dev/null
+++ b/module/webui/themes/Flat/img/MooDialog/dialog-error.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/MooDialog/dialog-question.png b/module/webui/themes/Flat/img/MooDialog/dialog-question.png
new file mode 100644
index 000000000..b0af3db5b
--- /dev/null
+++ b/module/webui/themes/Flat/img/MooDialog/dialog-question.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/MooDialog/dialog-warning.png b/module/webui/themes/Flat/img/MooDialog/dialog-warning.png
new file mode 100644
index 000000000..aad64d4be
--- /dev/null
+++ b/module/webui/themes/Flat/img/MooDialog/dialog-warning.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/arrow_refresh.png b/module/webui/themes/Flat/img/arrow_refresh.png
new file mode 100644
index 000000000..b1b6fa4dc
--- /dev/null
+++ b/module/webui/themes/Flat/img/arrow_refresh.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/arrow_right.png b/module/webui/themes/Flat/img/arrow_right.png
new file mode 100644
index 000000000..68f379fc7
--- /dev/null
+++ b/module/webui/themes/Flat/img/arrow_right.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/button.png b/module/webui/themes/Flat/img/button.png
new file mode 100644
index 000000000..bb408a7d6
--- /dev/null
+++ b/module/webui/themes/Flat/img/button.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/cog.png b/module/webui/themes/Flat/img/cog.png
new file mode 100644
index 000000000..833f779ac
--- /dev/null
+++ b/module/webui/themes/Flat/img/cog.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/control_add.png b/module/webui/themes/Flat/img/control_add.png
new file mode 100644
index 000000000..e3f29fab2
--- /dev/null
+++ b/module/webui/themes/Flat/img/control_add.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/control_add_blue.png b/module/webui/themes/Flat/img/control_add_blue.png
new file mode 100644
index 000000000..e3f29fab2
--- /dev/null
+++ b/module/webui/themes/Flat/img/control_add_blue.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/control_cancel.png b/module/webui/themes/Flat/img/control_cancel.png
new file mode 100644
index 000000000..07c9cad30
--- /dev/null
+++ b/module/webui/themes/Flat/img/control_cancel.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/control_cancel_blue.png b/module/webui/themes/Flat/img/control_cancel_blue.png
new file mode 100644
index 000000000..07c9cad30
--- /dev/null
+++ b/module/webui/themes/Flat/img/control_cancel_blue.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/control_pause.png b/module/webui/themes/Flat/img/control_pause.png
new file mode 100644
index 000000000..24e3705fa
--- /dev/null
+++ b/module/webui/themes/Flat/img/control_pause.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/control_pause_blue.png b/module/webui/themes/Flat/img/control_pause_blue.png
new file mode 100644
index 000000000..24e3705fa
--- /dev/null
+++ b/module/webui/themes/Flat/img/control_pause_blue.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/control_play.png b/module/webui/themes/Flat/img/control_play.png
new file mode 100644
index 000000000..15ced1e21
--- /dev/null
+++ b/module/webui/themes/Flat/img/control_play.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/control_play_blue.png b/module/webui/themes/Flat/img/control_play_blue.png
new file mode 100644
index 000000000..15ced1e21
--- /dev/null
+++ b/module/webui/themes/Flat/img/control_play_blue.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/control_stop.png b/module/webui/themes/Flat/img/control_stop.png
new file mode 100644
index 000000000..71215ef67
--- /dev/null
+++ b/module/webui/themes/Flat/img/control_stop.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/control_stop_blue.png b/module/webui/themes/Flat/img/control_stop_blue.png
new file mode 100644
index 000000000..71215ef67
--- /dev/null
+++ b/module/webui/themes/Flat/img/control_stop_blue.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/add_folder.png b/module/webui/themes/Flat/img/default/add_folder.png
new file mode 100644
index 000000000..8acbc411b
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/add_folder.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/ajax-loader.gif b/module/webui/themes/Flat/img/default/ajax-loader.gif
new file mode 100644
index 000000000..2fd8e0737
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/ajax-loader.gif
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/big_button.gif b/module/webui/themes/Flat/img/default/big_button.gif
new file mode 100644
index 000000000..7680490ea
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/big_button.gif
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/big_button_over.gif b/module/webui/themes/Flat/img/default/big_button_over.gif
new file mode 100644
index 000000000..2e3ee10d2
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/big_button_over.gif
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/body.png b/module/webui/themes/Flat/img/default/body.png
new file mode 100644
index 000000000..7ff1043e0
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/body.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/closebtn.gif b/module/webui/themes/Flat/img/default/closebtn.gif
new file mode 100644
index 000000000..3e27e6030
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/closebtn.gif
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/drag_corner.gif b/module/webui/themes/Flat/img/default/drag_corner.gif
new file mode 100644
index 000000000..befb1adf1
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/drag_corner.gif
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/full.png b/module/webui/themes/Flat/img/default/full.png
new file mode 100644
index 000000000..fea52af76
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/full.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/head-menu-recent.png b/module/webui/themes/Flat/img/default/head-menu-recent.png
new file mode 100644
index 000000000..fc9b0497f
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/head-menu-recent.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/head_bg1.png b/module/webui/themes/Flat/img/default/head_bg1.png
new file mode 100644
index 000000000..f2848c3cc
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/head_bg1.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/images.png b/module/webui/themes/Flat/img/default/images.png
new file mode 100644
index 000000000..184860d1e
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/images.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/parseUri.png b/module/webui/themes/Flat/img/default/parseUri.png
new file mode 100644
index 000000000..937bded9d
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/parseUri.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/pyload-logo.png b/module/webui/themes/Flat/img/default/pyload-logo.png
new file mode 100644
index 000000000..2443cd8b1
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/pyload-logo.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/tab-background.png b/module/webui/themes/Flat/img/default/tab-background.png
new file mode 100644
index 000000000..29a5d1991
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/tab-background.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/default/tabs-border-bottom.png b/module/webui/themes/Flat/img/default/tabs-border-bottom.png
new file mode 100644
index 000000000..02440f428
--- /dev/null
+++ b/module/webui/themes/Flat/img/default/tabs-border-bottom.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/delete.png b/module/webui/themes/Flat/img/delete.png
new file mode 100644
index 000000000..4539cff12
--- /dev/null
+++ b/module/webui/themes/Flat/img/delete.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/error.png b/module/webui/themes/Flat/img/error.png
new file mode 100644
index 000000000..6c565c99c
--- /dev/null
+++ b/module/webui/themes/Flat/img/error.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/folder.png b/module/webui/themes/Flat/img/folder.png
new file mode 100644
index 000000000..0b067dd3c
--- /dev/null
+++ b/module/webui/themes/Flat/img/folder.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/head-login.png b/module/webui/themes/Flat/img/head-login.png
new file mode 100644
index 000000000..6b57515bc
--- /dev/null
+++ b/module/webui/themes/Flat/img/head-login.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/head-menu-collector.png b/module/webui/themes/Flat/img/head-menu-collector.png
new file mode 100644
index 000000000..bbcbe6406
--- /dev/null
+++ b/module/webui/themes/Flat/img/head-menu-collector.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/head-menu-config.png b/module/webui/themes/Flat/img/head-menu-config.png
new file mode 100644
index 000000000..93e8f83ac
--- /dev/null
+++ b/module/webui/themes/Flat/img/head-menu-config.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/head-menu-development.png b/module/webui/themes/Flat/img/head-menu-development.png
new file mode 100644
index 000000000..33d8b062f
--- /dev/null
+++ b/module/webui/themes/Flat/img/head-menu-development.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/head-menu-download.png b/module/webui/themes/Flat/img/head-menu-download.png
new file mode 100644
index 000000000..3691deebc
--- /dev/null
+++ b/module/webui/themes/Flat/img/head-menu-download.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/head-menu-home.png b/module/webui/themes/Flat/img/head-menu-home.png
new file mode 100644
index 000000000..b77bef5eb
--- /dev/null
+++ b/module/webui/themes/Flat/img/head-menu-home.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/head-menu-index.png b/module/webui/themes/Flat/img/head-menu-index.png
new file mode 100644
index 000000000..8bc6e9604
--- /dev/null
+++ b/module/webui/themes/Flat/img/head-menu-index.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/head-menu-news.png b/module/webui/themes/Flat/img/head-menu-news.png
new file mode 100644
index 000000000..44e79a9a9
--- /dev/null
+++ b/module/webui/themes/Flat/img/head-menu-news.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/head-menu-queue.png b/module/webui/themes/Flat/img/head-menu-queue.png
new file mode 100644
index 000000000..e4fa41ad8
--- /dev/null
+++ b/module/webui/themes/Flat/img/head-menu-queue.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/head-menu-wiki.png b/module/webui/themes/Flat/img/head-menu-wiki.png
new file mode 100644
index 000000000..61b0e54ea
--- /dev/null
+++ b/module/webui/themes/Flat/img/head-menu-wiki.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/head-search-noshadow.png b/module/webui/themes/Flat/img/head-search-noshadow.png
new file mode 100644
index 000000000..16d39bd06
--- /dev/null
+++ b/module/webui/themes/Flat/img/head-search-noshadow.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/notice.png b/module/webui/themes/Flat/img/notice.png
new file mode 100644
index 000000000..a52b067cb
--- /dev/null
+++ b/module/webui/themes/Flat/img/notice.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/package_go.png b/module/webui/themes/Flat/img/package_go.png
new file mode 100644
index 000000000..80b2c42ee
--- /dev/null
+++ b/module/webui/themes/Flat/img/package_go.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/page-tools-backlinks.png b/module/webui/themes/Flat/img/page-tools-backlinks.png
new file mode 100644
index 000000000..fb8f55b38
--- /dev/null
+++ b/module/webui/themes/Flat/img/page-tools-backlinks.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/page-tools-edit.png b/module/webui/themes/Flat/img/page-tools-edit.png
new file mode 100644
index 000000000..67177cf89
--- /dev/null
+++ b/module/webui/themes/Flat/img/page-tools-edit.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/page-tools-revisions.png b/module/webui/themes/Flat/img/page-tools-revisions.png
new file mode 100644
index 000000000..088fe0087
--- /dev/null
+++ b/module/webui/themes/Flat/img/page-tools-revisions.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/pencil.png b/module/webui/themes/Flat/img/pencil.png
new file mode 100644
index 000000000..e39c93cd8
--- /dev/null
+++ b/module/webui/themes/Flat/img/pencil.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/reconnect.png b/module/webui/themes/Flat/img/reconnect.png
new file mode 100644
index 000000000..cd35c9325
--- /dev/null
+++ b/module/webui/themes/Flat/img/reconnect.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/status_None.png b/module/webui/themes/Flat/img/status_None.png
new file mode 100644
index 000000000..1400d3eb3
--- /dev/null
+++ b/module/webui/themes/Flat/img/status_None.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/status_downloading.png b/module/webui/themes/Flat/img/status_downloading.png
new file mode 100644
index 000000000..db8ad8cd6
--- /dev/null
+++ b/module/webui/themes/Flat/img/status_downloading.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/status_failed.png b/module/webui/themes/Flat/img/status_failed.png
new file mode 100644
index 000000000..6c565c99c
--- /dev/null
+++ b/module/webui/themes/Flat/img/status_failed.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/status_finished.png b/module/webui/themes/Flat/img/status_finished.png
new file mode 100644
index 000000000..2c4aca40d
--- /dev/null
+++ b/module/webui/themes/Flat/img/status_finished.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/status_offline.png b/module/webui/themes/Flat/img/status_offline.png
new file mode 100644
index 000000000..6c565c99c
--- /dev/null
+++ b/module/webui/themes/Flat/img/status_offline.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/status_proc.png b/module/webui/themes/Flat/img/status_proc.png
new file mode 100644
index 000000000..833f779ac
--- /dev/null
+++ b/module/webui/themes/Flat/img/status_proc.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/status_queue.png b/module/webui/themes/Flat/img/status_queue.png
new file mode 100644
index 000000000..1400d3eb3
--- /dev/null
+++ b/module/webui/themes/Flat/img/status_queue.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/status_waiting.png b/module/webui/themes/Flat/img/status_waiting.png
new file mode 100644
index 000000000..fd038175e
--- /dev/null
+++ b/module/webui/themes/Flat/img/status_waiting.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/success.png b/module/webui/themes/Flat/img/success.png
new file mode 100644
index 000000000..2c4aca40d
--- /dev/null
+++ b/module/webui/themes/Flat/img/success.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/user-actions-logout.png b/module/webui/themes/Flat/img/user-actions-logout.png
new file mode 100644
index 000000000..d4ef360e8
--- /dev/null
+++ b/module/webui/themes/Flat/img/user-actions-logout.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/user-actions-profile.png b/module/webui/themes/Flat/img/user-actions-profile.png
new file mode 100644
index 000000000..9ec410b13
--- /dev/null
+++ b/module/webui/themes/Flat/img/user-actions-profile.png
Binary files differ
diff --git a/module/webui/themes/Flat/img/user-info.png b/module/webui/themes/Flat/img/user-info.png
new file mode 100644
index 000000000..345ed52e4
--- /dev/null
+++ b/module/webui/themes/Flat/img/user-info.png
Binary files differ
diff --git a/module/webui/themes/Flat/js/render/admin.coffee b/module/webui/themes/Flat/js/render/admin.coffee
new file mode 100644
index 000000000..5afbcbb66
--- /dev/null
+++ b/module/webui/themes/Flat/js/render/admin.coffee
@@ -0,0 +1,58 @@
+root = this
+
+window.addEvent "domready", ->
+
+ root.passwordDialog = new MooDialog {destroyOnHide: false}
+ root.passwordDialog.setContent $ 'password_box'
+
+ $("login_password_reset").addEvent "click", (e) -> root.passwordDialog.close()
+ $("login_password_button").addEvent "click", (e) ->
+
+ newpw = $("login_new_password").get("value")
+ newpw2 = $("login_new_password2").get("value")
+
+ if newpw is newpw2
+ form = $("password_form")
+ form.set "send", {
+ onSuccess: (data) ->
+ root.notify.alert "Success", {
+ 'className': 'success'
+ }
+ onFailure: (data) ->
+ root.notify.alert "Error", {
+ 'className': 'error'
+ }
+ }
+
+ form.send()
+
+ root.passwordDialog.close()
+ else
+ alert '{{_("Passwords did not match.")}}'
+
+ e.stop()
+
+ for item in $$(".change_password")
+ id = item.get("id")
+ user = id.split("|")[1]
+ $("user_login").set("value", user)
+ item.addEvent "click", (e) -> root.passwordDialog.open()
+
+ $('quit-pyload').addEvent "click", (e) ->
+ new MooDialog.Confirm "{{_('You are really sure you want to quit pyLoad?')}}", ->
+ new Request.JSON({
+ url: '/api/kill'
+ method: 'get'
+ }).send()
+ , ->
+ e.stop()
+
+ $('restart-pyload').addEvent "click", (e) ->
+ new MooDialog.Confirm "{{_('Are you sure you want to restart pyLoad?')}}", ->
+ new Request.JSON({
+ url: '/api/restart'
+ method: 'get'
+ onSuccess: (data) -> alert "{{_('pyLoad restarted')}}"
+ }).send()
+ , ->
+ e.stop()
diff --git a/module/webui/themes/Flat/js/render/admin.min.js b/module/webui/themes/Flat/js/render/admin.min.js
new file mode 100644
index 000000000..94a5e494d
--- /dev/null
+++ b/module/webui/themes/Flat/js/render/admin.min.js
@@ -0,0 +1,3 @@
+{% autoescape true %}
+var root;root=this;window.addEvent("domready",function(){var f,c,b,e,a,d;root.passwordDialog=new MooDialog({destroyOnHide:false});root.passwordDialog.setContent($("password_box"));$("login_password_reset").addEvent("click",function(g){return root.passwordDialog.close()});$("login_password_button").addEvent("click",function(j){var h,i,g;i=$("login_new_password").get("value");g=$("login_new_password2").get("value");if(i===g){h=$("password_form");h.set("send",{onSuccess:function(k){return root.notify.alert("Success",{className:"success"})},onFailure:function(k){return root.notify.alert("Error",{className:"error"})}});h.send();root.passwordDialog.close()}else{alert('{{_("Passwords did not match.")}}')}return j.stop()});d=$$(".change_password");for(e=0,a=d.length;e<a;e++){c=d[e];f=c.get("id");b=f.split("|")[1];$("user_login").set("value",b);c.addEvent("click",function(g){return root.passwordDialog.open()})}$("quit-pyload").addEvent("click",function(g){new MooDialog.Confirm("{{_('You are really sure you want to quit pyLoad?')}}",function(){return new Request.JSON({url:"/api/kill",method:"get"}).send()},function(){});return g.stop()});return $("restart-pyload").addEvent("click",function(g){new MooDialog.Confirm("{{_('Are you sure you want to restart pyLoad?')}}",function(){return new Request.JSON({url:"/api/restart",method:"get",onSuccess:function(h){return alert("{{_('pyLoad restarted')}}")}}).send()},function(){});return g.stop()})});
+{% endautoescape %}
diff --git a/module/webui/themes/Flat/js/render/base.coffee b/module/webui/themes/Flat/js/render/base.coffee
new file mode 100644
index 000000000..07b8bfb6f
--- /dev/null
+++ b/module/webui/themes/Flat/js/render/base.coffee
@@ -0,0 +1,177 @@
+# External scope
+root = this
+
+# helper functions
+humanFileSize = (size) ->
+ filesizename = new Array("B", "KiB", "MiB", "GiB", "TiB", "PiB")
+ loga = Math.log(size) / Math.log(1024)
+ i = Math.floor(loga)
+ a = Math.pow(1024, i)
+ if size is 0 then "0 B" else (Math.round(size * 100 / a) / 100 + " " + filesizename[i])
+
+
+parseUri = () ->
+ oldString = $("add_links").value
+ regxp = new RegExp('(ht|f)tp(s?):\/\/[a-zA-Z0-9\-\.\/\?=_&%#]+[<| |\"|\'|\r|\n|\t]{1}', 'g')
+ resu = oldString.match regxp
+ return if resu == null
+ res = ""
+
+ for part in resu
+ if part.indexOf(" ") != -1
+ res = res + part.replace(" ", " \n")
+ else if part.indexOf("\t") != -1
+ res = res + part.replace("\t", " \n")
+ else if part.indexOf("\r") != -1
+ res = res + part.replace("\r", " \n")
+ else if part.indexOf("\"") != -1
+ res = res + part.replace("\"", " \n")
+ else if part.indexOf("<") != -1
+ res = res + part.replace("<", " \n")
+ else if part.indexOf("'") != -1
+ res = res + part.replace("'", " \n")
+ else
+ res = res + part.replace("\n", " \n")
+
+ $("add_links").value = res
+
+
+Array::remove = (from, to) ->
+ rest = this.slice((to || from) + 1 || this.length)
+ this.length = from < 0 ? this.length + from : from
+ return [] if this.length == 0
+ return this.push.apply(this, rest)
+
+
+document.addEvent "domready", ->
+
+ # global notification
+ root.notify = new Purr {
+ 'mode': 'top'
+ 'position': 'center'
+ }
+
+ root.captchaBox = new MooDialog {destroyOnHide: false}
+ root.captchaBox.setContent $ 'cap_box'
+
+ root.addBox = new MooDialog {destroyOnHide: false}
+ root.addBox.setContent $ 'add_box'
+
+ $('add_form').onsubmit = ->
+ $('add_form').target = 'upload_target'
+ if $('add_name').value is "" and $('add_file').value is ""
+ alert '{{_("Please Enter a packagename.")}}'
+ return false
+ else
+ root.addBox.close()
+ return true
+
+ $('add_reset').addEvent 'click', -> root.addBox.close()
+
+ $('action_add').addEvent 'click', -> $("add_form").reset(); root.addBox.open()
+ $('action_play').addEvent 'click', -> new Request({method: 'get', url: '/api/unpauseServer'}).send()
+ $('action_cancel').addEvent 'click', -> new Request({method: 'get', url: '/api/stopAllDownloads'}).send()
+ $('action_stop').addEvent 'click', -> new Request({method: 'get', url: '/api/pauseServer'}).send()
+
+
+ # captcha events
+
+ $('cap_info').addEvent 'click', ->
+ load_captcha "get", ""
+ root.captchaBox.open()
+ $('cap_reset').addEvent 'click', -> root.captchaBox.close()
+ $('cap_form').addEvent 'submit', (e) ->
+ submit_captcha()
+ e.stop()
+
+ $('cap_positional').addEvent 'click', on_captcha_click
+
+ new Request.JSON({
+ url: "/json/status"
+ onSuccess: LoadJsonToContent
+ secure: false
+ async: true
+ initialDelay: 0
+ delay: 4000
+ limit: 3000
+ }).startTimer()
+
+
+LoadJsonToContent = (data) ->
+ $("speed").set 'text', humanFileSize(data.speed)+"/s"
+ $("aktiv").set 'text', data.active
+ $("aktiv_from").set 'text', data.queue
+ $("aktiv_total").set 'text', data.total
+
+ if data.captcha
+ if $("cap_info").getStyle("display") != "inline"
+ $("cap_info").setStyle 'display', 'inline'
+ root.notify.alert '{{_("New Captcha Request")}}', {
+ 'className': 'notify'
+ }
+ else
+ $("cap_info").setStyle 'display', 'none'
+
+
+ if data.download
+ $("time").set 'text', ' {{_("on")}}'
+ $("time").setStyle 'background-color', "#8ffc25"
+ else
+ $("time").set 'text', ' {{_("off")}}'
+ $("time").setStyle 'background-color', "#fc6e26"
+
+ if data.reconnect
+ $("reconnect").set 'text', ' {{_("on")}}'
+ $("reconnect").setStyle 'background-color', "#8ffc25"
+ else
+ $("reconnect").set 'text', ' {{_("off")}}'
+ $("reconnect").setStyle 'background-color', "#fc6e26"
+
+ return null
+
+
+set_captcha = (data) ->
+ $('cap_id').set 'value', data.id
+ if (data.result_type is 'textual')
+ $('cap_textual_img').set 'src', data.src
+ $('cap_title').set 'text', '{{_("Please read the text on the captcha.")}}'
+ $('cap_submit').setStyle 'display', 'inline'
+ $('cap_textual').setStyle 'display', 'block'
+ $('cap_positional').setStyle 'display', 'none'
+
+ else if (data.result_type == 'positional')
+ $('cap_positional_img').set('src', data.src)
+ $('cap_title').set('text', '{{_("Please click on the right captcha position.")}}')
+ $('cap_submit').setStyle('display', 'none')
+ $('cap_textual').setStyle('display', 'none')
+
+
+load_captcha = (method, post) ->
+ new Request.JSON({
+ url: "/json/set_captcha"
+ onSuccess: (data) -> set_captcha(data) if data.captcha else clear_captcha()
+ secure: false
+ async: true
+ method: method
+ }).send(post)
+
+
+clear_captcha = ->
+ $('cap_textual').setStyle 'display', 'none'
+ $('cap_textual_img').set 'src', ''
+ $('cap_positional').setStyle 'display', 'none'
+ $('cap_positional_img').set 'src', ''
+ $('cap_title').set 'text', '{{_("No Captchas to read.")}}'
+
+
+submit_captcha = ->
+ load_captcha("post", "cap_id=" + $('cap_id').get('value') + "&cap_result=" + $('cap_result').get('value') )
+ $('cap_result').set('value', '')
+
+
+on_captcha_click = (e) ->
+ position = e.target.getPosition()
+ x = e.page.x - position.x
+ y = e.page.y - position.y
+ $('cap_result').value = x + "," + y
+ submit_captcha()
diff --git a/module/webui/themes/Flat/js/render/base.min.js b/module/webui/themes/Flat/js/render/base.min.js
new file mode 100644
index 000000000..1ba1d73f9
--- /dev/null
+++ b/module/webui/themes/Flat/js/render/base.min.js
@@ -0,0 +1,3 @@
+{% autoescape true %}
+var LoadJsonToContent,clear_captcha,humanFileSize,load_captcha,on_captcha_click,parseUri,root,set_captcha,submit_captcha;root=this;humanFileSize=function(f){var c,d,e,b;d=new Array("B","KiB","MiB","GiB","TiB","PiB");b=Math.log(f)/Math.log(1024);e=Math.floor(b);c=Math.pow(1024,e);if(f===0){return"0 B"}else{return Math.round(f*100/c)/100+" "+d[e]}};parseUri=function(){var b,c,g,e,d,f,a;b=$("add_links").value;g=new RegExp("(ht|f)tp(s?)://[a-zA-Z0-9-./?=_&%#]+[<| |\"|'|\r|\n|\t]{1}","g");d=b.match(g);if(d===null){return}e="";for(f=0,a=d.length;f<a;f++){c=d[f];if(c.indexOf(" ")!==-1){e=e+c.replace(" "," \n")}else{if(c.indexOf("\t")!==-1){e=e+c.replace("\t"," \n")}else{if(c.indexOf("\r")!==-1){e=e+c.replace("\r"," \n")}else{if(c.indexOf('"')!==-1){e=e+c.replace('"'," \n")}else{if(c.indexOf("<")!==-1){e=e+c.replace("<"," \n")}else{if(c.indexOf("'")!==-1){e=e+c.replace("'"," \n")}else{e=e+c.replace("\n"," \n")}}}}}}}return $("add_links").value=e};Array.prototype.remove=function(d,c){var a,b;a=this.slice((c||d)+1||this.length);this.length=(b=d<0)!=null?b:this.length+{from:d};if(this.length===0){return[]}return this.push.apply(this,a)};document.addEvent("domready",function(){root.notify=new Purr({mode:"top",position:"center"});root.captchaBox=new MooDialog({destroyOnHide:false});root.captchaBox.setContent($("cap_box"));root.addBox=new MooDialog({destroyOnHide:false});root.addBox.setContent($("add_box"));$("add_form").onsubmit=function(){$("add_form").target="upload_target";if($("add_name").value===""&&$("add_file").value===""){alert('{{_("Please Enter a packagename.")}}');return false}else{root.addBox.close();return true}};$("add_reset").addEvent("click",function(){return root.addBox.close()});$("action_add").addEvent("click",function(){$("add_form").reset();return root.addBox.open()});$("action_play").addEvent("click",function(){return new Request({method:"get",url:"/api/unpauseServer"}).send()});$("action_cancel").addEvent("click",function(){return new Request({method:"get",url:"/api/stopAllDownloads"}).send()});$("action_stop").addEvent("click",function(){return new Request({method:"get",url:"/api/pauseServer"}).send()});$("cap_info").addEvent("click",function(){load_captcha("get","");return root.captchaBox.open()});$("cap_reset").addEvent("click",function(){return root.captchaBox.close()});$("cap_form").addEvent("submit",function(a){submit_captcha();return a.stop()});$("cap_positional").addEvent("click",on_captcha_click);return new Request.JSON({url:"/json/status",onSuccess:LoadJsonToContent,secure:false,async:true,initialDelay:0,delay:4000,limit:3000}).startTimer()});LoadJsonToContent=function(a){$("speed").set("text",humanFileSize(a.speed)+"/s");$("aktiv").set("text",a.active);$("aktiv_from").set("text",a.queue);$("aktiv_total").set("text",a.total);if(a.captcha){if($("cap_info").getStyle("display")!=="inline"){$("cap_info").setStyle("display","inline");root.notify.alert('{{_("New Captcha Request")}}',{className:"notify"})}}else{$("cap_info").setStyle("display","none")}if(a.download){$("time").set("text",' {{_("on")}}');$("time").setStyle("background-color","#8ffc25")}else{$("time").set("text",' {{_("off")}}');$("time").setStyle("background-color","#fc6e26")}if(a.reconnect){$("reconnect").set("text",' {{_("on")}}');$("reconnect").setStyle("background-color","#8ffc25")}else{$("reconnect").set("text",' {{_("off")}}');$("reconnect").setStyle("background-color","#fc6e26")}return null};set_captcha=function(a){$("cap_id").set("value",a.id);if(a.result_type==="textual"){$("cap_textual_img").set("src",a.src);$("cap_title").set("text",'{{_("Please read the text on the captcha.")}}');$("cap_submit").setStyle("display","inline");$("cap_textual").setStyle("display","block");return $("cap_positional").setStyle("display","none")}else{if(a.result_type==="positional"){$("cap_positional_img").set("src",a.src);$("cap_title").set("text",'{{_("Please click on the right captcha position.")}}');$("cap_submit").setStyle("display","none");return $("cap_textual").setStyle("display","none")}}};load_captcha=function(b,a){return new Request.JSON({url:"/json/set_captcha",onSuccess:function(c){return set_captcha(c)(c.captcha?void 0:clear_captcha())},secure:false,async:true,method:b}).send(a)};clear_captcha=function(){$("cap_textual").setStyle("display","none");$("cap_textual_img").set("src","");$("cap_positional").setStyle("display","none");$("cap_positional_img").set("src","");return $("cap_title").set("text",'{{_("No Captchas to read.")}}')};submit_captcha=function(){load_captcha("post","cap_id="+$("cap_id").get("value")+"&cap_result="+$("cap_result").get("value"));$("cap_result").set("value","");return false};on_captcha_click=function(c){var b,a,d;b=c.target.getPosition();a=c.page.x-b.x;d=c.page.y-b.y;$("cap_result").value=a+","+d;return submit_captcha()};
+{% endautoescape %}
diff --git a/module/web/templates/default/filemanager_ui.js b/module/webui/themes/Flat/js/render/filemanager.js
index ed64ab69d..ab9b8b492 100644
--- a/module/web/templates/default/filemanager_ui.js
+++ b/module/webui/themes/Flat/js/render/filemanager.js
@@ -12,12 +12,12 @@ document.addEvent("domready", function() {
$('delete_reset').addEvent('click', function() {
hide_confirm_box()
});
-
+
/*$('filemanager_actions_list').getChildren("li").each(function(action) {
var action_name = action.className;
if(functions[action.className] != undefined)
{
- action.addEvent('click', functions[action.className]);
+ action.addEvent('click', functions[action.className]);
}
});*/
});
@@ -76,7 +76,7 @@ var FilemanagerUI = new Class({
this.url = url;
this.type = type;
this.directories = [];
- this.files = [];
+ this.files = [];
this.parseChildren();
},
@@ -86,8 +86,8 @@ var FilemanagerUI = new Class({
var name = ele.getElements("input.name")[0].get("value");
this.directories.push(new Item(this, path, name, ele))
}.bind(this));
-
- $("directories-list").getChildren("li.file").each(function(ele) {
+
+ $("directories-list").getChildren("li.file").each(function(ele) {
var path = ele.getElements("input.path")[0].get("value");
var name = ele.getElements("input.name")[0].get("value");
this.files.push(new Item(this, path, name, ele))
@@ -98,17 +98,17 @@ var FilemanagerUI = new Class({
var Item = new Class({
initialize: function(ui, path, name, ele) {
this.ui = ui;
- this.path = path;
- this.name = name;
+ this.path = path;
+ this.name = name;
this.ele = ele;
- this.directories = [];
- this.files = [];
- this.actions = new Array();
- this.actions["delete"] = this.del;
- this.actions["rename"] = this.rename;
- this.actions["mkdir"] = this.mkdir;
+ this.directories = [];
+ this.files = [];
+ this.actions = new Array();
+ this.actions["delete"] = this.del;
+ this.actions["rename"] = this.rename;
+ this.actions["mkdir"] = this.mkdir;
this.parseElement();
-
+
var pname = this.ele.getElements("span")[0];
this.buttons = new Fx.Tween(this.ele.getElements(".buttons")[0], {link: "cancel"});
this.buttons.set("opacity", 0);
@@ -125,167 +125,167 @@ var Item = new Class({
parseElement: function() {
this.ele.getChildren('span span.buttons img').each(function(img) {
- img.addEvent('click', this.actions[img.className].bind(this));
- }, this);
-
- //click on the directory name must open the directory itself
- this.ele.getElements('b')[0].addEvent('click', this.toggle.bind(this));
-
- //iterate over child directories
- var uls = this.ele.getElements('ul');
- if(uls.length > 0)
- {
- uls[0].getChildren("li.folder").each(function(fld) {
- var path = fld.getElements("input.path")[0].get("value");
- var name = fld.getElements("input.name")[0].get("value");
- this.directories.push(new Item(this, path, name, fld));
- }.bind(this));
- uls[0].getChildren("li.file").each(function(fld) {
- var path = fld.getElements("input.path")[0].get("value");
- var name = fld.getElements("input.name")[0].get("value");
- this.files.push(new Item(this, path, name, fld));
- }.bind(this));
- }
+ img.addEvent('click', this.actions[img.className].bind(this));
+ }, this);
+
+ //click on the directory name must open the directory itself
+ this.ele.getElements('b')[0].addEvent('click', this.toggle.bind(this));
+
+ //iterate over child directories
+ var uls = this.ele.getElements('ul');
+ if(uls.length > 0)
+ {
+ uls[0].getChildren("li.folder").each(function(fld) {
+ var path = fld.getElements("input.path")[0].get("value");
+ var name = fld.getElements("input.name")[0].get("value");
+ this.directories.push(new Item(this, path, name, fld));
+ }.bind(this));
+ uls[0].getChildren("li.file").each(function(fld) {
+ var path = fld.getElements("input.path")[0].get("value");
+ var name = fld.getElements("input.name")[0].get("value");
+ this.files.push(new Item(this, path, name, fld));
+ }.bind(this));
+ }
},
reorderElements: function() {
//TODO sort the main ul again (to keep data ordered after renaming something)
},
-
+
del: function(event) {
$("confirm_form").removeEvents("submit");
$("confirm_form").addEvent("submit", this.deleteDirectory.bind(this));
- $$("#confirm_form p").set('html', '{{_(("Are you sure you want to delete the selected item?"))}}');
-
+ $$("#confirm_form p").set('html', '{{_(("Are you sure you want to delete the selected item?"))}}');
+
show_confirm_box();
event.stop();
},
-
+
deleteDirectory: function(event) {
hide_confirm_box();
- new Request.JSON({
+ new Request.JSON({
method: 'POST',
url: "/json/filemanager/delete",
- data: {"path": this.path, "name": this.name},
+ data: {'path': this.path, 'name': this.name},
onSuccess: function(data) {
- if(data.response == "success")
- {
- new Fx.Tween(this.ele).start('opacity', 0);
- var ul = this.ele.parentNode;
- this.ele.dispose();
- //if this was the only child, add a "empty folder" div
- if(!ul.getChildren('li')[0])
- {
- var div = new Element("div", { 'html': '{{ _("Folder is empty") }}' });
- div.replaces(ul);
- }
-
- indicateSuccess();
- } else
- {
- //error from json code...
- indicateFail();
- }
+ if(data.response == "success")
+ {
+ new Fx.Tween(this.ele).start('opacity', 0);
+ var ul = this.ele.parentNode;
+ this.ele.dispose();
+ //if this was the only child, add a "empty folder" div
+ if(!ul.getChildren('li')[0])
+ {
+ var div = new Element("div", { 'html': '{{ _("Folder is empty") }}' });
+ div.replaces(ul);
+ }
+
+ indicateSuccess();
+ } else
+ {
+ //error from json code...
+ indicateFail();
+ }
}.bind(this),
onFailure: indicateFail
}).send();
-
+
event.stop();
},
-
+
rename: function(event) {
$("rename_form").removeEvents("submit");
$("rename_form").addEvent("submit", this.renameDirectory.bind(this));
- $("path").set("value", this.path);
+ $("path").set("value", this.path);
$("old_name").set("value", this.name);
$("new_name").set("value", this.name);
-
+
show_rename_box();
event.stop();
},
renameDirectory: function(event) {
hide_rename_box();
- new Request.JSON({
+ new Request.JSON({
method: 'POST',
url: "/json/filemanager/rename",
onSuccess: function(data) {
- if(data.response == "success")
- {
- this.name = $("new_name").get("value");
- this.ele.getElements("b")[0].set('html', $("new_name").get("value"));
- this.reorderElements();
- indicateSuccess();
- } else
- {
- //error from json code...
- indicateFail();
- }
+ if(data.response == "success")
+ {
+ this.name = $("new_name").get("value");
+ this.ele.getElements("b")[0].set('html', $("new_name").get("value"));
+ this.reorderElements();
+ indicateSuccess();
+ } else
+ {
+ //error from json code...
+ indicateFail();
+ }
}.bind(this),
onFailure: indicateFail
}).send($("rename_form").toQueryString());
-
+
event.stop();
},
-
+
mkdir: function(event) {
new Request.JSON({
- method: 'POST',
- url: "/json/filemanager/mkdir",
- data: {"path": this.path + "/" + this.name, "name": '{{_("New folder")}}'},
- onSuccess: function(data) {
- if(data.response == "success")
- {
- new Request.HTML({
- method: 'POST',
- url: "/filemanager/get_dir",
- data: {"path": data.path, "name": data.name},
- onSuccess: function(li) {
- //add node as first child of ul
- var ul = this.ele.getChildren('ul')[0];
- if(!ul)
- {
- //remove the "Folder Empty" div
- this.ele.getChildren('div').dispose();
-
- //create new ul to contain subfolder
- ul = new Element("ul");
- ul.inject(this.ele, 'bottom');
- }
- li[0].inject(ul, 'top');
-
- //add directory as a subdirectory of the current item
- this.directories.push(new Item(this.ui, data.path, data.name, ul.firstChild));
- }.bind(this),
- onFailure: indicateFail
- }).send();
- indicateSuccess();
- } else
- {
- //error from json code...
- indicateFail();
- }
- }.bind(this),
- onFailure: indicateFail
+ method: 'POST',
+ url: "/json/filemanager/mkdir",
+ data: {'path': this.path + "/" + this.name, 'name': '{{_("New folder")}}'},
+ onSuccess: function(data) {
+ if(data.response == "success")
+ {
+ new Request.HTML({
+ method: 'POST',
+ url: "/filemanager/get_dir",
+ data: {'path': data.path, 'name': data.name},
+ onSuccess: function(li) {
+ //add node as first child of ul
+ var ul = this.ele.getChildren('ul')[0];
+ if(!ul)
+ {
+ //remove the "Folder Empty" div
+ this.ele.getChildren('div').dispose();
+
+ //create new ul to contain subfolder
+ ul = new Element("ul");
+ ul.inject(this.ele, 'bottom');
+ }
+ li[0].inject(ul, 'top');
+
+ //add directory as a subdirectory of the current item
+ this.directories.push(new Item(this.ui, data.path, data.name, ul.firstChild));
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ indicateSuccess();
+ } else
+ {
+ //error from json code...
+ indicateFail();
+ }
+ }.bind(this),
+ onFailure: indicateFail
}).send();
-
+
event.stop();
},
-
+
toggle: function() {
var child = this.ele.getElement('ul');
- if(child == null)
- child = this.ele.getElement('div');
-
- if(child != null)
- {
- if (child.getStyle('display') == "block") {
- child.dissolve();
- } else {
- child.reveal();
- }
- }
+ if(child == null)
+ child = this.ele.getElement('div');
+
+ if(child != null)
+ {
+ if (child.getStyle('display') == "block") {
+ child.dissolve();
+ } else {
+ child.reveal();
+ }
+ }
}
});
diff --git a/module/webui/themes/Flat/js/render/package.js b/module/webui/themes/Flat/js/render/package.js
new file mode 100644
index 000000000..659a8e6fc
--- /dev/null
+++ b/module/webui/themes/Flat/js/render/package.js
@@ -0,0 +1,376 @@
+var root = this;
+
+document.addEvent("domready", function() {
+ root.load = new Fx.Tween($("load-indicator"), {link: "cancel"});
+ root.load.set("opacity", 0);
+
+
+ root.packageBox = new MooDialog({destroyOnHide: false});
+ root.packageBox.setContent($('pack_box'));
+
+ $('pack_reset').addEvent('click', function() {
+ $('pack_form').reset();
+ root.packageBox.close();
+ });
+});
+
+function indicateLoad() {
+ //$("load-indicator").reveal();
+ root.load.start("opacity", 1)
+}
+
+function indicateFinish() {
+ root.load.start("opacity", 0)
+}
+
+function indicateSuccess() {
+ indicateFinish();
+ root.notify.alert('{{_("Success")}}.', {
+ 'className': 'success'
+ });
+}
+
+function indicateFail() {
+ indicateFinish();
+ root.notify.alert('{{_("Failed")}}.', {
+ 'className': 'error'
+ });
+}
+
+var PackageUI = new Class({
+ initialize: function(url, type) {
+ this.url = url;
+ this.type = type;
+ this.packages = [];
+ this.parsePackages();
+
+ this.sorts = new Sortables($("package-list"), {
+ constrain: false,
+ clone: true,
+ revert: true,
+ opacity: 0.4,
+ handle: ".package_drag",
+ onComplete: this.saveSort.bind(this)
+ });
+
+ $("del_finished").addEvent("click", this.deleteFinished.bind(this));
+ $("restart_failed").addEvent("click", this.restartFailed.bind(this));
+
+ },
+
+ parsePackages: function() {
+ $("package-list").getChildren("li").each(function(ele) {
+ var id = ele.getFirst().get("id").match(/[0-9]+/);
+ this.packages.push(new Package(this, id, ele))
+ }.bind(this))
+ },
+
+ loadPackages: function() {
+ },
+
+ deleteFinished: function() {
+ indicateLoad();
+ new Request.JSON({
+ method: 'get',
+ url: '/api/deleteFinished',
+ onSuccess: function(data) {
+ if (data.length > 0) {
+ window.location.reload()
+ } else {
+ this.packages.each(function(pack) {
+ pack.close();
+ });
+ indicateSuccess();
+ }
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ },
+
+ restartFailed: function() {
+ indicateLoad();
+ new Request.JSON({
+ method: 'get',
+ url: '/api/restartFailed',
+ onSuccess: function(data) {
+ this.packages.each(function(pack) {
+ pack.close();
+ });
+ indicateSuccess();
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ },
+
+ startSort: function(ele, copy) {
+ },
+
+ saveSort: function(ele, copy) {
+ var order = [];
+ this.sorts.serialize(function(li, pos) {
+ if (li == ele && ele.retrieve("order") != pos) {
+ order.push(ele.retrieve("pid") + "|" + pos)
+ }
+ li.store("order", pos)
+ });
+ if (order.length > 0) {
+ indicateLoad();
+ new Request.JSON({
+ method: 'get',
+ url: '/json/package_order/' + order[0],
+ onSuccess: indicateFinish,
+ onFailure: indicateFail
+ }).send();
+ }
+ }
+
+});
+
+var Package = new Class({
+ initialize: function(ui, id, ele, data) {
+ this.ui = ui;
+ this.id = id;
+ this.linksLoaded = false;
+
+ if (!ele) {
+ this.createElement(data);
+ } else {
+ this.ele = ele;
+ this.order = ele.getElements("div.order")[0].get("html");
+ this.ele.store("order", this.order);
+ this.ele.store("pid", this.id);
+ this.parseElement();
+ }
+
+ var pname = this.ele.getElements(".packagename")[0];
+ this.buttons = new Fx.Tween(this.ele.getElements(".buttons")[0], {link: "cancel"});
+ this.buttons.set("opacity", 0);
+
+ pname.addEvent("mouseenter", function(e) {
+ this.buttons.start("opacity", 1)
+ }.bind(this));
+
+ pname.addEvent("mouseleave", function(e) {
+ this.buttons.start("opacity", 0)
+ }.bind(this));
+
+
+ },
+
+ createElement: function() {
+ alert("create")
+ },
+
+ parseElement: function() {
+ var imgs = this.ele.getElements('img');
+
+ this.name = this.ele.getElements('.name')[0];
+ this.folder = this.ele.getElements('.folder')[0];
+ this.password = this.ele.getElements('.password')[0];
+
+ imgs[1].addEvent('click', this.deletePackage.bind(this));
+ imgs[2].addEvent('click', this.restartPackage.bind(this));
+ imgs[3].addEvent('click', this.editPackage.bind(this));
+ imgs[4].addEvent('click', this.movePackage.bind(this));
+
+ this.ele.getElement('.packagename').addEvent('click', this.toggle.bind(this));
+
+ },
+
+ loadLinks: function() {
+ indicateLoad();
+ new Request.JSON({
+ method: 'get',
+ url: '/json/package/' + this.id,
+ onSuccess: this.createLinks.bind(this),
+ onFailure: indicateFail
+ }).send();
+ },
+
+ createLinks: function(data) {
+ var ul = $("sort_children_{id}".substitute({'id': this.id}));
+ ul.set("html", "");
+ data.links.each(function(link) {
+ link.id = link.fid;
+ var li = new Element("li", {
+ 'style': {
+ 'margin-left': 0
+ }
+ });
+
+ var html = "<span style='cursor: move' class='child_status sorthandle'><img src='../img/{icon}' style='width: 12px; height:12px;'/></span>\n".substitute({'icon': link.icon});
+ html += "<span style='font-size: 15px'><a href=\"{url}\" target=\"_blank\">{name}</a></span><br /><div class='child_secrow'>".substitute({'url': link.url, 'name': link.name});
+ html += "<span class='child_status'>{statusmsg}</span>{error}&nbsp;".substitute({'statusmsg': link.statusmsg, 'error':link.error});
+ html += "<span class='child_status'>{format_size}</span>".substitute({'format_size': link.format_size});
+ html += "<span class='child_status'>{plugin}</span>&nbsp;&nbsp;".substitute({'plugin': link.plugin});
+ html += "<img title='{{_(\"Delete Link\")}}' style='cursor: pointer;' width='10px' height='10px' src='../img/delete.png' />&nbsp;&nbsp;";
+ html += "<img title='{{_(\"Restart Link\")}}' style='cursor: pointer;margin-left: -4px' width='10px' height='10px' src='../img/arrow_refresh.png' /></div>";
+
+ var div = new Element("div", {
+ 'id': "file_" + link.id,
+ 'class': "child",
+ 'html': html
+ });
+
+ li.store("order", link.order);
+ li.store("lid", link.id);
+
+ li.adopt(div);
+ ul.adopt(li);
+ });
+ this.sorts = new Sortables(ul, {
+ constrain: false,
+ clone: true,
+ revert: true,
+ opacity: 0.4,
+ handle: ".sorthandle",
+ onComplete: this.saveSort.bind(this)
+ });
+ this.registerLinkEvents();
+ this.linksLoaded = true;
+ indicateFinish();
+ this.toggle();
+ },
+
+ registerLinkEvents: function() {
+ this.ele.getElements('.child').each(function(child) {
+ var lid = child.get('id').match(/[0-9]+/);
+ var imgs = child.getElements('.child_secrow img');
+ imgs[0].addEvent('click', function(e) {
+ new Request({
+ method: 'get',
+ url: '/api/deleteFiles/[' + this + "]",
+ onSuccess: function() {
+ $('file_' + this).nix()
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ }.bind(lid));
+
+ imgs[1].addEvent('click', function(e) {
+ new Request({
+ method: 'get',
+ url: '/api/restartFile/' + this,
+ onSuccess: function() {
+ var ele = $('file_' + this);
+ var imgs = ele.getElements("img");
+ imgs[0].set("src", "../img/status_queue.png");
+ var spans = ele.getElements(".child_status");
+ spans[1].set("html", "queued");
+ indicateSuccess();
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ }.bind(lid));
+ });
+ },
+
+ toggle: function() {
+ var child = this.ele.getElement('.children');
+ if (child.getStyle('display') == "block") {
+ child.dissolve();
+ } else {
+ if (!this.linksLoaded) {
+ this.loadLinks();
+ } else {
+ child.reveal();
+ }
+ }
+ },
+
+
+ deletePackage: function(event) {
+ indicateLoad();
+ new Request({
+ method: 'get',
+ url: '/api/deletePackages/[' + this.id + "]",
+ onSuccess: function() {
+ this.ele.nix();
+ indicateFinish();
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ //hide_pack();
+ event.stop();
+ },
+
+ restartPackage: function(event) {
+ indicateLoad();
+ new Request({
+ method: 'get',
+ url: '/api/restartPackage/' + this.id,
+ onSuccess: function() {
+ this.close();
+ indicateSuccess();
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ event.stop();
+ },
+
+ close: function() {
+ var child = this.ele.getElement('.children');
+ if (child.getStyle('display') == "block") {
+ child.dissolve();
+ }
+ var ul = $("sort_children_{id}".substitute({'id': this.id}));
+ ul.erase("html");
+ this.linksLoaded = false;
+ },
+
+ movePackage: function(event) {
+ indicateLoad();
+ new Request({
+ method: 'get',
+ url: '/json/move_package/' + ((this.ui.type + 1) % 2) + "/" + this.id,
+ onSuccess: function() {
+ this.ele.nix();
+ indicateFinish();
+ }.bind(this),
+ onFailure: indicateFail
+ }).send();
+ event.stop();
+ },
+
+ editPackage: function(event) {
+ $("pack_form").removeEvents("submit");
+ $("pack_form").addEvent("submit", this.savePackage.bind(this));
+
+ $("pack_id").set("value", this.id);
+ $("pack_name").set("value", this.name.get("text"));
+ $("pack_folder").set("value", this.folder.get("text"));
+ $("pack_pws").set("value", this.password.get("text"));
+
+ root.packageBox.open();
+ event.stop();
+ },
+
+ savePackage: function(event) {
+ $("pack_form").send();
+ this.name.set("text", $("pack_name").get("value"));
+ this.folder.set("text", $("pack_folder").get("value"));
+ this.password.set("text", $("pack_pws").get("value"));
+ root.packageBox.close();
+ event.stop();
+ },
+
+ saveSort: function(ele, copy) {
+ var order = [];
+ this.sorts.serialize(function(li, pos) {
+ if (li == ele && ele.retrieve("order") != pos) {
+ order.push(ele.retrieve("lid") + "|" + pos)
+ }
+ li.store("order", pos)
+ });
+ if (order.length > 0) {
+ indicateLoad();
+ new Request.JSON({
+ method: 'get',
+ url: '/json/link_order/' + order[0],
+ onSuccess: indicateFinish,
+ onFailure: indicateFail
+ }).send();
+ }
+ }
+
+});
diff --git a/module/webui/themes/Flat/js/render/settings.coffee b/module/webui/themes/Flat/js/render/settings.coffee
new file mode 100644
index 000000000..d522741b9
--- /dev/null
+++ b/module/webui/themes/Flat/js/render/settings.coffee
@@ -0,0 +1,107 @@
+root = this
+
+window.addEvent 'domready', ->
+ root.accountDialog = new MooDialog {destroyOnHide: false}
+ root.accountDialog.setContent $ 'account_box'
+
+ new TinyTab $$('#toptabs li a'), $$('#tabs-body > span')
+
+ $$('ul.nav').each (nav) ->
+ new MooDropMenu nav, {
+ onOpen: (el) -> el.fade 'in'
+ onClose: (el) -> el.fade 'out'
+ onInitialize: (el) -> el.fade('hide').set 'tween', {duration:500}
+ }
+
+ new SettingsUI()
+
+
+class SettingsUI
+ constructor: ->
+ @menu = $$ "#general-menu li"
+ @menu.append $$ "#plugin-menu li"
+
+ @name = $ "tabsback"
+ @general = $ "general_form_content"
+ @plugin = $ "plugin_form_content"
+
+ el.addEvent 'click', @menuClick.bind(this) for el in @menu
+
+ $("general|submit").addEvent "click", @configSubmit.bind(this)
+ $("plugin|submit").addEvent "click", @configSubmit.bind(this)
+
+ $("account_add").addEvent "click", (e) ->
+ root.accountDialog.open()
+ e.stop()
+
+ $("account_reset").addEvent "click", (e) ->
+ root.accountDialog.close()
+
+ $("account_add_button").addEvent "click", @addAccount.bind(this)
+ $("account_submit").addEvent "click", @submitAccounts.bind(this)
+
+
+ menuClick: (e) ->
+ [category, section] = e.target.get("id").split("|")
+ name = e.target.get "text"
+
+
+ target = if category is "general" then @general else @plugin
+ target.dissolve()
+
+ new Request({
+ "method" : "get"
+ "url" : "/json/load_config/#{category}/#{section}"
+ 'onSuccess': (data) =>
+ target.set "html", data
+ target.reveal()
+ this.name.set "text", name
+ }).send()
+
+
+ configSubmit: (e) ->
+ category = e.target.get("id").split("|")[0]
+ form = $("#{category}_form")
+
+ form.set "send", {
+ 'method': "post"
+ 'url': "/json/save_config/#{category}"
+ "onSuccess" : ->
+ root.notify.alert '{{ _("Settings saved.")}}', {
+ 'className': 'success'
+ }
+ 'onFailure': ->
+ root.notify.alert '{{ _("Error occured.")}}', {
+ 'className': 'error'
+ }
+ }
+ form.send()
+ e.stop()
+
+ addAccount: (e) ->
+ form = $ "add_account_form"
+ form.set "send", {
+ 'method': "post"
+ "onSuccess" : -> window.location.reload()
+ 'onFailure': ->
+ root.notify.alert '{{_("Error occured.")}}', {
+ 'className': 'error'
+ }
+ }
+
+ form.send()
+ e.stop()
+
+ submitAccounts: (e) ->
+ form = $ "account_form"
+ form.set "send", {
+ 'method': "post",
+ "onSuccess" : -> window.location.reload()
+ 'onFailure': ->
+ root.notify.alert('{{ _("Error occured.") }}', {
+ 'className': 'error'
+ })
+ }
+
+ form.send()
+ e.stop()
diff --git a/module/webui/themes/Flat/js/render/settings.min.js b/module/webui/themes/Flat/js/render/settings.min.js
new file mode 100644
index 000000000..41d1cb25a
--- /dev/null
+++ b/module/webui/themes/Flat/js/render/settings.min.js
@@ -0,0 +1,3 @@
+{% autoescape true %}
+var SettingsUI,root;var __bind=function(a,b){return function(){return a.apply(b,arguments)}};root=this;window.addEvent("domready",function(){root.accountDialog=new MooDialog({destroyOnHide:false});root.accountDialog.setContent($("account_box"));new TinyTab($$("#toptabs li a"),$$("#tabs-body > span"));$$("ul.nav").each(function(a){return new MooDropMenu(a,{onOpen:function(b){return b.fade("in")},onClose:function(b){return b.fade("out")},onInitialize:function(b){return b.fade("hide").set("tween",{duration:500})}})});return new SettingsUI()});SettingsUI=(function(){function a(){var c,e,b,d;this.menu=$$("#general-menu li");this.menu.append($$("#plugin-menu li"));this.name=$("tabsback");this.general=$("general_form_content");this.plugin=$("plugin_form_content");d=this.menu;for(e=0,b=d.length;e<b;e++){c=d[e];c.addEvent("click",this.menuClick.bind(this))}$("general|submit").addEvent("click",this.configSubmit.bind(this));$("plugin|submit").addEvent("click",this.configSubmit.bind(this));$("account_add").addEvent("click",function(f){root.accountDialog.open();return f.stop()});$("account_reset").addEvent("click",function(f){return root.accountDialog.close()});$("account_add_button").addEvent("click",this.addAccount.bind(this));$("account_submit").addEvent("click",this.submitAccounts.bind(this))}a.prototype.menuClick=function(h){var c,b,g,f,d;d=h.target.get("id").split("|"),c=d[0],g=d[1];b=h.target.get("text");f=c==="general"?this.general:this.plugin;f.dissolve();return new Request({method:"get",url:"/json/load_config/"+c+"/"+g,onSuccess:__bind(function(e){f.set("html",e);f.reveal();return this.name.set("text",b)},this)}).send()};a.prototype.configSubmit=function(d){var c,b;c=d.target.get("id").split("|")[0];b=$(""+c+"_form");b.set("send",{method:"post",url:"/json/save_config/"+c,onSuccess:function(){return root.notify.alert('{{ _("Settings saved.")}}',{className:"success"})},onFailure:function(){return root.notify.alert('{{ _("Error occured.")}}',{className:"error"})}});b.send();return d.stop()};a.prototype.addAccount=function(c){var b;b=$("add_account_form");b.set("send",{method:"post",onSuccess:function(){return window.location.reload()},onFailure:function(){return root.notify.alert('{{_("Error occured.")}}',{className:"error"})}});b.send();return c.stop()};a.prototype.submitAccounts=function(c){var b;b=$("account_form");b.set("send",{method:"post",onSuccess:function(){return window.location.reload()},onFailure:function(){return root.notify.alert('{{ _("Error occured.") }}',{className:"error"})}});b.send();return c.stop()};return a})();
+{% endautoescape %}
diff --git a/module/webui/themes/Flat/js/static/MooDialog.js b/module/webui/themes/Flat/js/static/MooDialog.js
new file mode 100644
index 000000000..45a52496f
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/MooDialog.js
@@ -0,0 +1,140 @@
+/*
+---
+name: MooDialog
+description: The base class of MooDialog
+authors: Arian Stolwijk
+license: MIT-style license
+requires: [Core/Class, Core/Element, Core/Element.Style, Core/Element.Event]
+provides: [MooDialog, Element.MooDialog]
+...
+*/
+
+
+var MooDialog = new Class({
+
+ Implements: [Options, Events],
+
+ options: {
+ 'class': 'MooDialog',
+ title: null,
+ scroll: true, // IE
+ forceScroll: false,
+ useEscKey: true,
+ destroyOnHide: true,
+ autoOpen: true,
+ closeButton: true,
+ onInitialize: function(){
+ this.wrapper.setStyle('display', 'none');
+ },
+ onBeforeOpen: function(){
+ this.wrapper.setStyle('display', 'block');
+ this.fireEvent('show');
+ },
+ onBeforeClose: function(){
+ this.wrapper.setStyle('display', 'none');
+ this.fireEvent('hide');
+ }/*,
+ onOpen: function(){},
+ onClose: function(){},
+ onShow: function(){},
+ onHide: function(){},
+ onInitialize: function(wrapper){},
+ onContentChange: function(content){}*/
+ },
+
+ initialize: function(options){
+ this.setOptions(options);
+ this.options.inject = this.options.inject || document.body;
+ options = this.options;
+
+ var wrapper = this.wrapper = new Element('div.' + options['class'].replace(' ', '.')).inject(options.inject);
+ this.content = new Element('div.content').inject(wrapper);
+
+ if (options.title){
+ this.title = new Element('div.title').set('text', options.title).inject(wrapper);
+ wrapper.addClass('MooDialogTitle');
+ }
+
+ if (options.closeButton){
+ this.closeButton = new Element('a.close', {
+ events: {click: this.close.bind(this)}
+ }).inject(wrapper);
+ }
+
+
+ /*<ie6>*/// IE 6 scroll
+ if ((options.scroll && Browser.ie6) || options.forceScroll){
+ wrapper.setStyle('position', 'absolute');
+ var position = wrapper.getPosition(options.inject);
+ window.addEvent('scroll', function(){
+ var scroll = document.getScroll();
+ wrapper.setPosition({
+ x: position.x + scroll.x,
+ y: position.y + scroll.y
+ });
+ });
+ }
+ /*</ie6>*/
+
+ if (options.useEscKey){
+ // Add event for the esc key
+ document.addEvent('keydown', function(e){
+ if (e.key == 'esc') this.close();
+ }.bind(this));
+ }
+
+ this.addEvent('hide', function(){
+ if (options.destroyOnHide) this.destroy();
+ }.bind(this));
+
+ this.fireEvent('initialize', wrapper);
+ },
+
+ setContent: function(){
+ var content = Array.from(arguments);
+ if (content.length == 1) content = content[0];
+
+ this.content.empty();
+
+ var type = typeOf(content);
+ if (['string', 'number'].contains(type)) this.content.set('text', content);
+ else this.content.adopt(content);
+
+ this.fireEvent('contentChange', this.content);
+
+ return this;
+ },
+
+ open: function(){
+ this.fireEvent('beforeOpen', this.wrapper).fireEvent('open');
+ this.opened = true;
+ return this;
+ },
+
+ close: function(){
+ this.fireEvent('beforeClose', this.wrapper).fireEvent('close');
+ this.opened = false;
+ return this;
+ },
+
+ destroy: function(){
+ this.wrapper.destroy();
+ },
+
+ toElement: function(){
+ return this.wrapper;
+ }
+
+});
+
+
+Element.implement({
+
+ MooDialog: function(options){
+ this.store('MooDialog',
+ new MooDialog(options).setContent(this).open()
+ );
+ return this;
+ }
+
+});
diff --git a/module/webui/themes/Flat/js/static/MooDialog.min.js b/module/webui/themes/Flat/js/static/MooDialog.min.js
new file mode 100644
index 000000000..90b3ae100
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/MooDialog.min.js
@@ -0,0 +1 @@
+var MooDialog=new Class({Implements:[Options,Events],options:{"class":"MooDialog",title:null,scroll:!0,forceScroll:!1,useEscKey:!0,destroyOnHide:!0,autoOpen:!0,closeButton:!0,onInitialize:function(){this.wrapper.setStyle("display","none")},onBeforeOpen:function(){this.wrapper.setStyle("display","block"),this.fireEvent("show")},onBeforeClose:function(){this.wrapper.setStyle("display","none"),this.fireEvent("hide")}},initialize:function(t){this.setOptions(t),this.options.inject=this.options.inject||document.body,t=this.options;var e=this.wrapper=new Element("div."+t["class"].replace(" ",".")).inject(t.inject);if(this.content=new Element("div.content").inject(e),t.title&&(this.title=new Element("div.title").set("text",t.title).inject(e),e.addClass("MooDialogTitle")),t.closeButton&&(this.closeButton=new Element("a.close",{events:{click:this.close.bind(this)}}).inject(e)),t.scroll&&Browser.ie6||t.forceScroll){e.setStyle("position","absolute");var n=e.getPosition(t.inject);window.addEvent("scroll",function(){var t=document.getScroll();e.setPosition({x:n.x+t.x,y:n.y+t.y})})}t.useEscKey&&document.addEvent("keydown",function(t){"esc"==t.key&&this.close()}.bind(this)),this.addEvent("hide",function(){t.destroyOnHide&&this.destroy()}.bind(this)),this.fireEvent("initialize",e)},setContent:function(){var t=Array.from(arguments);1==t.length&&(t=t[0]),this.content.empty();var e=typeOf(t);return["string","number"].contains(e)?this.content.set("text",t):this.content.adopt(t),this.fireEvent("contentChange",this.content),this},open:function(){return this.fireEvent("beforeOpen",this.wrapper).fireEvent("open"),this.opened=!0,this},close:function(){return this.fireEvent("beforeClose",this.wrapper).fireEvent("close"),this.opened=!1,this},destroy:function(){this.wrapper.destroy()},toElement:function(){return this.wrapper}});Element.implement({MooDialog:function(t){return this.store("MooDialog",new MooDialog(t).setContent(this).open()),this}}); \ No newline at end of file
diff --git a/module/webui/themes/Flat/js/static/MooDropMenu.js b/module/webui/themes/Flat/js/static/MooDropMenu.js
new file mode 100644
index 000000000..ac0fa1874
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/MooDropMenu.js
@@ -0,0 +1,86 @@
+/*
+---
+description: This provides a simple Drop Down menu with infinit levels
+
+license: MIT-style
+
+authors:
+- Arian Stolwijk
+
+requires:
+ - Core/Class.Extras
+ - Core/Element.Event
+ - Core/Selectors
+
+provides: [MooDropMenu, Element.MooDropMenu]
+
+...
+*/
+
+var MooDropMenu = new Class({
+
+ Implements: [Options, Events],
+
+ options: {
+ onOpen: function(el){
+ el.removeClass('close').addClass('open');
+ },
+ onClose: function(el){
+ el.removeClass('open').addClass('close');
+ },
+ onInitialize: function(el){
+ el.removeClass('open').addClass('close');
+ },
+ mouseoutDelay: 200,
+ mouseoverDelay: 0,
+ listSelector: 'ul',
+ itemSelector: 'li',
+ openEvent: 'mouseenter',
+ closeEvent: 'mouseleave'
+ },
+
+ initialize: function(menu, options, level){
+ this.setOptions(options);
+ options = this.options;
+
+ var menu = this.menu = document.id(menu);
+
+ menu.getElements(options.itemSelector + ' > ' + options.listSelector).each(function(el){
+
+ this.fireEvent('initialize', el);
+
+ var parent = el.getParent(options.itemSelector),
+ timer;
+
+ parent.addEvent(options.openEvent, function(){
+ parent.store('DropDownOpen', true);
+
+ clearTimeout(timer);
+ if (options.mouseoverDelay) timer = this.fireEvent.delay(options.mouseoverDelay, this, ['open', el]);
+ else this.fireEvent('open', el);
+
+ }.bind(this)).addEvent(options.closeEvent, function(){
+ parent.store('DropDownOpen', false);
+
+ clearTimeout(timer);
+ timer = (function(){
+ if (!parent.retrieve('DropDownOpen')) this.fireEvent('close', el);
+ }).delay(options.mouseoutDelay, this);
+
+ }.bind(this));
+
+ }, this);
+ },
+
+ toElement: function(){
+ return this.menu
+ }
+
+});
+
+/* So you can do like this $('nav').MooDropMenu(); or even $('nav').MooDropMenu().setStyle('border',1); */
+Element.implement({
+ MooDropMenu: function(options){
+ return this.store('MooDropMenu', new MooDropMenu(this, options));
+ }
+});
diff --git a/module/webui/themes/Flat/js/static/MooDropMenu.min.js b/module/webui/themes/Flat/js/static/MooDropMenu.min.js
new file mode 100644
index 000000000..552ae247a
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/MooDropMenu.min.js
@@ -0,0 +1 @@
+var MooDropMenu=new Class({Implements:[Options,Events],options:{onOpen:function(e){e.removeClass("close").addClass("open")},onClose:function(e){e.removeClass("open").addClass("close")},onInitialize:function(e){e.removeClass("open").addClass("close")},mouseoutDelay:200,mouseoverDelay:0,listSelector:"ul",itemSelector:"li",openEvent:"mouseenter",closeEvent:"mouseleave"},initialize:function(e,o){this.setOptions(o),o=this.options;var e=this.menu=document.id(e);e.getElements(o.itemSelector+" > "+o.listSelector).each(function(e){this.fireEvent("initialize",e);var n,t=e.getParent(o.itemSelector);t.addEvent(o.openEvent,function(){t.store("DropDownOpen",!0),clearTimeout(n),o.mouseoverDelay?n=this.fireEvent.delay(o.mouseoverDelay,this,["open",e]):this.fireEvent("open",e)}.bind(this)).addEvent(o.closeEvent,function(){t.store("DropDownOpen",!1),clearTimeout(n),n=function(){t.retrieve("DropDownOpen")||this.fireEvent("close",e)}.delay(o.mouseoutDelay,this)}.bind(this))},this)},toElement:function(){return this.menu}});Element.implement({MooDropMenu:function(e){return this.store("MooDropMenu",new MooDropMenu(this,e))}}); \ No newline at end of file
diff --git a/module/webui/themes/Flat/js/static/mootools-core.js b/module/webui/themes/Flat/js/static/mootools-core.js
new file mode 100644
index 000000000..db83850fd
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/mootools-core.js
@@ -0,0 +1,5977 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/core/8423c12ffd6a6bfcde9ea22554aec795
+
+packager build:
+ - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady
+
+...
+*/
+
+/*
+---
+
+name: Core
+
+description: The heart of MooTools.
+
+license: MIT-style license.
+
+copyright: Copyright (c) 2006-2014 [Valerio Proietti](http://mad4milk.net/).
+
+authors: The MooTools production team (http://mootools.net/developers/)
+
+inspiration:
+ - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
+ - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
+
+provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
+
+...
+*/
+
+(function(){
+
+this.MooTools = {
+ version: '1.5.0',
+ build: '0f7b690afee9349b15909f33016a25d2e4d9f4e3'
+};
+
+// typeOf, instanceOf
+
+var typeOf = this.typeOf = function(item){
+ if (item == null) return 'null';
+ if (item.$family != null) return item.$family();
+
+ if (item.nodeName){
+ if (item.nodeType == 1) return 'element';
+ if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
+ } else if (typeof item.length == 'number'){
+ if ('callee' in item) return 'arguments';
+ if ('item' in item) return 'collection';
+ }
+
+ return typeof item;
+};
+
+var instanceOf = this.instanceOf = function(item, object){
+ if (item == null) return false;
+ var constructor = item.$constructor || item.constructor;
+ while (constructor){
+ if (constructor === object) return true;
+ constructor = constructor.parent;
+ }
+ /*<ltIE8>*/
+ if (!item.hasOwnProperty) return false;
+ /*</ltIE8>*/
+ return item instanceof object;
+};
+
+// Function overloading
+
+var Function = this.Function;
+
+var enumerables = true;
+for (var i in {toString: 1}) enumerables = null;
+if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
+
+Function.prototype.overloadSetter = function(usePlural){
+ var self = this;
+ return function(a, b){
+ if (a == null) return this;
+ if (usePlural || typeof a != 'string'){
+ for (var k in a) self.call(this, k, a[k]);
+ if (enumerables) for (var i = enumerables.length; i--;){
+ k = enumerables[i];
+ if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
+ }
+ } else {
+ self.call(this, a, b);
+ }
+ return this;
+ };
+};
+
+Function.prototype.overloadGetter = function(usePlural){
+ var self = this;
+ return function(a){
+ var args, result;
+ if (typeof a != 'string') args = a;
+ else if (arguments.length > 1) args = arguments;
+ else if (usePlural) args = [a];
+ if (args){
+ result = {};
+ for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
+ } else {
+ result = self.call(this, a);
+ }
+ return result;
+ };
+};
+
+Function.prototype.extend = function(key, value){
+ this[key] = value;
+}.overloadSetter();
+
+Function.prototype.implement = function(key, value){
+ this.prototype[key] = value;
+}.overloadSetter();
+
+// From
+
+var slice = Array.prototype.slice;
+
+Function.from = function(item){
+ return (typeOf(item) == 'function') ? item : function(){
+ return item;
+ };
+};
+
+Array.from = function(item){
+ if (item == null) return [];
+ return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
+};
+
+Number.from = function(item){
+ var number = parseFloat(item);
+ return isFinite(number) ? number : null;
+};
+
+String.from = function(item){
+ return item + '';
+};
+
+// hide, protect
+
+Function.implement({
+
+ hide: function(){
+ this.$hidden = true;
+ return this;
+ },
+
+ protect: function(){
+ this.$protected = true;
+ return this;
+ }
+
+});
+
+// Type
+
+var Type = this.Type = function(name, object){
+ if (name){
+ var lower = name.toLowerCase();
+ var typeCheck = function(item){
+ return (typeOf(item) == lower);
+ };
+
+ Type['is' + name] = typeCheck;
+ if (object != null){
+ object.prototype.$family = (function(){
+ return lower;
+ }).hide();
+
+ }
+ }
+
+ if (object == null) return null;
+
+ object.extend(this);
+ object.$constructor = Type;
+ object.prototype.$constructor = object;
+
+ return object;
+};
+
+var toString = Object.prototype.toString;
+
+Type.isEnumerable = function(item){
+ return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
+};
+
+var hooks = {};
+
+var hooksOf = function(object){
+ var type = typeOf(object.prototype);
+ return hooks[type] || (hooks[type] = []);
+};
+
+var implement = function(name, method){
+ if (method && method.$hidden) return;
+
+ var hooks = hooksOf(this);
+
+ for (var i = 0; i < hooks.length; i++){
+ var hook = hooks[i];
+ if (typeOf(hook) == 'type') implement.call(hook, name, method);
+ else hook.call(this, name, method);
+ }
+
+ var previous = this.prototype[name];
+ if (previous == null || !previous.$protected) this.prototype[name] = method;
+
+ if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
+ return method.apply(item, slice.call(arguments, 1));
+ });
+};
+
+var extend = function(name, method){
+ if (method && method.$hidden) return;
+ var previous = this[name];
+ if (previous == null || !previous.$protected) this[name] = method;
+};
+
+Type.implement({
+
+ implement: implement.overloadSetter(),
+
+ extend: extend.overloadSetter(),
+
+ alias: function(name, existing){
+ implement.call(this, name, this.prototype[existing]);
+ }.overloadSetter(),
+
+ mirror: function(hook){
+ hooksOf(this).push(hook);
+ return this;
+ }
+
+});
+
+new Type('Type', Type);
+
+// Default Types
+
+var force = function(name, object, methods){
+ var isType = (object != Object),
+ prototype = object.prototype;
+
+ if (isType) object = new Type(name, object);
+
+ for (var i = 0, l = methods.length; i < l; i++){
+ var key = methods[i],
+ generic = object[key],
+ proto = prototype[key];
+
+ if (generic) generic.protect();
+ if (isType && proto) object.implement(key, proto.protect());
+ }
+
+ if (isType){
+ var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
+ object.forEachMethod = function(fn){
+ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
+ fn.call(prototype, prototype[methods[i]], methods[i]);
+ }
+ for (var key in prototype) fn.call(prototype, prototype[key], key);
+ };
+ }
+
+ return force;
+};
+
+force('String', String, [
+ 'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
+ 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
+])('Array', Array, [
+ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
+ 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
+])('Number', Number, [
+ 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
+])('Function', Function, [
+ 'apply', 'call', 'bind'
+])('RegExp', RegExp, [
+ 'exec', 'test'
+])('Object', Object, [
+ 'create', 'defineProperty', 'defineProperties', 'keys',
+ 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
+ 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
+])('Date', Date, ['now']);
+
+Object.extend = extend.overloadSetter();
+
+Date.extend('now', function(){
+ return +(new Date);
+});
+
+new Type('Boolean', Boolean);
+
+// fixes NaN returning as Number
+
+Number.prototype.$family = function(){
+ return isFinite(this) ? 'number' : 'null';
+}.hide();
+
+// Number.random
+
+Number.extend('random', function(min, max){
+ return Math.floor(Math.random() * (max - min + 1) + min);
+});
+
+// forEach, each
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+Object.extend('forEach', function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
+ }
+});
+
+Object.each = Object.forEach;
+
+Array.implement({
+
+ /*<!ES5>*/
+ forEach: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (i in this) fn.call(bind, this[i], i, this);
+ }
+ },
+ /*</!ES5>*/
+
+ each: function(fn, bind){
+ Array.forEach(this, fn, bind);
+ return this;
+ }
+
+});
+
+// Array & Object cloning, Object merging and appending
+
+var cloneOf = function(item){
+ switch (typeOf(item)){
+ case 'array': return item.clone();
+ case 'object': return Object.clone(item);
+ default: return item;
+ }
+};
+
+Array.implement('clone', function(){
+ var i = this.length, clone = new Array(i);
+ while (i--) clone[i] = cloneOf(this[i]);
+ return clone;
+});
+
+var mergeOne = function(source, key, current){
+ switch (typeOf(current)){
+ case 'object':
+ if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
+ else source[key] = Object.clone(current);
+ break;
+ case 'array': source[key] = current.clone(); break;
+ default: source[key] = current;
+ }
+ return source;
+};
+
+Object.extend({
+
+ merge: function(source, k, v){
+ if (typeOf(k) == 'string') return mergeOne(source, k, v);
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var object = arguments[i];
+ for (var key in object) mergeOne(source, key, object[key]);
+ }
+ return source;
+ },
+
+ clone: function(object){
+ var clone = {};
+ for (var key in object) clone[key] = cloneOf(object[key]);
+ return clone;
+ },
+
+ append: function(original){
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var extended = arguments[i] || {};
+ for (var key in extended) original[key] = extended[key];
+ }
+ return original;
+ }
+
+});
+
+// Object-less types
+
+['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
+ new Type(name);
+});
+
+// Unique ID
+
+var UID = Date.now();
+
+String.extend('uniqueID', function(){
+ return (UID++).toString(36);
+});
+
+
+
+})();
+
+
+/*
+---
+
+name: Array
+
+description: Contains Array Prototypes like each, contains, and erase.
+
+license: MIT-style license.
+
+requires: [Type]
+
+provides: Array
+
+...
+*/
+
+Array.implement({
+
+ /*<!ES5>*/
+ every: function(fn, bind){
+ for (var i = 0, l = this.length >>> 0; i < l; i++){
+ if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
+ }
+ return true;
+ },
+
+ filter: function(fn, bind){
+ var results = [];
+ for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this){
+ value = this[i];
+ if (fn.call(bind, value, i, this)) results.push(value);
+ }
+ return results;
+ },
+
+ indexOf: function(item, from){
+ var length = this.length >>> 0;
+ for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){
+ if (this[i] === item) return i;
+ }
+ return -1;
+ },
+
+ map: function(fn, bind){
+ var length = this.length >>> 0, results = Array(length);
+ for (var i = 0; i < length; i++){
+ if (i in this) results[i] = fn.call(bind, this[i], i, this);
+ }
+ return results;
+ },
+
+ some: function(fn, bind){
+ for (var i = 0, l = this.length >>> 0; i < l; i++){
+ if ((i in this) && fn.call(bind, this[i], i, this)) return true;
+ }
+ return false;
+ },
+ /*</!ES5>*/
+
+ clean: function(){
+ return this.filter(function(item){
+ return item != null;
+ });
+ },
+
+ invoke: function(methodName){
+ var args = Array.slice(arguments, 1);
+ return this.map(function(item){
+ return item[methodName].apply(item, args);
+ });
+ },
+
+ associate: function(keys){
+ var obj = {}, length = Math.min(this.length, keys.length);
+ for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
+ return obj;
+ },
+
+ link: function(object){
+ var result = {};
+ for (var i = 0, l = this.length; i < l; i++){
+ for (var key in object){
+ if (object[key](this[i])){
+ result[key] = this[i];
+ delete object[key];
+ break;
+ }
+ }
+ }
+ return result;
+ },
+
+ contains: function(item, from){
+ return this.indexOf(item, from) != -1;
+ },
+
+ append: function(array){
+ this.push.apply(this, array);
+ return this;
+ },
+
+ getLast: function(){
+ return (this.length) ? this[this.length - 1] : null;
+ },
+
+ getRandom: function(){
+ return (this.length) ? this[Number.random(0, this.length - 1)] : null;
+ },
+
+ include: function(item){
+ if (!this.contains(item)) this.push(item);
+ return this;
+ },
+
+ combine: function(array){
+ for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
+ return this;
+ },
+
+ erase: function(item){
+ for (var i = this.length; i--;){
+ if (this[i] === item) this.splice(i, 1);
+ }
+ return this;
+ },
+
+ empty: function(){
+ this.length = 0;
+ return this;
+ },
+
+ flatten: function(){
+ var array = [];
+ for (var i = 0, l = this.length; i < l; i++){
+ var type = typeOf(this[i]);
+ if (type == 'null') continue;
+ array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
+ }
+ return array;
+ },
+
+ pick: function(){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (this[i] != null) return this[i];
+ }
+ return null;
+ },
+
+ hexToRgb: function(array){
+ if (this.length != 3) return null;
+ var rgb = this.map(function(value){
+ if (value.length == 1) value += value;
+ return parseInt(value, 16);
+ });
+ return (array) ? rgb : 'rgb(' + rgb + ')';
+ },
+
+ rgbToHex: function(array){
+ if (this.length < 3) return null;
+ if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
+ var hex = [];
+ for (var i = 0; i < 3; i++){
+ var bit = (this[i] - 0).toString(16);
+ hex.push((bit.length == 1) ? '0' + bit : bit);
+ }
+ return (array) ? hex : '#' + hex.join('');
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: String
+
+description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
+
+license: MIT-style license.
+
+requires: [Type, Array]
+
+provides: String
+
+...
+*/
+
+String.implement({
+
+ //<!ES6>
+ contains: function(string, index){
+ return (index ? String(this).slice(index) : String(this)).indexOf(string) > -1;
+ },
+ //</!ES6>
+
+ test: function(regex, params){
+ return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
+ },
+
+ trim: function(){
+ return String(this).replace(/^\s+|\s+$/g, '');
+ },
+
+ clean: function(){
+ return String(this).replace(/\s+/g, ' ').trim();
+ },
+
+ camelCase: function(){
+ return String(this).replace(/-\D/g, function(match){
+ return match.charAt(1).toUpperCase();
+ });
+ },
+
+ hyphenate: function(){
+ return String(this).replace(/[A-Z]/g, function(match){
+ return ('-' + match.charAt(0).toLowerCase());
+ });
+ },
+
+ capitalize: function(){
+ return String(this).replace(/\b[a-z]/g, function(match){
+ return match.toUpperCase();
+ });
+ },
+
+ escapeRegExp: function(){
+ return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ hexToRgb: function(array){
+ var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
+ return (hex) ? hex.slice(1).hexToRgb(array) : null;
+ },
+
+ rgbToHex: function(array){
+ var rgb = String(this).match(/\d{1,3}/g);
+ return (rgb) ? rgb.rgbToHex(array) : null;
+ },
+
+ substitute: function(object, regexp){
+ return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
+ if (match.charAt(0) == '\\') return match.slice(1);
+ return (object[name] != null) ? object[name] : '';
+ });
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: Number
+
+description: Contains Number Prototypes like limit, round, times, and ceil.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Number
+
+...
+*/
+
+Number.implement({
+
+ limit: function(min, max){
+ return Math.min(max, Math.max(min, this));
+ },
+
+ round: function(precision){
+ precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
+ return Math.round(this * precision) / precision;
+ },
+
+ times: function(fn, bind){
+ for (var i = 0; i < this; i++) fn.call(bind, i, this);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ }
+
+});
+
+Number.alias('each', 'times');
+
+(function(math){
+ var methods = {};
+ math.each(function(name){
+ if (!Number[name]) methods[name] = function(){
+ return Math[name].apply(null, [this].concat(Array.from(arguments)));
+ };
+ });
+ Number.implement(methods);
+})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
+
+
+/*
+---
+
+name: Function
+
+description: Contains Function Prototypes like create, bind, pass, and delay.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Function
+
+...
+*/
+
+Function.extend({
+
+ attempt: function(){
+ for (var i = 0, l = arguments.length; i < l; i++){
+ try {
+ return arguments[i]();
+ } catch (e){}
+ }
+ return null;
+ }
+
+});
+
+Function.implement({
+
+ attempt: function(args, bind){
+ try {
+ return this.apply(bind, Array.from(args));
+ } catch (e){}
+
+ return null;
+ },
+
+ /*<!ES5-bind>*/
+ bind: function(that){
+ var self = this,
+ args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
+ F = function(){};
+
+ var bound = function(){
+ var context = that, length = arguments.length;
+ if (this instanceof bound){
+ F.prototype = self.prototype;
+ context = new F;
+ }
+ var result = (!args && !length)
+ ? self.call(context)
+ : self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
+ return context == that ? result : context;
+ };
+ return bound;
+ },
+ /*</!ES5-bind>*/
+
+ pass: function(args, bind){
+ var self = this;
+ if (args != null) args = Array.from(args);
+ return function(){
+ return self.apply(bind, args || arguments);
+ };
+ },
+
+ delay: function(delay, bind, args){
+ return setTimeout(this.pass((args == null ? [] : args), bind), delay);
+ },
+
+ periodical: function(periodical, bind, args){
+ return setInterval(this.pass((args == null ? [] : args), bind), periodical);
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: Object
+
+description: Object generic methods
+
+license: MIT-style license.
+
+requires: Type
+
+provides: [Object, Hash]
+
+...
+*/
+
+(function(){
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+Object.extend({
+
+ subset: function(object, keys){
+ var results = {};
+ for (var i = 0, l = keys.length; i < l; i++){
+ var k = keys[i];
+ if (k in object) results[k] = object[k];
+ }
+ return results;
+ },
+
+ map: function(object, fn, bind){
+ var results = {};
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
+ }
+ return results;
+ },
+
+ filter: function(object, fn, bind){
+ var results = {};
+ for (var key in object){
+ var value = object[key];
+ if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
+ }
+ return results;
+ },
+
+ every: function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
+ }
+ return true;
+ },
+
+ some: function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
+ }
+ return false;
+ },
+
+ keys: function(object){
+ var keys = [];
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) keys.push(key);
+ }
+ return keys;
+ },
+
+ values: function(object){
+ var values = [];
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) values.push(object[key]);
+ }
+ return values;
+ },
+
+ getLength: function(object){
+ return Object.keys(object).length;
+ },
+
+ keyOf: function(object, value){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && object[key] === value) return key;
+ }
+ return null;
+ },
+
+ contains: function(object, value){
+ return Object.keyOf(object, value) != null;
+ },
+
+ toQueryString: function(object, base){
+ var queryString = [];
+
+ Object.each(object, function(value, key){
+ if (base) key = base + '[' + key + ']';
+ var result;
+ switch (typeOf(value)){
+ case 'object': result = Object.toQueryString(value, key); break;
+ case 'array':
+ var qs = {};
+ value.each(function(val, i){
+ qs[i] = val;
+ });
+ result = Object.toQueryString(qs, key);
+ break;
+ default: result = key + '=' + encodeURIComponent(value);
+ }
+ if (value != null) queryString.push(result);
+ });
+
+ return queryString.join('&');
+ }
+
+});
+
+})();
+
+
+
+
+/*
+---
+
+name: Browser
+
+description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
+
+license: MIT-style license.
+
+requires: [Array, Function, Number, String]
+
+provides: [Browser, Window, Document]
+
+...
+*/
+
+(function(){
+
+var document = this.document;
+var window = document.window = this;
+
+var parse = function(ua, platform){
+ ua = ua.toLowerCase();
+ platform = (platform ? platform.toLowerCase() : '');
+
+ var UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/) || [null, 'unknown', 0];
+
+ if (UA[1] == 'trident'){
+ UA[1] = 'ie';
+ if (UA[4]) UA[2] = UA[4];
+ } else if (UA[1] == 'crios') {
+ UA[1] = 'chrome';
+ }
+
+ var platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0];
+ if (platform == 'win') platform = 'windows';
+
+ return {
+ extend: Function.prototype.extend,
+ name: (UA[1] == 'version') ? UA[3] : UA[1],
+ version: parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
+ platform: platform
+ };
+};
+
+var Browser = this.Browser = parse(navigator.userAgent, navigator.platform);
+
+if (Browser.ie){
+ Browser.version = document.documentMode;
+}
+
+Browser.extend({
+ Features: {
+ xpath: !!(document.evaluate),
+ air: !!(window.runtime),
+ query: !!(document.querySelector),
+ json: !!(window.JSON)
+ },
+ parseUA: parse
+});
+
+
+
+// Request
+
+Browser.Request = (function(){
+
+ var XMLHTTP = function(){
+ return new XMLHttpRequest();
+ };
+
+ var MSXML2 = function(){
+ return new ActiveXObject('MSXML2.XMLHTTP');
+ };
+
+ var MSXML = function(){
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ };
+
+ return Function.attempt(function(){
+ XMLHTTP();
+ return XMLHTTP;
+ }, function(){
+ MSXML2();
+ return MSXML2;
+ }, function(){
+ MSXML();
+ return MSXML;
+ });
+
+})();
+
+Browser.Features.xhr = !!(Browser.Request);
+
+
+
+// String scripts
+
+Browser.exec = function(text){
+ if (!text) return text;
+ if (window.execScript){
+ window.execScript(text);
+ } else {
+ var script = document.createElement('script');
+ script.setAttribute('type', 'text/javascript');
+ script.text = text;
+ document.head.appendChild(script);
+ document.head.removeChild(script);
+ }
+ return text;
+};
+
+String.implement('stripScripts', function(exec){
+ var scripts = '';
+ var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
+ scripts += code + '\n';
+ return '';
+ });
+ if (exec === true) Browser.exec(scripts);
+ else if (typeOf(exec) == 'function') exec(scripts, text);
+ return text;
+});
+
+// Window, Document
+
+Browser.extend({
+ Document: this.Document,
+ Window: this.Window,
+ Element: this.Element,
+ Event: this.Event
+});
+
+this.Window = this.$constructor = new Type('Window', function(){});
+
+this.$family = Function.from('window').hide();
+
+Window.mirror(function(name, method){
+ window[name] = method;
+});
+
+this.Document = document.$constructor = new Type('Document', function(){});
+
+document.$family = Function.from('document').hide();
+
+Document.mirror(function(name, method){
+ document[name] = method;
+});
+
+document.html = document.documentElement;
+if (!document.head) document.head = document.getElementsByTagName('head')[0];
+
+if (document.execCommand) try {
+ document.execCommand("BackgroundImageCache", false, true);
+} catch (e){}
+
+/*<ltIE9>*/
+if (this.attachEvent && !this.addEventListener){
+ var unloadEvent = function(){
+ this.detachEvent('onunload', unloadEvent);
+ document.head = document.html = document.window = null;
+ };
+ this.attachEvent('onunload', unloadEvent);
+}
+
+// IE fails on collections and <select>.options (refers to <select>)
+var arrayFrom = Array.from;
+try {
+ arrayFrom(document.html.childNodes);
+} catch(e){
+ Array.from = function(item){
+ if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
+ var i = item.length, array = new Array(i);
+ while (i--) array[i] = item[i];
+ return array;
+ }
+ return arrayFrom(item);
+ };
+
+ var prototype = Array.prototype,
+ slice = prototype.slice;
+ ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
+ var method = prototype[name];
+ Array[name] = function(item){
+ return method.apply(Array.from(item), slice.call(arguments, 1));
+ };
+ });
+}
+/*</ltIE9>*/
+
+
+
+})();
+
+
+/*
+---
+
+name: Event
+
+description: Contains the Event Type, to make the event object cross-browser.
+
+license: MIT-style license.
+
+requires: [Window, Document, Array, Function, String, Object]
+
+provides: Event
+
+...
+*/
+
+(function() {
+
+var _keys = {};
+
+var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){
+ if (!win) win = window;
+ event = event || win.event;
+ if (event.$extended) return event;
+ this.event = event;
+ this.$extended = true;
+ this.shift = event.shiftKey;
+ this.control = event.ctrlKey;
+ this.alt = event.altKey;
+ this.meta = event.metaKey;
+ var type = this.type = event.type;
+ var target = event.target || event.srcElement;
+ while (target && target.nodeType == 3) target = target.parentNode;
+ this.target = document.id(target);
+
+ if (type.indexOf('key') == 0){
+ var code = this.code = (event.which || event.keyCode);
+ this.key = _keys[code];
+ if (type == 'keydown' || type == 'keyup'){
+ if (code > 111 && code < 124) this.key = 'f' + (code - 111);
+ else if (code > 95 && code < 106) this.key = code - 96;
+ }
+ if (this.key == null) this.key = String.fromCharCode(code).toLowerCase();
+ } else if (type == 'click' || type == 'dblclick' || type == 'contextmenu' || type == 'DOMMouseScroll' || type.indexOf('mouse') == 0){
+ var doc = win.document;
+ doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
+ this.page = {
+ x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
+ y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
+ };
+ this.client = {
+ x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
+ y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
+ };
+ if (type == 'DOMMouseScroll' || type == 'mousewheel')
+ this.wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
+
+ this.rightClick = (event.which == 3 || event.button == 2);
+ if (type == 'mouseover' || type == 'mouseout'){
+ var related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
+ while (related && related.nodeType == 3) related = related.parentNode;
+ this.relatedTarget = document.id(related);
+ }
+ } else if (type.indexOf('touch') == 0 || type.indexOf('gesture') == 0){
+ this.rotation = event.rotation;
+ this.scale = event.scale;
+ this.targetTouches = event.targetTouches;
+ this.changedTouches = event.changedTouches;
+ var touches = this.touches = event.touches;
+ if (touches && touches[0]){
+ var touch = touches[0];
+ this.page = {x: touch.pageX, y: touch.pageY};
+ this.client = {x: touch.clientX, y: touch.clientY};
+ }
+ }
+
+ if (!this.client) this.client = {};
+ if (!this.page) this.page = {};
+});
+
+DOMEvent.implement({
+
+ stop: function(){
+ return this.preventDefault().stopPropagation();
+ },
+
+ stopPropagation: function(){
+ if (this.event.stopPropagation) this.event.stopPropagation();
+ else this.event.cancelBubble = true;
+ return this;
+ },
+
+ preventDefault: function(){
+ if (this.event.preventDefault) this.event.preventDefault();
+ else this.event.returnValue = false;
+ return this;
+ }
+
+});
+
+DOMEvent.defineKey = function(code, key){
+ _keys[code] = key;
+ return this;
+};
+
+DOMEvent.defineKeys = DOMEvent.defineKey.overloadSetter(true);
+
+DOMEvent.defineKeys({
+ '38': 'up', '40': 'down', '37': 'left', '39': 'right',
+ '27': 'esc', '32': 'space', '8': 'backspace', '9': 'tab',
+ '46': 'delete', '13': 'enter'
+});
+
+})();
+
+
+
+
+
+
+/*
+---
+
+name: Class
+
+description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
+
+license: MIT-style license.
+
+requires: [Array, String, Function, Number]
+
+provides: Class
+
+...
+*/
+
+(function(){
+
+var Class = this.Class = new Type('Class', function(params){
+ if (instanceOf(params, Function)) params = {initialize: params};
+
+ var newClass = function(){
+ reset(this);
+ if (newClass.$prototyping) return this;
+ this.$caller = null;
+ var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
+ this.$caller = this.caller = null;
+ return value;
+ }.extend(this).implement(params);
+
+ newClass.$constructor = Class;
+ newClass.prototype.$constructor = newClass;
+ newClass.prototype.parent = parent;
+
+ return newClass;
+});
+
+var parent = function(){
+ if (!this.$caller) throw new Error('The method "parent" cannot be called.');
+ var name = this.$caller.$name,
+ parent = this.$caller.$owner.parent,
+ previous = (parent) ? parent.prototype[name] : null;
+ if (!previous) throw new Error('The method "' + name + '" has no parent.');
+ return previous.apply(this, arguments);
+};
+
+var reset = function(object){
+ for (var key in object){
+ var value = object[key];
+ switch (typeOf(value)){
+ case 'object':
+ var F = function(){};
+ F.prototype = value;
+ object[key] = reset(new F);
+ break;
+ case 'array': object[key] = value.clone(); break;
+ }
+ }
+ return object;
+};
+
+var wrap = function(self, key, method){
+ if (method.$origin) method = method.$origin;
+ var wrapper = function(){
+ if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
+ var caller = this.caller, current = this.$caller;
+ this.caller = current; this.$caller = wrapper;
+ var result = method.apply(this, arguments);
+ this.$caller = current; this.caller = caller;
+ return result;
+ }.extend({$owner: self, $origin: method, $name: key});
+ return wrapper;
+};
+
+var implement = function(key, value, retain){
+ if (Class.Mutators.hasOwnProperty(key)){
+ value = Class.Mutators[key].call(this, value);
+ if (value == null) return this;
+ }
+
+ if (typeOf(value) == 'function'){
+ if (value.$hidden) return this;
+ this.prototype[key] = (retain) ? value : wrap(this, key, value);
+ } else {
+ Object.merge(this.prototype, key, value);
+ }
+
+ return this;
+};
+
+var getInstance = function(klass){
+ klass.$prototyping = true;
+ var proto = new klass;
+ delete klass.$prototyping;
+ return proto;
+};
+
+Class.implement('implement', implement.overloadSetter());
+
+Class.Mutators = {
+
+ Extends: function(parent){
+ this.parent = parent;
+ this.prototype = getInstance(parent);
+ },
+
+ Implements: function(items){
+ Array.from(items).each(function(item){
+ var instance = new item;
+ for (var key in instance) implement.call(this, key, instance[key], true);
+ }, this);
+ }
+};
+
+})();
+
+
+/*
+---
+
+name: Class.Extras
+
+description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
+
+license: MIT-style license.
+
+requires: Class
+
+provides: [Class.Extras, Chain, Events, Options]
+
+...
+*/
+
+(function(){
+
+this.Chain = new Class({
+
+ $chain: [],
+
+ chain: function(){
+ this.$chain.append(Array.flatten(arguments));
+ return this;
+ },
+
+ callChain: function(){
+ return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
+ },
+
+ clearChain: function(){
+ this.$chain.empty();
+ return this;
+ }
+
+});
+
+var removeOn = function(string){
+ return string.replace(/^on([A-Z])/, function(full, first){
+ return first.toLowerCase();
+ });
+};
+
+this.Events = new Class({
+
+ $events: {},
+
+ addEvent: function(type, fn, internal){
+ type = removeOn(type);
+
+
+
+ this.$events[type] = (this.$events[type] || []).include(fn);
+ if (internal) fn.internal = true;
+ return this;
+ },
+
+ addEvents: function(events){
+ for (var type in events) this.addEvent(type, events[type]);
+ return this;
+ },
+
+ fireEvent: function(type, args, delay){
+ type = removeOn(type);
+ var events = this.$events[type];
+ if (!events) return this;
+ args = Array.from(args);
+ events.each(function(fn){
+ if (delay) fn.delay(delay, this, args);
+ else fn.apply(this, args);
+ }, this);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ type = removeOn(type);
+ var events = this.$events[type];
+ if (events && !fn.internal){
+ var index = events.indexOf(fn);
+ if (index != -1) delete events[index];
+ }
+ return this;
+ },
+
+ removeEvents: function(events){
+ var type;
+ if (typeOf(events) == 'object'){
+ for (type in events) this.removeEvent(type, events[type]);
+ return this;
+ }
+ if (events) events = removeOn(events);
+ for (type in this.$events){
+ if (events && events != type) continue;
+ var fns = this.$events[type];
+ for (var i = fns.length; i--;) if (i in fns){
+ this.removeEvent(type, fns[i]);
+ }
+ }
+ return this;
+ }
+
+});
+
+this.Options = new Class({
+
+ setOptions: function(){
+ var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
+ if (this.addEvent) for (var option in options){
+ if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
+ this.addEvent(option, options[option]);
+ delete options[option];
+ }
+ return this;
+ }
+
+});
+
+})();
+
+
+/*
+---
+name: Slick.Parser
+description: Standalone CSS3 Selector parser
+provides: Slick.Parser
+...
+*/
+
+;(function(){
+
+var parsed,
+ separatorIndex,
+ combinatorIndex,
+ reversed,
+ cache = {},
+ reverseCache = {},
+ reUnescape = /\\/g;
+
+var parse = function(expression, isReversed){
+ if (expression == null) return null;
+ if (expression.Slick === true) return expression;
+ expression = ('' + expression).replace(/^\s+|\s+$/g, '');
+ reversed = !!isReversed;
+ var currentCache = (reversed) ? reverseCache : cache;
+ if (currentCache[expression]) return currentCache[expression];
+ parsed = {
+ Slick: true,
+ expressions: [],
+ raw: expression,
+ reverse: function(){
+ return parse(this.raw, true);
+ }
+ };
+ separatorIndex = -1;
+ while (expression != (expression = expression.replace(regexp, parser)));
+ parsed.length = parsed.expressions.length;
+ return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
+};
+
+var reverseCombinator = function(combinator){
+ if (combinator === '!') return ' ';
+ else if (combinator === ' ') return '!';
+ else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
+ else return '!' + combinator;
+};
+
+var reverse = function(expression){
+ var expressions = expression.expressions;
+ for (var i = 0; i < expressions.length; i++){
+ var exp = expressions[i];
+ var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
+
+ for (var j = 0; j < exp.length; j++){
+ var cexp = exp[j];
+ if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
+ cexp.combinator = cexp.reverseCombinator;
+ delete cexp.reverseCombinator;
+ }
+
+ exp.reverse().push(last);
+ }
+ return expression;
+};
+
+var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
+ return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
+ return '\\' + match;
+ });
+};
+
+var regexp = new RegExp(
+/*
+#!/usr/bin/env ruby
+puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
+__END__
+ "(?x)^(?:\
+ \\s* ( , ) \\s* # Separator \n\
+ | \\s* ( <combinator>+ ) \\s* # Combinator \n\
+ | ( \\s+ ) # CombinatorChildren \n\
+ | ( <unicode>+ | \\* ) # Tag \n\
+ | \\# ( <unicode>+ ) # ID \n\
+ | \\. ( <unicode>+ ) # ClassName \n\
+ | # Attribute \n\
+ \\[ \
+ \\s* (<unicode1>+) (?: \
+ \\s* ([*^$!~|]?=) (?: \
+ \\s* (?:\
+ ([\"']?)(.*?)\\9 \
+ )\
+ ) \
+ )? \\s* \
+ \\](?!\\]) \n\
+ | :+ ( <unicode>+ )(?:\
+ \\( (?:\
+ (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
+ ) \\)\
+ )?\
+ )"
+*/
+ "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
+ .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
+ .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+ .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+);
+
+function parser(
+ rawMatch,
+
+ separator,
+ combinator,
+ combinatorChildren,
+
+ tagName,
+ id,
+ className,
+
+ attributeKey,
+ attributeOperator,
+ attributeQuote,
+ attributeValue,
+
+ pseudoMarker,
+ pseudoClass,
+ pseudoQuote,
+ pseudoClassQuotedValue,
+ pseudoClassValue
+){
+ if (separator || separatorIndex === -1){
+ parsed.expressions[++separatorIndex] = [];
+ combinatorIndex = -1;
+ if (separator) return '';
+ }
+
+ if (combinator || combinatorChildren || combinatorIndex === -1){
+ combinator = combinator || ' ';
+ var currentSeparator = parsed.expressions[separatorIndex];
+ if (reversed && currentSeparator[combinatorIndex])
+ currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
+ currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
+ }
+
+ var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
+
+ if (tagName){
+ currentParsed.tag = tagName.replace(reUnescape, '');
+
+ } else if (id){
+ currentParsed.id = id.replace(reUnescape, '');
+
+ } else if (className){
+ className = className.replace(reUnescape, '');
+
+ if (!currentParsed.classList) currentParsed.classList = [];
+ if (!currentParsed.classes) currentParsed.classes = [];
+ currentParsed.classList.push(className);
+ currentParsed.classes.push({
+ value: className,
+ regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
+ });
+
+ } else if (pseudoClass){
+ pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
+ pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
+
+ if (!currentParsed.pseudos) currentParsed.pseudos = [];
+ currentParsed.pseudos.push({
+ key: pseudoClass.replace(reUnescape, ''),
+ value: pseudoClassValue,
+ type: pseudoMarker.length == 1 ? 'class' : 'element'
+ });
+
+ } else if (attributeKey){
+ attributeKey = attributeKey.replace(reUnescape, '');
+ attributeValue = (attributeValue || '').replace(reUnescape, '');
+
+ var test, regexp;
+
+ switch (attributeOperator){
+ case '^=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) ); break;
+ case '$=' : regexp = new RegExp( escapeRegExp(attributeValue) +'$' ); break;
+ case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
+ case '|=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) +'(-|$)' ); break;
+ case '=' : test = function(value){
+ return attributeValue == value;
+ }; break;
+ case '*=' : test = function(value){
+ return value && value.indexOf(attributeValue) > -1;
+ }; break;
+ case '!=' : test = function(value){
+ return attributeValue != value;
+ }; break;
+ default : test = function(value){
+ return !!value;
+ };
+ }
+
+ if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
+ return false;
+ };
+
+ if (!test) test = function(value){
+ return value && regexp.test(value);
+ };
+
+ if (!currentParsed.attributes) currentParsed.attributes = [];
+ currentParsed.attributes.push({
+ key: attributeKey,
+ operator: attributeOperator,
+ value: attributeValue,
+ test: test
+ });
+
+ }
+
+ return '';
+};
+
+// Slick NS
+
+var Slick = (this.Slick || {});
+
+Slick.parse = function(expression){
+ return parse(expression);
+};
+
+Slick.escapeRegExp = escapeRegExp;
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+
+/*
+---
+name: Slick.Finder
+description: The new, superfast css selector engine.
+provides: Slick.Finder
+requires: Slick.Parser
+...
+*/
+
+;(function(){
+
+var local = {},
+ featuresCache = {},
+ toString = Object.prototype.toString;
+
+// Feature / Bug detection
+
+local.isNativeCode = function(fn){
+ return (/\{\s*\[native code\]\s*\}/).test('' + fn);
+};
+
+local.isXML = function(document){
+ return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
+ (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
+};
+
+local.setDocument = function(document){
+
+ // convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
+ var nodeType = document.nodeType;
+ if (nodeType == 9); // document
+ else if (nodeType) document = document.ownerDocument; // node
+ else if (document.navigator) document = document.document; // window
+ else return;
+
+ // check if it's the old document
+
+ if (this.document === document) return;
+ this.document = document;
+
+ // check if we have done feature detection on this document before
+
+ var root = document.documentElement,
+ rootUid = this.getUIDXML(root),
+ features = featuresCache[rootUid],
+ feature;
+
+ if (features){
+ for (feature in features){
+ this[feature] = features[feature];
+ }
+ return;
+ }
+
+ features = featuresCache[rootUid] = {};
+
+ features.root = root;
+ features.isXMLDocument = this.isXML(document);
+
+ features.brokenStarGEBTN
+ = features.starSelectsClosedQSA
+ = features.idGetsName
+ = features.brokenMixedCaseQSA
+ = features.brokenGEBCN
+ = features.brokenCheckedQSA
+ = features.brokenEmptyAttributeQSA
+ = features.isHTMLDocument
+ = features.nativeMatchesSelector
+ = false;
+
+ var starSelectsClosed, starSelectsComments,
+ brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
+ brokenFormAttributeGetter;
+
+ var selected, id = 'slick_uniqueid';
+ var testNode = document.createElement('div');
+
+ var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
+ testRoot.appendChild(testNode);
+
+ // on non-HTML documents innerHTML and getElementsById doesnt work properly
+ try {
+ testNode.innerHTML = '<a id="'+id+'"></a>';
+ features.isHTMLDocument = !!document.getElementById(id);
+ } catch(e){};
+
+ if (features.isHTMLDocument){
+
+ testNode.style.display = 'none';
+
+ // IE returns comment nodes for getElementsByTagName('*') for some documents
+ testNode.appendChild(document.createComment(''));
+ starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
+
+ // IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
+ try {
+ testNode.innerHTML = 'foo</foo>';
+ selected = testNode.getElementsByTagName('*');
+ starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+ } catch(e){};
+
+ features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
+
+ // IE returns elements with the name instead of just id for getElementsById for some documents
+ try {
+ testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
+ features.idGetsName = document.getElementById(id) === testNode.firstChild;
+ } catch(e){};
+
+ if (testNode.getElementsByClassName){
+
+ // Safari 3.2 getElementsByClassName caches results
+ try {
+ testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
+ testNode.getElementsByClassName('b').length;
+ testNode.firstChild.className = 'b';
+ cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
+ } catch(e){};
+
+ // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
+ try {
+ testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
+ brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
+ } catch(e){};
+
+ features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
+ }
+
+ if (testNode.querySelectorAll){
+ // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
+ try {
+ testNode.innerHTML = 'foo</foo>';
+ selected = testNode.querySelectorAll('*');
+ features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+ } catch(e){};
+
+ // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
+ try {
+ testNode.innerHTML = '<a class="MiX"></a>';
+ features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
+ } catch(e){};
+
+ // Webkit and Opera dont return selected options on querySelectorAll
+ try {
+ testNode.innerHTML = '<select><option selected="selected">a</option></select>';
+ features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
+ } catch(e){};
+
+ // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
+ try {
+ testNode.innerHTML = '<a class=""></a>';
+ features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
+ } catch(e){};
+
+ }
+
+ // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
+ try {
+ testNode.innerHTML = '<form action="s"><input id="action"/></form>';
+ brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
+ } catch(e){};
+
+ // native matchesSelector function
+
+ features.nativeMatchesSelector = root.matches || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
+ if (features.nativeMatchesSelector) try {
+ // if matchesSelector trows errors on incorrect sintaxes we can use it
+ features.nativeMatchesSelector.call(root, ':slick');
+ features.nativeMatchesSelector = null;
+ } catch(e){};
+
+ }
+
+ try {
+ root.slick_expando = 1;
+ delete root.slick_expando;
+ features.getUID = this.getUIDHTML;
+ } catch(e) {
+ features.getUID = this.getUIDXML;
+ }
+
+ testRoot.removeChild(testNode);
+ testNode = selected = testRoot = null;
+
+ // getAttribute
+
+ features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
+ var method = this.attributeGetters[name];
+ if (method) return method.call(node);
+ var attributeNode = node.getAttributeNode(name);
+ return (attributeNode) ? attributeNode.nodeValue : null;
+ } : function(node, name){
+ var method = this.attributeGetters[name];
+ return (method) ? method.call(node) : node.getAttribute(name);
+ };
+
+ // hasAttribute
+
+ features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) {
+ return node.hasAttribute(attribute);
+ } : function(node, attribute) {
+ node = node.getAttributeNode(attribute);
+ return !!(node && (node.specified || node.nodeValue));
+ };
+
+ // contains
+ // FIXME: Add specs: local.contains should be different for xml and html documents?
+ var nativeRootContains = root && this.isNativeCode(root.contains),
+ nativeDocumentContains = document && this.isNativeCode(document.contains);
+
+ features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){
+ return context.contains(node);
+ } : (nativeRootContains && !nativeDocumentContains) ? function(context, node){
+ // IE8 does not have .contains on document.
+ return context === node || ((context === document) ? document.documentElement : context).contains(node);
+ } : (root && root.compareDocumentPosition) ? function(context, node){
+ return context === node || !!(context.compareDocumentPosition(node) & 16);
+ } : function(context, node){
+ if (node) do {
+ if (node === context) return true;
+ } while ((node = node.parentNode));
+ return false;
+ };
+
+ // document order sorting
+ // credits to Sizzle (http://sizzlejs.com/)
+
+ features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
+ if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
+ return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+ } : ('sourceIndex' in root) ? function(a, b){
+ if (!a.sourceIndex || !b.sourceIndex) return 0;
+ return a.sourceIndex - b.sourceIndex;
+ } : (document.createRange) ? function(a, b){
+ if (!a.ownerDocument || !b.ownerDocument) return 0;
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+ aRange.setStart(a, 0);
+ aRange.setEnd(a, 0);
+ bRange.setStart(b, 0);
+ bRange.setEnd(b, 0);
+ return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+ } : null ;
+
+ root = null;
+
+ for (feature in features){
+ this[feature] = features[feature];
+ }
+};
+
+// Main Method
+
+var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
+ reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
+ qsaFailExpCache = {};
+
+local.search = function(context, expression, append, first){
+
+ var found = this.found = (first) ? null : (append || []);
+
+ if (!context) return found;
+ else if (context.navigator) context = context.document; // Convert the node from a window to a document
+ else if (!context.nodeType) return found;
+
+ // setup
+
+ var parsed, i,
+ uniques = this.uniques = {},
+ hasOthers = !!(append && append.length),
+ contextIsDocument = (context.nodeType == 9);
+
+ if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
+
+ // avoid duplicating items already in the append array
+ if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
+
+ // expression checks
+
+ if (typeof expression == 'string'){ // expression is a string
+
+ /*<simple-selectors-override>*/
+ var simpleSelector = expression.match(reSimpleSelector);
+ simpleSelectors: if (simpleSelector) {
+
+ var symbol = simpleSelector[1],
+ name = simpleSelector[2],
+ node, nodes;
+
+ if (!symbol){
+
+ if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
+ nodes = context.getElementsByTagName(name);
+ if (first) return nodes[0] || null;
+ for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+
+ } else if (symbol == '#'){
+
+ if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
+ node = context.getElementById(name);
+ if (!node) return found;
+ if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
+ if (first) return node || null;
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+
+ } else if (symbol == '.'){
+
+ if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
+ if (context.getElementsByClassName && !this.brokenGEBCN){
+ nodes = context.getElementsByClassName(name);
+ if (first) return nodes[0] || null;
+ for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+ } else {
+ var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
+ nodes = context.getElementsByTagName('*');
+ for (i = 0; node = nodes[i++];){
+ className = node.className;
+ if (!(className && matchClass.test(className))) continue;
+ if (first) return node;
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+ }
+
+ }
+
+ if (hasOthers) this.sort(found);
+ return (first) ? null : found;
+
+ }
+ /*</simple-selectors-override>*/
+
+ /*<query-selector-override>*/
+ querySelector: if (context.querySelectorAll) {
+
+ if (!this.isHTMLDocument
+ || qsaFailExpCache[expression]
+ //TODO: only skip when expression is actually mixed case
+ || this.brokenMixedCaseQSA
+ || (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
+ || (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
+ || (!contextIsDocument //Abort when !contextIsDocument and...
+ // there are multiple expressions in the selector
+ // since we currently only fix non-document rooted QSA for single expression selectors
+ && expression.indexOf(',') > -1
+ )
+ || Slick.disableQSA
+ ) break querySelector;
+
+ var _expression = expression, _context = context;
+ if (!contextIsDocument){
+ // non-document rooted QSA
+ // credits to Andrew Dupont
+ var currentId = _context.getAttribute('id'), slickid = 'slickid__';
+ _context.setAttribute('id', slickid);
+ _expression = '#' + slickid + ' ' + _expression;
+ context = _context.parentNode;
+ }
+
+ try {
+ if (first) return context.querySelector(_expression) || null;
+ else nodes = context.querySelectorAll(_expression);
+ } catch(e) {
+ qsaFailExpCache[expression] = 1;
+ break querySelector;
+ } finally {
+ if (!contextIsDocument){
+ if (currentId) _context.setAttribute('id', currentId);
+ else _context.removeAttribute('id');
+ context = _context;
+ }
+ }
+
+ if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
+ if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ } else for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+
+ if (hasOthers) this.sort(found);
+ return found;
+
+ }
+ /*</query-selector-override>*/
+
+ parsed = this.Slick.parse(expression);
+ if (!parsed.length) return found;
+ } else if (expression == null){ // there is no expression
+ return found;
+ } else if (expression.Slick){ // expression is a parsed Slick object
+ parsed = expression;
+ } else if (this.contains(context.documentElement || context, expression)){ // expression is a node
+ (found) ? found.push(expression) : found = expression;
+ return found;
+ } else { // other junk
+ return found;
+ }
+
+ /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+ // cache elements for the nth selectors
+
+ this.posNTH = {};
+ this.posNTHLast = {};
+ this.posNTHType = {};
+ this.posNTHTypeLast = {};
+
+ /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+ // if append is null and there is only a single selector with one expression use pushArray, else use pushUID
+ this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
+
+ if (found == null) found = [];
+
+ // default engine
+
+ var j, m, n;
+ var combinator, tag, id, classList, classes, attributes, pseudos;
+ var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
+
+ search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
+
+ combinator = 'combinator:' + currentBit.combinator;
+ if (!this[combinator]) continue search;
+
+ tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
+ id = currentBit.id;
+ classList = currentBit.classList;
+ classes = currentBit.classes;
+ attributes = currentBit.attributes;
+ pseudos = currentBit.pseudos;
+ lastBit = (j === (currentExpression.length - 1));
+
+ this.bitUniques = {};
+
+ if (lastBit){
+ this.uniques = uniques;
+ this.found = found;
+ } else {
+ this.uniques = {};
+ this.found = [];
+ }
+
+ if (j === 0){
+ this[combinator](context, tag, id, classes, attributes, pseudos, classList);
+ if (first && lastBit && found.length) break search;
+ } else {
+ if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
+ this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+ if (found.length) break search;
+ } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+ }
+
+ currentItems = this.found;
+ }
+
+ // should sort if there are nodes in append and if you pass multiple expressions.
+ if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
+
+ return (first) ? (found[0] || null) : found;
+};
+
+// Utils
+
+local.uidx = 1;
+local.uidk = 'slick-uniqueid';
+
+local.getUIDXML = function(node){
+ var uid = node.getAttribute(this.uidk);
+ if (!uid){
+ uid = this.uidx++;
+ node.setAttribute(this.uidk, uid);
+ }
+ return uid;
+};
+
+local.getUIDHTML = function(node){
+ return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
+};
+
+// sort based on the setDocument documentSorter method.
+
+local.sort = function(results){
+ if (!this.documentSorter) return results;
+ results.sort(this.documentSorter);
+ return results;
+};
+
+/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+local.cacheNTH = {};
+
+local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
+
+local.parseNTHArgument = function(argument){
+ var parsed = argument.match(this.matchNTH);
+ if (!parsed) return false;
+ var special = parsed[2] || false;
+ var a = parsed[1] || 1;
+ if (a == '-') a = -1;
+ var b = +parsed[3] || 0;
+ parsed =
+ (special == 'n') ? {a: a, b: b} :
+ (special == 'odd') ? {a: 2, b: 1} :
+ (special == 'even') ? {a: 2, b: 0} : {a: 0, b: a};
+
+ return (this.cacheNTH[argument] = parsed);
+};
+
+local.createNTHPseudo = function(child, sibling, positions, ofType){
+ return function(node, argument){
+ var uid = this.getUID(node);
+ if (!this[positions][uid]){
+ var parent = node.parentNode;
+ if (!parent) return false;
+ var el = parent[child], count = 1;
+ if (ofType){
+ var nodeName = node.nodeName;
+ do {
+ if (el.nodeName != nodeName) continue;
+ this[positions][this.getUID(el)] = count++;
+ } while ((el = el[sibling]));
+ } else {
+ do {
+ if (el.nodeType != 1) continue;
+ this[positions][this.getUID(el)] = count++;
+ } while ((el = el[sibling]));
+ }
+ }
+ argument = argument || 'n';
+ var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
+ if (!parsed) return false;
+ var a = parsed.a, b = parsed.b, pos = this[positions][uid];
+ if (a == 0) return b == pos;
+ if (a > 0){
+ if (pos < b) return false;
+ } else {
+ if (b < pos) return false;
+ }
+ return ((pos - b) % a) == 0;
+ };
+};
+
+/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+local.pushArray = function(node, tag, id, classes, attributes, pseudos){
+ if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
+};
+
+local.pushUID = function(node, tag, id, classes, attributes, pseudos){
+ var uid = this.getUID(node);
+ if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
+ this.uniques[uid] = true;
+ this.found.push(node);
+ }
+};
+
+local.matchNode = function(node, selector){
+ if (this.isHTMLDocument && this.nativeMatchesSelector){
+ try {
+ return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
+ } catch(matchError) {}
+ }
+
+ var parsed = this.Slick.parse(selector);
+ if (!parsed) return true;
+
+ // simple (single) selectors
+ var expressions = parsed.expressions, simpleExpCounter = 0, i;
+ for (i = 0; (currentExpression = expressions[i]); i++){
+ if (currentExpression.length == 1){
+ var exp = currentExpression[0];
+ if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
+ simpleExpCounter++;
+ }
+ }
+
+ if (simpleExpCounter == parsed.length) return false;
+
+ var nodes = this.search(this.document, parsed), item;
+ for (i = 0; item = nodes[i++];){
+ if (item === node) return true;
+ }
+ return false;
+};
+
+local.matchPseudo = function(node, name, argument){
+ var pseudoName = 'pseudo:' + name;
+ if (this[pseudoName]) return this[pseudoName](node, argument);
+ var attribute = this.getAttribute(node, name);
+ return (argument) ? argument == attribute : !!attribute;
+};
+
+local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
+ if (tag){
+ var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
+ if (tag == '*'){
+ if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
+ } else {
+ if (nodeName != tag) return false;
+ }
+ }
+
+ if (id && node.getAttribute('id') != id) return false;
+
+ var i, part, cls;
+ if (classes) for (i = classes.length; i--;){
+ cls = this.getAttribute(node, 'class');
+ if (!(cls && classes[i].regexp.test(cls))) return false;
+ }
+ if (attributes) for (i = attributes.length; i--;){
+ part = attributes[i];
+ if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
+ }
+ if (pseudos) for (i = pseudos.length; i--;){
+ part = pseudos[i];
+ if (!this.matchPseudo(node, part.key, part.value)) return false;
+ }
+ return true;
+};
+
+var combinators = {
+
+ ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
+
+ var i, item, children;
+
+ if (this.isHTMLDocument){
+ getById: if (id){
+ item = this.document.getElementById(id);
+ if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
+ // all[id] returns all the elements with that name or id inside node
+ // if theres just one it will return the element, else it will be a collection
+ children = node.all[id];
+ if (!children) return;
+ if (!children[0]) children = [children];
+ for (i = 0; item = children[i++];){
+ var idNode = item.getAttributeNode('id');
+ if (idNode && idNode.nodeValue == id){
+ this.push(item, tag, null, classes, attributes, pseudos);
+ break;
+ }
+ }
+ return;
+ }
+ if (!item){
+ // if the context is in the dom we return, else we will try GEBTN, breaking the getById label
+ if (this.contains(this.root, node)) return;
+ else break getById;
+ } else if (this.document !== node && !this.contains(node, item)) return;
+ this.push(item, tag, null, classes, attributes, pseudos);
+ return;
+ }
+ getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
+ children = node.getElementsByClassName(classList.join(' '));
+ if (!(children && children.length)) break getByClass;
+ for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
+ return;
+ }
+ }
+ getByTag: {
+ children = node.getElementsByTagName(tag);
+ if (!(children && children.length)) break getByTag;
+ if (!this.brokenStarGEBTN) tag = null;
+ for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '>': function(node, tag, id, classes, attributes, pseudos){ // direct children
+ if ((node = node.firstChild)) do {
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ } while ((node = node.nextSibling));
+ },
+
+ '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
+ while ((node = node.nextSibling)) if (node.nodeType == 1){
+ this.push(node, tag, id, classes, attributes, pseudos);
+ break;
+ }
+ },
+
+ '^': function(node, tag, id, classes, attributes, pseudos){ // first child
+ node = node.firstChild;
+ if (node){
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
+ while ((node = node.nextSibling)){
+ if (node.nodeType != 1) continue;
+ var uid = this.getUID(node);
+ if (this.bitUniques[uid]) break;
+ this.bitUniques[uid] = true;
+ this.push(node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
+ this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+ this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+ },
+
+ '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
+ this['combinator:~'](node, tag, id, classes, attributes, pseudos);
+ this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
+ while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
+ node = node.parentNode;
+ if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
+ while ((node = node.previousSibling)) if (node.nodeType == 1){
+ this.push(node, tag, id, classes, attributes, pseudos);
+ break;
+ }
+ },
+
+ '!^': function(node, tag, id, classes, attributes, pseudos){ // last child
+ node = node.lastChild;
+ if (node){
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
+ while ((node = node.previousSibling)){
+ if (node.nodeType != 1) continue;
+ var uid = this.getUID(node);
+ if (this.bitUniques[uid]) break;
+ this.bitUniques[uid] = true;
+ this.push(node, tag, id, classes, attributes, pseudos);
+ }
+ }
+
+};
+
+for (var c in combinators) local['combinator:' + c] = combinators[c];
+
+var pseudos = {
+
+ /*<pseudo-selectors>*/
+
+ 'empty': function(node){
+ var child = node.firstChild;
+ return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
+ },
+
+ 'not': function(node, expression){
+ return !this.matchNode(node, expression);
+ },
+
+ 'contains': function(node, text){
+ return (node.innerText || node.textContent || '').indexOf(text) > -1;
+ },
+
+ 'first-child': function(node){
+ while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
+ return true;
+ },
+
+ 'last-child': function(node){
+ while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
+ return true;
+ },
+
+ 'only-child': function(node){
+ var prev = node;
+ while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
+ var next = node;
+ while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
+ return true;
+ },
+
+ /*<nth-pseudo-selectors>*/
+
+ 'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
+
+ 'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
+
+ 'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
+
+ 'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
+
+ 'index': function(node, index){
+ return this['pseudo:nth-child'](node, '' + (index + 1));
+ },
+
+ 'even': function(node){
+ return this['pseudo:nth-child'](node, '2n');
+ },
+
+ 'odd': function(node){
+ return this['pseudo:nth-child'](node, '2n+1');
+ },
+
+ /*</nth-pseudo-selectors>*/
+
+ /*<of-type-pseudo-selectors>*/
+
+ 'first-of-type': function(node){
+ var nodeName = node.nodeName;
+ while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
+ return true;
+ },
+
+ 'last-of-type': function(node){
+ var nodeName = node.nodeName;
+ while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
+ return true;
+ },
+
+ 'only-of-type': function(node){
+ var prev = node, nodeName = node.nodeName;
+ while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
+ var next = node;
+ while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
+ return true;
+ },
+
+ /*</of-type-pseudo-selectors>*/
+
+ // custom pseudos
+
+ 'enabled': function(node){
+ return !node.disabled;
+ },
+
+ 'disabled': function(node){
+ return node.disabled;
+ },
+
+ 'checked': function(node){
+ return node.checked || node.selected;
+ },
+
+ 'focus': function(node){
+ return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
+ },
+
+ 'root': function(node){
+ return (node === this.root);
+ },
+
+ 'selected': function(node){
+ return node.selected;
+ }
+
+ /*</pseudo-selectors>*/
+};
+
+for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
+
+// attributes methods
+
+var attributeGetters = local.attributeGetters = {
+
+ 'for': function(){
+ return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
+ },
+
+ 'href': function(){
+ return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
+ },
+
+ 'style': function(){
+ return (this.style) ? this.style.cssText : this.getAttribute('style');
+ },
+
+ 'tabindex': function(){
+ var attributeNode = this.getAttributeNode('tabindex');
+ return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+ },
+
+ 'type': function(){
+ return this.getAttribute('type');
+ },
+
+ 'maxlength': function(){
+ var attributeNode = this.getAttributeNode('maxLength');
+ return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+ }
+
+};
+
+attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
+
+// Slick
+
+var Slick = local.Slick = (this.Slick || {});
+
+Slick.version = '1.1.7';
+
+// Slick finder
+
+Slick.search = function(context, expression, append){
+ return local.search(context, expression, append);
+};
+
+Slick.find = function(context, expression){
+ return local.search(context, expression, null, true);
+};
+
+// Slick containment checker
+
+Slick.contains = function(container, node){
+ local.setDocument(container);
+ return local.contains(container, node);
+};
+
+// Slick attribute getter
+
+Slick.getAttribute = function(node, name){
+ local.setDocument(node);
+ return local.getAttribute(node, name);
+};
+
+Slick.hasAttribute = function(node, name){
+ local.setDocument(node);
+ return local.hasAttribute(node, name);
+};
+
+// Slick matcher
+
+Slick.match = function(node, selector){
+ if (!(node && selector)) return false;
+ if (!selector || selector === node) return true;
+ local.setDocument(node);
+ return local.matchNode(node, selector);
+};
+
+// Slick attribute accessor
+
+Slick.defineAttributeGetter = function(name, fn){
+ local.attributeGetters[name] = fn;
+ return this;
+};
+
+Slick.lookupAttributeGetter = function(name){
+ return local.attributeGetters[name];
+};
+
+// Slick pseudo accessor
+
+Slick.definePseudo = function(name, fn){
+ local['pseudo:' + name] = function(node, argument){
+ return fn.call(node, argument);
+ };
+ return this;
+};
+
+Slick.lookupPseudo = function(name){
+ var pseudo = local['pseudo:' + name];
+ if (pseudo) return function(argument){
+ return pseudo.call(this, argument);
+ };
+ return null;
+};
+
+// Slick overrides accessor
+
+Slick.override = function(regexp, fn){
+ local.override(regexp, fn);
+ return this;
+};
+
+Slick.isXML = local.isXML;
+
+Slick.uidOf = function(node){
+ return local.getUIDHTML(node);
+};
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+
+/*
+---
+
+name: Element
+
+description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements.
+
+license: MIT-style license.
+
+requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder]
+
+provides: [Element, Elements, $, $$, IFrame, Selectors]
+
+...
+*/
+
+var Element = this.Element = function(tag, props){
+ var konstructor = Element.Constructors[tag];
+ if (konstructor) return konstructor(props);
+ if (typeof tag != 'string') return document.id(tag).set(props);
+
+ if (!props) props = {};
+
+ if (!(/^[\w-]+$/).test(tag)){
+ var parsed = Slick.parse(tag).expressions[0][0];
+ tag = (parsed.tag == '*') ? 'div' : parsed.tag;
+ if (parsed.id && props.id == null) props.id = parsed.id;
+
+ var attributes = parsed.attributes;
+ if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){
+ attr = attributes[i];
+ if (props[attr.key] != null) continue;
+
+ if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value;
+ else if (!attr.value && !attr.operator) props[attr.key] = true;
+ }
+
+ if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
+ }
+
+ return document.newElement(tag, props);
+};
+
+
+if (Browser.Element){
+ Element.prototype = Browser.Element.prototype;
+ // IE8 and IE9 require the wrapping.
+ Element.prototype._fireEvent = (function(fireEvent){
+ return function(type, event){
+ return fireEvent.call(this, type, event);
+ };
+ })(Element.prototype.fireEvent);
+}
+
+new Type('Element', Element).mirror(function(name){
+ if (Array.prototype[name]) return;
+
+ var obj = {};
+ obj[name] = function(){
+ var results = [], args = arguments, elements = true;
+ for (var i = 0, l = this.length; i < l; i++){
+ var element = this[i], result = results[i] = element[name].apply(element, args);
+ elements = (elements && typeOf(result) == 'element');
+ }
+ return (elements) ? new Elements(results) : results;
+ };
+
+ Elements.implement(obj);
+});
+
+if (!Browser.Element){
+ Element.parent = Object;
+
+ Element.Prototype = {
+ '$constructor': Element,
+ '$family': Function.from('element').hide()
+ };
+
+ Element.mirror(function(name, method){
+ Element.Prototype[name] = method;
+ });
+}
+
+Element.Constructors = {};
+
+
+
+var IFrame = new Type('IFrame', function(){
+ var params = Array.link(arguments, {
+ properties: Type.isObject,
+ iframe: function(obj){
+ return (obj != null);
+ }
+ });
+
+ var props = params.properties || {}, iframe;
+ if (params.iframe) iframe = document.id(params.iframe);
+ var onload = props.onload || function(){};
+ delete props.onload;
+ props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
+ iframe = new Element(iframe || 'iframe', props);
+
+ var onLoad = function(){
+ onload.call(iframe.contentWindow);
+ };
+
+ if (window.frames[props.id]) onLoad();
+ else iframe.addListener('load', onLoad);
+ return iframe;
+});
+
+var Elements = this.Elements = function(nodes){
+ if (nodes && nodes.length){
+ var uniques = {}, node;
+ for (var i = 0; node = nodes[i++];){
+ var uid = Slick.uidOf(node);
+ if (!uniques[uid]){
+ uniques[uid] = true;
+ this.push(node);
+ }
+ }
+ }
+};
+
+Elements.prototype = {length: 0};
+Elements.parent = Array;
+
+new Type('Elements', Elements).implement({
+
+ filter: function(filter, bind){
+ if (!filter) return this;
+ return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){
+ return item.match(filter);
+ } : filter, bind));
+ }.protect(),
+
+ push: function(){
+ var length = this.length;
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = document.id(arguments[i]);
+ if (item) this[length++] = item;
+ }
+ return (this.length = length);
+ }.protect(),
+
+ unshift: function(){
+ var items = [];
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = document.id(arguments[i]);
+ if (item) items.push(item);
+ }
+ return Array.prototype.unshift.apply(this, items);
+ }.protect(),
+
+ concat: function(){
+ var newElements = new Elements(this);
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = arguments[i];
+ if (Type.isEnumerable(item)) newElements.append(item);
+ else newElements.push(item);
+ }
+ return newElements;
+ }.protect(),
+
+ append: function(collection){
+ for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
+ return this;
+ }.protect(),
+
+ empty: function(){
+ while (this.length) delete this[--this.length];
+ return this;
+ }.protect()
+
+});
+
+
+
+(function(){
+
+// FF, IE
+var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
+
+splice.call(object, 1, 1);
+if (object[1] == 1) Elements.implement('splice', function(){
+ var length = this.length;
+ var result = splice.apply(this, arguments);
+ while (length >= this.length) delete this[length--];
+ return result;
+}.protect());
+
+Array.forEachMethod(function(method, name){
+ Elements.implement(name, method);
+});
+
+Array.mirror(Elements);
+
+/*<ltIE8>*/
+var createElementAcceptsHTML;
+try {
+ createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x');
+} catch (e){}
+
+var escapeQuotes = function(html){
+ return ('' + html).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
+};
+/*</ltIE8>*/
+
+Document.implement({
+
+ newElement: function(tag, props){
+ if (props && props.checked != null) props.defaultChecked = props.checked;
+ /*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
+ if (createElementAcceptsHTML && props){
+ tag = '<' + tag;
+ if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
+ if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
+ tag += '>';
+ delete props.name;
+ delete props.type;
+ }
+ /*</ltIE8>*/
+ return this.id(this.createElement(tag)).set(props);
+ }
+
+});
+
+})();
+
+(function(){
+
+Slick.uidOf(window);
+Slick.uidOf(document);
+
+Document.implement({
+
+ newTextNode: function(text){
+ return this.createTextNode(text);
+ },
+
+ getDocument: function(){
+ return this;
+ },
+
+ getWindow: function(){
+ return this.window;
+ },
+
+ id: (function(){
+
+ var types = {
+
+ string: function(id, nocash, doc){
+ id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
+ return (id) ? types.element(id, nocash) : null;
+ },
+
+ element: function(el, nocash){
+ Slick.uidOf(el);
+ if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){
+ var fireEvent = el.fireEvent;
+ // wrapping needed in IE7, or else crash
+ el._fireEvent = function(type, event){
+ return fireEvent(type, event);
+ };
+ Object.append(el, Element.Prototype);
+ }
+ return el;
+ },
+
+ object: function(obj, nocash, doc){
+ if (obj.toElement) return types.element(obj.toElement(doc), nocash);
+ return null;
+ }
+
+ };
+
+ types.textnode = types.whitespace = types.window = types.document = function(zero){
+ return zero;
+ };
+
+ return function(el, nocash, doc){
+ if (el && el.$family && el.uniqueNumber) return el;
+ var type = typeOf(el);
+ return (types[type]) ? types[type](el, nocash, doc || document) : null;
+ };
+
+ })()
+
+});
+
+if (window.$ == null) Window.implement('$', function(el, nc){
+ return document.id(el, nc, this.document);
+});
+
+Window.implement({
+
+ getDocument: function(){
+ return this.document;
+ },
+
+ getWindow: function(){
+ return this;
+ }
+
+});
+
+[Document, Element].invoke('implement', {
+
+ getElements: function(expression){
+ return Slick.search(this, expression, new Elements);
+ },
+
+ getElement: function(expression){
+ return document.id(Slick.find(this, expression));
+ }
+
+});
+
+var contains = {contains: function(element){
+ return Slick.contains(this, element);
+}};
+
+if (!document.contains) Document.implement(contains);
+if (!document.createElement('div').contains) Element.implement(contains);
+
+
+
+// tree walking
+
+var injectCombinator = function(expression, combinator){
+ if (!expression) return combinator;
+
+ expression = Object.clone(Slick.parse(expression));
+
+ var expressions = expression.expressions;
+ for (var i = expressions.length; i--;)
+ expressions[i][0].combinator = combinator;
+
+ return expression;
+};
+
+Object.forEach({
+ getNext: '~',
+ getPrevious: '!~',
+ getParent: '!'
+}, function(combinator, method){
+ Element.implement(method, function(expression){
+ return this.getElement(injectCombinator(expression, combinator));
+ });
+});
+
+Object.forEach({
+ getAllNext: '~',
+ getAllPrevious: '!~',
+ getSiblings: '~~',
+ getChildren: '>',
+ getParents: '!'
+}, function(combinator, method){
+ Element.implement(method, function(expression){
+ return this.getElements(injectCombinator(expression, combinator));
+ });
+});
+
+Element.implement({
+
+ getFirst: function(expression){
+ return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
+ },
+
+ getLast: function(expression){
+ return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
+ },
+
+ getWindow: function(){
+ return this.ownerDocument.window;
+ },
+
+ getDocument: function(){
+ return this.ownerDocument;
+ },
+
+ getElementById: function(id){
+ return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
+ },
+
+ match: function(expression){
+ return !expression || Slick.match(this, expression);
+ }
+
+});
+
+
+
+if (window.$$ == null) Window.implement('$$', function(selector){
+ if (arguments.length == 1){
+ if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
+ else if (Type.isEnumerable(selector)) return new Elements(selector);
+ }
+ return new Elements(arguments);
+});
+
+// Inserters
+
+var inserters = {
+
+ before: function(context, element){
+ var parent = element.parentNode;
+ if (parent) parent.insertBefore(context, element);
+ },
+
+ after: function(context, element){
+ var parent = element.parentNode;
+ if (parent) parent.insertBefore(context, element.nextSibling);
+ },
+
+ bottom: function(context, element){
+ element.appendChild(context);
+ },
+
+ top: function(context, element){
+ element.insertBefore(context, element.firstChild);
+ }
+
+};
+
+inserters.inside = inserters.bottom;
+
+
+
+// getProperty / setProperty
+
+var propertyGetters = {}, propertySetters = {};
+
+// properties
+
+var properties = {};
+Array.forEach([
+ 'type', 'value', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan',
+ 'frameBorder', 'rowSpan', 'tabIndex', 'useMap'
+], function(property){
+ properties[property.toLowerCase()] = property;
+});
+
+properties.html = 'innerHTML';
+properties.text = (document.createElement('div').textContent == null) ? 'innerText': 'textContent';
+
+Object.forEach(properties, function(real, key){
+ propertySetters[key] = function(node, value){
+ node[real] = value;
+ };
+ propertyGetters[key] = function(node){
+ return node[real];
+ };
+});
+
+// Booleans
+
+var bools = [
+ 'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked',
+ 'disabled', 'readOnly', 'multiple', 'selected', 'noresize',
+ 'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay',
+ 'loop'
+];
+
+var booleans = {};
+Array.forEach(bools, function(bool){
+ var lower = bool.toLowerCase();
+ booleans[lower] = bool;
+ propertySetters[lower] = function(node, value){
+ node[bool] = !!value;
+ };
+ propertyGetters[lower] = function(node){
+ return !!node[bool];
+ };
+});
+
+// Special cases
+
+Object.append(propertySetters, {
+
+ 'class': function(node, value){
+ ('className' in node) ? node.className = (value || '') : node.setAttribute('class', value);
+ },
+
+ 'for': function(node, value){
+ ('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value);
+ },
+
+ 'style': function(node, value){
+ (node.style) ? node.style.cssText = value : node.setAttribute('style', value);
+ },
+
+ 'value': function(node, value){
+ node.value = (value != null) ? value : '';
+ }
+
+});
+
+propertyGetters['class'] = function(node){
+ return ('className' in node) ? node.className || null : node.getAttribute('class');
+};
+
+/* <webkit> */
+var el = document.createElement('button');
+// IE sets type as readonly and throws
+try { el.type = 'button'; } catch(e){}
+if (el.type != 'button') propertySetters.type = function(node, value){
+ node.setAttribute('type', value);
+};
+el = null;
+/* </webkit> */
+
+/*<IE>*/
+var input = document.createElement('input');
+input.value = 't';
+input.type = 'submit';
+if (input.value != 't') propertySetters.type = function(node, type){
+ var value = node.value;
+ node.type = type;
+ node.value = value;
+};
+input = null;
+/*</IE>*/
+
+/* getProperty, setProperty */
+
+/* <ltIE9> */
+var pollutesGetAttribute = (function(div){
+ div.random = 'attribute';
+ return (div.getAttribute('random') == 'attribute');
+})(document.createElement('div'));
+
+var hasCloneBug = (function(test){
+ test.innerHTML = '<object><param name="should_fix" value="the unknown"></object>';
+ return test.cloneNode(true).firstChild.childNodes.length != 1;
+})(document.createElement('div'));
+/* </ltIE9> */
+
+var hasClassList = !!document.createElement('div').classList;
+
+var classes = function(className){
+ var classNames = (className || '').clean().split(" "), uniques = {};
+ return classNames.filter(function(className){
+ if (className !== "" && !uniques[className]) return uniques[className] = className;
+ });
+};
+
+var addToClassList = function(name){
+ this.classList.add(name);
+};
+
+var removeFromClassList = function(name){
+ this.classList.remove(name);
+};
+
+Element.implement({
+
+ setProperty: function(name, value){
+ var setter = propertySetters[name.toLowerCase()];
+ if (setter){
+ setter(this, value);
+ } else {
+ /* <ltIE9> */
+ var attributeWhiteList;
+ if (pollutesGetAttribute) attributeWhiteList = this.retrieve('$attributeWhiteList', {});
+ /* </ltIE9> */
+
+ if (value == null){
+ this.removeAttribute(name);
+ /* <ltIE9> */
+ if (pollutesGetAttribute) delete attributeWhiteList[name];
+ /* </ltIE9> */
+ } else {
+ this.setAttribute(name, '' + value);
+ /* <ltIE9> */
+ if (pollutesGetAttribute) attributeWhiteList[name] = true;
+ /* </ltIE9> */
+ }
+ }
+ return this;
+ },
+
+ setProperties: function(attributes){
+ for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
+ return this;
+ },
+
+ getProperty: function(name){
+ var getter = propertyGetters[name.toLowerCase()];
+ if (getter) return getter(this);
+ /* <ltIE9> */
+ if (pollutesGetAttribute){
+ var attr = this.getAttributeNode(name), attributeWhiteList = this.retrieve('$attributeWhiteList', {});
+ if (!attr) return null;
+ if (attr.expando && !attributeWhiteList[name]){
+ var outer = this.outerHTML;
+ // segment by the opening tag and find mention of attribute name
+ if (outer.substr(0, outer.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(name) < 0) return null;
+ attributeWhiteList[name] = true;
+ }
+ }
+ /* </ltIE9> */
+ var result = Slick.getAttribute(this, name);
+ return (!result && !Slick.hasAttribute(this, name)) ? null : result;
+ },
+
+ getProperties: function(){
+ var args = Array.from(arguments);
+ return args.map(this.getProperty, this).associate(args);
+ },
+
+ removeProperty: function(name){
+ return this.setProperty(name, null);
+ },
+
+ removeProperties: function(){
+ Array.each(arguments, this.removeProperty, this);
+ return this;
+ },
+
+ set: function(prop, value){
+ var property = Element.Properties[prop];
+ (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
+ }.overloadSetter(),
+
+ get: function(prop){
+ var property = Element.Properties[prop];
+ return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
+ }.overloadGetter(),
+
+ erase: function(prop){
+ var property = Element.Properties[prop];
+ (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
+ return this;
+ },
+
+ hasClass: hasClassList ? function(className){
+ return this.classList.contains(className);
+ } : function(className){
+ return this.className.clean().contains(className, ' ');
+ },
+
+ addClass: hasClassList ? function(className){
+ classes(className).forEach(addToClassList, this);
+ return this;
+ } : function(className){
+ this.className = classes(className + ' ' + this.className).join(' ');
+ return this;
+ },
+
+ removeClass: hasClassList ? function(className){
+ classes(className).forEach(removeFromClassList, this);
+ return this;
+ } : function(className){
+ var classNames = classes(this.className);
+ classes(className).forEach(classNames.erase, classNames);
+ this.className = classNames.join(' ');
+ return this;
+ },
+
+ toggleClass: function(className, force){
+ if (force == null) force = !this.hasClass(className);
+ return (force) ? this.addClass(className) : this.removeClass(className);
+ },
+
+ adopt: function(){
+ var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
+ if (length > 1) parent = fragment = document.createDocumentFragment();
+
+ for (var i = 0; i < length; i++){
+ var element = document.id(elements[i], true);
+ if (element) parent.appendChild(element);
+ }
+
+ if (fragment) this.appendChild(fragment);
+
+ return this;
+ },
+
+ appendText: function(text, where){
+ return this.grab(this.getDocument().newTextNode(text), where);
+ },
+
+ grab: function(el, where){
+ inserters[where || 'bottom'](document.id(el, true), this);
+ return this;
+ },
+
+ inject: function(el, where){
+ inserters[where || 'bottom'](this, document.id(el, true));
+ return this;
+ },
+
+ replaces: function(el){
+ el = document.id(el, true);
+ el.parentNode.replaceChild(this, el);
+ return this;
+ },
+
+ wraps: function(el, where){
+ el = document.id(el, true);
+ return this.replaces(el).grab(el, where);
+ },
+
+ getSelected: function(){
+ this.selectedIndex; // Safari 3.2.1
+ return new Elements(Array.from(this.options).filter(function(option){
+ return option.selected;
+ }));
+ },
+
+ toQueryString: function(){
+ var queryString = [];
+ this.getElements('input, select, textarea').each(function(el){
+ var type = el.type;
+ if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
+
+ var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
+ // IE
+ return document.id(opt).get('value');
+ }) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
+
+ Array.from(value).each(function(val){
+ if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
+ });
+ });
+ return queryString.join('&');
+ }
+
+});
+
+
+// appendHTML
+
+var appendInserters = {
+ before: 'beforeBegin',
+ after: 'afterEnd',
+ bottom: 'beforeEnd',
+ top: 'afterBegin',
+ inside: 'beforeEnd'
+};
+
+Element.implement('appendHTML', ('insertAdjacentHTML' in document.createElement('div')) ? function(html, where){
+ this.insertAdjacentHTML(appendInserters[where || 'bottom'], html);
+ return this;
+} : function(html, where){
+ var temp = new Element('div', {html: html}),
+ children = temp.childNodes,
+ fragment = temp.firstChild;
+
+ if (!fragment) return this;
+ if (children.length > 1){
+ fragment = document.createDocumentFragment();
+ for (var i = 0, l = children.length; i < l; i++){
+ fragment.appendChild(children[i]);
+ }
+ }
+
+ inserters[where || 'bottom'](fragment, this);
+ return this;
+});
+
+var collected = {}, storage = {};
+
+var get = function(uid){
+ return (storage[uid] || (storage[uid] = {}));
+};
+
+var clean = function(item){
+ var uid = item.uniqueNumber;
+ if (item.removeEvents) item.removeEvents();
+ if (item.clearAttributes) item.clearAttributes();
+ if (uid != null){
+ delete collected[uid];
+ delete storage[uid];
+ }
+ return item;
+};
+
+var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
+
+Element.implement({
+
+ destroy: function(){
+ var children = clean(this).getElementsByTagName('*');
+ Array.each(children, clean);
+ Element.dispose(this);
+ return null;
+ },
+
+ empty: function(){
+ Array.from(this.childNodes).each(Element.dispose);
+ return this;
+ },
+
+ dispose: function(){
+ return (this.parentNode) ? this.parentNode.removeChild(this) : this;
+ },
+
+ clone: function(contents, keepid){
+ contents = contents !== false;
+ var clone = this.cloneNode(contents), ce = [clone], te = [this], i;
+
+ if (contents){
+ ce.append(Array.from(clone.getElementsByTagName('*')));
+ te.append(Array.from(this.getElementsByTagName('*')));
+ }
+
+ for (i = ce.length; i--;){
+ var node = ce[i], element = te[i];
+ if (!keepid) node.removeAttribute('id');
+ /*<ltIE9>*/
+ if (node.clearAttributes){
+ node.clearAttributes();
+ node.mergeAttributes(element);
+ node.removeAttribute('uniqueNumber');
+ if (node.options){
+ var no = node.options, eo = element.options;
+ for (var j = no.length; j--;) no[j].selected = eo[j].selected;
+ }
+ }
+ /*</ltIE9>*/
+ var prop = formProps[element.tagName.toLowerCase()];
+ if (prop && element[prop]) node[prop] = element[prop];
+ }
+
+ /*<ltIE9>*/
+ if (hasCloneBug){
+ var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
+ for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
+ }
+ /*</ltIE9>*/
+ return document.id(clone);
+ }
+
+});
+
+[Element, Window, Document].invoke('implement', {
+
+ addListener: function(type, fn){
+ if (window.attachEvent && !window.addEventListener){
+ collected[Slick.uidOf(this)] = this;
+ }
+ if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
+ else this.attachEvent('on' + type, fn);
+ return this;
+ },
+
+ removeListener: function(type, fn){
+ if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
+ else this.detachEvent('on' + type, fn);
+ return this;
+ },
+
+ retrieve: function(property, dflt){
+ var storage = get(Slick.uidOf(this)), prop = storage[property];
+ if (dflt != null && prop == null) prop = storage[property] = dflt;
+ return prop != null ? prop : null;
+ },
+
+ store: function(property, value){
+ var storage = get(Slick.uidOf(this));
+ storage[property] = value;
+ return this;
+ },
+
+ eliminate: function(property){
+ var storage = get(Slick.uidOf(this));
+ delete storage[property];
+ return this;
+ }
+
+});
+
+/*<ltIE9>*/
+if (window.attachEvent && !window.addEventListener){
+ var gc = function(){
+ Object.each(collected, clean);
+ if (window.CollectGarbage) CollectGarbage();
+ window.removeListener('unload', gc);
+ }
+ window.addListener('unload', gc);
+}
+/*</ltIE9>*/
+
+Element.Properties = {};
+
+
+
+Element.Properties.style = {
+
+ set: function(style){
+ this.style.cssText = style;
+ },
+
+ get: function(){
+ return this.style.cssText;
+ },
+
+ erase: function(){
+ this.style.cssText = '';
+ }
+
+};
+
+Element.Properties.tag = {
+
+ get: function(){
+ return this.tagName.toLowerCase();
+ }
+
+};
+
+Element.Properties.html = {
+
+ set: function(html){
+ if (html == null) html = '';
+ else if (typeOf(html) == 'array') html = html.join('');
+ this.innerHTML = html;
+ },
+
+ erase: function(){
+ this.innerHTML = '';
+ }
+
+};
+
+var supportsHTML5Elements = true, supportsTableInnerHTML = true, supportsTRInnerHTML = true;
+
+/*<ltIE9>*/
+// technique by jdbarlett - http://jdbartlett.com/innershiv/
+var div = document.createElement('div');
+div.innerHTML = '<nav></nav>';
+supportsHTML5Elements = (div.childNodes.length == 1);
+if (!supportsHTML5Elements){
+ var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '),
+ fragment = document.createDocumentFragment(), l = tags.length;
+ while (l--) fragment.createElement(tags[l]);
+}
+div = null;
+/*</ltIE9>*/
+
+/*<IE>*/
+supportsTableInnerHTML = Function.attempt(function(){
+ var table = document.createElement('table');
+ table.innerHTML = '<tr><td></td></tr>';
+ return true;
+});
+
+/*<ltFF4>*/
+var tr = document.createElement('tr'), html = '<td></td>';
+tr.innerHTML = html;
+supportsTRInnerHTML = (tr.innerHTML == html);
+tr = null;
+/*</ltFF4>*/
+
+if (!supportsTableInnerHTML || !supportsTRInnerHTML || !supportsHTML5Elements){
+
+ Element.Properties.html.set = (function(set){
+
+ var translations = {
+ table: [1, '<table>', '</table>'],
+ select: [1, '<select>', '</select>'],
+ tbody: [2, '<table><tbody>', '</tbody></table>'],
+ tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
+ };
+
+ translations.thead = translations.tfoot = translations.tbody;
+
+ return function(html){
+ var wrap = translations[this.get('tag')];
+ if (!wrap && !supportsHTML5Elements) wrap = [0, '', ''];
+ if (!wrap) return set.call(this, html);
+
+ var level = wrap[0], wrapper = document.createElement('div'), target = wrapper;
+ if (!supportsHTML5Elements) fragment.appendChild(wrapper);
+ wrapper.innerHTML = [wrap[1], html, wrap[2]].flatten().join('');
+ while (level--) target = target.firstChild;
+ this.empty().adopt(target.childNodes);
+ if (!supportsHTML5Elements) fragment.removeChild(wrapper);
+ wrapper = null;
+ };
+
+ })(Element.Properties.html.set);
+}
+/*</IE>*/
+
+/*<ltIE9>*/
+var testForm = document.createElement('form');
+testForm.innerHTML = '<select><option>s</option></select>';
+
+if (testForm.firstChild.value != 's') Element.Properties.value = {
+
+ set: function(value){
+ var tag = this.get('tag');
+ if (tag != 'select') return this.setProperty('value', value);
+ var options = this.getElements('option');
+ value = String(value);
+ for (var i = 0; i < options.length; i++){
+ var option = options[i],
+ attr = option.getAttributeNode('value'),
+ optionValue = (attr && attr.specified) ? option.value : option.get('text');
+ if (optionValue === value) return option.selected = true;
+ }
+ },
+
+ get: function(){
+ var option = this, tag = option.get('tag');
+
+ if (tag != 'select' && tag != 'option') return this.getProperty('value');
+
+ if (tag == 'select' && !(option = option.getSelected()[0])) return '';
+
+ var attr = option.getAttributeNode('value');
+ return (attr && attr.specified) ? option.value : option.get('text');
+ }
+
+};
+testForm = null;
+/*</ltIE9>*/
+
+/*<IE>*/
+if (document.createElement('div').getAttributeNode('id')) Element.Properties.id = {
+ set: function(id){
+ this.id = this.getAttributeNode('id').value = id;
+ },
+ get: function(){
+ return this.id || null;
+ },
+ erase: function(){
+ this.id = this.getAttributeNode('id').value = '';
+ }
+};
+/*</IE>*/
+
+})();
+
+
+/*
+---
+
+name: Element.Style
+
+description: Contains methods for interacting with the styles of Elements in a fashionable way.
+
+license: MIT-style license.
+
+requires: Element
+
+provides: Element.Style
+
+...
+*/
+
+(function(){
+
+var html = document.html, el;
+
+//<ltIE9>
+// Check for oldIE, which does not remove styles when they're set to null
+el = document.createElement('div');
+el.style.color = 'red';
+el.style.color = null;
+var doesNotRemoveStyles = el.style.color == 'red';
+
+// check for oldIE, which returns border* shorthand styles in the wrong order (color-width-style instead of width-style-color)
+var border = '1px solid #123abc';
+el.style.border = border;
+var returnsBordersInWrongOrder = el.style.border != border;
+el = null;
+//</ltIE9>
+
+var hasGetComputedStyle = !!window.getComputedStyle;
+
+Element.Properties.styles = {set: function(styles){
+ this.setStyles(styles);
+}};
+
+var hasOpacity = (html.style.opacity != null),
+ hasFilter = (html.style.filter != null),
+ reAlpha = /alpha\(opacity=([\d.]+)\)/i;
+
+var setVisibility = function(element, opacity){
+ element.store('$opacity', opacity);
+ element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden';
+};
+
+//<ltIE9>
+var setFilter = function(element, regexp, value){
+ var style = element.style,
+ filter = style.filter || element.getComputedStyle('filter') || '';
+ style.filter = (regexp.test(filter) ? filter.replace(regexp, value) : filter + ' ' + value).trim();
+ if (!style.filter) style.removeAttribute('filter');
+};
+//</ltIE9>
+
+var setOpacity = (hasOpacity ? function(element, opacity){
+ element.style.opacity = opacity;
+} : (hasFilter ? function(element, opacity){
+ if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
+ if (opacity == null || opacity == 1){
+ setFilter(element, reAlpha, '');
+ if (opacity == 1 && getOpacity(element) != 1) setFilter(element, reAlpha, 'alpha(opacity=100)');
+ } else {
+ setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')');
+ }
+} : setVisibility));
+
+var getOpacity = (hasOpacity ? function(element){
+ var opacity = element.style.opacity || element.getComputedStyle('opacity');
+ return (opacity == '') ? 1 : opacity.toFloat();
+} : (hasFilter ? function(element){
+ var filter = (element.style.filter || element.getComputedStyle('filter')),
+ opacity;
+ if (filter) opacity = filter.match(reAlpha);
+ return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
+} : function(element){
+ var opacity = element.retrieve('$opacity');
+ if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1);
+ return opacity;
+}));
+
+var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat',
+ namedPositions = {left: '0%', top: '0%', center: '50%', right: '100%', bottom: '100%'},
+ hasBackgroundPositionXY = (html.style.backgroundPositionX != null);
+
+//<ltIE9>
+var removeStyle = function(style, property){
+ if (property == 'backgroundPosition'){
+ style.removeAttribute(property + 'X');
+ property += 'Y';
+ }
+ style.removeAttribute(property);
+};
+//</ltIE9>
+
+Element.implement({
+
+ getComputedStyle: function(property){
+ if (!hasGetComputedStyle && this.currentStyle) return this.currentStyle[property.camelCase()];
+ var defaultView = Element.getDocument(this).defaultView,
+ computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
+ return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : '';
+ },
+
+ setStyle: function(property, value){
+ if (property == 'opacity'){
+ if (value != null) value = parseFloat(value);
+ setOpacity(this, value);
+ return this;
+ }
+ property = (property == 'float' ? floatName : property).camelCase();
+ if (typeOf(value) != 'string'){
+ var map = (Element.Styles[property] || '@').split(' ');
+ value = Array.from(value).map(function(val, i){
+ if (!map[i]) return '';
+ return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
+ }).join(' ');
+ } else if (value == String(Number(value))){
+ value = Math.round(value);
+ }
+ this.style[property] = value;
+ //<ltIE9>
+ if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
+ removeStyle(this.style, property);
+ }
+ //</ltIE9>
+ return this;
+ },
+
+ getStyle: function(property){
+ if (property == 'opacity') return getOpacity(this);
+ property = (property == 'float' ? floatName : property).camelCase();
+ var result = this.style[property];
+ if (!result || property == 'zIndex'){
+ if (Element.ShortStyles.hasOwnProperty(property)){
+ result = [];
+ for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s));
+ return result.join(' ');
+ }
+ result = this.getComputedStyle(property);
+ }
+ if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){
+ return result.replace(/(top|right|bottom|left)/g, function(position){
+ return namedPositions[position];
+ }) || '0px';
+ }
+ if (!result && property == 'backgroundPosition') return '0px 0px';
+ if (result){
+ result = String(result);
+ var color = result.match(/rgba?\([\d\s,]+\)/);
+ if (color) result = result.replace(color[0], color[0].rgbToHex());
+ }
+ if (!hasGetComputedStyle && !this.style[property]){
+ if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){
+ var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
+ values.each(function(value){
+ size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
+ }, this);
+ return this['offset' + property.capitalize()] - size + 'px';
+ }
+ if ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
+ return '0px';
+ }
+ }
+ //<ltIE9>
+ if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){
+ return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1');
+ }
+ //</ltIE9>
+ return result;
+ },
+
+ setStyles: function(styles){
+ for (var style in styles) this.setStyle(style, styles[style]);
+ return this;
+ },
+
+ getStyles: function(){
+ var result = {};
+ Array.flatten(arguments).each(function(key){
+ result[key] = this.getStyle(key);
+ }, this);
+ return result;
+ }
+
+});
+
+Element.Styles = {
+ left: '@px', top: '@px', bottom: '@px', right: '@px',
+ width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
+ backgroundColor: 'rgb(@, @, @)', backgroundSize: '@px', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
+ fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
+ margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
+ borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
+ zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
+};
+
+
+
+
+
+Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
+
+['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
+ var Short = Element.ShortStyles;
+ var All = Element.Styles;
+ ['margin', 'padding'].each(function(style){
+ var sd = style + direction;
+ Short[style][sd] = All[sd] = '@px';
+ });
+ var bd = 'border' + direction;
+ Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
+ var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
+ Short[bd] = {};
+ Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
+ Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
+ Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
+});
+
+if (hasBackgroundPositionXY) Element.ShortStyles.backgroundPosition = {backgroundPositionX: '@', backgroundPositionY: '@'};
+})();
+
+
+/*
+---
+
+name: Element.Event
+
+description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events, if necessary.
+
+license: MIT-style license.
+
+requires: [Element, Event]
+
+provides: Element.Event
+
+...
+*/
+
+(function(){
+
+Element.Properties.events = {set: function(events){
+ this.addEvents(events);
+}};
+
+[Element, Window, Document].invoke('implement', {
+
+ addEvent: function(type, fn){
+ var events = this.retrieve('events', {});
+ if (!events[type]) events[type] = {keys: [], values: []};
+ if (events[type].keys.contains(fn)) return this;
+ events[type].keys.push(fn);
+ var realType = type,
+ custom = Element.Events[type],
+ condition = fn,
+ self = this;
+ if (custom){
+ if (custom.onAdd) custom.onAdd.call(this, fn, type);
+ if (custom.condition){
+ condition = function(event){
+ if (custom.condition.call(this, event, type)) return fn.call(this, event);
+ return true;
+ };
+ }
+ if (custom.base) realType = Function.from(custom.base).call(this, type);
+ }
+ var defn = function(){
+ return fn.call(self);
+ };
+ var nativeEvent = Element.NativeEvents[realType];
+ if (nativeEvent){
+ if (nativeEvent == 2){
+ defn = function(event){
+ event = new DOMEvent(event, self.getWindow());
+ if (condition.call(self, event) === false) event.stop();
+ };
+ }
+ this.addListener(realType, defn, arguments[2]);
+ }
+ events[type].values.push(defn);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ var list = events[type];
+ var index = list.keys.indexOf(fn);
+ if (index == -1) return this;
+ var value = list.values[index];
+ delete list.keys[index];
+ delete list.values[index];
+ var custom = Element.Events[type];
+ if (custom){
+ if (custom.onRemove) custom.onRemove.call(this, fn, type);
+ if (custom.base) type = Function.from(custom.base).call(this, type);
+ }
+ return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
+ },
+
+ addEvents: function(events){
+ for (var event in events) this.addEvent(event, events[event]);
+ return this;
+ },
+
+ removeEvents: function(events){
+ var type;
+ if (typeOf(events) == 'object'){
+ for (type in events) this.removeEvent(type, events[type]);
+ return this;
+ }
+ var attached = this.retrieve('events');
+ if (!attached) return this;
+ if (!events){
+ for (type in attached) this.removeEvents(type);
+ this.eliminate('events');
+ } else if (attached[events]){
+ attached[events].keys.each(function(fn){
+ this.removeEvent(events, fn);
+ }, this);
+ delete attached[events];
+ }
+ return this;
+ },
+
+ fireEvent: function(type, args, delay){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ args = Array.from(args);
+
+ events[type].keys.each(function(fn){
+ if (delay) fn.delay(delay, this, args);
+ else fn.apply(this, args);
+ }, this);
+ return this;
+ },
+
+ cloneEvents: function(from, type){
+ from = document.id(from);
+ var events = from.retrieve('events');
+ if (!events) return this;
+ if (!type){
+ for (var eventType in events) this.cloneEvents(from, eventType);
+ } else if (events[type]){
+ events[type].keys.each(function(fn){
+ this.addEvent(type, fn);
+ }, this);
+ }
+ return this;
+ }
+
+});
+
+Element.NativeEvents = {
+ click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
+ mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
+ mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
+ keydown: 2, keypress: 2, keyup: 2, //keyboard
+ orientationchange: 2, // mobile
+ touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
+ gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
+ focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements
+ load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
+ hashchange: 1, popstate: 2, // history
+ error: 1, abort: 1, scroll: 1 //misc
+};
+
+Element.Events = {
+ mousewheel: {
+ base: 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll'
+ }
+};
+
+var check = function(event){
+ var related = event.relatedTarget;
+ if (related == null) return true;
+ if (!related) return false;
+ return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
+};
+
+if ('onmouseenter' in document.documentElement){
+ Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2;
+ Element.MouseenterCheck = check;
+} else {
+ Element.Events.mouseenter = {
+ base: 'mouseover',
+ condition: check
+ };
+
+ Element.Events.mouseleave = {
+ base: 'mouseout',
+ condition: check
+ };
+}
+
+/*<ltIE9>*/
+if (!window.addEventListener){
+ Element.NativeEvents.propertychange = 2;
+ Element.Events.change = {
+ base: function(){
+ var type = this.type;
+ return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change';
+ },
+ condition: function(event){
+ return event.type != 'propertychange' || event.event.propertyName == 'checked';
+ }
+ };
+}
+/*</ltIE9>*/
+
+
+
+})();
+
+
+/*
+---
+
+name: Element.Delegation
+
+description: Extends the Element native object to include the delegate method for more efficient event management.
+
+license: MIT-style license.
+
+requires: [Element.Event]
+
+provides: [Element.Delegation]
+
+...
+*/
+
+(function(){
+
+var eventListenerSupport = !!window.addEventListener;
+
+Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2;
+
+var bubbleUp = function(self, match, fn, event, target){
+ while (target && target != self){
+ if (match(target, event)) return fn.call(target, event, target);
+ target = document.id(target.parentNode);
+ }
+};
+
+var map = {
+ mouseenter: {
+ base: 'mouseover',
+ condition: Element.MouseenterCheck
+ },
+ mouseleave: {
+ base: 'mouseout',
+ condition: Element.MouseenterCheck
+ },
+ focus: {
+ base: 'focus' + (eventListenerSupport ? '' : 'in'),
+ capture: true
+ },
+ blur: {
+ base: eventListenerSupport ? 'blur' : 'focusout',
+ capture: true
+ }
+};
+
+/*<ltIE9>*/
+var _key = '$delegation:';
+var formObserver = function(type){
+
+ return {
+
+ base: 'focusin',
+
+ remove: function(self, uid){
+ var list = self.retrieve(_key + type + 'listeners', {})[uid];
+ if (list && list.forms) for (var i = list.forms.length; i--;){
+ list.forms[i].removeEvent(type, list.fns[i]);
+ }
+ },
+
+ listen: function(self, match, fn, event, target, uid){
+ var form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
+ if (!form) return;
+
+ var listeners = self.retrieve(_key + type + 'listeners', {}),
+ listener = listeners[uid] || {forms: [], fns: []},
+ forms = listener.forms, fns = listener.fns;
+
+ if (forms.indexOf(form) != -1) return;
+ forms.push(form);
+
+ var _fn = function(event){
+ bubbleUp(self, match, fn, event, target);
+ };
+ form.addEvent(type, _fn);
+ fns.push(_fn);
+
+ listeners[uid] = listener;
+ self.store(_key + type + 'listeners', listeners);
+ }
+ };
+};
+
+var inputObserver = function(type){
+ return {
+ base: 'focusin',
+ listen: function(self, match, fn, event, target){
+ var events = {blur: function(){
+ this.removeEvents(events);
+ }};
+ events[type] = function(event){
+ bubbleUp(self, match, fn, event, target);
+ };
+ event.target.addEvents(events);
+ }
+ };
+};
+
+if (!eventListenerSupport) Object.append(map, {
+ submit: formObserver('submit'),
+ reset: formObserver('reset'),
+ change: inputObserver('change'),
+ select: inputObserver('select')
+});
+/*</ltIE9>*/
+
+var proto = Element.prototype,
+ addEvent = proto.addEvent,
+ removeEvent = proto.removeEvent;
+
+var relay = function(old, method){
+ return function(type, fn, useCapture){
+ if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture);
+ var parsed = Slick.parse(type).expressions[0][0];
+ if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture);
+ var newType = parsed.tag;
+ parsed.pseudos.slice(1).each(function(pseudo){
+ newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : '');
+ });
+ old.call(this, type, fn);
+ return method.call(this, newType, parsed.pseudos[0].value, fn);
+ };
+};
+
+var delegation = {
+
+ addEvent: function(type, match, fn){
+ var storage = this.retrieve('$delegates', {}), stored = storage[type];
+ if (stored) for (var _uid in stored){
+ if (stored[_uid].fn == fn && stored[_uid].match == match) return this;
+ }
+
+ var _type = type, _match = match, _fn = fn, _map = map[type] || {};
+ type = _map.base || _type;
+
+ match = function(target){
+ return Slick.match(target, _match);
+ };
+
+ var elementEvent = Element.Events[_type];
+ if (_map.condition || elementEvent && elementEvent.condition){
+ var __match = match, condition = _map.condition || elementEvent.condition;
+ match = function(target, event){
+ return __match(target, event) && condition.call(target, event, type);
+ };
+ }
+
+ var self = this, uid = String.uniqueID();
+ var delegator = _map.listen ? function(event, target){
+ if (!target && event && event.target) target = event.target;
+ if (target) _map.listen(self, match, fn, event, target, uid);
+ } : function(event, target){
+ if (!target && event && event.target) target = event.target;
+ if (target) bubbleUp(self, match, fn, event, target);
+ };
+
+ if (!stored) stored = {};
+ stored[uid] = {
+ match: _match,
+ fn: _fn,
+ delegator: delegator
+ };
+ storage[_type] = stored;
+ return addEvent.call(this, type, delegator, _map.capture);
+ },
+
+ removeEvent: function(type, match, fn, _uid){
+ var storage = this.retrieve('$delegates', {}), stored = storage[type];
+ if (!stored) return this;
+
+ if (_uid){
+ var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {};
+ type = _map.base || _type;
+ if (_map.remove) _map.remove(this, _uid);
+ delete stored[_uid];
+ storage[_type] = stored;
+ return removeEvent.call(this, type, delegator, _map.capture);
+ }
+
+ var __uid, s;
+ if (fn) for (__uid in stored){
+ s = stored[__uid];
+ if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid);
+ } else for (__uid in stored){
+ s = stored[__uid];
+ if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid);
+ }
+ return this;
+ }
+
+};
+
+[Element, Window, Document].invoke('implement', {
+ addEvent: relay(addEvent, delegation.addEvent),
+ removeEvent: relay(removeEvent, delegation.removeEvent)
+});
+
+})();
+
+
+/*
+---
+
+name: Element.Dimensions
+
+description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
+
+license: MIT-style license.
+
+credits:
+ - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
+ - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
+
+requires: [Element, Element.Style]
+
+provides: [Element.Dimensions]
+
+...
+*/
+
+(function(){
+
+var element = document.createElement('div'),
+ child = document.createElement('div');
+element.style.height = '0';
+element.appendChild(child);
+var brokenOffsetParent = (child.offsetParent === element);
+element = child = null;
+
+var isOffset = function(el){
+ return styleString(el, 'position') != 'static' || isBody(el);
+};
+
+var isOffsetStatic = function(el){
+ return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
+};
+
+Element.implement({
+
+ scrollTo: function(x, y){
+ if (isBody(this)){
+ this.getWindow().scrollTo(x, y);
+ } else {
+ this.scrollLeft = x;
+ this.scrollTop = y;
+ }
+ return this;
+ },
+
+ getSize: function(){
+ if (isBody(this)) return this.getWindow().getSize();
+ return {x: this.offsetWidth, y: this.offsetHeight};
+ },
+
+ getScrollSize: function(){
+ if (isBody(this)) return this.getWindow().getScrollSize();
+ return {x: this.scrollWidth, y: this.scrollHeight};
+ },
+
+ getScroll: function(){
+ if (isBody(this)) return this.getWindow().getScroll();
+ return {x: this.scrollLeft, y: this.scrollTop};
+ },
+
+ getScrolls: function(){
+ var element = this.parentNode, position = {x: 0, y: 0};
+ while (element && !isBody(element)){
+ position.x += element.scrollLeft;
+ position.y += element.scrollTop;
+ element = element.parentNode;
+ }
+ return position;
+ },
+
+ getOffsetParent: brokenOffsetParent ? function(){
+ var element = this;
+ if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+ var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
+ while ((element = element.parentNode)){
+ if (isOffsetCheck(element)) return element;
+ }
+ return null;
+ } : function(){
+ var element = this;
+ if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+ try {
+ return element.offsetParent;
+ } catch(e) {}
+ return null;
+ },
+
+ getOffsets: function(){
+ var hasGetBoundingClientRect = this.getBoundingClientRect;
+
+ if (hasGetBoundingClientRect){
+ var bound = this.getBoundingClientRect(),
+ html = document.id(this.getDocument().documentElement),
+ htmlScroll = html.getScroll(),
+ elemScrolls = this.getScrolls(),
+ isFixed = (styleString(this, 'position') == 'fixed');
+
+ return {
+ x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
+ y: bound.top.toInt() + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
+ };
+ }
+
+ var element = this, position = {x: 0, y: 0};
+ if (isBody(this)) return position;
+
+ while (element && !isBody(element)){
+ position.x += element.offsetLeft;
+ position.y += element.offsetTop;
+
+ element = element.offsetParent;
+ }
+
+ return position;
+ },
+
+ getPosition: function(relative){
+ var offset = this.getOffsets(),
+ scroll = this.getScrolls();
+ var position = {
+ x: offset.x - scroll.x,
+ y: offset.y - scroll.y
+ };
+
+ if (relative && (relative = document.id(relative))){
+ var relativePosition = relative.getPosition();
+ return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
+ }
+ return position;
+ },
+
+ getCoordinates: function(element){
+ if (isBody(this)) return this.getWindow().getCoordinates();
+ var position = this.getPosition(element),
+ size = this.getSize();
+ var obj = {
+ left: position.x,
+ top: position.y,
+ width: size.x,
+ height: size.y
+ };
+ obj.right = obj.left + obj.width;
+ obj.bottom = obj.top + obj.height;
+ return obj;
+ },
+
+ computePosition: function(obj){
+ return {
+ left: obj.x - styleNumber(this, 'margin-left'),
+ top: obj.y - styleNumber(this, 'margin-top')
+ };
+ },
+
+ setPosition: function(obj){
+ return this.setStyles(this.computePosition(obj));
+ }
+
+});
+
+
+[Document, Window].invoke('implement', {
+
+ getSize: function(){
+ var doc = getCompatElement(this);
+ return {x: doc.clientWidth, y: doc.clientHeight};
+ },
+
+ getScroll: function(){
+ var win = this.getWindow(), doc = getCompatElement(this);
+ return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
+ },
+
+ getScrollSize: function(){
+ var doc = getCompatElement(this),
+ min = this.getSize(),
+ body = this.getDocument().body;
+
+ return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
+ },
+
+ getPosition: function(){
+ return {x: 0, y: 0};
+ },
+
+ getCoordinates: function(){
+ var size = this.getSize();
+ return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
+ }
+
+});
+
+// private methods
+
+var styleString = Element.getComputedStyle;
+
+function styleNumber(element, style){
+ return styleString(element, style).toInt() || 0;
+}
+
+function borderBox(element){
+ return styleString(element, '-moz-box-sizing') == 'border-box';
+}
+
+function topBorder(element){
+ return styleNumber(element, 'border-top-width');
+}
+
+function leftBorder(element){
+ return styleNumber(element, 'border-left-width');
+}
+
+function isBody(element){
+ return (/^(?:body|html)$/i).test(element.tagName);
+}
+
+function getCompatElement(element){
+ var doc = element.getDocument();
+ return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
+}
+
+})();
+
+//aliases
+Element.alias({position: 'setPosition'}); //compatability
+
+[Window, Document, Element].invoke('implement', {
+
+ getHeight: function(){
+ return this.getSize().y;
+ },
+
+ getWidth: function(){
+ return this.getSize().x;
+ },
+
+ getScrollTop: function(){
+ return this.getScroll().y;
+ },
+
+ getScrollLeft: function(){
+ return this.getScroll().x;
+ },
+
+ getScrollHeight: function(){
+ return this.getScrollSize().y;
+ },
+
+ getScrollWidth: function(){
+ return this.getScrollSize().x;
+ },
+
+ getTop: function(){
+ return this.getPosition().y;
+ },
+
+ getLeft: function(){
+ return this.getPosition().x;
+ }
+
+});
+
+
+/*
+---
+
+name: Fx
+
+description: Contains the basic animation logic to be extended by all other Fx Classes.
+
+license: MIT-style license.
+
+requires: [Chain, Events, Options]
+
+provides: Fx
+
+...
+*/
+
+(function(){
+
+var Fx = this.Fx = new Class({
+
+ Implements: [Chain, Events, Options],
+
+ options: {
+ /*
+ onStart: nil,
+ onCancel: nil,
+ onComplete: nil,
+ */
+ fps: 60,
+ unit: false,
+ duration: 500,
+ frames: null,
+ frameSkip: true,
+ link: 'ignore'
+ },
+
+ initialize: function(options){
+ this.subject = this.subject || this;
+ this.setOptions(options);
+ },
+
+ getTransition: function(){
+ return function(p){
+ return -(Math.cos(Math.PI * p) - 1) / 2;
+ };
+ },
+
+ step: function(now){
+ if (this.options.frameSkip){
+ var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
+ this.time = now;
+ this.frame += frames;
+ } else {
+ this.frame++;
+ }
+
+ if (this.frame < this.frames){
+ var delta = this.transition(this.frame / this.frames);
+ this.set(this.compute(this.from, this.to, delta));
+ } else {
+ this.frame = this.frames;
+ this.set(this.compute(this.from, this.to, 1));
+ this.stop();
+ }
+ },
+
+ set: function(now){
+ return now;
+ },
+
+ compute: function(from, to, delta){
+ return Fx.compute(from, to, delta);
+ },
+
+ check: function(){
+ if (!this.isRunning()) return true;
+ switch (this.options.link){
+ case 'cancel': this.cancel(); return true;
+ case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
+ }
+ return false;
+ },
+
+ start: function(from, to){
+ if (!this.check(from, to)) return this;
+ this.from = from;
+ this.to = to;
+ this.frame = (this.options.frameSkip) ? 0 : -1;
+ this.time = null;
+ this.transition = this.getTransition();
+ var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration;
+ this.duration = Fx.Durations[duration] || duration.toInt();
+ this.frameInterval = 1000 / fps;
+ this.frames = frames || Math.round(this.duration / this.frameInterval);
+ this.fireEvent('start', this.subject);
+ pushInstance.call(this, fps);
+ return this;
+ },
+
+ stop: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ if (this.frames == this.frame){
+ this.fireEvent('complete', this.subject);
+ if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
+ } else {
+ this.fireEvent('stop', this.subject);
+ }
+ }
+ return this;
+ },
+
+ cancel: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ this.frame = this.frames;
+ this.fireEvent('cancel', this.subject).clearChain();
+ }
+ return this;
+ },
+
+ pause: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ }
+ return this;
+ },
+
+ resume: function(){
+ if (this.isPaused()) pushInstance.call(this, this.options.fps);
+ return this;
+ },
+
+ isRunning: function(){
+ var list = instances[this.options.fps];
+ return list && list.contains(this);
+ },
+
+ isPaused: function(){
+ return (this.frame < this.frames) && !this.isRunning();
+ }
+
+});
+
+Fx.compute = function(from, to, delta){
+ return (to - from) * delta + from;
+};
+
+Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
+
+// global timers
+
+var instances = {}, timers = {};
+
+var loop = function(){
+ var now = Date.now();
+ for (var i = this.length; i--;){
+ var instance = this[i];
+ if (instance) instance.step(now);
+ }
+};
+
+var pushInstance = function(fps){
+ var list = instances[fps] || (instances[fps] = []);
+ list.push(this);
+ if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
+};
+
+var pullInstance = function(fps){
+ var list = instances[fps];
+ if (list){
+ list.erase(this);
+ if (!list.length && timers[fps]){
+ delete instances[fps];
+ timers[fps] = clearInterval(timers[fps]);
+ }
+ }
+};
+
+})();
+
+
+/*
+---
+
+name: Fx.CSS
+
+description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
+
+license: MIT-style license.
+
+requires: [Fx, Element.Style]
+
+provides: Fx.CSS
+
+...
+*/
+
+Fx.CSS = new Class({
+
+ Extends: Fx,
+
+ //prepares the base from/to object
+
+ prepare: function(element, property, values){
+ values = Array.from(values);
+ var from = values[0], to = values[1];
+ if (to == null){
+ to = from;
+ from = element.getStyle(property);
+ var unit = this.options.unit;
+ // adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299
+ if (unit && from && typeof from == 'string' && from.slice(-unit.length) != unit && parseFloat(from) != 0){
+ element.setStyle(property, to + unit);
+ var value = element.getComputedStyle(property);
+ // IE and Opera support pixelLeft or pixelWidth
+ if (!(/px$/.test(value))){
+ value = element.style[('pixel-' + property).camelCase()];
+ if (value == null){
+ // adapted from Dean Edwards' http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+ var left = element.style.left;
+ element.style.left = to + unit;
+ value = element.style.pixelLeft;
+ element.style.left = left;
+ }
+ }
+ from = (to || 1) / (parseFloat(value) || 1) * (parseFloat(from) || 0);
+ element.setStyle(property, from + unit);
+ }
+ }
+ return {from: this.parse(from), to: this.parse(to)};
+ },
+
+ //parses a value into an array
+
+ parse: function(value){
+ value = Function.from(value)();
+ value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
+ return value.map(function(val){
+ val = String(val);
+ var found = false;
+ Object.each(Fx.CSS.Parsers, function(parser, key){
+ if (found) return;
+ var parsed = parser.parse(val);
+ if (parsed || parsed === 0) found = {value: parsed, parser: parser};
+ });
+ found = found || {value: val, parser: Fx.CSS.Parsers.String};
+ return found;
+ });
+ },
+
+ //computes by a from and to prepared objects, using their parsers.
+
+ compute: function(from, to, delta){
+ var computed = [];
+ (Math.min(from.length, to.length)).times(function(i){
+ computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
+ });
+ computed.$family = Function.from('fx:css:value');
+ return computed;
+ },
+
+ //serves the value as settable
+
+ serve: function(value, unit){
+ if (typeOf(value) != 'fx:css:value') value = this.parse(value);
+ var returned = [];
+ value.each(function(bit){
+ returned = returned.concat(bit.parser.serve(bit.value, unit));
+ });
+ return returned;
+ },
+
+ //renders the change to an element
+
+ render: function(element, property, value, unit){
+ element.setStyle(property, this.serve(value, unit));
+ },
+
+ //searches inside the page css to find the values for a selector
+
+ search: function(selector){
+ if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
+ var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
+
+ var searchStyles = function(rules){
+ Array.each(rules, function(rule, i){
+ if (rule.media){
+ searchStyles(rule.rules || rule.cssRules);
+ return;
+ }
+ if (!rule.style) return;
+ var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
+ return m.toLowerCase();
+ }) : null;
+ if (!selectorText || !selectorTest.test(selectorText)) return;
+ Object.each(Element.Styles, function(value, style){
+ if (!rule.style[style] || Element.ShortStyles[style]) return;
+ value = String(rule.style[style]);
+ to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
+ });
+ });
+ };
+
+ Array.each(document.styleSheets, function(sheet, j){
+ var href = sheet.href;
+ if (href && href.indexOf('://') > -1 && href.indexOf(document.domain) == -1) return;
+ var rules = sheet.rules || sheet.cssRules;
+ searchStyles(rules);
+ });
+ return Fx.CSS.Cache[selector] = to;
+ }
+
+});
+
+Fx.CSS.Cache = {};
+
+Fx.CSS.Parsers = {
+
+ Color: {
+ parse: function(value){
+ if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
+ return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
+ },
+ compute: function(from, to, delta){
+ return from.map(function(value, i){
+ return Math.round(Fx.compute(from[i], to[i], delta));
+ });
+ },
+ serve: function(value){
+ return value.map(Number);
+ }
+ },
+
+ Number: {
+ parse: parseFloat,
+ compute: Fx.compute,
+ serve: function(value, unit){
+ return (unit) ? value + unit : value;
+ }
+ },
+
+ String: {
+ parse: Function.from(false),
+ compute: function(zero, one){
+ return one;
+ },
+ serve: function(zero){
+ return zero;
+ }
+ }
+
+};
+
+
+
+
+/*
+---
+
+name: Fx.Tween
+
+description: Formerly Fx.Style, effect to transition any CSS property for an element.
+
+license: MIT-style license.
+
+requires: Fx.CSS
+
+provides: [Fx.Tween, Element.fade, Element.highlight]
+
+...
+*/
+
+Fx.Tween = new Class({
+
+ Extends: Fx.CSS,
+
+ initialize: function(element, options){
+ this.element = this.subject = document.id(element);
+ this.parent(options);
+ },
+
+ set: function(property, now){
+ if (arguments.length == 1){
+ now = property;
+ property = this.property || this.options.property;
+ }
+ this.render(this.element, property, now, this.options.unit);
+ return this;
+ },
+
+ start: function(property, from, to){
+ if (!this.check(property, from, to)) return this;
+ var args = Array.flatten(arguments);
+ this.property = this.options.property || args.shift();
+ var parsed = this.prepare(this.element, this.property, args);
+ return this.parent(parsed.from, parsed.to);
+ }
+
+});
+
+Element.Properties.tween = {
+
+ set: function(options){
+ this.get('tween').cancel().setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var tween = this.retrieve('tween');
+ if (!tween){
+ tween = new Fx.Tween(this, {link: 'cancel'});
+ this.store('tween', tween);
+ }
+ return tween;
+ }
+
+};
+
+Element.implement({
+
+ tween: function(property, from, to){
+ this.get('tween').start(property, from, to);
+ return this;
+ },
+
+ fade: function(how){
+ var fade = this.get('tween'), method, args = ['opacity'].append(arguments), toggle;
+ if (args[1] == null) args[1] = 'toggle';
+ switch (args[1]){
+ case 'in': method = 'start'; args[1] = 1; break;
+ case 'out': method = 'start'; args[1] = 0; break;
+ case 'show': method = 'set'; args[1] = 1; break;
+ case 'hide': method = 'set'; args[1] = 0; break;
+ case 'toggle':
+ var flag = this.retrieve('fade:flag', this.getStyle('opacity') == 1);
+ method = 'start';
+ args[1] = flag ? 0 : 1;
+ this.store('fade:flag', !flag);
+ toggle = true;
+ break;
+ default: method = 'start';
+ }
+ if (!toggle) this.eliminate('fade:flag');
+ fade[method].apply(fade, args);
+ var to = args[args.length - 1];
+ if (method == 'set' || to != 0) this.setStyle('visibility', to == 0 ? 'hidden' : 'visible');
+ else fade.chain(function(){
+ this.element.setStyle('visibility', 'hidden');
+ this.callChain();
+ });
+ return this;
+ },
+
+ highlight: function(start, end){
+ if (!end){
+ end = this.retrieve('highlight:original', this.getStyle('background-color'));
+ end = (end == 'transparent') ? '#fff' : end;
+ }
+ var tween = this.get('tween');
+ tween.start('background-color', start || '#ffff88', end).chain(function(){
+ this.setStyle('background-color', this.retrieve('highlight:original'));
+ tween.callChain();
+ }.bind(this));
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+name: Fx.Morph
+
+description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
+
+license: MIT-style license.
+
+requires: Fx.CSS
+
+provides: Fx.Morph
+
+...
+*/
+
+Fx.Morph = new Class({
+
+ Extends: Fx.CSS,
+
+ initialize: function(element, options){
+ this.element = this.subject = document.id(element);
+ this.parent(options);
+ },
+
+ set: function(now){
+ if (typeof now == 'string') now = this.search(now);
+ for (var p in now) this.render(this.element, p, now[p], this.options.unit);
+ return this;
+ },
+
+ compute: function(from, to, delta){
+ var now = {};
+ for (var p in from) now[p] = this.parent(from[p], to[p], delta);
+ return now;
+ },
+
+ start: function(properties){
+ if (!this.check(properties)) return this;
+ if (typeof properties == 'string') properties = this.search(properties);
+ var from = {}, to = {};
+ for (var p in properties){
+ var parsed = this.prepare(this.element, p, properties[p]);
+ from[p] = parsed.from;
+ to[p] = parsed.to;
+ }
+ return this.parent(from, to);
+ }
+
+});
+
+Element.Properties.morph = {
+
+ set: function(options){
+ this.get('morph').cancel().setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var morph = this.retrieve('morph');
+ if (!morph){
+ morph = new Fx.Morph(this, {link: 'cancel'});
+ this.store('morph', morph);
+ }
+ return morph;
+ }
+
+};
+
+Element.implement({
+
+ morph: function(props){
+ this.get('morph').start(props);
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+name: Fx.Transitions
+
+description: Contains a set of advanced transitions to be used with any of the Fx Classes.
+
+license: MIT-style license.
+
+credits:
+ - Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
+
+requires: Fx
+
+provides: Fx.Transitions
+
+...
+*/
+
+Fx.implement({
+
+ getTransition: function(){
+ var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
+ if (typeof trans == 'string'){
+ var data = trans.split(':');
+ trans = Fx.Transitions;
+ trans = trans[data[0]] || trans[data[0].capitalize()];
+ if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
+ }
+ return trans;
+ }
+
+});
+
+Fx.Transition = function(transition, params){
+ params = Array.from(params);
+ var easeIn = function(pos){
+ return transition(pos, params);
+ };
+ return Object.append(easeIn, {
+ easeIn: easeIn,
+ easeOut: function(pos){
+ return 1 - transition(1 - pos, params);
+ },
+ easeInOut: function(pos){
+ return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2;
+ }
+ });
+};
+
+Fx.Transitions = {
+
+ linear: function(zero){
+ return zero;
+ }
+
+};
+
+
+
+Fx.Transitions.extend = function(transitions){
+ for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
+};
+
+Fx.Transitions.extend({
+
+ Pow: function(p, x){
+ return Math.pow(p, x && x[0] || 6);
+ },
+
+ Expo: function(p){
+ return Math.pow(2, 8 * (p - 1));
+ },
+
+ Circ: function(p){
+ return 1 - Math.sin(Math.acos(p));
+ },
+
+ Sine: function(p){
+ return 1 - Math.cos(p * Math.PI / 2);
+ },
+
+ Back: function(p, x){
+ x = x && x[0] || 1.618;
+ return Math.pow(p, 2) * ((x + 1) * p - x);
+ },
+
+ Bounce: function(p){
+ var value;
+ for (var a = 0, b = 1; 1; a += b, b /= 2){
+ if (p >= (7 - 4 * a) / 11){
+ value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
+ break;
+ }
+ }
+ return value;
+ },
+
+ Elastic: function(p, x){
+ return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3);
+ }
+
+});
+
+['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
+ Fx.Transitions[transition] = new Fx.Transition(function(p){
+ return Math.pow(p, i + 2);
+ });
+});
+
+
+/*
+---
+
+name: Request
+
+description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
+
+license: MIT-style license.
+
+requires: [Object, Element, Chain, Events, Options, Browser]
+
+provides: Request
+
+...
+*/
+
+(function(){
+
+var empty = function(){},
+ progressSupport = ('onprogress' in new Browser.Request);
+
+var Request = this.Request = new Class({
+
+ Implements: [Chain, Events, Options],
+
+ options: {/*
+ onRequest: function(){},
+ onLoadstart: function(event, xhr){},
+ onProgress: function(event, xhr){},
+ onComplete: function(){},
+ onCancel: function(){},
+ onSuccess: function(responseText, responseXML){},
+ onFailure: function(xhr){},
+ onException: function(headerName, value){},
+ onTimeout: function(){},
+ user: '',
+ password: '',*/
+ url: '',
+ data: '',
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ },
+ async: true,
+ format: false,
+ method: 'post',
+ link: 'ignore',
+ isSuccess: null,
+ emulation: true,
+ urlEncoded: true,
+ encoding: 'utf-8',
+ evalScripts: false,
+ evalResponse: false,
+ timeout: 0,
+ noCache: false
+ },
+
+ initialize: function(options){
+ this.xhr = new Browser.Request();
+ this.setOptions(options);
+ this.headers = this.options.headers;
+ },
+
+ onStateChange: function(){
+ var xhr = this.xhr;
+ if (xhr.readyState != 4 || !this.running) return;
+ this.running = false;
+ this.status = 0;
+ Function.attempt(function(){
+ var status = xhr.status;
+ this.status = (status == 1223) ? 204 : status;
+ }.bind(this));
+ xhr.onreadystatechange = empty;
+ if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
+ clearTimeout(this.timer);
+
+ this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
+ if (this.options.isSuccess.call(this, this.status))
+ this.success(this.response.text, this.response.xml);
+ else
+ this.failure();
+ },
+
+ isSuccess: function(){
+ var status = this.status;
+ return (status >= 200 && status < 300);
+ },
+
+ isRunning: function(){
+ return !!this.running;
+ },
+
+ processScripts: function(text){
+ if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text);
+ return text.stripScripts(this.options.evalScripts);
+ },
+
+ success: function(text, xml){
+ this.onSuccess(this.processScripts(text), xml);
+ },
+
+ onSuccess: function(){
+ this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
+ },
+
+ failure: function(){
+ this.onFailure();
+ },
+
+ onFailure: function(){
+ this.fireEvent('complete').fireEvent('failure', this.xhr);
+ },
+
+ loadstart: function(event){
+ this.fireEvent('loadstart', [event, this.xhr]);
+ },
+
+ progress: function(event){
+ this.fireEvent('progress', [event, this.xhr]);
+ },
+
+ timeout: function(){
+ this.fireEvent('timeout', this.xhr);
+ },
+
+ setHeader: function(name, value){
+ this.headers[name] = value;
+ return this;
+ },
+
+ getHeader: function(name){
+ return Function.attempt(function(){
+ return this.xhr.getResponseHeader(name);
+ }.bind(this));
+ },
+
+ check: function(){
+ if (!this.running) return true;
+ switch (this.options.link){
+ case 'cancel': this.cancel(); return true;
+ case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
+ }
+ return false;
+ },
+
+ send: function(options){
+ if (!this.check(options)) return this;
+
+ this.options.isSuccess = this.options.isSuccess || this.isSuccess;
+ this.running = true;
+
+ var type = typeOf(options);
+ if (type == 'string' || type == 'element') options = {data: options};
+
+ var old = this.options;
+ options = Object.append({data: old.data, url: old.url, method: old.method}, options);
+ var data = options.data, url = String(options.url), method = options.method.toLowerCase();
+
+ switch (typeOf(data)){
+ case 'element': data = document.id(data).toQueryString(); break;
+ case 'object': case 'hash': data = Object.toQueryString(data);
+ }
+
+ if (this.options.format){
+ var format = 'format=' + this.options.format;
+ data = (data) ? format + '&' + data : format;
+ }
+
+ if (this.options.emulation && !['get', 'post'].contains(method)){
+ var _method = '_method=' + method;
+ data = (data) ? _method + '&' + data : _method;
+ method = 'post';
+ }
+
+ if (this.options.urlEncoded && ['post', 'put'].contains(method)){
+ var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
+ this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
+ }
+
+ if (!url) url = document.location.pathname;
+
+ var trimPosition = url.lastIndexOf('/');
+ if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
+
+ if (this.options.noCache)
+ url += (url.indexOf('?') > -1 ? '&' : '?') + String.uniqueID();
+
+ if (data && (method == 'get' || method == 'delete')){
+ url += (url.indexOf('?') > -1 ? '&' : '?') + data;
+ data = null;
+ }
+
+ var xhr = this.xhr;
+ if (progressSupport){
+ xhr.onloadstart = this.loadstart.bind(this);
+ xhr.onprogress = this.progress.bind(this);
+ }
+
+ xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
+ if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true;
+
+ xhr.onreadystatechange = this.onStateChange.bind(this);
+
+ Object.each(this.headers, function(value, key){
+ try {
+ xhr.setRequestHeader(key, value);
+ } catch (e){
+ this.fireEvent('exception', [key, value]);
+ }
+ }, this);
+
+ this.fireEvent('request');
+ xhr.send(data);
+ if (!this.options.async) this.onStateChange();
+ else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
+ return this;
+ },
+
+ cancel: function(){
+ if (!this.running) return this;
+ this.running = false;
+ var xhr = this.xhr;
+ xhr.abort();
+ clearTimeout(this.timer);
+ xhr.onreadystatechange = empty;
+ if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
+ this.xhr = new Browser.Request();
+ this.fireEvent('cancel');
+ return this;
+ }
+
+});
+
+var methods = {};
+['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
+ methods[method] = function(data){
+ var object = {
+ method: method
+ };
+ if (data != null) object.data = data;
+ return this.send(object);
+ };
+});
+
+Request.implement(methods);
+
+Element.Properties.send = {
+
+ set: function(options){
+ var send = this.get('send').cancel();
+ send.setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var send = this.retrieve('send');
+ if (!send){
+ send = new Request({
+ data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
+ });
+ this.store('send', send);
+ }
+ return send;
+ }
+
+};
+
+Element.implement({
+
+ send: function(url){
+ var sender = this.get('send');
+ sender.send({data: this, url: url || sender.options.url});
+ return this;
+ }
+
+});
+
+})();
+
+
+/*
+---
+
+name: Request.HTML
+
+description: Extends the basic Request Class with additional methods for interacting with HTML responses.
+
+license: MIT-style license.
+
+requires: [Element, Request]
+
+provides: Request.HTML
+
+...
+*/
+
+Request.HTML = new Class({
+
+ Extends: Request,
+
+ options: {
+ update: false,
+ append: false,
+ evalScripts: true,
+ filter: false,
+ headers: {
+ Accept: 'text/html, application/xml, text/xml, */*'
+ }
+ },
+
+ success: function(text){
+ var options = this.options, response = this.response;
+
+ response.html = text.stripScripts(function(script){
+ response.javascript = script;
+ });
+
+ var match = response.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
+ if (match) response.html = match[1];
+ var temp = new Element('div').set('html', response.html);
+
+ response.tree = temp.childNodes;
+ response.elements = temp.getElements(options.filter || '*');
+
+ if (options.filter) response.tree = response.elements;
+ if (options.update){
+ var update = document.id(options.update).empty();
+ if (options.filter) update.adopt(response.elements);
+ else update.set('html', response.html);
+ } else if (options.append){
+ var append = document.id(options.append);
+ if (options.filter) response.elements.reverse().inject(append);
+ else append.adopt(temp.getChildren());
+ }
+ if (options.evalScripts) Browser.exec(response.javascript);
+
+ this.onSuccess(response.tree, response.elements, response.html, response.javascript);
+ }
+
+});
+
+Element.Properties.load = {
+
+ set: function(options){
+ var load = this.get('load').cancel();
+ load.setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var load = this.retrieve('load');
+ if (!load){
+ load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'});
+ this.store('load', load);
+ }
+ return load;
+ }
+
+};
+
+Element.implement({
+
+ load: function(){
+ this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString}));
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+name: JSON
+
+description: JSON encoder and decoder.
+
+license: MIT-style license.
+
+SeeAlso: <http://www.json.org/>
+
+requires: [Array, String, Number, Function]
+
+provides: JSON
+
+...
+*/
+
+if (typeof JSON == 'undefined') this.JSON = {};
+
+
+
+(function(){
+
+var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
+
+var escape = function(chr){
+ return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
+};
+
+JSON.validate = function(string){
+ string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+ replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+
+ return (/^[\],:{}\s]*$/).test(string);
+};
+
+JSON.encode = JSON.stringify ? function(obj){
+ return JSON.stringify(obj);
+} : function(obj){
+ if (obj && obj.toJSON) obj = obj.toJSON();
+
+ switch (typeOf(obj)){
+ case 'string':
+ return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
+ case 'array':
+ return '[' + obj.map(JSON.encode).clean() + ']';
+ case 'object': case 'hash':
+ var string = [];
+ Object.each(obj, function(value, key){
+ var json = JSON.encode(value);
+ if (json) string.push(JSON.encode(key) + ':' + json);
+ });
+ return '{' + string + '}';
+ case 'number': case 'boolean': return '' + obj;
+ case 'null': return 'null';
+ }
+
+ return null;
+};
+
+JSON.secure = true;
+
+
+JSON.decode = function(string, secure){
+ if (!string || typeOf(string) != 'string') return null;
+
+ if (secure == null) secure = JSON.secure;
+ if (secure){
+ if (JSON.parse) return JSON.parse(string);
+ if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
+ }
+
+ return eval('(' + string + ')');
+};
+
+})();
+
+
+/*
+---
+
+name: Request.JSON
+
+description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
+
+license: MIT-style license.
+
+requires: [Request, JSON]
+
+provides: Request.JSON
+
+...
+*/
+
+Request.JSON = new Class({
+
+ Extends: Request,
+
+ options: {
+ /*onError: function(text, error){},*/
+ secure: true
+ },
+
+ initialize: function(options){
+ this.parent(options);
+ Object.append(this.headers, {
+ 'Accept': 'application/json',
+ 'X-Request': 'JSON'
+ });
+ },
+
+ success: function(text){
+ var json;
+ try {
+ json = this.response.json = JSON.decode(text, this.options.secure);
+ } catch (error){
+ this.fireEvent('error', [text, error]);
+ return;
+ }
+ if (json == null) this.onFailure();
+ else this.onSuccess(json, text);
+ }
+
+});
+
+
+/*
+---
+
+name: Cookie
+
+description: Class for creating, reading, and deleting browser Cookies.
+
+license: MIT-style license.
+
+credits:
+ - Based on the functions by Peter-Paul Koch (http://quirksmode.org).
+
+requires: [Options, Browser]
+
+provides: Cookie
+
+...
+*/
+
+var Cookie = new Class({
+
+ Implements: Options,
+
+ options: {
+ path: '/',
+ domain: false,
+ duration: false,
+ secure: false,
+ document: document,
+ encode: true
+ },
+
+ initialize: function(key, options){
+ this.key = key;
+ this.setOptions(options);
+ },
+
+ write: function(value){
+ if (this.options.encode) value = encodeURIComponent(value);
+ if (this.options.domain) value += '; domain=' + this.options.domain;
+ if (this.options.path) value += '; path=' + this.options.path;
+ if (this.options.duration){
+ var date = new Date();
+ date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
+ value += '; expires=' + date.toGMTString();
+ }
+ if (this.options.secure) value += '; secure';
+ this.options.document.cookie = this.key + '=' + value;
+ return this;
+ },
+
+ read: function(){
+ var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
+ return (value) ? decodeURIComponent(value[1]) : null;
+ },
+
+ dispose: function(){
+ new Cookie(this.key, Object.merge({}, this.options, {duration: -1})).write('');
+ return this;
+ }
+
+});
+
+Cookie.write = function(key, value, options){
+ return new Cookie(key, options).write(value);
+};
+
+Cookie.read = function(key){
+ return new Cookie(key).read();
+};
+
+Cookie.dispose = function(key, options){
+ return new Cookie(key, options).dispose();
+};
+
+
+/*
+---
+
+name: DOMReady
+
+description: Contains the custom event domready.
+
+license: MIT-style license.
+
+requires: [Browser, Element, Element.Event]
+
+provides: [DOMReady, DomReady]
+
+...
+*/
+
+(function(window, document){
+
+var ready,
+ loaded,
+ checks = [],
+ shouldPoll,
+ timer,
+ testElement = document.createElement('div');
+
+var domready = function(){
+ clearTimeout(timer);
+ if (ready) return;
+ Browser.loaded = ready = true;
+ document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
+
+ document.fireEvent('domready');
+ window.fireEvent('domready');
+};
+
+var check = function(){
+ for (var i = checks.length; i--;) if (checks[i]()){
+ domready();
+ return true;
+ }
+ return false;
+};
+
+var poll = function(){
+ clearTimeout(timer);
+ if (!check()) timer = setTimeout(poll, 10);
+};
+
+document.addListener('DOMContentLoaded', domready);
+
+/*<ltIE8>*/
+// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
+// testElement.doScroll() throws when the DOM is not ready, only in the top window
+var doScrollWorks = function(){
+ try {
+ testElement.doScroll();
+ return true;
+ } catch (e){}
+ return false;
+};
+// If doScroll works already, it can't be used to determine domready
+// e.g. in an iframe
+if (testElement.doScroll && !doScrollWorks()){
+ checks.push(doScrollWorks);
+ shouldPoll = true;
+}
+/*</ltIE8>*/
+
+if (document.readyState) checks.push(function(){
+ var state = document.readyState;
+ return (state == 'loaded' || state == 'complete');
+});
+
+if ('onreadystatechange' in document) document.addListener('readystatechange', check);
+else shouldPoll = true;
+
+if (shouldPoll) poll();
+
+Element.Events.domready = {
+ onAdd: function(fn){
+ if (ready) fn.call(this);
+ }
+};
+
+// Make sure that domready fires before load
+Element.Events.load = {
+ base: 'load',
+ onAdd: function(fn){
+ if (loaded && this == window) fn.call(this);
+ },
+ condition: function(){
+ if (this == window){
+ domready();
+ delete Element.Events.load;
+ }
+ return true;
+ }
+};
+
+// This is based on the custom load event
+window.addEvent('load', function(){
+ loaded = true;
+});
+
+})(window, document);
+
diff --git a/module/webui/themes/Flat/js/static/mootools-core.min.js b/module/webui/themes/Flat/js/static/mootools-core.min.js
new file mode 100644
index 000000000..354f94196
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/mootools-core.min.js
@@ -0,0 +1,491 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/core/8423c12ffd6a6bfcde9ea22554aec795
+
+packager build:
+ - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady
+
+copyrights:
+ - [MooTools](http://mootools.net)
+
+licenses:
+ - [MIT License](http://mootools.net/license.txt)
+...
+*/
+
+(function(){this.MooTools={version:"1.5.0",build:"0f7b690afee9349b15909f33016a25d2e4d9f4e3"};var o=this.typeOf=function(i){if(i==null){return"null";}if(i.$family!=null){return i.$family();
+}if(i.nodeName){if(i.nodeType==1){return"element";}if(i.nodeType==3){return(/\S/).test(i.nodeValue)?"textnode":"whitespace";}}else{if(typeof i.length=="number"){if("callee" in i){return"arguments";
+}if("item" in i){return"collection";}}}return typeof i;};var j=this.instanceOf=function(t,i){if(t==null){return false;}var s=t.$constructor||t.constructor;
+while(s){if(s===i){return true;}s=s.parent;}if(!t.hasOwnProperty){return false;}return t instanceof i;};var f=this.Function;var p=true;for(var k in {toString:1}){p=null;
+}if(p){p=["hasOwnProperty","valueOf","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","constructor"];}f.prototype.overloadSetter=function(s){var i=this;
+return function(u,t){if(u==null){return this;}if(s||typeof u!="string"){for(var v in u){i.call(this,v,u[v]);}if(p){for(var w=p.length;w--;){v=p[w];if(u.hasOwnProperty(v)){i.call(this,v,u[v]);
+}}}}else{i.call(this,u,t);}return this;};};f.prototype.overloadGetter=function(s){var i=this;return function(u){var v,t;if(typeof u!="string"){v=u;}else{if(arguments.length>1){v=arguments;
+}else{if(s){v=[u];}}}if(v){t={};for(var w=0;w<v.length;w++){t[v[w]]=i.call(this,v[w]);}}else{t=i.call(this,u);}return t;};};f.prototype.extend=function(i,s){this[i]=s;
+}.overloadSetter();f.prototype.implement=function(i,s){this.prototype[i]=s;}.overloadSetter();var n=Array.prototype.slice;f.from=function(i){return(o(i)=="function")?i:function(){return i;
+};};Array.from=function(i){if(i==null){return[];}return(a.isEnumerable(i)&&typeof i!="string")?(o(i)=="array")?i:n.call(i):[i];};Number.from=function(s){var i=parseFloat(s);
+return isFinite(i)?i:null;};String.from=function(i){return i+"";};f.implement({hide:function(){this.$hidden=true;return this;},protect:function(){this.$protected=true;
+return this;}});var a=this.Type=function(u,t){if(u){var s=u.toLowerCase();var i=function(v){return(o(v)==s);};a["is"+u]=i;if(t!=null){t.prototype.$family=(function(){return s;
+}).hide();}}if(t==null){return null;}t.extend(this);t.$constructor=a;t.prototype.$constructor=t;return t;};var e=Object.prototype.toString;a.isEnumerable=function(i){return(i!=null&&typeof i.length=="number"&&e.call(i)!="[object Function]");
+};var q={};var r=function(i){var s=o(i.prototype);return q[s]||(q[s]=[]);};var b=function(t,x){if(x&&x.$hidden){return;}var s=r(this);for(var u=0;u<s.length;
+u++){var w=s[u];if(o(w)=="type"){b.call(w,t,x);}else{w.call(this,t,x);}}var v=this.prototype[t];if(v==null||!v.$protected){this.prototype[t]=x;}if(this[t]==null&&o(x)=="function"){m.call(this,t,function(i){return x.apply(i,n.call(arguments,1));
+});}};var m=function(i,t){if(t&&t.$hidden){return;}var s=this[i];if(s==null||!s.$protected){this[i]=t;}};a.implement({implement:b.overloadSetter(),extend:m.overloadSetter(),alias:function(i,s){b.call(this,i,this.prototype[s]);
+}.overloadSetter(),mirror:function(i){r(this).push(i);return this;}});new a("Type",a);var d=function(s,x,v){var u=(x!=Object),B=x.prototype;if(u){x=new a(s,x);
+}for(var y=0,w=v.length;y<w;y++){var C=v[y],A=x[C],z=B[C];if(A){A.protect();}if(u&&z){x.implement(C,z.protect());}}if(u){var t=B.propertyIsEnumerable(v[0]);
+x.forEachMethod=function(G){if(!t){for(var F=0,D=v.length;F<D;F++){G.call(B,B[v[F]],v[F]);}}for(var E in B){G.call(B,B[E],E);}};}return d;};d("String",String,["charAt","charCodeAt","concat","contains","indexOf","lastIndexOf","match","quote","replace","search","slice","split","substr","substring","trim","toLowerCase","toUpperCase"])("Array",Array,["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice","indexOf","lastIndexOf","filter","forEach","every","map","some","reduce","reduceRight"])("Number",Number,["toExponential","toFixed","toLocaleString","toPrecision"])("Function",f,["apply","call","bind"])("RegExp",RegExp,["exec","test"])("Object",Object,["create","defineProperty","defineProperties","keys","getPrototypeOf","getOwnPropertyDescriptor","getOwnPropertyNames","preventExtensions","isExtensible","seal","isSealed","freeze","isFrozen"])("Date",Date,["now"]);
+Object.extend=m.overloadSetter();Date.extend("now",function(){return +(new Date);});new a("Boolean",Boolean);Number.prototype.$family=function(){return isFinite(this)?"number":"null";
+}.hide();Number.extend("random",function(s,i){return Math.floor(Math.random()*(i-s+1)+s);});var g=Object.prototype.hasOwnProperty;Object.extend("forEach",function(i,t,u){for(var s in i){if(g.call(i,s)){t.call(u,i[s],s,i);
+}}});Object.each=Object.forEach;Array.implement({forEach:function(u,v){for(var t=0,s=this.length;t<s;t++){if(t in this){u.call(v,this[t],t,this);}}},each:function(i,s){Array.forEach(this,i,s);
+return this;}});var l=function(i){switch(o(i)){case"array":return i.clone();case"object":return Object.clone(i);default:return i;}};Array.implement("clone",function(){var s=this.length,t=new Array(s);
+while(s--){t[s]=l(this[s]);}return t;});var h=function(s,i,t){switch(o(t)){case"object":if(o(s[i])=="object"){Object.merge(s[i],t);}else{s[i]=Object.clone(t);
+}break;case"array":s[i]=t.clone();break;default:s[i]=t;}return s;};Object.extend({merge:function(z,u,t){if(o(u)=="string"){return h(z,u,t);}for(var y=1,s=arguments.length;
+y<s;y++){var w=arguments[y];for(var x in w){h(z,x,w[x]);}}return z;},clone:function(i){var t={};for(var s in i){t[s]=l(i[s]);}return t;},append:function(w){for(var v=1,t=arguments.length;
+v<t;v++){var s=arguments[v]||{};for(var u in s){w[u]=s[u];}}return w;}});["Object","WhiteSpace","TextNode","Collection","Arguments"].each(function(i){new a(i);
+});var c=Date.now();String.extend("uniqueID",function(){return(c++).toString(36);});})();Array.implement({every:function(c,d){for(var b=0,a=this.length>>>0;
+b<a;b++){if((b in this)&&!c.call(d,this[b],b,this)){return false;}}return true;},filter:function(d,f){var c=[];for(var e,b=0,a=this.length>>>0;b<a;b++){if(b in this){e=this[b];
+if(d.call(f,e,b,this)){c.push(e);}}}return c;},indexOf:function(c,d){var b=this.length>>>0;for(var a=(d<0)?Math.max(0,b+d):d||0;a<b;a++){if(this[a]===c){return a;
+}}return -1;},map:function(c,e){var d=this.length>>>0,b=Array(d);for(var a=0;a<d;a++){if(a in this){b[a]=c.call(e,this[a],a,this);}}return b;},some:function(c,d){for(var b=0,a=this.length>>>0;
+b<a;b++){if((b in this)&&c.call(d,this[b],b,this)){return true;}}return false;},clean:function(){return this.filter(function(a){return a!=null;});},invoke:function(a){var b=Array.slice(arguments,1);
+return this.map(function(c){return c[a].apply(c,b);});},associate:function(c){var d={},b=Math.min(this.length,c.length);for(var a=0;a<b;a++){d[c[a]]=this[a];
+}return d;},link:function(c){var a={};for(var e=0,b=this.length;e<b;e++){for(var d in c){if(c[d](this[e])){a[d]=this[e];delete c[d];break;}}}return a;},contains:function(a,b){return this.indexOf(a,b)!=-1;
+},append:function(a){this.push.apply(this,a);return this;},getLast:function(){return(this.length)?this[this.length-1]:null;},getRandom:function(){return(this.length)?this[Number.random(0,this.length-1)]:null;
+},include:function(a){if(!this.contains(a)){this.push(a);}return this;},combine:function(c){for(var b=0,a=c.length;b<a;b++){this.include(c[b]);}return this;
+},erase:function(b){for(var a=this.length;a--;){if(this[a]===b){this.splice(a,1);}}return this;},empty:function(){this.length=0;return this;},flatten:function(){var d=[];
+for(var b=0,a=this.length;b<a;b++){var c=typeOf(this[b]);if(c=="null"){continue;}d=d.concat((c=="array"||c=="collection"||c=="arguments"||instanceOf(this[b],Array))?Array.flatten(this[b]):this[b]);
+}return d;},pick:function(){for(var b=0,a=this.length;b<a;b++){if(this[b]!=null){return this[b];}}return null;},hexToRgb:function(b){if(this.length!=3){return null;
+}var a=this.map(function(c){if(c.length==1){c+=c;}return parseInt(c,16);});return(b)?a:"rgb("+a+")";},rgbToHex:function(d){if(this.length<3){return null;
+}if(this.length==4&&this[3]==0&&!d){return"transparent";}var b=[];for(var a=0;a<3;a++){var c=(this[a]-0).toString(16);b.push((c.length==1)?"0"+c:c);}return(d)?b:"#"+b.join("");
+}});String.implement({contains:function(b,a){return(a?String(this).slice(a):String(this)).indexOf(b)>-1;},test:function(a,b){return((typeOf(a)=="regexp")?a:new RegExp(""+a,b)).test(this);
+},trim:function(){return String(this).replace(/^\s+|\s+$/g,"");},clean:function(){return String(this).replace(/\s+/g," ").trim();},camelCase:function(){return String(this).replace(/-\D/g,function(a){return a.charAt(1).toUpperCase();
+});},hyphenate:function(){return String(this).replace(/[A-Z]/g,function(a){return("-"+a.charAt(0).toLowerCase());});},capitalize:function(){return String(this).replace(/\b[a-z]/g,function(a){return a.toUpperCase();
+});},escapeRegExp:function(){return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1");},toInt:function(a){return parseInt(this,a||10);},toFloat:function(){return parseFloat(this);
+},hexToRgb:function(b){var a=String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);return(a)?a.slice(1).hexToRgb(b):null;},rgbToHex:function(b){var a=String(this).match(/\d{1,3}/g);
+return(a)?a.rgbToHex(b):null;},substitute:function(a,b){return String(this).replace(b||(/\\?\{([^{}]+)\}/g),function(d,c){if(d.charAt(0)=="\\"){return d.slice(1);
+}return(a[c]!=null)?a[c]:"";});}});Number.implement({limit:function(b,a){return Math.min(a,Math.max(b,this));},round:function(a){a=Math.pow(10,a||0).toFixed(a<0?-a:0);
+return Math.round(this*a)/a;},times:function(b,c){for(var a=0;a<this;a++){b.call(c,a,this);}},toFloat:function(){return parseFloat(this);},toInt:function(a){return parseInt(this,a||10);
+}});Number.alias("each","times");(function(b){var a={};b.each(function(c){if(!Number[c]){a[c]=function(){return Math[c].apply(null,[this].concat(Array.from(arguments)));
+};}});Number.implement(a);})(["abs","acos","asin","atan","atan2","ceil","cos","exp","floor","log","max","min","pow","sin","sqrt","tan"]);Function.extend({attempt:function(){for(var b=0,a=arguments.length;
+b<a;b++){try{return arguments[b]();}catch(c){}}return null;}});Function.implement({attempt:function(a,c){try{return this.apply(c,Array.from(a));}catch(b){}return null;
+},bind:function(e){var a=this,b=arguments.length>1?Array.slice(arguments,1):null,d=function(){};var c=function(){var g=e,h=arguments.length;if(this instanceof c){d.prototype=a.prototype;
+g=new d;}var f=(!b&&!h)?a.call(g):a.apply(g,b&&h?b.concat(Array.slice(arguments)):b||arguments);return g==e?f:g;};return c;},pass:function(b,c){var a=this;
+if(b!=null){b=Array.from(b);}return function(){return a.apply(c,b||arguments);};},delay:function(b,c,a){return setTimeout(this.pass((a==null?[]:a),c),b);
+},periodical:function(c,b,a){return setInterval(this.pass((a==null?[]:a),b),c);}});(function(){var a=Object.prototype.hasOwnProperty;Object.extend({subset:function(d,g){var f={};
+for(var e=0,b=g.length;e<b;e++){var c=g[e];if(c in d){f[c]=d[c];}}return f;},map:function(b,e,f){var d={};for(var c in b){if(a.call(b,c)){d[c]=e.call(f,b[c],c,b);
+}}return d;},filter:function(b,e,g){var d={};for(var c in b){var f=b[c];if(a.call(b,c)&&e.call(g,f,c,b)){d[c]=f;}}return d;},every:function(b,d,e){for(var c in b){if(a.call(b,c)&&!d.call(e,b[c],c)){return false;
+}}return true;},some:function(b,d,e){for(var c in b){if(a.call(b,c)&&d.call(e,b[c],c)){return true;}}return false;},keys:function(b){var d=[];for(var c in b){if(a.call(b,c)){d.push(c);
+}}return d;},values:function(c){var b=[];for(var d in c){if(a.call(c,d)){b.push(c[d]);}}return b;},getLength:function(b){return Object.keys(b).length;},keyOf:function(b,d){for(var c in b){if(a.call(b,c)&&b[c]===d){return c;
+}}return null;},contains:function(b,c){return Object.keyOf(b,c)!=null;},toQueryString:function(b,c){var d=[];Object.each(b,function(h,g){if(c){g=c+"["+g+"]";
+}var f;switch(typeOf(h)){case"object":f=Object.toQueryString(h,g);break;case"array":var e={};h.each(function(k,j){e[j]=k;});f=Object.toQueryString(e,g);
+break;default:f=g+"="+encodeURIComponent(h);}if(h!=null){d.push(f);}});return d.join("&");}});})();(function(){var f=this.document;var d=f.window=this;
+var a=function(k,e){k=k.toLowerCase();e=(e?e.toLowerCase():"");var l=k.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/)||[null,"unknown",0];
+if(l[1]=="trident"){l[1]="ie";if(l[4]){l[2]=l[4];}}else{if(l[1]=="crios"){l[1]="chrome";}}var e=k.match(/ip(?:ad|od|hone)/)?"ios":(k.match(/(?:webos|android)/)||e.match(/mac|win|linux/)||["other"])[0];
+if(e=="win"){e="windows";}return{extend:Function.prototype.extend,name:(l[1]=="version")?l[3]:l[1],version:parseFloat((l[1]=="opera"&&l[4])?l[4]:l[2]),platform:e};
+};var j=this.Browser=a(navigator.userAgent,navigator.platform);if(j.ie){j.version=f.documentMode;}j.extend({Features:{xpath:!!(f.evaluate),air:!!(d.runtime),query:!!(f.querySelector),json:!!(d.JSON)},parseUA:a});
+j.Request=(function(){var l=function(){return new XMLHttpRequest();};var k=function(){return new ActiveXObject("MSXML2.XMLHTTP");};var e=function(){return new ActiveXObject("Microsoft.XMLHTTP");
+};return Function.attempt(function(){l();return l;},function(){k();return k;},function(){e();return e;});})();j.Features.xhr=!!(j.Request);j.exec=function(k){if(!k){return k;
+}if(d.execScript){d.execScript(k);}else{var e=f.createElement("script");e.setAttribute("type","text/javascript");e.text=k;f.head.appendChild(e);f.head.removeChild(e);
+}return k;};String.implement("stripScripts",function(k){var e="";var l=this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi,function(m,n){e+=n+"\n";return"";
+});if(k===true){j.exec(e);}else{if(typeOf(k)=="function"){k(e,l);}}return l;});j.extend({Document:this.Document,Window:this.Window,Element:this.Element,Event:this.Event});
+this.Window=this.$constructor=new Type("Window",function(){});this.$family=Function.from("window").hide();Window.mirror(function(e,k){d[e]=k;});this.Document=f.$constructor=new Type("Document",function(){});
+f.$family=Function.from("document").hide();Document.mirror(function(e,k){f[e]=k;});f.html=f.documentElement;if(!f.head){f.head=f.getElementsByTagName("head")[0];
+}if(f.execCommand){try{f.execCommand("BackgroundImageCache",false,true);}catch(c){}}if(this.attachEvent&&!this.addEventListener){var b=function(){this.detachEvent("onunload",b);
+f.head=f.html=f.window=null;};this.attachEvent("onunload",b);}var g=Array.from;try{g(f.html.childNodes);}catch(c){Array.from=function(k){if(typeof k!="string"&&Type.isEnumerable(k)&&typeOf(k)!="array"){var e=k.length,l=new Array(e);
+while(e--){l[e]=k[e];}return l;}return g(k);};var h=Array.prototype,i=h.slice;["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice"].each(function(e){var k=h[e];
+Array[e]=function(l){return k.apply(Array.from(l),i.call(arguments,1));};});}})();(function(){var b={};var a=this.DOMEvent=new Type("DOMEvent",function(c,g){if(!g){g=window;
+}c=c||g.event;if(c.$extended){return c;}this.event=c;this.$extended=true;this.shift=c.shiftKey;this.control=c.ctrlKey;this.alt=c.altKey;this.meta=c.metaKey;
+var i=this.type=c.type;var h=c.target||c.srcElement;while(h&&h.nodeType==3){h=h.parentNode;}this.target=document.id(h);if(i.indexOf("key")==0){var d=this.code=(c.which||c.keyCode);
+this.key=b[d];if(i=="keydown"||i=="keyup"){if(d>111&&d<124){this.key="f"+(d-111);}else{if(d>95&&d<106){this.key=d-96;}}}if(this.key==null){this.key=String.fromCharCode(d).toLowerCase();
+}}else{if(i=="click"||i=="dblclick"||i=="contextmenu"||i=="DOMMouseScroll"||i.indexOf("mouse")==0){var j=g.document;j=(!j.compatMode||j.compatMode=="CSS1Compat")?j.html:j.body;
+this.page={x:(c.pageX!=null)?c.pageX:c.clientX+j.scrollLeft,y:(c.pageY!=null)?c.pageY:c.clientY+j.scrollTop};this.client={x:(c.pageX!=null)?c.pageX-g.pageXOffset:c.clientX,y:(c.pageY!=null)?c.pageY-g.pageYOffset:c.clientY};
+if(i=="DOMMouseScroll"||i=="mousewheel"){this.wheel=(c.wheelDelta)?c.wheelDelta/120:-(c.detail||0)/3;}this.rightClick=(c.which==3||c.button==2);if(i=="mouseover"||i=="mouseout"){var k=c.relatedTarget||c[(i=="mouseover"?"from":"to")+"Element"];
+while(k&&k.nodeType==3){k=k.parentNode;}this.relatedTarget=document.id(k);}}else{if(i.indexOf("touch")==0||i.indexOf("gesture")==0){this.rotation=c.rotation;
+this.scale=c.scale;this.targetTouches=c.targetTouches;this.changedTouches=c.changedTouches;var f=this.touches=c.touches;if(f&&f[0]){var e=f[0];this.page={x:e.pageX,y:e.pageY};
+this.client={x:e.clientX,y:e.clientY};}}}}if(!this.client){this.client={};}if(!this.page){this.page={};}});a.implement({stop:function(){return this.preventDefault().stopPropagation();
+},stopPropagation:function(){if(this.event.stopPropagation){this.event.stopPropagation();}else{this.event.cancelBubble=true;}return this;},preventDefault:function(){if(this.event.preventDefault){this.event.preventDefault();
+}else{this.event.returnValue=false;}return this;}});a.defineKey=function(d,c){b[d]=c;return this;};a.defineKeys=a.defineKey.overloadSetter(true);a.defineKeys({"38":"up","40":"down","37":"left","39":"right","27":"esc","32":"space","8":"backspace","9":"tab","46":"delete","13":"enter"});
+})();(function(){var a=this.Class=new Type("Class",function(h){if(instanceOf(h,Function)){h={initialize:h};}var g=function(){e(this);if(g.$prototyping){return this;
+}this.$caller=null;var i=(this.initialize)?this.initialize.apply(this,arguments):this;this.$caller=this.caller=null;return i;}.extend(this).implement(h);
+g.$constructor=a;g.prototype.$constructor=g;g.prototype.parent=c;return g;});var c=function(){if(!this.$caller){throw new Error('The method "parent" cannot be called.');
+}var g=this.$caller.$name,h=this.$caller.$owner.parent,i=(h)?h.prototype[g]:null;if(!i){throw new Error('The method "'+g+'" has no parent.');}return i.apply(this,arguments);
+};var e=function(g){for(var h in g){var j=g[h];switch(typeOf(j)){case"object":var i=function(){};i.prototype=j;g[h]=e(new i);break;case"array":g[h]=j.clone();
+break;}}return g;};var b=function(g,h,j){if(j.$origin){j=j.$origin;}var i=function(){if(j.$protected&&this.$caller==null){throw new Error('The method "'+h+'" cannot be called.');
+}var l=this.caller,m=this.$caller;this.caller=m;this.$caller=i;var k=j.apply(this,arguments);this.$caller=m;this.caller=l;return k;}.extend({$owner:g,$origin:j,$name:h});
+return i;};var f=function(h,i,g){if(a.Mutators.hasOwnProperty(h)){i=a.Mutators[h].call(this,i);if(i==null){return this;}}if(typeOf(i)=="function"){if(i.$hidden){return this;
+}this.prototype[h]=(g)?i:b(this,h,i);}else{Object.merge(this.prototype,h,i);}return this;};var d=function(g){g.$prototyping=true;var h=new g;delete g.$prototyping;
+return h;};a.implement("implement",f.overloadSetter());a.Mutators={Extends:function(g){this.parent=g;this.prototype=d(g);},Implements:function(g){Array.from(g).each(function(j){var h=new j;
+for(var i in h){f.call(this,i,h[i],true);}},this);}};})();(function(){this.Chain=new Class({$chain:[],chain:function(){this.$chain.append(Array.flatten(arguments));
+return this;},callChain:function(){return(this.$chain.length)?this.$chain.shift().apply(this,arguments):false;},clearChain:function(){this.$chain.empty();
+return this;}});var a=function(b){return b.replace(/^on([A-Z])/,function(c,d){return d.toLowerCase();});};this.Events=new Class({$events:{},addEvent:function(d,c,b){d=a(d);
+this.$events[d]=(this.$events[d]||[]).include(c);if(b){c.internal=true;}return this;},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this;
+},fireEvent:function(e,c,b){e=a(e);var d=this.$events[e];if(!d){return this;}c=Array.from(c);d.each(function(f){if(b){f.delay(b,this,c);}else{f.apply(this,c);
+}},this);return this;},removeEvent:function(e,d){e=a(e);var c=this.$events[e];if(c&&!d.internal){var b=c.indexOf(d);if(b!=-1){delete c[b];}}return this;
+},removeEvents:function(d){var e;if(typeOf(d)=="object"){for(e in d){this.removeEvent(e,d[e]);}return this;}if(d){d=a(d);}for(e in this.$events){if(d&&d!=e){continue;
+}var c=this.$events[e];for(var b=c.length;b--;){if(b in c){this.removeEvent(e,c[b]);}}}return this;}});this.Options=new Class({setOptions:function(){var b=this.options=Object.merge.apply(null,[{},this.options].append(arguments));
+if(this.addEvent){for(var c in b){if(typeOf(b[c])!="function"||!(/^on[A-Z]/).test(c)){continue;}this.addEvent(c,b[c]);delete b[c];}}return this;}});})();
+(function(){var k,n,l,g,a={},c={},m=/\\/g;var e=function(q,p){if(q==null){return null;}if(q.Slick===true){return q;}q=(""+q).replace(/^\s+|\s+$/g,"");g=!!p;
+var o=(g)?c:a;if(o[q]){return o[q];}k={Slick:true,expressions:[],raw:q,reverse:function(){return e(this.raw,true);}};n=-1;while(q!=(q=q.replace(j,b))){}k.length=k.expressions.length;
+return o[k.raw]=(g)?h(k):k;};var i=function(o){if(o==="!"){return" ";}else{if(o===" "){return"!";}else{if((/^!/).test(o)){return o.replace(/^!/,"");}else{return"!"+o;
+}}}};var h=function(u){var r=u.expressions;for(var p=0;p<r.length;p++){var t=r[p];var q={parts:[],tag:"*",combinator:i(t[0].combinator)};for(var o=0;o<t.length;
+o++){var s=t[o];if(!s.reverseCombinator){s.reverseCombinator=" ";}s.combinator=s.reverseCombinator;delete s.reverseCombinator;}t.reverse().push(q);}return u;
+};var f=function(o){return o.replace(/[-[\]{}()*+?.\\^$|,#\s]/g,function(p){return"\\"+p;});};var j=new RegExp("^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)".replace(/<combinator>/,"["+f(">+~`!@$%^&={}\\;</")+"]").replace(/<unicode>/g,"(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])").replace(/<unicode1>/g,"(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])"));
+function b(x,s,D,z,r,C,q,B,A,y,u,F,G,v,p,w){if(s||n===-1){k.expressions[++n]=[];l=-1;if(s){return"";}}if(D||z||l===-1){D=D||" ";var t=k.expressions[n];
+if(g&&t[l]){t[l].reverseCombinator=i(D);}t[++l]={combinator:D,tag:"*"};}var o=k.expressions[n][l];if(r){o.tag=r.replace(m,"");}else{if(C){o.id=C.replace(m,"");
+}else{if(q){q=q.replace(m,"");if(!o.classList){o.classList=[];}if(!o.classes){o.classes=[];}o.classList.push(q);o.classes.push({value:q,regexp:new RegExp("(^|\\s)"+f(q)+"(\\s|$)")});
+}else{if(G){w=w||p;w=w?w.replace(m,""):null;if(!o.pseudos){o.pseudos=[];}o.pseudos.push({key:G.replace(m,""),value:w,type:F.length==1?"class":"element"});
+}else{if(B){B=B.replace(m,"");u=(u||"").replace(m,"");var E,H;switch(A){case"^=":H=new RegExp("^"+f(u));break;case"$=":H=new RegExp(f(u)+"$");break;case"~=":H=new RegExp("(^|\\s)"+f(u)+"(\\s|$)");
+break;case"|=":H=new RegExp("^"+f(u)+"(-|$)");break;case"=":E=function(I){return u==I;};break;case"*=":E=function(I){return I&&I.indexOf(u)>-1;};break;
+case"!=":E=function(I){return u!=I;};break;default:E=function(I){return !!I;};}if(u==""&&(/^[*$^]=$/).test(A)){E=function(){return false;};}if(!E){E=function(I){return I&&H.test(I);
+};}if(!o.attributes){o.attributes=[];}o.attributes.push({key:B,operator:A,value:u,test:E});}}}}}return"";}var d=(this.Slick||{});d.parse=function(o){return e(o);
+};d.escapeRegExp=f;if(!this.Slick){this.Slick=d;}}).apply((typeof exports!="undefined")?exports:this);(function(){var k={},m={},d=Object.prototype.toString;
+k.isNativeCode=function(c){return(/\{\s*\[native code\]\s*\}/).test(""+c);};k.isXML=function(c){return(!!c.xmlVersion)||(!!c.xml)||(d.call(c)=="[object XMLDocument]")||(c.nodeType==9&&c.documentElement.nodeName!="HTML");
+};k.setDocument=function(w){var p=w.nodeType;if(p==9){}else{if(p){w=w.ownerDocument;}else{if(w.navigator){w=w.document;}else{return;}}}if(this.document===w){return;
+}this.document=w;var A=w.documentElement,o=this.getUIDXML(A),s=m[o],r;if(s){for(r in s){this[r]=s[r];}return;}s=m[o]={};s.root=A;s.isXMLDocument=this.isXML(w);
+s.brokenStarGEBTN=s.starSelectsClosedQSA=s.idGetsName=s.brokenMixedCaseQSA=s.brokenGEBCN=s.brokenCheckedQSA=s.brokenEmptyAttributeQSA=s.isHTMLDocument=s.nativeMatchesSelector=false;
+var q,u,y,z,t;var x,v="slick_uniqueid";var c=w.createElement("div");var n=w.body||w.getElementsByTagName("body")[0]||A;n.appendChild(c);try{c.innerHTML='<a id="'+v+'"></a>';
+s.isHTMLDocument=!!w.getElementById(v);}catch(C){}if(s.isHTMLDocument){c.style.display="none";c.appendChild(w.createComment(""));u=(c.getElementsByTagName("*").length>1);
+try{c.innerHTML="foo</foo>";x=c.getElementsByTagName("*");q=(x&&!!x.length&&x[0].nodeName.charAt(0)=="/");}catch(C){}s.brokenStarGEBTN=u||q;try{c.innerHTML='<a name="'+v+'"></a><b id="'+v+'"></b>';
+s.idGetsName=w.getElementById(v)===c.firstChild;}catch(C){}if(c.getElementsByClassName){try{c.innerHTML='<a class="f"></a><a class="b"></a>';c.getElementsByClassName("b").length;
+c.firstChild.className="b";z=(c.getElementsByClassName("b").length!=2);}catch(C){}try{c.innerHTML='<a class="a"></a><a class="f b a"></a>';y=(c.getElementsByClassName("a").length!=2);
+}catch(C){}s.brokenGEBCN=z||y;}if(c.querySelectorAll){try{c.innerHTML="foo</foo>";x=c.querySelectorAll("*");s.starSelectsClosedQSA=(x&&!!x.length&&x[0].nodeName.charAt(0)=="/");
+}catch(C){}try{c.innerHTML='<a class="MiX"></a>';s.brokenMixedCaseQSA=!c.querySelectorAll(".MiX").length;}catch(C){}try{c.innerHTML='<select><option selected="selected">a</option></select>';
+s.brokenCheckedQSA=(c.querySelectorAll(":checked").length==0);}catch(C){}try{c.innerHTML='<a class=""></a>';s.brokenEmptyAttributeQSA=(c.querySelectorAll('[class*=""]').length!=0);
+}catch(C){}}try{c.innerHTML='<form action="s"><input id="action"/></form>';t=(c.firstChild.getAttribute("action")!="s");}catch(C){}s.nativeMatchesSelector=A.matches||A.mozMatchesSelector||A.webkitMatchesSelector;
+if(s.nativeMatchesSelector){try{s.nativeMatchesSelector.call(A,":slick");s.nativeMatchesSelector=null;}catch(C){}}}try{A.slick_expando=1;delete A.slick_expando;
+s.getUID=this.getUIDHTML;}catch(C){s.getUID=this.getUIDXML;}n.removeChild(c);c=x=n=null;s.getAttribute=(s.isHTMLDocument&&t)?function(G,E){var H=this.attributeGetters[E];
+if(H){return H.call(G);}var F=G.getAttributeNode(E);return(F)?F.nodeValue:null;}:function(F,E){var G=this.attributeGetters[E];return(G)?G.call(F):F.getAttribute(E);
+};s.hasAttribute=(A&&this.isNativeCode(A.hasAttribute))?function(F,E){return F.hasAttribute(E);}:function(F,E){F=F.getAttributeNode(E);return !!(F&&(F.specified||F.nodeValue));
+};var D=A&&this.isNativeCode(A.contains),B=w&&this.isNativeCode(w.contains);s.contains=(D&&B)?function(E,F){return E.contains(F);}:(D&&!B)?function(E,F){return E===F||((E===w)?w.documentElement:E).contains(F);
+}:(A&&A.compareDocumentPosition)?function(E,F){return E===F||!!(E.compareDocumentPosition(F)&16);}:function(E,F){if(F){do{if(F===E){return true;}}while((F=F.parentNode));
+}return false;};s.documentSorter=(A.compareDocumentPosition)?function(F,E){if(!F.compareDocumentPosition||!E.compareDocumentPosition){return 0;}return F.compareDocumentPosition(E)&4?-1:F===E?0:1;
+}:("sourceIndex" in A)?function(F,E){if(!F.sourceIndex||!E.sourceIndex){return 0;}return F.sourceIndex-E.sourceIndex;}:(w.createRange)?function(H,F){if(!H.ownerDocument||!F.ownerDocument){return 0;
+}var G=H.ownerDocument.createRange(),E=F.ownerDocument.createRange();G.setStart(H,0);G.setEnd(H,0);E.setStart(F,0);E.setEnd(F,0);return G.compareBoundaryPoints(Range.START_TO_END,E);
+}:null;A=null;for(r in s){this[r]=s[r];}};var f=/^([#.]?)((?:[\w-]+|\*))$/,h=/\[.+[*$^]=(?:""|'')?\]/,g={};k.search=function(U,z,H,s){var p=this.found=(s)?null:(H||[]);
+if(!U){return p;}else{if(U.navigator){U=U.document;}else{if(!U.nodeType){return p;}}}var F,O,V=this.uniques={},I=!!(H&&H.length),y=(U.nodeType==9);if(this.document!==(y?U:U.ownerDocument)){this.setDocument(U);
+}if(I){for(O=p.length;O--;){V[this.getUID(p[O])]=true;}}if(typeof z=="string"){var r=z.match(f);simpleSelectors:if(r){var u=r[1],v=r[2],A,E;if(!u){if(v=="*"&&this.brokenStarGEBTN){break simpleSelectors;
+}E=U.getElementsByTagName(v);if(s){return E[0]||null;}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{if(u=="#"){if(!this.isHTMLDocument||!y){break simpleSelectors;
+}A=U.getElementById(v);if(!A){return p;}if(this.idGetsName&&A.getAttributeNode("id").nodeValue!=v){break simpleSelectors;}if(s){return A||null;}if(!(I&&V[this.getUID(A)])){p.push(A);
+}}else{if(u=="."){if(!this.isHTMLDocument||((!U.getElementsByClassName||this.brokenGEBCN)&&U.querySelectorAll)){break simpleSelectors;}if(U.getElementsByClassName&&!this.brokenGEBCN){E=U.getElementsByClassName(v);
+if(s){return E[0]||null;}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{var T=new RegExp("(^|\\s)"+e.escapeRegExp(v)+"(\\s|$)");E=U.getElementsByTagName("*");
+for(O=0;A=E[O++];){className=A.className;if(!(className&&T.test(className))){continue;}if(s){return A;}if(!(I&&V[this.getUID(A)])){p.push(A);}}}}}}if(I){this.sort(p);
+}return(s)?null:p;}querySelector:if(U.querySelectorAll){if(!this.isHTMLDocument||g[z]||this.brokenMixedCaseQSA||(this.brokenCheckedQSA&&z.indexOf(":checked")>-1)||(this.brokenEmptyAttributeQSA&&h.test(z))||(!y&&z.indexOf(",")>-1)||e.disableQSA){break querySelector;
+}var S=z,x=U;if(!y){var C=x.getAttribute("id"),t="slickid__";x.setAttribute("id",t);S="#"+t+" "+S;U=x.parentNode;}try{if(s){return U.querySelector(S)||null;
+}else{E=U.querySelectorAll(S);}}catch(Q){g[z]=1;break querySelector;}finally{if(!y){if(C){x.setAttribute("id",C);}else{x.removeAttribute("id");}U=x;}}if(this.starSelectsClosedQSA){for(O=0;
+A=E[O++];){if(A.nodeName>"@"&&!(I&&V[this.getUID(A)])){p.push(A);}}}else{for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}if(I){this.sort(p);
+}return p;}F=this.Slick.parse(z);if(!F.length){return p;}}else{if(z==null){return p;}else{if(z.Slick){F=z;}else{if(this.contains(U.documentElement||U,z)){(p)?p.push(z):p=z;
+return p;}else{return p;}}}}this.posNTH={};this.posNTHLast={};this.posNTHType={};this.posNTHTypeLast={};this.push=(!I&&(s||(F.length==1&&F.expressions[0].length==1)))?this.pushArray:this.pushUID;
+if(p==null){p=[];}var M,L,K;var B,J,D,c,q,G,W;var N,P,o,w,R=F.expressions;search:for(O=0;(P=R[O]);O++){for(M=0;(o=P[M]);M++){B="combinator:"+o.combinator;
+if(!this[B]){continue search;}J=(this.isXMLDocument)?o.tag:o.tag.toUpperCase();D=o.id;c=o.classList;q=o.classes;G=o.attributes;W=o.pseudos;w=(M===(P.length-1));
+this.bitUniques={};if(w){this.uniques=V;this.found=p;}else{this.uniques={};this.found=[];}if(M===0){this[B](U,J,D,q,G,W,c);if(s&&w&&p.length){break search;
+}}else{if(s&&w){for(L=0,K=N.length;L<K;L++){this[B](N[L],J,D,q,G,W,c);if(p.length){break search;}}}else{for(L=0,K=N.length;L<K;L++){this[B](N[L],J,D,q,G,W,c);
+}}}N=this.found;}}if(I||(F.expressions.length>1)){this.sort(p);}return(s)?(p[0]||null):p;};k.uidx=1;k.uidk="slick-uniqueid";k.getUIDXML=function(n){var c=n.getAttribute(this.uidk);
+if(!c){c=this.uidx++;n.setAttribute(this.uidk,c);}return c;};k.getUIDHTML=function(c){return c.uniqueNumber||(c.uniqueNumber=this.uidx++);};k.sort=function(c){if(!this.documentSorter){return c;
+}c.sort(this.documentSorter);return c;};k.cacheNTH={};k.matchNTH=/^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;k.parseNTHArgument=function(q){var o=q.match(this.matchNTH);
+if(!o){return false;}var p=o[2]||false;var n=o[1]||1;if(n=="-"){n=-1;}var c=+o[3]||0;o=(p=="n")?{a:n,b:c}:(p=="odd")?{a:2,b:1}:(p=="even")?{a:2,b:0}:{a:0,b:n};
+return(this.cacheNTH[q]=o);};k.createNTHPseudo=function(p,n,c,o){return function(s,q){var u=this.getUID(s);if(!this[c][u]){var A=s.parentNode;if(!A){return false;
+}var r=A[p],t=1;if(o){var z=s.nodeName;do{if(r.nodeName!=z){continue;}this[c][this.getUID(r)]=t++;}while((r=r[n]));}else{do{if(r.nodeType!=1){continue;
+}this[c][this.getUID(r)]=t++;}while((r=r[n]));}}q=q||"n";var v=this.cacheNTH[q]||this.parseNTHArgument(q);if(!v){return false;}var y=v.a,x=v.b,w=this[c][u];
+if(y==0){return x==w;}if(y>0){if(w<x){return false;}}else{if(x<w){return false;}}return((w-x)%y)==0;};};k.pushArray=function(p,c,r,o,n,q){if(this.matchSelector(p,c,r,o,n,q)){this.found.push(p);
+}};k.pushUID=function(q,c,s,p,n,r){var o=this.getUID(q);if(!this.uniques[o]&&this.matchSelector(q,c,s,p,n,r)){this.uniques[o]=true;this.found.push(q);}};
+k.matchNode=function(n,o){if(this.isHTMLDocument&&this.nativeMatchesSelector){try{return this.nativeMatchesSelector.call(n,o.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g,'[$1="$2"]'));
+}catch(u){}}var t=this.Slick.parse(o);if(!t){return true;}var r=t.expressions,s=0,q;for(q=0;(currentExpression=r[q]);q++){if(currentExpression.length==1){var p=currentExpression[0];
+if(this.matchSelector(n,(this.isXMLDocument)?p.tag:p.tag.toUpperCase(),p.id,p.classes,p.attributes,p.pseudos)){return true;}s++;}}if(s==t.length){return false;
+}var c=this.search(this.document,t),v;for(q=0;v=c[q++];){if(v===n){return true;}}return false;};k.matchPseudo=function(q,c,p){var n="pseudo:"+c;if(this[n]){return this[n](q,p);
+}var o=this.getAttribute(q,c);return(p)?p==o:!!o;};k.matchSelector=function(o,v,c,p,q,s){if(v){var t=(this.isXMLDocument)?o.nodeName:o.nodeName.toUpperCase();
+if(v=="*"){if(t<"@"){return false;}}else{if(t!=v){return false;}}}if(c&&o.getAttribute("id")!=c){return false;}var r,n,u;if(p){for(r=p.length;r--;){u=this.getAttribute(o,"class");
+if(!(u&&p[r].regexp.test(u))){return false;}}}if(q){for(r=q.length;r--;){n=q[r];if(n.operator?!n.test(this.getAttribute(o,n.key)):!this.hasAttribute(o,n.key)){return false;
+}}}if(s){for(r=s.length;r--;){n=s[r];if(!this.matchPseudo(o,n.key,n.value)){return false;}}}return true;};var j={" ":function(q,w,n,r,s,u,p){var t,v,o;
+if(this.isHTMLDocument){getById:if(n){v=this.document.getElementById(n);if((!v&&q.all)||(this.idGetsName&&v&&v.getAttributeNode("id").nodeValue!=n)){o=q.all[n];
+if(!o){return;}if(!o[0]){o=[o];}for(t=0;v=o[t++];){var c=v.getAttributeNode("id");if(c&&c.nodeValue==n){this.push(v,w,null,r,s,u);break;}}return;}if(!v){if(this.contains(this.root,q)){return;
+}else{break getById;}}else{if(this.document!==q&&!this.contains(q,v)){return;}}this.push(v,w,null,r,s,u);return;}getByClass:if(r&&q.getElementsByClassName&&!this.brokenGEBCN){o=q.getElementsByClassName(p.join(" "));
+if(!(o&&o.length)){break getByClass;}for(t=0;v=o[t++];){this.push(v,w,n,null,s,u);}return;}}getByTag:{o=q.getElementsByTagName(w);if(!(o&&o.length)){break getByTag;
+}if(!this.brokenStarGEBTN){w=null;}for(t=0;v=o[t++];){this.push(v,w,n,r,s,u);}}},">":function(p,c,r,o,n,q){if((p=p.firstChild)){do{if(p.nodeType==1){this.push(p,c,r,o,n,q);
+}}while((p=p.nextSibling));}},"+":function(p,c,r,o,n,q){while((p=p.nextSibling)){if(p.nodeType==1){this.push(p,c,r,o,n,q);break;}}},"^":function(p,c,r,o,n,q){p=p.firstChild;
+if(p){if(p.nodeType==1){this.push(p,c,r,o,n,q);}else{this["combinator:+"](p,c,r,o,n,q);}}},"~":function(q,c,s,p,n,r){while((q=q.nextSibling)){if(q.nodeType!=1){continue;
+}var o=this.getUID(q);if(this.bitUniques[o]){break;}this.bitUniques[o]=true;this.push(q,c,s,p,n,r);}},"++":function(p,c,r,o,n,q){this["combinator:+"](p,c,r,o,n,q);
+this["combinator:!+"](p,c,r,o,n,q);},"~~":function(p,c,r,o,n,q){this["combinator:~"](p,c,r,o,n,q);this["combinator:!~"](p,c,r,o,n,q);},"!":function(p,c,r,o,n,q){while((p=p.parentNode)){if(p!==this.document){this.push(p,c,r,o,n,q);
+}}},"!>":function(p,c,r,o,n,q){p=p.parentNode;if(p!==this.document){this.push(p,c,r,o,n,q);}},"!+":function(p,c,r,o,n,q){while((p=p.previousSibling)){if(p.nodeType==1){this.push(p,c,r,o,n,q);
+break;}}},"!^":function(p,c,r,o,n,q){p=p.lastChild;if(p){if(p.nodeType==1){this.push(p,c,r,o,n,q);}else{this["combinator:!+"](p,c,r,o,n,q);}}},"!~":function(q,c,s,p,n,r){while((q=q.previousSibling)){if(q.nodeType!=1){continue;
+}var o=this.getUID(q);if(this.bitUniques[o]){break;}this.bitUniques[o]=true;this.push(q,c,s,p,n,r);}}};for(var i in j){k["combinator:"+i]=j[i];}var l={empty:function(c){var n=c.firstChild;
+return !(n&&n.nodeType==1)&&!(c.innerText||c.textContent||"").length;},not:function(c,n){return !this.matchNode(c,n);},contains:function(c,n){return(c.innerText||c.textContent||"").indexOf(n)>-1;
+},"first-child":function(c){while((c=c.previousSibling)){if(c.nodeType==1){return false;}}return true;},"last-child":function(c){while((c=c.nextSibling)){if(c.nodeType==1){return false;
+}}return true;},"only-child":function(o){var n=o;while((n=n.previousSibling)){if(n.nodeType==1){return false;}}var c=o;while((c=c.nextSibling)){if(c.nodeType==1){return false;
+}}return true;},"nth-child":k.createNTHPseudo("firstChild","nextSibling","posNTH"),"nth-last-child":k.createNTHPseudo("lastChild","previousSibling","posNTHLast"),"nth-of-type":k.createNTHPseudo("firstChild","nextSibling","posNTHType",true),"nth-last-of-type":k.createNTHPseudo("lastChild","previousSibling","posNTHTypeLast",true),index:function(n,c){return this["pseudo:nth-child"](n,""+(c+1));
+},even:function(c){return this["pseudo:nth-child"](c,"2n");},odd:function(c){return this["pseudo:nth-child"](c,"2n+1");},"first-of-type":function(c){var n=c.nodeName;
+while((c=c.previousSibling)){if(c.nodeName==n){return false;}}return true;},"last-of-type":function(c){var n=c.nodeName;while((c=c.nextSibling)){if(c.nodeName==n){return false;
+}}return true;},"only-of-type":function(o){var n=o,p=o.nodeName;while((n=n.previousSibling)){if(n.nodeName==p){return false;}}var c=o;while((c=c.nextSibling)){if(c.nodeName==p){return false;
+}}return true;},enabled:function(c){return !c.disabled;},disabled:function(c){return c.disabled;},checked:function(c){return c.checked||c.selected;},focus:function(c){return this.isHTMLDocument&&this.document.activeElement===c&&(c.href||c.type||this.hasAttribute(c,"tabindex"));
+},root:function(c){return(c===this.root);},selected:function(c){return c.selected;}};for(var b in l){k["pseudo:"+b]=l[b];}var a=k.attributeGetters={"for":function(){return("htmlFor" in this)?this.htmlFor:this.getAttribute("for");
+},href:function(){return("href" in this)?this.getAttribute("href",2):this.getAttribute("href");},style:function(){return(this.style)?this.style.cssText:this.getAttribute("style");
+},tabindex:function(){var c=this.getAttributeNode("tabindex");return(c&&c.specified)?c.nodeValue:null;},type:function(){return this.getAttribute("type");
+},maxlength:function(){var c=this.getAttributeNode("maxLength");return(c&&c.specified)?c.nodeValue:null;}};a.MAXLENGTH=a.maxLength=a.maxlength;var e=k.Slick=(this.Slick||{});
+e.version="1.1.7";e.search=function(n,o,c){return k.search(n,o,c);};e.find=function(c,n){return k.search(c,n,null,true);};e.contains=function(c,n){k.setDocument(c);
+return k.contains(c,n);};e.getAttribute=function(n,c){k.setDocument(n);return k.getAttribute(n,c);};e.hasAttribute=function(n,c){k.setDocument(n);return k.hasAttribute(n,c);
+};e.match=function(n,c){if(!(n&&c)){return false;}if(!c||c===n){return true;}k.setDocument(n);return k.matchNode(n,c);};e.defineAttributeGetter=function(c,n){k.attributeGetters[c]=n;
+return this;};e.lookupAttributeGetter=function(c){return k.attributeGetters[c];};e.definePseudo=function(c,n){k["pseudo:"+c]=function(p,o){return n.call(p,o);
+};return this;};e.lookupPseudo=function(c){var n=k["pseudo:"+c];if(n){return function(o){return n.call(this,o);};}return null;};e.override=function(n,c){k.override(n,c);
+return this;};e.isXML=k.isXML;e.uidOf=function(c){return k.getUIDHTML(c);};if(!this.Slick){this.Slick=e;}}).apply((typeof exports!="undefined")?exports:this);
+var Element=this.Element=function(b,g){var h=Element.Constructors[b];if(h){return h(g);}if(typeof b!="string"){return document.id(b).set(g);}if(!g){g={};
+}if(!(/^[\w-]+$/).test(b)){var e=Slick.parse(b).expressions[0][0];b=(e.tag=="*")?"div":e.tag;if(e.id&&g.id==null){g.id=e.id;}var d=e.attributes;if(d){for(var a,f=0,c=d.length;
+f<c;f++){a=d[f];if(g[a.key]!=null){continue;}if(a.value!=null&&a.operator=="="){g[a.key]=a.value;}else{if(!a.value&&!a.operator){g[a.key]=true;}}}}if(e.classList&&g["class"]==null){g["class"]=e.classList.join(" ");
+}}return document.newElement(b,g);};if(Browser.Element){Element.prototype=Browser.Element.prototype;Element.prototype._fireEvent=(function(a){return function(b,c){return a.call(this,b,c);
+};})(Element.prototype.fireEvent);}new Type("Element",Element).mirror(function(a){if(Array.prototype[a]){return;}var b={};b[a]=function(){var h=[],e=arguments,j=true;
+for(var g=0,d=this.length;g<d;g++){var f=this[g],c=h[g]=f[a].apply(f,e);j=(j&&typeOf(c)=="element");}return(j)?new Elements(h):h;};Elements.implement(b);
+});if(!Browser.Element){Element.parent=Object;Element.Prototype={"$constructor":Element,"$family":Function.from("element").hide()};Element.mirror(function(a,b){Element.Prototype[a]=b;
+});}Element.Constructors={};var IFrame=new Type("IFrame",function(){var e=Array.link(arguments,{properties:Type.isObject,iframe:function(f){return(f!=null);
+}});var c=e.properties||{},b;if(e.iframe){b=document.id(e.iframe);}var d=c.onload||function(){};delete c.onload;c.id=c.name=[c.id,c.name,b?(b.id||b.name):"IFrame_"+String.uniqueID()].pick();
+b=new Element(b||"iframe",c);var a=function(){d.call(b.contentWindow);};if(window.frames[c.id]){a();}else{b.addListener("load",a);}return b;});var Elements=this.Elements=function(a){if(a&&a.length){var e={},d;
+for(var c=0;d=a[c++];){var b=Slick.uidOf(d);if(!e[b]){e[b]=true;this.push(d);}}}};Elements.prototype={length:0};Elements.parent=Array;new Type("Elements",Elements).implement({filter:function(a,b){if(!a){return this;
+}return new Elements(Array.filter(this,(typeOf(a)=="string")?function(c){return c.match(a);}:a,b));}.protect(),push:function(){var d=this.length;for(var b=0,a=arguments.length;
+b<a;b++){var c=document.id(arguments[b]);if(c){this[d++]=c;}}return(this.length=d);}.protect(),unshift:function(){var b=[];for(var c=0,a=arguments.length;
+c<a;c++){var d=document.id(arguments[c]);if(d){b.push(d);}}return Array.prototype.unshift.apply(this,b);}.protect(),concat:function(){var b=new Elements(this);
+for(var c=0,a=arguments.length;c<a;c++){var d=arguments[c];if(Type.isEnumerable(d)){b.append(d);}else{b.push(d);}}return b;}.protect(),append:function(c){for(var b=0,a=c.length;
+b<a;b++){this.push(c[b]);}return this;}.protect(),empty:function(){while(this.length){delete this[--this.length];}return this;}.protect()});(function(){var f=Array.prototype.splice,a={"0":0,"1":1,length:2};
+f.call(a,1,1);if(a[1]==1){Elements.implement("splice",function(){var g=this.length;var e=f.apply(this,arguments);while(g>=this.length){delete this[g--];
+}return e;}.protect());}Array.forEachMethod(function(g,e){Elements.implement(e,g);});Array.mirror(Elements);var d;try{d=(document.createElement("<input name=x>").name=="x");
+}catch(b){}var c=function(e){return(""+e).replace(/&/g,"&amp;").replace(/"/g,"&quot;");};Document.implement({newElement:function(e,g){if(g&&g.checked!=null){g.defaultChecked=g.checked;
+}if(d&&g){e="<"+e;if(g.name){e+=' name="'+c(g.name)+'"';}if(g.type){e+=' type="'+c(g.type)+'"';}e+=">";delete g.name;delete g.type;}return this.id(this.createElement(e)).set(g);
+}});})();(function(){Slick.uidOf(window);Slick.uidOf(document);Document.implement({newTextNode:function(e){return this.createTextNode(e);},getDocument:function(){return this;
+},getWindow:function(){return this.window;},id:(function(){var e={string:function(L,K,l){L=Slick.find(l,"#"+L.replace(/(\W)/g,"\\$1"));return(L)?e.element(L,K):null;
+},element:function(K,L){Slick.uidOf(K);if(!L&&!K.$family&&!(/^(?:object|embed)$/i).test(K.tagName)){var l=K.fireEvent;K._fireEvent=function(M,N){return l(M,N);
+};Object.append(K,Element.Prototype);}return K;},object:function(K,L,l){if(K.toElement){return e.element(K.toElement(l),L);}return null;}};e.textnode=e.whitespace=e.window=e.document=function(l){return l;
+};return function(K,M,L){if(K&&K.$family&&K.uniqueNumber){return K;}var l=typeOf(K);return(e[l])?e[l](K,M,L||document):null;};})()});if(window.$==null){Window.implement("$",function(e,l){return document.id(e,l,this.document);
+});}Window.implement({getDocument:function(){return this.document;},getWindow:function(){return this;}});[Document,Element].invoke("implement",{getElements:function(e){return Slick.search(this,e,new Elements);
+},getElement:function(e){return document.id(Slick.find(this,e));}});var p={contains:function(e){return Slick.contains(this,e);}};if(!document.contains){Document.implement(p);
+}if(!document.createElement("div").contains){Element.implement(p);}var v=function(L,K){if(!L){return K;}L=Object.clone(Slick.parse(L));var l=L.expressions;
+for(var e=l.length;e--;){l[e][0].combinator=K;}return L;};Object.forEach({getNext:"~",getPrevious:"!~",getParent:"!"},function(e,l){Element.implement(l,function(K){return this.getElement(v(K,e));
+});});Object.forEach({getAllNext:"~",getAllPrevious:"!~",getSiblings:"~~",getChildren:">",getParents:"!"},function(e,l){Element.implement(l,function(K){return this.getElements(v(K,e));
+});});Element.implement({getFirst:function(e){return document.id(Slick.search(this,v(e,">"))[0]);},getLast:function(e){return document.id(Slick.search(this,v(e,">")).getLast());
+},getWindow:function(){return this.ownerDocument.window;},getDocument:function(){return this.ownerDocument;},getElementById:function(e){return document.id(Slick.find(this,"#"+(""+e).replace(/(\W)/g,"\\$1")));
+},match:function(e){return !e||Slick.match(this,e);}});if(window.$$==null){Window.implement("$$",function(e){if(arguments.length==1){if(typeof e=="string"){return Slick.search(this.document,e,new Elements);
+}else{if(Type.isEnumerable(e)){return new Elements(e);}}}return new Elements(arguments);});}var A={before:function(l,e){var K=e.parentNode;if(K){K.insertBefore(l,e);
+}},after:function(l,e){var K=e.parentNode;if(K){K.insertBefore(l,e.nextSibling);}},bottom:function(l,e){e.appendChild(l);},top:function(l,e){e.insertBefore(l,e.firstChild);
+}};A.inside=A.bottom;var n={},d={};var o={};Array.forEach(["type","value","defaultValue","accessKey","cellPadding","cellSpacing","colSpan","frameBorder","rowSpan","tabIndex","useMap"],function(e){o[e.toLowerCase()]=e;
+});o.html="innerHTML";o.text=(document.createElement("div").textContent==null)?"innerText":"textContent";Object.forEach(o,function(l,e){d[e]=function(K,L){K[l]=L;
+};n[e]=function(K){return K[l];};});var B=["compact","nowrap","ismap","declare","noshade","checked","disabled","readOnly","multiple","selected","noresize","defer","defaultChecked","autofocus","controls","autoplay","loop"];
+var k={};Array.forEach(B,function(e){var l=e.toLowerCase();k[l]=e;d[l]=function(K,L){K[e]=!!L;};n[l]=function(K){return !!K[e];};});Object.append(d,{"class":function(e,l){("className" in e)?e.className=(l||""):e.setAttribute("class",l);
+},"for":function(e,l){("htmlFor" in e)?e.htmlFor=l:e.setAttribute("for",l);},style:function(e,l){(e.style)?e.style.cssText=l:e.setAttribute("style",l);
+},value:function(e,l){e.value=(l!=null)?l:"";}});n["class"]=function(e){return("className" in e)?e.className||null:e.getAttribute("class");};var f=document.createElement("button");
+try{f.type="button";}catch(E){}if(f.type!="button"){d.type=function(e,l){e.setAttribute("type",l);};}f=null;var s=document.createElement("input");s.value="t";
+s.type="submit";if(s.value!="t"){d.type=function(l,e){var K=l.value;l.type=e;l.value=K;};}s=null;var u=(function(e){e.random="attribute";return(e.getAttribute("random")=="attribute");
+})(document.createElement("div"));var i=(function(e){e.innerHTML='<object><param name="should_fix" value="the unknown"></object>';return e.cloneNode(true).firstChild.childNodes.length!=1;
+})(document.createElement("div"));var j=!!document.createElement("div").classList;var F=function(e){var l=(e||"").clean().split(" "),K={};return l.filter(function(L){if(L!==""&&!K[L]){return K[L]=L;
+}});};var t=function(e){this.classList.add(e);};var g=function(e){this.classList.remove(e);};Element.implement({setProperty:function(l,K){var L=d[l.toLowerCase()];
+if(L){L(this,K);}else{var e;if(u){e=this.retrieve("$attributeWhiteList",{});}if(K==null){this.removeAttribute(l);if(u){delete e[l];}}else{this.setAttribute(l,""+K);
+if(u){e[l]=true;}}}return this;},setProperties:function(e){for(var l in e){this.setProperty(l,e[l]);}return this;},getProperty:function(M){var K=n[M.toLowerCase()];
+if(K){return K(this);}if(u){var l=this.getAttributeNode(M),L=this.retrieve("$attributeWhiteList",{});if(!l){return null;}if(l.expando&&!L[M]){var N=this.outerHTML;
+if(N.substr(0,N.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(M)<0){return null;}L[M]=true;}}var e=Slick.getAttribute(this,M);return(!e&&!Slick.hasAttribute(this,M))?null:e;
+},getProperties:function(){var e=Array.from(arguments);return e.map(this.getProperty,this).associate(e);},removeProperty:function(e){return this.setProperty(e,null);
+},removeProperties:function(){Array.each(arguments,this.removeProperty,this);return this;},set:function(K,l){var e=Element.Properties[K];(e&&e.set)?e.set.call(this,l):this.setProperty(K,l);
+}.overloadSetter(),get:function(l){var e=Element.Properties[l];return(e&&e.get)?e.get.apply(this):this.getProperty(l);}.overloadGetter(),erase:function(l){var e=Element.Properties[l];
+(e&&e.erase)?e.erase.apply(this):this.removeProperty(l);return this;},hasClass:j?function(e){return this.classList.contains(e);}:function(e){return this.className.clean().contains(e," ");
+},addClass:j?function(e){F(e).forEach(t,this);return this;}:function(e){this.className=F(e+" "+this.className).join(" ");return this;},removeClass:j?function(e){F(e).forEach(g,this);
+return this;}:function(e){var l=F(this.className);F(e).forEach(l.erase,l);this.className=l.join(" ");return this;},toggleClass:function(e,l){if(l==null){l=!this.hasClass(e);
+}return(l)?this.addClass(e):this.removeClass(e);},adopt:function(){var L=this,e,N=Array.flatten(arguments),M=N.length;if(M>1){L=e=document.createDocumentFragment();
+}for(var K=0;K<M;K++){var l=document.id(N[K],true);if(l){L.appendChild(l);}}if(e){this.appendChild(e);}return this;},appendText:function(l,e){return this.grab(this.getDocument().newTextNode(l),e);
+},grab:function(l,e){A[e||"bottom"](document.id(l,true),this);return this;},inject:function(l,e){A[e||"bottom"](this,document.id(l,true));return this;},replaces:function(e){e=document.id(e,true);
+e.parentNode.replaceChild(this,e);return this;},wraps:function(l,e){l=document.id(l,true);return this.replaces(l).grab(l,e);},getSelected:function(){this.selectedIndex;
+return new Elements(Array.from(this.options).filter(function(e){return e.selected;}));},toQueryString:function(){var e=[];this.getElements("input, select, textarea").each(function(K){var l=K.type;
+if(!K.name||K.disabled||l=="submit"||l=="reset"||l=="file"||l=="image"){return;}var L=(K.get("tag")=="select")?K.getSelected().map(function(M){return document.id(M).get("value");
+}):((l=="radio"||l=="checkbox")&&!K.checked)?null:K.get("value");Array.from(L).each(function(M){if(typeof M!="undefined"){e.push(encodeURIComponent(K.name)+"="+encodeURIComponent(M));
+}});});return e.join("&");}});var I={before:"beforeBegin",after:"afterEnd",bottom:"beforeEnd",top:"afterBegin",inside:"beforeEnd"};Element.implement("appendHTML",("insertAdjacentHTML" in document.createElement("div"))?function(l,e){this.insertAdjacentHTML(I[e||"bottom"],l);
+return this;}:function(P,M){var K=new Element("div",{html:P}),O=K.childNodes,L=K.firstChild;if(!L){return this;}if(O.length>1){L=document.createDocumentFragment();
+for(var N=0,e=O.length;N<e;N++){L.appendChild(O[N]);}}A[M||"bottom"](L,this);return this;});var m={},D={};var G=function(e){return(D[e]||(D[e]={}));};var z=function(l){var e=l.uniqueNumber;
+if(l.removeEvents){l.removeEvents();}if(l.clearAttributes){l.clearAttributes();}if(e!=null){delete m[e];delete D[e];}return l;};var H={input:"checked",option:"selected",textarea:"value"};
+Element.implement({destroy:function(){var e=z(this).getElementsByTagName("*");Array.each(e,z);Element.dispose(this);return null;},empty:function(){Array.from(this.childNodes).each(Element.dispose);
+return this;},dispose:function(){return(this.parentNode)?this.parentNode.removeChild(this):this;},clone:function(N,L){N=N!==false;var S=this.cloneNode(N),K=[S],M=[this],Q;
+if(N){K.append(Array.from(S.getElementsByTagName("*")));M.append(Array.from(this.getElementsByTagName("*")));}for(Q=K.length;Q--;){var O=K[Q],R=M[Q];if(!L){O.removeAttribute("id");
+}if(O.clearAttributes){O.clearAttributes();O.mergeAttributes(R);O.removeAttribute("uniqueNumber");if(O.options){var V=O.options,e=R.options;for(var P=V.length;
+P--;){V[P].selected=e[P].selected;}}}var l=H[R.tagName.toLowerCase()];if(l&&R[l]){O[l]=R[l];}}if(i){var T=S.getElementsByTagName("object"),U=this.getElementsByTagName("object");
+for(Q=T.length;Q--;){T[Q].outerHTML=U[Q].outerHTML;}}return document.id(S);}});[Element,Window,Document].invoke("implement",{addListener:function(l,e){if(window.attachEvent&&!window.addEventListener){m[Slick.uidOf(this)]=this;
+}if(this.addEventListener){this.addEventListener(l,e,!!arguments[2]);}else{this.attachEvent("on"+l,e);}return this;},removeListener:function(l,e){if(this.removeEventListener){this.removeEventListener(l,e,!!arguments[2]);
+}else{this.detachEvent("on"+l,e);}return this;},retrieve:function(l,e){var L=G(Slick.uidOf(this)),K=L[l];if(e!=null&&K==null){K=L[l]=e;}return K!=null?K:null;
+},store:function(l,e){var K=G(Slick.uidOf(this));K[l]=e;return this;},eliminate:function(e){var l=G(Slick.uidOf(this));delete l[e];return this;}});if(window.attachEvent&&!window.addEventListener){var J=function(){Object.each(m,z);
+if(window.CollectGarbage){CollectGarbage();}window.removeListener("unload",J);};window.addListener("unload",J);}Element.Properties={};Element.Properties.style={set:function(e){this.style.cssText=e;
+},get:function(){return this.style.cssText;},erase:function(){this.style.cssText="";}};Element.Properties.tag={get:function(){return this.tagName.toLowerCase();
+}};Element.Properties.html={set:function(e){if(e==null){e="";}else{if(typeOf(e)=="array"){e=e.join("");}}this.innerHTML=e;},erase:function(){this.innerHTML="";
+}};var a=true,h=true,C=true;var x=document.createElement("div");x.innerHTML="<nav></nav>";a=(x.childNodes.length==1);if(!a){var w="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video".split(" "),b=document.createDocumentFragment(),y=w.length;
+while(y--){b.createElement(w[y]);}}x=null;h=Function.attempt(function(){var e=document.createElement("table");e.innerHTML="<tr><td></td></tr>";return true;
+});var c=document.createElement("tr"),r="<td></td>";c.innerHTML=r;C=(c.innerHTML==r);c=null;if(!h||!C||!a){Element.Properties.html.set=(function(l){var e={table:[1,"<table>","</table>"],select:[1,"<select>","</select>"],tbody:[2,"<table><tbody>","</tbody></table>"],tr:[3,"<table><tbody><tr>","</tr></tbody></table>"]};
+e.thead=e.tfoot=e.tbody;return function(K){var L=e[this.get("tag")];if(!L&&!a){L=[0,"",""];}if(!L){return l.call(this,K);}var O=L[0],N=document.createElement("div"),M=N;
+if(!a){b.appendChild(N);}N.innerHTML=[L[1],K,L[2]].flatten().join("");while(O--){M=M.firstChild;}this.empty().adopt(M.childNodes);if(!a){b.removeChild(N);
+}N=null;};})(Element.Properties.html.set);}var q=document.createElement("form");q.innerHTML="<select><option>s</option></select>";if(q.firstChild.value!="s"){Element.Properties.value={set:function(N){var l=this.get("tag");
+if(l!="select"){return this.setProperty("value",N);}var K=this.getElements("option");N=String(N);for(var L=0;L<K.length;L++){var M=K[L],e=M.getAttributeNode("value"),O=(e&&e.specified)?M.value:M.get("text");
+if(O===N){return M.selected=true;}}},get:function(){var K=this,l=K.get("tag");if(l!="select"&&l!="option"){return this.getProperty("value");}if(l=="select"&&!(K=K.getSelected()[0])){return"";
+}var e=K.getAttributeNode("value");return(e&&e.specified)?K.value:K.get("text");}};}q=null;if(document.createElement("div").getAttributeNode("id")){Element.Properties.id={set:function(e){this.id=this.getAttributeNode("id").value=e;
+},get:function(){return this.id||null;},erase:function(){this.id=this.getAttributeNode("id").value="";}};}})();(function(){var l=document.html,f;f=document.createElement("div");
+f.style.color="red";f.style.color=null;var e=f.style.color=="red";var k="1px solid #123abc";f.style.border=k;var o=f.style.border!=k;f=null;var n=!!window.getComputedStyle;
+Element.Properties.styles={set:function(r){this.setStyles(r);}};var j=(l.style.opacity!=null),g=(l.style.filter!=null),q=/alpha\(opacity=([\d.]+)\)/i;var b=function(s,r){s.store("$opacity",r);
+s.style.visibility=r>0||r==null?"visible":"hidden";};var p=function(r,v,u){var t=r.style,s=t.filter||r.getComputedStyle("filter")||"";t.filter=(v.test(s)?s.replace(v,u):s+" "+u).trim();
+if(!t.filter){t.removeAttribute("filter");}};var h=(j?function(s,r){s.style.opacity=r;}:(g?function(s,r){if(!s.currentStyle||!s.currentStyle.hasLayout){s.style.zoom=1;
+}if(r==null||r==1){p(s,q,"");if(r==1&&i(s)!=1){p(s,q,"alpha(opacity=100)");}}else{p(s,q,"alpha(opacity="+(r*100).limit(0,100).round()+")");}}:b));var i=(j?function(s){var r=s.style.opacity||s.getComputedStyle("opacity");
+return(r=="")?1:r.toFloat();}:(g?function(s){var t=(s.style.filter||s.getComputedStyle("filter")),r;if(t){r=t.match(q);}return(r==null||t==null)?1:(r[1]/100);
+}:function(s){var r=s.retrieve("$opacity");if(r==null){r=(s.style.visibility=="hidden"?0:1);}return r;}));var d=(l.style.cssFloat==null)?"styleFloat":"cssFloat",a={left:"0%",top:"0%",center:"50%",right:"100%",bottom:"100%"},c=(l.style.backgroundPositionX!=null);
+var m=function(r,s){if(s=="backgroundPosition"){r.removeAttribute(s+"X");s+="Y";}r.removeAttribute(s);};Element.implement({getComputedStyle:function(t){if(!n&&this.currentStyle){return this.currentStyle[t.camelCase()];
+}var s=Element.getDocument(this).defaultView,r=s?s.getComputedStyle(this,null):null;return(r)?r.getPropertyValue((t==d)?"float":t.hyphenate()):"";},setStyle:function(s,r){if(s=="opacity"){if(r!=null){r=parseFloat(r);
+}h(this,r);return this;}s=(s=="float"?d:s).camelCase();if(typeOf(r)!="string"){var t=(Element.Styles[s]||"@").split(" ");r=Array.from(r).map(function(v,u){if(!t[u]){return"";
+}return(typeOf(v)=="number")?t[u].replace("@",Math.round(v)):v;}).join(" ");}else{if(r==String(Number(r))){r=Math.round(r);}}this.style[s]=r;if((r==""||r==null)&&e&&this.style.removeAttribute){m(this.style,s);
+}return this;},getStyle:function(x){if(x=="opacity"){return i(this);}x=(x=="float"?d:x).camelCase();var r=this.style[x];if(!r||x=="zIndex"){if(Element.ShortStyles.hasOwnProperty(x)){r=[];
+for(var w in Element.ShortStyles[x]){r.push(this.getStyle(w));}return r.join(" ");}r=this.getComputedStyle(x);}if(c&&/^backgroundPosition[XY]?$/.test(x)){return r.replace(/(top|right|bottom|left)/g,function(s){return a[s];
+})||"0px";}if(!r&&x=="backgroundPosition"){return"0px 0px";}if(r){r=String(r);var u=r.match(/rgba?\([\d\s,]+\)/);if(u){r=r.replace(u[0],u[0].rgbToHex());
+}}if(!n&&!this.style[x]){if((/^(height|width)$/).test(x)&&!(/px$/.test(r))){var t=(x=="width")?["left","right"]:["top","bottom"],v=0;t.each(function(s){v+=this.getStyle("border-"+s+"-width").toInt()+this.getStyle("padding-"+s).toInt();
+},this);return this["offset"+x.capitalize()]-v+"px";}if((/^border(.+)Width|margin|padding/).test(x)&&isNaN(parseFloat(r))){return"0px";}}if(o&&/^border(Top|Right|Bottom|Left)?$/.test(x)&&/^#/.test(r)){return r.replace(/^(.+)\s(.+)\s(.+)$/,"$2 $3 $1");
+}return r;},setStyles:function(s){for(var r in s){this.setStyle(r,s[r]);}return this;},getStyles:function(){var r={};Array.flatten(arguments).each(function(s){r[s]=this.getStyle(s);
+},this);return r;}});Element.Styles={left:"@px",top:"@px",bottom:"@px",right:"@px",width:"@px",height:"@px",maxWidth:"@px",maxHeight:"@px",minWidth:"@px",minHeight:"@px",backgroundColor:"rgb(@, @, @)",backgroundSize:"@px",backgroundPosition:"@px @px",color:"rgb(@, @, @)",fontSize:"@px",letterSpacing:"@px",lineHeight:"@px",clip:"rect(@px @px @px @px)",margin:"@px @px @px @px",padding:"@px @px @px @px",border:"@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)",borderWidth:"@px @px @px @px",borderStyle:"@ @ @ @",borderColor:"rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)",zIndex:"@",zoom:"@",fontWeight:"@",textIndent:"@px",opacity:"@"};
+Element.ShortStyles={margin:{},padding:{},border:{},borderWidth:{},borderStyle:{},borderColor:{}};["Top","Right","Bottom","Left"].each(function(x){var w=Element.ShortStyles;
+var s=Element.Styles;["margin","padding"].each(function(y){var z=y+x;w[y][z]=s[z]="@px";});var v="border"+x;w.border[v]=s[v]="@px @ rgb(@, @, @)";var u=v+"Width",r=v+"Style",t=v+"Color";
+w[v]={};w.borderWidth[u]=w[v][u]=s[u]="@px";w.borderStyle[r]=w[v][r]=s[r]="@";w.borderColor[t]=w[v][t]=s[t]="rgb(@, @, @)";});if(c){Element.ShortStyles.backgroundPosition={backgroundPositionX:"@",backgroundPositionY:"@"};
+}})();(function(){Element.Properties.events={set:function(b){this.addEvents(b);}};[Element,Window,Document].invoke("implement",{addEvent:function(f,h){var i=this.retrieve("events",{});
+if(!i[f]){i[f]={keys:[],values:[]};}if(i[f].keys.contains(h)){return this;}i[f].keys.push(h);var g=f,b=Element.Events[f],d=h,j=this;if(b){if(b.onAdd){b.onAdd.call(this,h,f);
+}if(b.condition){d=function(k){if(b.condition.call(this,k,f)){return h.call(this,k);}return true;};}if(b.base){g=Function.from(b.base).call(this,f);}}var e=function(){return h.call(j);
+};var c=Element.NativeEvents[g];if(c){if(c==2){e=function(k){k=new DOMEvent(k,j.getWindow());if(d.call(j,k)===false){k.stop();}};}this.addListener(g,e,arguments[2]);
+}i[f].values.push(e);return this;},removeEvent:function(e,d){var c=this.retrieve("events");if(!c||!c[e]){return this;}var h=c[e];var b=h.keys.indexOf(d);
+if(b==-1){return this;}var g=h.values[b];delete h.keys[b];delete h.values[b];var f=Element.Events[e];if(f){if(f.onRemove){f.onRemove.call(this,d,e);}if(f.base){e=Function.from(f.base).call(this,e);
+}}return(Element.NativeEvents[e])?this.removeListener(e,g,arguments[2]):this;},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this;
+},removeEvents:function(b){var d;if(typeOf(b)=="object"){for(d in b){this.removeEvent(d,b[d]);}return this;}var c=this.retrieve("events");if(!c){return this;
+}if(!b){for(d in c){this.removeEvents(d);}this.eliminate("events");}else{if(c[b]){c[b].keys.each(function(e){this.removeEvent(b,e);},this);delete c[b];
+}}return this;},fireEvent:function(e,c,b){var d=this.retrieve("events");if(!d||!d[e]){return this;}c=Array.from(c);d[e].keys.each(function(f){if(b){f.delay(b,this,c);
+}else{f.apply(this,c);}},this);return this;},cloneEvents:function(e,d){e=document.id(e);var c=e.retrieve("events");if(!c){return this;}if(!d){for(var b in c){this.cloneEvents(e,b);
+}}else{if(c[d]){c[d].keys.each(function(f){this.addEvent(d,f);},this);}}return this;}});Element.NativeEvents={click:2,dblclick:2,mouseup:2,mousedown:2,contextmenu:2,mousewheel:2,DOMMouseScroll:2,mouseover:2,mouseout:2,mousemove:2,selectstart:2,selectend:2,keydown:2,keypress:2,keyup:2,orientationchange:2,touchstart:2,touchmove:2,touchend:2,touchcancel:2,gesturestart:2,gesturechange:2,gestureend:2,focus:2,blur:2,change:2,reset:2,select:2,submit:2,paste:2,input:2,load:2,unload:1,beforeunload:2,resize:1,move:1,DOMContentLoaded:1,readystatechange:1,hashchange:1,popstate:2,error:1,abort:1,scroll:1};
+Element.Events={mousewheel:{base:"onwheel" in document?"wheel":"onmousewheel" in document?"mousewheel":"DOMMouseScroll"}};var a=function(b){var c=b.relatedTarget;
+if(c==null){return true;}if(!c){return false;}return(c!=this&&c.prefix!="xul"&&typeOf(this)!="document"&&!this.contains(c));};if("onmouseenter" in document.documentElement){Element.NativeEvents.mouseenter=Element.NativeEvents.mouseleave=2;
+Element.MouseenterCheck=a;}else{Element.Events.mouseenter={base:"mouseover",condition:a};Element.Events.mouseleave={base:"mouseout",condition:a};}if(!window.addEventListener){Element.NativeEvents.propertychange=2;
+Element.Events.change={base:function(){var b=this.type;return(this.get("tag")=="input"&&(b=="radio"||b=="checkbox"))?"propertychange":"change";},condition:function(b){return b.type!="propertychange"||b.event.propertyName=="checked";
+}};}})();(function(){var c=!!window.addEventListener;Element.NativeEvents.focusin=Element.NativeEvents.focusout=2;var k=function(l,m,n,o,p){while(p&&p!=l){if(m(p,o)){return n.call(p,o,p);
+}p=document.id(p.parentNode);}};var a={mouseenter:{base:"mouseover",condition:Element.MouseenterCheck},mouseleave:{base:"mouseout",condition:Element.MouseenterCheck},focus:{base:"focus"+(c?"":"in"),capture:true},blur:{base:c?"blur":"focusout",capture:true}};
+var b="$delegation:";var i=function(l){return{base:"focusin",remove:function(m,o){var p=m.retrieve(b+l+"listeners",{})[o];if(p&&p.forms){for(var n=p.forms.length;
+n--;){p.forms[n].removeEvent(l,p.fns[n]);}}},listen:function(x,r,v,n,t,s){var o=(t.get("tag")=="form")?t:n.target.getParent("form");if(!o){return;}var u=x.retrieve(b+l+"listeners",{}),p=u[s]||{forms:[],fns:[]},m=p.forms,w=p.fns;
+if(m.indexOf(o)!=-1){return;}m.push(o);var q=function(y){k(x,r,v,y,t);};o.addEvent(l,q);w.push(q);u[s]=p;x.store(b+l+"listeners",u);}};};var d=function(l){return{base:"focusin",listen:function(m,n,p,q,r){var o={blur:function(){this.removeEvents(o);
+}};o[l]=function(s){k(m,n,p,s,r);};q.target.addEvents(o);}};};if(!c){Object.append(a,{submit:i("submit"),reset:i("reset"),change:d("change"),select:d("select")});
+}var h=Element.prototype,f=h.addEvent,j=h.removeEvent;var e=function(l,m){return function(r,q,n){if(r.indexOf(":relay")==-1){return l.call(this,r,q,n);
+}var o=Slick.parse(r).expressions[0][0];if(o.pseudos[0].key!="relay"){return l.call(this,r,q,n);}var p=o.tag;o.pseudos.slice(1).each(function(s){p+=":"+s.key+(s.value?"("+s.value+")":"");
+});l.call(this,r,q);return m.call(this,p,o.pseudos[0].value,q);};};var g={addEvent:function(v,q,x){var t=this.retrieve("$delegates",{}),r=t[v];if(r){for(var y in r){if(r[y].fn==x&&r[y].match==q){return this;
+}}}var p=v,u=q,o=x,n=a[v]||{};v=n.base||p;q=function(B){return Slick.match(B,u);};var w=Element.Events[p];if(n.condition||w&&w.condition){var l=q,m=n.condition||w.condition;
+q=function(C,B){return l(C,B)&&m.call(C,B,v);};}var z=this,s=String.uniqueID();var A=n.listen?function(B,C){if(!C&&B&&B.target){C=B.target;}if(C){n.listen(z,q,x,B,C,s);
+}}:function(B,C){if(!C&&B&&B.target){C=B.target;}if(C){k(z,q,x,B,C);}};if(!r){r={};}r[s]={match:u,fn:o,delegator:A};t[p]=r;return f.call(this,v,A,n.capture);
+},removeEvent:function(r,n,t,u){var q=this.retrieve("$delegates",{}),p=q[r];if(!p){return this;}if(u){var m=r,w=p[u].delegator,l=a[r]||{};r=l.base||m;if(l.remove){l.remove(this,u);
+}delete p[u];q[m]=p;return j.call(this,r,w,l.capture);}var o,v;if(t){for(o in p){v=p[o];if(v.match==n&&v.fn==t){return g.removeEvent.call(this,r,n,t,o);
+}}}else{for(o in p){v=p[o];if(v.match==n){g.removeEvent.call(this,r,n,v.fn,o);}}}return this;}};[Element,Window,Document].invoke("implement",{addEvent:e(f,g.addEvent),removeEvent:e(j,g.removeEvent)});
+})();(function(){var h=document.createElement("div"),e=document.createElement("div");h.style.height="0";h.appendChild(e);var d=(e.offsetParent===h);h=e=null;
+var l=function(m){return k(m,"position")!="static"||a(m);};var i=function(m){return l(m)||(/^(?:table|td|th)$/i).test(m.tagName);};Element.implement({scrollTo:function(m,n){if(a(this)){this.getWindow().scrollTo(m,n);
+}else{this.scrollLeft=m;this.scrollTop=n;}return this;},getSize:function(){if(a(this)){return this.getWindow().getSize();}return{x:this.offsetWidth,y:this.offsetHeight};
+},getScrollSize:function(){if(a(this)){return this.getWindow().getScrollSize();}return{x:this.scrollWidth,y:this.scrollHeight};},getScroll:function(){if(a(this)){return this.getWindow().getScroll();
+}return{x:this.scrollLeft,y:this.scrollTop};},getScrolls:function(){var n=this.parentNode,m={x:0,y:0};while(n&&!a(n)){m.x+=n.scrollLeft;m.y+=n.scrollTop;
+n=n.parentNode;}return m;},getOffsetParent:d?function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;}var n=(k(m,"position")=="static")?i:l;
+while((m=m.parentNode)){if(n(m)){return m;}}return null;}:function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;}try{return m.offsetParent;
+}catch(n){}return null;},getOffsets:function(){var n=this.getBoundingClientRect;if(n){var r=this.getBoundingClientRect(),p=document.id(this.getDocument().documentElement),q=p.getScroll(),t=this.getScrolls(),s=(k(this,"position")=="fixed");
+return{x:r.left.toInt()+t.x+((s)?0:q.x)-p.clientLeft,y:r.top.toInt()+t.y+((s)?0:q.y)-p.clientTop};}var o=this,m={x:0,y:0};if(a(this)){return m;}while(o&&!a(o)){m.x+=o.offsetLeft;
+m.y+=o.offsetTop;o=o.offsetParent;}return m;},getPosition:function(p){var q=this.getOffsets(),n=this.getScrolls();var m={x:q.x-n.x,y:q.y-n.y};if(p&&(p=document.id(p))){var o=p.getPosition();
+return{x:m.x-o.x-b(p),y:m.y-o.y-g(p)};}return m;},getCoordinates:function(o){if(a(this)){return this.getWindow().getCoordinates();}var m=this.getPosition(o),n=this.getSize();
+var p={left:m.x,top:m.y,width:n.x,height:n.y};p.right=p.left+p.width;p.bottom=p.top+p.height;return p;},computePosition:function(m){return{left:m.x-j(this,"margin-left"),top:m.y-j(this,"margin-top")};
+},setPosition:function(m){return this.setStyles(this.computePosition(m));}});[Document,Window].invoke("implement",{getSize:function(){var m=f(this);return{x:m.clientWidth,y:m.clientHeight};
+},getScroll:function(){var n=this.getWindow(),m=f(this);return{x:n.pageXOffset||m.scrollLeft,y:n.pageYOffset||m.scrollTop};},getScrollSize:function(){var o=f(this),n=this.getSize(),m=this.getDocument().body;
+return{x:Math.max(o.scrollWidth,m.scrollWidth,n.x),y:Math.max(o.scrollHeight,m.scrollHeight,n.y)};},getPosition:function(){return{x:0,y:0};},getCoordinates:function(){var m=this.getSize();
+return{top:0,left:0,bottom:m.y,right:m.x,height:m.y,width:m.x};}});var k=Element.getComputedStyle;function j(m,n){return k(m,n).toInt()||0;}function c(m){return k(m,"-moz-box-sizing")=="border-box";
+}function g(m){return j(m,"border-top-width");}function b(m){return j(m,"border-left-width");}function a(m){return(/^(?:body|html)$/i).test(m.tagName);
+}function f(m){var n=m.getDocument();return(!n.compatMode||n.compatMode=="CSS1Compat")?n.html:n.body;}})();Element.alias({position:"setPosition"});[Window,Document,Element].invoke("implement",{getHeight:function(){return this.getSize().y;
+},getWidth:function(){return this.getSize().x;},getScrollTop:function(){return this.getScroll().y;},getScrollLeft:function(){return this.getScroll().x;
+},getScrollHeight:function(){return this.getScrollSize().y;},getScrollWidth:function(){return this.getScrollSize().x;},getTop:function(){return this.getPosition().y;
+},getLeft:function(){return this.getPosition().x;}});(function(){var f=this.Fx=new Class({Implements:[Chain,Events,Options],options:{fps:60,unit:false,duration:500,frames:null,frameSkip:true,link:"ignore"},initialize:function(g){this.subject=this.subject||this;
+this.setOptions(g);},getTransition:function(){return function(g){return -(Math.cos(Math.PI*g)-1)/2;};},step:function(g){if(this.options.frameSkip){var h=(this.time!=null)?(g-this.time):0,i=h/this.frameInterval;
+this.time=g;this.frame+=i;}else{this.frame++;}if(this.frame<this.frames){var j=this.transition(this.frame/this.frames);this.set(this.compute(this.from,this.to,j));
+}else{this.frame=this.frames;this.set(this.compute(this.from,this.to,1));this.stop();}},set:function(g){return g;},compute:function(i,h,g){return f.compute(i,h,g);
+},check:function(){if(!this.isRunning()){return true;}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.pass(arguments,this));
+return false;}return false;},start:function(k,j){if(!this.check(k,j)){return this;}this.from=k;this.to=j;this.frame=(this.options.frameSkip)?0:-1;this.time=null;
+this.transition=this.getTransition();var i=this.options.frames,h=this.options.fps,g=this.options.duration;this.duration=f.Durations[g]||g.toInt();this.frameInterval=1000/h;
+this.frames=i||Math.round(this.duration/this.frameInterval);this.fireEvent("start",this.subject);b.call(this,h);return this;},stop:function(){if(this.isRunning()){this.time=null;
+d.call(this,this.options.fps);if(this.frames==this.frame){this.fireEvent("complete",this.subject);if(!this.callChain()){this.fireEvent("chainComplete",this.subject);
+}}else{this.fireEvent("stop",this.subject);}}return this;},cancel:function(){if(this.isRunning()){this.time=null;d.call(this,this.options.fps);this.frame=this.frames;
+this.fireEvent("cancel",this.subject).clearChain();}return this;},pause:function(){if(this.isRunning()){this.time=null;d.call(this,this.options.fps);}return this;
+},resume:function(){if(this.isPaused()){b.call(this,this.options.fps);}return this;},isRunning:function(){var g=e[this.options.fps];return g&&g.contains(this);
+},isPaused:function(){return(this.frame<this.frames)&&!this.isRunning();}});f.compute=function(i,h,g){return(h-i)*g+i;};f.Durations={"short":250,normal:500,"long":1000};
+var e={},c={};var a=function(){var h=Date.now();for(var j=this.length;j--;){var g=this[j];if(g){g.step(h);}}};var b=function(h){var g=e[h]||(e[h]=[]);g.push(this);
+if(!c[h]){c[h]=a.periodical(Math.round(1000/h),g);}};var d=function(h){var g=e[h];if(g){g.erase(this);if(!g.length&&c[h]){delete e[h];c[h]=clearInterval(c[h]);
+}}};})();Fx.CSS=new Class({Extends:Fx,prepare:function(b,e,a){a=Array.from(a);var h=a[0],g=a[1];if(g==null){g=h;h=b.getStyle(e);var c=this.options.unit;
+if(c&&h&&typeof h=="string"&&h.slice(-c.length)!=c&&parseFloat(h)!=0){b.setStyle(e,g+c);var d=b.getComputedStyle(e);if(!(/px$/.test(d))){d=b.style[("pixel-"+e).camelCase()];
+if(d==null){var f=b.style.left;b.style.left=g+c;d=b.style.pixelLeft;b.style.left=f;}}h=(g||1)/(parseFloat(d)||1)*(parseFloat(h)||0);b.setStyle(e,h+c);}}return{from:this.parse(h),to:this.parse(g)};
+},parse:function(a){a=Function.from(a)();a=(typeof a=="string")?a.split(" "):Array.from(a);return a.map(function(c){c=String(c);var b=false;Object.each(Fx.CSS.Parsers,function(f,e){if(b){return;
+}var d=f.parse(c);if(d||d===0){b={value:d,parser:f};}});b=b||{value:c,parser:Fx.CSS.Parsers.String};return b;});},compute:function(d,c,b){var a=[];(Math.min(d.length,c.length)).times(function(e){a.push({value:d[e].parser.compute(d[e].value,c[e].value,b),parser:d[e].parser});
+});a.$family=Function.from("fx:css:value");return a;},serve:function(c,b){if(typeOf(c)!="fx:css:value"){c=this.parse(c);}var a=[];c.each(function(d){a=a.concat(d.parser.serve(d.value,b));
+});return a;},render:function(a,d,c,b){a.setStyle(d,this.serve(c,b));},search:function(a){if(Fx.CSS.Cache[a]){return Fx.CSS.Cache[a];}var d={},c=new RegExp("^"+a.escapeRegExp()+"$");
+var b=function(e){Array.each(e,function(h,f){if(h.media){b(h.rules||h.cssRules);return;}if(!h.style){return;}var g=(h.selectorText)?h.selectorText.replace(/^\w+/,function(i){return i.toLowerCase();
+}):null;if(!g||!c.test(g)){return;}Object.each(Element.Styles,function(j,i){if(!h.style[i]||Element.ShortStyles[i]){return;}j=String(h.style[i]);d[i]=((/^rgb/).test(j))?j.rgbToHex():j;
+});});};Array.each(document.styleSheets,function(g,f){var e=g.href;if(e&&e.indexOf("://")>-1&&e.indexOf(document.domain)==-1){return;}var h=g.rules||g.cssRules;
+b(h);});return Fx.CSS.Cache[a]=d;}});Fx.CSS.Cache={};Fx.CSS.Parsers={Color:{parse:function(a){if(a.match(/^#[0-9a-f]{3,6}$/i)){return a.hexToRgb(true);
+}return((a=a.match(/(\d+),\s*(\d+),\s*(\d+)/)))?[a[1],a[2],a[3]]:false;},compute:function(c,b,a){return c.map(function(e,d){return Math.round(Fx.compute(c[d],b[d],a));
+});},serve:function(a){return a.map(Number);}},Number:{parse:parseFloat,compute:Fx.compute,serve:function(b,a){return(a)?b+a:b;}},String:{parse:Function.from(false),compute:function(b,a){return a;
+},serve:function(a){return a;}}};Fx.Tween=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=document.id(b);this.parent(a);},set:function(b,a){if(arguments.length==1){a=b;
+b=this.property||this.options.property;}this.render(this.element,b,a,this.options.unit);return this;},start:function(c,e,d){if(!this.check(c,e,d)){return this;
+}var b=Array.flatten(arguments);this.property=this.options.property||b.shift();var a=this.prepare(this.element,this.property,b);return this.parent(a.from,a.to);
+}});Element.Properties.tween={set:function(a){this.get("tween").cancel().setOptions(a);return this;},get:function(){var a=this.retrieve("tween");if(!a){a=new Fx.Tween(this,{link:"cancel"});
+this.store("tween",a);}return a;}};Element.implement({tween:function(a,c,b){this.get("tween").start(a,c,b);return this;},fade:function(d){var e=this.get("tween"),g,c=["opacity"].append(arguments),a;
+if(c[1]==null){c[1]="toggle";}switch(c[1]){case"in":g="start";c[1]=1;break;case"out":g="start";c[1]=0;break;case"show":g="set";c[1]=1;break;case"hide":g="set";
+c[1]=0;break;case"toggle":var b=this.retrieve("fade:flag",this.getStyle("opacity")==1);g="start";c[1]=b?0:1;this.store("fade:flag",!b);a=true;break;default:g="start";
+}if(!a){this.eliminate("fade:flag");}e[g].apply(e,c);var f=c[c.length-1];if(g=="set"||f!=0){this.setStyle("visibility",f==0?"hidden":"visible");}else{e.chain(function(){this.element.setStyle("visibility","hidden");
+this.callChain();});}return this;},highlight:function(c,a){if(!a){a=this.retrieve("highlight:original",this.getStyle("background-color"));a=(a=="transparent")?"#fff":a;
+}var b=this.get("tween");b.start("background-color",c||"#ffff88",a).chain(function(){this.setStyle("background-color",this.retrieve("highlight:original"));
+b.callChain();}.bind(this));return this;}});Fx.Morph=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=document.id(b);this.parent(a);
+},set:function(a){if(typeof a=="string"){a=this.search(a);}for(var b in a){this.render(this.element,b,a[b],this.options.unit);}return this;},compute:function(e,d,c){var a={};
+for(var b in e){a[b]=this.parent(e[b],d[b],c);}return a;},start:function(b){if(!this.check(b)){return this;}if(typeof b=="string"){b=this.search(b);}var e={},d={};
+for(var c in b){var a=this.prepare(this.element,c,b[c]);e[c]=a.from;d[c]=a.to;}return this.parent(e,d);}});Element.Properties.morph={set:function(a){this.get("morph").cancel().setOptions(a);
+return this;},get:function(){var a=this.retrieve("morph");if(!a){a=new Fx.Morph(this,{link:"cancel"});this.store("morph",a);}return a;}};Element.implement({morph:function(a){this.get("morph").start(a);
+return this;}});Fx.implement({getTransition:function(){var a=this.options.transition||Fx.Transitions.Sine.easeInOut;if(typeof a=="string"){var b=a.split(":");
+a=Fx.Transitions;a=a[b[0]]||a[b[0].capitalize()];if(b[1]){a=a["ease"+b[1].capitalize()+(b[2]?b[2].capitalize():"")];}}return a;}});Fx.Transition=function(c,b){b=Array.from(b);
+var a=function(d){return c(d,b);};return Object.append(a,{easeIn:a,easeOut:function(d){return 1-c(1-d,b);},easeInOut:function(d){return(d<=0.5?c(2*d,b):(2-c(2*(1-d),b)))/2;
+}});};Fx.Transitions={linear:function(a){return a;}};Fx.Transitions.extend=function(a){for(var b in a){Fx.Transitions[b]=new Fx.Transition(a[b]);}};Fx.Transitions.extend({Pow:function(b,a){return Math.pow(b,a&&a[0]||6);
+},Expo:function(a){return Math.pow(2,8*(a-1));},Circ:function(a){return 1-Math.sin(Math.acos(a));},Sine:function(a){return 1-Math.cos(a*Math.PI/2);},Back:function(b,a){a=a&&a[0]||1.618;
+return Math.pow(b,2)*((a+1)*b-a);},Bounce:function(f){var e;for(var d=0,c=1;1;d+=c,c/=2){if(f>=(7-4*d)/11){e=c*c-Math.pow((11-6*d-11*f)/4,2);break;}}return e;
+},Elastic:function(b,a){return Math.pow(2,10*--b)*Math.cos(20*b*Math.PI*(a&&a[0]||1)/3);}});["Quad","Cubic","Quart","Quint"].each(function(b,a){Fx.Transitions[b]=new Fx.Transition(function(c){return Math.pow(c,a+2);
+});});(function(){var d=function(){},a=("onprogress" in new Browser.Request);var c=this.Request=new Class({Implements:[Chain,Events,Options],options:{url:"",data:"",headers:{"X-Requested-With":"XMLHttpRequest",Accept:"text/javascript, text/html, application/xml, text/xml, */*"},async:true,format:false,method:"post",link:"ignore",isSuccess:null,emulation:true,urlEncoded:true,encoding:"utf-8",evalScripts:false,evalResponse:false,timeout:0,noCache:false},initialize:function(e){this.xhr=new Browser.Request();
+this.setOptions(e);this.headers=this.options.headers;},onStateChange:function(){var e=this.xhr;if(e.readyState!=4||!this.running){return;}this.running=false;
+this.status=0;Function.attempt(function(){var f=e.status;this.status=(f==1223)?204:f;}.bind(this));e.onreadystatechange=d;if(a){e.onprogress=e.onloadstart=d;
+}clearTimeout(this.timer);this.response={text:this.xhr.responseText||"",xml:this.xhr.responseXML};if(this.options.isSuccess.call(this,this.status)){this.success(this.response.text,this.response.xml);
+}else{this.failure();}},isSuccess:function(){var e=this.status;return(e>=200&&e<300);},isRunning:function(){return !!this.running;},processScripts:function(e){if(this.options.evalResponse||(/(ecma|java)script/).test(this.getHeader("Content-type"))){return Browser.exec(e);
+}return e.stripScripts(this.options.evalScripts);},success:function(f,e){this.onSuccess(this.processScripts(f),e);},onSuccess:function(){this.fireEvent("complete",arguments).fireEvent("success",arguments).callChain();
+},failure:function(){this.onFailure();},onFailure:function(){this.fireEvent("complete").fireEvent("failure",this.xhr);},loadstart:function(e){this.fireEvent("loadstart",[e,this.xhr]);
+},progress:function(e){this.fireEvent("progress",[e,this.xhr]);},timeout:function(){this.fireEvent("timeout",this.xhr);},setHeader:function(e,f){this.headers[e]=f;
+return this;},getHeader:function(e){return Function.attempt(function(){return this.xhr.getResponseHeader(e);}.bind(this));},check:function(){if(!this.running){return true;
+}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.pass(arguments,this));return false;}return false;},send:function(o){if(!this.check(o)){return this;
+}this.options.isSuccess=this.options.isSuccess||this.isSuccess;this.running=true;var l=typeOf(o);if(l=="string"||l=="element"){o={data:o};}var h=this.options;
+o=Object.append({data:h.data,url:h.url,method:h.method},o);var j=o.data,f=String(o.url),e=o.method.toLowerCase();switch(typeOf(j)){case"element":j=document.id(j).toQueryString();
+break;case"object":case"hash":j=Object.toQueryString(j);}if(this.options.format){var m="format="+this.options.format;j=(j)?m+"&"+j:m;}if(this.options.emulation&&!["get","post"].contains(e)){var k="_method="+e;
+j=(j)?k+"&"+j:k;e="post";}if(this.options.urlEncoded&&["post","put"].contains(e)){var g=(this.options.encoding)?"; charset="+this.options.encoding:"";this.headers["Content-type"]="application/x-www-form-urlencoded"+g;
+}if(!f){f=document.location.pathname;}var i=f.lastIndexOf("/");if(i>-1&&(i=f.indexOf("#"))>-1){f=f.substr(0,i);}if(this.options.noCache){f+=(f.indexOf("?")>-1?"&":"?")+String.uniqueID();
+}if(j&&(e=="get"||e=="delete")){f+=(f.indexOf("?")>-1?"&":"?")+j;j=null;}var n=this.xhr;if(a){n.onloadstart=this.loadstart.bind(this);n.onprogress=this.progress.bind(this);
+}n.open(e.toUpperCase(),f,this.options.async,this.options.user,this.options.password);if(this.options.user&&"withCredentials" in n){n.withCredentials=true;
+}n.onreadystatechange=this.onStateChange.bind(this);Object.each(this.headers,function(q,p){try{n.setRequestHeader(p,q);}catch(r){this.fireEvent("exception",[p,q]);
+}},this);this.fireEvent("request");n.send(j);if(!this.options.async){this.onStateChange();}else{if(this.options.timeout){this.timer=this.timeout.delay(this.options.timeout,this);
+}}return this;},cancel:function(){if(!this.running){return this;}this.running=false;var e=this.xhr;e.abort();clearTimeout(this.timer);e.onreadystatechange=d;
+if(a){e.onprogress=e.onloadstart=d;}this.xhr=new Browser.Request();this.fireEvent("cancel");return this;}});var b={};["get","post","put","delete","GET","POST","PUT","DELETE"].each(function(e){b[e]=function(g){var f={method:e};
+if(g!=null){f.data=g;}return this.send(f);};});c.implement(b);Element.Properties.send={set:function(e){var f=this.get("send").cancel();f.setOptions(e);
+return this;},get:function(){var e=this.retrieve("send");if(!e){e=new c({data:this,link:"cancel",method:this.get("method")||"post",url:this.get("action")});
+this.store("send",e);}return e;}};Element.implement({send:function(e){var f=this.get("send");f.send({data:this,url:e||f.options.url});return this;}});})();
+Request.HTML=new Class({Extends:Request,options:{update:false,append:false,evalScripts:true,filter:false,headers:{Accept:"text/html, application/xml, text/xml, */*"}},success:function(f){var e=this.options,c=this.response;
+c.html=f.stripScripts(function(h){c.javascript=h;});var d=c.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);if(d){c.html=d[1];}var b=new Element("div").set("html",c.html);
+c.tree=b.childNodes;c.elements=b.getElements(e.filter||"*");if(e.filter){c.tree=c.elements;}if(e.update){var g=document.id(e.update).empty();if(e.filter){g.adopt(c.elements);
+}else{g.set("html",c.html);}}else{if(e.append){var a=document.id(e.append);if(e.filter){c.elements.reverse().inject(a);}else{a.adopt(b.getChildren());}}}if(e.evalScripts){Browser.exec(c.javascript);
+}this.onSuccess(c.tree,c.elements,c.html,c.javascript);}});Element.Properties.load={set:function(a){var b=this.get("load").cancel();b.setOptions(a);return this;
+},get:function(){var a=this.retrieve("load");if(!a){a=new Request.HTML({data:this,link:"cancel",update:this,method:"get"});this.store("load",a);}return a;
+}};Element.implement({load:function(){this.get("load").send(Array.link(arguments,{data:Type.isObject,url:Type.isString}));return this;}});if(typeof JSON=="undefined"){this.JSON={};
+}(function(){var special={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};var escape=function(chr){return special[chr]||"\\u"+("0000"+chr.charCodeAt(0).toString(16)).slice(-4);
+};JSON.validate=function(string){string=string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"");
+return(/^[\],:{}\s]*$/).test(string);};JSON.encode=JSON.stringify?function(obj){return JSON.stringify(obj);}:function(obj){if(obj&&obj.toJSON){obj=obj.toJSON();
+}switch(typeOf(obj)){case"string":return'"'+obj.replace(/[\x00-\x1f\\"]/g,escape)+'"';case"array":return"["+obj.map(JSON.encode).clean()+"]";case"object":case"hash":var string=[];
+Object.each(obj,function(value,key){var json=JSON.encode(value);if(json){string.push(JSON.encode(key)+":"+json);}});return"{"+string+"}";case"number":case"boolean":return""+obj;
+case"null":return"null";}return null;};JSON.secure=true;JSON.decode=function(string,secure){if(!string||typeOf(string)!="string"){return null;}if(secure==null){secure=JSON.secure;
+}if(secure){if(JSON.parse){return JSON.parse(string);}if(!JSON.validate(string)){throw new Error("JSON could not decode the input; security is enabled and the value is not secure.");
+}}return eval("("+string+")");};})();Request.JSON=new Class({Extends:Request,options:{secure:true},initialize:function(a){this.parent(a);Object.append(this.headers,{Accept:"application/json","X-Request":"JSON"});
+},success:function(c){var b;try{b=this.response.json=JSON.decode(c,this.options.secure);}catch(a){this.fireEvent("error",[c,a]);return;}if(b==null){this.onFailure();
+}else{this.onSuccess(b,c);}}});var Cookie=new Class({Implements:Options,options:{path:"/",domain:false,duration:false,secure:false,document:document,encode:true},initialize:function(b,a){this.key=b;
+this.setOptions(a);},write:function(b){if(this.options.encode){b=encodeURIComponent(b);}if(this.options.domain){b+="; domain="+this.options.domain;}if(this.options.path){b+="; path="+this.options.path;
+}if(this.options.duration){var a=new Date();a.setTime(a.getTime()+this.options.duration*24*60*60*1000);b+="; expires="+a.toGMTString();}if(this.options.secure){b+="; secure";
+}this.options.document.cookie=this.key+"="+b;return this;},read:function(){var a=this.options.document.cookie.match("(?:^|;)\\s*"+this.key.escapeRegExp()+"=([^;]*)");
+return(a)?decodeURIComponent(a[1]):null;},dispose:function(){new Cookie(this.key,Object.merge({},this.options,{duration:-1})).write("");return this;}});
+Cookie.write=function(b,c,a){return new Cookie(b,a).write(c);};Cookie.read=function(a){return new Cookie(a).read();};Cookie.dispose=function(b,a){return new Cookie(b,a).dispose();
+};(function(i,k){var l,f,e=[],c,b,d=k.createElement("div");var g=function(){clearTimeout(b);if(l){return;}Browser.loaded=l=true;k.removeListener("DOMContentLoaded",g).removeListener("readystatechange",a);
+k.fireEvent("domready");i.fireEvent("domready");};var a=function(){for(var m=e.length;m--;){if(e[m]()){g();return true;}}return false;};var j=function(){clearTimeout(b);
+if(!a()){b=setTimeout(j,10);}};k.addListener("DOMContentLoaded",g);var h=function(){try{d.doScroll();return true;}catch(m){}return false;};if(d.doScroll&&!h()){e.push(h);
+c=true;}if(k.readyState){e.push(function(){var m=k.readyState;return(m=="loaded"||m=="complete");});}if("onreadystatechange" in k){k.addListener("readystatechange",a);
+}else{c=true;}if(c){j();}Element.Events.domready={onAdd:function(m){if(l){m.call(this);}}};Element.Events.load={base:"load",onAdd:function(m){if(f&&this==i){m.call(this);
+}},condition:function(){if(this==i){g();delete Element.Events.load;}return true;}};i.addEvent("load",function(){f=true;});})(window,document); \ No newline at end of file
diff --git a/module/webui/themes/Flat/js/static/mootools-more.js b/module/webui/themes/Flat/js/static/mootools-more.js
new file mode 100644
index 000000000..c7f4a1a0e
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/mootools-more.js
@@ -0,0 +1,2856 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/more/c1cc18c2fff04bcc58921b4dff80a6f1
+
+packager build:
+ - packager build More/Form.Request More/Fx.Reveal More/Sortables More/Request.Periodical More/Color
+
+...
+*/
+
+/*
+---
+
+script: More.js
+
+name: More
+
+description: MooTools More
+
+license: MIT-style license
+
+authors:
+ - Guillermo Rauch
+ - Thomas Aylott
+ - Scott Kyle
+ - Arian Stolwijk
+ - Tim Wienk
+ - Christoph Pojer
+ - Aaron Newton
+ - Jacob Thornton
+
+requires:
+ - Core/MooTools
+
+provides: [MooTools.More]
+
+...
+*/
+
+MooTools.More = {
+ version: '1.5.0',
+ build: '73db5e24e6e9c5c87b3a27aebef2248053f7db37'
+};
+
+
+/*
+---
+
+script: Class.Binds.js
+
+name: Class.Binds
+
+description: Automagically binds specified methods in a class to the instance of the class.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Class
+ - MooTools.More
+
+provides: [Class.Binds]
+
+...
+*/
+
+Class.Mutators.Binds = function(binds){
+ if (!this.prototype.initialize) this.implement('initialize', function(){});
+ return Array.from(binds).concat(this.prototype.Binds || []);
+};
+
+Class.Mutators.initialize = function(initialize){
+ return function(){
+ Array.from(this.Binds).each(function(name){
+ var original = this[name];
+ if (original) this[name] = original.bind(this);
+ }, this);
+ return initialize.apply(this, arguments);
+ };
+};
+
+
+/*
+---
+
+script: Class.Occlude.js
+
+name: Class.Occlude
+
+description: Prevents a class from being applied to a DOM element twice.
+
+license: MIT-style license.
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Class
+ - Core/Element
+ - MooTools.More
+
+provides: [Class.Occlude]
+
+...
+*/
+
+Class.Occlude = new Class({
+
+ occlude: function(property, element){
+ element = document.id(element || this.element);
+ var instance = element.retrieve(property || this.property);
+ if (instance && !this.occluded)
+ return (this.occluded = instance);
+
+ this.occluded = false;
+ element.store(property || this.property, this);
+ return this.occluded;
+ }
+
+});
+
+
+/*
+---
+
+script: Class.Refactor.js
+
+name: Class.Refactor
+
+description: Extends a class onto itself with new property, preserving any items attached to the class's namespace.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Class
+ - MooTools.More
+
+# Some modules declare themselves dependent on Class.Refactor
+provides: [Class.refactor, Class.Refactor]
+
+...
+*/
+
+Class.refactor = function(original, refactors){
+
+ Object.each(refactors, function(item, name){
+ var origin = original.prototype[name];
+ origin = (origin && origin.$origin) || origin || function(){};
+ original.implement(name, (typeof item == 'function') ? function(){
+ var old = this.previous;
+ this.previous = origin;
+ var value = item.apply(this, arguments);
+ this.previous = old;
+ return value;
+ } : item);
+ });
+
+ return original;
+
+};
+
+
+/*
+---
+
+script: Element.Measure.js
+
+name: Element.Measure
+
+description: Extends the Element native object to include methods useful in measuring dimensions.
+
+credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Element.Style
+ - Core/Element.Dimensions
+ - MooTools.More
+
+provides: [Element.Measure]
+
+...
+*/
+
+(function(){
+
+var getStylesList = function(styles, planes){
+ var list = [];
+ Object.each(planes, function(directions){
+ Object.each(directions, function(edge){
+ styles.each(function(style){
+ list.push(style + '-' + edge + (style == 'border' ? '-width' : ''));
+ });
+ });
+ });
+ return list;
+};
+
+var calculateEdgeSize = function(edge, styles){
+ var total = 0;
+ Object.each(styles, function(value, style){
+ if (style.test(edge)) total = total + value.toInt();
+ });
+ return total;
+};
+
+var isVisible = function(el){
+ return !!(!el || el.offsetHeight || el.offsetWidth);
+};
+
+
+Element.implement({
+
+ measure: function(fn){
+ if (isVisible(this)) return fn.call(this);
+ var parent = this.getParent(),
+ toMeasure = [];
+ while (!isVisible(parent) && parent != document.body){
+ toMeasure.push(parent.expose());
+ parent = parent.getParent();
+ }
+ var restore = this.expose(),
+ result = fn.call(this);
+ restore();
+ toMeasure.each(function(restore){
+ restore();
+ });
+ return result;
+ },
+
+ expose: function(){
+ if (this.getStyle('display') != 'none') return function(){};
+ var before = this.style.cssText;
+ this.setStyles({
+ display: 'block',
+ position: 'absolute',
+ visibility: 'hidden'
+ });
+ return function(){
+ this.style.cssText = before;
+ }.bind(this);
+ },
+
+ getDimensions: function(options){
+ options = Object.merge({computeSize: false}, options);
+ var dim = {x: 0, y: 0};
+
+ var getSize = function(el, options){
+ return (options.computeSize) ? el.getComputedSize(options) : el.getSize();
+ };
+
+ var parent = this.getParent('body');
+
+ if (parent && this.getStyle('display') == 'none'){
+ dim = this.measure(function(){
+ return getSize(this, options);
+ });
+ } else if (parent){
+ try { //safari sometimes crashes here, so catch it
+ dim = getSize(this, options);
+ }catch(e){}
+ }
+
+ return Object.append(dim, (dim.x || dim.x === 0) ? {
+ width: dim.x,
+ height: dim.y
+ } : {
+ x: dim.width,
+ y: dim.height
+ }
+ );
+ },
+
+ getComputedSize: function(options){
+
+
+ options = Object.merge({
+ styles: ['padding','border'],
+ planes: {
+ height: ['top','bottom'],
+ width: ['left','right']
+ },
+ mode: 'both'
+ }, options);
+
+ var styles = {},
+ size = {width: 0, height: 0},
+ dimensions;
+
+ if (options.mode == 'vertical'){
+ delete size.width;
+ delete options.planes.width;
+ } else if (options.mode == 'horizontal'){
+ delete size.height;
+ delete options.planes.height;
+ }
+
+ getStylesList(options.styles, options.planes).each(function(style){
+ styles[style] = this.getStyle(style).toInt();
+ }, this);
+
+ Object.each(options.planes, function(edges, plane){
+
+ var capitalized = plane.capitalize(),
+ style = this.getStyle(plane);
+
+ if (style == 'auto' && !dimensions) dimensions = this.getDimensions();
+
+ style = styles[plane] = (style == 'auto') ? dimensions[plane] : style.toInt();
+ size['total' + capitalized] = style;
+
+ edges.each(function(edge){
+ var edgesize = calculateEdgeSize(edge, styles);
+ size['computed' + edge.capitalize()] = edgesize;
+ size['total' + capitalized] += edgesize;
+ });
+
+ }, this);
+
+ return Object.append(size, styles);
+ }
+
+});
+
+})();
+
+
+/*
+---
+
+script: Element.Position.js
+
+name: Element.Position
+
+description: Extends the Element native object to include methods useful positioning elements relative to others.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+ - Jacob Thornton
+
+requires:
+ - Core/Options
+ - Core/Element.Dimensions
+ - Element.Measure
+
+provides: [Element.Position]
+
+...
+*/
+
+(function(original){
+
+var local = Element.Position = {
+
+ options: {/*
+ edge: false,
+ returnPos: false,
+ minimum: {x: 0, y: 0},
+ maximum: {x: 0, y: 0},
+ relFixedPosition: false,
+ ignoreMargins: false,
+ ignoreScroll: false,
+ allowNegative: false,*/
+ relativeTo: document.body,
+ position: {
+ x: 'center', //left, center, right
+ y: 'center' //top, center, bottom
+ },
+ offset: {x: 0, y: 0}
+ },
+
+ getOptions: function(element, options){
+ options = Object.merge({}, local.options, options);
+ local.setPositionOption(options);
+ local.setEdgeOption(options);
+ local.setOffsetOption(element, options);
+ local.setDimensionsOption(element, options);
+ return options;
+ },
+
+ setPositionOption: function(options){
+ options.position = local.getCoordinateFromValue(options.position);
+ },
+
+ setEdgeOption: function(options){
+ var edgeOption = local.getCoordinateFromValue(options.edge);
+ options.edge = edgeOption ? edgeOption :
+ (options.position.x == 'center' && options.position.y == 'center') ? {x: 'center', y: 'center'} :
+ {x: 'left', y: 'top'};
+ },
+
+ setOffsetOption: function(element, options){
+ var parentOffset = {x: 0, y: 0};
+ var parentScroll = {x: 0, y: 0};
+ var offsetParent = element.measure(function(){
+ return document.id(this.getOffsetParent());
+ });
+
+ if (!offsetParent || offsetParent == element.getDocument().body) return;
+
+ parentScroll = offsetParent.getScroll();
+ parentOffset = offsetParent.measure(function(){
+ var position = this.getPosition();
+ if (this.getStyle('position') == 'fixed'){
+ var scroll = window.getScroll();
+ position.x += scroll.x;
+ position.y += scroll.y;
+ }
+ return position;
+ });
+
+ options.offset = {
+ parentPositioned: offsetParent != document.id(options.relativeTo),
+ x: options.offset.x - parentOffset.x + parentScroll.x,
+ y: options.offset.y - parentOffset.y + parentScroll.y
+ };
+ },
+
+ setDimensionsOption: function(element, options){
+ options.dimensions = element.getDimensions({
+ computeSize: true,
+ styles: ['padding', 'border', 'margin']
+ });
+ },
+
+ getPosition: function(element, options){
+ var position = {};
+ options = local.getOptions(element, options);
+ var relativeTo = document.id(options.relativeTo) || document.body;
+
+ local.setPositionCoordinates(options, position, relativeTo);
+ if (options.edge) local.toEdge(position, options);
+
+ var offset = options.offset;
+ position.left = ((position.x >= 0 || offset.parentPositioned || options.allowNegative) ? position.x : 0).toInt();
+ position.top = ((position.y >= 0 || offset.parentPositioned || options.allowNegative) ? position.y : 0).toInt();
+
+ local.toMinMax(position, options);
+
+ if (options.relFixedPosition || relativeTo.getStyle('position') == 'fixed') local.toRelFixedPosition(relativeTo, position);
+ if (options.ignoreScroll) local.toIgnoreScroll(relativeTo, position);
+ if (options.ignoreMargins) local.toIgnoreMargins(position, options);
+
+ position.left = Math.ceil(position.left);
+ position.top = Math.ceil(position.top);
+ delete position.x;
+ delete position.y;
+
+ return position;
+ },
+
+ setPositionCoordinates: function(options, position, relativeTo){
+ var offsetY = options.offset.y,
+ offsetX = options.offset.x,
+ calc = (relativeTo == document.body) ? window.getScroll() : relativeTo.getPosition(),
+ top = calc.y,
+ left = calc.x,
+ winSize = window.getSize();
+
+ switch(options.position.x){
+ case 'left': position.x = left + offsetX; break;
+ case 'right': position.x = left + offsetX + relativeTo.offsetWidth; break;
+ default: position.x = left + ((relativeTo == document.body ? winSize.x : relativeTo.offsetWidth) / 2) + offsetX; break;
+ }
+
+ switch(options.position.y){
+ case 'top': position.y = top + offsetY; break;
+ case 'bottom': position.y = top + offsetY + relativeTo.offsetHeight; break;
+ default: position.y = top + ((relativeTo == document.body ? winSize.y : relativeTo.offsetHeight) / 2) + offsetY; break;
+ }
+ },
+
+ toMinMax: function(position, options){
+ var xy = {left: 'x', top: 'y'}, value;
+ ['minimum', 'maximum'].each(function(minmax){
+ ['left', 'top'].each(function(lr){
+ value = options[minmax] ? options[minmax][xy[lr]] : null;
+ if (value != null && ((minmax == 'minimum') ? position[lr] < value : position[lr] > value)) position[lr] = value;
+ });
+ });
+ },
+
+ toRelFixedPosition: function(relativeTo, position){
+ var winScroll = window.getScroll();
+ position.top += winScroll.y;
+ position.left += winScroll.x;
+ },
+
+ toIgnoreScroll: function(relativeTo, position){
+ var relScroll = relativeTo.getScroll();
+ position.top -= relScroll.y;
+ position.left -= relScroll.x;
+ },
+
+ toIgnoreMargins: function(position, options){
+ position.left += options.edge.x == 'right'
+ ? options.dimensions['margin-right']
+ : (options.edge.x != 'center'
+ ? -options.dimensions['margin-left']
+ : -options.dimensions['margin-left'] + ((options.dimensions['margin-right'] + options.dimensions['margin-left']) / 2));
+
+ position.top += options.edge.y == 'bottom'
+ ? options.dimensions['margin-bottom']
+ : (options.edge.y != 'center'
+ ? -options.dimensions['margin-top']
+ : -options.dimensions['margin-top'] + ((options.dimensions['margin-bottom'] + options.dimensions['margin-top']) / 2));
+ },
+
+ toEdge: function(position, options){
+ var edgeOffset = {},
+ dimensions = options.dimensions,
+ edge = options.edge;
+
+ switch(edge.x){
+ case 'left': edgeOffset.x = 0; break;
+ case 'right': edgeOffset.x = -dimensions.x - dimensions.computedRight - dimensions.computedLeft; break;
+ // center
+ default: edgeOffset.x = -(Math.round(dimensions.totalWidth / 2)); break;
+ }
+
+ switch(edge.y){
+ case 'top': edgeOffset.y = 0; break;
+ case 'bottom': edgeOffset.y = -dimensions.y - dimensions.computedTop - dimensions.computedBottom; break;
+ // center
+ default: edgeOffset.y = -(Math.round(dimensions.totalHeight / 2)); break;
+ }
+
+ position.x += edgeOffset.x;
+ position.y += edgeOffset.y;
+ },
+
+ getCoordinateFromValue: function(option){
+ if (typeOf(option) != 'string') return option;
+ option = option.toLowerCase();
+
+ return {
+ x: option.test('left') ? 'left'
+ : (option.test('right') ? 'right' : 'center'),
+ y: option.test(/upper|top/) ? 'top'
+ : (option.test('bottom') ? 'bottom' : 'center')
+ };
+ }
+
+};
+
+Element.implement({
+
+ position: function(options){
+ if (options && (options.x != null || options.y != null)){
+ return (original ? original.apply(this, arguments) : this);
+ }
+ var position = this.setStyle('position', 'absolute').calculatePosition(options);
+ return (options && options.returnPos) ? position : this.setStyles(position);
+ },
+
+ calculatePosition: function(options){
+ return local.getPosition(this, options);
+ }
+
+});
+
+})(Element.prototype.position);
+
+
+/*
+---
+
+script: IframeShim.js
+
+name: IframeShim
+
+description: Defines IframeShim, a class for obscuring select lists and flash objects in IE.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Element.Event
+ - Core/Element.Style
+ - Core/Options
+ - Core/Events
+ - Element.Position
+ - Class.Occlude
+
+provides: [IframeShim]
+
+...
+*/
+
+(function(){
+
+var browsers = false;
+
+
+this.IframeShim = new Class({
+
+ Implements: [Options, Events, Class.Occlude],
+
+ options: {
+ className: 'iframeShim',
+ src: 'javascript:false;document.write("");',
+ display: false,
+ zIndex: null,
+ margin: 0,
+ offset: {x: 0, y: 0},
+ browsers: browsers
+ },
+
+ property: 'IframeShim',
+
+ initialize: function(element, options){
+ this.element = document.id(element);
+ if (this.occlude()) return this.occluded;
+ this.setOptions(options);
+ this.makeShim();
+ return this;
+ },
+
+ makeShim: function(){
+ if (this.options.browsers){
+ var zIndex = this.element.getStyle('zIndex').toInt();
+
+ if (!zIndex){
+ zIndex = 1;
+ var pos = this.element.getStyle('position');
+ if (pos == 'static' || !pos) this.element.setStyle('position', 'relative');
+ this.element.setStyle('zIndex', zIndex);
+ }
+ zIndex = ((this.options.zIndex != null || this.options.zIndex === 0) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1;
+ if (zIndex < 0) zIndex = 1;
+ this.shim = new Element('iframe', {
+ src: this.options.src,
+ scrolling: 'no',
+ frameborder: 0,
+ styles: {
+ zIndex: zIndex,
+ position: 'absolute',
+ border: 'none',
+ filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
+ },
+ 'class': this.options.className
+ }).store('IframeShim', this);
+ var inject = (function(){
+ this.shim.inject(this.element, 'after');
+ this[this.options.display ? 'show' : 'hide']();
+ this.fireEvent('inject');
+ }).bind(this);
+ if (!IframeShim.ready) window.addEvent('load', inject);
+ else inject();
+ } else {
+ this.position = this.hide = this.show = this.dispose = Function.from(this);
+ }
+ },
+
+ position: function(){
+ if (!IframeShim.ready || !this.shim) return this;
+ var size = this.element.measure(function(){
+ return this.getSize();
+ });
+ if (this.options.margin != undefined){
+ size.x = size.x - (this.options.margin * 2);
+ size.y = size.y - (this.options.margin * 2);
+ this.options.offset.x += this.options.margin;
+ this.options.offset.y += this.options.margin;
+ }
+ this.shim.set({width: size.x, height: size.y}).position({
+ relativeTo: this.element,
+ offset: this.options.offset
+ });
+ return this;
+ },
+
+ hide: function(){
+ if (this.shim) this.shim.setStyle('display', 'none');
+ return this;
+ },
+
+ show: function(){
+ if (this.shim) this.shim.setStyle('display', 'block');
+ return this.position();
+ },
+
+ dispose: function(){
+ if (this.shim) this.shim.dispose();
+ return this;
+ },
+
+ destroy: function(){
+ if (this.shim) this.shim.destroy();
+ return this;
+ }
+
+});
+
+})();
+
+window.addEvent('load', function(){
+ IframeShim.ready = true;
+});
+
+
+/*
+---
+
+script: Mask.js
+
+name: Mask
+
+description: Creates a mask element to cover another.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Options
+ - Core/Events
+ - Core/Element.Event
+ - Class.Binds
+ - Element.Position
+ - IframeShim
+
+provides: [Mask]
+
+...
+*/
+
+var Mask = new Class({
+
+ Implements: [Options, Events],
+
+ Binds: ['position'],
+
+ options: {/*
+ onShow: function(){},
+ onHide: function(){},
+ onDestroy: function(){},
+ onClick: function(event){},
+ inject: {
+ where: 'after',
+ target: null,
+ },
+ hideOnClick: false,
+ id: null,
+ destroyOnHide: false,*/
+ style: {},
+ 'class': 'mask',
+ maskMargins: false,
+ useIframeShim: true,
+ iframeShimOptions: {}
+ },
+
+ initialize: function(target, options){
+ this.target = document.id(target) || document.id(document.body);
+ this.target.store('mask', this);
+ this.setOptions(options);
+ this.render();
+ this.inject();
+ },
+
+ render: function(){
+ this.element = new Element('div', {
+ 'class': this.options['class'],
+ id: this.options.id || 'mask-' + String.uniqueID(),
+ styles: Object.merge({}, this.options.style, {
+ display: 'none'
+ }),
+ events: {
+ click: function(event){
+ this.fireEvent('click', event);
+ if (this.options.hideOnClick) this.hide();
+ }.bind(this)
+ }
+ });
+
+ this.hidden = true;
+ },
+
+ toElement: function(){
+ return this.element;
+ },
+
+ inject: function(target, where){
+ where = where || (this.options.inject ? this.options.inject.where : '') || (this.target == document.body ? 'inside' : 'after');
+ target = target || (this.options.inject && this.options.inject.target) || this.target;
+
+ this.element.inject(target, where);
+
+ if (this.options.useIframeShim){
+ this.shim = new IframeShim(this.element, this.options.iframeShimOptions);
+
+ this.addEvents({
+ show: this.shim.show.bind(this.shim),
+ hide: this.shim.hide.bind(this.shim),
+ destroy: this.shim.destroy.bind(this.shim)
+ });
+ }
+ },
+
+ position: function(){
+ this.resize(this.options.width, this.options.height);
+
+ this.element.position({
+ relativeTo: this.target,
+ position: 'topLeft',
+ ignoreMargins: !this.options.maskMargins,
+ ignoreScroll: this.target == document.body
+ });
+
+ return this;
+ },
+
+ resize: function(x, y){
+ var opt = {
+ styles: ['padding', 'border']
+ };
+ if (this.options.maskMargins) opt.styles.push('margin');
+
+ var dim = this.target.getComputedSize(opt);
+ if (this.target == document.body){
+ this.element.setStyles({width: 0, height: 0});
+ var win = window.getScrollSize();
+ if (dim.totalHeight < win.y) dim.totalHeight = win.y;
+ if (dim.totalWidth < win.x) dim.totalWidth = win.x;
+ }
+ this.element.setStyles({
+ width: Array.pick([x, dim.totalWidth, dim.x]),
+ height: Array.pick([y, dim.totalHeight, dim.y])
+ });
+
+ return this;
+ },
+
+ show: function(){
+ if (!this.hidden) return this;
+
+ window.addEvent('resize', this.position);
+ this.position();
+ this.showMask.apply(this, arguments);
+
+ return this;
+ },
+
+ showMask: function(){
+ this.element.setStyle('display', 'block');
+ this.hidden = false;
+ this.fireEvent('show');
+ },
+
+ hide: function(){
+ if (this.hidden) return this;
+
+ window.removeEvent('resize', this.position);
+ this.hideMask.apply(this, arguments);
+ if (this.options.destroyOnHide) return this.destroy();
+
+ return this;
+ },
+
+ hideMask: function(){
+ this.element.setStyle('display', 'none');
+ this.hidden = true;
+ this.fireEvent('hide');
+ },
+
+ toggle: function(){
+ this[this.hidden ? 'show' : 'hide']();
+ },
+
+ destroy: function(){
+ this.hide();
+ this.element.destroy();
+ this.fireEvent('destroy');
+ this.target.eliminate('mask');
+ }
+
+});
+
+Element.Properties.mask = {
+
+ set: function(options){
+ var mask = this.retrieve('mask');
+ if (mask) mask.destroy();
+ return this.eliminate('mask').store('mask:options', options);
+ },
+
+ get: function(){
+ var mask = this.retrieve('mask');
+ if (!mask){
+ mask = new Mask(this, this.retrieve('mask:options'));
+ this.store('mask', mask);
+ }
+ return mask;
+ }
+
+};
+
+Element.implement({
+
+ mask: function(options){
+ if (options) this.set('mask', options);
+ this.get('mask').show();
+ return this;
+ },
+
+ unmask: function(){
+ this.get('mask').hide();
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+script: Spinner.js
+
+name: Spinner
+
+description: Adds a semi-transparent overlay over a dom element with a spinnin ajax icon.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Fx.Tween
+ - Core/Request
+ - Class.refactor
+ - Mask
+
+provides: [Spinner]
+
+...
+*/
+
+var Spinner = new Class({
+
+ Extends: Mask,
+
+ Implements: Chain,
+
+ options: {/*
+ message: false,*/
+ 'class': 'spinner',
+ containerPosition: {},
+ content: {
+ 'class': 'spinner-content'
+ },
+ messageContainer: {
+ 'class': 'spinner-msg'
+ },
+ img: {
+ 'class': 'spinner-img'
+ },
+ fxOptions: {
+ link: 'chain'
+ }
+ },
+
+ initialize: function(target, options){
+ this.target = document.id(target) || document.id(document.body);
+ this.target.store('spinner', this);
+ this.setOptions(options);
+ this.render();
+ this.inject();
+
+ // Add this to events for when noFx is true; parent methods handle hide/show.
+ var deactivate = function(){ this.active = false; }.bind(this);
+ this.addEvents({
+ hide: deactivate,
+ show: deactivate
+ });
+ },
+
+ render: function(){
+ this.parent();
+
+ this.element.set('id', this.options.id || 'spinner-' + String.uniqueID());
+
+ this.content = document.id(this.options.content) || new Element('div', this.options.content);
+ this.content.inject(this.element);
+
+ if (this.options.message){
+ this.msg = document.id(this.options.message) || new Element('p', this.options.messageContainer).appendText(this.options.message);
+ this.msg.inject(this.content);
+ }
+
+ if (this.options.img){
+ this.img = document.id(this.options.img) || new Element('div', this.options.img);
+ this.img.inject(this.content);
+ }
+
+ this.element.set('tween', this.options.fxOptions);
+ },
+
+ show: function(noFx){
+ if (this.active) return this.chain(this.show.bind(this));
+ if (!this.hidden){
+ this.callChain.delay(20, this);
+ return this;
+ }
+
+ this.target.set('aria-busy', 'true');
+ this.active = true;
+
+ return this.parent(noFx);
+ },
+
+ showMask: function(noFx){
+ var pos = function(){
+ this.content.position(Object.merge({
+ relativeTo: this.element
+ }, this.options.containerPosition));
+ }.bind(this);
+
+ if (noFx){
+ this.parent();
+ pos();
+ } else {
+ if (!this.options.style.opacity) this.options.style.opacity = this.element.getStyle('opacity').toFloat();
+ this.element.setStyles({
+ display: 'block',
+ opacity: 0
+ }).tween('opacity', this.options.style.opacity);
+ pos();
+ this.hidden = false;
+ this.fireEvent('show');
+ this.callChain();
+ }
+ },
+
+ hide: function(noFx){
+ if (this.active) return this.chain(this.hide.bind(this));
+ if (this.hidden){
+ this.callChain.delay(20, this);
+ return this;
+ }
+
+ this.target.set('aria-busy', 'false');
+ this.active = true;
+
+ return this.parent(noFx);
+ },
+
+ hideMask: function(noFx){
+ if (noFx) return this.parent();
+ this.element.tween('opacity', 0).get('tween').chain(function(){
+ this.element.setStyle('display', 'none');
+ this.hidden = true;
+ this.fireEvent('hide');
+ this.callChain();
+ }.bind(this));
+ },
+
+ destroy: function(){
+ this.content.destroy();
+ this.parent();
+ this.target.eliminate('spinner');
+ }
+
+});
+
+Request = Class.refactor(Request, {
+
+ options: {
+ useSpinner: false,
+ spinnerOptions: {},
+ spinnerTarget: false
+ },
+
+ initialize: function(options){
+ this._send = this.send;
+ this.send = function(options){
+ var spinner = this.getSpinner();
+ if (spinner) spinner.chain(this._send.pass(options, this)).show();
+ else this._send(options);
+ return this;
+ };
+ this.previous(options);
+ },
+
+ getSpinner: function(){
+ if (!this.spinner){
+ var update = document.id(this.options.spinnerTarget) || document.id(this.options.update);
+ if (this.options.useSpinner && update){
+ update.set('spinner', this.options.spinnerOptions);
+ var spinner = this.spinner = update.get('spinner');
+ ['complete', 'exception', 'cancel'].each(function(event){
+ this.addEvent(event, spinner.hide.bind(spinner));
+ }, this);
+ }
+ }
+ return this.spinner;
+ }
+
+});
+
+Element.Properties.spinner = {
+
+ set: function(options){
+ var spinner = this.retrieve('spinner');
+ if (spinner) spinner.destroy();
+ return this.eliminate('spinner').store('spinner:options', options);
+ },
+
+ get: function(){
+ var spinner = this.retrieve('spinner');
+ if (!spinner){
+ spinner = new Spinner(this, this.retrieve('spinner:options'));
+ this.store('spinner', spinner);
+ }
+ return spinner;
+ }
+
+};
+
+Element.implement({
+
+ spin: function(options){
+ if (options) this.set('spinner', options);
+ this.get('spinner').show();
+ return this;
+ },
+
+ unspin: function(){
+ this.get('spinner').hide();
+ return this;
+ }
+
+});
+
+
+/*
+---
+
+script: String.QueryString.js
+
+name: String.QueryString
+
+description: Methods for dealing with URI query strings.
+
+license: MIT-style license
+
+authors:
+ - Sebastian Markbåge
+ - Aaron Newton
+ - Lennart Pilon
+ - Valerio Proietti
+
+requires:
+ - Core/Array
+ - Core/String
+ - MooTools.More
+
+provides: [String.QueryString]
+
+...
+*/
+
+String.implement({
+
+ parseQueryString: function(decodeKeys, decodeValues){
+ if (decodeKeys == null) decodeKeys = true;
+ if (decodeValues == null) decodeValues = true;
+
+ var vars = this.split(/[&;]/),
+ object = {};
+ if (!vars.length) return object;
+
+ vars.each(function(val){
+ var index = val.indexOf('=') + 1,
+ value = index ? val.substr(index) : '',
+ keys = index ? val.substr(0, index - 1).match(/([^\]\[]+|(\B)(?=\]))/g) : [val],
+ obj = object;
+ if (!keys) return;
+ if (decodeValues) value = decodeURIComponent(value);
+ keys.each(function(key, i){
+ if (decodeKeys) key = decodeURIComponent(key);
+ var current = obj[key];
+
+ if (i < keys.length - 1) obj = obj[key] = current || {};
+ else if (typeOf(current) == 'array') current.push(value);
+ else obj[key] = current != null ? [current, value] : value;
+ });
+ });
+
+ return object;
+ },
+
+ cleanQueryString: function(method){
+ return this.split('&').filter(function(val){
+ var index = val.indexOf('='),
+ key = index < 0 ? '' : val.substr(0, index),
+ value = val.substr(index + 1);
+
+ return method ? method.call(null, key, value) : (value || value === 0);
+ }).join('&');
+ }
+
+});
+
+
+/*
+---
+
+name: Events.Pseudos
+
+description: Adds the functionality to add pseudo events
+
+license: MIT-style license
+
+authors:
+ - Arian Stolwijk
+
+requires: [Core/Class.Extras, Core/Slick.Parser, MooTools.More]
+
+provides: [Events.Pseudos]
+
+...
+*/
+
+(function(){
+
+Events.Pseudos = function(pseudos, addEvent, removeEvent){
+
+ var storeKey = '_monitorEvents:';
+
+ var storageOf = function(object){
+ return {
+ store: object.store ? function(key, value){
+ object.store(storeKey + key, value);
+ } : function(key, value){
+ (object._monitorEvents || (object._monitorEvents = {}))[key] = value;
+ },
+ retrieve: object.retrieve ? function(key, dflt){
+ return object.retrieve(storeKey + key, dflt);
+ } : function(key, dflt){
+ if (!object._monitorEvents) return dflt;
+ return object._monitorEvents[key] || dflt;
+ }
+ };
+ };
+
+ var splitType = function(type){
+ if (type.indexOf(':') == -1 || !pseudos) return null;
+
+ var parsed = Slick.parse(type).expressions[0][0],
+ parsedPseudos = parsed.pseudos,
+ l = parsedPseudos.length,
+ splits = [];
+
+ while (l--){
+ var pseudo = parsedPseudos[l].key,
+ listener = pseudos[pseudo];
+ if (listener != null) splits.push({
+ event: parsed.tag,
+ value: parsedPseudos[l].value,
+ pseudo: pseudo,
+ original: type,
+ listener: listener
+ });
+ }
+ return splits.length ? splits : null;
+ };
+
+ return {
+
+ addEvent: function(type, fn, internal){
+ var split = splitType(type);
+ if (!split) return addEvent.call(this, type, fn, internal);
+
+ var storage = storageOf(this),
+ events = storage.retrieve(type, []),
+ eventType = split[0].event,
+ args = Array.slice(arguments, 2),
+ stack = fn,
+ self = this;
+
+ split.each(function(item){
+ var listener = item.listener,
+ stackFn = stack;
+ if (listener == false) eventType += ':' + item.pseudo + '(' + item.value + ')';
+ else stack = function(){
+ listener.call(self, item, stackFn, arguments, stack);
+ };
+ });
+
+ events.include({type: eventType, event: fn, monitor: stack});
+ storage.store(type, events);
+
+ if (type != eventType) addEvent.apply(this, [type, fn].concat(args));
+ return addEvent.apply(this, [eventType, stack].concat(args));
+ },
+
+ removeEvent: function(type, fn){
+ var split = splitType(type);
+ if (!split) return removeEvent.call(this, type, fn);
+
+ var storage = storageOf(this),
+ events = storage.retrieve(type);
+ if (!events) return this;
+
+ var args = Array.slice(arguments, 2);
+
+ removeEvent.apply(this, [type, fn].concat(args));
+ events.each(function(monitor, i){
+ if (!fn || monitor.event == fn) removeEvent.apply(this, [monitor.type, monitor.monitor].concat(args));
+ delete events[i];
+ }, this);
+
+ storage.store(type, events);
+ return this;
+ }
+
+ };
+
+};
+
+var pseudos = {
+
+ once: function(split, fn, args, monitor){
+ fn.apply(this, args);
+ this.removeEvent(split.event, monitor)
+ .removeEvent(split.original, fn);
+ },
+
+ throttle: function(split, fn, args){
+ if (!fn._throttled){
+ fn.apply(this, args);
+ fn._throttled = setTimeout(function(){
+ fn._throttled = false;
+ }, split.value || 250);
+ }
+ },
+
+ pause: function(split, fn, args){
+ clearTimeout(fn._pause);
+ fn._pause = fn.delay(split.value || 250, this, args);
+ }
+
+};
+
+Events.definePseudo = function(key, listener){
+ pseudos[key] = listener;
+ return this;
+};
+
+Events.lookupPseudo = function(key){
+ return pseudos[key];
+};
+
+var proto = Events.prototype;
+Events.implement(Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
+
+['Request', 'Fx'].each(function(klass){
+ if (this[klass]) this[klass].implement(Events.prototype);
+});
+
+})();
+
+
+/*
+---
+
+name: Element.Event.Pseudos
+
+description: Adds the functionality to add pseudo events for Elements
+
+license: MIT-style license
+
+authors:
+ - Arian Stolwijk
+
+requires: [Core/Element.Event, Core/Element.Delegation, Events.Pseudos]
+
+provides: [Element.Event.Pseudos, Element.Delegation.Pseudo]
+
+...
+*/
+
+(function(){
+
+var pseudos = {relay: false},
+ copyFromEvents = ['once', 'throttle', 'pause'],
+ count = copyFromEvents.length;
+
+while (count--) pseudos[copyFromEvents[count]] = Events.lookupPseudo(copyFromEvents[count]);
+
+DOMEvent.definePseudo = function(key, listener){
+ pseudos[key] = listener;
+ return this;
+};
+
+var proto = Element.prototype;
+[Element, Window, Document].invoke('implement', Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
+
+})();
+
+
+/*
+---
+
+script: Form.Request.js
+
+name: Form.Request
+
+description: Handles the basic functionality of submitting a form and updating a dom element with the result.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Request.HTML
+ - Class.Binds
+ - Class.Occlude
+ - Spinner
+ - String.QueryString
+ - Element.Delegation.Pseudo
+
+provides: [Form.Request]
+
+...
+*/
+
+if (!window.Form) window.Form = {};
+
+(function(){
+
+ Form.Request = new Class({
+
+ Binds: ['onSubmit', 'onFormValidate'],
+
+ Implements: [Options, Events, Class.Occlude],
+
+ options: {/*
+ onFailure: function(){},
+ onSuccess: function(){}, // aliased to onComplete,
+ onSend: function(){}*/
+ requestOptions: {
+ evalScripts: true,
+ useSpinner: true,
+ emulation: false,
+ link: 'ignore'
+ },
+ sendButtonClicked: true,
+ extraData: {},
+ resetForm: true
+ },
+
+ property: 'form.request',
+
+ initialize: function(form, target, options){
+ this.element = document.id(form);
+ if (this.occlude()) return this.occluded;
+ this.setOptions(options)
+ .setTarget(target)
+ .attach();
+ },
+
+ setTarget: function(target){
+ this.target = document.id(target);
+ if (!this.request){
+ this.makeRequest();
+ } else {
+ this.request.setOptions({
+ update: this.target
+ });
+ }
+ return this;
+ },
+
+ toElement: function(){
+ return this.element;
+ },
+
+ makeRequest: function(){
+ var self = this;
+ this.request = new Request.HTML(Object.merge({
+ update: this.target,
+ emulation: false,
+ spinnerTarget: this.element,
+ method: this.element.get('method') || 'post'
+ }, this.options.requestOptions)).addEvents({
+ success: function(tree, elements, html, javascript){
+ ['complete', 'success'].each(function(evt){
+ self.fireEvent(evt, [self.target, tree, elements, html, javascript]);
+ });
+ },
+ failure: function(){
+ self.fireEvent('complete', arguments).fireEvent('failure', arguments);
+ },
+ exception: function(){
+ self.fireEvent('failure', arguments);
+ }
+ });
+ return this.attachReset();
+ },
+
+ attachReset: function(){
+ if (!this.options.resetForm) return this;
+ this.request.addEvent('success', function(){
+ Function.attempt(function(){
+ this.element.reset();
+ }.bind(this));
+ if (window.OverText) OverText.update();
+ }.bind(this));
+ return this;
+ },
+
+ attach: function(attach){
+ var method = (attach != false) ? 'addEvent' : 'removeEvent';
+ this.element[method]('click:relay(button, input[type=submit])', this.saveClickedButton.bind(this));
+
+ var fv = this.element.retrieve('validator');
+ if (fv) fv[method]('onFormValidate', this.onFormValidate);
+ else this.element[method]('submit', this.onSubmit);
+
+ return this;
+ },
+
+ detach: function(){
+ return this.attach(false);
+ },
+
+ //public method
+ enable: function(){
+ return this.attach();
+ },
+
+ //public method
+ disable: function(){
+ return this.detach();
+ },
+
+ onFormValidate: function(valid, form, event){
+ //if there's no event, then this wasn't a submit event
+ if (!event) return;
+ var fv = this.element.retrieve('validator');
+ if (valid || (fv && !fv.options.stopOnFailure)){
+ event.stop();
+ this.send();
+ }
+ },
+
+ onSubmit: function(event){
+ var fv = this.element.retrieve('validator');
+ if (fv){
+ //form validator was created after Form.Request
+ this.element.removeEvent('submit', this.onSubmit);
+ fv.addEvent('onFormValidate', this.onFormValidate);
+ fv.validate(event);
+ return;
+ }
+ if (event) event.stop();
+ this.send();
+ },
+
+ saveClickedButton: function(event, target){
+ var targetName = target.get('name');
+ if (!targetName || !this.options.sendButtonClicked) return;
+ this.options.extraData[targetName] = target.get('value') || true;
+ this.clickedCleaner = function(){
+ delete this.options.extraData[targetName];
+ this.clickedCleaner = function(){};
+ }.bind(this);
+ },
+
+ clickedCleaner: function(){},
+
+ send: function(){
+ var str = this.element.toQueryString().trim(),
+ data = Object.toQueryString(this.options.extraData);
+
+ if (str) str += "&" + data;
+ else str = data;
+
+ this.fireEvent('send', [this.element, str.parseQueryString()]);
+ this.request.send({
+ data: str,
+ url: this.options.requestOptions.url || this.element.get('action')
+ });
+ this.clickedCleaner();
+ return this;
+ }
+
+ });
+
+ Element.implement('formUpdate', function(update, options){
+ var fq = this.retrieve('form.request');
+ if (!fq){
+ fq = new Form.Request(this, update, options);
+ } else {
+ if (update) fq.setTarget(update);
+ if (options) fq.setOptions(options).makeRequest();
+ }
+ fq.send();
+ return this;
+ });
+
+})();
+
+
+/*
+---
+
+script: Element.Shortcuts.js
+
+name: Element.Shortcuts
+
+description: Extends the Element native object to include some shortcut methods.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Element.Style
+ - MooTools.More
+
+provides: [Element.Shortcuts]
+
+...
+*/
+
+Element.implement({
+
+ isDisplayed: function(){
+ return this.getStyle('display') != 'none';
+ },
+
+ isVisible: function(){
+ var w = this.offsetWidth,
+ h = this.offsetHeight;
+ return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.style.display != 'none';
+ },
+
+ toggle: function(){
+ return this[this.isDisplayed() ? 'hide' : 'show']();
+ },
+
+ hide: function(){
+ var d;
+ try {
+ //IE fails here if the element is not in the dom
+ d = this.getStyle('display');
+ } catch(e){}
+ if (d == 'none') return this;
+ return this.store('element:_originalDisplay', d || '').setStyle('display', 'none');
+ },
+
+ show: function(display){
+ if (!display && this.isDisplayed()) return this;
+ display = display || this.retrieve('element:_originalDisplay') || 'block';
+ return this.setStyle('display', (display == 'none') ? 'block' : display);
+ },
+
+ swapClass: function(remove, add){
+ return this.removeClass(remove).addClass(add);
+ }
+
+});
+
+Document.implement({
+
+ clearSelection: function(){
+ if (window.getSelection){
+ var selection = window.getSelection();
+ if (selection && selection.removeAllRanges) selection.removeAllRanges();
+ } else if (document.selection && document.selection.empty){
+ try {
+ //IE fails here if selected element is not in dom
+ document.selection.empty();
+ } catch(e){}
+ }
+ }
+
+});
+
+
+/*
+---
+
+script: Fx.Reveal.js
+
+name: Fx.Reveal
+
+description: Defines Fx.Reveal, a class that shows and hides elements with a transition.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - Core/Fx.Morph
+ - Element.Shortcuts
+ - Element.Measure
+
+provides: [Fx.Reveal]
+
+...
+*/
+
+(function(){
+
+
+var hideTheseOf = function(object){
+ var hideThese = object.options.hideInputs;
+ if (window.OverText){
+ var otClasses = [null];
+ OverText.each(function(ot){
+ otClasses.include('.' + ot.options.labelClass);
+ });
+ if (otClasses) hideThese += otClasses.join(', ');
+ }
+ return (hideThese) ? object.element.getElements(hideThese) : null;
+};
+
+
+Fx.Reveal = new Class({
+
+ Extends: Fx.Morph,
+
+ options: {/*
+ onShow: function(thisElement){},
+ onHide: function(thisElement){},
+ onComplete: function(thisElement){},
+ heightOverride: null,
+ widthOverride: null,*/
+ link: 'cancel',
+ styles: ['padding', 'border', 'margin'],
+ transitionOpacity: 'opacity' in document.documentElement,
+ mode: 'vertical',
+ display: function(){
+ return this.element.get('tag') != 'tr' ? 'block' : 'table-row';
+ },
+ opacity: 1,
+ hideInputs: !('opacity' in document.documentElement) ? 'select, input, textarea, object, embed' : null
+ },
+
+ dissolve: function(){
+ if (!this.hiding && !this.showing){
+ if (this.element.getStyle('display') != 'none'){
+ this.hiding = true;
+ this.showing = false;
+ this.hidden = true;
+ this.cssText = this.element.style.cssText;
+
+ var startStyles = this.element.getComputedSize({
+ styles: this.options.styles,
+ mode: this.options.mode
+ });
+ if (this.options.transitionOpacity) startStyles.opacity = this.options.opacity;
+
+ var zero = {};
+ Object.each(startStyles, function(style, name){
+ zero[name] = [style, 0];
+ });
+
+ this.element.setStyles({
+ display: Function.from(this.options.display).call(this),
+ overflow: 'hidden'
+ });
+
+ var hideThese = hideTheseOf(this);
+ if (hideThese) hideThese.setStyle('visibility', 'hidden');
+
+ this.$chain.unshift(function(){
+ if (this.hidden){
+ this.hiding = false;
+ this.element.style.cssText = this.cssText;
+ this.element.setStyle('display', 'none');
+ if (hideThese) hideThese.setStyle('visibility', 'visible');
+ }
+ this.fireEvent('hide', this.element);
+ this.callChain();
+ }.bind(this));
+
+ this.start(zero);
+ } else {
+ this.callChain.delay(10, this);
+ this.fireEvent('complete', this.element);
+ this.fireEvent('hide', this.element);
+ }
+ } else if (this.options.link == 'chain'){
+ this.chain(this.dissolve.bind(this));
+ } else if (this.options.link == 'cancel' && !this.hiding){
+ this.cancel();
+ this.dissolve();
+ }
+ return this;
+ },
+
+ reveal: function(){
+ if (!this.showing && !this.hiding){
+ if (this.element.getStyle('display') == 'none'){
+ this.hiding = false;
+ this.showing = true;
+ this.hidden = false;
+ this.cssText = this.element.style.cssText;
+
+ var startStyles;
+ this.element.measure(function(){
+ startStyles = this.element.getComputedSize({
+ styles: this.options.styles,
+ mode: this.options.mode
+ });
+ }.bind(this));
+ if (this.options.heightOverride != null) startStyles.height = this.options.heightOverride.toInt();
+ if (this.options.widthOverride != null) startStyles.width = this.options.widthOverride.toInt();
+ if (this.options.transitionOpacity){
+ this.element.setStyle('opacity', 0);
+ startStyles.opacity = this.options.opacity;
+ }
+
+ var zero = {
+ height: 0,
+ display: Function.from(this.options.display).call(this)
+ };
+ Object.each(startStyles, function(style, name){
+ zero[name] = 0;
+ });
+ zero.overflow = 'hidden';
+
+ this.element.setStyles(zero);
+
+ var hideThese = hideTheseOf(this);
+ if (hideThese) hideThese.setStyle('visibility', 'hidden');
+
+ this.$chain.unshift(function(){
+ this.element.style.cssText = this.cssText;
+ this.element.setStyle('display', Function.from(this.options.display).call(this));
+ if (!this.hidden) this.showing = false;
+ if (hideThese) hideThese.setStyle('visibility', 'visible');
+ this.callChain();
+ this.fireEvent('show', this.element);
+ }.bind(this));
+
+ this.start(startStyles);
+ } else {
+ this.callChain();
+ this.fireEvent('complete', this.element);
+ this.fireEvent('show', this.element);
+ }
+ } else if (this.options.link == 'chain'){
+ this.chain(this.reveal.bind(this));
+ } else if (this.options.link == 'cancel' && !this.showing){
+ this.cancel();
+ this.reveal();
+ }
+ return this;
+ },
+
+ toggle: function(){
+ if (this.element.getStyle('display') == 'none'){
+ this.reveal();
+ } else {
+ this.dissolve();
+ }
+ return this;
+ },
+
+ cancel: function(){
+ this.parent.apply(this, arguments);
+ if (this.cssText != null) this.element.style.cssText = this.cssText;
+ this.hiding = false;
+ this.showing = false;
+ return this;
+ }
+
+});
+
+Element.Properties.reveal = {
+
+ set: function(options){
+ this.get('reveal').cancel().setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var reveal = this.retrieve('reveal');
+ if (!reveal){
+ reveal = new Fx.Reveal(this);
+ this.store('reveal', reveal);
+ }
+ return reveal;
+ }
+
+};
+
+Element.Properties.dissolve = Element.Properties.reveal;
+
+Element.implement({
+
+ reveal: function(options){
+ this.get('reveal').setOptions(options).reveal();
+ return this;
+ },
+
+ dissolve: function(options){
+ this.get('reveal').setOptions(options).dissolve();
+ return this;
+ },
+
+ nix: function(options){
+ var params = Array.link(arguments, {destroy: Type.isBoolean, options: Type.isObject});
+ this.get('reveal').setOptions(options).dissolve().chain(function(){
+ this[params.destroy ? 'destroy' : 'dispose']();
+ }.bind(this));
+ return this;
+ },
+
+ wink: function(){
+ var params = Array.link(arguments, {duration: Type.isNumber, options: Type.isObject});
+ var reveal = this.get('reveal').setOptions(params.options);
+ reveal.reveal().chain(function(){
+ (function(){
+ reveal.dissolve();
+ }).delay(params.duration || 2000);
+ });
+ }
+
+});
+
+})();
+
+
+/*
+---
+
+script: Drag.js
+
+name: Drag
+
+description: The base Drag Class. Can be used to drag and resize Elements using mouse events.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+ - Tom Occhinno
+ - Jan Kassens
+
+requires:
+ - Core/Events
+ - Core/Options
+ - Core/Element.Event
+ - Core/Element.Style
+ - Core/Element.Dimensions
+ - MooTools.More
+
+provides: [Drag]
+...
+
+*/
+
+var Drag = new Class({
+
+ Implements: [Events, Options],
+
+ options: {/*
+ onBeforeStart: function(thisElement){},
+ onStart: function(thisElement, event){},
+ onSnap: function(thisElement){},
+ onDrag: function(thisElement, event){},
+ onCancel: function(thisElement){},
+ onComplete: function(thisElement, event){},*/
+ snap: 6,
+ unit: 'px',
+ grid: false,
+ style: true,
+ limit: false,
+ handle: false,
+ invert: false,
+ preventDefault: false,
+ stopPropagation: false,
+ modifiers: {x: 'left', y: 'top'}
+ },
+
+ initialize: function(){
+ var params = Array.link(arguments, {
+ 'options': Type.isObject,
+ 'element': function(obj){
+ return obj != null;
+ }
+ });
+
+ this.element = document.id(params.element);
+ this.document = this.element.getDocument();
+ this.setOptions(params.options || {});
+ var htype = typeOf(this.options.handle);
+ this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
+ this.mouse = {'now': {}, 'pos': {}};
+ this.value = {'start': {}, 'now': {}};
+
+ this.selection = 'selectstart' in document ? 'selectstart' : 'mousedown';
+
+
+ if ('ondragstart' in document && !('FileReader' in window) && !Drag.ondragstartFixed){
+ document.ondragstart = Function.from(false);
+ Drag.ondragstartFixed = true;
+ }
+
+ this.bound = {
+ start: this.start.bind(this),
+ check: this.check.bind(this),
+ drag: this.drag.bind(this),
+ stop: this.stop.bind(this),
+ cancel: this.cancel.bind(this),
+ eventStop: Function.from(false)
+ };
+ this.attach();
+ },
+
+ attach: function(){
+ this.handles.addEvent('mousedown', this.bound.start);
+ return this;
+ },
+
+ detach: function(){
+ this.handles.removeEvent('mousedown', this.bound.start);
+ return this;
+ },
+
+ start: function(event){
+ var options = this.options;
+
+ if (event.rightClick) return;
+
+ if (options.preventDefault) event.preventDefault();
+ if (options.stopPropagation) event.stopPropagation();
+ this.mouse.start = event.page;
+
+ this.fireEvent('beforeStart', this.element);
+
+ var limit = options.limit;
+ this.limit = {x: [], y: []};
+
+ var z, coordinates;
+ for (z in options.modifiers){
+ if (!options.modifiers[z]) continue;
+
+ var style = this.element.getStyle(options.modifiers[z]);
+
+ // Some browsers (IE and Opera) don't always return pixels.
+ if (style && !style.match(/px$/)){
+ if (!coordinates) coordinates = this.element.getCoordinates(this.element.getOffsetParent());
+ style = coordinates[options.modifiers[z]];
+ }
+
+ if (options.style) this.value.now[z] = (style || 0).toInt();
+ else this.value.now[z] = this.element[options.modifiers[z]];
+
+ if (options.invert) this.value.now[z] *= -1;
+
+ this.mouse.pos[z] = event.page[z] - this.value.now[z];
+
+ if (limit && limit[z]){
+ var i = 2;
+ while (i--){
+ var limitZI = limit[z][i];
+ if (limitZI || limitZI === 0) this.limit[z][i] = (typeof limitZI == 'function') ? limitZI() : limitZI;
+ }
+ }
+ }
+
+ if (typeOf(this.options.grid) == 'number') this.options.grid = {
+ x: this.options.grid,
+ y: this.options.grid
+ };
+
+ var events = {
+ mousemove: this.bound.check,
+ mouseup: this.bound.cancel
+ };
+ events[this.selection] = this.bound.eventStop;
+ this.document.addEvents(events);
+ },
+
+ check: function(event){
+ if (this.options.preventDefault) event.preventDefault();
+ var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
+ if (distance > this.options.snap){
+ this.cancel();
+ this.document.addEvents({
+ mousemove: this.bound.drag,
+ mouseup: this.bound.stop
+ });
+ this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
+ }
+ },
+
+ drag: function(event){
+ var options = this.options;
+
+ if (options.preventDefault) event.preventDefault();
+ this.mouse.now = event.page;
+
+ for (var z in options.modifiers){
+ if (!options.modifiers[z]) continue;
+ this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
+
+ if (options.invert) this.value.now[z] *= -1;
+
+ if (options.limit && this.limit[z]){
+ if ((this.limit[z][1] || this.limit[z][1] === 0) && (this.value.now[z] > this.limit[z][1])){
+ this.value.now[z] = this.limit[z][1];
+ } else if ((this.limit[z][0] || this.limit[z][0] === 0) && (this.value.now[z] < this.limit[z][0])){
+ this.value.now[z] = this.limit[z][0];
+ }
+ }
+
+ if (options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % options.grid[z]);
+
+ if (options.style) this.element.setStyle(options.modifiers[z], this.value.now[z] + options.unit);
+ else this.element[options.modifiers[z]] = this.value.now[z];
+ }
+
+ this.fireEvent('drag', [this.element, event]);
+ },
+
+ cancel: function(event){
+ this.document.removeEvents({
+ mousemove: this.bound.check,
+ mouseup: this.bound.cancel
+ });
+ if (event){
+ this.document.removeEvent(this.selection, this.bound.eventStop);
+ this.fireEvent('cancel', this.element);
+ }
+ },
+
+ stop: function(event){
+ var events = {
+ mousemove: this.bound.drag,
+ mouseup: this.bound.stop
+ };
+ events[this.selection] = this.bound.eventStop;
+ this.document.removeEvents(events);
+ if (event) this.fireEvent('complete', [this.element, event]);
+ }
+
+});
+
+Element.implement({
+
+ makeResizable: function(options){
+ var drag = new Drag(this, Object.merge({
+ modifiers: {
+ x: 'width',
+ y: 'height'
+ }
+ }, options));
+
+ this.store('resizer', drag);
+ return drag.addEvent('drag', function(){
+ this.fireEvent('resize', drag);
+ }.bind(this));
+ }
+
+});
+
+
+/*
+---
+
+script: Drag.Move.js
+
+name: Drag.Move
+
+description: A Drag extension that provides support for the constraining of draggables to containers and droppables.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+ - Tom Occhinno
+ - Jan Kassens
+ - Aaron Newton
+ - Scott Kyle
+
+requires:
+ - Core/Element.Dimensions
+ - Drag
+
+provides: [Drag.Move]
+
+...
+*/
+
+Drag.Move = new Class({
+
+ Extends: Drag,
+
+ options: {/*
+ onEnter: function(thisElement, overed){},
+ onLeave: function(thisElement, overed){},
+ onDrop: function(thisElement, overed, event){},*/
+ droppables: [],
+ container: false,
+ precalculate: false,
+ includeMargins: true,
+ checkDroppables: true
+ },
+
+ initialize: function(element, options){
+ this.parent(element, options);
+ element = this.element;
+
+ this.droppables = $$(this.options.droppables);
+ this.setContainer(this.options.container);
+
+ if (this.options.style){
+ if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){
+ var parent = element.getOffsetParent(),
+ styles = element.getStyles('left', 'top');
+ if (parent && (styles.left == 'auto' || styles.top == 'auto')){
+ element.setPosition(element.getPosition(parent));
+ }
+ }
+
+ if (element.getStyle('position') == 'static') element.setStyle('position', 'absolute');
+ }
+
+ this.addEvent('start', this.checkDroppables, true);
+ this.overed = null;
+ },
+
+ setContainer: function(container) {
+ this.container = document.id(container);
+ if (this.container && typeOf(this.container) != 'element'){
+ this.container = document.id(this.container.getDocument().body);
+ }
+ },
+
+ start: function(event){
+ if (this.container) this.options.limit = this.calculateLimit();
+
+ if (this.options.precalculate){
+ this.positions = this.droppables.map(function(el){
+ return el.getCoordinates();
+ });
+ }
+
+ this.parent(event);
+ },
+
+ calculateLimit: function(){
+ var element = this.element,
+ container = this.container,
+
+ offsetParent = document.id(element.getOffsetParent()) || document.body,
+ containerCoordinates = container.getCoordinates(offsetParent),
+ elementMargin = {},
+ elementBorder = {},
+ containerMargin = {},
+ containerBorder = {},
+ offsetParentPadding = {};
+
+ ['top', 'right', 'bottom', 'left'].each(function(pad){
+ elementMargin[pad] = element.getStyle('margin-' + pad).toInt();
+ elementBorder[pad] = element.getStyle('border-' + pad).toInt();
+ containerMargin[pad] = container.getStyle('margin-' + pad).toInt();
+ containerBorder[pad] = container.getStyle('border-' + pad).toInt();
+ offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
+ }, this);
+
+ var width = element.offsetWidth + elementMargin.left + elementMargin.right,
+ height = element.offsetHeight + elementMargin.top + elementMargin.bottom,
+ left = 0,
+ top = 0,
+ right = containerCoordinates.right - containerBorder.right - width,
+ bottom = containerCoordinates.bottom - containerBorder.bottom - height;
+
+ if (this.options.includeMargins){
+ left += elementMargin.left;
+ top += elementMargin.top;
+ } else {
+ right += elementMargin.right;
+ bottom += elementMargin.bottom;
+ }
+
+ if (element.getStyle('position') == 'relative'){
+ var coords = element.getCoordinates(offsetParent);
+ coords.left -= element.getStyle('left').toInt();
+ coords.top -= element.getStyle('top').toInt();
+
+ left -= coords.left;
+ top -= coords.top;
+ if (container.getStyle('position') != 'relative'){
+ left += containerBorder.left;
+ top += containerBorder.top;
+ }
+ right += elementMargin.left - coords.left;
+ bottom += elementMargin.top - coords.top;
+
+ if (container != offsetParent){
+ left += containerMargin.left + offsetParentPadding.left;
+ if (!offsetParentPadding.left && left < 0) left = 0;
+ top += offsetParent == document.body ? 0 : containerMargin.top + offsetParentPadding.top;
+ if (!offsetParentPadding.top && top < 0) top = 0;
+ }
+ } else {
+ left -= elementMargin.left;
+ top -= elementMargin.top;
+ if (container != offsetParent){
+ left += containerCoordinates.left + containerBorder.left;
+ top += containerCoordinates.top + containerBorder.top;
+ }
+ }
+
+ return {
+ x: [left, right],
+ y: [top, bottom]
+ };
+ },
+
+ getDroppableCoordinates: function(element){
+ var position = element.getCoordinates();
+ if (element.getStyle('position') == 'fixed'){
+ var scroll = window.getScroll();
+ position.left += scroll.x;
+ position.right += scroll.x;
+ position.top += scroll.y;
+ position.bottom += scroll.y;
+ }
+ return position;
+ },
+
+ checkDroppables: function(){
+ var overed = this.droppables.filter(function(el, i){
+ el = this.positions ? this.positions[i] : this.getDroppableCoordinates(el);
+ var now = this.mouse.now;
+ return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
+ }, this).getLast();
+
+ if (this.overed != overed){
+ if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
+ if (overed) this.fireEvent('enter', [this.element, overed]);
+ this.overed = overed;
+ }
+ },
+
+ drag: function(event){
+ this.parent(event);
+ if (this.options.checkDroppables && this.droppables.length) this.checkDroppables();
+ },
+
+ stop: function(event){
+ this.checkDroppables();
+ this.fireEvent('drop', [this.element, this.overed, event]);
+ this.overed = null;
+ return this.parent(event);
+ }
+
+});
+
+Element.implement({
+
+ makeDraggable: function(options){
+ var drag = new Drag.Move(this, options);
+ this.store('dragger', drag);
+ return drag;
+ }
+
+});
+
+
+/*
+---
+
+script: Sortables.js
+
+name: Sortables
+
+description: Class for creating a drag and drop sorting interface for lists of items.
+
+license: MIT-style license
+
+authors:
+ - Tom Occhino
+
+requires:
+ - Core/Fx.Morph
+ - Drag.Move
+
+provides: [Sortables]
+
+...
+*/
+
+var Sortables = new Class({
+
+ Implements: [Events, Options],
+
+ options: {/*
+ onSort: function(element, clone){},
+ onStart: function(element, clone){},
+ onComplete: function(element){},*/
+ opacity: 1,
+ clone: false,
+ revert: false,
+ handle: false,
+ dragOptions: {},
+ unDraggableTags: ['button', 'input', 'a', 'textarea', 'select', 'option']
+ },
+
+ initialize: function(lists, options){
+ this.setOptions(options);
+
+ this.elements = [];
+ this.lists = [];
+ this.idle = true;
+
+ this.addLists($$(document.id(lists) || lists));
+
+ if (!this.options.clone) this.options.revert = false;
+ if (this.options.revert) this.effect = new Fx.Morph(null, Object.merge({
+ duration: 250,
+ link: 'cancel'
+ }, this.options.revert));
+ },
+
+ attach: function(){
+ this.addLists(this.lists);
+ return this;
+ },
+
+ detach: function(){
+ this.lists = this.removeLists(this.lists);
+ return this;
+ },
+
+ addItems: function(){
+ Array.flatten(arguments).each(function(element){
+ this.elements.push(element);
+ var start = element.retrieve('sortables:start', function(event){
+ this.start.call(this, event, element);
+ }.bind(this));
+ (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
+ }, this);
+ return this;
+ },
+
+ addLists: function(){
+ Array.flatten(arguments).each(function(list){
+ this.lists.include(list);
+ this.addItems(list.getChildren());
+ }, this);
+ return this;
+ },
+
+ removeItems: function(){
+ return $$(Array.flatten(arguments).map(function(element){
+ this.elements.erase(element);
+ var start = element.retrieve('sortables:start');
+ (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
+
+ return element;
+ }, this));
+ },
+
+ removeLists: function(){
+ return $$(Array.flatten(arguments).map(function(list){
+ this.lists.erase(list);
+ this.removeItems(list.getChildren());
+
+ return list;
+ }, this));
+ },
+
+ getDroppableCoordinates: function (element){
+ var offsetParent = element.getOffsetParent();
+ var position = element.getPosition(offsetParent);
+ var scroll = {
+ w: window.getScroll(),
+ offsetParent: offsetParent.getScroll()
+ };
+ position.x += scroll.offsetParent.x;
+ position.y += scroll.offsetParent.y;
+
+ if (offsetParent.getStyle('position') == 'fixed'){
+ position.x -= scroll.w.x;
+ position.y -= scroll.w.y;
+ }
+
+ return position;
+ },
+
+ getClone: function(event, element){
+ if (!this.options.clone) return new Element(element.tagName).inject(document.body);
+ if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
+ var clone = element.clone(true).setStyles({
+ margin: 0,
+ position: 'absolute',
+ visibility: 'hidden',
+ width: element.getStyle('width')
+ }).addEvent('mousedown', function(event){
+ element.fireEvent('mousedown', event);
+ });
+ //prevent the duplicated radio inputs from unchecking the real one
+ if (clone.get('html').test('radio')){
+ clone.getElements('input[type=radio]').each(function(input, i){
+ input.set('name', 'clone_' + i);
+ if (input.get('checked')) element.getElements('input[type=radio]')[i].set('checked', true);
+ });
+ }
+
+ return clone.inject(this.list).setPosition(this.getDroppableCoordinates(this.element));
+ },
+
+ getDroppables: function(){
+ var droppables = this.list.getChildren().erase(this.clone).erase(this.element);
+ if (!this.options.constrain) droppables.append(this.lists).erase(this.list);
+ return droppables;
+ },
+
+ insert: function(dragging, element){
+ var where = 'inside';
+ if (this.lists.contains(element)){
+ this.list = element;
+ this.drag.droppables = this.getDroppables();
+ } else {
+ where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
+ }
+ this.element.inject(element, where);
+ this.fireEvent('sort', [this.element, this.clone]);
+ },
+
+ start: function(event, element){
+ if (
+ !this.idle ||
+ event.rightClick ||
+ (!this.options.handle && this.options.unDraggableTags.contains(event.target.get('tag')))
+ ) return;
+
+ this.idle = false;
+ this.element = element;
+ this.opacity = element.getStyle('opacity');
+ this.list = element.getParent();
+ this.clone = this.getClone(event, element);
+
+ this.drag = new Drag.Move(this.clone, Object.merge({
+
+ droppables: this.getDroppables()
+ }, this.options.dragOptions)).addEvents({
+ onSnap: function(){
+ event.stop();
+ this.clone.setStyle('visibility', 'visible');
+ this.element.setStyle('opacity', this.options.opacity || 0);
+ this.fireEvent('start', [this.element, this.clone]);
+ }.bind(this),
+ onEnter: this.insert.bind(this),
+ onCancel: this.end.bind(this),
+ onComplete: this.end.bind(this)
+ });
+
+ this.clone.inject(this.element, 'before');
+ this.drag.start(event);
+ },
+
+ end: function(){
+ this.drag.detach();
+ this.element.setStyle('opacity', this.opacity);
+ var self = this;
+ if (this.effect){
+ var dim = this.element.getStyles('width', 'height'),
+ clone = this.clone,
+ pos = clone.computePosition(this.getDroppableCoordinates(clone));
+
+ var destroy = function(){
+ this.removeEvent('cancel', destroy);
+ clone.destroy();
+ self.reset();
+ };
+
+ this.effect.element = clone;
+ this.effect.start({
+ top: pos.top,
+ left: pos.left,
+ width: dim.width,
+ height: dim.height,
+ opacity: 0.25
+ }).addEvent('cancel', destroy).chain(destroy);
+ } else {
+ this.clone.destroy();
+ self.reset();
+ }
+
+ },
+
+ reset: function(){
+ this.idle = true;
+ this.fireEvent('complete', this.element);
+ },
+
+ serialize: function(){
+ var params = Array.link(arguments, {
+ modifier: Type.isFunction,
+ index: function(obj){
+ return obj != null;
+ }
+ });
+ var serial = this.lists.map(function(list){
+ return list.getChildren().map(params.modifier || function(element){
+ return element.get('id');
+ }, this);
+ }, this);
+
+ var index = params.index;
+ if (this.lists.length == 1) index = 0;
+ return (index || index === 0) && index >= 0 && index < this.lists.length ? serial[index] : serial;
+ }
+
+});
+
+
+/*
+---
+
+script: Request.Periodical.js
+
+name: Request.Periodical
+
+description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load
+
+license: MIT-style license
+
+authors:
+ - Christoph Pojer
+
+requires:
+ - Core/Request
+ - MooTools.More
+
+provides: [Request.Periodical]
+
+...
+*/
+
+Request.implement({
+
+ options: {
+ initialDelay: 5000,
+ delay: 5000,
+ limit: 60000
+ },
+
+ startTimer: function(data){
+ var fn = function(){
+ if (!this.running) this.send({data: data});
+ };
+ this.lastDelay = this.options.initialDelay;
+ this.timer = fn.delay(this.lastDelay, this);
+ this.completeCheck = function(response){
+ clearTimeout(this.timer);
+ this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit);
+ this.timer = fn.delay(this.lastDelay, this);
+ };
+ return this.addEvent('complete', this.completeCheck);
+ },
+
+ stopTimer: function(){
+ clearTimeout(this.timer);
+ return this.removeEvent('complete', this.completeCheck);
+ }
+
+});
+
+
+/*
+---
+
+script: Color.js
+
+name: Color
+
+description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - Core/Array
+ - Core/String
+ - Core/Number
+ - Core/Hash
+ - Core/Function
+ - MooTools.More
+
+provides: [Color]
+
+...
+*/
+
+(function(){
+
+var Color = this.Color = new Type('Color', function(color, type){
+ if (arguments.length >= 3){
+ type = 'rgb'; color = Array.slice(arguments, 0, 3);
+ } else if (typeof color == 'string'){
+ if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
+ else if (color.match(/hsb/)) color = color.hsbToRgb();
+ else color = color.hexToRgb(true);
+ }
+ type = type || 'rgb';
+ switch (type){
+ case 'hsb':
+ var old = color;
+ color = color.hsbToRgb();
+ color.hsb = old;
+ break;
+ case 'hex': color = color.hexToRgb(true); break;
+ }
+ color.rgb = color.slice(0, 3);
+ color.hsb = color.hsb || color.rgbToHsb();
+ color.hex = color.rgbToHex();
+ return Object.append(color, this);
+});
+
+Color.implement({
+
+ mix: function(){
+ var colors = Array.slice(arguments);
+ var alpha = (typeOf(colors.getLast()) == 'number') ? colors.pop() : 50;
+ var rgb = this.slice();
+ colors.each(function(color){
+ color = new Color(color);
+ for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
+ });
+ return new Color(rgb, 'rgb');
+ },
+
+ invert: function(){
+ return new Color(this.map(function(value){
+ return 255 - value;
+ }));
+ },
+
+ setHue: function(value){
+ return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
+ },
+
+ setSaturation: function(percent){
+ return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
+ },
+
+ setBrightness: function(percent){
+ return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
+ }
+
+});
+
+this.$RGB = function(r, g, b){
+ return new Color([r, g, b], 'rgb');
+};
+
+this.$HSB = function(h, s, b){
+ return new Color([h, s, b], 'hsb');
+};
+
+this.$HEX = function(hex){
+ return new Color(hex, 'hex');
+};
+
+Array.implement({
+
+ rgbToHsb: function(){
+ var red = this[0],
+ green = this[1],
+ blue = this[2],
+ hue = 0;
+ var max = Math.max(red, green, blue),
+ min = Math.min(red, green, blue);
+ var delta = max - min;
+ var brightness = max / 255,
+ saturation = (max != 0) ? delta / max : 0;
+ if (saturation != 0){
+ var rr = (max - red) / delta;
+ var gr = (max - green) / delta;
+ var br = (max - blue) / delta;
+ if (red == max) hue = br - gr;
+ else if (green == max) hue = 2 + rr - br;
+ else hue = 4 + gr - rr;
+ hue /= 6;
+ if (hue < 0) hue++;
+ }
+ return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
+ },
+
+ hsbToRgb: function(){
+ var br = Math.round(this[2] / 100 * 255);
+ if (this[1] == 0){
+ return [br, br, br];
+ } else {
+ var hue = this[0] % 360;
+ var f = hue % 60;
+ var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
+ var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
+ var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
+ switch (Math.floor(hue / 60)){
+ case 0: return [br, t, p];
+ case 1: return [q, br, p];
+ case 2: return [p, br, t];
+ case 3: return [p, q, br];
+ case 4: return [t, p, br];
+ case 5: return [br, p, q];
+ }
+ }
+ return false;
+ }
+
+});
+
+String.implement({
+
+ rgbToHsb: function(){
+ var rgb = this.match(/\d{1,3}/g);
+ return (rgb) ? rgb.rgbToHsb() : null;
+ },
+
+ hsbToRgb: function(){
+ var hsb = this.match(/\d{1,3}/g);
+ return (hsb) ? hsb.hsbToRgb() : null;
+ }
+
+});
+
+})();
+
+
diff --git a/module/webui/themes/Flat/js/static/mootools-more.min.js b/module/webui/themes/Flat/js/static/mootools-more.min.js
new file mode 100644
index 000000000..ce03a60fd
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/mootools-more.min.js
@@ -0,0 +1,226 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/more/c1cc18c2fff04bcc58921b4dff80a6f1
+
+packager build:
+ - packager build More/Form.Request More/Fx.Reveal More/Sortables More/Request.Periodical More/Color
+
+copyrights:
+ - [MooTools](http://mootools.net)
+
+licenses:
+ - [MIT License](http://mootools.net/license.txt)
+...
+*/
+
+MooTools.More={version:"1.5.0",build:"73db5e24e6e9c5c87b3a27aebef2248053f7db37"};Class.Mutators.Binds=function(a){if(!this.prototype.initialize){this.implement("initialize",function(){});
+}return Array.from(a).concat(this.prototype.Binds||[]);};Class.Mutators.initialize=function(a){return function(){Array.from(this.Binds).each(function(b){var c=this[b];
+if(c){this[b]=c.bind(this);}},this);return a.apply(this,arguments);};};Class.Occlude=new Class({occlude:function(c,b){b=document.id(b||this.element);var a=b.retrieve(c||this.property);
+if(a&&!this.occluded){return(this.occluded=a);}this.occluded=false;b.store(c||this.property,this);return this.occluded;}});Class.refactor=function(b,a){Object.each(a,function(e,d){var c=b.prototype[d];
+c=(c&&c.$origin)||c||function(){};b.implement(d,(typeof e=="function")?function(){var f=this.previous;this.previous=c;var g=e.apply(this,arguments);this.previous=f;
+return g;}:e);});return b;};(function(){var b=function(e,d){var f=[];Object.each(d,function(g){Object.each(g,function(h){e.each(function(i){f.push(i+"-"+h+(i=="border"?"-width":""));
+});});});return f;};var c=function(f,e){var d=0;Object.each(e,function(h,g){if(g.test(f)){d=d+h.toInt();}});return d;};var a=function(d){return !!(!d||d.offsetHeight||d.offsetWidth);
+};Element.implement({measure:function(h){if(a(this)){return h.call(this);}var g=this.getParent(),e=[];while(!a(g)&&g!=document.body){e.push(g.expose());
+g=g.getParent();}var f=this.expose(),d=h.call(this);f();e.each(function(i){i();});return d;},expose:function(){if(this.getStyle("display")!="none"){return function(){};
+}var d=this.style.cssText;this.setStyles({display:"block",position:"absolute",visibility:"hidden"});return function(){this.style.cssText=d;}.bind(this);
+},getDimensions:function(d){d=Object.merge({computeSize:false},d);var i={x:0,y:0};var h=function(j,e){return(e.computeSize)?j.getComputedSize(e):j.getSize();
+};var f=this.getParent("body");if(f&&this.getStyle("display")=="none"){i=this.measure(function(){return h(this,d);});}else{if(f){try{i=h(this,d);}catch(g){}}}return Object.append(i,(i.x||i.x===0)?{width:i.x,height:i.y}:{x:i.width,y:i.height});
+},getComputedSize:function(d){d=Object.merge({styles:["padding","border"],planes:{height:["top","bottom"],width:["left","right"]},mode:"both"},d);var g={},e={width:0,height:0},f;
+if(d.mode=="vertical"){delete e.width;delete d.planes.width;}else{if(d.mode=="horizontal"){delete e.height;delete d.planes.height;}}b(d.styles,d.planes).each(function(h){g[h]=this.getStyle(h).toInt();
+},this);Object.each(d.planes,function(i,h){var k=h.capitalize(),j=this.getStyle(h);if(j=="auto"&&!f){f=this.getDimensions();}j=g[h]=(j=="auto")?f[h]:j.toInt();
+e["total"+k]=j;i.each(function(m){var l=c(m,g);e["computed"+m.capitalize()]=l;e["total"+k]+=l;});},this);return Object.append(e,g);}});})();(function(b){var a=Element.Position={options:{relativeTo:document.body,position:{x:"center",y:"center"},offset:{x:0,y:0}},getOptions:function(d,c){c=Object.merge({},a.options,c);
+a.setPositionOption(c);a.setEdgeOption(c);a.setOffsetOption(d,c);a.setDimensionsOption(d,c);return c;},setPositionOption:function(c){c.position=a.getCoordinateFromValue(c.position);
+},setEdgeOption:function(d){var c=a.getCoordinateFromValue(d.edge);d.edge=c?c:(d.position.x=="center"&&d.position.y=="center")?{x:"center",y:"center"}:{x:"left",y:"top"};
+},setOffsetOption:function(f,d){var c={x:0,y:0};var e={x:0,y:0};var g=f.measure(function(){return document.id(this.getOffsetParent());});if(!g||g==f.getDocument().body){return;
+}e=g.getScroll();c=g.measure(function(){var i=this.getPosition();if(this.getStyle("position")=="fixed"){var h=window.getScroll();i.x+=h.x;i.y+=h.y;}return i;
+});d.offset={parentPositioned:g!=document.id(d.relativeTo),x:d.offset.x-c.x+e.x,y:d.offset.y-c.y+e.y};},setDimensionsOption:function(d,c){c.dimensions=d.getDimensions({computeSize:true,styles:["padding","border","margin"]});
+},getPosition:function(e,d){var c={};d=a.getOptions(e,d);var f=document.id(d.relativeTo)||document.body;a.setPositionCoordinates(d,c,f);if(d.edge){a.toEdge(c,d);
+}var g=d.offset;c.left=((c.x>=0||g.parentPositioned||d.allowNegative)?c.x:0).toInt();c.top=((c.y>=0||g.parentPositioned||d.allowNegative)?c.y:0).toInt();
+a.toMinMax(c,d);if(d.relFixedPosition||f.getStyle("position")=="fixed"){a.toRelFixedPosition(f,c);}if(d.ignoreScroll){a.toIgnoreScroll(f,c);}if(d.ignoreMargins){a.toIgnoreMargins(c,d);
+}c.left=Math.ceil(c.left);c.top=Math.ceil(c.top);delete c.x;delete c.y;return c;},setPositionCoordinates:function(k,g,d){var f=k.offset.y,h=k.offset.x,e=(d==document.body)?window.getScroll():d.getPosition(),j=e.y,c=e.x,i=window.getSize();
+switch(k.position.x){case"left":g.x=c+h;break;case"right":g.x=c+h+d.offsetWidth;break;default:g.x=c+((d==document.body?i.x:d.offsetWidth)/2)+h;break;}switch(k.position.y){case"top":g.y=j+f;
+break;case"bottom":g.y=j+f+d.offsetHeight;break;default:g.y=j+((d==document.body?i.y:d.offsetHeight)/2)+f;break;}},toMinMax:function(c,d){var f={left:"x",top:"y"},e;
+["minimum","maximum"].each(function(g){["left","top"].each(function(h){e=d[g]?d[g][f[h]]:null;if(e!=null&&((g=="minimum")?c[h]<e:c[h]>e)){c[h]=e;}});});
+},toRelFixedPosition:function(e,c){var d=window.getScroll();c.top+=d.y;c.left+=d.x;},toIgnoreScroll:function(e,d){var c=e.getScroll();d.top-=c.y;d.left-=c.x;
+},toIgnoreMargins:function(c,d){c.left+=d.edge.x=="right"?d.dimensions["margin-right"]:(d.edge.x!="center"?-d.dimensions["margin-left"]:-d.dimensions["margin-left"]+((d.dimensions["margin-right"]+d.dimensions["margin-left"])/2));
+c.top+=d.edge.y=="bottom"?d.dimensions["margin-bottom"]:(d.edge.y!="center"?-d.dimensions["margin-top"]:-d.dimensions["margin-top"]+((d.dimensions["margin-bottom"]+d.dimensions["margin-top"])/2));
+},toEdge:function(c,d){var e={},g=d.dimensions,f=d.edge;switch(f.x){case"left":e.x=0;break;case"right":e.x=-g.x-g.computedRight-g.computedLeft;break;default:e.x=-(Math.round(g.totalWidth/2));
+break;}switch(f.y){case"top":e.y=0;break;case"bottom":e.y=-g.y-g.computedTop-g.computedBottom;break;default:e.y=-(Math.round(g.totalHeight/2));break;}c.x+=e.x;
+c.y+=e.y;},getCoordinateFromValue:function(c){if(typeOf(c)!="string"){return c;}c=c.toLowerCase();return{x:c.test("left")?"left":(c.test("right")?"right":"center"),y:c.test(/upper|top/)?"top":(c.test("bottom")?"bottom":"center")};
+}};Element.implement({position:function(d){if(d&&(d.x!=null||d.y!=null)){return(b?b.apply(this,arguments):this);}var c=this.setStyle("position","absolute").calculatePosition(d);
+return(d&&d.returnPos)?c:this.setStyles(c);},calculatePosition:function(c){return a.getPosition(this,c);}});})(Element.prototype.position);(function(){var a=false;
+this.IframeShim=new Class({Implements:[Options,Events,Class.Occlude],options:{className:"iframeShim",src:'javascript:false;document.write("");',display:false,zIndex:null,margin:0,offset:{x:0,y:0},browsers:a},property:"IframeShim",initialize:function(c,b){this.element=document.id(c);
+if(this.occlude()){return this.occluded;}this.setOptions(b);this.makeShim();return this;},makeShim:function(){if(this.options.browsers){var d=this.element.getStyle("zIndex").toInt();
+if(!d){d=1;var c=this.element.getStyle("position");if(c=="static"||!c){this.element.setStyle("position","relative");}this.element.setStyle("zIndex",d);
+}d=((this.options.zIndex!=null||this.options.zIndex===0)&&d>this.options.zIndex)?this.options.zIndex:d-1;if(d<0){d=1;}this.shim=new Element("iframe",{src:this.options.src,scrolling:"no",frameborder:0,styles:{zIndex:d,position:"absolute",border:"none",filter:"progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)"},"class":this.options.className}).store("IframeShim",this);
+var b=(function(){this.shim.inject(this.element,"after");this[this.options.display?"show":"hide"]();this.fireEvent("inject");}).bind(this);if(!IframeShim.ready){window.addEvent("load",b);
+}else{b();}}else{this.position=this.hide=this.show=this.dispose=Function.from(this);}},position:function(){if(!IframeShim.ready||!this.shim){return this;
+}var b=this.element.measure(function(){return this.getSize();});if(this.options.margin!=undefined){b.x=b.x-(this.options.margin*2);b.y=b.y-(this.options.margin*2);
+this.options.offset.x+=this.options.margin;this.options.offset.y+=this.options.margin;}this.shim.set({width:b.x,height:b.y}).position({relativeTo:this.element,offset:this.options.offset});
+return this;},hide:function(){if(this.shim){this.shim.setStyle("display","none");}return this;},show:function(){if(this.shim){this.shim.setStyle("display","block");
+}return this.position();},dispose:function(){if(this.shim){this.shim.dispose();}return this;},destroy:function(){if(this.shim){this.shim.destroy();}return this;
+}});})();window.addEvent("load",function(){IframeShim.ready=true;});var Mask=new Class({Implements:[Options,Events],Binds:["position"],options:{style:{},"class":"mask",maskMargins:false,useIframeShim:true,iframeShimOptions:{}},initialize:function(b,a){this.target=document.id(b)||document.id(document.body);
+this.target.store("mask",this);this.setOptions(a);this.render();this.inject();},render:function(){this.element=new Element("div",{"class":this.options["class"],id:this.options.id||"mask-"+String.uniqueID(),styles:Object.merge({},this.options.style,{display:"none"}),events:{click:function(a){this.fireEvent("click",a);
+if(this.options.hideOnClick){this.hide();}}.bind(this)}});this.hidden=true;},toElement:function(){return this.element;},inject:function(b,a){a=a||(this.options.inject?this.options.inject.where:"")||(this.target==document.body?"inside":"after");
+b=b||(this.options.inject&&this.options.inject.target)||this.target;this.element.inject(b,a);if(this.options.useIframeShim){this.shim=new IframeShim(this.element,this.options.iframeShimOptions);
+this.addEvents({show:this.shim.show.bind(this.shim),hide:this.shim.hide.bind(this.shim),destroy:this.shim.destroy.bind(this.shim)});}},position:function(){this.resize(this.options.width,this.options.height);
+this.element.position({relativeTo:this.target,position:"topLeft",ignoreMargins:!this.options.maskMargins,ignoreScroll:this.target==document.body});return this;
+},resize:function(a,e){var b={styles:["padding","border"]};if(this.options.maskMargins){b.styles.push("margin");}var d=this.target.getComputedSize(b);if(this.target==document.body){this.element.setStyles({width:0,height:0});
+var c=window.getScrollSize();if(d.totalHeight<c.y){d.totalHeight=c.y;}if(d.totalWidth<c.x){d.totalWidth=c.x;}}this.element.setStyles({width:Array.pick([a,d.totalWidth,d.x]),height:Array.pick([e,d.totalHeight,d.y])});
+return this;},show:function(){if(!this.hidden){return this;}window.addEvent("resize",this.position);this.position();this.showMask.apply(this,arguments);
+return this;},showMask:function(){this.element.setStyle("display","block");this.hidden=false;this.fireEvent("show");},hide:function(){if(this.hidden){return this;
+}window.removeEvent("resize",this.position);this.hideMask.apply(this,arguments);if(this.options.destroyOnHide){return this.destroy();}return this;},hideMask:function(){this.element.setStyle("display","none");
+this.hidden=true;this.fireEvent("hide");},toggle:function(){this[this.hidden?"show":"hide"]();},destroy:function(){this.hide();this.element.destroy();this.fireEvent("destroy");
+this.target.eliminate("mask");}});Element.Properties.mask={set:function(b){var a=this.retrieve("mask");if(a){a.destroy();}return this.eliminate("mask").store("mask:options",b);
+},get:function(){var a=this.retrieve("mask");if(!a){a=new Mask(this,this.retrieve("mask:options"));this.store("mask",a);}return a;}};Element.implement({mask:function(a){if(a){this.set("mask",a);
+}this.get("mask").show();return this;},unmask:function(){this.get("mask").hide();return this;}});var Spinner=new Class({Extends:Mask,Implements:Chain,options:{"class":"spinner",containerPosition:{},content:{"class":"spinner-content"},messageContainer:{"class":"spinner-msg"},img:{"class":"spinner-img"},fxOptions:{link:"chain"}},initialize:function(c,a){this.target=document.id(c)||document.id(document.body);
+this.target.store("spinner",this);this.setOptions(a);this.render();this.inject();var b=function(){this.active=false;}.bind(this);this.addEvents({hide:b,show:b});
+},render:function(){this.parent();this.element.set("id",this.options.id||"spinner-"+String.uniqueID());this.content=document.id(this.options.content)||new Element("div",this.options.content);
+this.content.inject(this.element);if(this.options.message){this.msg=document.id(this.options.message)||new Element("p",this.options.messageContainer).appendText(this.options.message);
+this.msg.inject(this.content);}if(this.options.img){this.img=document.id(this.options.img)||new Element("div",this.options.img);this.img.inject(this.content);
+}this.element.set("tween",this.options.fxOptions);},show:function(a){if(this.active){return this.chain(this.show.bind(this));}if(!this.hidden){this.callChain.delay(20,this);
+return this;}this.target.set("aria-busy","true");this.active=true;return this.parent(a);},showMask:function(a){var b=function(){this.content.position(Object.merge({relativeTo:this.element},this.options.containerPosition));
+}.bind(this);if(a){this.parent();b();}else{if(!this.options.style.opacity){this.options.style.opacity=this.element.getStyle("opacity").toFloat();}this.element.setStyles({display:"block",opacity:0}).tween("opacity",this.options.style.opacity);
+b();this.hidden=false;this.fireEvent("show");this.callChain();}},hide:function(a){if(this.active){return this.chain(this.hide.bind(this));}if(this.hidden){this.callChain.delay(20,this);
+return this;}this.target.set("aria-busy","false");this.active=true;return this.parent(a);},hideMask:function(a){if(a){return this.parent();}this.element.tween("opacity",0).get("tween").chain(function(){this.element.setStyle("display","none");
+this.hidden=true;this.fireEvent("hide");this.callChain();}.bind(this));},destroy:function(){this.content.destroy();this.parent();this.target.eliminate("spinner");
+}});Request=Class.refactor(Request,{options:{useSpinner:false,spinnerOptions:{},spinnerTarget:false},initialize:function(a){this._send=this.send;this.send=function(b){var c=this.getSpinner();
+if(c){c.chain(this._send.pass(b,this)).show();}else{this._send(b);}return this;};this.previous(a);},getSpinner:function(){if(!this.spinner){var b=document.id(this.options.spinnerTarget)||document.id(this.options.update);
+if(this.options.useSpinner&&b){b.set("spinner",this.options.spinnerOptions);var a=this.spinner=b.get("spinner");["complete","exception","cancel"].each(function(c){this.addEvent(c,a.hide.bind(a));
+},this);}}return this.spinner;}});Element.Properties.spinner={set:function(a){var b=this.retrieve("spinner");if(b){b.destroy();}return this.eliminate("spinner").store("spinner:options",a);
+},get:function(){var a=this.retrieve("spinner");if(!a){a=new Spinner(this,this.retrieve("spinner:options"));this.store("spinner",a);}return a;}};Element.implement({spin:function(a){if(a){this.set("spinner",a);
+}this.get("spinner").show();return this;},unspin:function(){this.get("spinner").hide();return this;}});String.implement({parseQueryString:function(d,a){if(d==null){d=true;
+}if(a==null){a=true;}var c=this.split(/[&;]/),b={};if(!c.length){return b;}c.each(function(i){var e=i.indexOf("=")+1,g=e?i.substr(e):"",f=e?i.substr(0,e-1).match(/([^\]\[]+|(\B)(?=\]))/g):[i],h=b;
+if(!f){return;}if(a){g=decodeURIComponent(g);}f.each(function(k,j){if(d){k=decodeURIComponent(k);}var l=h[k];if(j<f.length-1){h=h[k]=l||{};}else{if(typeOf(l)=="array"){l.push(g);
+}else{h[k]=l!=null?[l,g]:g;}}});});return b;},cleanQueryString:function(a){return this.split("&").filter(function(e){var b=e.indexOf("="),c=b<0?"":e.substr(0,b),d=e.substr(b+1);
+return a?a.call(null,c,d):(d||d===0);}).join("&");}});(function(){Events.Pseudos=function(h,e,f){var d="_monitorEvents:";var c=function(i){return{store:i.store?function(j,k){i.store(d+j,k);
+}:function(j,k){(i._monitorEvents||(i._monitorEvents={}))[j]=k;},retrieve:i.retrieve?function(j,k){return i.retrieve(d+j,k);}:function(j,k){if(!i._monitorEvents){return k;
+}return i._monitorEvents[j]||k;}};};var g=function(k){if(k.indexOf(":")==-1||!h){return null;}var j=Slick.parse(k).expressions[0][0],p=j.pseudos,i=p.length,o=[];
+while(i--){var n=p[i].key,m=h[n];if(m!=null){o.push({event:j.tag,value:p[i].value,pseudo:n,original:k,listener:m});}}return o.length?o:null;};return{addEvent:function(m,p,j){var n=g(m);
+if(!n){return e.call(this,m,p,j);}var k=c(this),r=k.retrieve(m,[]),i=n[0].event,l=Array.slice(arguments,2),o=p,q=this;n.each(function(s){var t=s.listener,u=o;
+if(t==false){i+=":"+s.pseudo+"("+s.value+")";}else{o=function(){t.call(q,s,u,arguments,o);};}});r.include({type:i,event:p,monitor:o});k.store(m,r);if(m!=i){e.apply(this,[m,p].concat(l));
+}return e.apply(this,[i,o].concat(l));},removeEvent:function(m,l){var k=g(m);if(!k){return f.call(this,m,l);}var n=c(this),j=n.retrieve(m);if(!j){return this;
+}var i=Array.slice(arguments,2);f.apply(this,[m,l].concat(i));j.each(function(o,p){if(!l||o.event==l){f.apply(this,[o.type,o.monitor].concat(i));}delete j[p];
+},this);n.store(m,j);return this;}};};var b={once:function(e,f,d,c){f.apply(this,d);this.removeEvent(e.event,c).removeEvent(e.original,f);},throttle:function(d,e,c){if(!e._throttled){e.apply(this,c);
+e._throttled=setTimeout(function(){e._throttled=false;},d.value||250);}},pause:function(d,e,c){clearTimeout(e._pause);e._pause=e.delay(d.value||250,this,c);
+}};Events.definePseudo=function(c,d){b[c]=d;return this;};Events.lookupPseudo=function(c){return b[c];};var a=Events.prototype;Events.implement(Events.Pseudos(b,a.addEvent,a.removeEvent));
+["Request","Fx"].each(function(c){if(this[c]){this[c].implement(Events.prototype);}});})();(function(){var d={relay:false},c=["once","throttle","pause"],b=c.length;
+while(b--){d[c[b]]=Events.lookupPseudo(c[b]);}DOMEvent.definePseudo=function(e,f){d[e]=f;return this;};var a=Element.prototype;[Element,Window,Document].invoke("implement",Events.Pseudos(d,a.addEvent,a.removeEvent));
+})();if(!window.Form){window.Form={};}(function(){Form.Request=new Class({Binds:["onSubmit","onFormValidate"],Implements:[Options,Events,Class.Occlude],options:{requestOptions:{evalScripts:true,useSpinner:true,emulation:false,link:"ignore"},sendButtonClicked:true,extraData:{},resetForm:true},property:"form.request",initialize:function(b,c,a){this.element=document.id(b);
+if(this.occlude()){return this.occluded;}this.setOptions(a).setTarget(c).attach();},setTarget:function(a){this.target=document.id(a);if(!this.request){this.makeRequest();
+}else{this.request.setOptions({update:this.target});}return this;},toElement:function(){return this.element;},makeRequest:function(){var a=this;this.request=new Request.HTML(Object.merge({update:this.target,emulation:false,spinnerTarget:this.element,method:this.element.get("method")||"post"},this.options.requestOptions)).addEvents({success:function(c,e,d,b){["complete","success"].each(function(f){a.fireEvent(f,[a.target,c,e,d,b]);
+});},failure:function(){a.fireEvent("complete",arguments).fireEvent("failure",arguments);},exception:function(){a.fireEvent("failure",arguments);}});return this.attachReset();
+},attachReset:function(){if(!this.options.resetForm){return this;}this.request.addEvent("success",function(){Function.attempt(function(){this.element.reset();
+}.bind(this));if(window.OverText){OverText.update();}}.bind(this));return this;},attach:function(a){var c=(a!=false)?"addEvent":"removeEvent";this.element[c]("click:relay(button, input[type=submit])",this.saveClickedButton.bind(this));
+var b=this.element.retrieve("validator");if(b){b[c]("onFormValidate",this.onFormValidate);}else{this.element[c]("submit",this.onSubmit);}return this;},detach:function(){return this.attach(false);
+},enable:function(){return this.attach();},disable:function(){return this.detach();},onFormValidate:function(c,b,a){if(!a){return;}var d=this.element.retrieve("validator");
+if(c||(d&&!d.options.stopOnFailure)){a.stop();this.send();}},onSubmit:function(a){var b=this.element.retrieve("validator");if(b){this.element.removeEvent("submit",this.onSubmit);
+b.addEvent("onFormValidate",this.onFormValidate);b.validate(a);return;}if(a){a.stop();}this.send();},saveClickedButton:function(b,c){var a=c.get("name");
+if(!a||!this.options.sendButtonClicked){return;}this.options.extraData[a]=c.get("value")||true;this.clickedCleaner=function(){delete this.options.extraData[a];
+this.clickedCleaner=function(){};}.bind(this);},clickedCleaner:function(){},send:function(){var b=this.element.toQueryString().trim(),a=Object.toQueryString(this.options.extraData);
+if(b){b+="&"+a;}else{b=a;}this.fireEvent("send",[this.element,b.parseQueryString()]);this.request.send({data:b,url:this.options.requestOptions.url||this.element.get("action")});
+this.clickedCleaner();return this;}});Element.implement("formUpdate",function(c,b){var a=this.retrieve("form.request");if(!a){a=new Form.Request(this,c,b);
+}else{if(c){a.setTarget(c);}if(b){a.setOptions(b).makeRequest();}}a.send();return this;});})();Element.implement({isDisplayed:function(){return this.getStyle("display")!="none";
+},isVisible:function(){var a=this.offsetWidth,b=this.offsetHeight;return(a==0&&b==0)?false:(a>0&&b>0)?true:this.style.display!="none";},toggle:function(){return this[this.isDisplayed()?"hide":"show"]();
+},hide:function(){var b;try{b=this.getStyle("display");}catch(a){}if(b=="none"){return this;}return this.store("element:_originalDisplay",b||"").setStyle("display","none");
+},show:function(a){if(!a&&this.isDisplayed()){return this;}a=a||this.retrieve("element:_originalDisplay")||"block";return this.setStyle("display",(a=="none")?"block":a);
+},swapClass:function(a,b){return this.removeClass(a).addClass(b);}});Document.implement({clearSelection:function(){if(window.getSelection){var a=window.getSelection();
+if(a&&a.removeAllRanges){a.removeAllRanges();}}else{if(document.selection&&document.selection.empty){try{document.selection.empty();}catch(b){}}}}});(function(){var a=function(d){var b=d.options.hideInputs;
+if(window.OverText){var c=[null];OverText.each(function(e){c.include("."+e.options.labelClass);});if(c){b+=c.join(", ");}}return(b)?d.element.getElements(b):null;
+};Fx.Reveal=new Class({Extends:Fx.Morph,options:{link:"cancel",styles:["padding","border","margin"],transitionOpacity:"opacity" in document.documentElement,mode:"vertical",display:function(){return this.element.get("tag")!="tr"?"block":"table-row";
+},opacity:1,hideInputs:!("opacity" in document.documentElement)?"select, input, textarea, object, embed":null},dissolve:function(){if(!this.hiding&&!this.showing){if(this.element.getStyle("display")!="none"){this.hiding=true;
+this.showing=false;this.hidden=true;this.cssText=this.element.style.cssText;var d=this.element.getComputedSize({styles:this.options.styles,mode:this.options.mode});
+if(this.options.transitionOpacity){d.opacity=this.options.opacity;}var c={};Object.each(d,function(f,e){c[e]=[f,0];});this.element.setStyles({display:Function.from(this.options.display).call(this),overflow:"hidden"});
+var b=a(this);if(b){b.setStyle("visibility","hidden");}this.$chain.unshift(function(){if(this.hidden){this.hiding=false;this.element.style.cssText=this.cssText;
+this.element.setStyle("display","none");if(b){b.setStyle("visibility","visible");}}this.fireEvent("hide",this.element);this.callChain();}.bind(this));this.start(c);
+}else{this.callChain.delay(10,this);this.fireEvent("complete",this.element);this.fireEvent("hide",this.element);}}else{if(this.options.link=="chain"){this.chain(this.dissolve.bind(this));
+}else{if(this.options.link=="cancel"&&!this.hiding){this.cancel();this.dissolve();}}}return this;},reveal:function(){if(!this.showing&&!this.hiding){if(this.element.getStyle("display")=="none"){this.hiding=false;
+this.showing=true;this.hidden=false;this.cssText=this.element.style.cssText;var d;this.element.measure(function(){d=this.element.getComputedSize({styles:this.options.styles,mode:this.options.mode});
+}.bind(this));if(this.options.heightOverride!=null){d.height=this.options.heightOverride.toInt();}if(this.options.widthOverride!=null){d.width=this.options.widthOverride.toInt();
+}if(this.options.transitionOpacity){this.element.setStyle("opacity",0);d.opacity=this.options.opacity;}var c={height:0,display:Function.from(this.options.display).call(this)};
+Object.each(d,function(f,e){c[e]=0;});c.overflow="hidden";this.element.setStyles(c);var b=a(this);if(b){b.setStyle("visibility","hidden");}this.$chain.unshift(function(){this.element.style.cssText=this.cssText;
+this.element.setStyle("display",Function.from(this.options.display).call(this));if(!this.hidden){this.showing=false;}if(b){b.setStyle("visibility","visible");
+}this.callChain();this.fireEvent("show",this.element);}.bind(this));this.start(d);}else{this.callChain();this.fireEvent("complete",this.element);this.fireEvent("show",this.element);
+}}else{if(this.options.link=="chain"){this.chain(this.reveal.bind(this));}else{if(this.options.link=="cancel"&&!this.showing){this.cancel();this.reveal();
+}}}return this;},toggle:function(){if(this.element.getStyle("display")=="none"){this.reveal();}else{this.dissolve();}return this;},cancel:function(){this.parent.apply(this,arguments);
+if(this.cssText!=null){this.element.style.cssText=this.cssText;}this.hiding=false;this.showing=false;return this;}});Element.Properties.reveal={set:function(b){this.get("reveal").cancel().setOptions(b);
+return this;},get:function(){var b=this.retrieve("reveal");if(!b){b=new Fx.Reveal(this);this.store("reveal",b);}return b;}};Element.Properties.dissolve=Element.Properties.reveal;
+Element.implement({reveal:function(b){this.get("reveal").setOptions(b).reveal();return this;},dissolve:function(b){this.get("reveal").setOptions(b).dissolve();
+return this;},nix:function(b){var c=Array.link(arguments,{destroy:Type.isBoolean,options:Type.isObject});this.get("reveal").setOptions(b).dissolve().chain(function(){this[c.destroy?"destroy":"dispose"]();
+}.bind(this));return this;},wink:function(){var c=Array.link(arguments,{duration:Type.isNumber,options:Type.isObject});var b=this.get("reveal").setOptions(c.options);
+b.reveal().chain(function(){(function(){b.dissolve();}).delay(c.duration||2000);});}});})();var Drag=new Class({Implements:[Events,Options],options:{snap:6,unit:"px",grid:false,style:true,limit:false,handle:false,invert:false,preventDefault:false,stopPropagation:false,modifiers:{x:"left",y:"top"}},initialize:function(){var b=Array.link(arguments,{options:Type.isObject,element:function(c){return c!=null;
+}});this.element=document.id(b.element);this.document=this.element.getDocument();this.setOptions(b.options||{});var a=typeOf(this.options.handle);this.handles=((a=="array"||a=="collection")?$$(this.options.handle):document.id(this.options.handle))||this.element;
+this.mouse={now:{},pos:{}};this.value={start:{},now:{}};this.selection="selectstart" in document?"selectstart":"mousedown";if("ondragstart" in document&&!("FileReader" in window)&&!Drag.ondragstartFixed){document.ondragstart=Function.from(false);
+Drag.ondragstartFixed=true;}this.bound={start:this.start.bind(this),check:this.check.bind(this),drag:this.drag.bind(this),stop:this.stop.bind(this),cancel:this.cancel.bind(this),eventStop:Function.from(false)};
+this.attach();},attach:function(){this.handles.addEvent("mousedown",this.bound.start);return this;},detach:function(){this.handles.removeEvent("mousedown",this.bound.start);
+return this;},start:function(a){var j=this.options;if(a.rightClick){return;}if(j.preventDefault){a.preventDefault();}if(j.stopPropagation){a.stopPropagation();
+}this.mouse.start=a.page;this.fireEvent("beforeStart",this.element);var c=j.limit;this.limit={x:[],y:[]};var e,g;for(e in j.modifiers){if(!j.modifiers[e]){continue;
+}var b=this.element.getStyle(j.modifiers[e]);if(b&&!b.match(/px$/)){if(!g){g=this.element.getCoordinates(this.element.getOffsetParent());}b=g[j.modifiers[e]];
+}if(j.style){this.value.now[e]=(b||0).toInt();}else{this.value.now[e]=this.element[j.modifiers[e]];}if(j.invert){this.value.now[e]*=-1;}this.mouse.pos[e]=a.page[e]-this.value.now[e];
+if(c&&c[e]){var d=2;while(d--){var f=c[e][d];if(f||f===0){this.limit[e][d]=(typeof f=="function")?f():f;}}}}if(typeOf(this.options.grid)=="number"){this.options.grid={x:this.options.grid,y:this.options.grid};
+}var h={mousemove:this.bound.check,mouseup:this.bound.cancel};h[this.selection]=this.bound.eventStop;this.document.addEvents(h);},check:function(a){if(this.options.preventDefault){a.preventDefault();
+}var b=Math.round(Math.sqrt(Math.pow(a.page.x-this.mouse.start.x,2)+Math.pow(a.page.y-this.mouse.start.y,2)));if(b>this.options.snap){this.cancel();this.document.addEvents({mousemove:this.bound.drag,mouseup:this.bound.stop});
+this.fireEvent("start",[this.element,a]).fireEvent("snap",this.element);}},drag:function(b){var a=this.options;if(a.preventDefault){b.preventDefault();
+}this.mouse.now=b.page;for(var c in a.modifiers){if(!a.modifiers[c]){continue;}this.value.now[c]=this.mouse.now[c]-this.mouse.pos[c];if(a.invert){this.value.now[c]*=-1;
+}if(a.limit&&this.limit[c]){if((this.limit[c][1]||this.limit[c][1]===0)&&(this.value.now[c]>this.limit[c][1])){this.value.now[c]=this.limit[c][1];}else{if((this.limit[c][0]||this.limit[c][0]===0)&&(this.value.now[c]<this.limit[c][0])){this.value.now[c]=this.limit[c][0];
+}}}if(a.grid[c]){this.value.now[c]-=((this.value.now[c]-(this.limit[c][0]||0))%a.grid[c]);}if(a.style){this.element.setStyle(a.modifiers[c],this.value.now[c]+a.unit);
+}else{this.element[a.modifiers[c]]=this.value.now[c];}}this.fireEvent("drag",[this.element,b]);},cancel:function(a){this.document.removeEvents({mousemove:this.bound.check,mouseup:this.bound.cancel});
+if(a){this.document.removeEvent(this.selection,this.bound.eventStop);this.fireEvent("cancel",this.element);}},stop:function(b){var a={mousemove:this.bound.drag,mouseup:this.bound.stop};
+a[this.selection]=this.bound.eventStop;this.document.removeEvents(a);if(b){this.fireEvent("complete",[this.element,b]);}}});Element.implement({makeResizable:function(a){var b=new Drag(this,Object.merge({modifiers:{x:"width",y:"height"}},a));
+this.store("resizer",b);return b.addEvent("drag",function(){this.fireEvent("resize",b);}.bind(this));}});Drag.Move=new Class({Extends:Drag,options:{droppables:[],container:false,precalculate:false,includeMargins:true,checkDroppables:true},initialize:function(b,a){this.parent(b,a);
+b=this.element;this.droppables=$$(this.options.droppables);this.setContainer(this.options.container);if(this.options.style){if(this.options.modifiers.x=="left"&&this.options.modifiers.y=="top"){var c=b.getOffsetParent(),d=b.getStyles("left","top");
+if(c&&(d.left=="auto"||d.top=="auto")){b.setPosition(b.getPosition(c));}}if(b.getStyle("position")=="static"){b.setStyle("position","absolute");}}this.addEvent("start",this.checkDroppables,true);
+this.overed=null;},setContainer:function(a){this.container=document.id(a);if(this.container&&typeOf(this.container)!="element"){this.container=document.id(this.container.getDocument().body);
+}},start:function(a){if(this.container){this.options.limit=this.calculateLimit();}if(this.options.precalculate){this.positions=this.droppables.map(function(b){return b.getCoordinates();
+});}this.parent(a);},calculateLimit:function(){var j=this.element,e=this.container,d=document.id(j.getOffsetParent())||document.body,h=e.getCoordinates(d),c={},b={},k={},g={},m={};
+["top","right","bottom","left"].each(function(q){c[q]=j.getStyle("margin-"+q).toInt();b[q]=j.getStyle("border-"+q).toInt();k[q]=e.getStyle("margin-"+q).toInt();
+g[q]=e.getStyle("border-"+q).toInt();m[q]=d.getStyle("padding-"+q).toInt();},this);var f=j.offsetWidth+c.left+c.right,p=j.offsetHeight+c.top+c.bottom,i=0,l=0,o=h.right-g.right-f,a=h.bottom-g.bottom-p;
+if(this.options.includeMargins){i+=c.left;l+=c.top;}else{o+=c.right;a+=c.bottom;}if(j.getStyle("position")=="relative"){var n=j.getCoordinates(d);n.left-=j.getStyle("left").toInt();
+n.top-=j.getStyle("top").toInt();i-=n.left;l-=n.top;if(e.getStyle("position")!="relative"){i+=g.left;l+=g.top;}o+=c.left-n.left;a+=c.top-n.top;if(e!=d){i+=k.left+m.left;
+if(!m.left&&i<0){i=0;}l+=d==document.body?0:k.top+m.top;if(!m.top&&l<0){l=0;}}}else{i-=c.left;l-=c.top;if(e!=d){i+=h.left+g.left;l+=h.top+g.top;}}return{x:[i,o],y:[l,a]};
+},getDroppableCoordinates:function(c){var b=c.getCoordinates();if(c.getStyle("position")=="fixed"){var a=window.getScroll();b.left+=a.x;b.right+=a.x;b.top+=a.y;
+b.bottom+=a.y;}return b;},checkDroppables:function(){var a=this.droppables.filter(function(d,c){d=this.positions?this.positions[c]:this.getDroppableCoordinates(d);
+var b=this.mouse.now;return(b.x>d.left&&b.x<d.right&&b.y<d.bottom&&b.y>d.top);},this).getLast();if(this.overed!=a){if(this.overed){this.fireEvent("leave",[this.element,this.overed]);
+}if(a){this.fireEvent("enter",[this.element,a]);}this.overed=a;}},drag:function(a){this.parent(a);if(this.options.checkDroppables&&this.droppables.length){this.checkDroppables();
+}},stop:function(a){this.checkDroppables();this.fireEvent("drop",[this.element,this.overed,a]);this.overed=null;return this.parent(a);}});Element.implement({makeDraggable:function(a){var b=new Drag.Move(this,a);
+this.store("dragger",b);return b;}});var Sortables=new Class({Implements:[Events,Options],options:{opacity:1,clone:false,revert:false,handle:false,dragOptions:{},unDraggableTags:["button","input","a","textarea","select","option"]},initialize:function(a,b){this.setOptions(b);
+this.elements=[];this.lists=[];this.idle=true;this.addLists($$(document.id(a)||a));if(!this.options.clone){this.options.revert=false;}if(this.options.revert){this.effect=new Fx.Morph(null,Object.merge({duration:250,link:"cancel"},this.options.revert));
+}},attach:function(){this.addLists(this.lists);return this;},detach:function(){this.lists=this.removeLists(this.lists);return this;},addItems:function(){Array.flatten(arguments).each(function(a){this.elements.push(a);
+var b=a.retrieve("sortables:start",function(c){this.start.call(this,c,a);}.bind(this));(this.options.handle?a.getElement(this.options.handle)||a:a).addEvent("mousedown",b);
+},this);return this;},addLists:function(){Array.flatten(arguments).each(function(a){this.lists.include(a);this.addItems(a.getChildren());},this);return this;
+},removeItems:function(){return $$(Array.flatten(arguments).map(function(a){this.elements.erase(a);var b=a.retrieve("sortables:start");(this.options.handle?a.getElement(this.options.handle)||a:a).removeEvent("mousedown",b);
+return a;},this));},removeLists:function(){return $$(Array.flatten(arguments).map(function(a){this.lists.erase(a);this.removeItems(a.getChildren());return a;
+},this));},getDroppableCoordinates:function(c){var d=c.getOffsetParent();var b=c.getPosition(d);var a={w:window.getScroll(),offsetParent:d.getScroll()};
+b.x+=a.offsetParent.x;b.y+=a.offsetParent.y;if(d.getStyle("position")=="fixed"){b.x-=a.w.x;b.y-=a.w.y;}return b;},getClone:function(b,a){if(!this.options.clone){return new Element(a.tagName).inject(document.body);
+}if(typeOf(this.options.clone)=="function"){return this.options.clone.call(this,b,a,this.list);}var c=a.clone(true).setStyles({margin:0,position:"absolute",visibility:"hidden",width:a.getStyle("width")}).addEvent("mousedown",function(d){a.fireEvent("mousedown",d);
+});if(c.get("html").test("radio")){c.getElements("input[type=radio]").each(function(d,e){d.set("name","clone_"+e);if(d.get("checked")){a.getElements("input[type=radio]")[e].set("checked",true);
+}});}return c.inject(this.list).setPosition(this.getDroppableCoordinates(this.element));},getDroppables:function(){var a=this.list.getChildren().erase(this.clone).erase(this.element);
+if(!this.options.constrain){a.append(this.lists).erase(this.list);}return a;},insert:function(c,b){var a="inside";if(this.lists.contains(b)){this.list=b;
+this.drag.droppables=this.getDroppables();}else{a=this.element.getAllPrevious().contains(b)?"before":"after";}this.element.inject(b,a);this.fireEvent("sort",[this.element,this.clone]);
+},start:function(b,a){if(!this.idle||b.rightClick||(!this.options.handle&&this.options.unDraggableTags.contains(b.target.get("tag")))){return;}this.idle=false;
+this.element=a;this.opacity=a.getStyle("opacity");this.list=a.getParent();this.clone=this.getClone(b,a);this.drag=new Drag.Move(this.clone,Object.merge({droppables:this.getDroppables()},this.options.dragOptions)).addEvents({onSnap:function(){b.stop();
+this.clone.setStyle("visibility","visible");this.element.setStyle("opacity",this.options.opacity||0);this.fireEvent("start",[this.element,this.clone]);
+}.bind(this),onEnter:this.insert.bind(this),onCancel:this.end.bind(this),onComplete:this.end.bind(this)});this.clone.inject(this.element,"before");this.drag.start(b);
+},end:function(){this.drag.detach();this.element.setStyle("opacity",this.opacity);var a=this;if(this.effect){var c=this.element.getStyles("width","height"),e=this.clone,d=e.computePosition(this.getDroppableCoordinates(e));
+var b=function(){this.removeEvent("cancel",b);e.destroy();a.reset();};this.effect.element=e;this.effect.start({top:d.top,left:d.left,width:c.width,height:c.height,opacity:0.25}).addEvent("cancel",b).chain(b);
+}else{this.clone.destroy();a.reset();}},reset:function(){this.idle=true;this.fireEvent("complete",this.element);},serialize:function(){var c=Array.link(arguments,{modifier:Type.isFunction,index:function(d){return d!=null;
+}});var b=this.lists.map(function(d){return d.getChildren().map(c.modifier||function(e){return e.get("id");},this);},this);var a=c.index;if(this.lists.length==1){a=0;
+}return(a||a===0)&&a>=0&&a<this.lists.length?b[a]:b;}});Request.implement({options:{initialDelay:5000,delay:5000,limit:60000},startTimer:function(b){var a=function(){if(!this.running){this.send({data:b});
+}};this.lastDelay=this.options.initialDelay;this.timer=a.delay(this.lastDelay,this);this.completeCheck=function(c){clearTimeout(this.timer);this.lastDelay=(c)?this.options.delay:(this.lastDelay+this.options.delay).min(this.options.limit);
+this.timer=a.delay(this.lastDelay,this);};return this.addEvent("complete",this.completeCheck);},stopTimer:function(){clearTimeout(this.timer);return this.removeEvent("complete",this.completeCheck);
+}});(function(){var a=this.Color=new Type("Color",function(c,d){if(arguments.length>=3){d="rgb";c=Array.slice(arguments,0,3);}else{if(typeof c=="string"){if(c.match(/rgb/)){c=c.rgbToHex().hexToRgb(true);
+}else{if(c.match(/hsb/)){c=c.hsbToRgb();}else{c=c.hexToRgb(true);}}}}d=d||"rgb";switch(d){case"hsb":var b=c;c=c.hsbToRgb();c.hsb=b;break;case"hex":c=c.hexToRgb(true);
+break;}c.rgb=c.slice(0,3);c.hsb=c.hsb||c.rgbToHsb();c.hex=c.rgbToHex();return Object.append(c,this);});a.implement({mix:function(){var b=Array.slice(arguments);
+var d=(typeOf(b.getLast())=="number")?b.pop():50;var c=this.slice();b.each(function(e){e=new a(e);for(var f=0;f<3;f++){c[f]=Math.round((c[f]/100*(100-d))+(e[f]/100*d));
+}});return new a(c,"rgb");},invert:function(){return new a(this.map(function(b){return 255-b;}));},setHue:function(b){return new a([b,this.hsb[1],this.hsb[2]],"hsb");
+},setSaturation:function(b){return new a([this.hsb[0],b,this.hsb[2]],"hsb");},setBrightness:function(b){return new a([this.hsb[0],this.hsb[1],b],"hsb");
+}});this.$RGB=function(e,d,c){return new a([e,d,c],"rgb");};this.$HSB=function(e,d,c){return new a([e,d,c],"hsb");};this.$HEX=function(b){return new a(b,"hex");
+};Array.implement({rgbToHsb:function(){var c=this[0],d=this[1],k=this[2],h=0;var j=Math.max(c,d,k),f=Math.min(c,d,k);var l=j-f;var i=j/255,g=(j!=0)?l/j:0;
+if(g!=0){var e=(j-c)/l;var b=(j-d)/l;var m=(j-k)/l;if(c==j){h=m-b;}else{if(d==j){h=2+e-m;}else{h=4+b-e;}}h/=6;if(h<0){h++;}}return[Math.round(h*360),Math.round(g*100),Math.round(i*100)];
+},hsbToRgb:function(){var d=Math.round(this[2]/100*255);if(this[1]==0){return[d,d,d];}else{var b=this[0]%360;var g=b%60;var h=Math.round((this[2]*(100-this[1]))/10000*255);
+var e=Math.round((this[2]*(6000-this[1]*g))/600000*255);var c=Math.round((this[2]*(6000-this[1]*(60-g)))/600000*255);switch(Math.floor(b/60)){case 0:return[d,c,h];
+case 1:return[e,d,h];case 2:return[h,d,c];case 3:return[h,e,d];case 4:return[c,h,d];case 5:return[d,h,e];}}return false;}});String.implement({rgbToHsb:function(){var b=this.match(/\d{1,3}/g);
+return(b)?b.rgbToHsb():null;},hsbToRgb:function(){var b=this.match(/\d{1,3}/g);return(b)?b.hsbToRgb():null;}});})(); \ No newline at end of file
diff --git a/module/webui/themes/Flat/js/static/purr.js b/module/webui/themes/Flat/js/static/purr.js
new file mode 100644
index 000000000..9cbc503d9
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/purr.js
@@ -0,0 +1,309 @@
+/*
+---
+script: purr.js
+
+description: Class to create growl-style popup notifications.
+
+license: MIT-style
+
+authors: [atom smith]
+
+requires:
+- core/1.3: [Core, Browser, Array, Function, Number, String, Hash, Event, Class.Extras, Element.Event, Element.Style, Element.Dimensions, Fx.CSS, FX.Tween, Fx.Morph]
+
+provides: [Purr, Element.alert]
+...
+*/
+
+
+var Purr = new Class({
+
+ 'options': {
+ 'mode': 'top',
+ 'position': 'left',
+ 'elementAlertClass': 'purr-element-alert',
+ 'elements': {
+ 'wrapper': 'div',
+ 'alert': 'div',
+ 'buttonWrapper': 'div',
+ 'button': 'button'
+ },
+ 'elementOptions': {
+ 'wrapper': {
+ 'styles': {
+ 'position': 'fixed',
+ 'z-index': '9999'
+ },
+ 'class': 'purr-wrapper'
+ },
+ 'alert': {
+ 'class': 'purr-alert',
+ 'styles': {
+ 'opacity': '.85'
+ }
+ },
+ 'buttonWrapper': {
+ 'class': 'purr-button-wrapper'
+ },
+ 'button': {
+ 'class': 'purr-button'
+ }
+ },
+ 'alert': {
+ 'buttons': [],
+ 'clickDismiss': true,
+ 'hoverWait': true,
+ 'hideAfter': 5000,
+ 'fx': {
+ 'duration': 500
+ },
+ 'highlight': false,
+ 'highlightRepeat': false,
+ 'highlight': {
+ 'start': '#FF0',
+ 'end': false
+ }
+ }
+ },
+
+ 'Implements': [Options, Events, Chain],
+
+ 'initialize': function(options){
+ this.setOptions(options);
+ this.createWrapper();
+ return this;
+ },
+
+ 'bindAlert': function(){
+ return this.alert.bind(this);
+ },
+
+ 'createWrapper': function(){
+ this.wrapper = new Element(this.options.elements.wrapper, this.options.elementOptions.wrapper);
+ if(this.options.mode == 'top')
+ {
+ this.wrapper.setStyle('top', 0);
+ }
+ else
+ {
+ this.wrapper.setStyle('bottom', 0);
+ }
+ document.id(document.body).grab(this.wrapper);
+ this.positionWrapper(this.options.position);
+ },
+
+ 'positionWrapper': function(position){
+ if(typeOf(position) == 'object')
+ {
+
+ var wrapperCoords = this.getWrapperCoords();
+
+ this.wrapper.setStyles({
+ 'bottom': '',
+ 'left': position.x,
+ 'top': position.y - wrapperCoords.height,
+ 'position': 'absolute'
+ });
+ }
+ else if(position == 'left')
+ {
+ this.wrapper.setStyle('left', 0);
+ }
+ else if(position == 'right')
+ {
+ this.wrapper.setStyle('right', 0);
+ }
+ else
+ {
+ this.wrapper.setStyle('left', (window.innerWidth / 2) - (this.getWrapperCoords().width / 2));
+ }
+ return this;
+ },
+
+ 'getWrapperCoords': function(){
+ this.wrapper.setStyle('visibility', 'hidden');
+ var measurer = this.alert('need something in here to measure');
+ var coords = this.wrapper.getCoordinates();
+ measurer.destroy();
+ this.wrapper.setStyle('visibility','');
+ return coords;
+ },
+
+ 'alert': function(msg, options){
+
+ options = Object.merge({}, this.options.alert, options || {});
+
+ var alert = new Element(this.options.elements.alert, this.options.elementOptions.alert);
+
+ if(typeOf(msg) == 'string')
+ {
+ alert.set('html', msg);
+ }
+ else if(typeOf(msg) == 'element')
+ {
+ alert.grab(msg);
+ }
+ else if(typeOf(msg) == 'array')
+ {
+ var alerts = [];
+ msg.each(function(m){
+ alerts.push(this.alert(m, options));
+ }, this);
+ return alerts;
+ }
+
+ alert.store('options', options);
+
+ if(options.buttons.length > 0)
+ {
+ options.clickDismiss = false;
+ options.hideAfter = false;
+ options.hoverWait = false;
+ var buttonWrapper = new Element(this.options.elements.buttonWrapper, this.options.elementOptions.buttonWrapper);
+ alert.grab(buttonWrapper);
+ options.buttons.each(function(button){
+ if(button.text != undefined)
+ {
+ var callbackButton = new Element(this.options.elements.button, this.options.elementOptions.button);
+ callbackButton.set('html', button.text);
+ if(button.callback != undefined)
+ {
+ callbackButton.addEvent('click', button.callback.pass(alert));
+ }
+ if(button.dismiss != undefined && button.dismiss)
+ {
+ callbackButton.addEvent('click', this.dismiss.pass(alert, this));
+ }
+ buttonWrapper.grab(callbackButton);
+ }
+ }, this);
+ }
+ if(options.className != undefined)
+ {
+ alert.addClass(options.className);
+ }
+
+ this.wrapper.grab(alert, (this.options.mode == 'top') ? 'bottom' : 'top');
+
+ var fx = Object.merge(this.options.alert.fx, options.fx);
+ var alertFx = new Fx.Morph(alert, fx);
+ alert.store('fx', alertFx);
+ this.fadeIn(alert);
+
+ if(options.highlight)
+ {
+ alertFx.addEvent('complete', function(){
+ alert.highlight(options.highlight.start, options.highlight.end);
+ if(options.highlightRepeat)
+ {
+ alert.highlight.periodical(options.highlightRepeat, alert, [options.highlight.start, options.highlight.end]);
+ }
+ });
+ }
+ if(options.hideAfter)
+ {
+ this.dismiss(alert);
+ }
+
+ if(options.clickDismiss)
+ {
+ alert.addEvent('click', function(){
+ this.holdUp = false;
+ this.dismiss(alert, true);
+ }.bind(this));
+ }
+
+ if(options.hoverWait)
+ {
+ alert.addEvents({
+ 'mouseenter': function(){
+ this.holdUp = true;
+ }.bind(this),
+ 'mouseleave': function(){
+ this.holdUp = false;
+ }.bind(this)
+ });
+ }
+
+ return alert;
+ },
+
+ 'fadeIn': function(alert){
+ var alertFx = alert.retrieve('fx');
+ alertFx.set({
+ 'opacity': 0
+ });
+ alertFx.start({
+ 'opacity': [this.options.elementOptions.alert.styles.opacity, .9].pick(),
+ });
+ },
+
+ 'dismiss': function(alert, now){
+ now = now || false;
+ var options = alert.retrieve('options');
+ if(now)
+ {
+ this.fadeOut(alert);
+ }
+ else
+ {
+ this.fadeOut.delay(options.hideAfter, this, alert);
+ }
+ },
+
+ 'fadeOut': function(alert){
+ if(this.holdUp)
+ {
+ this.dismiss.delay(100, this, [alert, true])
+ return null;
+ }
+ var alertFx = alert.retrieve('fx');
+ if(!alertFx)
+ {
+ return null;
+ }
+ var to = {
+ 'opacity': 0
+ }
+ if(this.options.mode == 'top')
+ {
+ to['margin-top'] = '-'+alert.offsetHeight+'px';
+ }
+ else
+ {
+ to['margin-bottom'] = '-'+alert.offsetHeight+'px';
+ }
+ alertFx.start(to);
+ alertFx.addEvent('complete', function(){
+ alert.destroy();
+ });
+ }
+});
+
+Element.implement({
+
+ 'alert': function(msg, options){
+ var alert = this.retrieve('alert');
+ if(!alert)
+ {
+ options = options || {
+ 'mode':'top'
+ };
+ alert = new Purr(options)
+ this.store('alert', alert);
+ }
+
+ var coords = this.getCoordinates();
+
+ alert.alert(msg, options);
+
+ alert.wrapper.setStyles({
+ 'bottom': '',
+ 'left': (coords.left - (alert.wrapper.getWidth() / 2)) + (this.getWidth() / 2),
+ 'top': coords.top - (alert.wrapper.getHeight()),
+ 'position': 'absolute'
+ });
+
+ }
+
+}); \ No newline at end of file
diff --git a/module/webui/themes/Flat/js/static/purr.min.js b/module/webui/themes/Flat/js/static/purr.min.js
new file mode 100644
index 000000000..bf70e357d
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/purr.min.js
@@ -0,0 +1 @@
+var Purr=new Class({options:{mode:"top",position:"left",elementAlertClass:"purr-element-alert",elements:{wrapper:"div",alert:"div",buttonWrapper:"div",button:"button"},elementOptions:{wrapper:{styles:{position:"fixed","z-index":"9999"},"class":"purr-wrapper"},alert:{"class":"purr-alert",styles:{opacity:".85"}},buttonWrapper:{"class":"purr-button-wrapper"},button:{"class":"purr-button"}},alert:{buttons:[],clickDismiss:!0,hoverWait:!0,hideAfter:5e3,fx:{duration:500},highlight:!1,highlightRepeat:!1,highlight:{start:"#FF0",end:!1}}},Implements:[Options,Events,Chain],initialize:function(t){return this.setOptions(t),this.createWrapper(),this},bindAlert:function(){return this.alert.bind(this)},createWrapper:function(){this.wrapper=new Element(this.options.elements.wrapper,this.options.elementOptions.wrapper),"top"==this.options.mode?this.wrapper.setStyle("top",0):this.wrapper.setStyle("bottom",0),document.id(document.body).grab(this.wrapper),this.positionWrapper(this.options.position)},positionWrapper:function(t){if("object"==typeOf(t)){var e=this.getWrapperCoords();this.wrapper.setStyles({bottom:"",left:t.x,top:t.y-e.height,position:"absolute"})}else"left"==t?this.wrapper.setStyle("left",0):"right"==t?this.wrapper.setStyle("right",0):this.wrapper.setStyle("left",window.innerWidth/2-this.getWrapperCoords().width/2);return this},getWrapperCoords:function(){this.wrapper.setStyle("visibility","hidden");var t=this.alert("need something in here to measure"),e=this.wrapper.getCoordinates();return t.destroy(),this.wrapper.setStyle("visibility",""),e},alert:function(t,e){e=Object.merge({},this.options.alert,e||{});var i=new Element(this.options.elements.alert,this.options.elementOptions.alert);if("string"==typeOf(t))i.set("html",t);else if("element"==typeOf(t))i.grab(t);else if("array"==typeOf(t)){var s=[];return t.each(function(t){s.push(this.alert(t,e))},this),s}if(i.store("options",e),e.buttons.length>0){e.clickDismiss=!1,e.hideAfter=!1,e.hoverWait=!1;var r=new Element(this.options.elements.buttonWrapper,this.options.elementOptions.buttonWrapper);i.grab(r),e.buttons.each(function(t){if(void 0!=t.text){var e=new Element(this.options.elements.button,this.options.elementOptions.button);e.set("html",t.text),void 0!=t.callback&&e.addEvent("click",t.callback.pass(i)),void 0!=t.dismiss&&t.dismiss&&e.addEvent("click",this.dismiss.pass(i,this)),r.grab(e)}},this)}void 0!=e.className&&i.addClass(e.className),this.wrapper.grab(i,"top"==this.options.mode?"bottom":"top");var o=Object.merge(this.options.alert.fx,e.fx),n=new Fx.Morph(i,o);return i.store("fx",n),this.fadeIn(i),e.highlight&&n.addEvent("complete",function(){i.highlight(e.highlight.start,e.highlight.end),e.highlightRepeat&&i.highlight.periodical(e.highlightRepeat,i,[e.highlight.start,e.highlight.end])}),e.hideAfter&&this.dismiss(i),e.clickDismiss&&i.addEvent("click",function(){this.holdUp=!1,this.dismiss(i,!0)}.bind(this)),e.hoverWait&&i.addEvents({mouseenter:function(){this.holdUp=!0}.bind(this),mouseleave:function(){this.holdUp=!1}.bind(this)}),i},fadeIn:function(t){var e=t.retrieve("fx");e.set({opacity:0}),e.start({opacity:[this.options.elementOptions.alert.styles.opacity,.9].pick()})},dismiss:function(t,e){e=e||!1;var i=t.retrieve("options");e?this.fadeOut(t):this.fadeOut.delay(i.hideAfter,this,t)},fadeOut:function(t){if(this.holdUp)return this.dismiss.delay(100,this,[t,!0]),null;var e=t.retrieve("fx");if(!e)return null;var i={opacity:0};"top"==this.options.mode?i["margin-top"]="-"+t.offsetHeight+"px":i["margin-bottom"]="-"+t.offsetHeight+"px",e.start(i),e.addEvent("complete",function(){t.destroy()})}});Element.implement({alert:function(t,e){var i=this.retrieve("alert");i||(e=e||{mode:"top"},i=new Purr(e),this.store("alert",i));var s=this.getCoordinates();i.alert(t,e),i.wrapper.setStyles({bottom:"",left:s.left-i.wrapper.getWidth()/2+this.getWidth()/2,top:s.top-i.wrapper.getHeight(),position:"absolute"})}}); \ No newline at end of file
diff --git a/module/webui/themes/Flat/js/static/tinytab.js b/module/webui/themes/Flat/js/static/tinytab.js
new file mode 100644
index 000000000..de50279fc
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/tinytab.js
@@ -0,0 +1,43 @@
+/*
+---
+description: TinyTab - Tiny and simple tab handler for Mootools.
+
+license: MIT-style
+
+authors:
+- Danillo César de O. Melo
+
+requires:
+- core/1.2.4: '*'
+
+provides: TinyTab
+
+...
+*/
+(function($) {
+ this.TinyTab = new Class({
+ Implements: Events,
+ initialize: function(tabs, contents, opt) {
+ this.tabs = tabs;
+ this.contents = contents;
+ if(!opt) opt = {};
+ this.css = opt.selectedClass || 'selected';
+ this.select(this.tabs[0]);
+ tabs.each(function(el){
+ el.addEvent('click',function(e){
+ this.select(el);
+ e.stop();
+ }.bind(this));
+ }.bind(this));
+ },
+
+ select: function(el) {
+ this.tabs.removeClass(this.css);
+ el.addClass(this.css);
+ this.contents.setStyle('display','none');
+ var content = this.contents[this.tabs.indexOf(el)];
+ content.setStyle('display','block');
+ this.fireEvent('change',[content,el]);
+ }
+ });
+})(document.id); \ No newline at end of file
diff --git a/module/webui/themes/Flat/js/static/tinytab.min.js b/module/webui/themes/Flat/js/static/tinytab.min.js
new file mode 100644
index 000000000..2f4fa0436
--- /dev/null
+++ b/module/webui/themes/Flat/js/static/tinytab.min.js
@@ -0,0 +1 @@
+!function(){this.TinyTab=new Class({Implements:Events,initialize:function(s,t,e){this.tabs=s,this.contents=t,e||(e={}),this.css=e.selectedClass||"selected",this.select(this.tabs[0]),s.each(function(s){s.addEvent("click",function(t){this.select(s),t.stop()}.bind(this))}.bind(this))},select:function(s){this.tabs.removeClass(this.css),s.addClass(this.css),this.contents.setStyle("display","none");var t=this.contents[this.tabs.indexOf(s)];t.setStyle("display","block"),this.fireEvent("change",[t,s])}})}(document.id); \ No newline at end of file
diff --git a/module/webui/themes/Flat/tml/admin.html b/module/webui/themes/Flat/tml/admin.html
new file mode 100644
index 000000000..eb87666ff
--- /dev/null
+++ b/module/webui/themes/Flat/tml/admin.html
@@ -0,0 +1,98 @@
+{% extends '/Flat/tml/base.html' %}
+
+{% block head %}
+ <script type="text/javascript" src="/Flat/js/render/admin.min.js"></script>
+{% endblock %}
+
+
+{% block title %}{{ _("Administrate") }} - {{ super() }} {% endblock %}
+{% block subtitle %}{{ _("Administrate") }}{% endblock %}
+
+{% block content %}
+
+ <a href="#" id="quit-pyload" style="font-size: large; font-weight: bold;">{{_("Quit pyLoad")}}</a> |
+ <a href="#" id="restart-pyload" style="font-size: large; font-weight: bold;">{{_("Restart pyLoad")}}</a>
+ <br>
+ <br>
+
+ {{ _("To add user or change passwords use:") }} <b>python pyload.py -u</b><br>
+ {{ _("Important: Admin user have always all permissions!") }}
+
+ <form action="" method="POST">
+ <table class="settable wide">
+ <thead style="font-size: 11px">
+ <th>
+ {{ _("Name") }}
+ </th>
+ <th>
+ {{ _("Change Password") }}
+ </th>
+ <th>
+ {{ _("Admin") }}
+ </th>
+ <th>
+ {{ _("Permissions") }}
+ </th>
+ </thead>
+
+ {% for name, data in users.iteritems() %}
+ <tr>
+ <td>{{ name }}</td>
+ <td><a class="change_password" href="#" id="change_pw|{{name}}">{{ _("change") }}</a></td>
+ <td><input name="{{ name }}|admin" type="checkbox" {% if data.perms.admin %}
+ checked="True" {% endif %}"></td>
+ <td>
+ <select multiple="multiple" size="{{ permlist|length }}" name="{{ name }}|perms">
+ {% for perm in permlist %}
+ {% if data.perms|getitem(perm) %}
+ <option selected="selected">{{ perm }}</option>
+ {% else %}
+ <option>{{ perm }}</option>
+ {% endif %}
+ {% endfor %}
+ </select>
+ </td>
+ </tr>
+ {% endfor %}
+
+
+ </table>
+
+ <button class="styled_button" type="submit">{{ _("Submit") }}</button>
+ </form>
+{% endblock %}
+{% block hidden %}
+ <div id="password_box" class="window_box" style="z-index: 2">
+ <form id="password_form" action="/json/change_password" method="POST" enctype="multipart/form-data">
+ <h1>{{ _("Change Password") }}</h1>
+
+ <p>{{ _("Enter your current and desired Password.") }}</p>
+ <label for="user_login">{{ _("User") }}
+ <span class="small">{{ _("Your username.") }}</span>
+ </label>
+ <input id="user_login" name="user_login" type="text" size="20"/>
+
+ <label for="login_current_password">{{ _("Current password") }}
+ <span class="small">{{ _("The password for this account.") }}</span>
+ </label>
+ <input id="login_current_password" name="login_current_password" type="password" size="20"/>
+
+ <label for="login_new_password">{{ _("New password") }}
+ <span class="small">{{ _("The new password.") }}</span>
+ </label>
+ <input id="login_new_password" name="login_new_password" type="password" size="20"/>
+
+ <label for="login_new_password2">{{ _("New password (repeat)") }}
+ <span class="small">{{ _("Please repeat the new password.") }}</span>
+ </label>
+ <input id="login_new_password2" name="login_new_password2" type="password" size="20"/>
+
+
+ <button id="login_password_button" type="submit">{{ _("Submit") }}</button>
+ <button id="login_password_reset" style="margin-left: 0" type="reset">{{ _("Reset") }}</button>
+ <div class="spacer"></div>
+
+ </form>
+
+ </div>
+{% endblock %}
diff --git a/module/webui/themes/Flat/tml/base.html b/module/webui/themes/Flat/tml/base.html
new file mode 100644
index 000000000..4473ee5e2
--- /dev/null
+++ b/module/webui/themes/Flat/tml/base.html
@@ -0,0 +1,180 @@
+<?xml version="1.0" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<link rel="stylesheet" type="text/css" href="/Flat/css/base.min.css"/>
+<link rel="stylesheet" type="text/css" href="/Flat/css/window.min.css"/>
+<link rel="stylesheet" type="text/css" href="/Flat/css/MooDialog.min.css"/>
+
+<script type="text/javascript" src="/Flat/js/static/mootools-core.min.js"></script>
+<script type="text/javascript" src="/Flat/js/static/mootools-more.min.js"></script>
+<script type="text/javascript" src="/Flat/js/static/MooDialog.min.js"></script>
+<script type="text/javascript" src="/Flat/js/static/purr.min.js"></script>
+
+
+<script type="text/javascript" src="/Flat/js/render/base.min.js"></script>
+
+<title>{% block title %}pyLoad {{_("Web UI")}}{% endblock %}</title>
+
+{% block head %}
+{% endblock %}
+</head>
+<body>
+<a class="anchor" name="top" id="top"></a>
+
+<div id="head-panel">
+
+
+ <div id="head-search-and-login">
+ {% block headpanel %}
+
+ {% if user.is_authenticated %}
+
+
+{% if update %}
+<span>
+<span style="font-weight: 300; margin: 0 2px 0 2px;">{{_("pyLoad Update available!")}}</span>
+</span>
+{% endif %}
+
+
+{% if plugins %}
+<span>
+<span style="font-weight: 300; margin: 0 2px 0 2px;">{{_("Plugins updated, please restart!")}}</span>
+</span>
+{% endif %}
+
+<span id="cap_info" style="display: {% if captcha %}inline{%else%}none{% endif %}">
+<img src="/Flat/img/default/images.png" alt="Captcha:" style="vertical-align:middle; margin:2px" />
+<span style="font-weight: 300; cursor: pointer; margin-right: 2px;">{{_("Captcha waiting")}}</span>
+</span>
+
+ <img src="/Flat/img/head-login.png" alt="User:" style="vertical-align:middle; margin:2px" /><span style="padding-right: 2px;">{{user.name}}</span>
+ <ul id="user-actions">
+ <li><a href="/logout" class="action logout" rel="nofollow">{{_("Logout")}}</a></li>
+ {% if user.is_admin %}
+ <li><a href="/admin" class="action profile" rel="nofollow">{{_("Administrate")}}</a></li>
+ {% endif %}
+ <li><a href="/info" class="action info" rel="nofollow">{{_("Info")}}</a></li>
+
+ </ul>
+{% else %}
+ <span style="padding-right: 2px;">{{_("Please Login!")}}</span>
+{% endif %}
+
+ {% endblock %}
+ </div>
+
+ <a href="/"><img id="head-logo" src="/Flat/img/default/pyload-logo.png" alt="pyLoad" /></a>
+
+ <div id="head-menu">
+ <ul>
+
+ {% macro selected(name, right=False) -%}
+ {% if name in url -%}class="{% if right -%}right {% endif %}selected"{%- endif %}
+ {% if not name in url and right -%}class="right"{%- endif %}
+ {%- endmacro %}
+
+
+ {% block menu %}
+ <li>
+ <a href="/" title=""><img src="/Flat/img/head-menu-home.png" alt="" /> {{_("Home")}}</a>
+ </li>
+ <li {{ selected('queue') }}>
+ <a href="/queue/" title=""><img src="/Flat/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
+ </li>
+ <li {{ selected('collector') }}>
+ <a href="/collector/" title=""><img src="/Flat/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
+ </li>
+ <li {{ selected('downloads') }}>
+ <a href="/downloads/" title=""><img src="/Flat/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
+ </li>
+ <li {{ selected('filemanager') }}>
+ <a href="/filemanager/" title=""><img src="/Flat/img/head-menu-download.png" alt="" /> {{_("FileManager")}}</a>
+ </li>
+ <li {{ selected('logs', True) }}>
+ <a href="/logs/" title=""><img src="/Flat/img/head-menu-index.png" alt="" />{{_("Logs")}}</a>
+ </li>
+ <li {{ selected('settings', True) }}>
+ <a href="/settings/" title=""><img src="/Flat/img/head-menu-config.png" alt="" />{{_("Settings")}}</a>
+ </li>
+ {% endblock %}
+
+ </ul>
+ </div>
+
+ <div style="clear:both;"></div>
+</div>
+
+{% if perms.STATUS %}
+<ul id="page-actions2">
+ <li id="action_play"><a href="#" class="action play" accesskey="o" rel="nofollow">{{_("Start")}}</a></li>
+ <li id="action_stop"><a href="#" class="action stop" accesskey="o" rel="nofollow">{{_("Stop")}}</a></li>
+ <li id="action_cancel"><a href="#" class="action cancel" accesskey="o" rel="nofollow">{{_("Cancel")}}</a></li>
+ <li id="action_add"><a href="#" class="action add" accesskey="o" rel="nofollow" >{{_("Add")}}</a></li>
+</ul>
+{% endif %}
+
+{% if perms.LIST %}
+<ul id="page-actions">
+ <li><span class="time">{{_("Download:")}}</span><a id="time" style=" background-color: {% if status.download %}#8ffc25{% else %} #fc6e26{% endif %}; padding-left: 0cm; padding-right: 0.1cm; "> {% if status.download %}{{_("on")}}{% else %}{{_("off")}}{% endif %}</a></li>
+ <li><span class="reconnect">{{_("Reconnect:")}}</span><a id="reconnect" style=" background-color: {% if status.reconnect %}#8ffc25{% else %} #fc6e26{% endif %}; padding-left: 0cm; padding-right: 0.1cm;color:black; "> {% if status.reconnect %}{{_("on")}}{% else %}{{_("off")}}{% endif %}</a></li>
+ <li><a class="action backlink">{{_("Speed:")}} <b id="speed">{{ status.speed }}</b></a></li>
+ <li><a class="action cog">{{_("Active:")}} <b id="aktiv" title="{{_("Active")}}">{{ status.active }}</b> / <b id="aktiv_from" title="{{_("Queued")}}">{{ status.queue }}</b> / <b id="aktiv_total" title="{{_("Total")}}">{{ status.total }}</b></a></li>
+ <li><a href="" class="action revisions" accesskey="o" rel="nofollow">{{_("Reload page")}}</a></li>
+</ul>
+{% endif %}
+
+{% block pageactions %}
+{% endblock %}
+<br/>
+
+<div id="body-wrapper" class="dokuwiki">
+
+<div id="content" lang="en" dir="ltr">
+
+<h1>{% block subtitle %}pyLoad - {{_("Web UI")}}{% endblock %}</h1>
+
+{% block statusbar %}
+{% endblock %}
+
+
+<br/>
+
+<div class="level1" style="clear:both">
+</div>
+<noscript><h1>Enable JavaScript to use the web ui.</h1></noscript>
+
+{% for message in messages %}
+ <b><p>{{message}}</p></b>
+{% endfor %}
+
+<div id="load-indicator" style="opacity: 0; float: right; margin-top: -10px;">
+ <img src="/Flat/img/default/ajax-loader.gif" alt="" style="padding-right: 5px"/>
+ {{_("loading")}}
+</div>
+
+{% block content %}
+{% endblock content %}
+
+ <hr style="clear: both;" />
+
+<div id="foot">&copy; 2008-2015 pyLoad Team
+<a href="#top" class="action top" accesskey="x"><span>{{_("Back to top")}}</span></a><br />
+<!--<div class="breadcrumbs"></div>-->
+
+</div>
+</div>
+</div>
+
+<div style="display: none;">
+ {% include '/Flat/tml/window.html' %}
+ {% include '/Flat/tml/captcha.html' %}
+ {% block hidden %}
+ {% endblock %}
+</div>
+</body>
+</html>
diff --git a/module/webui/themes/Flat/tml/captcha.html b/module/webui/themes/Flat/tml/captcha.html
new file mode 100644
index 000000000..731d97d11
--- /dev/null
+++ b/module/webui/themes/Flat/tml/captcha.html
@@ -0,0 +1,42 @@
+<!-- Captcha box -->
+<div id="cap_box" class="window_box">
+
+ <form id="cap_form" action="/json/set_captcha" method="POST" enctype="multipart/form-data" onsubmit="return false;">
+
+ <h1>{{_("Captcha reading")}}</h1>
+ <p id="cap_title">{{_("Please read the text on the captcha.")}}</p>
+
+ <div id="cap_textual">
+
+ <input id="cap_id" name="cap_id" type="hidden" value="" />
+
+ <label>{{_("Captcha")}}
+ <span class="small">{{_("The captcha.")}}</span>
+ </label>
+ <span class="cont">
+ <img id="cap_textual_img" src="">
+ </span>
+
+ <label>{{_("Text")}}
+ <span class="small">{{_("Input the text on the captcha.")}}</span>
+ </label>
+ <input id="cap_result" name="cap_result" type="text" size="20" />
+
+ </div>
+
+ <div id="cap_positional" style="text-align: center">
+ <img id="cap_positional_img" src="" style="margin: 10px; cursor:pointer">
+ </div>
+
+ <div id="button_bar" style="text-align: center">
+ <span>
+ <button id="cap_submit" type="submit" style="margin-left: 0">{{_("Submit")}}</button>
+ <button id="cap_reset" type="reset" style="margin-left: 0">{{_("Close")}}</button>
+ </span>
+ </div>
+
+ <div class="spacer"></div>
+
+ </form>
+
+</div> \ No newline at end of file
diff --git a/module/webui/themes/Flat/tml/downloads.html b/module/webui/themes/Flat/tml/downloads.html
new file mode 100644
index 000000000..4b9ae7baf
--- /dev/null
+++ b/module/webui/themes/Flat/tml/downloads.html
@@ -0,0 +1,29 @@
+{% extends '/Flat/tml/base.html' %}
+
+{% block title %}Downloads - {{super()}} {% endblock %}
+
+{% block subtitle %}
+{{_("Downloads")}}
+{% endblock %}
+
+{% block content %}
+
+<ul>
+ {% for folder in files.folder %}
+ <li>
+ {{ folder.name }}
+ <ul>
+ {% for file in folder.files %}
+ <li><a href='get/{{ folder.path|escape }}/{{ file|escape }}'>{{file}}</a></li>
+ {% endfor %}
+ </ul>
+ </li>
+ {% endfor %}
+
+ {% for file in files.files %}
+ <li> <a href='get/{{ file|escape }}'>{{ file }}</a></li>
+ {% endfor %}
+
+</ul>
+
+{% endblock %} \ No newline at end of file
diff --git a/module/web/templates/default/filemanager.html b/module/webui/themes/Flat/tml/filemanager.html
index 97095c13e..4dbbd288c 100644
--- a/module/web/templates/default/filemanager.html
+++ b/module/webui/themes/Flat/tml/filemanager.html
@@ -1,8 +1,8 @@
-{% extends 'default/base.html' %}
+{% extends '/Flat/tml/base.html' %}
{% block head %}
-<script type="text/javascript" src="/filemanager_ui.js"></script>
+<script type="text/javascript" src="/Flat/js/render/filemanager.min.js"></script>
<script type="text/javascript">
@@ -26,9 +26,9 @@ document.addEvent("domready", function(){
<span>
<b>{{ file.name }}</b>
<span class="buttons" style="opacity:0">
- <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/media/default/img/pencil.png" />
- &nbsp;&nbsp;
- <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/media/default/img/delete.png" />
+ <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/Flat/img/pencil.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Flat/img/delete.png" />
</span>
</span>
</li>
@@ -39,30 +39,30 @@ document.addEvent("domready", function(){
<input type="hidden" name="path" class="path" value="{{ fld.path }}" />
<input type="hidden" name="name" class="name" value="{{ fld.name }}" />
<span>
- <b>{{ fld.name }}</b>
- <span class="buttons" style="opacity:0">
- <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/media/default/img/pencil.png" />
- &nbsp;&nbsp;
- <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/media/default/img/delete.png" />
- &nbsp;&nbsp;
- <img title="{{_("Add subdirectory")}}" class="mkdir" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/media/default/img/add_folder.png" />
- </span>
+ <b>{{ fld.name }}</b>
+ <span class="buttons" style="opacity:0">
+ <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/Flat/img/pencil.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Flat/img/delete.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Add subdirectory")}}" class="mkdir" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Flat/img/default/add_folder.png" />
+ </span>
</span>
{% if (fld.folders|length + fld.files|length) > 0 %}
- {% if open %}
- <ul>
- {% else %}
- <ul style="display:none">
- {% endif %}
- {% for child in fld.folders %}
- {{ display_folder(child) }}
- {% endfor %}
- {% for child in fld.files %}
- {{ display_file(child) }}
- {% endfor %}
- </ul>
+ {% if open %}
+ <ul>
+ {% else %}
+ <ul style="display:none">
+ {% endif %}
+ {% for child in fld.folders %}
+ {{ display_folder(child) }}
+ {% endfor %}
+ {% for child in fld.files %}
+ {{ display_file(child) }}
+ {% endfor %}
+ </ul>
{% else %}
- <div style="display:none">{{ _("Folder is empty") }}</div>
+ <div style="display:none">{{ _("Folder is empty") }}</div>
{% endif %}
</li>
{%- endmacro %}
@@ -75,6 +75,4 @@ document.addEvent("domready", function(){
{{ display_folder(root, true) }}
</ul>
-{% include "default/rename_directory.html" %}
-
{% endblock %}
diff --git a/module/webui/themes/Flat/tml/folder.html b/module/webui/themes/Flat/tml/folder.html
new file mode 100644
index 000000000..a0d1dcec9
--- /dev/null
+++ b/module/webui/themes/Flat/tml/folder.html
@@ -0,0 +1,15 @@
+<li class="folder">
+ <input type="hidden" name="path" class="path" value="{{ path }}" />
+ <input type="hidden" name="name" class="name" value="{{ name }}" />
+ <span>
+ <b>{{ name }}</b>
+ <span class="buttons" style="opacity:0">
+ <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/Flat/img/pencil.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Flat/img/delete.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Add subdirectory")}}" class="mkdir" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/Flat/img/default/add_folder.png" />
+ </span>
+ </span>
+ <div style="display:none">{{ _("Folder is empty") }}</div>
+</li> \ No newline at end of file
diff --git a/module/webui/themes/Flat/tml/home.html b/module/webui/themes/Flat/tml/home.html
new file mode 100644
index 000000000..ec7f4aae2
--- /dev/null
+++ b/module/webui/themes/Flat/tml/home.html
@@ -0,0 +1,266 @@
+{% extends '/Flat/tml/base.html' %}
+{% block head %}
+
+<script type="text/javascript">
+
+var em;
+var operafix = (navigator.userAgent.toLowerCase().search("opera") >= 0);
+
+document.addEvent("domready", function(){
+ em = new EntryManager();
+});
+
+var EntryManager = new Class({
+ initialize: function(){
+ this.json = new Request.JSON({
+ url: "json/links",
+ secure: false,
+ async: true,
+ onSuccess: this.update.bind(this),
+ initialDelay: 0,
+ delay: 2500,
+ limit: 30000
+ });
+
+ this.ids = [{% for link in content %}
+ {% if forloop.last %}
+ {{ link.id }}
+ {% else %}
+ {{ link.id }},
+ {% endif %}
+ {% endfor %}];
+
+ this.entries = [];
+ this.container = $('LinksAktiv');
+
+ this.parseFromContent();
+
+ this.json.startTimer();
+ },
+ parseFromContent: function(){
+ this.ids.each(function(id,index){
+ var entry = new LinkEntry(id);
+ entry.parse();
+ this.entries.push(entry)
+ }, this);
+ },
+ update: function(data){
+
+ try{
+ this.ids = this.entries.map(function(item){
+ return item.fid
+ });
+
+ this.ids.filter(function(id){
+ return !this.ids.contains(id)
+ },data).each(function(id){
+ var index = this.ids.indexOf(id);
+ this.entries[index].remove();
+ this.entries = this.entries.filter(function(item){return item.fid != this},id);
+ this.ids = this.ids.erase(id)
+ }, this);
+
+ data.links.each(function(link, i){
+ if (this.ids.contains(link.fid)){
+
+ var index = this.ids.indexOf(link.fid);
+ this.entries[index].update(link)
+
+ }else{
+ var entry = new LinkEntry(link.fid);
+ entry.insert(link);
+ this.entries.push(entry);
+ this.ids.push(link.fid);
+ this.container.adopt(entry.elements.tr,entry.elements.pgbTr);
+ entry.fade.start('opacity', 1);
+ entry.fadeBar.start('opacity', 1);
+
+ }
+ }, this)
+ }catch(e){
+ //alert(e)
+ }
+ }
+});
+
+
+var LinkEntry = new Class({
+ initialize: function(id){
+ this.fid = id;
+ this.id = id;
+ },
+ parse: function(){
+ this.elements = {
+ tr: $("link_{id}".substitute({id: this.id})),
+ name: $("link_{id}_name".substitute({id: this.id})),
+ status: $("link_{id}_status".substitute({id: this.id})),
+ info: $("link_{id}_info".substitute({id: this.id})),
+ bleft: $("link_{id}_bleft".substitute({id: this.id})),
+ percent: $("link_{id}_percent".substitute({id: this.id})),
+ remove: $("link_{id}_remove".substitute({id: this.id})),
+ pgbTr: $("link_{id}_pgb_tr".substitute({id: this.id})),
+ pgb: $("link_{id}_pgb".substitute({id: this.id}))
+ };
+ this.initEffects();
+ },
+ insert: function(item){
+ try{
+
+ this.elements = {
+ tr: new Element('tr', {
+ 'html': '',
+ 'styles':{
+ 'opacity': 0
+ }
+ }),
+ name: new Element('td', {
+ 'html': item.name
+ }),
+ status: new Element('td', {
+ 'html': item.statusmsg
+ }),
+ info: new Element('td', {
+ 'html': item.info
+ }),
+ bleft: new Element('td', {
+ 'html': humanFileSize(item.size)
+ }),
+ percent: new Element('span', {
+ 'html': item.percent+ '% / '+ humanFileSize(item.size-item.bleft)
+ }),
+ remove: new Element('img',{
+ 'src': '/Flat/img/control_cancel.png',
+ 'styles':{
+ 'vertical-align': 'middle',
+ 'margin-right': '-20px',
+ 'margin-left': '5px',
+ 'margin-top': '-2px',
+ 'cursor': 'pointer'
+ }
+ }),
+ pgbTr: new Element('tr', {
+ 'html':''
+ }),
+ pgb: new Element('div', {
+ 'html': '&nbsp;',
+ 'styles':{
+ 'height': '4px',
+ 'width': item.percent+'%',
+ 'background-color': '#ddd'
+ }
+ })
+ };
+
+ this.elements.tr.adopt(this.elements.name,this.elements.status,this.elements.info,this.elements.bleft,new Element('td').adopt(this.elements.percent,this.elements.remove));
+ this.elements.pgbTr.adopt(new Element('td',{'colspan':5}).adopt(this.elements.pgb));
+ this.initEffects();
+ }catch(e){
+ alert(e)
+ }
+ },
+ initEffects: function(){
+ if(!operafix)
+ this.bar = new Fx.Morph(this.elements.pgb, {unit: '%', duration: 5000, link: 'link', fps:30});
+ this.fade = new Fx.Tween(this.elements.tr);
+ this.fadeBar = new Fx.Tween(this.elements.pgbTr);
+
+ this.elements.remove.addEvent('click', function(){
+ new Request({method: 'get', url: '/json/abort_link/'+this.id}).send();
+ }.bind(this));
+
+ },
+ update: function(item){
+ this.elements.name.set('text', item.name);
+ this.elements.status.set('text', item.statusmsg);
+ this.elements.info.set('text', item.info);
+ this.elements.bleft.set('text', item.format_size);
+ this.elements.percent.set('text', item.percent+ '% / '+ humanFileSize(item.size-item.bleft));
+ if(!operafix)
+ {
+ this.bar.start({
+ 'width': item.percent,
+ 'background-color': [Math.round(120/100*item.percent),100,100].hsbToRgb().rgbToHex()
+ });
+ }
+ else
+ {
+ this.elements.pgb.set(
+ 'styles', {
+ 'height': '4px',
+ 'width': item.percent+'%',
+ 'background-color': [Math.round(120/100*item.percent),100,100].hsbToRgb().rgbToHex(),
+ });
+ }
+ },
+ remove: function(){
+ this.fade.start('opacity',0).chain(function(){this.elements.tr.dispose();}.bind(this));
+ this.fadeBar.start('opacity',0).chain(function(){this.elements.pgbTr.dispose();}.bind(this));
+
+ }
+ });
+</script>
+
+{% endblock %}
+
+{% block subtitle %}
+{{_("Active Downloads")}}
+{% endblock %}
+
+{% block menu %}
+<li class="selected">
+ <a href="/" title=""><img src="/Flat/img/head-menu-home.png" alt="" /> {{_("Home")}}</a>
+</li>
+<li>
+ <a href="/queue/" title=""><img src="/Flat/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
+</li>
+<li>
+ <a href="/collector/" title=""><img src="/Flat/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
+</li>
+<li>
+ <a href="/downloads/" title=""><img src="/Flat/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
+</li>
+<li>
+ <a href="/filemanager/" title=""><img src="/Flat/img/head-menu-download.png" alt="" /> {{_("FileManager")}}</a>
+</li>
+<li class="right">
+ <a href="/logs/" title=""><img src="/Flat/img/head-menu-index.png" alt="" />{{_("Logs")}}</a>
+</li>
+<li class="right">
+ <a href="/settings/" title=""><img src="/Flat/img/head-menu-config.png" alt="" />{{_("Config")}}</a>
+</li>
+{% endblock %}
+
+{% block content %}
+<table width="100%" class="queue">
+ <thead>
+ <tr class="header">
+ <th>{{_("Name")}}</th>
+ <th>{{_("Status")}}</th>
+ <th>{{_("Information")}}</th>
+ <th>{{_("Size")}}</th>
+ <th>{{_("Progress")}}</th>
+ </tr>
+ </thead>
+ <tbody id="LinksAktiv">
+
+ {% for link in content %}
+ <tr id="link_{{ link.id }}">
+ <td id="link_{{ link.id }}_name">{{ link.name }}</td>
+ <td id="link_{{ link.id }}_status">{{ link.status }}</td>
+ <td id="link_{{ link.id }}_info">{{ link.info }}</td>
+ <td id="link_{{ link.id }}_bleft">{{ link.format_size }}</td>
+ <td>
+ <span id="link_{{ link.id }}_percent">{{ link.percent }}% /{{ link.bleft }}</span>
+ <img id="link_{{ link.id }}_remove" style="vertical-align: middle; margin-right: -20px; margin-left: 5px; margin-top: -2px; cursor:pointer;" src="/Flat/img/control_cancel.png"/>
+ </td>
+ </tr>
+ <tr id="link_{{ link.id }}_pgb_tr">
+ <td colspan="5">
+ <div id="link_{{ link.id }}_pgb" class="progressBar" style="background-color: green; height:4px; width: {{ link.percent }}%;">&nbsp;</div>
+ </td>
+ </tr>
+ {% endfor %}
+
+ </tbody>
+</table>
+{% endblock %} \ No newline at end of file
diff --git a/module/webui/themes/Flat/tml/info.html b/module/webui/themes/Flat/tml/info.html
new file mode 100644
index 000000000..a650afc5a
--- /dev/null
+++ b/module/webui/themes/Flat/tml/info.html
@@ -0,0 +1,81 @@
+{% extends '/Flat/tml/base.html' %}
+
+{% block head %}
+ <script type="text/javascript">
+ window.addEvent("domready", function() {
+ var ul = new Element('ul#twitter_update_list');
+ var script1 = new Element('script[src=http://twitter.com/javascripts/blogger.min.js][type=text/javascript]');
+ var script2 = new Element('script[src=http://twitter.com/statuses/user_timeline/pyLoad.json?callback=twitterCallback2&count=6][type=text/javascript]');
+ $("twitter").adopt(ul, script1, script2);
+ });
+ </script>
+{% endblock %}
+
+{% block title %}{{ _("Info") }} - {{ super() }} {% endblock %}
+{% block subtitle %}{{ _("Info") }}{% endblock %}
+
+{% block content %}
+ <h3>{{ _("News") }}</h3>
+ <div id="twitter"></div>
+
+ <h3>{{ _("Support") }}</h3>
+
+ <ul>
+ <li style="font-weight:bold;">
+ <a href="http://pyload.org/wiki" target="_blank">Wiki</a>
+ &nbsp;&nbsp;|&nbsp;&nbsp;
+ <a href="http://forum.pyload.org/" target="_blank">Forum</a>
+ &nbsp;&nbsp;|&nbsp;&nbsp;
+ <a href="http://pyload.org/irc/" target="_blank">Chat</a>
+ </li>
+ <li style="font-weight:bold;"><a href="http://docs.pyload.org" target="_blank">Documentation</a></li>
+ <li style="font-weight:bold;"><a href="https://bitbucket.org/spoob/pyload/overview" target="_blank">Development</a></li>
+ <li style="font-weight:bold;"><a href="https://bitbucket.org/spoob/pyload/issues?status=new&status=open" target="_blank">Issue Tracker</a></li>
+
+ </ul>
+
+ <h3>{{ _("System") }}</h3>
+ <table class="system">
+ <tr>
+ <td>{{ _("Python:") }}</td>
+ <td>{{ python }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("OS:") }}</td>
+ <td>{{ os }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("pyLoad version:") }}</td>
+ <td>{{ version }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Installation Folder:") }}</td>
+ <td>{{ folder }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Config Folder:") }}</td>
+ <td>{{ config }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Download Folder:") }}</td>
+ <td>{{ download }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Free Space:") }}</td>
+ <td>{{ freespace }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Language:") }}</td>
+ <td>{{ language }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Web UI Port:") }}</td>
+ <td>{{ webif }}</td>
+ </tr>
+ <tr>
+ <td>{{ _("Remote Interface Port:") }}</td>
+ <td>{{ remote }}</td>
+ </tr>
+ </table>
+
+{% endblock %} \ No newline at end of file
diff --git a/module/webui/themes/Flat/tml/login.html b/module/webui/themes/Flat/tml/login.html
new file mode 100644
index 000000000..64ed28426
--- /dev/null
+++ b/module/webui/themes/Flat/tml/login.html
@@ -0,0 +1,36 @@
+{% extends '/Flat/tml/base.html' %}
+
+{% block title %}{{_("Login")}} - {{super()}} {% endblock %}
+
+{% block content %}
+
+<div class="centeralign">
+<form action="" method="post" accept-charset="utf-8" id="login">
+ <div class="no">
+ <input type="hidden" name="do" value="login" />
+ <fieldset>
+ <legend>Login</legend>
+ <label>
+ <span>{{_("Username")}}</span>
+ <input type="text" size="20" name="username" />
+ </label>
+ <br />
+ <label>
+ <span>{{_("Password")}}</span>
+ <input type="password" size="20" name="password" />
+ </label>
+ <br />
+ <input type="submit" value="Login" class="button" />
+ </fieldset>
+ </div>
+</form>
+
+{% if errors %}
+<p>{{_("Your username and password didn't match. Please try again.")}}</p>
+ {{ _("To reset your login data or add an user run:") }} <b> python pyload.py -u</b>
+{% endif %}
+
+</div>
+<br>
+
+{% endblock %}
diff --git a/module/webui/themes/Flat/tml/logout.html b/module/webui/themes/Flat/tml/logout.html
new file mode 100644
index 000000000..f91b16914
--- /dev/null
+++ b/module/webui/themes/Flat/tml/logout.html
@@ -0,0 +1,9 @@
+{% extends '/Flat/tml/base.html' %}
+
+{% block head %}
+<meta http-equiv="refresh" content="3; url=/">
+{% endblock %}
+
+{% block content %}
+<p><b>{{_("You were successfully logged out.")}}</b></p>
+{% endblock %} \ No newline at end of file
diff --git a/module/webui/themes/Flat/tml/logs.html b/module/webui/themes/Flat/tml/logs.html
new file mode 100644
index 000000000..eada9ad3f
--- /dev/null
+++ b/module/webui/themes/Flat/tml/logs.html
@@ -0,0 +1,41 @@
+{% extends '/Flat/tml/base.html' %}
+
+{% block title %}{{_("Logs")}} - {{super()}} {% endblock %}
+{% block subtitle %}{{_("Logs")}}{% endblock %}
+{% block head %}
+<link rel="stylesheet" type="text/css" href="/Flat/css/log.min.css"/>
+{% endblock %}
+
+{% block content %}
+<div style="clear: both;"></div>
+
+<div class="logpaginator"><a href="{{ "/logs/1" }}">&lt;&lt; {{_("Start")}}</a> <a href="{{ "/logs/" + iprev|string }}">&lt; {{_("prev")}}</a> <a href="{{ "/logs/" + inext|string }}">{{_("next")}} &gt;</a> <a href="/logs/">{{_("End")}} &gt;&gt;</a></div>
+<div class="logperpage">
+ <form id="logform1" action="" method="POST">
+ <label for="reversed">Reversed:</label>
+ <input type="checkbox" name="reversed" onchange="this.form.submit();" {% if reversed %} checked="checked" {% endif %} />&nbsp;
+ <label for="perpage">Lines per page:</label>
+ <select name="perpage" onchange="this.form.submit();">
+ {% for value in perpage_p %}
+ <option value="{{value.0}}"{% if value.0 == perpage %} selected="selected" {% endif %}>{{value.1}}</option>
+ {% endfor %}
+ </select>
+ </form>
+</div>
+<div class="logwarn">{{warning}}</div>
+<div style="clear: both;"></div>
+<div class="logdiv">
+ <table class="logtable" cellpadding="0" cellspacing="0">
+ {% for line in log %}
+ <tr><td class="logline">{{line.line}}</td><td>{{line.date}}</td><td class="loglevel">{{line.level}}</td><td>{{line.message}}</td></tr>
+ {% endfor %}
+ </table>
+</div>
+<div class="logform">
+<form id="logform2" action="" method="POST">
+ <label for="from">Jump to time:</label><input type="text" name="from" size="15" value="{{from}}"/>
+ <input type="submit" value="ok" />
+</form>
+</div>
+<div style="clear: both; height: 10px;">&nbsp; </div>
+{% endblock %} \ No newline at end of file
diff --git a/module/webui/themes/Flat/tml/pathchooser.html b/module/webui/themes/Flat/tml/pathchooser.html
new file mode 100644
index 000000000..5ad1fd6d7
--- /dev/null
+++ b/module/webui/themes/Flat/tml/pathchooser.html
@@ -0,0 +1,76 @@
+<html>
+<head>
+ <script class="javascript">
+ function chosen()
+ {
+ opener.ifield.value = document.forms[0].p.value;
+ close();
+ }
+ function exit()
+ {
+ close();
+ }
+ function setInvalid() {
+ document.forms[0].send.disabled = 'disabled';
+ document.forms[0].p.style.color = '#FF0000';
+ }
+ function setValid() {
+ document.forms[0].send.disabled = '';
+ document.forms[0].p.style.color = '#000000';
+ }
+ function setFile(file)
+ {
+ document.forms[0].p.value = file;
+ setValid();
+
+ }
+ </script>
+ <link rel="stylesheet" type="text/css" href="/Flat/css/pathchooser.min.css"/>
+</head>
+<body{% if type == 'file' %}{% if not oldfile %} onload="setInvalid();"{% endif %}{% endif %}>
+<center>
+ <div id="paths">
+ <form method="get" action="?" onSubmit="chosen();" onReset="exit();">
+ <input type="text" name="p" value="{{ oldfile|default(cwd) }}" size="60" onfocus="setValid();">
+ <input type="submit" value="Ok" name="send">
+ </form>
+
+ {% if type == 'folder' %}
+ <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/pathchooser" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/pathchooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span>
+ {% else %}
+ <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/filechooser/" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/filechooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span>
+ {% endif %}
+ </div>
+ <table border="0" cellspacing="0" cellpadding="3">
+ <tr>
+ <th>{{_("name")}}</th>
+ <th>{{_("size")}}</th>
+ <th>{{_("type")}}</th>
+ <th>{{_("last modified")}}</th>
+ </tr>
+ {% if parentdir %}
+ <tr>
+ <td colspan="4">
+ <a href="{% if type == 'folder' %}{{ "/pathchooser/" + parentdir|quotepath }}{% else %}{{ "/filechooser/" + parentdir|quotepath }}{% endif %}"><span class="parentdir">{{_("parent directory")}}</span></a>
+ </td>
+ </tr>
+ {% endif %}
+{% for file in files %}
+ <tr>
+ {% if type == 'folder' %}
+ <td class="name">{% if file.type == 'dir' %}<a href="{{ "/pathchooser/" + file.fullpath|quotepath }}" title="{{ file.fullpath }}"><span class="path_directory">{{ file.name|truncate(25) }}</span></a>{% else %}<span class="path_file" title="{{ file.fullpath }}">{{ file.name|truncate(25) }}{% endif %}</span></td>
+ {% else %}
+ <td class="name">{% if file.type == 'dir' %}<a href="{{ "/filechooser/" + file.fullpath|quotepath }}" title="{{ file.fullpath }}"><span class="file_directory">{{ file.name|truncate(25) }}</span></a>{% else %}<a href="#" onclick="setFile('{{ file.fullpath }}');" title="{{ file.fullpath }}"><span class="file_file">{{ file.name|truncate(25) }}{% endif %}</span></a></td>
+ {% endif %}
+ <td class="size">{{ file.size|float|filesizeformat }}</td>
+ <td class="type">{% if file.type == 'dir' %}directory{% else %}{{ file.ext|default("file") }}{% endif %}</td>
+ <td class="mtime">{{ file.modified|date("d.m.Y - H:i:s") }}</td>
+ <tr>
+<!-- <tr>
+ <td colspan="4">{{_("no content")}}</td>
+ </tr> -->
+{% endfor %}
+ </table>
+ </center>
+</body>
+</html> \ No newline at end of file
diff --git a/module/webui/themes/Flat/tml/queue.html b/module/webui/themes/Flat/tml/queue.html
new file mode 100644
index 000000000..80e6c864c
--- /dev/null
+++ b/module/webui/themes/Flat/tml/queue.html
@@ -0,0 +1,104 @@
+{% extends '/Flat/tml/base.html' %}
+{% block head %}
+
+<script type="text/javascript" src="/Flat/js/render/package.min.js"></script>
+
+<script type="text/javascript">
+
+document.addEvent("domready", function(){
+ var pUI = new PackageUI("url", {{ target }});
+});
+</script>
+{% endblock %}
+
+{% if target %}
+ {% set name = _("Queue") %}
+{% else %}
+ {% set name = _("Collector") %}
+{% endif %}
+
+{% block title %}{{name}} - {{super()}} {% endblock %}
+{% block subtitle %}{{name}}{% endblock %}
+
+{% block pageactions %}
+<ul id="page-actions-more">
+ <li id="del_finished"><a style="padding: 0; font-weight: 300;" href="#">{{_("Delete Finished")}}</a></li>
+ <li id="restart_failed"><a style="padding: 0; font-weight: 300;" href="#">{{_("Restart Failed")}}</a></li>
+</ul>
+{% endblock %}
+
+{% block content %}
+{% autoescape true %}
+
+<ul id="package-list" style="list-style: none; padding-left: 0; margin-top: -10px;">
+{% for package in content %}
+ <li>
+<div id="package_{{package.pid}}" class="package">
+ <div class="order" style="display: none;">{{ package.order }}</div>
+
+ <div class="packagename" style="cursor: pointer">
+ <img class="package_drag" src="/Flat/img/folder.png" style="cursor: move; margin-bottom: -2px">
+ <span class="name">{{package.name}}</span>
+ &nbsp;&nbsp;
+ <span class="buttons" style="opacity:0">
+ <img title="{{_("Delete Package")}}" style="cursor: pointer" width="12px" height="12px" src="/Flat/img/delete.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Restart Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/Flat/img/arrow_refresh.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Edit Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/Flat/img/pencil.png" />
+ &nbsp;&nbsp;
+ <img title="{{_("Move Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/Flat/img/package_go.png" />
+ </span>
+ </div>
+ {% set progress = (package.linksdone * 100) / package.linkstotal %}
+
+ <div id="progress" style="border-radius: 0px; border: 1px solid #AAAAAA; width: 50%; height: 1em">
+ <div style="width: {{ progress }}%; height: 100%; background-color: #add8e6;"></div>
+ <label style="font-size: 0.8em; font-weight: 300; padding-left: 5px; position: relative; top: -17px">
+ {{ package.sizedone|formatsize }} / {{ package.sizetotal|formatsize }}</label>
+ <label style="font-size: 0.8em; font-weight: 300; padding-right: 5px ;float: right; position: relative; top: -17px">
+ {{ package.linksdone }} / {{ package.linkstotal }}</label>
+ </div>
+ <div style="clear: both; margin-bottom: -10px"></div>
+
+ <div id="children_{{package.pid}}" style="display: none;" class="children">
+ <span class="child_secrow">{{_("Folder:")}} <span class="folder">{{package.folder}}</span> | {{_("Password:")}} <span class="password">{{package.password}}</span></span>
+ <ul id="sort_children_{{package.pid}}" style="list-style: none; padding-left: 0">
+ </ul>
+ </div>
+</div>
+ </li>
+{% endfor %}
+</ul>
+{% endautoescape %}
+{% endblock %}
+
+{% block hidden %}
+<div id="pack_box" class="window_box" style="z-index: 2">
+ <form id="pack_form" action="/json/edit_package" method="POST" enctype="multipart/form-data">
+ <h1>{{_("Edit Package")}}</h1>
+ <p>{{_("Edit the package detais below.")}}</p>
+ <input name="pack_id" id="pack_id" type="hidden" value=""/>
+ <label for="pack_name">{{_("Name")}}
+ <span class="small">{{_("The name of the package.")}}</span>
+ </label>
+ <input id="pack_name" name="pack_name" type="text" size="20" />
+
+ <label for="pack_folder">{{_("Folder")}}
+ <span class="small">{{_("Name of subfolder for these downloads.")}}</span>
+ </label>
+ <input id="pack_folder" name="pack_folder" type="text" size="20" />
+
+ <label for="pack_pws">{{_("Password")}}
+ <span class="small">{{_("List of passwords used for unrar.")}}</span>
+ </label>
+ <textarea rows="3" name="pack_pws" id="pack_pws"></textarea>
+
+ <button type="submit">{{_("Submit")}}</button>
+ <button id="pack_reset" style="margin-left: 0" type="reset" >{{_("Reset")}}</button>
+ <div class="spacer"></div>
+
+ </form>
+
+</div>
+{% endblock %} \ No newline at end of file
diff --git a/module/webui/themes/Flat/tml/settings.html b/module/webui/themes/Flat/tml/settings.html
new file mode 100644
index 000000000..8a8e54b0f
--- /dev/null
+++ b/module/webui/themes/Flat/tml/settings.html
@@ -0,0 +1,204 @@
+{% extends '/Flat/tml/base.html' %}
+
+{% block title %}{{ _("Settings") }} - {{ super() }} {% endblock %}
+{% block subtitle %}{{ _("Settings") }}{% endblock %}
+
+{% block head %}
+ <script type="text/javascript" src="/Flat/js/static/tinytab.min.js"></script>
+ <script type="text/javascript" src="/Flat/js/static/MooDropMenu.min.js"></script>
+ <script type="text/javascript" src="/Flat/js/render/settings.min.js"></script>
+
+{% endblock %}
+
+{% block content %}
+
+ <ul id="toptabs" class="tabs">
+ <li><a class="selected" href="#">{{ _("General") }}</a></li>
+ <li><a href="#">{{ _("Plugins") }}</a></li>
+ <li><a href="#">{{ _("Accounts") }}</a></li>
+ </ul>
+
+ <div id="tabsback" style="height: 20px; padding-left: 150px; color: white; font-weight: bold;">
+
+ </div>
+
+ <span id="tabs-body">
+ <!-- General -->
+ <span id="general" class="active tabContent">
+ <ul class="nav tabs">
+ <li class>
+ <a>Menu</a>
+ <ul id="general-menu">
+ {% for entry,name in conf.general %}
+ <nobr>
+ <li id="general|{{ entry }}">{{ name }}</li>
+ </nobr>
+ <br>
+ {% endfor %}
+ </ul>
+ </li>
+ </ul>
+
+ <form id="general_form" action="" method="POST" autocomplete="off">
+ <span id="general_form_content">
+ <br>
+ <h3>&nbsp;&nbsp; {{ _("Choose a section from the menu") }}</h3>
+ <br>
+ </span>
+
+ <input id="general|submit" class="styled_button" type="submit" value="{{_("Submit")}}"/>
+ </form>
+ </span>
+
+ <!-- Plugins -->
+ <span id="plugins" class="tabContent">
+ <ul class="nav tabs">
+ <li class>
+ <a>Menu</a>
+ <ul id="plugin-menu">
+ {% for entry,name in conf.plugin %}
+ <nobr>
+ <li id="plugin|{{ entry }}">{{ name }}</li>
+ </nobr>
+ <br>
+ {% endfor %}
+ </ul>
+ </li>
+ </ul>
+
+
+ <form id="plugin_form" action="" method="POST" autocomplete="off">
+
+ <span id="plugin_form_content">
+ <br>
+ <h3>&nbsp;&nbsp; {{ _("Choose a section from the menu") }}</h3>
+ <br>
+ </span>
+ <input id="plugin|submit" class="styled_button" type="submit" value="{{_("Submit")}}"/>
+ </form>
+
+ </span>
+
+ <!-- Accounts -->
+ <span id="accounts" class="tabContent">
+ <form id="account_form" action="/json/update_accounts" method="POST">
+
+ <table class="settable wide">
+
+ <thead>
+ <tr>
+ <th>{{ _("Plugin") }}</th>
+ <th>{{ _("Name") }}</th>
+ <th>{{ _("Password") }}</th>
+ <th>{{ _("Status") }}</th>
+ <th>{{ _("Premium") }}</th>
+ <th>{{ _("Valid until") }}</th>
+ <th>{{ _("Traffic left") }}</th>
+ <th>{{ _("Time") }}</th>
+ <th>{{ _("Max Parallel") }}</th>
+ <th>{{ _("Delete?") }}</th>
+ </tr>
+ </thead>
+
+
+ {% for account in conf.accs %}
+ {% set plugin = account.type %}
+ <tr>
+ <td>
+ <span style="padding:5px">{{ plugin }}</span>
+ </td>
+
+ <td><label for="{{plugin}}|password;{{account.login}}"
+ style="color:#424242;">{{ account.login }}</label></td>
+ <td>
+ <input id="{{plugin}}|password;{{account.login}}"
+ name="{{plugin}}|password;{{account.login}}"
+ type="password" value="{{account.password}}" size="12"/>
+ </td>
+ <td>
+ {% if account.valid %}
+ <span style="font-weight: bold; color: #006400;">
+ {{ _("valid") }}
+ {% else %}
+ <span style="font-weight: bold; color: #8b0000;">
+ {{ _("not valid") }}
+ {% endif %}
+ </span>
+ </td>
+ <td>
+ {% if account.premium %}
+ <span style="font-weight: bold; color: #006400;">
+ {{ _("yes") }}
+ {% else %}
+ <span style="font-weight: bold; color: #8b0000;">
+ {{ _("no") }}
+ {% endif %}
+ </span>
+ </td>
+ <td>
+ <span style="font-weight: bold;">
+ {{ account.validuntil }}
+ </span>
+ </td>
+ <td>
+ <span style="font-weight: bold;">
+ {{ account.trafficleft }}
+ </span>
+ </td>
+ <td>
+ <input id="{{plugin}}|time;{{account.login}}"
+ name="{{plugin}}|time;{{account.login}}" type="text"
+ size="7" value="{{account.time}}"/>
+ </td>
+ <td>
+ <input id="{{plugin}}|limitdl;{{account.login}}"
+ name="{{plugin}}|limitdl;{{account.login}}" type="text"
+ size="2" value="{{account.limitdl}}"/>
+ </td>
+ <td>
+ <input id="{{plugin}}|delete;{{account.login}}"
+ name="{{plugin}}|delete;{{account.login}}" type="checkbox"
+ value="True"/>
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
+
+ <button id="account_submit" type="submit" class="styled_button">{{_("Submit")}}</button>
+ <button id="account_add" style="margin-left: 0" type="submit" class="styled_button">{{_("Add")}}</button>
+ </form>
+ </span>
+ </span>
+{% endblock %}
+{% block hidden %}
+<div id="account_box" class="window_box" style="z-index: 2">
+<form id="add_account_form" action="/json/add_account" method="POST" enctype="multipart/form-data">
+<h1>{{_("Add Account")}}</h1>
+<p>{{_("Enter your account data to use premium features.")}}</p>
+<label for="account_login">{{_("Login")}}
+<span class="small">{{_("Your username.")}}</span>
+</label>
+<input id="account_login" name="account_login" type="text" size="20" />
+
+<label for="account_password">{{_("Password")}}
+<span class="small">{{_("The password for this account.")}}</span>
+</label>
+<input id="account_password" name="account_password" type="password" size="20" />
+
+<label for="account_type">{{_("Type")}}
+<span class="small">{{_("Choose the hoster for your account.")}}</span>
+</label>
+ <select name=account_type id="account_type">
+ {% for type in types|sort %}
+ <option value="{{ type }}">{{ type }}</option>
+ {% endfor %}
+ </select>
+
+<button id="account_add_button" type="submit">{{_("Add")}}</button>
+<button id="account_reset" style="margin-left: 0" type="reset">{{_("Reset")}}</button>
+<div class="spacer"></div>
+
+</form>
+
+</div>
+{% endblock %} \ No newline at end of file
diff --git a/module/web/templates/default/settings_item.html b/module/webui/themes/Flat/tml/settings_item.html
index 813383343..813383343 100644
--- a/module/web/templates/default/settings_item.html
+++ b/module/webui/themes/Flat/tml/settings_item.html
diff --git a/module/webui/themes/Flat/tml/window.html b/module/webui/themes/Flat/tml/window.html
new file mode 100644
index 000000000..ea5d65e46
--- /dev/null
+++ b/module/webui/themes/Flat/tml/window.html
@@ -0,0 +1,46 @@
+<iframe id="upload_target" name="upload_target" src="" style="display: none; width:0;height:0"></iframe>
+
+<div id="add_box" class="window_box">
+<form id="add_form" action="/json/add_package" method="POST" enctype="multipart/form-data">
+<h1>{{_("Add Package")}}</h1>
+<p>{{_("Paste your links or upload a container.")}}</p>
+<label for="add_name">{{_("Name")}}
+<span class="small">{{_("The name of the new package.")}}</span>
+</label>
+<input id="add_name" name="add_name" type="text" size="20" />
+
+<label for="add_links">{{_("Links")}}
+<span class="small">{{_("Paste your links here or any text and press the filter button.")}}</span>
+<span class="small"> {{ _("Filter urls") }}
+<img alt="URIParsing" Title="Parse Uri" src="/Flat/img/default/parseUri.png" style="cursor:pointer; vertical-align: text-bottom;" onclick="parseUri()"/>
+</span>
+
+</label>
+<textarea rows="5" name="add_links" id="add_links"></textarea>
+
+<label for="add_password">{{_("Password")}}
+ <span class="small">{{_("Password for RAR-Archive")}}</span>
+</label>
+<input id="add_password" name="add_password" type="text" size="20">
+
+<label>{{_("File")}}
+<span class="small">{{_("Upload a container.")}}</span>
+</label>
+<input type="file" name="add_file" id="add_file"/>
+
+<label for="add_dest">{{_("Destination")}}
+</label>
+<span class="cont">
+ {{_("Queue")}}
+ <input type="radio" name="add_dest" id="add_dest" value="1" checked="checked"/>
+ {{_("Collector")}}
+ <input type="radio" name="add_dest" id="add_dest2" value="0"/>
+</span>
+
+<button type="submit">{{_("Add Package")}}</button>
+<button id="add_reset" style="margin-left:0;" type="reset">{{_("Reset")}}</button>
+<div class="spacer"></div>
+
+</form>
+
+</div> \ No newline at end of file