diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/.gitattributes b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/.gitattributes new file mode 100644 index 0000000..acc5026 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/.gitattributes @@ -0,0 +1,15 @@ +# Set default behaviour, in case users don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files we want to always be normalized and converted +# to native line endings on checkout. +*.php text eol=lf +*.js text eol=lf +*.css text eol=lf +*.html text eol=lf +*.xml text eol=lf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/.gitignore b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/.gitignore new file mode 100644 index 0000000..16af611 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/.gitignore @@ -0,0 +1,28 @@ +.*.swp +.*.swo +._* +.DS_Store +/ImgCache/ +/Backup_rar/ +/Debug/ +/debug/ +/upload/ +/avatar/ +/.idea/ +.svn/ +*.orig +*.aps +*.APS +*.chm +*.exp +*.pdb +*.rar +*.mo +*.po +*.pot +.smbdelete* +*.sublime* +.sass-cache +config.rb +/config.inc.php +/usr/uploads/ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/.gitmodules b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/.travis.yml b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/.travis.yml new file mode 100644 index 0000000..02b4d16 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/.travis.yml @@ -0,0 +1,12 @@ +language: php + +php: + - 5.6 + - 5.5 + - 5.4 + - 5.3 + - 5.2 + - hhvm + +script: cd ./tools/ && set -e && make test + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/README.md b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/README.md new file mode 100644 index 0000000..69fcb17 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/README.md @@ -0,0 +1,15 @@ +Typecho Blogging Platform +========================= + +####Homepage +http://typecho.org/ + +####Document +http://docs.typecho.org/ + +####Forum +http://forum.typecho.org/ + +####Download +http://typecho.org/download + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/category.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/category.php new file mode 100644 index 0000000..97de61d --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/category.php @@ -0,0 +1,23 @@ + + +
+
+ +
+
+ form()->render(); ?> +
+
+
+
+ + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/common-js.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/common-js.php new file mode 100644 index 0000000..e652930 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/common-js.php @@ -0,0 +1,105 @@ + + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/common.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/common.php new file mode 100644 index 0000000..e3a6702 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/common.php @@ -0,0 +1,59 @@ +begin(); + +Typecho_Widget::widget('Widget_Options')->to($options); +Typecho_Widget::widget('Widget_User')->to($user); +Typecho_Widget::widget('Widget_Security')->to($security); +Typecho_Widget::widget('Widget_Menu')->to($menu); + +/** 初始化上下文 */ +$request = $options->request; +$response = $options->response; + +/** 检测是否是第一次登录 */ +$currentMenu = $menu->getCurrentMenu(); +list($prefixVersion, $suffixVersion) = explode('/', $options->version); +$params = parse_url($currentMenu[2]); +$adminFile = basename($params['path']); + +if (!$user->logged && !Typecho_Cookie::get('__typecho_first_run') && !empty($currentMenu)) { + + if ('welcome.php' != $adminFile) { + $response->redirect(Typecho_Common::url('welcome.php', $options->adminUrl)); + } else { + Typecho_Cookie::set('__typecho_first_run', 1); + } + +} else { + + /** 检测版本是否升级 */ + if ($user->pass('administrator', true) && !empty($currentMenu)) { + $mustUpgrade = (!defined('Typecho_Common::VERSION') || version_compare(str_replace('/', '.', Typecho_Common::VERSION), + str_replace('/', '.', $options->version), '>')); + + if ($mustUpgrade && 'upgrade.php' != $adminFile) { + $response->redirect(Typecho_Common::url('upgrade.php', $options->adminUrl)); + } else if (!$mustUpgrade && 'upgrade.php' == $adminFile) { + $response->redirect($options->adminUrl); + } else if (!$mustUpgrade && 'welcome.php' == $adminFile && $user->logged) { + $response->redirect($options->adminUrl); + } + } + +} diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/copyright.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/copyright.php new file mode 100644 index 0000000..b17e928 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/copyright.php @@ -0,0 +1,13 @@ + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/css/grid.css b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/css/grid.css new file mode 100644 index 0000000..6303d21 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/css/grid.css @@ -0,0 +1,748 @@ +/* + * Bento Grid System + * Source: https://github.com/fenbox/bento + * Version: 1.2.8 + * Update: 2013.11.25 + */ +/* line 23, ../scss/grid.scss */ +.container, .row [class*="col-"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } + +/* line 31, ../scss/grid.scss */ +.container { + margin-left: auto; + margin-right: auto; + padding-left: 10px; + padding-right: 10px; } + +/* line 40, ../scss/grid.scss */ +.row { + margin-right: -10px; + margin-left: -10px; } + +/* line 46, ../scss/grid.scss */ +.row [class*="col-"] { + float: left; + min-height: 1px; + padding-right: 10px; + padding-left: 10px; } + +/* line 54, ../scss/grid.scss */ +.row [class*="-push-"], +.row [class*="-pull-"] { + position: relative; } + +/* + * Mobile and up + */ +/* line 65, ../scss/grid.scss */ +.col-mb-1 { + width: 8.33333%; } + +/* line 65, ../scss/grid.scss */ +.col-mb-2 { + width: 16.66667%; } + +/* line 65, ../scss/grid.scss */ +.col-mb-3 { + width: 25%; } + +/* line 65, ../scss/grid.scss */ +.col-mb-4 { + width: 33.33333%; } + +/* line 65, ../scss/grid.scss */ +.col-mb-5 { + width: 41.66667%; } + +/* line 65, ../scss/grid.scss */ +.col-mb-6 { + width: 50%; } + +/* line 65, ../scss/grid.scss */ +.col-mb-7 { + width: 58.33333%; } + +/* line 65, ../scss/grid.scss */ +.col-mb-8 { + width: 66.66667%; } + +/* line 65, ../scss/grid.scss */ +.col-mb-9 { + width: 75%; } + +/* line 65, ../scss/grid.scss */ +.col-mb-10 { + width: 83.33333%; } + +/* line 65, ../scss/grid.scss */ +.col-mb-11 { + width: 91.66667%; } + +/* line 65, ../scss/grid.scss */ +.col-mb-12 { + width: 100%; } + +/* + * Tablet and up + */ +@media (min-width: 768px) { + /* line 76, ../scss/grid.scss */ + .container { + max-width: 728px; } + + /* line 82, ../scss/grid.scss */ + .col-tb-1 { + width: 8.33333%; } + + /* line 82, ../scss/grid.scss */ + .col-tb-2 { + width: 16.66667%; } + + /* line 82, ../scss/grid.scss */ + .col-tb-3 { + width: 25%; } + + /* line 82, ../scss/grid.scss */ + .col-tb-4 { + width: 33.33333%; } + + /* line 82, ../scss/grid.scss */ + .col-tb-5 { + width: 41.66667%; } + + /* line 82, ../scss/grid.scss */ + .col-tb-6 { + width: 50%; } + + /* line 82, ../scss/grid.scss */ + .col-tb-7 { + width: 58.33333%; } + + /* line 82, ../scss/grid.scss */ + .col-tb-8 { + width: 66.66667%; } + + /* line 82, ../scss/grid.scss */ + .col-tb-9 { + width: 75%; } + + /* line 82, ../scss/grid.scss */ + .col-tb-10 { + width: 83.33333%; } + + /* line 82, ../scss/grid.scss */ + .col-tb-11 { + width: 91.66667%; } + + /* line 82, ../scss/grid.scss */ + .col-tb-12 { + width: 100%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-0 { + margin-left: 0%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-1 { + margin-left: 8.33333%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-2 { + margin-left: 16.66667%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-3 { + margin-left: 25%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-4 { + margin-left: 33.33333%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-5 { + margin-left: 41.66667%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-6 { + margin-left: 50%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-7 { + margin-left: 58.33333%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-8 { + margin-left: 66.66667%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-9 { + margin-left: 75%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-10 { + margin-left: 83.33333%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-11 { + margin-left: 91.66667%; } + + /* line 89, ../scss/grid.scss */ + .col-tb-offset-12 { + margin-left: 100%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-0 { + right: 0%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-1 { + right: 8.33333%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-2 { + right: 16.66667%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-3 { + right: 25%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-4 { + right: 33.33333%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-5 { + right: 41.66667%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-6 { + right: 50%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-7 { + right: 58.33333%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-8 { + right: 66.66667%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-9 { + right: 75%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-10 { + right: 83.33333%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-11 { + right: 91.66667%; } + + /* line 96, ../scss/grid.scss */ + .col-tb-pull-12 { + right: 100%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-0 { + left: 0%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-1 { + left: 8.33333%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-2 { + left: 16.66667%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-3 { + left: 25%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-4 { + left: 33.33333%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-5 { + left: 41.66667%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-6 { + left: 50%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-7 { + left: 58.33333%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-8 { + left: 66.66667%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-9 { + left: 75%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-10 { + left: 83.33333%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-11 { + left: 91.66667%; } + + /* line 103, ../scss/grid.scss */ + .col-tb-push-12 { + left: 100%; } } +/* + * Desktop and up + */ +@media (min-width: 992px) { + /* line 115, ../scss/grid.scss */ + .container { + max-width: 952px; } + + /* line 121, ../scss/grid.scss */ + .col-1 { + width: 8.33333%; } + + /* line 121, ../scss/grid.scss */ + .col-2 { + width: 16.66667%; } + + /* line 121, ../scss/grid.scss */ + .col-3 { + width: 25%; } + + /* line 121, ../scss/grid.scss */ + .col-4 { + width: 33.33333%; } + + /* line 121, ../scss/grid.scss */ + .col-5 { + width: 41.66667%; } + + /* line 121, ../scss/grid.scss */ + .col-6 { + width: 50%; } + + /* line 121, ../scss/grid.scss */ + .col-7 { + width: 58.33333%; } + + /* line 121, ../scss/grid.scss */ + .col-8 { + width: 66.66667%; } + + /* line 121, ../scss/grid.scss */ + .col-9 { + width: 75%; } + + /* line 121, ../scss/grid.scss */ + .col-10 { + width: 83.33333%; } + + /* line 121, ../scss/grid.scss */ + .col-11 { + width: 91.66667%; } + + /* line 121, ../scss/grid.scss */ + .col-12 { + width: 100%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-0 { + margin-left: 0%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-1 { + margin-left: 8.33333%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-2 { + margin-left: 16.66667%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-3 { + margin-left: 25%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-4 { + margin-left: 33.33333%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-5 { + margin-left: 41.66667%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-6 { + margin-left: 50%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-7 { + margin-left: 58.33333%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-8 { + margin-left: 66.66667%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-9 { + margin-left: 75%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-10 { + margin-left: 83.33333%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-11 { + margin-left: 91.66667%; } + + /* line 128, ../scss/grid.scss */ + .col-offset-12 { + margin-left: 100%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-0 { + right: 0%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-1 { + right: 8.33333%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-2 { + right: 16.66667%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-3 { + right: 25%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-4 { + right: 33.33333%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-5 { + right: 41.66667%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-6 { + right: 50%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-7 { + right: 58.33333%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-8 { + right: 66.66667%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-9 { + right: 75%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-10 { + right: 83.33333%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-11 { + right: 91.66667%; } + + /* line 135, ../scss/grid.scss */ + .col-pull-12 { + right: 100%; } + + /* line 142, ../scss/grid.scss */ + .col-push-0 { + left: 0%; } + + /* line 142, ../scss/grid.scss */ + .col-push-1 { + left: 8.33333%; } + + /* line 142, ../scss/grid.scss */ + .col-push-2 { + left: 16.66667%; } + + /* line 142, ../scss/grid.scss */ + .col-push-3 { + left: 25%; } + + /* line 142, ../scss/grid.scss */ + .col-push-4 { + left: 33.33333%; } + + /* line 142, ../scss/grid.scss */ + .col-push-5 { + left: 41.66667%; } + + /* line 142, ../scss/grid.scss */ + .col-push-6 { + left: 50%; } + + /* line 142, ../scss/grid.scss */ + .col-push-7 { + left: 58.33333%; } + + /* line 142, ../scss/grid.scss */ + .col-push-8 { + left: 66.66667%; } + + /* line 142, ../scss/grid.scss */ + .col-push-9 { + left: 75%; } + + /* line 142, ../scss/grid.scss */ + .col-push-10 { + left: 83.33333%; } + + /* line 142, ../scss/grid.scss */ + .col-push-11 { + left: 91.66667%; } + + /* line 142, ../scss/grid.scss */ + .col-push-12 { + left: 100%; } } +/* + * Widescreen and up + */ +@media (min-width: 1200px) { + /* line 154, ../scss/grid.scss */ + .container { + max-width: 1160px; } + + /* line 160, ../scss/grid.scss */ + .col-wd-1 { + width: 8.33333%; } + + /* line 160, ../scss/grid.scss */ + .col-wd-2 { + width: 16.66667%; } + + /* line 160, ../scss/grid.scss */ + .col-wd-3 { + width: 25%; } + + /* line 160, ../scss/grid.scss */ + .col-wd-4 { + width: 33.33333%; } + + /* line 160, ../scss/grid.scss */ + .col-wd-5 { + width: 41.66667%; } + + /* line 160, ../scss/grid.scss */ + .col-wd-6 { + width: 50%; } + + /* line 160, ../scss/grid.scss */ + .col-wd-7 { + width: 58.33333%; } + + /* line 160, ../scss/grid.scss */ + .col-wd-8 { + width: 66.66667%; } + + /* line 160, ../scss/grid.scss */ + .col-wd-9 { + width: 75%; } + + /* line 160, ../scss/grid.scss */ + .col-wd-10 { + width: 83.33333%; } + + /* line 160, ../scss/grid.scss */ + .col-wd-11 { + width: 91.66667%; } + + /* line 160, ../scss/grid.scss */ + .col-wd-12 { + width: 100%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-0 { + margin-left: 0%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-1 { + margin-left: 8.33333%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-2 { + margin-left: 16.66667%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-3 { + margin-left: 25%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-4 { + margin-left: 33.33333%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-5 { + margin-left: 41.66667%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-6 { + margin-left: 50%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-7 { + margin-left: 58.33333%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-8 { + margin-left: 66.66667%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-9 { + margin-left: 75%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-10 { + margin-left: 83.33333%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-11 { + margin-left: 91.66667%; } + + /* line 167, ../scss/grid.scss */ + .col-wd-offset-12 { + margin-left: 100%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-0 { + right: 0%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-1 { + right: 8.33333%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-2 { + right: 16.66667%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-3 { + right: 25%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-4 { + right: 33.33333%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-5 { + right: 41.66667%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-6 { + right: 50%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-7 { + right: 58.33333%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-8 { + right: 66.66667%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-9 { + right: 75%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-10 { + right: 83.33333%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-11 { + right: 91.66667%; } + + /* line 174, ../scss/grid.scss */ + .col-wd-pull-12 { + right: 100%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-0 { + left: 0%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-1 { + left: 8.33333%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-2 { + left: 16.66667%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-3 { + left: 25%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-4 { + left: 33.33333%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-5 { + left: 41.66667%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-6 { + left: 50%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-7 { + left: 58.33333%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-8 { + left: 66.66667%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-9 { + left: 75%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-10 { + left: 83.33333%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-11 { + left: 91.66667%; } + + /* line 181, ../scss/grid.scss */ + .col-wd-push-12 { + left: 100%; } } +/* + * Responsive kit + */ +@media (max-width: 767px) { + /* line 194, ../scss/grid.scss */ + .kit-hidden-mb { + display: none; } } +@media (max-width: 991px) { + /* line 201, ../scss/grid.scss */ + .kit-hidden-tb { + display: none; } } +@media (max-width: 1199px) { + /* line 208, ../scss/grid.scss */ + .kit-hidden { + display: none; } } +/* + * Clearfix + */ +/* line 217, ../scss/grid.scss */ +.clearfix, .row { + zoom: 1; } + /* line 219, ../scss/grid.scss */ + .clearfix:before, .row:before, .clearfix:after, .row:after { + content: " "; + display: table; } + /* line 223, ../scss/grid.scss */ + .clearfix:after, .row:after { + clear: both; } diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/css/normalize.css b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/css/normalize.css new file mode 100644 index 0000000..c2de8df --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/css/normalize.css @@ -0,0 +1,406 @@ +/*! normalize.css v2.1.3 | MIT License | git.io/normalize */ + +/* ========================================================================== + HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined in IE 8/9. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} + +/** + * Correct `inline-block` display not defined in IE 8/9. + */ + +audio, +canvas, +video { + display: inline-block; +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address `[hidden]` styling not present in IE 8/9. + * Hide the `template` element in IE, Safari, and Firefox < 22. + */ + +[hidden], +template { + display: none; +} + +/* ========================================================================== + Base + ========================================================================== */ + +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* ========================================================================== + Links + ========================================================================== */ + +/** + * Remove the gray background color from active links in IE 10. + */ + +a { + background: transparent; +} + +/** + * Address `outline` inconsistency between Chrome and other browsers. + */ + +a:focus { + outline: thin dotted; +} + +/** + * Improve readability when focused and also mouse hovered in all browsers. + */ + +a:active, +a:hover { + outline: 0; +} + +/* ========================================================================== + Typography + ========================================================================== */ + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari 5, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/** + * Address styling not present in IE 8/9, Safari 5, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/** + * Address styling not present in Safari 5 and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Correct font family set oddly in Safari 5 and Chrome. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 1em; +} + +/** + * Improve readability of pre-formatted text in all browsers. + */ + +pre { + white-space: pre-wrap; +} + +/** + * Set consistent quote types. + */ + +q { + quotes: "\201C" "\201D" "\2018" "\2019"; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* ========================================================================== + Embedded content + ========================================================================== */ + +/** + * Remove border when inside `a` element in IE 8/9. + */ + +img { + border: 0; +} + +/** + * Correct overflow displayed oddly in IE 9. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* ========================================================================== + Figures + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari 5. + */ + +figure { + margin: 0; +} + +/* ========================================================================== + Forms + ========================================================================== */ + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * 1. Correct font family not being inherited in all browsers. + * 2. Correct font size not being inherited in all browsers. + * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. + */ + +button, +input, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 2 */ + margin: 0; /* 3 */ +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +button, +input { + line-height: normal; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. + * Correct `select` style inheritance in Firefox 4+ and Opera. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/** + * Remove inner padding and search cancel button in Safari 5 and Chrome + * on OS X. + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * 1. Remove default vertical scrollbar in IE 8/9. + * 2. Improve readability and alignment in all browsers. + */ + +textarea { + overflow: auto; /* 1 */ + vertical-align: top; /* 2 */ +} + +/* ========================================================================== + Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/css/style.css b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/css/style.css new file mode 100644 index 0000000..3037620 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/css/style.css @@ -0,0 +1,2118 @@ +@charset "UTF-8"; +/* vim: set et sw=2 ts=2 sts=2 fdm=marker ff=unix fenc=utf8 */ +/** +* Typecho 后台样式 +* +* @author Typecho Team +* @since 2008-09-26 +* @update 2013-11-02 +* @link http://www.typecho.org/ +* @version 0.9 +*/ +/** +* Typecho 全局样式 +*/ +/* line 18, ../scss/style.scss */ +html { + height: 100%; } + +/* line 22, ../scss/style.scss */ +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + background: #F6F6F3; + color: #444; + font-size: 87.5%; + line-height: 1.5; } + +/* line 30, ../scss/style.scss */ +a { + color: #467B96; + text-decoration: none; } + /* line 33, ../scss/style.scss */ + a:hover { + color: #499BC3; + text-decoration: underline; } + +/* line 39, ../scss/style.scss */ +code, pre, .mono { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } + +/* line 43, ../scss/style.scss */ +.p { + margin: 1em 0; } + +/* line 45, ../scss/style.scss */ +.body-100 { + height: 100%; } + +/* line 49, ../scss/style.scss */ +a.balloon-button { + display: inline-block; + padding: 0 6px; + min-width: 12px; + height: 18px; + line-height: 18px; + background: #D8E7EE; + font-size: .85714em; + text-align: center; + text-decoration: none; + /** 修正ie中文不对齐 */ + zoom: 1; + -moz-border-radius: 30px; + -webkit-border-radius: 30px; + border-radius: 30px; } + +/* line 68, ../scss/style.scss */ +a.button:hover, a.balloon-button:hover { + background-color: #A5CADC; + color: #FFF; + text-decoration: none; } + +/** +* Forms +*/ +/* line 5, ../scss/_forms.scss */ +input[type=text], input[type=password], input[type=email], +textarea { + background: #FFF; + border: 1px solid #D9D9D6; + padding: 7px; + border-radius: 2px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } + +/* line 18, ../scss/_forms.scss */ +textarea { + resize: vertical; + line-height: 1.5; } + +/* line 23, ../scss/_forms.scss */ +input[type="radio"], input[type="checkbox"] { + margin-right: 3px; } + +/* line 26, ../scss/_forms.scss */ +input.text-s, textarea.text-s { + padding: 5px; } +/* line 27, ../scss/_forms.scss */ +input.text-l, textarea.text-l { + padding: 10px; + font-size: 1.14286em; } + +/* line 33, ../scss/_forms.scss */ +.w-10 { + width: 10%; } + +/* line 34, ../scss/_forms.scss */ +.w-20 { + width: 20%; } + +/* line 35, ../scss/_forms.scss */ +.w-30 { + width: 30%; } + +/* line 36, ../scss/_forms.scss */ +.w-40 { + width: 40%; } + +/* line 37, ../scss/_forms.scss */ +.w-50 { + width: 50%; } + +/* line 38, ../scss/_forms.scss */ +.w-60 { + width: 60%; } + +/* line 39, ../scss/_forms.scss */ +.w-70 { + width: 70%; } + +/* line 40, ../scss/_forms.scss */ +.w-80 { + width: 80%; } + +/* line 41, ../scss/_forms.scss */ +.w-90 { + width: 90%; } + +/* line 42, ../scss/_forms.scss */ +.w-100 { + width: 100%; } + +/* line 44, ../scss/_forms.scss */ +select { + border: 1px solid #CCC; + height: 28px; } + +/** +* Buttons +*/ +/* line 28, ../scss/_buttons.scss */ +.btn, #ui-datepicker-div .ui-datepicker-current, +#ui-datepicker-div .ui-datepicker-close { + border: none; + background-color: #E9E9E6; + cursor: pointer; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + display: inline-block; + padding: 0 12px; + height: 32px; + color: #666; + vertical-align: middle; + zoom: 1; } + /* line 15, ../scss/_buttons.scss */ + .btn:hover, #ui-datepicker-div .ui-datepicker-current:hover, + #ui-datepicker-div .ui-datepicker-close:hover { + -moz-transition-duration: 0.4s; + -o-transition-duration: 0.4s; + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; + background-color: #dbdbd6; } + /* line 19, ../scss/_buttons.scss */ + .btn:active, #ui-datepicker-div .ui-datepicker-current:active, + #ui-datepicker-div .ui-datepicker-close:active, .btn.active, #ui-datepicker-div .active.ui-datepicker-current, + #ui-datepicker-div .active.ui-datepicker-close { + background-color: #d6d6d0; } + /* line 22, ../scss/_buttons.scss */ + .btn:disabled, #ui-datepicker-div .ui-datepicker-current:disabled, + #ui-datepicker-div .ui-datepicker-close:disabled { + background-color: #f7f7f6; + cursor: default; } + /* line 38, ../scss/_buttons.scss */ + .btn:disabled, #ui-datepicker-div .ui-datepicker-current:disabled, + #ui-datepicker-div .ui-datepicker-close:disabled { + color: #999; } + +/* line 43, ../scss/_buttons.scss */ +.btn-xs, #ui-datepicker-div .ui-datepicker-current, +#ui-datepicker-div .ui-datepicker-close { + padding: 0 10px; + height: 25px; + font-size: 13px; } + +/* line 48, ../scss/_buttons.scss */ +.btn-s { + height: 28px; } + +/* line 49, ../scss/_buttons.scss */ +.btn-l { + height: 40px; + font-size: 1.14286em; + font-weight: bold; } + +/* line 55, ../scss/_buttons.scss */ +.primary { + border: none; + background-color: #467B96; + cursor: pointer; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + color: #FFF; } + /* line 15, ../scss/_buttons.scss */ + .primary:hover { + -moz-transition-duration: 0.4s; + -o-transition-duration: 0.4s; + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; + background-color: #3c6a81; } + /* line 19, ../scss/_buttons.scss */ + .primary:active, .primary.active { + background-color: #39647a; } + /* line 22, ../scss/_buttons.scss */ + .primary:disabled { + background-color: #508cab; + cursor: default; } + +/* line 60, ../scss/_buttons.scss */ +.btn-group { + display: inline-block; } + +/* line 64, ../scss/_buttons.scss */ +.btn-warn { + border: none; + background-color: #B94A48; + cursor: pointer; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + color: #FFF; } + /* line 15, ../scss/_buttons.scss */ + .btn-warn:hover { + -moz-transition-duration: 0.4s; + -o-transition-duration: 0.4s; + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; + background-color: #a4403f; } + /* line 19, ../scss/_buttons.scss */ + .btn-warn:active, .btn-warn.active { + background-color: #9c3e3c; } + /* line 22, ../scss/_buttons.scss */ + .btn-warn:disabled { + background-color: #c1605e; + cursor: default; } + +/* line 69, ../scss/_buttons.scss */ +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active, +.btn-link.active { + background-color: transparent; } + +/* 下拉菜单 */ +/* line 78, ../scss/_buttons.scss */ +.btn-drop { + position: relative; } + +/* line 81, ../scss/_buttons.scss */ +.dropdown-toggle { + padding-right: 8px; } + +/* line 84, ../scss/_buttons.scss */ +.dropdown-menu { + list-style: none; + position: absolute; + z-index: 2; + left: 0; + margin: 0; + padding: 0; + border: 1px solid #D9D9D6; + background: #FFF; + text-align: left; + min-width: 108px; + display: none; } + /* line 97, ../scss/_buttons.scss */ + .dropdown-menu li { + white-space: nowrap; } + /* line 99, ../scss/_buttons.scss */ + .dropdown-menu li.multiline { + padding: 5px 12px 12px; } + /* line 104, ../scss/_buttons.scss */ + .dropdown-menu a { + display: block; + padding: 5px 12px; + color: #666; } + /* line 108, ../scss/_buttons.scss */ + .dropdown-menu a:hover { + background: #F6F6F3; + text-decoration: none !important; } + +/** +* 提示信息框 +*/ +/* line 6, ../scss/_messages.scss */ +.message { + padding: 8px 10px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; } + +/* line 11, ../scss/_messages.scss */ +.message a { + font-weight: bold; + text-decoration: underline; } + +/* line 16, ../scss/_messages.scss */ +.error { + background: #FBE3E4; + color: #8A1F11; } + +/* line 20, ../scss/_messages.scss */ +.error a { + color: #8A1F11; } + +/* line 22, ../scss/_messages.scss */ +.notice { + background: #FFF6BF; + color: #8A6D3B; } + +/* line 26, ../scss/_messages.scss */ +.notice a { + color: #8A6D3B; } + +/* line 28, ../scss/_messages.scss */ +.success { + background: #E6EFC2; + color: #264409; } + +/* line 32, ../scss/_messages.scss */ +.success a { + color: #264409; } + +/* line 36, ../scss/_messages.scss */ +.balloon { + display: inline-block; + padding: 0 4px; + min-width: 10px; + height: 14px; + line-height: 14px; + background: #B9B9B6; + vertical-align: text-top; + text-align: center; + font-size: 12px; + color: #FFF; + -moz-border-radius: 20px; + -webkit-border-radius: 20px; + border-radius: 20px; } + +/** +* 后台分页 +*/ +/* line 5, ../scss/_pagenavi.scss */ +.typecho-pager { + list-style: none; + float: right; + margin: 0; + padding: 0; + line-height: 1; + text-align: center; + zoom: 1; } + +/* line 15, ../scss/_pagenavi.scss */ +.typecho-pager li { + display: inline-block; + margin: 0 3px; + height: 28px; + line-height: 28px; } + +/* line 22, ../scss/_pagenavi.scss */ +.typecho-pager a { + display: block; + padding: 0 10px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; } + +/* line 28, ../scss/_pagenavi.scss */ +.typecho-pager a:hover { + text-decoration: none; + background: #E9E9E6; } + +/* line 33, ../scss/_pagenavi.scss */ +.typecho-pager li.current a { + background: #E9E9E6; + color: #444; } + +/** +* 后台头部导航 +*/ +/* line 4, ../scss/_header.scss */ +.typecho-head-nav { + padding: 0 10px; + background: #292D33; } + +/* line 9, ../scss/_header.scss */ +.typecho-head-nav a { + color: #BBB; } + +/* line 12, ../scss/_header.scss */ +.typecho-head-nav a:hover, +.typecho-head-nav a:focus { + color: #FFF; + text-decoration: none; } + +/* line 18, ../scss/_header.scss */ +#typecho-nav-list { + float: left; } + /* line 20, ../scss/_header.scss */ + #typecho-nav-list ul { + list-style: none; + margin: 0; + padding: 0; } + +/* line 27, ../scss/_header.scss */ +#typecho-nav-list ul:first-child { + border-left: 1px solid #383D45; } + +/* line 31, ../scss/_header.scss */ +#typecho-nav-list .root { + position: relative; + float: left; } + +/* line 36, ../scss/_header.scss */ +#typecho-nav-list .parent a { + display: block; + float: left; + padding: 0 20px; + border-right: 1px solid #383D45; + height: 36px; + line-height: 36px; + color: #BBB; } + +/* line 46, ../scss/_header.scss */ +#typecho-nav-list .parent a:hover, +#typecho-nav-list .focus .parent a, +#typecho-nav-list .root:hover .parent a { + background: #202328; + color: #FFF; + text-decoration: none; } + +/* line 54, ../scss/_header.scss */ +#typecho-nav-list .focus .parent a { + font-weight: bold; } + +/* line 58, ../scss/_header.scss */ +#typecho-nav-list .child { + position: absolute; + top: 36px; + display: none; + margin: 0; + min-width: 160px; + max-width: 240px; + background: #202328; + z-index: 250; } + +/* line 69, ../scss/_header.scss */ +#typecho-nav-list .root:hover .child { + display: block; } + +/* line 73, ../scss/_header.scss */ +#typecho-nav-list .child li a { + color: #BBB; + display: block; + padding: 0 20px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + height: 36px; + line-height: 36px; } + +/* line 84, ../scss/_header.scss */ +#typecho-nav-list .child li a:hover, +#typecho-nav-list .child li a:focus { + background: #292D33; + color: #FFF; } + +/* line 89, ../scss/_header.scss */ +#typecho-nav-list .child li.focus a { + color: #6DA1BB; + font-weight: bold; } + +/* line 94, ../scss/_header.scss */ +.typecho-head-nav .operate { + float: right; } + +/* line 97, ../scss/_header.scss */ +.typecho-head-nav .operate a { + display: inline-block; + margin-left: -1px; + padding: 0 20px; + border: 1px solid #383D45; + border-width: 0 1px; + line-height: 36px; + color: #BBB; } + +/* line 106, ../scss/_header.scss */ +.typecho-head-nav .operate a:hover { + background-color: #202328; + color: #FFF; } + +/** +* 注脚 +*/ +/* line 4, ../scss/_footer.scss */ +.typecho-foot { + padding: 4em 0 3em; + color: #999; + line-height: 1.8; + text-align: center; } + /* line 10, ../scss/_footer.scss */ + .typecho-foot .copyright p { + margin: 10px 0 0; } + /* line 13, ../scss/_footer.scss */ + .typecho-foot .resource { + color: #CCC; } + /* line 16, ../scss/_footer.scss */ + .typecho-foot .resource a { + margin: 0 3px; + color: #999; } + +/* 低版本浏览器升级提示 */ +/* line 84, ../scss/style.scss */ +.browsehappy { + border: none; + text-align: center; } + +/** 顶部消息样式 by 70 */ +/* line 90, ../scss/style.scss */ +.popup { + display: none; + position: absolute; + top: 0; + left: 0; + margin: 0; + padding: 8px 0; + border: none; + width: 100%; + z-index: 10; + text-align: center; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; } + +/* line 107, ../scss/style.scss */ +.popup ul { + list-style: none; + margin: 0; + padding: 0; + text-align: center; } + +/* line 114, ../scss/style.scss */ +.popup ul li { + display: inline-block; + margin-right: 10px; } + +/** +* logo 的样式 +*/ +/** +* 载入状态 +*/ +/* line 128, ../scss/style.scss */ +.loading { + padding-left: 20px !important; + background: transparent url(../img/ajax-loader.gif) no-repeat left center; } + +/** +* 典型配置选项 +*/ +/* line 137, ../scss/style.scss */ +.typecho-option { + list-style: none; + margin: 1em 0; + padding: 0; } + +/* line 146, ../scss/style.scss */ +.typecho-option-submit li { + border-bottom: none; } + +/* line 150, ../scss/style.scss */ +.typecho-option label.typecho-label { + display: block; + margin-bottom: .5em; + font-weight: bold; } + +/* line 155, ../scss/style.scss */ +.typecho-option label.required:after { + content: " *"; + color: #B94A48; } + +/* line 161, ../scss/style.scss */ +.typecho-option span { + margin-right: 15px; } + +/* line 162, ../scss/style.scss */ +.typecho-option .description { + margin: .5em 0 0; + color: #999; + font-size: .92857em; } + +/* line 168, ../scss/style.scss */ +.front-archive { + padding-left: 1.5em; } + +/* line 172, ../scss/style.scss */ +.profile-avatar { + border: 1px dashed #D9D9D6; + max-width: 100%; } + +/** 增加配置面板内部的错误样式 by 70 */ +/** +* 安装样式 +* +* @author mingcheng +* @date 2008-09-06 +*/ +/** +* 安装向导 +*/ +/* line 190, ../scss/style.scss */ +.typecho-install { + padding-bottom: 2em; } + +/* line 193, ../scss/style.scss */ +.typecho-install-patch { + margin-bottom: 2em; + padding: 2em 0; + background-color: #292D33; + color: #FFF; + text-align: center; } + +/* line 201, ../scss/style.scss */ +.typecho-install-patch ol { + list-style: none; + margin: 3em 0 1em; + padding: 0; + color: #999; } + +/* line 207, ../scss/style.scss */ +.typecho-install-patch li { + display: inline-block; + margin: 0 .8em; } + +/* line 211, ../scss/style.scss */ +.typecho-install-patch span { + display: inline-block; + margin-right: 5px; + width: 20px; + height: 20px; + line-height: 20px; + border: 2px solid #999; + text-align: center; + border-radius: 2em; } + +/* line 221, ../scss/style.scss */ +.typecho-install-patch li.current { + color: #FFF; + font-weight: bold; } + +/* line 225, ../scss/style.scss */ +.typecho-install-patch li.current span { + border-color: #FFF; } + +/** +* 安装主体内容 +*/ +/* line 234, ../scss/style.scss */ +.typecho-install .typecho-install-body input { + width: 100%; } + +/* line 237, ../scss/style.scss */ +.typecho-install-body .typecho-option li { + margin: 1em 0; } + +/** +* 欢迎界面 +*/ +/* line 246, ../scss/style.scss */ +#typecho-welcome { + margin: 1em 0; + padding: 1em 2em; + background-color: #E9E9E6; } + +/* line 252, ../scss/style.scss */ +.welcome-board { + color: #999; + font-size: 1.15em; } + /* line 255, ../scss/style.scss */ + .welcome-board em { + color: #444; + font-size: 2em; + font-style: normal; + font-family: Georgia, serif; } + +/* line 263, ../scss/style.scss */ +#start-link { + margin-bottom: 25px; + padding: 0 0 35px; + border-bottom: 1px solid #ECECEC; } + /* line 267, ../scss/style.scss */ + #start-link li { + float: left; + margin-right: 1.5em; } + /* line 271, ../scss/style.scss */ + #start-link .balloon { + margin-top: 2px; } + +/* line 277, ../scss/style.scss */ +.latest-link li { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } +/* line 282, ../scss/style.scss */ +.latest-link span { + display: inline-block; + margin-right: 4px; + padding-right: 8px; + border-right: 1px solid #ECECEC; + width: 37px; + text-align: right; + color: #999; } + +/* line 293, ../scss/style.scss */ +.update-check { + font-size: 14px; } + +/** +* 登录框 +*/ +/* line 300, ../scss/style.scss */ +.typecho-login-wrap { + display: table; + margin: 0 auto; + height: 100%; } + +/* line 305, ../scss/style.scss */ +.typecho-login { + display: table-cell; + padding: 30px 0 100px; + width: 280px; + text-align: center; + vertical-align: middle; } + /* line 311, ../scss/style.scss */ + .typecho-login h1 { + margin: 0 0 1em; } + +/* line 316, ../scss/style.scss */ +.typecho-login .more-link { + margin-top: 2em; + color: #CCC; } + +/* line 320, ../scss/style.scss */ +.typecho-login .more-link a { + margin: 0 3px; } + +/** +* 标题 +*/ +/* line 327, ../scss/style.scss */ +.typecho-page-title h2 { + margin: 25px 0 10px; + font-size: 1.28571em; } + +/* line 331, ../scss/style.scss */ +.typecho-page-title h2 a { + margin-left: 10px; + padding: 3px 8px; + background: #E9E9E6; + font-size: .8em; + border-radius: 2px; } + +/* line 339, ../scss/style.scss */ +.typecho-page-title h2 a:hover { + text-decoration: none; } + +/** +* 后台页面主体 +*/ +/** +* 主页主体 +*/ +/* line 353, ../scss/style.scss */ +.typecho-dashboard ul { + list-style: none; + padding: 0; } + +/* line 357, ../scss/style.scss */ +.typecho-dashboard li { + margin-bottom: 5px; } + +/** +* 标签页 +*/ +/* line 365, ../scss/style.scss */ +.typecho-option-tabs { + list-style: none; + margin: 1em 0 0; + padding: 0; + font-size: 13px; + text-align: center; } + /* line 371, ../scss/style.scss */ + .typecho-option-tabs.fix-tabs { + margin-bottom: 1em; } + +/* line 376, ../scss/style.scss */ +.typecho-option-tabs a { + display: block; + margin-right: -1px; + border: 1px solid #D9D9D6; + padding: 0 15px; + height: 26px; + line-height: 26px; + color: #666; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; } + +/* line 387, ../scss/style.scss */ +.typecho-option-tabs a:hover { + background-color: #E9E9E6; + color: #666; + text-decoration: none; } + +/* line 393, ../scss/style.scss */ +.typecho-option-tabs li { + float: left; } + /* line 395, ../scss/style.scss */ + .typecho-option-tabs li:first-child a { + -moz-border-radius: 2px 0 0 2px; + -webkit-border-radius: 2px; + border-radius: 2px 0 0 2px; } + /* line 398, ../scss/style.scss */ + .typecho-option-tabs li:last-child a { + -moz-border-radius: 0 2px 2px 0; + -webkit-border-radius: 0; + border-radius: 0 2px 2px 0; } + +/* line 403, ../scss/style.scss */ +.typecho-option-tabs.right { + float: right; } + +/* line 407, ../scss/style.scss */ +.typecho-option-tabs li.current a, +.typecho-option-tabs li.active a { + background-color: #E9E9E6; } + +/** +* 表格列表页 +*/ +/** +* 列表页选项 +*/ +/* line 423, ../scss/style.scss */ +.typecho-list-operate { + margin: 1em 0; } + +/* line 427, ../scss/style.scss */ +.typecho-list-operate input, +.typecho-list-operate button, +.typecho-list-operate select { + vertical-align: bottom; } + +/* line 433, ../scss/style.scss */ +.typecho-list-operate input[type="checkbox"] { + vertical-align: text-top; } + +/* line 437, ../scss/style.scss */ +.typecho-list-operate .operate { + float: left; } + +/* line 441, ../scss/style.scss */ +.typecho-list-operate .search { + float: right; } + +/* line 445, ../scss/style.scss */ +.typecho-list-operate span.operate-delete, a.operate-delete, +.typecho-list-operate span.operate-button-delete, a.operate-button-delete { + color: #B94A48; } + +/* line 450, ../scss/style.scss */ +a.operate-edit { + color: #007700; } + +/* line 454, ../scss/style.scss */ +a.operate-reply { + color: #545c30; } + +/* line 458, ../scss/style.scss */ +.typecho-list-operate a:hover { + text-decoration: none; } + +/** +* 列表表格 +*/ +/** 增加表格标题 by 70 */ +/* line 466, ../scss/style.scss */ +.typecho-list-table-title { + margin: 1em 0; + color: #999; + text-align: center; } + +/* line 471, ../scss/style.scss */ +.typecho-table-wrap { + padding: 30px; + background: #FFF; } + +/* line 475, ../scss/style.scss */ +.typecho-list-table { + width: 100%; } + +/* line 479, ../scss/style.scss */ +.typecho-list-table.deactivate { + color: #999; } + +/* line 483, ../scss/style.scss */ +.typecho-list-table .right { + text-align: right; } + +/* line 487, ../scss/style.scss */ +.typecho-list-table th { + padding: 0 10px 10px; + border-bottom: 2px solid #F0F0EC; + text-align: left; } + +/* line 493, ../scss/style.scss */ +.typecho-list-table td { + padding: 10px; + border-top: 1px solid #F0F0EC; + word-break: break-all; } + +/* line 498, ../scss/style.scss */ +.typecho-list-table .status { + margin-left: 5px; + color: #999; + font-size: .92857em; + font-style: normal; } + +/* line 504, ../scss/style.scss */ +.typecho-list-table tbody tr:hover td { + background-color: #F6F6F3; } + +/* line 508, ../scss/style.scss */ +.typecho-list-table tbody tr.checked td { + background-color: #FFF9E8; } + +/* line 512, ../scss/style.scss */ +.warning { + color: #B94A48; } + +/* line 517, ../scss/style.scss */ +.typecho-list-table tr td .hidden-by-mouse { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; } + +/* line 521, ../scss/style.scss */ +.typecho-list-table tr:hover td .hidden-by-mouse { + filter: progid:DXImageTransform.Microsoft.Alpha(enabled=false); + opacity: 1; } + +/** +* 评论管理 +*/ +/* line 530, ../scss/style.scss */ +.comment-reply-content { + position: relative; + margin: 1em 0; + padding: 0 1em; + border: 1px solid transparent; + background-color: #F0F0EC; } + +/* line 537, ../scss/style.scss */ +.comment-reply-content:after { + position: absolute; + right: 1em; + border: 8px solid #F0F0EC; + border-color: #F0F0EC #F0F0EC transparent transparent; + content: " "; } + +/* line 545, ../scss/style.scss */ +.comment-meta span, +.comment-date { + font-size: .92857em; + color: #999; } + +/* line 551, ../scss/style.scss */ +.comment-action a, .comment-action span { + margin-right: 4px; } + +/* line 553, ../scss/style.scss */ +.comment-edit label { + display: block; } + +/** +* 评论回复 +*/ +/* line 561, ../scss/style.scss */ +#typecho-respond { + padding: 10px; + display: none; } + +/** +* 模板列表 +*/ +/* line 579, ../scss/style.scss */ +.typecho-theme-list img { + margin: 1em 0; + max-width: 100%; + max-height: 240px; } + +/* line 585, ../scss/style.scss */ +.typecho-theme-list cite { + font-style: normal; + color: #999; } + +/* line 590, ../scss/style.scss */ +.typecho-theme-list tbody tr.current td { + background-color: #FFF9E8; } + +/** +* 后台配置项 +*/ +/* line 599, ../scss/style.scss */ +.typecho-page-main .typecho-option input.text { + width: 100%; } + +/* line 603, ../scss/style.scss */ +.typecho-page-main .typecho-option input.num { + width: 40px; } + +/* line 607, ../scss/style.scss */ +.typecho-page-main .typecho-option textarea { + width: 100%; + height: 100px; } + +/* line 612, ../scss/style.scss */ +.typecho-page-main .typecho-option .multiline { + display: block; + margin: .3em 0; } + /* line 615, ../scss/style.scss */ + .typecho-page-main .typecho-option .multiline.hidden { + display: none; } + +/** +* 编辑模板 +*/ +/* line 624, ../scss/style.scss */ +.typecho-select-theme { + height: 25px; + line-height: 25px; + margin: 15px 0px; } + +/* line 630, ../scss/style.scss */ +.typecho-select-theme h5 { + color: #E47E00; + font-weight: bold; + float: left; + font-size: 14px; + width: 120px; + margin-right: 10px; } + +/* line 639, ../scss/style.scss */ +.typecho-select-theme select { + width: 150px; } + +/** +* 编辑模板(编辑详情) +*/ +/* line 647, ../scss/style.scss */ +.typecho-edit-theme ul { + list-style: none; + margin: 0; + padding: 0; } + +/* line 653, ../scss/style.scss */ +.typecho-edit-theme li { + padding: 3px 10px; } + +/* line 657, ../scss/style.scss */ +.typecho-edit-theme .current { + background-color: #E6E6E3; } + +/* line 660, ../scss/style.scss */ +.typecho-edit-theme .current a { + color: #444; } + +/* line 664, ../scss/style.scss */ +.typecho-edit-theme textarea { + font-size: .92857em; + line-height: 1.2; + height: 500px; } + +/** +* 编写页面 +*/ +/* line 674, ../scss/style.scss */ +.typecho-post-area .edit-draft-notice { + color: #999; + font-size: .92857em; } + +/* line 678, ../scss/style.scss */ +.typecho-post-area .edit-draft-notice a { + color: #B94A48; } + +/* line 680, ../scss/style.scss */ +.typecho-post-area .typecho-label { + display: block; + margin: 1em 0 -0.5em; + font-weight: bold; } + +/* line 686, ../scss/style.scss */ +.typecho-post-area #auto-save-message { + display: block; + margin-top: 0.5em; + color: #999; + font-size: .92857em; } + +/* line 693, ../scss/style.scss */ +.typecho-post-area .submit .right button { + margin-left: 5px; } + +/* line 697, ../scss/style.scss */ +.typecho-post-area .right { + float: right; + padding-left: 24px; } + +/* line 702, ../scss/style.scss */ +.typecho-post-area .left { + float: left; } + +/* line 709, ../scss/style.scss */ +.typecho-post-area .out-date { + border: 1px solid #D3DBB3; + padding: 3px; + background: #fff; } + +/* line 715, ../scss/style.scss */ +.typecho-post-area input.title { + font-size: 1.17em; + font-weight: bold; } + +/* line 719, ../scss/style.scss */ +.typecho-post-area .url-slug { + margin-top: -0.5em; + color: #AAA; + font-size: .92857em; + word-break: break-word; } + +/* line 725, ../scss/style.scss */ +.typecho-post-area #slug { + padding: 2px; + border: none; + background: #FFFBCC; + color: #666; } + +/* line 732, ../scss/style.scss */ +.typecho-post-area #text { + resize: none; } + +/* line 736, ../scss/style.scss */ +#advance-panel { + display: none; } + +/* line 740, ../scss/style.scss */ +#custom-field { + margin: 1em 0; + padding: 10px 15px; + background: #FFF; } + /* line 745, ../scss/style.scss */ + #custom-field.fold table, #custom-field.fold .description { + display: none; } + /* line 748, ../scss/style.scss */ + #custom-field .description { + margin-top: 10px; + text-align: right; } + /* line 751, ../scss/style.scss */ + #custom-field .description button { + float: left; } + /* line 756, ../scss/style.scss */ + #custom-field p.description { + text-align: left; } + /* line 760, ../scss/style.scss */ + #custom-field .typecho-label { + margin: 0; } + /* line 762, ../scss/style.scss */ + #custom-field .typecho-label a { + display: block; + color: #444; } + /* line 765, ../scss/style.scss */ + #custom-field .typecho-label a:hover { + color: #467B96; + text-decoration: none; } + /* line 771, ../scss/style.scss */ + #custom-field table { + margin-top: 10px; } + /* line 774, ../scss/style.scss */ + #custom-field td { + padding: 10px 5px; + font-size: .92857em; + border-bottom: 1px solid #F0F0EC; + vertical-align: top; } + /* line 779, ../scss/style.scss */ + #custom-field td label { + font-size: 1em; + font-weight: normal; } + /* line 784, ../scss/style.scss */ + #custom-field select { + height: 27px; } + +/* line 787, ../scss/style.scss */ +.typecho-post-area .is-draft { + background: #FFF1A8; } + +/* line 791, ../scss/style.scss */ +.typecho-post-option .description { + margin-top: -0.5em; + color: #999; + font-size: .92857em; } + +/* line 797, ../scss/style.scss */ +.category-option ul { + list-style: none; + border: 1px solid #D9D9D6; + padding: 6px 12px; + max-height: 240px; + overflow: auto; + background-color: #FFF; + border-radius: 2px; } + +/* line 806, ../scss/style.scss */ +.category-option li { + margin: 3px 0; } + +/* line 810, ../scss/style.scss */ +.visibility-option ul, +.allow-option ul { + list-style: none; + padding: 0; } + +/** +* 标签列表 +*/ +/* line 821, ../scss/style.scss */ +.typecho-page-main ul.tag-list { + list-style: none; + margin: 0; + padding: 20px; + background-color: #FFF; } + +/* line 828, ../scss/style.scss */ +.typecho-page-main ul.tag-list li { + display: inline-block; + margin: 0 0 5px 0; + padding: 5px 5px 5px 10px; + cursor: pointer; } + +/* line 834, ../scss/style.scss */ +.typecho-page-main ul.tag-list li:hover { + background-color: #E9E9E6; } + +/* line 838, ../scss/style.scss */ +.typecho-page-main ul.tag-list li input { + display: none; } + +/* line 843, ../scss/style.scss */ +.typecho-page-main ul.tag-list li.checked { + background-color: #FFFBCC; } + +/* line 847, ../scss/style.scss */ +.typecho-page-main ul.tag-list li.size-5 { + font-size: 1em; } + +/* line 848, ../scss/style.scss */ +.typecho-page-main ul.tag-list li.size-10 { + font-size: 1.2em; } + +/* line 849, ../scss/style.scss */ +.typecho-page-main ul.tag-list li.size-20 { + font-size: 1.4em; } + +/* line 850, ../scss/style.scss */ +.typecho-page-main ul.tag-list li.size-30 { + font-size: 1.6em; } + +/* line 851, ../scss/style.scss */ +.typecho-page-main ul.tag-list li.size-0 { + font-size: 1.8em; } + +/* line 853, ../scss/style.scss */ +.typecho-page-main .tag-edit-link { + visibility: hidden; } + +/* line 854, ../scss/style.scss */ +.typecho-page-main li:hover .tag-edit-link { + visibility: visible; } + +/* line 856, ../scss/style.scss */ +.typecho-attachment-photo { + border: 1px solid #E6E6E3; + max-width: 100%; } + +/* +* Upload +*/ +/* line 865, ../scss/style.scss */ +#upload-panel { + border: 1px dashed #D9D9D6; + background-color: #FFF; + color: #999; + font-size: .92857em; } + /* line 870, ../scss/style.scss */ + #upload-panel.drag { + background-color: #FFFBCC; } + +/* line 875, ../scss/style.scss */ +.upload-area { + padding: 15px; + text-align: center; } + +/* line 880, ../scss/style.scss */ +#file-list { + list-style: none; + margin: 0 10px; + padding: 0; + max-height: 450px; + overflow: auto; + word-break: break-all; } + /* line 887, ../scss/style.scss */ + #file-list li, + #file-list .insert { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } + /* line 893, ../scss/style.scss */ + #file-list li { + padding: 8px 0; + border-top: 1px dashed #D9D9D6; } + /* line 897, ../scss/style.scss */ + #file-list .insert { + display: block; + max-width: 100%; } + /* line 901, ../scss/style.scss */ + #file-list .file { + margin-left: 5px; } + /* line 904, ../scss/style.scss */ + #file-list .info { + text-transform: uppercase; } + +/* line 909, ../scss/style.scss */ +#btn-fullscreen-upload { + visibility: hidden; } + +/** +* 附件管理 +*/ +/* line 917, ../scss/style.scss */ +.edit-media button { + margin-right: 6px; } + +/* 拖动调整 textarea 大小 */ +/* line 920, ../scss/style.scss */ +.resize { + display: block; + margin: 2px auto 0; + padding: 2px 0; + border: 1px solid #D9D9D6; + border-width: 1px 0; + width: 60px; + cursor: row-resize; } + /* line 928, ../scss/style.scss */ + .resize i { + display: block; + height: 1px; + background-color: #D9D9D6; } + +/* 拖动排序 */ +/* line 936, ../scss/style.scss */ +.tDnD_whileDrag { + background-color: #FFFBCC; } + +/** +* 导入扩展样式 +*/ +/** + * icons + */ +/* line 29, ../scss/_icons.scss */ +.i-edit, .i-delete, .i-exlink, .mime-office, .mime-text, .mime-image, .mime-html, .mime-archive, .mime-application, .mime-audio, .mime-script, .mime-video, .mime-unknow, .i-upload, .i-upload-active { + display: inline-block; + vertical-align: text-bottom; + text-indent: -9999em; + background-image: url('../img/icons-s0c4f1c5ae6.png'); + background-repeat: no-repeat; } + /* line 35, ../scss/_icons.scss */ + .i-edit:hover, .i-delete:hover, .i-exlink:hover, .mime-office:hover, .mime-text:hover, .mime-image:hover, .mime-html:hover, .mime-archive:hover, .mime-application:hover, .mime-audio:hover, .mime-script:hover, .mime-video:hover, .mime-unknow:hover, .i-upload:hover, .i-upload-active:hover { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=75); + opacity: 0.75; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 29, ../scss/_icons.scss */ + .i-edit, .i-delete, .i-exlink, .mime-office, .mime-text, .mime-image, .mime-html, .mime-archive, .mime-application, .mime-audio, .mime-script, .mime-video, .mime-unknow, .i-upload, .i-upload-active { + -moz-background-size: auto 256px; + -o-background-size: auto 256px; + -webkit-background-size: auto 256px; + background-size: auto 256px; + background-image: url('../img/icons-2x-s481937020b.png'); } } + +/* line 47, ../scss/_icons.scss */ +.i-edit, .i-delete, .i-exlink, .mime-office, .mime-text, .mime-image, .mime-html, .mime-archive, .mime-application, .mime-audio, .mime-script, .mime-video, .mime-unknow { + width: 16px; + height: 16px; } + +/* line 53, ../scss/_icons.scss */ +.i-upload, .i-upload-active { + width: 24px; + height: 24px; } + +/* line 59, ../scss/_icons.scss */ +.i-edit { + background-position: 0 -16px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 59, ../scss/_icons.scss */ + .i-edit { + background-position: 0 -16px; } } + +/* line 63, ../scss/_icons.scss */ +.i-delete { + background-position: 0 0; } + +/* line 71, ../scss/_icons.scss */ +.i-upload { + background-position: 0 -72px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 71, ../scss/_icons.scss */ + .i-upload { + background-position: 0 -72px; } } + +/* line 76, ../scss/_icons.scss */ +.i-upload-active { + background-position: 0 -48px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 76, ../scss/_icons.scss */ + .i-upload-active { + background-position: 0 -48px; } } + +/* line 82, ../scss/_icons.scss */ +.i-caret-up, .i-caret-down, .i-caret-left, .i-caret-right { + display: inline-block; + border-style: solid; + border-color: transparent transparent #BBB transparent; + border-width: 3px 4px 5px; } + +/* line 88, ../scss/_icons.scss */ +.i-caret-down { + border-color: #BBB transparent transparent transparent; + border-width: 5px 4px 3px; } + +/* line 92, ../scss/_icons.scss */ +.i-caret-left { + border-color: transparent #BBB transparent transparent; + border-width: 4px 5px 4px 3px; } + +/* line 96, ../scss/_icons.scss */ +.i-caret-right { + border-color: transparent transparent transparent #BBB; + border-width: 4px 3px 4px 5px; } + +/* line 101, ../scss/_icons.scss */ +.i-exlink { + background-position: 0 -32px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 101, ../scss/_icons.scss */ + .i-exlink { + background-position: 0 -32px; } } + +/* 文件类型图标 */ +/* line 109, ../scss/_icons.scss */ +.mime-office { + background-position: 0 -176px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 109, ../scss/_icons.scss */ + .mime-office { + background-position: 0 -176px; } } + +/* line 114, ../scss/_icons.scss */ +.mime-text { + background-position: 0 -208px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 114, ../scss/_icons.scss */ + .mime-text { + background-position: 0 -208px; } } + +/* line 119, ../scss/_icons.scss */ +.mime-image { + background-position: 0 -160px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 119, ../scss/_icons.scss */ + .mime-image { + background-position: 0 -160px; } } + +/* line 124, ../scss/_icons.scss */ +.mime-html { + background-position: 0 -144px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 124, ../scss/_icons.scss */ + .mime-html { + background-position: 0 -144px; } } + +/* line 129, ../scss/_icons.scss */ +.mime-archive { + background-position: 0 -112px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 129, ../scss/_icons.scss */ + .mime-archive { + background-position: 0 -112px; } } + +/* line 134, ../scss/_icons.scss */ +.mime-application { + background-position: 0 -96px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 134, ../scss/_icons.scss */ + .mime-application { + background-position: 0 -96px; } } + +/* line 139, ../scss/_icons.scss */ +.mime-audio { + background-position: 0 -128px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 139, ../scss/_icons.scss */ + .mime-audio { + background-position: 0 -128px; } } + +/* line 144, ../scss/_icons.scss */ +.mime-script { + background-position: 0 -192px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 144, ../scss/_icons.scss */ + .mime-script { + background-position: 0 -192px; } } + +/* line 149, ../scss/_icons.scss */ +.mime-video { + background-position: 0 -240px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 149, ../scss/_icons.scss */ + .mime-video { + background-position: 0 -240px; } } + +/* line 154, ../scss/_icons.scss */ +.mime-unknow { + background-position: 0 -224px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 154, ../scss/_icons.scss */ + .mime-unknow { + background-position: 0 -224px; } } + +/* Logo 图标 */ +/* line 161, ../scss/_icons.scss */ +.i-logo, .i-logo-s { + width: 169px; + height: 40px; + display: inline-block; + background: url("../img/typecho-logo.svg") no-repeat; + text-indent: -9999em; + -moz-background-size: auto 40px; + -o-background-size: auto 40px; + -webkit-background-size: auto 40px; + background-size: auto 40px; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=15); + opacity: 0.15; } + /* line 169, ../scss/_icons.scss */ + .i-logo:hover, .i-logo-s:hover { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20); + opacity: 0.2; } + +/* line 173, ../scss/_icons.scss */ +.i-logo-s { + width: 26px; + height: 26px; + -moz-background-size: auto 26px; + -o-background-size: auto 26px; + -webkit-background-size: auto 26px; + background-size: auto 26px; } + +/* +* Editor +*/ +/* line 4, ../scss/components/_editor.scss */ +.editor { + margin-bottom: -0.5em; } + +/* line 8, ../scss/components/_editor.scss */ +.wmd-button-row { + list-style: none; + margin: 0; + padding: 0; + height: 26px; + line-height: 1; } + /* line 15, ../scss/components/_editor.scss */ + .wmd-button-row li { + display: inline-block; + margin-right: 4px; + padding: 3px; + cursor: pointer; + vertical-align: middle; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; } + /* line 22, ../scss/components/_editor.scss */ + .wmd-button-row li:hover { + background-color: #E9E9E6; } + /* line 25, ../scss/components/_editor.scss */ + .wmd-button-row li.wmd-spacer { + height: 20px; + margin: 0 10px 0 6px; + padding: 0; + width: 1px; + background: #E9E9E6; + cursor: default; } + +/* line 36, ../scss/components/_editor.scss */ +#wmd-button-row span { + display: block; + width: 20px; + height: 20px; + background: transparent url(../img/editor.png) no-repeat; } + +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 46, ../scss/components/_editor.scss */ + #wmd-button-row span { + background-image: url(../img/editor@2x.png); + -moz-background-size: 320px auto; + -o-background-size: 320px auto; + -webkit-background-size: 320px auto; + background-size: 320px auto; } } +/* line 53, ../scss/components/_editor.scss */ +.wmd-edittab { + float: right; + margin-top: 3px; + font-size: .92857em; } + /* line 57, ../scss/components/_editor.scss */ + .wmd-edittab a { + display: inline-block; + padding: 0 8px; + margin-left: 5px; + height: 20px; + line-height: 20px; } + /* line 63, ../scss/components/_editor.scss */ + .wmd-edittab a:hover { + text-decoration: none; } + /* line 66, ../scss/components/_editor.scss */ + .wmd-edittab a.active { + background: #E9E9E6; + color: #999; } + +/* line 74, ../scss/components/_editor.scss */ +.wmd-hidetab { + display: none; } + +/* line 78, ../scss/components/_editor.scss */ +.wmd-visualhide { + visibility: hidden; } + +/* 对话框 */ +/* line 83, ../scss/components/_editor.scss */ +.wmd-prompt-background { + background-color: #000; } + +/* line 86, ../scss/components/_editor.scss */ +.wmd-prompt-dialog { + position: fixed; + z-index: 1001; + top: 50%; + left: 50%; + margin-top: -95px; + margin-left: -200px; + padding: 20px; + width: 360px; + background: #F6F6F3; } + /* line 97, ../scss/components/_editor.scss */ + .wmd-prompt-dialog p { + margin: 0 0 5px; } + /* line 98, ../scss/components/_editor.scss */ + .wmd-prompt-dialog form { + margin-top: 10px; } + /* line 99, ../scss/components/_editor.scss */ + .wmd-prompt-dialog input[type="text"] { + margin-bottom: 10px; + width: 100%; } + /* line 103, ../scss/components/_editor.scss */ + .wmd-prompt-dialog button { + margin-right: 10px; } + +/* 预览 */ +/* line 107, ../scss/components/_editor.scss */ +#wmd-preview { + background: #FFF; + margin: 1em 0; + padding: 0 15px; + word-wrap: break-word; + overflow: auto; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; } + /* line 114, ../scss/components/_editor.scss */ + #wmd-preview img { + max-width: 100%; } + /* line 115, ../scss/components/_editor.scss */ + #wmd-preview code, #wmd-preview pre { + padding: 2px 4px; + background: #F3F3F0; + font-size: .92857em; } + /* line 120, ../scss/components/_editor.scss */ + #wmd-preview code { + color: #C13; } + /* line 121, ../scss/components/_editor.scss */ + #wmd-preview pre { + padding: 1em; } + /* line 123, ../scss/components/_editor.scss */ + #wmd-preview pre code { + padding: 0; + color: #444; } + /* line 128, ../scss/components/_editor.scss */ + #wmd-preview blockquote { + margin: 1em 1.5em; + padding-left: 1.5em; + border-left: 4px solid #E9E9E6; + color: #777; } + /* line 134, ../scss/components/_editor.scss */ + #wmd-preview hr { + margin: 2em auto; + width: 100px; + border: 1px solid #E9E9E6; + border-width: 2px 0 0 0; } + /* line 140, ../scss/components/_editor.scss */ + #wmd-preview .summary:after { + display: block; + margin: 2em 0; + background: #FFF9E8; + color: #ce9900; + font-size: .85714em; + text-align: center; + content: "- more -"; } + +/* 上传面板动画效果 */ +@keyframes fullscreen-upload { + 0% { + right: -280px; } + 100% { + right: -1px; } } +@-moz-keyframes fullscreen-upload { + 0% { + right: -280px; } + 100% { + right: -1px; } } +@-webkit-keyframes fullscreen-upload { + 0% { + right: -280px; } + 100% { + right: -1px; } } +@-o-keyframes fullscreen-upload { + 0% { + right: -280px; } + 100% { + right: -1px; } } +/* 编辑器全屏 */ +/* line 174, ../scss/components/_editor.scss */ +.fullscreen #wmd-button-bar, .fullscreen #text, .fullscreen #wmd-preview, .fullscreen .submit { + position: absolute; + top: 0; + width: 50%; + background: #FFF; + z-index: 999; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; } +/* line 184, ../scss/components/_editor.scss */ +.fullscreen #wmd-button-bar { + left: 0; + padding: 13px 20px; + border-bottom: 1px solid #F3F3F0; + z-index: 1000; } +/* line 190, ../scss/components/_editor.scss */ +.fullscreen #text { + top: 53px; + left: 0; + padding: 20px; + border: none; + outline: none; } +/* line 197, ../scss/components/_editor.scss */ +.fullscreen #wmd-preview { + top: 53px; + right: 0; + margin: 0; + padding: 5px 20px; + border: none; + border-left: 1px solid #F3F3F0; + background: #F6F6F3; + overflow: auto; } + /* line 206, ../scss/components/_editor.scss */ + .fullscreen #wmd-preview code, .fullscreen #wmd-preview pre { + background: #F0F0EC; } +/* line 210, ../scss/components/_editor.scss */ +.fullscreen .submit { + right: 0; + margin: 0; + padding: 10px 20px; + border-bottom: 1px solid #F3F3F0; } +/* line 216, ../scss/components/_editor.scss */ +.fullscreen #upload-panel { + -webkit-box-shadow: 0 4px 16px rgba(0, 0, 0, 0.225); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.225); + border-style: solid; } +/* line 223, ../scss/components/_editor.scss */ +.fullscreen #tab-files { + position: absolute; + top: 52px; + right: -1px; + width: 280px; + z-index: 1001; + animation: fullscreen-upload 0.5s; + -moz-animation: fullscreen-upload 0.5s; + -webkit-animation: fullscreen-upload 0.5s; + -o-animation: fullscreen-upload 0.5s; } +/* line 236, ../scss/components/_editor.scss */ +.fullscreen .wmd-edittab, +.fullscreen .typecho-post-option, +.fullscreen .title, +.fullscreen .url-slug, +.fullscreen .typecho-page-title, +.fullscreen .typecho-head-nav, +.fullscreen .message { + display: none; } +/* line 243, ../scss/components/_editor.scss */ +.fullscreen .wmd-hidetab { + display: block; } +/* line 244, ../scss/components/_editor.scss */ +.fullscreen .wmd-visualhide, +.fullscreen #btn-fullscreen-upload { + visibility: visible; } + +/** +* Jquery Timepicker +*/ +/* line 5, ../scss/components/_timepicker.scss */ +#ui-datepicker-div { + display: none; + margin-top: -1px; + padding: 10px; + border: 1px solid #D9D9D6; + background: #FFF; } + +/* line 12, ../scss/components/_timepicker.scss */ +.ui-timepicker-div .ui-widget-header { + margin-bottom: 8px; } + +/* line 13, ../scss/components/_timepicker.scss */ +.ui-timepicker-div dl { + text-align: left; } + +/* line 14, ../scss/components/_timepicker.scss */ +.ui-timepicker-div dl dt { + float: left; + clear: left; } + +/* line 15, ../scss/components/_timepicker.scss */ +.ui-timepicker-div dl dd { + margin: 0 0 10px 40%; } + +/* line 16, ../scss/components/_timepicker.scss */ +.ui-tpicker-grid-label { + background: none; + border: none; + margin: 0; + padding: 0; } + +/* line 18, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-header { + margin-bottom: 10px; + padding-bottom: 10px; + border-bottom: 1px solid #EEE; } + +/* line 23, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-prev { + float: left; + cursor: pointer; } + +/* line 24, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-next { + float: right; + cursor: pointer; } + +/* line 25, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-title { + font-weight: bold; + text-align: center; } + +/* line 29, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-calendar th { + line-height: 24px; } + +/* line 30, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-calendar a { + display: block; + width: 30px; + background-color: #F3F3F0; + line-height: 24px; + text-align: center; } + +/* line 37, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-calendar a:hover { + background-color: #E9E9E6; + text-decoration: none; } + +/* line 41, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-today a { + background-color: #E9E9E6; + color: #444; } + +/* line 45, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-current-day a { + background-color: #467B96 !important; + color: #FFF; } + +/* line 49, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-timepicker-div { + margin-top: 20px; + border-top: 1px solid #EEE; } + +/* line 53, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-slider { + position: relative; + margin-top: 18px; + border: 1px solid #E9E9E6; + background-color: #F6F6F3; + height: 4px; } + +/* line 60, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-slider .ui-slider-handle { + position: absolute; + top: -7px; + margin-left: -5px; + z-index: 2; + width: 10px; + height: 16px; + background-color: #467B96; } + +/* line 70, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-buttonpane { + padding-top: 10px; + border-top: 1px solid #EEE; } + +/* line 74, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-current, +#ui-datepicker-div .ui-datepicker-close { + float: left; } + +/* line 80, ../scss/components/_timepicker.scss */ +#ui-datepicker-div .ui-datepicker-close { + float: right; } + +/* line 84, ../scss/components/_timepicker.scss */ +.ui-effects-transfer { + border: 2px dotted #ccc; } + +/** +* Jquery Tokeninput +*/ +/* line 5, ../scss/components/_tokeninput.scss */ +ul.token-input-list { + list-style: none; + margin: 0; + padding: 0 4px; + min-height: 32px; + border: 1px solid #D9D9D6; + cursor: text; + z-index: 999; + background-color: #FFF; + clear: left; + border-radius: 2px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } + /* line 22, ../scss/components/_tokeninput.scss */ + ul.token-input-list li { + margin: 4px 0; } + +/* line 28, ../scss/components/_tokeninput.scss */ +ul.token-input-list li input { + padding: 0; + border: 0; + width: 100%; + -webkit-appearance: caret; } + +/* line 35, ../scss/components/_tokeninput.scss */ +li.token-input-token { + padding: 0 6px; + height: 27px; + line-height: 27px; + background-color: #F3F3F0; + cursor: default; + font-size: .92857em; + text-align: right; + white-space: nowrap; } + /* line 44, ../scss/components/_tokeninput.scss */ + li.token-input-token p { + float: left; + display: inline; + margin: 0; } + /* line 49, ../scss/components/_tokeninput.scss */ + li.token-input-token span { + color: #BBB; + font-weight: bold; + cursor: pointer; } + +/* line 58, ../scss/components/_tokeninput.scss */ +li.token-input-selected-token { + background-color: #E9E9E6; } + +/* line 62, ../scss/components/_tokeninput.scss */ +li.token-input-input-token { + padding: 0 4px; } + +/* line 66, ../scss/components/_tokeninput.scss */ +div.token-input-dropdown { + position: absolute; + background-color: #FFF; + overflow: hidden; + border: 1px solid #D9D9D6; + border-top-width: 0; + cursor: default; + z-index: 1; + font-size: .92857em; } + +/* line 77, ../scss/components/_tokeninput.scss */ +div.token-input-dropdown p { + margin: 0; + padding: 5px 10px; + color: #777; + font-weight: bold; } + +/* line 84, ../scss/components/_tokeninput.scss */ +div.token-input-dropdown ul { + list-style: none; + margin: 0; + padding: 0; } + +/* line 90, ../scss/components/_tokeninput.scss */ +div.token-input-dropdown ul li { + padding: 4px 10px; + background-color: #FFF; } + +/* line 95, ../scss/components/_tokeninput.scss */ +div.token-input-dropdown ul li.token-input-dropdown-item { + background-color: #FFF; } + +/* line 99, ../scss/components/_tokeninput.scss */ +div.token-input-dropdown ul li em { + font-style: normal; } + +/* line 103, ../scss/components/_tokeninput.scss */ +div.token-input-dropdown ul li.token-input-selected-dropdown-item { + background-color: #467B96; + color: #FFF; } + +/* +* Hide from both screenreaders and browsers: h5bp.com/u +*/ +/* line 5, ../scss/_hidden.scss */ +.hidden { + display: none; } + +/* +* Hide only visually, but have it available for screenreaders: h5bp.com/v +*/ +/* line 15, ../scss/_hidden.scss */ +.sr-only { + border: 0; + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; } + +/* +* Extends the .sr-only class to allow the element to be focusable +* when navigated to via the keyboard: h5bp.com/p +*/ +/* line 30, ../scss/_hidden.scss */ +.sr-only.focusable:active, +.sr-only.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; } + +/* +* Hide visually and from screenreaders, but maintain layout +*/ +/* line 44, ../scss/_hidden.scss */ +.invisible { + visibility: hidden; } diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/custom-fields-js.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/custom-fields-js.php new file mode 100644 index 0000000..b1011f7 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/custom-fields-js.php @@ -0,0 +1,44 @@ + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/custom-fields.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/custom-fields.php new file mode 100644 index 0000000..6fd85b2 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/custom-fields.php @@ -0,0 +1,73 @@ + +getFieldItems() : $page->getFieldItems(); +$defaultFields = isset($post) ? $post->getDefaultFieldItems() : $page->getDefaultFieldItems(); +?> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
render(); ?>render(); ?>
+ + + + + + + + + + +
+ + + + + + + + + + +
+
+ + 帮助文档'); ?> +
+
diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/editor-js.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/editor-js.php new file mode 100644 index 0000000..949305c --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/editor-js.php @@ -0,0 +1,330 @@ + +markdown): ?> + + + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/extending.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/extending.php new file mode 100644 index 0000000..be228ef --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/extending.php @@ -0,0 +1,13 @@ +get('panel'); +$panelTable = unserialize($options->panelTable); + +if (!isset($panelTable['file']) || !in_array(urlencode($panel), $panelTable['file'])) { + throw new Typecho_Plugin_Exception(_t('页面不存在'), 404); +} + +list ($pluginName, $file) = explode('/', trim($panel, '/'), 2); + +require_once $options->pluginDir($pluginName) . '/' . $panel; diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/file-upload-js.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/file-upload-js.php new file mode 100644 index 0000000..133dde3 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/file-upload-js.php @@ -0,0 +1,208 @@ + +have()) { + $fileParentContent = $post; +} else if (isset($page) && $page instanceof Typecho_Widget && $page->have()) { + $fileParentContent = $page; +} + +$phpMaxFilesize = function_exists('ini_get') ? trim(ini_get('upload_max_filesize')) : 0; + +if (preg_match("/^([0-9]+)([a-z]{1,2})$/i", $phpMaxFilesize, $matches)) { + $phpMaxFilesize = strtolower($matches[1] . $matches[2] . (1 == strlen($matches[2]) ? 'b' : '')); +} +?> + + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/file-upload.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/file-upload.php new file mode 100644 index 0000000..d276094 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/file-upload.php @@ -0,0 +1,30 @@ + + +cid : $page->cid; + + if ($cid) { + Typecho_Widget::widget('Widget_Contents_Attachment_Related', 'parentId=' . $cid)->to($attachment); + } else { + Typecho_Widget::widget('Widget_Contents_Attachment_Unattached')->to($attachment); + } +} +?> + +
+
或者 %s选择文件上传%s', '', ''); ?>
+ +
+ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/footer.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/footer.php new file mode 100644 index 0000000..57c0084 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/footer.php @@ -0,0 +1,6 @@ + + + +end(); diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/form-js.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/form-js.php new file mode 100644 index 0000000..7c52d69 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/form-js.php @@ -0,0 +1,26 @@ + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/header.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/header.php new file mode 100644 index 0000000..50e3c6e --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/header.php @@ -0,0 +1,31 @@ +adminStaticUrl('css')) . '"> + + +'; + +/** 注册一个初始化插件 */ +$header = Typecho_Plugin::factory('admin/header.php')->header($header); + +?> + + + + + + + <?php _e('%s - %s - Powered by Typecho', $menu->title, $options->title); ?> + + + + > + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/ajax-loader.gif b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/ajax-loader.gif new file mode 100644 index 0000000..32666da Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/ajax-loader.gif differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor.png new file mode 100644 index 0000000..aeee188 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-bold@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-bold@2x.png new file mode 100644 index 0000000..bcbab97 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-bold@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-code@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-code@2x.png new file mode 100644 index 0000000..21d2fa0 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-code@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-exitfull@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-exitfull@2x.png new file mode 100644 index 0000000..2d26190 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-exitfull@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-fullscreen@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-fullscreen@2x.png new file mode 100644 index 0000000..420ea7c Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-fullscreen@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-heading@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-heading@2x.png new file mode 100644 index 0000000..5096e41 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-heading@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-hr@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-hr@2x.png new file mode 100644 index 0000000..8371458 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-hr@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-image@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-image@2x.png new file mode 100644 index 0000000..e8857a8 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-image@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-italic@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-italic@2x.png new file mode 100644 index 0000000..666dfb6 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-italic@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-link@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-link@2x.png new file mode 100644 index 0000000..68e4f87 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-link@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-more@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-more@2x.png new file mode 100644 index 0000000..87f58e6 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-more@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-olist@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-olist@2x.png new file mode 100644 index 0000000..17eca6e Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-olist@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-quote@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-quote@2x.png new file mode 100644 index 0000000..6c703ad Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-quote@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-redo@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-redo@2x.png new file mode 100644 index 0000000..baad4d6 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-redo@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-ulist@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-ulist@2x.png new file mode 100644 index 0000000..356ab46 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-ulist@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-undo@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-undo@2x.png new file mode 100644 index 0000000..36d001f Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/2x/editor-undo@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-bold.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-bold.png new file mode 100644 index 0000000..6111560 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-bold.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-code.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-code.png new file mode 100644 index 0000000..3a2c176 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-code.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-exitfull.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-exitfull.png new file mode 100644 index 0000000..4827612 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-exitfull.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-fullscreen.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-fullscreen.png new file mode 100644 index 0000000..4593cf3 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-fullscreen.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-heading.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-heading.png new file mode 100644 index 0000000..228a76d Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-heading.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-hr.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-hr.png new file mode 100644 index 0000000..d248f66 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-hr.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-image.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-image.png new file mode 100644 index 0000000..c091557 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-image.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-italic.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-italic.png new file mode 100644 index 0000000..275a487 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-italic.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-link.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-link.png new file mode 100644 index 0000000..e1de15d Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-link.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-more.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-more.png new file mode 100644 index 0000000..4b24a6b Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-more.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-olist.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-olist.png new file mode 100644 index 0000000..590df03 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-olist.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-quote.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-quote.png new file mode 100644 index 0000000..5d3060a Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-quote.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-redo.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-redo.png new file mode 100644 index 0000000..b59377e Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-redo.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-ulist.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-ulist.png new file mode 100644 index 0000000..8aa1c9d Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-ulist.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-undo.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-undo.png new file mode 100644 index 0000000..98cbcc8 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor/editor-undo.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor@2x.png new file mode 100644 index 0000000..b361ff6 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/editor@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x-s481937020b.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x-s481937020b.png new file mode 100644 index 0000000..f887f62 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x-s481937020b.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-delete.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-delete.png new file mode 100644 index 0000000..09b954d Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-delete.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-edit.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-edit.png new file mode 100644 index 0000000..f7798ca Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-edit.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-exlink.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-exlink.png new file mode 100644 index 0000000..be6bcca Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-exlink.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-upload-active.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-upload-active.png new file mode 100644 index 0000000..b7ffae8 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-upload-active.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-upload.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-upload.png new file mode 100644 index 0000000..b5b4d76 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/icon-upload.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-application.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-application.png new file mode 100644 index 0000000..1f1bfe0 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-application.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-archive.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-archive.png new file mode 100644 index 0000000..bc723bc Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-archive.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-audio.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-audio.png new file mode 100644 index 0000000..67c95f1 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-audio.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-html.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-html.png new file mode 100644 index 0000000..e636cf3 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-html.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-image.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-image.png new file mode 100644 index 0000000..4e515e1 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-image.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-office.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-office.png new file mode 100644 index 0000000..32f524f Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-office.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-script.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-script.png new file mode 100644 index 0000000..ab22750 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-script.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-text.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-text.png new file mode 100644 index 0000000..3640ae5 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-text.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-unknow.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-unknow.png new file mode 100644 index 0000000..88be1a4 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-unknow.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-video.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-video.png new file mode 100644 index 0000000..ce948d9 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-2x/mime-video.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-s0c4f1c5ae6.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-s0c4f1c5ae6.png new file mode 100644 index 0000000..b82a6da Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons-s0c4f1c5ae6.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-delete.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-delete.png new file mode 100644 index 0000000..d159e4c Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-delete.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-edit.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-edit.png new file mode 100644 index 0000000..cf8a4f7 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-edit.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-exlink.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-exlink.png new file mode 100644 index 0000000..d3283c7 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-exlink.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-upload-active.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-upload-active.png new file mode 100644 index 0000000..036d1c9 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-upload-active.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-upload.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-upload.png new file mode 100644 index 0000000..c4f677f Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/icon-upload.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-application.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-application.png new file mode 100644 index 0000000..531b7bc Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-application.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-archive.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-archive.png new file mode 100644 index 0000000..2d300b3 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-archive.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-audio.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-audio.png new file mode 100644 index 0000000..8b970a1 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-audio.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-html.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-html.png new file mode 100644 index 0000000..67436e6 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-html.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-image.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-image.png new file mode 100644 index 0000000..4020983 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-image.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-office.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-office.png new file mode 100644 index 0000000..97f2380 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-office.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-script.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-script.png new file mode 100644 index 0000000..c6c06fd Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-script.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-text.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-text.png new file mode 100644 index 0000000..8d9b2e5 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-text.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-unknow.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-unknow.png new file mode 100644 index 0000000..ec3126d Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-unknow.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-video.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-video.png new file mode 100644 index 0000000..0f19420 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/icons/mime-video.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/noscreen.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/noscreen.png new file mode 100644 index 0000000..67b7a6a Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/noscreen.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/typecho-logo.svg b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/typecho-logo.svg new file mode 100644 index 0000000..b447bb5 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/img/typecho-logo.svg @@ -0,0 +1,10 @@ + + + typecho-logo + Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + \ No newline at end of file diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/index.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/index.php new file mode 100644 index 0000000..1ee87fc --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/index.php @@ -0,0 +1,159 @@ + +
+
+ +
+
+

%s 篇文章, 并有 %s 条关于你的评论在 %s 个分类中.', + $stat->myPublishedPostsNum, $stat->myPublishedCommentsNum, $stat->categoriesNum); ?> +

+ + + + +
+

+ → + +

+
+ +
+ + + + + + +
+
+
+ + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/Moxie.swf b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/Moxie.swf new file mode 100644 index 0000000..6493572 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/Moxie.swf differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/diff.js b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/diff.js new file mode 100644 index 0000000..112130e --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/diff.js @@ -0,0 +1,2193 @@ +/** + * Diff Match and Patch + * + * Copyright 2006 Google Inc. + * http://code.google.com/p/google-diff-match-patch/ + * + * Licensed 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. + */ + +/** + * @fileoverview Computes the difference between two texts to create a patch. + * Applies the patch onto another text, allowing for errors. + * @author fraser@google.com (Neil Fraser) + */ + +/** + * Class containing the diff, match and patch methods. + * @constructor + */ +function diff_match_patch() { + + // Defaults. + // Redefine these in your program to override the defaults. + + // Number of seconds to map a diff before giving up (0 for infinity). + this.Diff_Timeout = 1.0; + // Cost of an empty edit operation in terms of edit characters. + this.Diff_EditCost = 4; + // At what point is no match declared (0.0 = perfection, 1.0 = very loose). + this.Match_Threshold = 0.5; + // How far to search for a match (0 = exact location, 1000+ = broad match). + // A match this many characters away from the expected location will add + // 1.0 to the score (0.0 is a perfect match). + this.Match_Distance = 1000; + // When deleting a large block of text (over ~64 characters), how close do + // the contents have to be to match the expected contents. (0.0 = perfection, + // 1.0 = very loose). Note that Match_Threshold controls how closely the + // end points of a delete need to match. + this.Patch_DeleteThreshold = 0.5; + // Chunk size for context length. + this.Patch_Margin = 4; + + // The number of bits in an int. + this.Match_MaxBits = 32; +} + + +// DIFF FUNCTIONS + + +/** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ +var DIFF_DELETE = -1; +var DIFF_INSERT = 1; +var DIFF_EQUAL = 0; + +/** @typedef {{0: number, 1: string}} */ +diff_match_patch.Diff; + + +/** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean=} opt_checklines Optional speedup flag. If present and false, + * then don't run a line-level diff first to identify the changed areas. + * Defaults to true, which does a faster, slightly less optimal diff. + * @param {number} opt_deadline Optional time when the diff should be complete + * by. Used internally for recursive calls. Users should set DiffTimeout + * instead. + * @return {!Array.} Array of diff tuples. + */ +diff_match_patch.prototype.diff_main = function(text1, text2, opt_checklines, + opt_deadline) { + // Set a deadline by which time the diff must be complete. + if (typeof opt_deadline == 'undefined') { + if (this.Diff_Timeout <= 0) { + opt_deadline = Number.MAX_VALUE; + } else { + opt_deadline = (new Date).getTime() + this.Diff_Timeout * 1000; + } + } + var deadline = opt_deadline; + + // Check for null inputs. + if (text1 == null || text2 == null) { + throw new Error('Null input. (diff_main)'); + } + + // Check for equality (speedup). + if (text1 == text2) { + if (text1) { + return [[DIFF_EQUAL, text1]]; + } + return []; + } + + if (typeof opt_checklines == 'undefined') { + opt_checklines = true; + } + var checklines = opt_checklines; + + // Trim off common prefix (speedup). + var commonlength = this.diff_commonPrefix(text1, text2); + var commonprefix = text1.substring(0, commonlength); + text1 = text1.substring(commonlength); + text2 = text2.substring(commonlength); + + // Trim off common suffix (speedup). + commonlength = this.diff_commonSuffix(text1, text2); + var commonsuffix = text1.substring(text1.length - commonlength); + text1 = text1.substring(0, text1.length - commonlength); + text2 = text2.substring(0, text2.length - commonlength); + + // Compute the diff on the middle block. + var diffs = this.diff_compute_(text1, text2, checklines, deadline); + + // Restore the prefix and suffix. + if (commonprefix) { + diffs.unshift([DIFF_EQUAL, commonprefix]); + } + if (commonsuffix) { + diffs.push([DIFF_EQUAL, commonsuffix]); + } + this.diff_cleanupMerge(diffs); + return diffs; +}; + + +/** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean} checklines Speedup flag. If false, then don't run a + * line-level diff first to identify the changed areas. + * If true, then run a faster, slightly less optimal diff. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_compute_ = function(text1, text2, checklines, + deadline) { + var diffs; + + if (!text1) { + // Just add some text (speedup). + return [[DIFF_INSERT, text2]]; + } + + if (!text2) { + // Just delete some text (speedup). + return [[DIFF_DELETE, text1]]; + } + + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + var i = longtext.indexOf(shorttext); + if (i != -1) { + // Shorter text is inside the longer text (speedup). + diffs = [[DIFF_INSERT, longtext.substring(0, i)], + [DIFF_EQUAL, shorttext], + [DIFF_INSERT, longtext.substring(i + shorttext.length)]]; + // Swap insertions for deletions if diff is reversed. + if (text1.length > text2.length) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if (shorttext.length == 1) { + // Single character string. + // After the previous speedup, the character can't be an equality. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + } + + // Check to see if the problem can be split in two. + var hm = this.diff_halfMatch_(text1, text2); + if (hm) { + // A half-match was found, sort out the return data. + var text1_a = hm[0]; + var text1_b = hm[1]; + var text2_a = hm[2]; + var text2_b = hm[3]; + var mid_common = hm[4]; + // Send both pairs off for separate processing. + var diffs_a = this.diff_main(text1_a, text2_a, checklines, deadline); + var diffs_b = this.diff_main(text1_b, text2_b, checklines, deadline); + // Merge the results. + return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b); + } + + if (checklines && text1.length > 100 && text2.length > 100) { + return this.diff_lineMode_(text1, text2, deadline); + } + + return this.diff_bisect_(text1, text2, deadline); +}; + + +/** + * Do a quick line-level diff on both strings, then rediff the parts for + * greater accuracy. + * This speedup can produce non-minimal diffs. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_lineMode_ = function(text1, text2, deadline) { + // Scan the text on a line-by-line basis first. + var a = this.diff_linesToChars_(text1, text2); + text1 = a.chars1; + text2 = a.chars2; + var linearray = a.lineArray; + + var diffs = this.diff_main(text1, text2, false, deadline); + + // Convert the diff back to original text. + this.diff_charsToLines_(diffs, linearray); + // Eliminate freak matches (e.g. blank lines) + this.diff_cleanupSemantic(diffs); + + // Rediff any replacement blocks, this time character-by-character. + // Add a dummy entry at the end. + diffs.push([DIFF_EQUAL, '']); + var pointer = 0; + var count_delete = 0; + var count_insert = 0; + var text_delete = ''; + var text_insert = ''; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + count_insert++; + text_insert += diffs[pointer][1]; + break; + case DIFF_DELETE: + count_delete++; + text_delete += diffs[pointer][1]; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (count_delete >= 1 && count_insert >= 1) { + // Delete the offending records and add the merged ones. + diffs.splice(pointer - count_delete - count_insert, + count_delete + count_insert); + pointer = pointer - count_delete - count_insert; + var a = this.diff_main(text_delete, text_insert, false, deadline); + for (var j = a.length - 1; j >= 0; j--) { + diffs.splice(pointer, 0, a[j]); + } + pointer = pointer + a.length; + } + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + break; + } + pointer++; + } + diffs.pop(); // Remove the dummy entry at the end. + + return diffs; +}; + + +/** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_bisect_ = function(text1, text2, deadline) { + // Cache the text lengths to prevent multiple calls. + var text1_length = text1.length; + var text2_length = text2.length; + var max_d = Math.ceil((text1_length + text2_length) / 2); + var v_offset = max_d; + var v_length = 2 * max_d; + var v1 = new Array(v_length); + var v2 = new Array(v_length); + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (var x = 0; x < v_length; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[v_offset + 1] = 0; + v2[v_offset + 1] = 0; + var delta = text1_length - text2_length; + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + var front = (delta % 2 != 0); + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + var k1start = 0; + var k1end = 0; + var k2start = 0; + var k2end = 0; + for (var d = 0; d < max_d; d++) { + // Bail out if deadline is reached. + if ((new Date()).getTime() > deadline) { + break; + } + + // Walk the front path one step. + for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + var k1_offset = v_offset + k1; + var x1; + if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) { + x1 = v1[k1_offset + 1]; + } else { + x1 = v1[k1_offset - 1] + 1; + } + var y1 = x1 - k1; + while (x1 < text1_length && y1 < text2_length && + text1.charAt(x1) == text2.charAt(y1)) { + x1++; + y1++; + } + v1[k1_offset] = x1; + if (x1 > text1_length) { + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2_length) { + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + var k2_offset = v_offset + delta - k1; + if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) { + // Mirror x2 onto top-left coordinate system. + var x2 = text1_length - v2[k2_offset]; + if (x1 >= x2) { + // Overlap detected. + return this.diff_bisectSplit_(text1, text2, x1, y1, deadline); + } + } + } + } + + // Walk the reverse path one step. + for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + var k2_offset = v_offset + k2; + var x2; + if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) { + x2 = v2[k2_offset + 1]; + } else { + x2 = v2[k2_offset - 1] + 1; + } + var y2 = x2 - k2; + while (x2 < text1_length && y2 < text2_length && + text1.charAt(text1_length - x2 - 1) == + text2.charAt(text2_length - y2 - 1)) { + x2++; + y2++; + } + v2[k2_offset] = x2; + if (x2 > text1_length) { + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2_length) { + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + var k1_offset = v_offset + delta - k2; + if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) { + var x1 = v1[k1_offset]; + var y1 = v_offset + x1 - k1_offset; + // Mirror x2 onto top-left coordinate system. + x2 = text1_length - x2; + if (x1 >= x2) { + // Overlap detected. + return this.diff_bisectSplit_(text1, text2, x1, y1, deadline); + } + } + } + } + } + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; +}; + + +/** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_bisectSplit_ = function(text1, text2, x, y, + deadline) { + var text1a = text1.substring(0, x); + var text2a = text2.substring(0, y); + var text1b = text1.substring(x); + var text2b = text2.substring(y); + + // Compute both diffs serially. + var diffs = this.diff_main(text1a, text2a, false, deadline); + var diffsb = this.diff_main(text1b, text2b, false, deadline); + + return diffs.concat(diffsb); +}; + + +/** + * Split two texts into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {{chars1: string, chars2: string, lineArray: !Array.}} + * An object containing the encoded text1, the encoded text2 and + * the array of unique strings. + * The zeroth element of the array of unique strings is intentionally blank. + * @private + */ +diff_match_patch.prototype.diff_linesToChars_ = function(text1, text2) { + var lineArray = []; // e.g. lineArray[4] == 'Hello\n' + var lineHash = {}; // e.g. lineHash['Hello\n'] == 4 + + // '\x00' is a valid character, but various debuggers don't like it. + // So we'll insert a junk entry to avoid generating a null character. + lineArray[0] = ''; + + /** + * Split a text into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * Modifies linearray and linehash through being a closure. + * @param {string} text String to encode. + * @return {string} Encoded string. + * @private + */ + function diff_linesToCharsMunge_(text) { + var chars = ''; + // Walk the text, pulling out a substring for each line. + // text.split('\n') would would temporarily double our memory footprint. + // Modifying text would create many large strings to garbage collect. + var lineStart = 0; + var lineEnd = -1; + // Keeping our own length variable is faster than looking it up. + var lineArrayLength = lineArray.length; + while (lineEnd < text.length - 1) { + lineEnd = text.indexOf('\n', lineStart); + if (lineEnd == -1) { + lineEnd = text.length - 1; + } + var line = text.substring(lineStart, lineEnd + 1); + lineStart = lineEnd + 1; + + if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : + (lineHash[line] !== undefined)) { + chars += String.fromCharCode(lineHash[line]); + } else { + chars += String.fromCharCode(lineArrayLength); + lineHash[line] = lineArrayLength; + lineArray[lineArrayLength++] = line; + } + } + return chars; + } + + var chars1 = diff_linesToCharsMunge_(text1); + var chars2 = diff_linesToCharsMunge_(text2); + return {chars1: chars1, chars2: chars2, lineArray: lineArray}; +}; + + +/** + * Rehydrate the text in a diff from a string of line hashes to real lines of + * text. + * @param {!Array.} diffs Array of diff tuples. + * @param {!Array.} lineArray Array of unique strings. + * @private + */ +diff_match_patch.prototype.diff_charsToLines_ = function(diffs, lineArray) { + for (var x = 0; x < diffs.length; x++) { + var chars = diffs[x][1]; + var text = []; + for (var y = 0; y < chars.length; y++) { + text[y] = lineArray[chars.charCodeAt(y)]; + } + diffs[x][1] = text.join(''); + } +}; + + +/** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ +diff_match_patch.prototype.diff_commonPrefix = function(text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerstart = 0; + while (pointermin < pointermid) { + if (text1.substring(pointerstart, pointermid) == + text2.substring(pointerstart, pointermid)) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +}; + + +/** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ +diff_match_patch.prototype.diff_commonSuffix = function(text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || + text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerend = 0; + while (pointermin < pointermid) { + if (text1.substring(text1.length - pointermid, text1.length - pointerend) == + text2.substring(text2.length - pointermid, text2.length - pointerend)) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +}; + + +/** + * Determine if the suffix of one string is the prefix of another. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of the first + * string and the start of the second string. + * @private + */ +diff_match_patch.prototype.diff_commonOverlap_ = function(text1, text2) { + // Cache the text lengths to prevent multiple calls. + var text1_length = text1.length; + var text2_length = text2.length; + // Eliminate the null case. + if (text1_length == 0 || text2_length == 0) { + return 0; + } + // Truncate the longer string. + if (text1_length > text2_length) { + text1 = text1.substring(text1_length - text2_length); + } else if (text1_length < text2_length) { + text2 = text2.substring(0, text1_length); + } + var text_length = Math.min(text1_length, text2_length); + // Quick check for the worst case. + if (text1 == text2) { + return text_length; + } + + // Start by looking for a single character match + // and increase length until no match is found. + // Performance analysis: http://neil.fraser.name/news/2010/11/04/ + var best = 0; + var length = 1; + while (true) { + var pattern = text1.substring(text_length - length); + var found = text2.indexOf(pattern); + if (found == -1) { + return best; + } + length += found; + if (found == 0 || text1.substring(text_length - length) == + text2.substring(0, length)) { + best = length; + length++; + } + } +}; + + +/** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + * @private + */ +diff_match_patch.prototype.diff_halfMatch_ = function(text1, text2) { + if (this.Diff_Timeout <= 0) { + // Don't risk returning a non-optimal diff if we have unlimited time. + return null; + } + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + var dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diff_halfMatchI_(longtext, shorttext, i) { + // Start with a 1/4 length substring at position i as a seed. + var seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + var j = -1; + var best_common = ''; + var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b; + while ((j = shorttext.indexOf(seed, j + 1)) != -1) { + var prefixLength = dmp.diff_commonPrefix(longtext.substring(i), + shorttext.substring(j)); + var suffixLength = dmp.diff_commonSuffix(longtext.substring(0, i), + shorttext.substring(0, j)); + if (best_common.length < suffixLength + prefixLength) { + best_common = shorttext.substring(j - suffixLength, j) + + shorttext.substring(j, j + prefixLength); + best_longtext_a = longtext.substring(0, i - suffixLength); + best_longtext_b = longtext.substring(i + prefixLength); + best_shorttext_a = shorttext.substring(0, j - suffixLength); + best_shorttext_b = shorttext.substring(j + prefixLength); + } + } + if (best_common.length * 2 >= longtext.length) { + return [best_longtext_a, best_longtext_b, + best_shorttext_a, best_shorttext_b, best_common]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + var hm1 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 4)); + // Check again based on the third quarter. + var hm2 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 2)); + var hm; + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + var text1_a, text1_b, text2_a, text2_b; + if (text1.length > text2.length) { + text1_a = hm[0]; + text1_b = hm[1]; + text2_a = hm[2]; + text2_b = hm[3]; + } else { + text2_a = hm[0]; + text2_b = hm[1]; + text1_a = hm[2]; + text1_b = hm[3]; + } + var mid_common = hm[4]; + return [text1_a, text1_b, text2_a, text2_b, mid_common]; +}; + + +/** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) { + var changes = false; + var equalities = []; // Stack of indices where equalities are found. + var equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + var lastequality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + var pointer = 0; // Index of current position. + // Number of characters that changed prior to the equality. + var length_insertions1 = 0; + var length_deletions1 = 0; + // Number of characters that changed after the equality. + var length_insertions2 = 0; + var length_deletions2 = 0; + while (pointer < diffs.length) { + if (diffs[pointer][0] == DIFF_EQUAL) { // Equality found. + equalities[equalitiesLength++] = pointer; + length_insertions1 = length_insertions2; + length_deletions1 = length_deletions2; + length_insertions2 = 0; + length_deletions2 = 0; + lastequality = diffs[pointer][1]; + } else { // An insertion or deletion. + if (diffs[pointer][0] == DIFF_INSERT) { + length_insertions2 += diffs[pointer][1].length; + } else { + length_deletions2 += diffs[pointer][1].length; + } + // Eliminate an equality that is smaller or equal to the edits on both + // sides of it. + if (lastequality && (lastequality.length <= + Math.max(length_insertions1, length_deletions1)) && + (lastequality.length <= Math.max(length_insertions2, + length_deletions2))) { + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, + [DIFF_DELETE, lastequality]); + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + // Throw away the equality we just deleted. + equalitiesLength--; + // Throw away the previous equality (it needs to be reevaluated). + equalitiesLength--; + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + length_insertions1 = 0; // Reset the counters. + length_deletions1 = 0; + length_insertions2 = 0; + length_deletions2 = 0; + lastequality = null; + changes = true; + } + } + pointer++; + } + + // Normalize the diff. + if (changes) { + this.diff_cleanupMerge(diffs); + } + this.diff_cleanupSemanticLossless(diffs); + + // Find any overlaps between deletions and insertions. + // e.g: abcxxxxxxdef + // -> abcxxxdef + // e.g: xxxabcdefxxx + // -> defxxxabc + // Only extract an overlap if it is as big as the edit ahead or behind it. + pointer = 1; + while (pointer < diffs.length) { + if (diffs[pointer - 1][0] == DIFF_DELETE && + diffs[pointer][0] == DIFF_INSERT) { + var deletion = diffs[pointer - 1][1]; + var insertion = diffs[pointer][1]; + var overlap_length1 = this.diff_commonOverlap_(deletion, insertion); + var overlap_length2 = this.diff_commonOverlap_(insertion, deletion); + if (overlap_length1 >= overlap_length2) { + if (overlap_length1 >= deletion.length / 2 || + overlap_length1 >= insertion.length / 2) { + // Overlap found. Insert an equality and trim the surrounding edits. + diffs.splice(pointer, 0, + [DIFF_EQUAL, insertion.substring(0, overlap_length1)]); + diffs[pointer - 1][1] = + deletion.substring(0, deletion.length - overlap_length1); + diffs[pointer + 1][1] = insertion.substring(overlap_length1); + pointer++; + } + } else { + if (overlap_length2 >= deletion.length / 2 || + overlap_length2 >= insertion.length / 2) { + // Reverse overlap found. + // Insert an equality and swap and trim the surrounding edits. + diffs.splice(pointer, 0, + [DIFF_EQUAL, deletion.substring(0, overlap_length2)]); + diffs[pointer - 1][0] = DIFF_INSERT; + diffs[pointer - 1][1] = + insertion.substring(0, insertion.length - overlap_length2); + diffs[pointer + 1][0] = DIFF_DELETE; + diffs[pointer + 1][1] = + deletion.substring(overlap_length2); + pointer++; + } + } + pointer++; + } + pointer++; + } +}; + + +/** + * Look for single edits surrounded on both sides by equalities + * which can be shifted sideways to align the edit to a word boundary. + * e.g: The cat came. -> The cat came. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupSemanticLossless = function(diffs) { + /** + * Given two strings, compute a score representing whether the internal + * boundary falls on logical boundaries. + * Scores range from 6 (best) to 0 (worst). + * Closure, but does not reference any external variables. + * @param {string} one First string. + * @param {string} two Second string. + * @return {number} The score. + * @private + */ + function diff_cleanupSemanticScore_(one, two) { + if (!one || !two) { + // Edges are the best. + return 6; + } + + // Each port of this function behaves slightly differently due to + // subtle differences in each language's definition of things like + // 'whitespace'. Since this function's purpose is largely cosmetic, + // the choice has been made to use each language's native features + // rather than force total conformity. + var char1 = one.charAt(one.length - 1); + var char2 = two.charAt(0); + var nonAlphaNumeric1 = char1.match(diff_match_patch.nonAlphaNumericRegex_); + var nonAlphaNumeric2 = char2.match(diff_match_patch.nonAlphaNumericRegex_); + var whitespace1 = nonAlphaNumeric1 && + char1.match(diff_match_patch.whitespaceRegex_); + var whitespace2 = nonAlphaNumeric2 && + char2.match(diff_match_patch.whitespaceRegex_); + var lineBreak1 = whitespace1 && + char1.match(diff_match_patch.linebreakRegex_); + var lineBreak2 = whitespace2 && + char2.match(diff_match_patch.linebreakRegex_); + var blankLine1 = lineBreak1 && + one.match(diff_match_patch.blanklineEndRegex_); + var blankLine2 = lineBreak2 && + two.match(diff_match_patch.blanklineStartRegex_); + + if (blankLine1 || blankLine2) { + // Five points for blank lines. + return 5; + } else if (lineBreak1 || lineBreak2) { + // Four points for line breaks. + return 4; + } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) { + // Three points for end of sentences. + return 3; + } else if (whitespace1 || whitespace2) { + // Two points for whitespace. + return 2; + } else if (nonAlphaNumeric1 || nonAlphaNumeric2) { + // One point for non-alphanumeric. + return 1; + } + return 0; + } + + var pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] == DIFF_EQUAL && + diffs[pointer + 1][0] == DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + var equality1 = diffs[pointer - 1][1]; + var edit = diffs[pointer][1]; + var equality2 = diffs[pointer + 1][1]; + + // First, shift the edit as far left as possible. + var commonOffset = this.diff_commonSuffix(equality1, edit); + if (commonOffset) { + var commonString = edit.substring(edit.length - commonOffset); + equality1 = equality1.substring(0, equality1.length - commonOffset); + edit = commonString + edit.substring(0, edit.length - commonOffset); + equality2 = commonString + equality2; + } + + // Second, step character by character right, looking for the best fit. + var bestEquality1 = equality1; + var bestEdit = edit; + var bestEquality2 = equality2; + var bestScore = diff_cleanupSemanticScore_(equality1, edit) + + diff_cleanupSemanticScore_(edit, equality2); + while (edit.charAt(0) === equality2.charAt(0)) { + equality1 += edit.charAt(0); + edit = edit.substring(1) + equality2.charAt(0); + equality2 = equality2.substring(1); + var score = diff_cleanupSemanticScore_(equality1, edit) + + diff_cleanupSemanticScore_(edit, equality2); + // The >= encourages trailing rather than leading whitespace on edits. + if (score >= bestScore) { + bestScore = score; + bestEquality1 = equality1; + bestEdit = edit; + bestEquality2 = equality2; + } + } + + if (diffs[pointer - 1][1] != bestEquality1) { + // We have an improvement, save it back to the diff. + if (bestEquality1) { + diffs[pointer - 1][1] = bestEquality1; + } else { + diffs.splice(pointer - 1, 1); + pointer--; + } + diffs[pointer][1] = bestEdit; + if (bestEquality2) { + diffs[pointer + 1][1] = bestEquality2; + } else { + diffs.splice(pointer + 1, 1); + pointer--; + } + } + } + pointer++; + } +}; + +// Define some regex patterns for matching boundaries. +diff_match_patch.nonAlphaNumericRegex_ = /[^a-zA-Z0-9]/; +diff_match_patch.whitespaceRegex_ = /\s/; +diff_match_patch.linebreakRegex_ = /[\r\n]/; +diff_match_patch.blanklineEndRegex_ = /\n\r?\n$/; +diff_match_patch.blanklineStartRegex_ = /^\r?\n\r?\n/; + +/** + * Reduce the number of edits by eliminating operationally trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupEfficiency = function(diffs) { + var changes = false; + var equalities = []; // Stack of indices where equalities are found. + var equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + var lastequality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + var pointer = 0; // Index of current position. + // Is there an insertion operation before the last equality. + var pre_ins = false; + // Is there a deletion operation before the last equality. + var pre_del = false; + // Is there an insertion operation after the last equality. + var post_ins = false; + // Is there a deletion operation after the last equality. + var post_del = false; + while (pointer < diffs.length) { + if (diffs[pointer][0] == DIFF_EQUAL) { // Equality found. + if (diffs[pointer][1].length < this.Diff_EditCost && + (post_ins || post_del)) { + // Candidate found. + equalities[equalitiesLength++] = pointer; + pre_ins = post_ins; + pre_del = post_del; + lastequality = diffs[pointer][1]; + } else { + // Not a candidate, and can never become one. + equalitiesLength = 0; + lastequality = null; + } + post_ins = post_del = false; + } else { // An insertion or deletion. + if (diffs[pointer][0] == DIFF_DELETE) { + post_del = true; + } else { + post_ins = true; + } + /* + * Five types to be split: + * ABXYCD + * AXCD + * ABXC + * AXCD + * ABXC + */ + if (lastequality && ((pre_ins && pre_del && post_ins && post_del) || + ((lastequality.length < this.Diff_EditCost / 2) && + (pre_ins + pre_del + post_ins + post_del) == 3))) { + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, + [DIFF_DELETE, lastequality]); + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + equalitiesLength--; // Throw away the equality we just deleted; + lastequality = null; + if (pre_ins && pre_del) { + // No changes made which could affect previous entry, keep going. + post_ins = post_del = true; + equalitiesLength = 0; + } else { + equalitiesLength--; // Throw away the previous equality. + pointer = equalitiesLength > 0 ? + equalities[equalitiesLength - 1] : -1; + post_ins = post_del = false; + } + changes = true; + } + } + pointer++; + } + + if (changes) { + this.diff_cleanupMerge(diffs); + } +}; + + +/** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupMerge = function(diffs) { + diffs.push([DIFF_EQUAL, '']); // Add a dummy entry at the end. + var pointer = 0; + var count_delete = 0; + var count_insert = 0; + var text_delete = ''; + var text_insert = ''; + var commonlength; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + count_insert++; + text_insert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + count_delete++; + text_delete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (count_delete + count_insert > 1) { + if (count_delete !== 0 && count_insert !== 0) { + // Factor out any common prefixies. + commonlength = this.diff_commonPrefix(text_insert, text_delete); + if (commonlength !== 0) { + if ((pointer - count_delete - count_insert) > 0 && + diffs[pointer - count_delete - count_insert - 1][0] == + DIFF_EQUAL) { + diffs[pointer - count_delete - count_insert - 1][1] += + text_insert.substring(0, commonlength); + } else { + diffs.splice(0, 0, [DIFF_EQUAL, + text_insert.substring(0, commonlength)]); + pointer++; + } + text_insert = text_insert.substring(commonlength); + text_delete = text_delete.substring(commonlength); + } + // Factor out any common suffixies. + commonlength = this.diff_commonSuffix(text_insert, text_delete); + if (commonlength !== 0) { + diffs[pointer][1] = text_insert.substring(text_insert.length - + commonlength) + diffs[pointer][1]; + text_insert = text_insert.substring(0, text_insert.length - + commonlength); + text_delete = text_delete.substring(0, text_delete.length - + commonlength); + } + } + // Delete the offending records and add the merged ones. + if (count_delete === 0) { + diffs.splice(pointer - count_insert, + count_delete + count_insert, [DIFF_INSERT, text_insert]); + } else if (count_insert === 0) { + diffs.splice(pointer - count_delete, + count_delete + count_insert, [DIFF_DELETE, text_delete]); + } else { + diffs.splice(pointer - count_delete - count_insert, + count_delete + count_insert, [DIFF_DELETE, text_delete], + [DIFF_INSERT, text_insert]); + } + pointer = pointer - count_delete - count_insert + + (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1; + } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) { + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + break; + } + } + if (diffs[diffs.length - 1][1] === '') { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + var changes = false; + pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] == DIFF_EQUAL && + diffs[pointer + 1][0] == DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + if (diffs[pointer][1].substring(diffs[pointer][1].length - + diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) { + // Shift the edit over the previous equality. + diffs[pointer][1] = diffs[pointer - 1][1] + + diffs[pointer][1].substring(0, diffs[pointer][1].length - + diffs[pointer - 1][1].length); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == + diffs[pointer + 1][1]) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + this.diff_cleanupMerge(diffs); + } +}; + + +/** + * loc is a location in text1, compute and return the equivalent location in + * text2. + * e.g. 'The cat' vs 'The big cat', 1->1, 5->8 + * @param {!Array.} diffs Array of diff tuples. + * @param {number} loc Location within text1. + * @return {number} Location within text2. + */ +diff_match_patch.prototype.diff_xIndex = function(diffs, loc) { + var chars1 = 0; + var chars2 = 0; + var last_chars1 = 0; + var last_chars2 = 0; + var x; + for (x = 0; x < diffs.length; x++) { + if (diffs[x][0] !== DIFF_INSERT) { // Equality or deletion. + chars1 += diffs[x][1].length; + } + if (diffs[x][0] !== DIFF_DELETE) { // Equality or insertion. + chars2 += diffs[x][1].length; + } + if (chars1 > loc) { // Overshot the location. + break; + } + last_chars1 = chars1; + last_chars2 = chars2; + } + // Was the location was deleted? + if (diffs.length != x && diffs[x][0] === DIFF_DELETE) { + return last_chars2; + } + // Add the remaining character length. + return last_chars2 + (loc - last_chars1); +}; + + +/** + * Convert a diff array into a pretty HTML report. + * @param {!Array.} diffs Array of diff tuples. + * @return {string} HTML representation. + */ +diff_match_patch.prototype.diff_prettyHtml = function(diffs) { + var html = []; + var pattern_amp = /&/g; + var pattern_lt = //g; + var pattern_para = /\n/g; + for (var x = 0; x < diffs.length; x++) { + var op = diffs[x][0]; // Operation (insert, delete, equal) + var data = diffs[x][1]; // Text of change. + var text = data.replace(pattern_amp, '&').replace(pattern_lt, '<') + .replace(pattern_gt, '>').replace(pattern_para, '¶
'); + switch (op) { + case DIFF_INSERT: + html[x] = '' + text + ''; + break; + case DIFF_DELETE: + html[x] = '' + text + ''; + break; + case DIFF_EQUAL: + html[x] = '' + text + ''; + break; + } + } + return html.join(''); +}; + + +/** + * Compute and return the source text (all equalities and deletions). + * @param {!Array.} diffs Array of diff tuples. + * @return {string} Source text. + */ +diff_match_patch.prototype.diff_text1 = function(diffs) { + var text = []; + for (var x = 0; x < diffs.length; x++) { + if (diffs[x][0] !== DIFF_INSERT) { + text[x] = diffs[x][1]; + } + } + return text.join(''); +}; + + +/** + * Compute and return the destination text (all equalities and insertions). + * @param {!Array.} diffs Array of diff tuples. + * @return {string} Destination text. + */ +diff_match_patch.prototype.diff_text2 = function(diffs) { + var text = []; + for (var x = 0; x < diffs.length; x++) { + if (diffs[x][0] !== DIFF_DELETE) { + text[x] = diffs[x][1]; + } + } + return text.join(''); +}; + + +/** + * Compute the Levenshtein distance; the number of inserted, deleted or + * substituted characters. + * @param {!Array.} diffs Array of diff tuples. + * @return {number} Number of changes. + */ +diff_match_patch.prototype.diff_levenshtein = function(diffs) { + var levenshtein = 0; + var insertions = 0; + var deletions = 0; + for (var x = 0; x < diffs.length; x++) { + var op = diffs[x][0]; + var data = diffs[x][1]; + switch (op) { + case DIFF_INSERT: + insertions += data.length; + break; + case DIFF_DELETE: + deletions += data.length; + break; + case DIFF_EQUAL: + // A deletion and an insertion is one substitution. + levenshtein += Math.max(insertions, deletions); + insertions = 0; + deletions = 0; + break; + } + } + levenshtein += Math.max(insertions, deletions); + return levenshtein; +}; + + +/** + * Crush the diff into an encoded string which describes the operations + * required to transform text1 into text2. + * E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. + * Operations are tab-separated. Inserted text is escaped using %xx notation. + * @param {!Array.} diffs Array of diff tuples. + * @return {string} Delta text. + */ +diff_match_patch.prototype.diff_toDelta = function(diffs) { + var text = []; + for (var x = 0; x < diffs.length; x++) { + switch (diffs[x][0]) { + case DIFF_INSERT: + text[x] = '+' + encodeURI(diffs[x][1]); + break; + case DIFF_DELETE: + text[x] = '-' + diffs[x][1].length; + break; + case DIFF_EQUAL: + text[x] = '=' + diffs[x][1].length; + break; + } + } + return text.join('\t').replace(/%20/g, ' '); +}; + + +/** + * Given the original text1, and an encoded string which describes the + * operations required to transform text1 into text2, compute the full diff. + * @param {string} text1 Source string for the diff. + * @param {string} delta Delta text. + * @return {!Array.} Array of diff tuples. + * @throws {!Error} If invalid input. + */ +diff_match_patch.prototype.diff_fromDelta = function(text1, delta) { + var diffs = []; + var diffsLength = 0; // Keeping our own length var is faster in JS. + var pointer = 0; // Cursor in text1 + var tokens = delta.split(/\t/g); + for (var x = 0; x < tokens.length; x++) { + // Each token begins with a one character parameter which specifies the + // operation of this token (delete, insert, equality). + var param = tokens[x].substring(1); + switch (tokens[x].charAt(0)) { + case '+': + try { + diffs[diffsLength++] = [DIFF_INSERT, decodeURI(param)]; + } catch (ex) { + // Malformed URI sequence. + throw new Error('Illegal escape in diff_fromDelta: ' + param); + } + break; + case '-': + // Fall through. + case '=': + var n = parseInt(param, 10); + if (isNaN(n) || n < 0) { + throw new Error('Invalid number in diff_fromDelta: ' + param); + } + var text = text1.substring(pointer, pointer += n); + if (tokens[x].charAt(0) == '=') { + diffs[diffsLength++] = [DIFF_EQUAL, text]; + } else { + diffs[diffsLength++] = [DIFF_DELETE, text]; + } + break; + default: + // Blank tokens are ok (from a trailing \t). + // Anything else is an error. + if (tokens[x]) { + throw new Error('Invalid diff operation in diff_fromDelta: ' + + tokens[x]); + } + } + } + if (pointer != text1.length) { + throw new Error('Delta length (' + pointer + + ') does not equal source text length (' + text1.length + ').'); + } + return diffs; +}; + + +// MATCH FUNCTIONS + + +/** + * Locate the best instance of 'pattern' in 'text' near 'loc'. + * @param {string} text The text to search. + * @param {string} pattern The pattern to search for. + * @param {number} loc The location to search around. + * @return {number} Best match index or -1. + */ +diff_match_patch.prototype.match_main = function(text, pattern, loc) { + // Check for null inputs. + if (text == null || pattern == null || loc == null) { + throw new Error('Null input. (match_main)'); + } + + loc = Math.max(0, Math.min(loc, text.length)); + if (text == pattern) { + // Shortcut (potentially not guaranteed by the algorithm) + return 0; + } else if (!text.length) { + // Nothing to match. + return -1; + } else if (text.substring(loc, loc + pattern.length) == pattern) { + // Perfect match at the perfect spot! (Includes case of null pattern) + return loc; + } else { + // Do a fuzzy compare. + return this.match_bitap_(text, pattern, loc); + } +}; + + +/** + * Locate the best instance of 'pattern' in 'text' near 'loc' using the + * Bitap algorithm. + * @param {string} text The text to search. + * @param {string} pattern The pattern to search for. + * @param {number} loc The location to search around. + * @return {number} Best match index or -1. + * @private + */ +diff_match_patch.prototype.match_bitap_ = function(text, pattern, loc) { + if (pattern.length > this.Match_MaxBits) { + throw new Error('Pattern too long for this browser.'); + } + + // Initialise the alphabet. + var s = this.match_alphabet_(pattern); + + var dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Compute and return the score for a match with e errors and x location. + * Accesses loc and pattern through being a closure. + * @param {number} e Number of errors in match. + * @param {number} x Location of match. + * @return {number} Overall score for match (0.0 = good, 1.0 = bad). + * @private + */ + function match_bitapScore_(e, x) { + var accuracy = e / pattern.length; + var proximity = Math.abs(loc - x); + if (!dmp.Match_Distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy; + } + return accuracy + (proximity / dmp.Match_Distance); + } + + // Highest score beyond which we give up. + var score_threshold = this.Match_Threshold; + // Is there a nearby exact match? (speedup) + var best_loc = text.indexOf(pattern, loc); + if (best_loc != -1) { + score_threshold = Math.min(match_bitapScore_(0, best_loc), score_threshold); + // What about in the other direction? (speedup) + best_loc = text.lastIndexOf(pattern, loc + pattern.length); + if (best_loc != -1) { + score_threshold = + Math.min(match_bitapScore_(0, best_loc), score_threshold); + } + } + + // Initialise the bit arrays. + var matchmask = 1 << (pattern.length - 1); + best_loc = -1; + + var bin_min, bin_mid; + var bin_max = pattern.length + text.length; + var last_rd; + for (var d = 0; d < pattern.length; d++) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from 'loc' we can stray at this + // error level. + bin_min = 0; + bin_mid = bin_max; + while (bin_min < bin_mid) { + if (match_bitapScore_(d, loc + bin_mid) <= score_threshold) { + bin_min = bin_mid; + } else { + bin_max = bin_mid; + } + bin_mid = Math.floor((bin_max - bin_min) / 2 + bin_min); + } + // Use the result from this iteration as the maximum for the next. + bin_max = bin_mid; + var start = Math.max(1, loc - bin_mid + 1); + var finish = Math.min(loc + bin_mid, text.length) + pattern.length; + + var rd = Array(finish + 2); + rd[finish + 1] = (1 << d) - 1; + for (var j = finish; j >= start; j--) { + // The alphabet (s) is a sparse hash, so the following line generates + // warnings. + var charMatch = s[text.charAt(j - 1)]; + if (d === 0) { // First pass: exact match. + rd[j] = ((rd[j + 1] << 1) | 1) & charMatch; + } else { // Subsequent passes: fuzzy match. + rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) | + (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | + last_rd[j + 1]; + } + if (rd[j] & matchmask) { + var score = match_bitapScore_(d, j - 1); + // This match will almost certainly be better than any existing match. + // But check anyway. + if (score <= score_threshold) { + // Told you so. + score_threshold = score; + best_loc = j - 1; + if (best_loc > loc) { + // When passing loc, don't exceed our current distance from loc. + start = Math.max(1, 2 * loc - best_loc); + } else { + // Already passed loc, downhill from here on in. + break; + } + } + } + } + // No hope for a (better) match at greater error levels. + if (match_bitapScore_(d + 1, loc) > score_threshold) { + break; + } + last_rd = rd; + } + return best_loc; +}; + + +/** + * Initialise the alphabet for the Bitap algorithm. + * @param {string} pattern The text to encode. + * @return {!Object} Hash of character locations. + * @private + */ +diff_match_patch.prototype.match_alphabet_ = function(pattern) { + var s = {}; + for (var i = 0; i < pattern.length; i++) { + s[pattern.charAt(i)] = 0; + } + for (var i = 0; i < pattern.length; i++) { + s[pattern.charAt(i)] |= 1 << (pattern.length - i - 1); + } + return s; +}; + + +// PATCH FUNCTIONS + + +/** + * Increase the context until it is unique, + * but don't let the pattern expand beyond Match_MaxBits. + * @param {!diff_match_patch.patch_obj} patch The patch to grow. + * @param {string} text Source text. + * @private + */ +diff_match_patch.prototype.patch_addContext_ = function(patch, text) { + if (text.length == 0) { + return; + } + var pattern = text.substring(patch.start2, patch.start2 + patch.length1); + var padding = 0; + + // Look for the first and last matches of pattern in text. If two different + // matches are found, increase the pattern length. + while (text.indexOf(pattern) != text.lastIndexOf(pattern) && + pattern.length < this.Match_MaxBits - this.Patch_Margin - + this.Patch_Margin) { + padding += this.Patch_Margin; + pattern = text.substring(patch.start2 - padding, + patch.start2 + patch.length1 + padding); + } + // Add one chunk for good luck. + padding += this.Patch_Margin; + + // Add the prefix. + var prefix = text.substring(patch.start2 - padding, patch.start2); + if (prefix) { + patch.diffs.unshift([DIFF_EQUAL, prefix]); + } + // Add the suffix. + var suffix = text.substring(patch.start2 + patch.length1, + patch.start2 + patch.length1 + padding); + if (suffix) { + patch.diffs.push([DIFF_EQUAL, suffix]); + } + + // Roll back the start points. + patch.start1 -= prefix.length; + patch.start2 -= prefix.length; + // Extend the lengths. + patch.length1 += prefix.length + suffix.length; + patch.length2 += prefix.length + suffix.length; +}; + + +/** + * Compute a list of patches to turn text1 into text2. + * Use diffs if provided, otherwise compute it ourselves. + * There are four ways to call this function, depending on what data is + * available to the caller: + * Method 1: + * a = text1, b = text2 + * Method 2: + * a = diffs + * Method 3 (optimal): + * a = text1, b = diffs + * Method 4 (deprecated, use method 3): + * a = text1, b = text2, c = diffs + * + * @param {string|!Array.} a text1 (methods 1,3,4) or + * Array of diff tuples for text1 to text2 (method 2). + * @param {string|!Array.} opt_b text2 (methods 1,4) or + * Array of diff tuples for text1 to text2 (method 3) or undefined (method 2). + * @param {string|!Array.} opt_c Array of diff tuples + * for text1 to text2 (method 4) or undefined (methods 1,2,3). + * @return {!Array.} Array of Patch objects. + */ +diff_match_patch.prototype.patch_make = function(a, opt_b, opt_c) { + var text1, diffs; + if (typeof a == 'string' && typeof opt_b == 'string' && + typeof opt_c == 'undefined') { + // Method 1: text1, text2 + // Compute diffs from text1 and text2. + text1 = /** @type {string} */(a); + diffs = this.diff_main(text1, /** @type {string} */(opt_b), true); + if (diffs.length > 2) { + this.diff_cleanupSemantic(diffs); + this.diff_cleanupEfficiency(diffs); + } + } else if (a && typeof a == 'object' && typeof opt_b == 'undefined' && + typeof opt_c == 'undefined') { + // Method 2: diffs + // Compute text1 from diffs. + diffs = /** @type {!Array.} */(a); + text1 = this.diff_text1(diffs); + } else if (typeof a == 'string' && opt_b && typeof opt_b == 'object' && + typeof opt_c == 'undefined') { + // Method 3: text1, diffs + text1 = /** @type {string} */(a); + diffs = /** @type {!Array.} */(opt_b); + } else if (typeof a == 'string' && typeof opt_b == 'string' && + opt_c && typeof opt_c == 'object') { + // Method 4: text1, text2, diffs + // text2 is not used. + text1 = /** @type {string} */(a); + diffs = /** @type {!Array.} */(opt_c); + } else { + throw new Error('Unknown call format to patch_make.'); + } + + if (diffs.length === 0) { + return []; // Get rid of the null case. + } + var patches = []; + var patch = new diff_match_patch.patch_obj(); + var patchDiffLength = 0; // Keeping our own length var is faster in JS. + var char_count1 = 0; // Number of characters into the text1 string. + var char_count2 = 0; // Number of characters into the text2 string. + // Start with text1 (prepatch_text) and apply the diffs until we arrive at + // text2 (postpatch_text). We recreate the patches one by one to determine + // context info. + var prepatch_text = text1; + var postpatch_text = text1; + for (var x = 0; x < diffs.length; x++) { + var diff_type = diffs[x][0]; + var diff_text = diffs[x][1]; + + if (!patchDiffLength && diff_type !== DIFF_EQUAL) { + // A new patch starts here. + patch.start1 = char_count1; + patch.start2 = char_count2; + } + + switch (diff_type) { + case DIFF_INSERT: + patch.diffs[patchDiffLength++] = diffs[x]; + patch.length2 += diff_text.length; + postpatch_text = postpatch_text.substring(0, char_count2) + diff_text + + postpatch_text.substring(char_count2); + break; + case DIFF_DELETE: + patch.length1 += diff_text.length; + patch.diffs[patchDiffLength++] = diffs[x]; + postpatch_text = postpatch_text.substring(0, char_count2) + + postpatch_text.substring(char_count2 + + diff_text.length); + break; + case DIFF_EQUAL: + if (diff_text.length <= 2 * this.Patch_Margin && + patchDiffLength && diffs.length != x + 1) { + // Small equality inside a patch. + patch.diffs[patchDiffLength++] = diffs[x]; + patch.length1 += diff_text.length; + patch.length2 += diff_text.length; + } else if (diff_text.length >= 2 * this.Patch_Margin) { + // Time for a new patch. + if (patchDiffLength) { + this.patch_addContext_(patch, prepatch_text); + patches.push(patch); + patch = new diff_match_patch.patch_obj(); + patchDiffLength = 0; + // Unlike Unidiff, our patch lists have a rolling context. + // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff + // Update prepatch text & pos to reflect the application of the + // just completed patch. + prepatch_text = postpatch_text; + char_count1 = char_count2; + } + } + break; + } + + // Update the current character count. + if (diff_type !== DIFF_INSERT) { + char_count1 += diff_text.length; + } + if (diff_type !== DIFF_DELETE) { + char_count2 += diff_text.length; + } + } + // Pick up the leftover patch if not empty. + if (patchDiffLength) { + this.patch_addContext_(patch, prepatch_text); + patches.push(patch); + } + + return patches; +}; + + +/** + * Given an array of patches, return another array that is identical. + * @param {!Array.} patches Array of Patch objects. + * @return {!Array.} Array of Patch objects. + */ +diff_match_patch.prototype.patch_deepCopy = function(patches) { + // Making deep copies is hard in JavaScript. + var patchesCopy = []; + for (var x = 0; x < patches.length; x++) { + var patch = patches[x]; + var patchCopy = new diff_match_patch.patch_obj(); + patchCopy.diffs = []; + for (var y = 0; y < patch.diffs.length; y++) { + patchCopy.diffs[y] = patch.diffs[y].slice(); + } + patchCopy.start1 = patch.start1; + patchCopy.start2 = patch.start2; + patchCopy.length1 = patch.length1; + patchCopy.length2 = patch.length2; + patchesCopy[x] = patchCopy; + } + return patchesCopy; +}; + + +/** + * Merge a set of patches onto the text. Return a patched text, as well + * as a list of true/false values indicating which patches were applied. + * @param {!Array.} patches Array of Patch objects. + * @param {string} text Old text. + * @return {!Array.>} Two element Array, containing the + * new text and an array of boolean values. + */ +diff_match_patch.prototype.patch_apply = function(patches, text) { + if (patches.length == 0) { + return [text, []]; + } + + // Deep copy the patches so that no changes are made to originals. + patches = this.patch_deepCopy(patches); + + var nullPadding = this.patch_addPadding(patches); + text = nullPadding + text + nullPadding; + + this.patch_splitMax(patches); + // delta keeps track of the offset between the expected and actual location + // of the previous patch. If there are patches expected at positions 10 and + // 20, but the first patch was found at 12, delta is 2 and the second patch + // has an effective expected position of 22. + var delta = 0; + var results = []; + for (var x = 0; x < patches.length; x++) { + var expected_loc = patches[x].start2 + delta; + var text1 = this.diff_text1(patches[x].diffs); + var start_loc; + var end_loc = -1; + if (text1.length > this.Match_MaxBits) { + // patch_splitMax will only provide an oversized pattern in the case of + // a monster delete. + start_loc = this.match_main(text, text1.substring(0, this.Match_MaxBits), + expected_loc); + if (start_loc != -1) { + end_loc = this.match_main(text, + text1.substring(text1.length - this.Match_MaxBits), + expected_loc + text1.length - this.Match_MaxBits); + if (end_loc == -1 || start_loc >= end_loc) { + // Can't find valid trailing context. Drop this patch. + start_loc = -1; + } + } + } else { + start_loc = this.match_main(text, text1, expected_loc); + } + if (start_loc == -1) { + // No match found. :( + results[x] = false; + // Subtract the delta for this failed patch from subsequent patches. + delta -= patches[x].length2 - patches[x].length1; + } else { + // Found a match. :) + results[x] = true; + delta = start_loc - expected_loc; + var text2; + if (end_loc == -1) { + text2 = text.substring(start_loc, start_loc + text1.length); + } else { + text2 = text.substring(start_loc, end_loc + this.Match_MaxBits); + } + if (text1 == text2) { + // Perfect match, just shove the replacement text in. + text = text.substring(0, start_loc) + + this.diff_text2(patches[x].diffs) + + text.substring(start_loc + text1.length); + } else { + // Imperfect match. Run a diff to get a framework of equivalent + // indices. + var diffs = this.diff_main(text1, text2, false); + if (text1.length > this.Match_MaxBits && + this.diff_levenshtein(diffs) / text1.length > + this.Patch_DeleteThreshold) { + // The end points match, but the content is unacceptably bad. + results[x] = false; + } else { + this.diff_cleanupSemanticLossless(diffs); + var index1 = 0; + var index2; + for (var y = 0; y < patches[x].diffs.length; y++) { + var mod = patches[x].diffs[y]; + if (mod[0] !== DIFF_EQUAL) { + index2 = this.diff_xIndex(diffs, index1); + } + if (mod[0] === DIFF_INSERT) { // Insertion + text = text.substring(0, start_loc + index2) + mod[1] + + text.substring(start_loc + index2); + } else if (mod[0] === DIFF_DELETE) { // Deletion + text = text.substring(0, start_loc + index2) + + text.substring(start_loc + this.diff_xIndex(diffs, + index1 + mod[1].length)); + } + if (mod[0] !== DIFF_DELETE) { + index1 += mod[1].length; + } + } + } + } + } + } + // Strip the padding off. + text = text.substring(nullPadding.length, text.length - nullPadding.length); + return [text, results]; +}; + + +/** + * Add some padding on text start and end so that edges can match something. + * Intended to be called only from within patch_apply. + * @param {!Array.} patches Array of Patch objects. + * @return {string} The padding string added to each side. + */ +diff_match_patch.prototype.patch_addPadding = function(patches) { + var paddingLength = this.Patch_Margin; + var nullPadding = ''; + for (var x = 1; x <= paddingLength; x++) { + nullPadding += String.fromCharCode(x); + } + + // Bump all the patches forward. + for (var x = 0; x < patches.length; x++) { + patches[x].start1 += paddingLength; + patches[x].start2 += paddingLength; + } + + // Add some padding on start of first diff. + var patch = patches[0]; + var diffs = patch.diffs; + if (diffs.length == 0 || diffs[0][0] != DIFF_EQUAL) { + // Add nullPadding equality. + diffs.unshift([DIFF_EQUAL, nullPadding]); + patch.start1 -= paddingLength; // Should be 0. + patch.start2 -= paddingLength; // Should be 0. + patch.length1 += paddingLength; + patch.length2 += paddingLength; + } else if (paddingLength > diffs[0][1].length) { + // Grow first equality. + var extraLength = paddingLength - diffs[0][1].length; + diffs[0][1] = nullPadding.substring(diffs[0][1].length) + diffs[0][1]; + patch.start1 -= extraLength; + patch.start2 -= extraLength; + patch.length1 += extraLength; + patch.length2 += extraLength; + } + + // Add some padding on end of last diff. + patch = patches[patches.length - 1]; + diffs = patch.diffs; + if (diffs.length == 0 || diffs[diffs.length - 1][0] != DIFF_EQUAL) { + // Add nullPadding equality. + diffs.push([DIFF_EQUAL, nullPadding]); + patch.length1 += paddingLength; + patch.length2 += paddingLength; + } else if (paddingLength > diffs[diffs.length - 1][1].length) { + // Grow last equality. + var extraLength = paddingLength - diffs[diffs.length - 1][1].length; + diffs[diffs.length - 1][1] += nullPadding.substring(0, extraLength); + patch.length1 += extraLength; + patch.length2 += extraLength; + } + + return nullPadding; +}; + + +/** + * Look through the patches and break up any which are longer than the maximum + * limit of the match algorithm. + * Intended to be called only from within patch_apply. + * @param {!Array.} patches Array of Patch objects. + */ +diff_match_patch.prototype.patch_splitMax = function(patches) { + var patch_size = this.Match_MaxBits; + for (var x = 0; x < patches.length; x++) { + if (patches[x].length1 <= patch_size) { + continue; + } + var bigpatch = patches[x]; + // Remove the big old patch. + patches.splice(x--, 1); + var start1 = bigpatch.start1; + var start2 = bigpatch.start2; + var precontext = ''; + while (bigpatch.diffs.length !== 0) { + // Create one of several smaller patches. + var patch = new diff_match_patch.patch_obj(); + var empty = true; + patch.start1 = start1 - precontext.length; + patch.start2 = start2 - precontext.length; + if (precontext !== '') { + patch.length1 = patch.length2 = precontext.length; + patch.diffs.push([DIFF_EQUAL, precontext]); + } + while (bigpatch.diffs.length !== 0 && + patch.length1 < patch_size - this.Patch_Margin) { + var diff_type = bigpatch.diffs[0][0]; + var diff_text = bigpatch.diffs[0][1]; + if (diff_type === DIFF_INSERT) { + // Insertions are harmless. + patch.length2 += diff_text.length; + start2 += diff_text.length; + patch.diffs.push(bigpatch.diffs.shift()); + empty = false; + } else if (diff_type === DIFF_DELETE && patch.diffs.length == 1 && + patch.diffs[0][0] == DIFF_EQUAL && + diff_text.length > 2 * patch_size) { + // This is a large deletion. Let it pass in one chunk. + patch.length1 += diff_text.length; + start1 += diff_text.length; + empty = false; + patch.diffs.push([diff_type, diff_text]); + bigpatch.diffs.shift(); + } else { + // Deletion or equality. Only take as much as we can stomach. + diff_text = diff_text.substring(0, + patch_size - patch.length1 - this.Patch_Margin); + patch.length1 += diff_text.length; + start1 += diff_text.length; + if (diff_type === DIFF_EQUAL) { + patch.length2 += diff_text.length; + start2 += diff_text.length; + } else { + empty = false; + } + patch.diffs.push([diff_type, diff_text]); + if (diff_text == bigpatch.diffs[0][1]) { + bigpatch.diffs.shift(); + } else { + bigpatch.diffs[0][1] = + bigpatch.diffs[0][1].substring(diff_text.length); + } + } + } + // Compute the head context for the next patch. + precontext = this.diff_text2(patch.diffs); + precontext = + precontext.substring(precontext.length - this.Patch_Margin); + // Append the end context for this patch. + var postcontext = this.diff_text1(bigpatch.diffs) + .substring(0, this.Patch_Margin); + if (postcontext !== '') { + patch.length1 += postcontext.length; + patch.length2 += postcontext.length; + if (patch.diffs.length !== 0 && + patch.diffs[patch.diffs.length - 1][0] === DIFF_EQUAL) { + patch.diffs[patch.diffs.length - 1][1] += postcontext; + } else { + patch.diffs.push([DIFF_EQUAL, postcontext]); + } + } + if (!empty) { + patches.splice(++x, 0, patch); + } + } + } +}; + + +/** + * Take a list of patches and return a textual representation. + * @param {!Array.} patches Array of Patch objects. + * @return {string} Text representation of patches. + */ +diff_match_patch.prototype.patch_toText = function(patches) { + var text = []; + for (var x = 0; x < patches.length; x++) { + text[x] = patches[x]; + } + return text.join(''); +}; + + +/** + * Parse a textual representation of patches and return a list of Patch objects. + * @param {string} textline Text representation of patches. + * @return {!Array.} Array of Patch objects. + * @throws {!Error} If invalid input. + */ +diff_match_patch.prototype.patch_fromText = function(textline) { + var patches = []; + if (!textline) { + return patches; + } + var text = textline.split('\n'); + var textPointer = 0; + var patchHeader = /^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/; + while (textPointer < text.length) { + var m = text[textPointer].match(patchHeader); + if (!m) { + throw new Error('Invalid patch string: ' + text[textPointer]); + } + var patch = new diff_match_patch.patch_obj(); + patches.push(patch); + patch.start1 = parseInt(m[1], 10); + if (m[2] === '') { + patch.start1--; + patch.length1 = 1; + } else if (m[2] == '0') { + patch.length1 = 0; + } else { + patch.start1--; + patch.length1 = parseInt(m[2], 10); + } + + patch.start2 = parseInt(m[3], 10); + if (m[4] === '') { + patch.start2--; + patch.length2 = 1; + } else if (m[4] == '0') { + patch.length2 = 0; + } else { + patch.start2--; + patch.length2 = parseInt(m[4], 10); + } + textPointer++; + + while (textPointer < text.length) { + var sign = text[textPointer].charAt(0); + try { + var line = decodeURI(text[textPointer].substring(1)); + } catch (ex) { + // Malformed URI sequence. + throw new Error('Illegal escape in patch_fromText: ' + line); + } + if (sign == '-') { + // Deletion. + patch.diffs.push([DIFF_DELETE, line]); + } else if (sign == '+') { + // Insertion. + patch.diffs.push([DIFF_INSERT, line]); + } else if (sign == ' ') { + // Minor equality. + patch.diffs.push([DIFF_EQUAL, line]); + } else if (sign == '@') { + // Start of next patch. + break; + } else if (sign === '') { + // Blank line? Whatever. + } else { + // WTF? + throw new Error('Invalid patch mode "' + sign + '" in: ' + line); + } + textPointer++; + } + } + return patches; +}; + + +/** + * Class representing one patch operation. + * @constructor + */ +diff_match_patch.patch_obj = function() { + /** @type {!Array.} */ + this.diffs = []; + /** @type {?number} */ + this.start1 = null; + /** @type {?number} */ + this.start2 = null; + /** @type {number} */ + this.length1 = 0; + /** @type {number} */ + this.length2 = 0; +}; + + +/** + * Emmulate GNU diff's format. + * Header: @@ -382,8 +481,9 @@ + * Indicies are printed as 1-based, not 0-based. + * @return {string} The GNU diff string. + */ +diff_match_patch.patch_obj.prototype.toString = function() { + var coords1, coords2; + if (this.length1 === 0) { + coords1 = this.start1 + ',0'; + } else if (this.length1 == 1) { + coords1 = this.start1 + 1; + } else { + coords1 = (this.start1 + 1) + ',' + this.length1; + } + if (this.length2 === 0) { + coords2 = this.start2 + ',0'; + } else if (this.length2 == 1) { + coords2 = this.start2 + 1; + } else { + coords2 = (this.start2 + 1) + ',' + this.length2; + } + var text = ['@@ -' + coords1 + ' +' + coords2 + ' @@\n']; + var op; + // Escape the body of the patch with %xx notation. + for (var x = 0; x < this.diffs.length; x++) { + switch (this.diffs[x][0]) { + case DIFF_INSERT: + op = '+'; + break; + case DIFF_DELETE: + op = '-'; + break; + case DIFF_EQUAL: + op = ' '; + break; + } + text[x + 1] = op + encodeURI(this.diffs[x][1]) + '\n'; + } + return text.join('').replace(/%20/g, ' '); +}; + + +// Export these global variables so that they survive Google's JS compiler. +// In a browser, 'this' will be 'window'. +// Users of node.js should 'require' the uncompressed version since Google's +// JS compiler may break the following exports for non-browser environments. +this['diff_match_patch'] = diff_match_patch; +this['DIFF_DELETE'] = DIFF_DELETE; +this['DIFF_INSERT'] = DIFF_INSERT; +this['DIFF_EQUAL'] = DIFF_EQUAL; diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/html5shiv.js b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/html5shiv.js new file mode 100644 index 0000000..d074da7 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/html5shiv.js @@ -0,0 +1,301 @@ +/** +* @preserve HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +;(function(window, document) { +/*jshint evil:true */ + /** version */ + var version = '3.7.0'; + + /** Preset options */ + var options = window.html5 || {}; + + /** Used to skip problem elements */ + var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; + + /** Not all elements can be cloned in IE **/ + var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i; + + /** Detect whether the browser supports default html5 styles */ + var supportsHtml5Styles; + + /** Name of the expando, to work with multiple documents or to re-shiv one document */ + var expando = '_html5shiv'; + + /** The id for the the documents expando */ + var expanID = 0; + + /** Cached data for each document */ + var expandoData = {}; + + /** Detect whether the browser supports unknown elements */ + var supportsUnknownElements; + + (function() { + try { + var a = document.createElement('a'); + a.innerHTML = ''; + //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles + supportsHtml5Styles = ('hidden' in a); + + supportsUnknownElements = a.childNodes.length == 1 || (function() { + // assign a false positive if unable to shiv + (document.createElement)('a'); + var frag = document.createDocumentFragment(); + return ( + typeof frag.cloneNode == 'undefined' || + typeof frag.createDocumentFragment == 'undefined' || + typeof frag.createElement == 'undefined' + ); + }()); + } catch(e) { + // assign a false positive if detection fails => unable to shiv + supportsHtml5Styles = true; + supportsUnknownElements = true; + } + + }()); + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a style sheet with the given CSS text and adds it to the document. + * @private + * @param {Document} ownerDocument The document. + * @param {String} cssText The CSS text. + * @returns {StyleSheet} The style element. + */ + function addStyleSheet(ownerDocument, cssText) { + var p = ownerDocument.createElement('p'), + parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; + + p.innerHTML = 'x'; + return parent.insertBefore(p.lastChild, parent.firstChild); + } + + /** + * Returns the value of `html5.elements` as an array. + * @private + * @returns {Array} An array of shived element node names. + */ + function getElements() { + var elements = html5.elements; + return typeof elements == 'string' ? elements.split(' ') : elements; + } + + /** + * Returns the data associated to the given document + * @private + * @param {Document} ownerDocument The document. + * @returns {Object} An object of data. + */ + function getExpandoData(ownerDocument) { + var data = expandoData[ownerDocument[expando]]; + if (!data) { + data = {}; + expanID++; + ownerDocument[expando] = expanID; + expandoData[expanID] = data; + } + return data; + } + + /** + * returns a shived element for the given nodeName and document + * @memberOf html5 + * @param {String} nodeName name of the element + * @param {Document} ownerDocument The context document. + * @returns {Object} The shived element. + */ + function createElement(nodeName, ownerDocument, data){ + if (!ownerDocument) { + ownerDocument = document; + } + if(supportsUnknownElements){ + return ownerDocument.createElement(nodeName); + } + if (!data) { + data = getExpandoData(ownerDocument); + } + var node; + + if (data.cache[nodeName]) { + node = data.cache[nodeName].cloneNode(); + } else if (saveClones.test(nodeName)) { + node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); + } else { + node = data.createElem(nodeName); + } + + // Avoid adding some elements to fragments in IE < 9 because + // * Attributes like `name` or `type` cannot be set/changed once an element + // is inserted into a document/fragment + // * Link elements with `src` attributes that are inaccessible, as with + // a 403 response, will cause the tab/window to crash + // * Script elements appended to fragments will execute when their `src` + // or `text` property is set + return node.canHaveChildren && !reSkip.test(nodeName) ? data.frag.appendChild(node) : node; + } + + /** + * returns a shived DocumentFragment for the given document + * @memberOf html5 + * @param {Document} ownerDocument The context document. + * @returns {Object} The shived DocumentFragment. + */ + function createDocumentFragment(ownerDocument, data){ + if (!ownerDocument) { + ownerDocument = document; + } + if(supportsUnknownElements){ + return ownerDocument.createDocumentFragment(); + } + data = data || getExpandoData(ownerDocument); + var clone = data.frag.cloneNode(), + i = 0, + elems = getElements(), + l = elems.length; + for(;i
+ value = parseInt( elem.css( "zIndex" ), 10 ); + if ( !isNaN( value ) && value !== 0 ) { + return value; + } + } + elem = elem.parent(); + } + } + + return 0; + }, + + uniqueId: function() { + return this.each(function() { + if ( !this.id ) { + this.id = "ui-id-" + (++uuid); + } + }); + }, + + removeUniqueId: function() { + return this.each(function() { + if ( runiqueId.test( this.id ) ) { + $( this ).removeAttr( "id" ); + } + }); + } +}); + +// selectors +function focusable( element, isTabIndexNotNaN ) { + var map, mapName, img, + nodeName = element.nodeName.toLowerCase(); + if ( "area" === nodeName ) { + map = element.parentNode; + mapName = map.name; + if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { + return false; + } + img = $( "img[usemap=#" + mapName + "]" )[0]; + return !!img && visible( img ); + } + return ( /input|select|textarea|button|object/.test( nodeName ) ? + !element.disabled : + "a" === nodeName ? + element.href || isTabIndexNotNaN : + isTabIndexNotNaN) && + // the element and all of its ancestors must be visible + visible( element ); +} + +function visible( element ) { + return $.expr.filters.visible( element ) && + !$( element ).parents().addBack().filter(function() { + return $.css( this, "visibility" ) === "hidden"; + }).length; +} + +$.extend( $.expr[ ":" ], { + data: $.expr.createPseudo ? + $.expr.createPseudo(function( dataName ) { + return function( elem ) { + return !!$.data( elem, dataName ); + }; + }) : + // support: jQuery <1.8 + function( elem, i, match ) { + return !!$.data( elem, match[ 3 ] ); + }, + + focusable: function( element ) { + return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); + }, + + tabbable: function( element ) { + var tabIndex = $.attr( element, "tabindex" ), + isTabIndexNaN = isNaN( tabIndex ); + return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); + } +}); + +// support: jQuery <1.8 +if ( !$( "" ).outerWidth( 1 ).jquery ) { + $.each( [ "Width", "Height" ], function( i, name ) { + var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], + type = name.toLowerCase(), + orig = { + innerWidth: $.fn.innerWidth, + innerHeight: $.fn.innerHeight, + outerWidth: $.fn.outerWidth, + outerHeight: $.fn.outerHeight + }; + + function reduce( elem, size, border, margin ) { + $.each( side, function() { + size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; + if ( border ) { + size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; + } + if ( margin ) { + size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; + } + }); + return size; + } + + $.fn[ "inner" + name ] = function( size ) { + if ( size === undefined ) { + return orig[ "inner" + name ].call( this ); + } + + return this.each(function() { + $( this ).css( type, reduce( this, size ) + "px" ); + }); + }; + + $.fn[ "outer" + name] = function( size, margin ) { + if ( typeof size !== "number" ) { + return orig[ "outer" + name ].call( this, size ); + } + + return this.each(function() { + $( this).css( type, reduce( this, size, true, margin ) + "px" ); + }); + }; + }); +} + +// support: jQuery <1.8 +if ( !$.fn.addBack ) { + $.fn.addBack = function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + }; +} + +// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413) +if ( $( "" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) { + $.fn.removeData = (function( removeData ) { + return function( key ) { + if ( arguments.length ) { + return removeData.call( this, $.camelCase( key ) ); + } else { + return removeData.call( this ); + } + }; + })( $.fn.removeData ); +} + + + + + +// deprecated +$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); + +$.support.selectstart = "onselectstart" in document.createElement( "div" ); +$.fn.extend({ + disableSelection: function() { + return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + + ".ui-disableSelection", function( event ) { + event.preventDefault(); + }); + }, + + enableSelection: function() { + return this.unbind( ".ui-disableSelection" ); + } +}); + +$.extend( $.ui, { + // $.ui.plugin is deprecated. Use $.widget() extensions instead. + plugin: { + add: function( module, option, set ) { + var i, + proto = $.ui[ module ].prototype; + for ( i in set ) { + proto.plugins[ i ] = proto.plugins[ i ] || []; + proto.plugins[ i ].push( [ option, set[ i ] ] ); + } + }, + call: function( instance, name, args ) { + var i, + set = instance.plugins[ name ]; + if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) { + return; + } + + for ( i = 0; i < set.length; i++ ) { + if ( instance.options[ set[ i ][ 0 ] ] ) { + set[ i ][ 1 ].apply( instance.element, args ); + } + } + } + }, + + // only used by resizable + hasScroll: function( el, a ) { + + //If overflow is hidden, the element might have extra content, but the user wants to hide it + if ( $( el ).css( "overflow" ) === "hidden") { + return false; + } + + var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop", + has = false; + + if ( el[ scroll ] > 0 ) { + return true; + } + + // TODO: determine which cases actually cause this to happen + // if the element doesn't have the scroll set, see if it's possible to + // set the scroll + el[ scroll ] = 1; + has = ( el[ scroll ] > 0 ); + el[ scroll ] = 0; + return has; + } +}); + +})( jQuery ); +(function( $, undefined ) { + +var uuid = 0, + slice = Array.prototype.slice, + _cleanData = $.cleanData; +$.cleanData = function( elems ) { + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + try { + $( elem ).triggerHandler( "remove" ); + // http://bugs.jquery.com/ticket/8235 + } catch( e ) {} + } + _cleanData( elems ); +}; + +$.widget = function( name, base, prototype ) { + var fullName, existingConstructor, constructor, basePrototype, + // proxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + proxiedPrototype = {}, + namespace = name.split( "." )[ 0 ]; + + name = name.split( "." )[ 1 ]; + fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + // create selector for plugin + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { + return !!$.data( elem, fullName ); + }; + + $[ namespace ] = $[ namespace ] || {}; + existingConstructor = $[ namespace ][ name ]; + constructor = $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without "new" keyword + if ( !this._createWidget ) { + return new constructor( options, element ); + } + + // allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + // extend with the existing constructor to carry over any static properties + $.extend( constructor, existingConstructor, { + version: prototype.version, + // copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend( {}, prototype ), + // track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + }); + + basePrototype = new base(); + // we need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from + basePrototype.options = $.widget.extend( {}, basePrototype.options ); + $.each( prototype, function( prop, value ) { + if ( !$.isFunction( value ) ) { + proxiedPrototype[ prop ] = value; + return; + } + proxiedPrototype[ prop ] = (function() { + var _super = function() { + return base.prototype[ prop ].apply( this, arguments ); + }, + _superApply = function( args ) { + return base.prototype[ prop ].apply( this, args ); + }; + return function() { + var __super = this._super, + __superApply = this._superApply, + returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + })(); + }); + constructor.prototype = $.widget.extend( basePrototype, { + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name + }, proxiedPrototype, { + constructor: constructor, + namespace: namespace, + widgetName: name, + widgetFullName: fullName + }); + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if ( existingConstructor ) { + $.each( existingConstructor._childConstructors, function( i, child ) { + var childPrototype = child.prototype; + + // redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); + }); + // remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors; + } else { + base._childConstructors.push( constructor ); + } + + $.widget.bridge( name, constructor ); +}; + +$.widget.extend = function( target ) { + var input = slice.call( arguments, 1 ), + inputIndex = 0, + inputLength = input.length, + key, + value; + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + // Clone objects + if ( $.isPlainObject( value ) ) { + target[ key ] = $.isPlainObject( target[ key ] ) ? + $.widget.extend( {}, target[ key ], value ) : + // Don't extend strings, arrays, etc. with objects + $.widget.extend( {}, value ); + // Copy everything else by reference + } else { + target[ key ] = value; + } + } + } + } + return target; +}; + +$.widget.bridge = function( name, object ) { + var fullName = object.prototype.widgetFullName || name; + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string", + args = slice.call( arguments, 1 ), + returnValue = this; + + // allow multiple hashes to be passed on init + options = !isMethodCall && args.length ? + $.widget.extend.apply( null, [ options ].concat(args) ) : + options; + + if ( isMethodCall ) { + this.each(function() { + var methodValue, + instance = $.data( this, fullName ); + if ( !instance ) { + return $.error( "cannot call methods on " + name + " prior to initialization; " + + "attempted to call method '" + options + "'" ); + } + if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { + return $.error( "no such method '" + options + "' for " + name + " widget instance" ); + } + methodValue = instance[ options ].apply( instance, args ); + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue && methodValue.jquery ? + returnValue.pushStack( methodValue.get() ) : + methodValue; + return false; + } + }); + } else { + this.each(function() { + var instance = $.data( this, fullName ); + if ( instance ) { + instance.option( options || {} )._init(); + } else { + $.data( this, fullName, new object( options, this ) ); + } + }); + } + + return returnValue; + }; +}; + +$.Widget = function( /* options, element */ ) {}; +$.Widget._childConstructors = []; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + defaultElement: "
", + options: { + disabled: false, + + // callbacks + create: null + }, + _createWidget: function( options, element ) { + element = $( element || this.defaultElement || this )[ 0 ]; + this.element = $( element ); + this.uuid = uuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + this.options = $.widget.extend( {}, + this.options, + this._getCreateOptions(), + options ); + + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + + if ( element !== this ) { + $.data( element, this.widgetFullName, this ); + this._on( true, this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + }); + this.document = $( element.style ? + // element within the document + element.ownerDocument : + // element is window or document + element.document || element ); + this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); + } + + this._create(); + this._trigger( "create", null, this._getCreateEventData() ); + this._init(); + }, + _getCreateOptions: $.noop, + _getCreateEventData: $.noop, + _create: $.noop, + _init: $.noop, + + destroy: function() { + this._destroy(); + // we can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element + .unbind( this.eventNamespace ) + // 1.9 BC for #7810 + // TODO remove dual storage + .removeData( this.widgetName ) + .removeData( this.widgetFullName ) + // support: jquery <1.6.3 + // http://bugs.jquery.com/ticket/9413 + .removeData( $.camelCase( this.widgetFullName ) ); + this.widget() + .unbind( this.eventNamespace ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetFullName + "-disabled " + + "ui-state-disabled" ); + + // clean up events and states + this.bindings.unbind( this.eventNamespace ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + }, + _destroy: $.noop, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key, + parts, + curOption, + i; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.widget.extend( {}, this.options ); + } + + if ( typeof key === "string" ) { + // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {}; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( value === undefined ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( value === undefined ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } + } + + this._setOptions( options ); + + return this; + }, + _setOptions: function( options ) { + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } + + return this; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) + .attr( "aria-disabled", value ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + } + + return this; + }, + + enable: function() { + return this._setOption( "disabled", false ); + }, + disable: function() { + return this._setOption( "disabled", true ); + }, + + _on: function( suppressDisabledCheck, element, handlers ) { + var delegateElement, + instance = this; + + // no suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + + // no element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + // accept selectors, DOM elements + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + // allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^(\w+)\s*(.*)$/ ), + eventName = match[1] + instance.eventNamespace, + selector = match[2]; + if ( selector ) { + delegateElement.delegate( selector, eventName, handlerProxy ); + } else { + element.bind( eventName, handlerProxy ); + } + }); + }, + + _off: function( element, eventName ) { + eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; + element.unbind( eventName ).undelegate( eventName ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + $( event.currentTarget ).addClass( "ui-state-hover" ); + }, + mouseleave: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-hover" ); + } + }); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + $( event.currentTarget ).addClass( "ui-state-focus" ); + }, + focusout: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-focus" ); + } + }); + }, + + _trigger: function( type, event, data ) { + var prop, orig, + callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + // the original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + return !( $.isFunction( callback ) && + callback.apply( this.element[0], [ event ].concat( data ) ) === false || + event.isDefaultPrevented() ); + } +}; + +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + var hasOptions, + effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + if ( options.delay ) { + element.delay( options.delay ); + } + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue(function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + }); + } + }; +}); + +})( jQuery ); +(function( $, undefined ) { + +var mouseHandled = false; +$( document ).mouseup( function() { + mouseHandled = false; +}); + +$.widget("ui.mouse", { + version: "1.10.3", + options: { + cancel: "input,textarea,button,select,option", + distance: 1, + delay: 0 + }, + _mouseInit: function() { + var that = this; + + this.element + .bind("mousedown."+this.widgetName, function(event) { + return that._mouseDown(event); + }) + .bind("click."+this.widgetName, function(event) { + if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) { + $.removeData(event.target, that.widgetName + ".preventClickEvent"); + event.stopImmediatePropagation(); + return false; + } + }); + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.unbind("."+this.widgetName); + if ( this._mouseMoveDelegate ) { + $(document) + .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) + .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); + } + }, + + _mouseDown: function(event) { + // don't let more than one widget handle mouseStart + if( mouseHandled ) { return; } + + // we may have missed mouseup (out of window) + (this._mouseStarted && this._mouseUp(event)); + + this._mouseDownEvent = event; + + var that = this, + btnIsLeft = (event.which === 1), + // event.target.nodeName works around a bug in IE 8 with + // disabled inputs (#7620) + elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); + if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if (!this.mouseDelayMet) { + this._mouseDelayTimer = setTimeout(function() { + that.mouseDelayMet = true; + }, this.options.delay); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = (this._mouseStart(event) !== false); + if (!this._mouseStarted) { + event.preventDefault(); + return true; + } + } + + // Click event may never have fired (Gecko & Opera) + if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) { + $.removeData(event.target, this.widgetName + ".preventClickEvent"); + } + + // these delegates are required to keep context + this._mouseMoveDelegate = function(event) { + return that._mouseMove(event); + }; + this._mouseUpDelegate = function(event) { + return that._mouseUp(event); + }; + $(document) + .bind("mousemove."+this.widgetName, this._mouseMoveDelegate) + .bind("mouseup."+this.widgetName, this._mouseUpDelegate); + + event.preventDefault(); + + mouseHandled = true; + return true; + }, + + _mouseMove: function(event) { + // IE mouseup check - mouseup happened when mouse was out of window + if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) { + return this._mouseUp(event); + } + + if (this._mouseStarted) { + this._mouseDrag(event); + return event.preventDefault(); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = + (this._mouseStart(this._mouseDownEvent, event) !== false); + (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); + } + + return !this._mouseStarted; + }, + + _mouseUp: function(event) { + $(document) + .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) + .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); + + if (this._mouseStarted) { + this._mouseStarted = false; + + if (event.target === this._mouseDownEvent.target) { + $.data(event.target, this.widgetName + ".preventClickEvent", true); + } + + this._mouseStop(event); + } + + return false; + }, + + _mouseDistanceMet: function(event) { + return (Math.max( + Math.abs(this._mouseDownEvent.pageX - event.pageX), + Math.abs(this._mouseDownEvent.pageY - event.pageY) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function(/* event */) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function(/* event */) {}, + _mouseDrag: function(/* event */) {}, + _mouseStop: function(/* event */) {}, + _mouseCapture: function(/* event */) { return true; } +}); + +})(jQuery); +(function( $, undefined ) { + +$.extend($.ui, { datepicker: { version: "1.10.3" } }); + +var PROP_NAME = "datepicker", + instActive; + +/* Date picker manager. + Use the singleton instance of this class, $.datepicker, to interact with the date picker. + Settings for (groups of) date pickers are maintained in an instance object, + allowing multiple different settings on the same page. */ + +function Datepicker() { + this._curInst = null; // The current instance in use + this._keyEvent = false; // If the last event was a key event + this._disabledInputs = []; // List of date picker inputs that have been disabled + this._datepickerShowing = false; // True if the popup picker is showing , false if not + this._inDialog = false; // True if showing within a "dialog", false if not + this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division + this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class + this._appendClass = "ui-datepicker-append"; // The name of the append marker class + this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class + this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class + this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class + this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class + this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class + this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class + this.regional = []; // Available regional settings, indexed by language code + this.regional[""] = { // Default regional settings + closeText: "Done", // Display text for close link + prevText: "Prev", // Display text for previous month link + nextText: "Next", // Display text for next month link + currentText: "Today", // Display text for current month link + monthNames: ["January","February","March","April","May","June", + "July","August","September","October","November","December"], // Names of months for drop-down and formatting + monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting + dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting + dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting + dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday + weekHeader: "Wk", // Column header for week of the year + dateFormat: "mm/dd/yy", // See format options on parseDate + firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ... + isRTL: false, // True if right-to-left language, false if left-to-right + showMonthAfterYear: false, // True if the year select precedes month, false for month then year + yearSuffix: "" // Additional text to append to the year in the month headers + }; + this._defaults = { // Global defaults for all the date picker instances + showOn: "focus", // "focus" for popup on focus, + // "button" for trigger button, or "both" for either + showAnim: "fadeIn", // Name of jQuery animation for popup + showOptions: {}, // Options for enhanced animations + defaultDate: null, // Used when field is blank: actual date, + // +/-number for offset from today, null for today + appendText: "", // Display text following the input box, e.g. showing the format + buttonText: "...", // Text for trigger button + buttonImage: "", // URL for trigger button image + buttonImageOnly: false, // True if the image appears alone, false if it appears on a button + hideIfNoPrevNext: false, // True to hide next/previous month links + // if not applicable, false to just disable them + navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links + gotoCurrent: false, // True if today link goes back to current selection instead + changeMonth: false, // True if month can be selected directly, false if only prev/next + changeYear: false, // True if year can be selected directly, false if only prev/next + yearRange: "c-10:c+10", // Range of years to display in drop-down, + // either relative to today's year (-nn:+nn), relative to currently displayed year + // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n) + showOtherMonths: false, // True to show dates in other months, false to leave blank + selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable + showWeek: false, // True to show week of the year, false to not show it + calculateWeek: this.iso8601Week, // How to calculate the week of the year, + // takes a Date and returns the number of the week for it + shortYearCutoff: "+10", // Short year values < this are in the current century, + // > this are in the previous century, + // string value starting with "+" for current year + value + minDate: null, // The earliest selectable date, or null for no limit + maxDate: null, // The latest selectable date, or null for no limit + duration: "fast", // Duration of display/closure + beforeShowDay: null, // Function that takes a date and returns an array with + // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "", + // [2] = cell title (optional), e.g. $.datepicker.noWeekends + beforeShow: null, // Function that takes an input field and + // returns a set of custom settings for the date picker + onSelect: null, // Define a callback function when a date is selected + onChangeMonthYear: null, // Define a callback function when the month or year is changed + onClose: null, // Define a callback function when the datepicker is closed + numberOfMonths: 1, // Number of months to show at a time + showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0) + stepMonths: 1, // Number of months to step back/forward + stepBigMonths: 12, // Number of months to step back/forward for the big links + altField: "", // Selector for an alternate field to store selected dates into + altFormat: "", // The date format to use for the alternate field + constrainInput: true, // The input is constrained by the current date format + showButtonPanel: false, // True to show button panel, false to not show it + autoSize: false, // True to size the input for the date format, false to leave as is + disabled: false // The initial disabled state + }; + $.extend(this._defaults, this.regional[""]); + this.dpDiv = bindHover($("
")); +} + +$.extend(Datepicker.prototype, { + /* Class name added to elements to indicate already configured with a date picker. */ + markerClassName: "hasDatepicker", + + //Keep track of the maximum number of rows displayed (see #7043) + maxRows: 4, + + // TODO rename to "widget" when switching to widget factory + _widgetDatepicker: function() { + return this.dpDiv; + }, + + /* Override the default settings for all instances of the date picker. + * @param settings object - the new settings to use as defaults (anonymous object) + * @return the manager object + */ + setDefaults: function(settings) { + extendRemove(this._defaults, settings || {}); + return this; + }, + + /* Attach the date picker to a jQuery selection. + * @param target element - the target input field or division or span + * @param settings object - the new settings to use for this date picker instance (anonymous) + */ + _attachDatepicker: function(target, settings) { + var nodeName, inline, inst; + nodeName = target.nodeName.toLowerCase(); + inline = (nodeName === "div" || nodeName === "span"); + if (!target.id) { + this.uuid += 1; + target.id = "dp" + this.uuid; + } + inst = this._newInst($(target), inline); + inst.settings = $.extend({}, settings || {}); + if (nodeName === "input") { + this._connectDatepicker(target, inst); + } else if (inline) { + this._inlineDatepicker(target, inst); + } + }, + + /* Create a new instance object. */ + _newInst: function(target, inline) { + var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars + return {id: id, input: target, // associated target + selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection + drawMonth: 0, drawYear: 0, // month being drawn + inline: inline, // is datepicker inline or not + dpDiv: (!inline ? this.dpDiv : // presentation div + bindHover($("
")))}; + }, + + /* Attach the date picker to an input field. */ + _connectDatepicker: function(target, inst) { + var input = $(target); + inst.append = $([]); + inst.trigger = $([]); + if (input.hasClass(this.markerClassName)) { + return; + } + this._attachments(input, inst); + input.addClass(this.markerClassName).keydown(this._doKeyDown). + keypress(this._doKeyPress).keyup(this._doKeyUp); + this._autoSize(inst); + $.data(target, PROP_NAME, inst); + //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665) + if( inst.settings.disabled ) { + this._disableDatepicker( target ); + } + }, + + /* Make attachments based on settings. */ + _attachments: function(input, inst) { + var showOn, buttonText, buttonImage, + appendText = this._get(inst, "appendText"), + isRTL = this._get(inst, "isRTL"); + + if (inst.append) { + inst.append.remove(); + } + if (appendText) { + inst.append = $("" + appendText + ""); + input[isRTL ? "before" : "after"](inst.append); + } + + input.unbind("focus", this._showDatepicker); + + if (inst.trigger) { + inst.trigger.remove(); + } + + showOn = this._get(inst, "showOn"); + if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field + input.focus(this._showDatepicker); + } + if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked + buttonText = this._get(inst, "buttonText"); + buttonImage = this._get(inst, "buttonImage"); + inst.trigger = $(this._get(inst, "buttonImageOnly") ? + $("").addClass(this._triggerClass). + attr({ src: buttonImage, alt: buttonText, title: buttonText }) : + $("").addClass(this._triggerClass). + html(!buttonImage ? buttonText : $("").attr( + { src:buttonImage, alt:buttonText, title:buttonText }))); + input[isRTL ? "before" : "after"](inst.trigger); + inst.trigger.click(function() { + if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) { + $.datepicker._hideDatepicker(); + } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) { + $.datepicker._hideDatepicker(); + $.datepicker._showDatepicker(input[0]); + } else { + $.datepicker._showDatepicker(input[0]); + } + return false; + }); + } + }, + + /* Apply the maximum length for the date format. */ + _autoSize: function(inst) { + if (this._get(inst, "autoSize") && !inst.inline) { + var findMax, max, maxI, i, + date = new Date(2009, 12 - 1, 20), // Ensure double digits + dateFormat = this._get(inst, "dateFormat"); + + if (dateFormat.match(/[DM]/)) { + findMax = function(names) { + max = 0; + maxI = 0; + for (i = 0; i < names.length; i++) { + if (names[i].length > max) { + max = names[i].length; + maxI = i; + } + } + return maxI; + }; + date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ? + "monthNames" : "monthNamesShort")))); + date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ? + "dayNames" : "dayNamesShort"))) + 20 - date.getDay()); + } + inst.input.attr("size", this._formatDate(inst, date).length); + } + }, + + /* Attach an inline date picker to a div. */ + _inlineDatepicker: function(target, inst) { + var divSpan = $(target); + if (divSpan.hasClass(this.markerClassName)) { + return; + } + divSpan.addClass(this.markerClassName).append(inst.dpDiv); + $.data(target, PROP_NAME, inst); + this._setDate(inst, this._getDefaultDate(inst), true); + this._updateDatepicker(inst); + this._updateAlternate(inst); + //If disabled option is true, disable the datepicker before showing it (see ticket #5665) + if( inst.settings.disabled ) { + this._disableDatepicker( target ); + } + // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements + // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height + inst.dpDiv.css( "display", "block" ); + }, + + /* Pop-up the date picker in a "dialog" box. + * @param input element - ignored + * @param date string or Date - the initial date to display + * @param onSelect function - the function to call when a date is selected + * @param settings object - update the dialog date picker instance's settings (anonymous object) + * @param pos int[2] - coordinates for the dialog's position within the screen or + * event - with x/y coordinates or + * leave empty for default (screen centre) + * @return the manager object + */ + _dialogDatepicker: function(input, date, onSelect, settings, pos) { + var id, browserWidth, browserHeight, scrollX, scrollY, + inst = this._dialogInst; // internal instance + + if (!inst) { + this.uuid += 1; + id = "dp" + this.uuid; + this._dialogInput = $(""); + this._dialogInput.keydown(this._doKeyDown); + $("body").append(this._dialogInput); + inst = this._dialogInst = this._newInst(this._dialogInput, false); + inst.settings = {}; + $.data(this._dialogInput[0], PROP_NAME, inst); + } + extendRemove(inst.settings, settings || {}); + date = (date && date.constructor === Date ? this._formatDate(inst, date) : date); + this._dialogInput.val(date); + + this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null); + if (!this._pos) { + browserWidth = document.documentElement.clientWidth; + browserHeight = document.documentElement.clientHeight; + scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; + scrollY = document.documentElement.scrollTop || document.body.scrollTop; + this._pos = // should use actual width/height below + [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY]; + } + + // move input on screen for focus, but hidden behind dialog + this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px"); + inst.settings.onSelect = onSelect; + this._inDialog = true; + this.dpDiv.addClass(this._dialogClass); + this._showDatepicker(this._dialogInput[0]); + if ($.blockUI) { + $.blockUI(this.dpDiv); + } + $.data(this._dialogInput[0], PROP_NAME, inst); + return this; + }, + + /* Detach a datepicker from its control. + * @param target element - the target input field or division or span + */ + _destroyDatepicker: function(target) { + var nodeName, + $target = $(target), + inst = $.data(target, PROP_NAME); + + if (!$target.hasClass(this.markerClassName)) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + $.removeData(target, PROP_NAME); + if (nodeName === "input") { + inst.append.remove(); + inst.trigger.remove(); + $target.removeClass(this.markerClassName). + unbind("focus", this._showDatepicker). + unbind("keydown", this._doKeyDown). + unbind("keypress", this._doKeyPress). + unbind("keyup", this._doKeyUp); + } else if (nodeName === "div" || nodeName === "span") { + $target.removeClass(this.markerClassName).empty(); + } + }, + + /* Enable the date picker to a jQuery selection. + * @param target element - the target input field or division or span + */ + _enableDatepicker: function(target) { + var nodeName, inline, + $target = $(target), + inst = $.data(target, PROP_NAME); + + if (!$target.hasClass(this.markerClassName)) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + if (nodeName === "input") { + target.disabled = false; + inst.trigger.filter("button"). + each(function() { this.disabled = false; }).end(). + filter("img").css({opacity: "1.0", cursor: ""}); + } else if (nodeName === "div" || nodeName === "span") { + inline = $target.children("." + this._inlineClass); + inline.children().removeClass("ui-state-disabled"); + inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). + prop("disabled", false); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value === target ? null : value); }); // delete entry + }, + + /* Disable the date picker to a jQuery selection. + * @param target element - the target input field or division or span + */ + _disableDatepicker: function(target) { + var nodeName, inline, + $target = $(target), + inst = $.data(target, PROP_NAME); + + if (!$target.hasClass(this.markerClassName)) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + if (nodeName === "input") { + target.disabled = true; + inst.trigger.filter("button"). + each(function() { this.disabled = true; }).end(). + filter("img").css({opacity: "0.5", cursor: "default"}); + } else if (nodeName === "div" || nodeName === "span") { + inline = $target.children("." + this._inlineClass); + inline.children().addClass("ui-state-disabled"); + inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). + prop("disabled", true); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value === target ? null : value); }); // delete entry + this._disabledInputs[this._disabledInputs.length] = target; + }, + + /* Is the first field in a jQuery collection disabled as a datepicker? + * @param target element - the target input field or division or span + * @return boolean - true if disabled, false if enabled + */ + _isDisabledDatepicker: function(target) { + if (!target) { + return false; + } + for (var i = 0; i < this._disabledInputs.length; i++) { + if (this._disabledInputs[i] === target) { + return true; + } + } + return false; + }, + + /* Retrieve the instance data for the target control. + * @param target element - the target input field or division or span + * @return object - the associated instance data + * @throws error if a jQuery problem getting data + */ + _getInst: function(target) { + try { + return $.data(target, PROP_NAME); + } + catch (err) { + throw "Missing instance data for this datepicker"; + } + }, + + /* Update or retrieve the settings for a date picker attached to an input field or division. + * @param target element - the target input field or division or span + * @param name object - the new settings to update or + * string - the name of the setting to change or retrieve, + * when retrieving also "all" for all instance settings or + * "defaults" for all global defaults + * @param value any - the new value for the setting + * (omit if above is an object or to retrieve a value) + */ + _optionDatepicker: function(target, name, value) { + var settings, date, minDate, maxDate, + inst = this._getInst(target); + + if (arguments.length === 2 && typeof name === "string") { + return (name === "defaults" ? $.extend({}, $.datepicker._defaults) : + (inst ? (name === "all" ? $.extend({}, inst.settings) : + this._get(inst, name)) : null)); + } + + settings = name || {}; + if (typeof name === "string") { + settings = {}; + settings[name] = value; + } + + if (inst) { + if (this._curInst === inst) { + this._hideDatepicker(); + } + + date = this._getDateDatepicker(target, true); + minDate = this._getMinMaxDate(inst, "min"); + maxDate = this._getMinMaxDate(inst, "max"); + extendRemove(inst.settings, settings); + // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided + if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) { + inst.settings.minDate = this._formatDate(inst, minDate); + } + if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) { + inst.settings.maxDate = this._formatDate(inst, maxDate); + } + if ( "disabled" in settings ) { + if ( settings.disabled ) { + this._disableDatepicker(target); + } else { + this._enableDatepicker(target); + } + } + this._attachments($(target), inst); + this._autoSize(inst); + this._setDate(inst, date); + this._updateAlternate(inst); + this._updateDatepicker(inst); + } + }, + + // change method deprecated + _changeDatepicker: function(target, name, value) { + this._optionDatepicker(target, name, value); + }, + + /* Redraw the date picker attached to an input field or division. + * @param target element - the target input field or division or span + */ + _refreshDatepicker: function(target) { + var inst = this._getInst(target); + if (inst) { + this._updateDatepicker(inst); + } + }, + + /* Set the dates for a jQuery selection. + * @param target element - the target input field or division or span + * @param date Date - the new date + */ + _setDateDatepicker: function(target, date) { + var inst = this._getInst(target); + if (inst) { + this._setDate(inst, date); + this._updateDatepicker(inst); + this._updateAlternate(inst); + } + }, + + /* Get the date(s) for the first entry in a jQuery selection. + * @param target element - the target input field or division or span + * @param noDefault boolean - true if no default date is to be used + * @return Date - the current date + */ + _getDateDatepicker: function(target, noDefault) { + var inst = this._getInst(target); + if (inst && !inst.inline) { + this._setDateFromField(inst, noDefault); + } + return (inst ? this._getDate(inst) : null); + }, + + /* Handle keystrokes. */ + _doKeyDown: function(event) { + var onSelect, dateStr, sel, + inst = $.datepicker._getInst(event.target), + handled = true, + isRTL = inst.dpDiv.is(".ui-datepicker-rtl"); + + inst._keyEvent = true; + if ($.datepicker._datepickerShowing) { + switch (event.keyCode) { + case 9: $.datepicker._hideDatepicker(); + handled = false; + break; // hide on tab out + case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." + + $.datepicker._currentClass + ")", inst.dpDiv); + if (sel[0]) { + $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]); + } + + onSelect = $.datepicker._get(inst, "onSelect"); + if (onSelect) { + dateStr = $.datepicker._formatDate(inst); + + // trigger custom callback + onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); + } else { + $.datepicker._hideDatepicker(); + } + + return false; // don't submit the form + case 27: $.datepicker._hideDatepicker(); + break; // hide on escape + case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ? + -$.datepicker._get(inst, "stepBigMonths") : + -$.datepicker._get(inst, "stepMonths")), "M"); + break; // previous month/year on page up/+ ctrl + case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ? + +$.datepicker._get(inst, "stepBigMonths") : + +$.datepicker._get(inst, "stepMonths")), "M"); + break; // next month/year on page down/+ ctrl + case 35: if (event.ctrlKey || event.metaKey) { + $.datepicker._clearDate(event.target); + } + handled = event.ctrlKey || event.metaKey; + break; // clear on ctrl or command +end + case 36: if (event.ctrlKey || event.metaKey) { + $.datepicker._gotoToday(event.target); + } + handled = event.ctrlKey || event.metaKey; + break; // current on ctrl or command +home + case 37: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D"); + } + handled = event.ctrlKey || event.metaKey; + // -1 day on ctrl or command +left + if (event.originalEvent.altKey) { + $.datepicker._adjustDate(event.target, (event.ctrlKey ? + -$.datepicker._get(inst, "stepBigMonths") : + -$.datepicker._get(inst, "stepMonths")), "M"); + } + // next month/year on alt +left on Mac + break; + case 38: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, -7, "D"); + } + handled = event.ctrlKey || event.metaKey; + break; // -1 week on ctrl or command +up + case 39: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D"); + } + handled = event.ctrlKey || event.metaKey; + // +1 day on ctrl or command +right + if (event.originalEvent.altKey) { + $.datepicker._adjustDate(event.target, (event.ctrlKey ? + +$.datepicker._get(inst, "stepBigMonths") : + +$.datepicker._get(inst, "stepMonths")), "M"); + } + // next month/year on alt +right + break; + case 40: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, +7, "D"); + } + handled = event.ctrlKey || event.metaKey; + break; // +1 week on ctrl or command +down + default: handled = false; + } + } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home + $.datepicker._showDatepicker(this); + } else { + handled = false; + } + + if (handled) { + event.preventDefault(); + event.stopPropagation(); + } + }, + + /* Filter entered characters - based on date format. */ + _doKeyPress: function(event) { + var chars, chr, + inst = $.datepicker._getInst(event.target); + + if ($.datepicker._get(inst, "constrainInput")) { + chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat")); + chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode); + return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1); + } + }, + + /* Synchronise manual entry and field/alternate field. */ + _doKeyUp: function(event) { + var date, + inst = $.datepicker._getInst(event.target); + + if (inst.input.val() !== inst.lastVal) { + try { + date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"), + (inst.input ? inst.input.val() : null), + $.datepicker._getFormatConfig(inst)); + + if (date) { // only if valid + $.datepicker._setDateFromField(inst); + $.datepicker._updateAlternate(inst); + $.datepicker._updateDatepicker(inst); + } + } + catch (err) { + } + } + return true; + }, + + /* Pop-up the date picker for a given input field. + * If false returned from beforeShow event handler do not show. + * @param input element - the input field attached to the date picker or + * event - if triggered by focus + */ + _showDatepicker: function(input) { + input = input.target || input; + if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger + input = $("input", input.parentNode)[0]; + } + + if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here + return; + } + + var inst, beforeShow, beforeShowSettings, isFixed, + offset, showAnim, duration; + + inst = $.datepicker._getInst(input); + if ($.datepicker._curInst && $.datepicker._curInst !== inst) { + $.datepicker._curInst.dpDiv.stop(true, true); + if ( inst && $.datepicker._datepickerShowing ) { + $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] ); + } + } + + beforeShow = $.datepicker._get(inst, "beforeShow"); + beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {}; + if(beforeShowSettings === false){ + return; + } + extendRemove(inst.settings, beforeShowSettings); + + inst.lastVal = null; + $.datepicker._lastInput = input; + $.datepicker._setDateFromField(inst); + + if ($.datepicker._inDialog) { // hide cursor + input.value = ""; + } + if (!$.datepicker._pos) { // position below input + $.datepicker._pos = $.datepicker._findPos(input); + $.datepicker._pos[1] += input.offsetHeight; // add the height + } + + isFixed = false; + $(input).parents().each(function() { + isFixed |= $(this).css("position") === "fixed"; + return !isFixed; + }); + + offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]}; + $.datepicker._pos = null; + //to avoid flashes on Firefox + inst.dpDiv.empty(); + // determine sizing offscreen + inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"}); + $.datepicker._updateDatepicker(inst); + // fix width for dynamic number of date pickers + // and adjust position before showing + offset = $.datepicker._checkOffset(inst, offset, isFixed); + inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ? + "static" : (isFixed ? "fixed" : "absolute")), display: "none", + left: offset.left + "px", top: offset.top + "px"}); + + if (!inst.inline) { + showAnim = $.datepicker._get(inst, "showAnim"); + duration = $.datepicker._get(inst, "duration"); + inst.dpDiv.zIndex($(input).zIndex()+1); + $.datepicker._datepickerShowing = true; + + if ( $.effects && $.effects.effect[ showAnim ] ) { + inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration); + } else { + inst.dpDiv[showAnim || "show"](showAnim ? duration : null); + } + + if ( $.datepicker._shouldFocusInput( inst ) ) { + inst.input.focus(); + } + + $.datepicker._curInst = inst; + } + }, + + /* Generate the date picker content. */ + _updateDatepicker: function(inst) { + this.maxRows = 4; //Reset the max number of rows being displayed (see #7043) + instActive = inst; // for delegate hover events + inst.dpDiv.empty().append(this._generateHTML(inst)); + this._attachHandlers(inst); + inst.dpDiv.find("." + this._dayOverClass + " a").mouseover(); + + var origyearshtml, + numMonths = this._getNumberOfMonths(inst), + cols = numMonths[1], + width = 17; + + inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""); + if (cols > 1) { + inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em"); + } + inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") + + "Class"]("ui-datepicker-multi"); + inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") + + "Class"]("ui-datepicker-rtl"); + + if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) { + inst.input.focus(); + } + + // deffered render of the years select (to avoid flashes on Firefox) + if( inst.yearshtml ){ + origyearshtml = inst.yearshtml; + setTimeout(function(){ + //assure that inst.yearshtml didn't change. + if( origyearshtml === inst.yearshtml && inst.yearshtml ){ + inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml); + } + origyearshtml = inst.yearshtml = null; + }, 0); + } + }, + + // #6694 - don't focus the input if it's already focused + // this breaks the change event in IE + // Support: IE and jQuery <1.9 + _shouldFocusInput: function( inst ) { + return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" ); + }, + + /* Check positioning to remain on screen. */ + _checkOffset: function(inst, offset, isFixed) { + var dpWidth = inst.dpDiv.outerWidth(), + dpHeight = inst.dpDiv.outerHeight(), + inputWidth = inst.input ? inst.input.outerWidth() : 0, + inputHeight = inst.input ? inst.input.outerHeight() : 0, + viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()), + viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop()); + + offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0); + offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0; + offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0; + + // now check if datepicker is showing outside window viewport - move to a better place if so. + offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? + Math.abs(offset.left + dpWidth - viewWidth) : 0); + offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? + Math.abs(dpHeight + inputHeight) : 0); + + return offset; + }, + + /* Find an object's position on the screen. */ + _findPos: function(obj) { + var position, + inst = this._getInst(obj), + isRTL = this._get(inst, "isRTL"); + + while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) { + obj = obj[isRTL ? "previousSibling" : "nextSibling"]; + } + + position = $(obj).offset(); + return [position.left, position.top]; + }, + + /* Hide the date picker from view. + * @param input element - the input field attached to the date picker + */ + _hideDatepicker: function(input) { + var showAnim, duration, postProcess, onClose, + inst = this._curInst; + + if (!inst || (input && inst !== $.data(input, PROP_NAME))) { + return; + } + + if (this._datepickerShowing) { + showAnim = this._get(inst, "showAnim"); + duration = this._get(inst, "duration"); + postProcess = function() { + $.datepicker._tidyDialog(inst); + }; + + // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed + if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) { + inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess); + } else { + inst.dpDiv[(showAnim === "slideDown" ? "slideUp" : + (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess); + } + + if (!showAnim) { + postProcess(); + } + this._datepickerShowing = false; + + onClose = this._get(inst, "onClose"); + if (onClose) { + onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]); + } + + this._lastInput = null; + if (this._inDialog) { + this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" }); + if ($.blockUI) { + $.unblockUI(); + $("body").append(this.dpDiv); + } + } + this._inDialog = false; + } + }, + + /* Tidy up after a dialog display. */ + _tidyDialog: function(inst) { + inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar"); + }, + + /* Close date picker if clicked elsewhere. */ + _checkExternalClick: function(event) { + if (!$.datepicker._curInst) { + return; + } + + var $target = $(event.target), + inst = $.datepicker._getInst($target[0]); + + if ( ( ( $target[0].id !== $.datepicker._mainDivId && + $target.parents("#" + $.datepicker._mainDivId).length === 0 && + !$target.hasClass($.datepicker.markerClassName) && + !$target.closest("." + $.datepicker._triggerClass).length && + $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) || + ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) { + $.datepicker._hideDatepicker(); + } + }, + + /* Adjust one of the date sub-fields. */ + _adjustDate: function(id, offset, period) { + var target = $(id), + inst = this._getInst(target[0]); + + if (this._isDisabledDatepicker(target[0])) { + return; + } + this._adjustInstDate(inst, offset + + (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning + period); + this._updateDatepicker(inst); + }, + + /* Action for current link. */ + _gotoToday: function(id) { + var date, + target = $(id), + inst = this._getInst(target[0]); + + if (this._get(inst, "gotoCurrent") && inst.currentDay) { + inst.selectedDay = inst.currentDay; + inst.drawMonth = inst.selectedMonth = inst.currentMonth; + inst.drawYear = inst.selectedYear = inst.currentYear; + } else { + date = new Date(); + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + } + this._notifyChange(inst); + this._adjustDate(target); + }, + + /* Action for selecting a new month/year. */ + _selectMonthYear: function(id, select, period) { + var target = $(id), + inst = this._getInst(target[0]); + + inst["selected" + (period === "M" ? "Month" : "Year")] = + inst["draw" + (period === "M" ? "Month" : "Year")] = + parseInt(select.options[select.selectedIndex].value,10); + + this._notifyChange(inst); + this._adjustDate(target); + }, + + /* Action for selecting a day. */ + _selectDay: function(id, month, year, td) { + var inst, + target = $(id); + + if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) { + return; + } + + inst = this._getInst(target[0]); + inst.selectedDay = inst.currentDay = $("a", td).html(); + inst.selectedMonth = inst.currentMonth = month; + inst.selectedYear = inst.currentYear = year; + this._selectDate(id, this._formatDate(inst, + inst.currentDay, inst.currentMonth, inst.currentYear)); + }, + + /* Erase the input field and hide the date picker. */ + _clearDate: function(id) { + var target = $(id); + this._selectDate(target, ""); + }, + + /* Update the input field with the selected date. */ + _selectDate: function(id, dateStr) { + var onSelect, + target = $(id), + inst = this._getInst(target[0]); + + dateStr = (dateStr != null ? dateStr : this._formatDate(inst)); + if (inst.input) { + inst.input.val(dateStr); + } + this._updateAlternate(inst); + + onSelect = this._get(inst, "onSelect"); + if (onSelect) { + onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback + } else if (inst.input) { + inst.input.trigger("change"); // fire the change event + } + + if (inst.inline){ + this._updateDatepicker(inst); + } else { + this._hideDatepicker(); + this._lastInput = inst.input[0]; + if (typeof(inst.input[0]) !== "object") { + inst.input.focus(); // restore focus + } + this._lastInput = null; + } + }, + + /* Update any alternate field to synchronise with the main field. */ + _updateAlternate: function(inst) { + var altFormat, date, dateStr, + altField = this._get(inst, "altField"); + + if (altField) { // update alternate field too + altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat"); + date = this._getDate(inst); + dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst)); + $(altField).each(function() { $(this).val(dateStr); }); + } + }, + + /* Set as beforeShowDay function to prevent selection of weekends. + * @param date Date - the date to customise + * @return [boolean, string] - is this date selectable?, what is its CSS class? + */ + noWeekends: function(date) { + var day = date.getDay(); + return [(day > 0 && day < 6), ""]; + }, + + /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. + * @param date Date - the date to get the week for + * @return number - the number of the week within the year that contains this date + */ + iso8601Week: function(date) { + var time, + checkDate = new Date(date.getTime()); + + // Find Thursday of this week starting on Monday + checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); + + time = checkDate.getTime(); + checkDate.setMonth(0); // Compare with Jan 1 + checkDate.setDate(1); + return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; + }, + + /* Parse a string value into a date object. + * See formatDate below for the possible formats. + * + * @param format string - the expected format of the date + * @param value string - the date in the above format + * @param settings Object - attributes include: + * shortYearCutoff number - the cutoff year for determining the century (optional) + * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) + * dayNames string[7] - names of the days from Sunday (optional) + * monthNamesShort string[12] - abbreviated names of the months (optional) + * monthNames string[12] - names of the months (optional) + * @return Date - the extracted date value or null if value is blank + */ + parseDate: function (format, value, settings) { + if (format == null || value == null) { + throw "Invalid arguments"; + } + + value = (typeof value === "object" ? value.toString() : value + ""); + if (value === "") { + return null; + } + + var iFormat, dim, extra, + iValue = 0, + shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff, + shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp : + new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)), + dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort, + dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames, + monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort, + monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames, + year = -1, + month = -1, + day = -1, + doy = -1, + literal = false, + date, + // Check whether a format character is doubled + lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); + if (matches) { + iFormat++; + } + return matches; + }, + // Extract a number from the string value + getNumber = function(match) { + var isDoubled = lookAhead(match), + size = (match === "@" ? 14 : (match === "!" ? 20 : + (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))), + digits = new RegExp("^\\d{1," + size + "}"), + num = value.substring(iValue).match(digits); + if (!num) { + throw "Missing number at position " + iValue; + } + iValue += num[0].length; + return parseInt(num[0], 10); + }, + // Extract a name from the string value and convert to an index + getName = function(match, shortNames, longNames) { + var index = -1, + names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) { + return [ [k, v] ]; + }).sort(function (a, b) { + return -(a[1].length - b[1].length); + }); + + $.each(names, function (i, pair) { + var name = pair[1]; + if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) { + index = pair[0]; + iValue += name.length; + return false; + } + }); + if (index !== -1) { + return index + 1; + } else { + throw "Unknown name at position " + iValue; + } + }, + // Confirm that a literal character matches the string value + checkLiteral = function() { + if (value.charAt(iValue) !== format.charAt(iFormat)) { + throw "Unexpected literal at position " + iValue; + } + iValue++; + }; + + for (iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === "'" && !lookAhead("'")) { + literal = false; + } else { + checkLiteral(); + } + } else { + switch (format.charAt(iFormat)) { + case "d": + day = getNumber("d"); + break; + case "D": + getName("D", dayNamesShort, dayNames); + break; + case "o": + doy = getNumber("o"); + break; + case "m": + month = getNumber("m"); + break; + case "M": + month = getName("M", monthNamesShort, monthNames); + break; + case "y": + year = getNumber("y"); + break; + case "@": + date = new Date(getNumber("@")); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case "!": + date = new Date((getNumber("!") - this._ticksTo1970) / 10000); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case "'": + if (lookAhead("'")){ + checkLiteral(); + } else { + literal = true; + } + break; + default: + checkLiteral(); + } + } + } + + if (iValue < value.length){ + extra = value.substr(iValue); + if (!/^\s+/.test(extra)) { + throw "Extra/unparsed characters found in date: " + extra; + } + } + + if (year === -1) { + year = new Date().getFullYear(); + } else if (year < 100) { + year += new Date().getFullYear() - new Date().getFullYear() % 100 + + (year <= shortYearCutoff ? 0 : -100); + } + + if (doy > -1) { + month = 1; + day = doy; + do { + dim = this._getDaysInMonth(year, month - 1); + if (day <= dim) { + break; + } + month++; + day -= dim; + } while (true); + } + + date = this._daylightSavingAdjust(new Date(year, month - 1, day)); + if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) { + throw "Invalid date"; // E.g. 31/02/00 + } + return date; + }, + + /* Standard date formats. */ + ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601) + COOKIE: "D, dd M yy", + ISO_8601: "yy-mm-dd", + RFC_822: "D, d M y", + RFC_850: "DD, dd-M-y", + RFC_1036: "D, d M y", + RFC_1123: "D, d M yy", + RFC_2822: "D, d M yy", + RSS: "D, d M y", // RFC 822 + TICKS: "!", + TIMESTAMP: "@", + W3C: "yy-mm-dd", // ISO 8601 + + _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) + + Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000), + + /* Format a date object into a string value. + * The format can be combinations of the following: + * d - day of month (no leading zero) + * dd - day of month (two digit) + * o - day of year (no leading zeros) + * oo - day of year (three digit) + * D - day name short + * DD - day name long + * m - month of year (no leading zero) + * mm - month of year (two digit) + * M - month name short + * MM - month name long + * y - year (two digit) + * yy - year (four digit) + * @ - Unix timestamp (ms since 01/01/1970) + * ! - Windows ticks (100ns since 01/01/0001) + * "..." - literal text + * '' - single quote + * + * @param format string - the desired format of the date + * @param date Date - the date value to format + * @param settings Object - attributes include: + * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) + * dayNames string[7] - names of the days from Sunday (optional) + * monthNamesShort string[12] - abbreviated names of the months (optional) + * monthNames string[12] - names of the months (optional) + * @return string - the date in the above format + */ + formatDate: function (format, date, settings) { + if (!date) { + return ""; + } + + var iFormat, + dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort, + dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames, + monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort, + monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames, + // Check whether a format character is doubled + lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); + if (matches) { + iFormat++; + } + return matches; + }, + // Format a number, with leading zero if necessary + formatNumber = function(match, value, len) { + var num = "" + value; + if (lookAhead(match)) { + while (num.length < len) { + num = "0" + num; + } + } + return num; + }, + // Format a name, short or long as requested + formatName = function(match, value, shortNames, longNames) { + return (lookAhead(match) ? longNames[value] : shortNames[value]); + }, + output = "", + literal = false; + + if (date) { + for (iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === "'" && !lookAhead("'")) { + literal = false; + } else { + output += format.charAt(iFormat); + } + } else { + switch (format.charAt(iFormat)) { + case "d": + output += formatNumber("d", date.getDate(), 2); + break; + case "D": + output += formatName("D", date.getDay(), dayNamesShort, dayNames); + break; + case "o": + output += formatNumber("o", + Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3); + break; + case "m": + output += formatNumber("m", date.getMonth() + 1, 2); + break; + case "M": + output += formatName("M", date.getMonth(), monthNamesShort, monthNames); + break; + case "y": + output += (lookAhead("y") ? date.getFullYear() : + (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100); + break; + case "@": + output += date.getTime(); + break; + case "!": + output += date.getTime() * 10000 + this._ticksTo1970; + break; + case "'": + if (lookAhead("'")) { + output += "'"; + } else { + literal = true; + } + break; + default: + output += format.charAt(iFormat); + } + } + } + } + return output; + }, + + /* Extract all possible characters from the date format. */ + _possibleChars: function (format) { + var iFormat, + chars = "", + literal = false, + // Check whether a format character is doubled + lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); + if (matches) { + iFormat++; + } + return matches; + }; + + for (iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === "'" && !lookAhead("'")) { + literal = false; + } else { + chars += format.charAt(iFormat); + } + } else { + switch (format.charAt(iFormat)) { + case "d": case "m": case "y": case "@": + chars += "0123456789"; + break; + case "D": case "M": + return null; // Accept anything + case "'": + if (lookAhead("'")) { + chars += "'"; + } else { + literal = true; + } + break; + default: + chars += format.charAt(iFormat); + } + } + } + return chars; + }, + + /* Get a setting value, defaulting if necessary. */ + _get: function(inst, name) { + return inst.settings[name] !== undefined ? + inst.settings[name] : this._defaults[name]; + }, + + /* Parse existing date and initialise date picker. */ + _setDateFromField: function(inst, noDefault) { + if (inst.input.val() === inst.lastVal) { + return; + } + + var dateFormat = this._get(inst, "dateFormat"), + dates = inst.lastVal = inst.input ? inst.input.val() : null, + defaultDate = this._getDefaultDate(inst), + date = defaultDate, + settings = this._getFormatConfig(inst); + + try { + date = this.parseDate(dateFormat, dates, settings) || defaultDate; + } catch (event) { + dates = (noDefault ? "" : dates); + } + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + inst.currentDay = (dates ? date.getDate() : 0); + inst.currentMonth = (dates ? date.getMonth() : 0); + inst.currentYear = (dates ? date.getFullYear() : 0); + this._adjustInstDate(inst); + }, + + /* Retrieve the default date shown on opening. */ + _getDefaultDate: function(inst) { + return this._restrictMinMax(inst, + this._determineDate(inst, this._get(inst, "defaultDate"), new Date())); + }, + + /* A date may be specified as an exact value or a relative one. */ + _determineDate: function(inst, date, defaultDate) { + var offsetNumeric = function(offset) { + var date = new Date(); + date.setDate(date.getDate() + offset); + return date; + }, + offsetString = function(offset) { + try { + return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"), + offset, $.datepicker._getFormatConfig(inst)); + } + catch (e) { + // Ignore + } + + var date = (offset.toLowerCase().match(/^c/) ? + $.datepicker._getDate(inst) : null) || new Date(), + year = date.getFullYear(), + month = date.getMonth(), + day = date.getDate(), + pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g, + matches = pattern.exec(offset); + + while (matches) { + switch (matches[2] || "d") { + case "d" : case "D" : + day += parseInt(matches[1],10); break; + case "w" : case "W" : + day += parseInt(matches[1],10) * 7; break; + case "m" : case "M" : + month += parseInt(matches[1],10); + day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); + break; + case "y": case "Y" : + year += parseInt(matches[1],10); + day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); + break; + } + matches = pattern.exec(offset); + } + return new Date(year, month, day); + }, + newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) : + (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime())))); + + newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate); + if (newDate) { + newDate.setHours(0); + newDate.setMinutes(0); + newDate.setSeconds(0); + newDate.setMilliseconds(0); + } + return this._daylightSavingAdjust(newDate); + }, + + /* Handle switch to/from daylight saving. + * Hours may be non-zero on daylight saving cut-over: + * > 12 when midnight changeover, but then cannot generate + * midnight datetime, so jump to 1AM, otherwise reset. + * @param date (Date) the date to check + * @return (Date) the corrected date + */ + _daylightSavingAdjust: function(date) { + if (!date) { + return null; + } + date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0); + return date; + }, + + /* Set the date(s) directly. */ + _setDate: function(inst, date, noChange) { + var clear = !date, + origMonth = inst.selectedMonth, + origYear = inst.selectedYear, + newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date())); + + inst.selectedDay = inst.currentDay = newDate.getDate(); + inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth(); + inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear(); + if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) { + this._notifyChange(inst); + } + this._adjustInstDate(inst); + if (inst.input) { + inst.input.val(clear ? "" : this._formatDate(inst)); + } + }, + + /* Retrieve the date(s) directly. */ + _getDate: function(inst) { + var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null : + this._daylightSavingAdjust(new Date( + inst.currentYear, inst.currentMonth, inst.currentDay))); + return startDate; + }, + + /* Attach the onxxx handlers. These are declared statically so + * they work with static code transformers like Caja. + */ + _attachHandlers: function(inst) { + var stepMonths = this._get(inst, "stepMonths"), + id = "#" + inst.id.replace( /\\\\/g, "\\" ); + inst.dpDiv.find("[data-handler]").map(function () { + var handler = { + prev: function () { + $.datepicker._adjustDate(id, -stepMonths, "M"); + }, + next: function () { + $.datepicker._adjustDate(id, +stepMonths, "M"); + }, + hide: function () { + $.datepicker._hideDatepicker(); + }, + today: function () { + $.datepicker._gotoToday(id); + }, + selectDay: function () { + $.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this); + return false; + }, + selectMonth: function () { + $.datepicker._selectMonthYear(id, this, "M"); + return false; + }, + selectYear: function () { + $.datepicker._selectMonthYear(id, this, "Y"); + return false; + } + }; + $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]); + }); + }, + + /* Generate the HTML for the current state of the date picker. */ + _generateHTML: function(inst) { + var maxDraw, prevText, prev, nextText, next, currentText, gotoDate, + controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin, + monthNames, monthNamesShort, beforeShowDay, showOtherMonths, + selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate, + cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows, + printDate, dRow, tbody, daySettings, otherMonth, unselectable, + tempDate = new Date(), + today = this._daylightSavingAdjust( + new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time + isRTL = this._get(inst, "isRTL"), + showButtonPanel = this._get(inst, "showButtonPanel"), + hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"), + navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"), + numMonths = this._getNumberOfMonths(inst), + showCurrentAtPos = this._get(inst, "showCurrentAtPos"), + stepMonths = this._get(inst, "stepMonths"), + isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1), + currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) : + new Date(inst.currentYear, inst.currentMonth, inst.currentDay))), + minDate = this._getMinMaxDate(inst, "min"), + maxDate = this._getMinMaxDate(inst, "max"), + drawMonth = inst.drawMonth - showCurrentAtPos, + drawYear = inst.drawYear; + + if (drawMonth < 0) { + drawMonth += 12; + drawYear--; + } + if (maxDate) { + maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(), + maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate())); + maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw); + while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) { + drawMonth--; + if (drawMonth < 0) { + drawMonth = 11; + drawYear--; + } + } + } + inst.drawMonth = drawMonth; + inst.drawYear = drawYear; + + prevText = this._get(inst, "prevText"); + prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText, + this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)), + this._getFormatConfig(inst))); + + prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ? + "
" + prevText + "" : + (hideIfNoPrevNext ? "" : "" + prevText + "")); + + nextText = this._get(inst, "nextText"); + nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText, + this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)), + this._getFormatConfig(inst))); + + next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ? + "" + nextText + "" : + (hideIfNoPrevNext ? "" : "" + nextText + "")); + + currentText = this._get(inst, "currentText"); + gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today); + currentText = (!navigationAsDateFormat ? currentText : + this.formatDate(currentText, gotoDate, this._getFormatConfig(inst))); + + controls = (!inst.inline ? "" : ""); + + buttonPanel = (showButtonPanel) ? "
" + (isRTL ? controls : "") + + (this._isInRange(inst, gotoDate) ? "" : "") + (isRTL ? "" : controls) + "
" : ""; + + firstDay = parseInt(this._get(inst, "firstDay"),10); + firstDay = (isNaN(firstDay) ? 0 : firstDay); + + showWeek = this._get(inst, "showWeek"); + dayNames = this._get(inst, "dayNames"); + dayNamesMin = this._get(inst, "dayNamesMin"); + monthNames = this._get(inst, "monthNames"); + monthNamesShort = this._get(inst, "monthNamesShort"); + beforeShowDay = this._get(inst, "beforeShowDay"); + showOtherMonths = this._get(inst, "showOtherMonths"); + selectOtherMonths = this._get(inst, "selectOtherMonths"); + defaultDate = this._getDefaultDate(inst); + html = ""; + dow; + for (row = 0; row < numMonths[0]; row++) { + group = ""; + this.maxRows = 4; + for (col = 0; col < numMonths[1]; col++) { + selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay)); + cornerClass = " ui-corner-all"; + calender = ""; + if (isMultiMonth) { + calender += "
"; + } + calender += "
" + + (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") + + (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") + + this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate, + row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers + "
" + + ""; + thead = (showWeek ? "" : ""); + for (dow = 0; dow < 7; dow++) { // days of the week + day = (dow + firstDay) % 7; + thead += "= 5 ? " class='ui-datepicker-week-end'" : "") + ">" + + "" + dayNamesMin[day] + ""; + } + calender += thead + ""; + daysInMonth = this._getDaysInMonth(drawYear, drawMonth); + if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) { + inst.selectedDay = Math.min(inst.selectedDay, daysInMonth); + } + leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7; + curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate + numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043) + this.maxRows = numRows; + printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays)); + for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows + calender += ""; + tbody = (!showWeek ? "" : ""); + for (dow = 0; dow < 7; dow++) { // create date picker days + daySettings = (beforeShowDay ? + beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]); + otherMonth = (printDate.getMonth() !== drawMonth); + unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] || + (minDate && printDate < minDate) || (maxDate && printDate > maxDate); + tbody += ""; // display selectable date + printDate.setDate(printDate.getDate() + 1); + printDate = this._daylightSavingAdjust(printDate); + } + calender += tbody + ""; + } + drawMonth++; + if (drawMonth > 11) { + drawMonth = 0; + drawYear++; + } + calender += "
" + this._get(inst, "weekHeader") + "
" + + this._get(inst, "calculateWeek")(printDate) + "" + // actions + (otherMonth && !showOtherMonths ? " " : // display for other months + (unselectable ? "" + printDate.getDate() + "" : "" + printDate.getDate() + "")) + "
" + (isMultiMonth ? "
" + + ((numMonths[0] > 0 && col === numMonths[1]-1) ? "
" : "") : ""); + group += calender; + } + html += group; + } + html += buttonPanel; + inst._keyEvent = false; + return html; + }, + + /* Generate the month and year header. */ + _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate, + secondary, monthNames, monthNamesShort) { + + var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear, + changeMonth = this._get(inst, "changeMonth"), + changeYear = this._get(inst, "changeYear"), + showMonthAfterYear = this._get(inst, "showMonthAfterYear"), + html = "
", + monthHtml = ""; + + // month selection + if (secondary || !changeMonth) { + monthHtml += "" + monthNames[drawMonth] + ""; + } else { + inMinYear = (minDate && minDate.getFullYear() === drawYear); + inMaxYear = (maxDate && maxDate.getFullYear() === drawYear); + monthHtml += ""; + } + + if (!showMonthAfterYear) { + html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : ""); + } + + // year selection + if ( !inst.yearshtml ) { + inst.yearshtml = ""; + if (secondary || !changeYear) { + html += "" + drawYear + ""; + } else { + // determine range of years to display + years = this._get(inst, "yearRange").split(":"); + thisYear = new Date().getFullYear(); + determineYear = function(value) { + var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) : + (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) : + parseInt(value, 10))); + return (isNaN(year) ? thisYear : year); + }; + year = determineYear(years[0]); + endYear = Math.max(year, determineYear(years[1] || "")); + year = (minDate ? Math.max(year, minDate.getFullYear()) : year); + endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear); + inst.yearshtml += ""; + + html += inst.yearshtml; + inst.yearshtml = null; + } + } + + html += this._get(inst, "yearSuffix"); + if (showMonthAfterYear) { + html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml; + } + html += "
"; // Close datepicker_header + return html; + }, + + /* Adjust one of the date sub-fields. */ + _adjustInstDate: function(inst, offset, period) { + var year = inst.drawYear + (period === "Y" ? offset : 0), + month = inst.drawMonth + (period === "M" ? offset : 0), + day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0), + date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day))); + + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + if (period === "M" || period === "Y") { + this._notifyChange(inst); + } + }, + + /* Ensure a date is within any min/max bounds. */ + _restrictMinMax: function(inst, date) { + var minDate = this._getMinMaxDate(inst, "min"), + maxDate = this._getMinMaxDate(inst, "max"), + newDate = (minDate && date < minDate ? minDate : date); + return (maxDate && newDate > maxDate ? maxDate : newDate); + }, + + /* Notify change of month/year. */ + _notifyChange: function(inst) { + var onChange = this._get(inst, "onChangeMonthYear"); + if (onChange) { + onChange.apply((inst.input ? inst.input[0] : null), + [inst.selectedYear, inst.selectedMonth + 1, inst]); + } + }, + + /* Determine the number of months to show. */ + _getNumberOfMonths: function(inst) { + var numMonths = this._get(inst, "numberOfMonths"); + return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths)); + }, + + /* Determine the current maximum date - ensure no time components are set. */ + _getMinMaxDate: function(inst, minMax) { + return this._determineDate(inst, this._get(inst, minMax + "Date"), null); + }, + + /* Find the number of days in a given month. */ + _getDaysInMonth: function(year, month) { + return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate(); + }, + + /* Find the day of the week of the first of a month. */ + _getFirstDayOfMonth: function(year, month) { + return new Date(year, month, 1).getDay(); + }, + + /* Determines if we should allow a "next/prev" month display change. */ + _canAdjustMonth: function(inst, offset, curYear, curMonth) { + var numMonths = this._getNumberOfMonths(inst), + date = this._daylightSavingAdjust(new Date(curYear, + curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1)); + + if (offset < 0) { + date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth())); + } + return this._isInRange(inst, date); + }, + + /* Is the given date in the accepted range? */ + _isInRange: function(inst, date) { + var yearSplit, currentYear, + minDate = this._getMinMaxDate(inst, "min"), + maxDate = this._getMinMaxDate(inst, "max"), + minYear = null, + maxYear = null, + years = this._get(inst, "yearRange"); + if (years){ + yearSplit = years.split(":"); + currentYear = new Date().getFullYear(); + minYear = parseInt(yearSplit[0], 10); + maxYear = parseInt(yearSplit[1], 10); + if ( yearSplit[0].match(/[+\-].*/) ) { + minYear += currentYear; + } + if ( yearSplit[1].match(/[+\-].*/) ) { + maxYear += currentYear; + } + } + + return ((!minDate || date.getTime() >= minDate.getTime()) && + (!maxDate || date.getTime() <= maxDate.getTime()) && + (!minYear || date.getFullYear() >= minYear) && + (!maxYear || date.getFullYear() <= maxYear)); + }, + + /* Provide the configuration settings for formatting/parsing. */ + _getFormatConfig: function(inst) { + var shortYearCutoff = this._get(inst, "shortYearCutoff"); + shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff : + new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); + return {shortYearCutoff: shortYearCutoff, + dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"), + monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")}; + }, + + /* Format the given date for display. */ + _formatDate: function(inst, day, month, year) { + if (!day) { + inst.currentDay = inst.selectedDay; + inst.currentMonth = inst.selectedMonth; + inst.currentYear = inst.selectedYear; + } + var date = (day ? (typeof day === "object" ? day : + this._daylightSavingAdjust(new Date(year, month, day))) : + this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay))); + return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst)); + } +}); + +/* + * Bind hover events for datepicker elements. + * Done via delegate so the binding only occurs once in the lifetime of the parent div. + * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker. + */ +function bindHover(dpDiv) { + var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a"; + return dpDiv.delegate(selector, "mouseout", function() { + $(this).removeClass("ui-state-hover"); + if (this.className.indexOf("ui-datepicker-prev") !== -1) { + $(this).removeClass("ui-datepicker-prev-hover"); + } + if (this.className.indexOf("ui-datepicker-next") !== -1) { + $(this).removeClass("ui-datepicker-next-hover"); + } + }) + .delegate(selector, "mouseover", function(){ + if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) { + $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"); + $(this).addClass("ui-state-hover"); + if (this.className.indexOf("ui-datepicker-prev") !== -1) { + $(this).addClass("ui-datepicker-prev-hover"); + } + if (this.className.indexOf("ui-datepicker-next") !== -1) { + $(this).addClass("ui-datepicker-next-hover"); + } + } + }); +} + +/* jQuery extend now ignores nulls! */ +function extendRemove(target, props) { + $.extend(target, props); + for (var name in props) { + if (props[name] == null) { + target[name] = props[name]; + } + } + return target; +} + +/* Invoke the datepicker functionality. + @param options string - a command, optionally followed by additional parameters or + Object - settings for attaching new datepicker functionality + @return jQuery object */ +$.fn.datepicker = function(options){ + + /* Verify an empty collection wasn't passed - Fixes #6976 */ + if ( !this.length ) { + return this; + } + + /* Initialise the date picker. */ + if (!$.datepicker.initialized) { + $(document).mousedown($.datepicker._checkExternalClick); + $.datepicker.initialized = true; + } + + /* Append datepicker main container to body if not exist. */ + if ($("#"+$.datepicker._mainDivId).length === 0) { + $("body").append($.datepicker.dpDiv); + } + + var otherArgs = Array.prototype.slice.call(arguments, 1); + if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) { + return $.datepicker["_" + options + "Datepicker"]. + apply($.datepicker, [this[0]].concat(otherArgs)); + } + if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") { + return $.datepicker["_" + options + "Datepicker"]. + apply($.datepicker, [this[0]].concat(otherArgs)); + } + return this.each(function() { + typeof options === "string" ? + $.datepicker["_" + options + "Datepicker"]. + apply($.datepicker, [this].concat(otherArgs)) : + $.datepicker._attachDatepicker(this, options); + }); +}; + +$.datepicker = new Datepicker(); // singleton instance +$.datepicker.initialized = false; +$.datepicker.uuid = new Date().getTime(); +$.datepicker.version = "1.10.3"; + +})(jQuery); +(function( $, undefined ) { + +// number of pages in a slider +// (how many times can you page up/down to go through the whole range) +var numPages = 5; + +$.widget( "ui.slider", $.ui.mouse, { + version: "1.10.3", + widgetEventPrefix: "slide", + + options: { + animate: false, + distance: 0, + max: 100, + min: 0, + orientation: "horizontal", + range: false, + step: 1, + value: 0, + values: null, + + // callbacks + change: null, + slide: null, + start: null, + stop: null + }, + + _create: function() { + this._keySliding = false; + this._mouseSliding = false; + this._animateOff = true; + this._handleIndex = null; + this._detectOrientation(); + this._mouseInit(); + + this.element + .addClass( "ui-slider" + + " ui-slider-" + this.orientation + + " ui-widget" + + " ui-widget-content" + + " ui-corner-all"); + + this._refresh(); + this._setOption( "disabled", this.options.disabled ); + + this._animateOff = false; + }, + + _refresh: function() { + this._createRange(); + this._createHandles(); + this._setupEvents(); + this._refreshValue(); + }, + + _createHandles: function() { + var i, handleCount, + options = this.options, + existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ), + handle = "", + handles = []; + + handleCount = ( options.values && options.values.length ) || 1; + + if ( existingHandles.length > handleCount ) { + existingHandles.slice( handleCount ).remove(); + existingHandles = existingHandles.slice( 0, handleCount ); + } + + for ( i = existingHandles.length; i < handleCount; i++ ) { + handles.push( handle ); + } + + this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) ); + + this.handle = this.handles.eq( 0 ); + + this.handles.each(function( i ) { + $( this ).data( "ui-slider-handle-index", i ); + }); + }, + + _createRange: function() { + var options = this.options, + classes = ""; + + if ( options.range ) { + if ( options.range === true ) { + if ( !options.values ) { + options.values = [ this._valueMin(), this._valueMin() ]; + } else if ( options.values.length && options.values.length !== 2 ) { + options.values = [ options.values[0], options.values[0] ]; + } else if ( $.isArray( options.values ) ) { + options.values = options.values.slice(0); + } + } + + if ( !this.range || !this.range.length ) { + this.range = $( "
" ) + .appendTo( this.element ); + + classes = "ui-slider-range" + + // note: this isn't the most fittingly semantic framework class for this element, + // but worked best visually with a variety of themes + " ui-widget-header ui-corner-all"; + } else { + this.range.removeClass( "ui-slider-range-min ui-slider-range-max" ) + // Handle range switching from true to min/max + .css({ + "left": "", + "bottom": "" + }); + } + + this.range.addClass( classes + + ( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) ); + } else { + this.range = $([]); + } + }, + + _setupEvents: function() { + var elements = this.handles.add( this.range ).filter( "a" ); + this._off( elements ); + this._on( elements, this._handleEvents ); + this._hoverable( elements ); + this._focusable( elements ); + }, + + _destroy: function() { + this.handles.remove(); + this.range.remove(); + + this.element + .removeClass( "ui-slider" + + " ui-slider-horizontal" + + " ui-slider-vertical" + + " ui-widget" + + " ui-widget-content" + + " ui-corner-all" ); + + this._mouseDestroy(); + }, + + _mouseCapture: function( event ) { + var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle, + that = this, + o = this.options; + + if ( o.disabled ) { + return false; + } + + this.elementSize = { + width: this.element.outerWidth(), + height: this.element.outerHeight() + }; + this.elementOffset = this.element.offset(); + + position = { x: event.pageX, y: event.pageY }; + normValue = this._normValueFromMouse( position ); + distance = this._valueMax() - this._valueMin() + 1; + this.handles.each(function( i ) { + var thisDistance = Math.abs( normValue - that.values(i) ); + if (( distance > thisDistance ) || + ( distance === thisDistance && + (i === that._lastChangedValue || that.values(i) === o.min ))) { + distance = thisDistance; + closestHandle = $( this ); + index = i; + } + }); + + allowed = this._start( event, index ); + if ( allowed === false ) { + return false; + } + this._mouseSliding = true; + + this._handleIndex = index; + + closestHandle + .addClass( "ui-state-active" ) + .focus(); + + offset = closestHandle.offset(); + mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" ); + this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : { + left: event.pageX - offset.left - ( closestHandle.width() / 2 ), + top: event.pageY - offset.top - + ( closestHandle.height() / 2 ) - + ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) - + ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) + + ( parseInt( closestHandle.css("marginTop"), 10 ) || 0) + }; + + if ( !this.handles.hasClass( "ui-state-hover" ) ) { + this._slide( event, index, normValue ); + } + this._animateOff = true; + return true; + }, + + _mouseStart: function() { + return true; + }, + + _mouseDrag: function( event ) { + var position = { x: event.pageX, y: event.pageY }, + normValue = this._normValueFromMouse( position ); + + this._slide( event, this._handleIndex, normValue ); + + return false; + }, + + _mouseStop: function( event ) { + this.handles.removeClass( "ui-state-active" ); + this._mouseSliding = false; + + this._stop( event, this._handleIndex ); + this._change( event, this._handleIndex ); + + this._handleIndex = null; + this._clickOffset = null; + this._animateOff = false; + + return false; + }, + + _detectOrientation: function() { + this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal"; + }, + + _normValueFromMouse: function( position ) { + var pixelTotal, + pixelMouse, + percentMouse, + valueTotal, + valueMouse; + + if ( this.orientation === "horizontal" ) { + pixelTotal = this.elementSize.width; + pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 ); + } else { + pixelTotal = this.elementSize.height; + pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 ); + } + + percentMouse = ( pixelMouse / pixelTotal ); + if ( percentMouse > 1 ) { + percentMouse = 1; + } + if ( percentMouse < 0 ) { + percentMouse = 0; + } + if ( this.orientation === "vertical" ) { + percentMouse = 1 - percentMouse; + } + + valueTotal = this._valueMax() - this._valueMin(); + valueMouse = this._valueMin() + percentMouse * valueTotal; + + return this._trimAlignValue( valueMouse ); + }, + + _start: function( event, index ) { + var uiHash = { + handle: this.handles[ index ], + value: this.value() + }; + if ( this.options.values && this.options.values.length ) { + uiHash.value = this.values( index ); + uiHash.values = this.values(); + } + return this._trigger( "start", event, uiHash ); + }, + + _slide: function( event, index, newVal ) { + var otherVal, + newValues, + allowed; + + if ( this.options.values && this.options.values.length ) { + otherVal = this.values( index ? 0 : 1 ); + + if ( ( this.options.values.length === 2 && this.options.range === true ) && + ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) ) + ) { + newVal = otherVal; + } + + if ( newVal !== this.values( index ) ) { + newValues = this.values(); + newValues[ index ] = newVal; + // A slide can be canceled by returning false from the slide callback + allowed = this._trigger( "slide", event, { + handle: this.handles[ index ], + value: newVal, + values: newValues + } ); + otherVal = this.values( index ? 0 : 1 ); + if ( allowed !== false ) { + this.values( index, newVal, true ); + } + } + } else { + if ( newVal !== this.value() ) { + // A slide can be canceled by returning false from the slide callback + allowed = this._trigger( "slide", event, { + handle: this.handles[ index ], + value: newVal + } ); + if ( allowed !== false ) { + this.value( newVal ); + } + } + } + }, + + _stop: function( event, index ) { + var uiHash = { + handle: this.handles[ index ], + value: this.value() + }; + if ( this.options.values && this.options.values.length ) { + uiHash.value = this.values( index ); + uiHash.values = this.values(); + } + + this._trigger( "stop", event, uiHash ); + }, + + _change: function( event, index ) { + if ( !this._keySliding && !this._mouseSliding ) { + var uiHash = { + handle: this.handles[ index ], + value: this.value() + }; + if ( this.options.values && this.options.values.length ) { + uiHash.value = this.values( index ); + uiHash.values = this.values(); + } + + //store the last changed value index for reference when handles overlap + this._lastChangedValue = index; + + this._trigger( "change", event, uiHash ); + } + }, + + value: function( newValue ) { + if ( arguments.length ) { + this.options.value = this._trimAlignValue( newValue ); + this._refreshValue(); + this._change( null, 0 ); + return; + } + + return this._value(); + }, + + values: function( index, newValue ) { + var vals, + newValues, + i; + + if ( arguments.length > 1 ) { + this.options.values[ index ] = this._trimAlignValue( newValue ); + this._refreshValue(); + this._change( null, index ); + return; + } + + if ( arguments.length ) { + if ( $.isArray( arguments[ 0 ] ) ) { + vals = this.options.values; + newValues = arguments[ 0 ]; + for ( i = 0; i < vals.length; i += 1 ) { + vals[ i ] = this._trimAlignValue( newValues[ i ] ); + this._change( null, i ); + } + this._refreshValue(); + } else { + if ( this.options.values && this.options.values.length ) { + return this._values( index ); + } else { + return this.value(); + } + } + } else { + return this._values(); + } + }, + + _setOption: function( key, value ) { + var i, + valsLength = 0; + + if ( key === "range" && this.options.range === true ) { + if ( value === "min" ) { + this.options.value = this._values( 0 ); + this.options.values = null; + } else if ( value === "max" ) { + this.options.value = this._values( this.options.values.length-1 ); + this.options.values = null; + } + } + + if ( $.isArray( this.options.values ) ) { + valsLength = this.options.values.length; + } + + $.Widget.prototype._setOption.apply( this, arguments ); + + switch ( key ) { + case "orientation": + this._detectOrientation(); + this.element + .removeClass( "ui-slider-horizontal ui-slider-vertical" ) + .addClass( "ui-slider-" + this.orientation ); + this._refreshValue(); + break; + case "value": + this._animateOff = true; + this._refreshValue(); + this._change( null, 0 ); + this._animateOff = false; + break; + case "values": + this._animateOff = true; + this._refreshValue(); + for ( i = 0; i < valsLength; i += 1 ) { + this._change( null, i ); + } + this._animateOff = false; + break; + case "min": + case "max": + this._animateOff = true; + this._refreshValue(); + this._animateOff = false; + break; + case "range": + this._animateOff = true; + this._refresh(); + this._animateOff = false; + break; + } + }, + + //internal value getter + // _value() returns value trimmed by min and max, aligned by step + _value: function() { + var val = this.options.value; + val = this._trimAlignValue( val ); + + return val; + }, + + //internal values getter + // _values() returns array of values trimmed by min and max, aligned by step + // _values( index ) returns single value trimmed by min and max, aligned by step + _values: function( index ) { + var val, + vals, + i; + + if ( arguments.length ) { + val = this.options.values[ index ]; + val = this._trimAlignValue( val ); + + return val; + } else if ( this.options.values && this.options.values.length ) { + // .slice() creates a copy of the array + // this copy gets trimmed by min and max and then returned + vals = this.options.values.slice(); + for ( i = 0; i < vals.length; i+= 1) { + vals[ i ] = this._trimAlignValue( vals[ i ] ); + } + + return vals; + } else { + return []; + } + }, + + // returns the step-aligned value that val is closest to, between (inclusive) min and max + _trimAlignValue: function( val ) { + if ( val <= this._valueMin() ) { + return this._valueMin(); + } + if ( val >= this._valueMax() ) { + return this._valueMax(); + } + var step = ( this.options.step > 0 ) ? this.options.step : 1, + valModStep = (val - this._valueMin()) % step, + alignValue = val - valModStep; + + if ( Math.abs(valModStep) * 2 >= step ) { + alignValue += ( valModStep > 0 ) ? step : ( -step ); + } + + // Since JavaScript has problems with large floats, round + // the final value to 5 digits after the decimal point (see #4124) + return parseFloat( alignValue.toFixed(5) ); + }, + + _valueMin: function() { + return this.options.min; + }, + + _valueMax: function() { + return this.options.max; + }, + + _refreshValue: function() { + var lastValPercent, valPercent, value, valueMin, valueMax, + oRange = this.options.range, + o = this.options, + that = this, + animate = ( !this._animateOff ) ? o.animate : false, + _set = {}; + + if ( this.options.values && this.options.values.length ) { + this.handles.each(function( i ) { + valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100; + _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; + $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); + if ( that.options.range === true ) { + if ( that.orientation === "horizontal" ) { + if ( i === 0 ) { + that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate ); + } + if ( i === 1 ) { + that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + } else { + if ( i === 0 ) { + that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate ); + } + if ( i === 1 ) { + that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + } + } + lastValPercent = valPercent; + }); + } else { + value = this.value(); + valueMin = this._valueMin(); + valueMax = this._valueMax(); + valPercent = ( valueMax !== valueMin ) ? + ( value - valueMin ) / ( valueMax - valueMin ) * 100 : + 0; + _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; + this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); + + if ( oRange === "min" && this.orientation === "horizontal" ) { + this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate ); + } + if ( oRange === "max" && this.orientation === "horizontal" ) { + this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + if ( oRange === "min" && this.orientation === "vertical" ) { + this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate ); + } + if ( oRange === "max" && this.orientation === "vertical" ) { + this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + } + }, + + _handleEvents: { + keydown: function( event ) { + /*jshint maxcomplexity:25*/ + var allowed, curVal, newVal, step, + index = $( event.target ).data( "ui-slider-handle-index" ); + + switch ( event.keyCode ) { + case $.ui.keyCode.HOME: + case $.ui.keyCode.END: + case $.ui.keyCode.PAGE_UP: + case $.ui.keyCode.PAGE_DOWN: + case $.ui.keyCode.UP: + case $.ui.keyCode.RIGHT: + case $.ui.keyCode.DOWN: + case $.ui.keyCode.LEFT: + event.preventDefault(); + if ( !this._keySliding ) { + this._keySliding = true; + $( event.target ).addClass( "ui-state-active" ); + allowed = this._start( event, index ); + if ( allowed === false ) { + return; + } + } + break; + } + + step = this.options.step; + if ( this.options.values && this.options.values.length ) { + curVal = newVal = this.values( index ); + } else { + curVal = newVal = this.value(); + } + + switch ( event.keyCode ) { + case $.ui.keyCode.HOME: + newVal = this._valueMin(); + break; + case $.ui.keyCode.END: + newVal = this._valueMax(); + break; + case $.ui.keyCode.PAGE_UP: + newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) ); + break; + case $.ui.keyCode.PAGE_DOWN: + newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) ); + break; + case $.ui.keyCode.UP: + case $.ui.keyCode.RIGHT: + if ( curVal === this._valueMax() ) { + return; + } + newVal = this._trimAlignValue( curVal + step ); + break; + case $.ui.keyCode.DOWN: + case $.ui.keyCode.LEFT: + if ( curVal === this._valueMin() ) { + return; + } + newVal = this._trimAlignValue( curVal - step ); + break; + } + + this._slide( event, index, newVal ); + }, + click: function( event ) { + event.preventDefault(); + }, + keyup: function( event ) { + var index = $( event.target ).data( "ui-slider-handle-index" ); + + if ( this._keySliding ) { + this._keySliding = false; + this._stop( event, index ); + this._change( event, index ); + $( event.target ).removeClass( "ui-state-active" ); + } + } + } + +}); + +}(jQuery)); +(function($, undefined) { + +var dataSpace = "ui-effects-"; + +$.effects = { + effect: {} +}; + +/*! + * jQuery Color Animations v2.1.2 + * https://github.com/jquery/jquery-color + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * Date: Wed Jan 16 08:47:09 2013 -0600 + */ +(function( jQuery, undefined ) { + + var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor", + + // plusequals test for += 100 -= 100 + rplusequals = /^([\-+])=\s*(\d+\.?\d*)/, + // a set of RE's that can match strings and generate color tuples. + stringParsers = [{ + re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + parse: function( execResult ) { + return [ + execResult[ 1 ], + execResult[ 2 ], + execResult[ 3 ], + execResult[ 4 ] + ]; + } + }, { + re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + parse: function( execResult ) { + return [ + execResult[ 1 ] * 2.55, + execResult[ 2 ] * 2.55, + execResult[ 3 ] * 2.55, + execResult[ 4 ] + ]; + } + }, { + // this regex ignores A-F because it's compared against an already lowercased string + re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/, + parse: function( execResult ) { + return [ + parseInt( execResult[ 1 ], 16 ), + parseInt( execResult[ 2 ], 16 ), + parseInt( execResult[ 3 ], 16 ) + ]; + } + }, { + // this regex ignores A-F because it's compared against an already lowercased string + re: /#([a-f0-9])([a-f0-9])([a-f0-9])/, + parse: function( execResult ) { + return [ + parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ), + parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ), + parseInt( execResult[ 3 ] + execResult[ 3 ], 16 ) + ]; + } + }, { + re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + space: "hsla", + parse: function( execResult ) { + return [ + execResult[ 1 ], + execResult[ 2 ] / 100, + execResult[ 3 ] / 100, + execResult[ 4 ] + ]; + } + }], + + // jQuery.Color( ) + color = jQuery.Color = function( color, green, blue, alpha ) { + return new jQuery.Color.fn.parse( color, green, blue, alpha ); + }, + spaces = { + rgba: { + props: { + red: { + idx: 0, + type: "byte" + }, + green: { + idx: 1, + type: "byte" + }, + blue: { + idx: 2, + type: "byte" + } + } + }, + + hsla: { + props: { + hue: { + idx: 0, + type: "degrees" + }, + saturation: { + idx: 1, + type: "percent" + }, + lightness: { + idx: 2, + type: "percent" + } + } + } + }, + propTypes = { + "byte": { + floor: true, + max: 255 + }, + "percent": { + max: 1 + }, + "degrees": { + mod: 360, + floor: true + } + }, + support = color.support = {}, + + // element for support tests + supportElem = jQuery( "

" )[ 0 ], + + // colors = jQuery.Color.names + colors, + + // local aliases of functions called often + each = jQuery.each; + +// determine rgba support immediately +supportElem.style.cssText = "background-color:rgba(1,1,1,.5)"; +support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1; + +// define cache name and alpha properties +// for rgba and hsla spaces +each( spaces, function( spaceName, space ) { + space.cache = "_" + spaceName; + space.props.alpha = { + idx: 3, + type: "percent", + def: 1 + }; +}); + +function clamp( value, prop, allowEmpty ) { + var type = propTypes[ prop.type ] || {}; + + if ( value == null ) { + return (allowEmpty || !prop.def) ? null : prop.def; + } + + // ~~ is an short way of doing floor for positive numbers + value = type.floor ? ~~value : parseFloat( value ); + + // IE will pass in empty strings as value for alpha, + // which will hit this case + if ( isNaN( value ) ) { + return prop.def; + } + + if ( type.mod ) { + // we add mod before modding to make sure that negatives values + // get converted properly: -10 -> 350 + return (value + type.mod) % type.mod; + } + + // for now all property types without mod have min and max + return 0 > value ? 0 : type.max < value ? type.max : value; +} + +function stringParse( string ) { + var inst = color(), + rgba = inst._rgba = []; + + string = string.toLowerCase(); + + each( stringParsers, function( i, parser ) { + var parsed, + match = parser.re.exec( string ), + values = match && parser.parse( match ), + spaceName = parser.space || "rgba"; + + if ( values ) { + parsed = inst[ spaceName ]( values ); + + // if this was an rgba parse the assignment might happen twice + // oh well.... + inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ]; + rgba = inst._rgba = parsed._rgba; + + // exit each( stringParsers ) here because we matched + return false; + } + }); + + // Found a stringParser that handled it + if ( rgba.length ) { + + // if this came from a parsed string, force "transparent" when alpha is 0 + // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0) + if ( rgba.join() === "0,0,0,0" ) { + jQuery.extend( rgba, colors.transparent ); + } + return inst; + } + + // named colors + return colors[ string ]; +} + +color.fn = jQuery.extend( color.prototype, { + parse: function( red, green, blue, alpha ) { + if ( red === undefined ) { + this._rgba = [ null, null, null, null ]; + return this; + } + if ( red.jquery || red.nodeType ) { + red = jQuery( red ).css( green ); + green = undefined; + } + + var inst = this, + type = jQuery.type( red ), + rgba = this._rgba = []; + + // more than 1 argument specified - assume ( red, green, blue, alpha ) + if ( green !== undefined ) { + red = [ red, green, blue, alpha ]; + type = "array"; + } + + if ( type === "string" ) { + return this.parse( stringParse( red ) || colors._default ); + } + + if ( type === "array" ) { + each( spaces.rgba.props, function( key, prop ) { + rgba[ prop.idx ] = clamp( red[ prop.idx ], prop ); + }); + return this; + } + + if ( type === "object" ) { + if ( red instanceof color ) { + each( spaces, function( spaceName, space ) { + if ( red[ space.cache ] ) { + inst[ space.cache ] = red[ space.cache ].slice(); + } + }); + } else { + each( spaces, function( spaceName, space ) { + var cache = space.cache; + each( space.props, function( key, prop ) { + + // if the cache doesn't exist, and we know how to convert + if ( !inst[ cache ] && space.to ) { + + // if the value was null, we don't need to copy it + // if the key was alpha, we don't need to copy it either + if ( key === "alpha" || red[ key ] == null ) { + return; + } + inst[ cache ] = space.to( inst._rgba ); + } + + // this is the only case where we allow nulls for ALL properties. + // call clamp with alwaysAllowEmpty + inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true ); + }); + + // everything defined but alpha? + if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) { + // use the default of 1 + inst[ cache ][ 3 ] = 1; + if ( space.from ) { + inst._rgba = space.from( inst[ cache ] ); + } + } + }); + } + return this; + } + }, + is: function( compare ) { + var is = color( compare ), + same = true, + inst = this; + + each( spaces, function( _, space ) { + var localCache, + isCache = is[ space.cache ]; + if (isCache) { + localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || []; + each( space.props, function( _, prop ) { + if ( isCache[ prop.idx ] != null ) { + same = ( isCache[ prop.idx ] === localCache[ prop.idx ] ); + return same; + } + }); + } + return same; + }); + return same; + }, + _space: function() { + var used = [], + inst = this; + each( spaces, function( spaceName, space ) { + if ( inst[ space.cache ] ) { + used.push( spaceName ); + } + }); + return used.pop(); + }, + transition: function( other, distance ) { + var end = color( other ), + spaceName = end._space(), + space = spaces[ spaceName ], + startColor = this.alpha() === 0 ? color( "transparent" ) : this, + start = startColor[ space.cache ] || space.to( startColor._rgba ), + result = start.slice(); + + end = end[ space.cache ]; + each( space.props, function( key, prop ) { + var index = prop.idx, + startValue = start[ index ], + endValue = end[ index ], + type = propTypes[ prop.type ] || {}; + + // if null, don't override start value + if ( endValue === null ) { + return; + } + // if null - use end + if ( startValue === null ) { + result[ index ] = endValue; + } else { + if ( type.mod ) { + if ( endValue - startValue > type.mod / 2 ) { + startValue += type.mod; + } else if ( startValue - endValue > type.mod / 2 ) { + startValue -= type.mod; + } + } + result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop ); + } + }); + return this[ spaceName ]( result ); + }, + blend: function( opaque ) { + // if we are already opaque - return ourself + if ( this._rgba[ 3 ] === 1 ) { + return this; + } + + var rgb = this._rgba.slice(), + a = rgb.pop(), + blend = color( opaque )._rgba; + + return color( jQuery.map( rgb, function( v, i ) { + return ( 1 - a ) * blend[ i ] + a * v; + })); + }, + toRgbaString: function() { + var prefix = "rgba(", + rgba = jQuery.map( this._rgba, function( v, i ) { + return v == null ? ( i > 2 ? 1 : 0 ) : v; + }); + + if ( rgba[ 3 ] === 1 ) { + rgba.pop(); + prefix = "rgb("; + } + + return prefix + rgba.join() + ")"; + }, + toHslaString: function() { + var prefix = "hsla(", + hsla = jQuery.map( this.hsla(), function( v, i ) { + if ( v == null ) { + v = i > 2 ? 1 : 0; + } + + // catch 1 and 2 + if ( i && i < 3 ) { + v = Math.round( v * 100 ) + "%"; + } + return v; + }); + + if ( hsla[ 3 ] === 1 ) { + hsla.pop(); + prefix = "hsl("; + } + return prefix + hsla.join() + ")"; + }, + toHexString: function( includeAlpha ) { + var rgba = this._rgba.slice(), + alpha = rgba.pop(); + + if ( includeAlpha ) { + rgba.push( ~~( alpha * 255 ) ); + } + + return "#" + jQuery.map( rgba, function( v ) { + + // default to 0 when nulls exist + v = ( v || 0 ).toString( 16 ); + return v.length === 1 ? "0" + v : v; + }).join(""); + }, + toString: function() { + return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString(); + } +}); +color.fn.parse.prototype = color.fn; + +// hsla conversions adapted from: +// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021 + +function hue2rgb( p, q, h ) { + h = ( h + 1 ) % 1; + if ( h * 6 < 1 ) { + return p + (q - p) * h * 6; + } + if ( h * 2 < 1) { + return q; + } + if ( h * 3 < 2 ) { + return p + (q - p) * ((2/3) - h) * 6; + } + return p; +} + +spaces.hsla.to = function ( rgba ) { + if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) { + return [ null, null, null, rgba[ 3 ] ]; + } + var r = rgba[ 0 ] / 255, + g = rgba[ 1 ] / 255, + b = rgba[ 2 ] / 255, + a = rgba[ 3 ], + max = Math.max( r, g, b ), + min = Math.min( r, g, b ), + diff = max - min, + add = max + min, + l = add * 0.5, + h, s; + + if ( min === max ) { + h = 0; + } else if ( r === max ) { + h = ( 60 * ( g - b ) / diff ) + 360; + } else if ( g === max ) { + h = ( 60 * ( b - r ) / diff ) + 120; + } else { + h = ( 60 * ( r - g ) / diff ) + 240; + } + + // chroma (diff) == 0 means greyscale which, by definition, saturation = 0% + // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add) + if ( diff === 0 ) { + s = 0; + } else if ( l <= 0.5 ) { + s = diff / add; + } else { + s = diff / ( 2 - add ); + } + return [ Math.round(h) % 360, s, l, a == null ? 1 : a ]; +}; + +spaces.hsla.from = function ( hsla ) { + if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) { + return [ null, null, null, hsla[ 3 ] ]; + } + var h = hsla[ 0 ] / 360, + s = hsla[ 1 ], + l = hsla[ 2 ], + a = hsla[ 3 ], + q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s, + p = 2 * l - q; + + return [ + Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ), + Math.round( hue2rgb( p, q, h ) * 255 ), + Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ), + a + ]; +}; + + +each( spaces, function( spaceName, space ) { + var props = space.props, + cache = space.cache, + to = space.to, + from = space.from; + + // makes rgba() and hsla() + color.fn[ spaceName ] = function( value ) { + + // generate a cache for this space if it doesn't exist + if ( to && !this[ cache ] ) { + this[ cache ] = to( this._rgba ); + } + if ( value === undefined ) { + return this[ cache ].slice(); + } + + var ret, + type = jQuery.type( value ), + arr = ( type === "array" || type === "object" ) ? value : arguments, + local = this[ cache ].slice(); + + each( props, function( key, prop ) { + var val = arr[ type === "object" ? key : prop.idx ]; + if ( val == null ) { + val = local[ prop.idx ]; + } + local[ prop.idx ] = clamp( val, prop ); + }); + + if ( from ) { + ret = color( from( local ) ); + ret[ cache ] = local; + return ret; + } else { + return color( local ); + } + }; + + // makes red() green() blue() alpha() hue() saturation() lightness() + each( props, function( key, prop ) { + // alpha is included in more than one space + if ( color.fn[ key ] ) { + return; + } + color.fn[ key ] = function( value ) { + var vtype = jQuery.type( value ), + fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ), + local = this[ fn ](), + cur = local[ prop.idx ], + match; + + if ( vtype === "undefined" ) { + return cur; + } + + if ( vtype === "function" ) { + value = value.call( this, cur ); + vtype = jQuery.type( value ); + } + if ( value == null && prop.empty ) { + return this; + } + if ( vtype === "string" ) { + match = rplusequals.exec( value ); + if ( match ) { + value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 ); + } + } + local[ prop.idx ] = value; + return this[ fn ]( local ); + }; + }); +}); + +// add cssHook and .fx.step function for each named hook. +// accept a space separated string of properties +color.hook = function( hook ) { + var hooks = hook.split( " " ); + each( hooks, function( i, hook ) { + jQuery.cssHooks[ hook ] = { + set: function( elem, value ) { + var parsed, curElem, + backgroundColor = ""; + + if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) { + value = color( parsed || value ); + if ( !support.rgba && value._rgba[ 3 ] !== 1 ) { + curElem = hook === "backgroundColor" ? elem.parentNode : elem; + while ( + (backgroundColor === "" || backgroundColor === "transparent") && + curElem && curElem.style + ) { + try { + backgroundColor = jQuery.css( curElem, "backgroundColor" ); + curElem = curElem.parentNode; + } catch ( e ) { + } + } + + value = value.blend( backgroundColor && backgroundColor !== "transparent" ? + backgroundColor : + "_default" ); + } + + value = value.toRgbaString(); + } + try { + elem.style[ hook ] = value; + } catch( e ) { + // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit' + } + } + }; + jQuery.fx.step[ hook ] = function( fx ) { + if ( !fx.colorInit ) { + fx.start = color( fx.elem, hook ); + fx.end = color( fx.end ); + fx.colorInit = true; + } + jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) ); + }; + }); + +}; + +color.hook( stepHooks ); + +jQuery.cssHooks.borderColor = { + expand: function( value ) { + var expanded = {}; + + each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) { + expanded[ "border" + part + "Color" ] = value; + }); + return expanded; + } +}; + +// Basic color names only. +// Usage of any of the other color names requires adding yourself or including +// jquery.color.svg-names.js. +colors = jQuery.Color.names = { + // 4.1. Basic color keywords + aqua: "#00ffff", + black: "#000000", + blue: "#0000ff", + fuchsia: "#ff00ff", + gray: "#808080", + green: "#008000", + lime: "#00ff00", + maroon: "#800000", + navy: "#000080", + olive: "#808000", + purple: "#800080", + red: "#ff0000", + silver: "#c0c0c0", + teal: "#008080", + white: "#ffffff", + yellow: "#ffff00", + + // 4.2.3. "transparent" color keyword + transparent: [ null, null, null, 0 ], + + _default: "#ffffff" +}; + +})( jQuery ); + + +/******************************************************************************/ +/****************************** CLASS ANIMATIONS ******************************/ +/******************************************************************************/ +(function() { + +var classAnimationActions = [ "add", "remove", "toggle" ], + shorthandStyles = { + border: 1, + borderBottom: 1, + borderColor: 1, + borderLeft: 1, + borderRight: 1, + borderTop: 1, + borderWidth: 1, + margin: 1, + padding: 1 + }; + +$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) { + $.fx.step[ prop ] = function( fx ) { + if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) { + jQuery.style( fx.elem, prop, fx.end ); + fx.setAttr = true; + } + }; +}); + +function getElementStyles( elem ) { + var key, len, + style = elem.ownerDocument.defaultView ? + elem.ownerDocument.defaultView.getComputedStyle( elem, null ) : + elem.currentStyle, + styles = {}; + + if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) { + len = style.length; + while ( len-- ) { + key = style[ len ]; + if ( typeof style[ key ] === "string" ) { + styles[ $.camelCase( key ) ] = style[ key ]; + } + } + // support: Opera, IE <9 + } else { + for ( key in style ) { + if ( typeof style[ key ] === "string" ) { + styles[ key ] = style[ key ]; + } + } + } + + return styles; +} + + +function styleDifference( oldStyle, newStyle ) { + var diff = {}, + name, value; + + for ( name in newStyle ) { + value = newStyle[ name ]; + if ( oldStyle[ name ] !== value ) { + if ( !shorthandStyles[ name ] ) { + if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) { + diff[ name ] = value; + } + } + } + } + + return diff; +} + +// support: jQuery <1.8 +if ( !$.fn.addBack ) { + $.fn.addBack = function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + }; +} + +$.effects.animateClass = function( value, duration, easing, callback ) { + var o = $.speed( duration, easing, callback ); + + return this.queue( function() { + var animated = $( this ), + baseClass = animated.attr( "class" ) || "", + applyClassChange, + allAnimations = o.children ? animated.find( "*" ).addBack() : animated; + + // map the animated objects to store the original styles. + allAnimations = allAnimations.map(function() { + var el = $( this ); + return { + el: el, + start: getElementStyles( this ) + }; + }); + + // apply class change + applyClassChange = function() { + $.each( classAnimationActions, function(i, action) { + if ( value[ action ] ) { + animated[ action + "Class" ]( value[ action ] ); + } + }); + }; + applyClassChange(); + + // map all animated objects again - calculate new styles and diff + allAnimations = allAnimations.map(function() { + this.end = getElementStyles( this.el[ 0 ] ); + this.diff = styleDifference( this.start, this.end ); + return this; + }); + + // apply original class + animated.attr( "class", baseClass ); + + // map all animated objects again - this time collecting a promise + allAnimations = allAnimations.map(function() { + var styleInfo = this, + dfd = $.Deferred(), + opts = $.extend({}, o, { + queue: false, + complete: function() { + dfd.resolve( styleInfo ); + } + }); + + this.el.animate( this.diff, opts ); + return dfd.promise(); + }); + + // once all animations have completed: + $.when.apply( $, allAnimations.get() ).done(function() { + + // set the final class + applyClassChange(); + + // for each animated element, + // clear all css properties that were animated + $.each( arguments, function() { + var el = this.el; + $.each( this.diff, function(key) { + el.css( key, "" ); + }); + }); + + // this is guarnteed to be there if you use jQuery.speed() + // it also handles dequeuing the next anim... + o.complete.call( animated[ 0 ] ); + }); + }); +}; + +$.fn.extend({ + addClass: (function( orig ) { + return function( classNames, speed, easing, callback ) { + return speed ? + $.effects.animateClass.call( this, + { add: classNames }, speed, easing, callback ) : + orig.apply( this, arguments ); + }; + })( $.fn.addClass ), + + removeClass: (function( orig ) { + return function( classNames, speed, easing, callback ) { + return arguments.length > 1 ? + $.effects.animateClass.call( this, + { remove: classNames }, speed, easing, callback ) : + orig.apply( this, arguments ); + }; + })( $.fn.removeClass ), + + toggleClass: (function( orig ) { + return function( classNames, force, speed, easing, callback ) { + if ( typeof force === "boolean" || force === undefined ) { + if ( !speed ) { + // without speed parameter + return orig.apply( this, arguments ); + } else { + return $.effects.animateClass.call( this, + (force ? { add: classNames } : { remove: classNames }), + speed, easing, callback ); + } + } else { + // without force parameter + return $.effects.animateClass.call( this, + { toggle: classNames }, force, speed, easing ); + } + }; + })( $.fn.toggleClass ), + + switchClass: function( remove, add, speed, easing, callback) { + return $.effects.animateClass.call( this, { + add: add, + remove: remove + }, speed, easing, callback ); + } +}); + +})(); + +/******************************************************************************/ +/*********************************** EFFECTS **********************************/ +/******************************************************************************/ + +(function() { + +$.extend( $.effects, { + version: "1.10.3", + + // Saves a set of properties in a data storage + save: function( element, set ) { + for( var i=0; i < set.length; i++ ) { + if ( set[ i ] !== null ) { + element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] ); + } + } + }, + + // Restores a set of previously saved properties from a data storage + restore: function( element, set ) { + var val, i; + for( i=0; i < set.length; i++ ) { + if ( set[ i ] !== null ) { + val = element.data( dataSpace + set[ i ] ); + // support: jQuery 1.6.2 + // http://bugs.jquery.com/ticket/9917 + // jQuery 1.6.2 incorrectly returns undefined for any falsy value. + // We can't differentiate between "" and 0 here, so we just assume + // empty string since it's likely to be a more common value... + if ( val === undefined ) { + val = ""; + } + element.css( set[ i ], val ); + } + } + }, + + setMode: function( el, mode ) { + if (mode === "toggle") { + mode = el.is( ":hidden" ) ? "show" : "hide"; + } + return mode; + }, + + // Translates a [top,left] array into a baseline value + // this should be a little more flexible in the future to handle a string & hash + getBaseline: function( origin, original ) { + var y, x; + switch ( origin[ 0 ] ) { + case "top": y = 0; break; + case "middle": y = 0.5; break; + case "bottom": y = 1; break; + default: y = origin[ 0 ] / original.height; + } + switch ( origin[ 1 ] ) { + case "left": x = 0; break; + case "center": x = 0.5; break; + case "right": x = 1; break; + default: x = origin[ 1 ] / original.width; + } + return { + x: x, + y: y + }; + }, + + // Wraps the element around a wrapper that copies position properties + createWrapper: function( element ) { + + // if the element is already wrapped, return it + if ( element.parent().is( ".ui-effects-wrapper" )) { + return element.parent(); + } + + // wrap the element + var props = { + width: element.outerWidth(true), + height: element.outerHeight(true), + "float": element.css( "float" ) + }, + wrapper = $( "

" ) + .addClass( "ui-effects-wrapper" ) + .css({ + fontSize: "100%", + background: "transparent", + border: "none", + margin: 0, + padding: 0 + }), + // Store the size in case width/height are defined in % - Fixes #5245 + size = { + width: element.width(), + height: element.height() + }, + active = document.activeElement; + + // support: Firefox + // Firefox incorrectly exposes anonymous content + // https://bugzilla.mozilla.org/show_bug.cgi?id=561664 + try { + active.id; + } catch( e ) { + active = document.body; + } + + element.wrap( wrapper ); + + // Fixes #7595 - Elements lose focus when wrapped. + if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { + $( active ).focus(); + } + + wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element + + // transfer positioning properties to the wrapper + if ( element.css( "position" ) === "static" ) { + wrapper.css({ position: "relative" }); + element.css({ position: "relative" }); + } else { + $.extend( props, { + position: element.css( "position" ), + zIndex: element.css( "z-index" ) + }); + $.each([ "top", "left", "bottom", "right" ], function(i, pos) { + props[ pos ] = element.css( pos ); + if ( isNaN( parseInt( props[ pos ], 10 ) ) ) { + props[ pos ] = "auto"; + } + }); + element.css({ + position: "relative", + top: 0, + left: 0, + right: "auto", + bottom: "auto" + }); + } + element.css(size); + + return wrapper.css( props ).show(); + }, + + removeWrapper: function( element ) { + var active = document.activeElement; + + if ( element.parent().is( ".ui-effects-wrapper" ) ) { + element.parent().replaceWith( element ); + + // Fixes #7595 - Elements lose focus when wrapped. + if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { + $( active ).focus(); + } + } + + + return element; + }, + + setTransition: function( element, list, factor, value ) { + value = value || {}; + $.each( list, function( i, x ) { + var unit = element.cssUnit( x ); + if ( unit[ 0 ] > 0 ) { + value[ x ] = unit[ 0 ] * factor + unit[ 1 ]; + } + }); + return value; + } +}); + +// return an effect options object for the given parameters: +function _normalizeArguments( effect, options, speed, callback ) { + + // allow passing all options as the first parameter + if ( $.isPlainObject( effect ) ) { + options = effect; + effect = effect.effect; + } + + // convert to an object + effect = { effect: effect }; + + // catch (effect, null, ...) + if ( options == null ) { + options = {}; + } + + // catch (effect, callback) + if ( $.isFunction( options ) ) { + callback = options; + speed = null; + options = {}; + } + + // catch (effect, speed, ?) + if ( typeof options === "number" || $.fx.speeds[ options ] ) { + callback = speed; + speed = options; + options = {}; + } + + // catch (effect, options, callback) + if ( $.isFunction( speed ) ) { + callback = speed; + speed = null; + } + + // add options to effect + if ( options ) { + $.extend( effect, options ); + } + + speed = speed || options.duration; + effect.duration = $.fx.off ? 0 : + typeof speed === "number" ? speed : + speed in $.fx.speeds ? $.fx.speeds[ speed ] : + $.fx.speeds._default; + + effect.complete = callback || options.complete; + + return effect; +} + +function standardAnimationOption( option ) { + // Valid standard speeds (nothing, number, named speed) + if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) { + return true; + } + + // Invalid strings - treat as "normal" speed + if ( typeof option === "string" && !$.effects.effect[ option ] ) { + return true; + } + + // Complete callback + if ( $.isFunction( option ) ) { + return true; + } + + // Options hash (but not naming an effect) + if ( typeof option === "object" && !option.effect ) { + return true; + } + + // Didn't match any standard API + return false; +} + +$.fn.extend({ + effect: function( /* effect, options, speed, callback */ ) { + var args = _normalizeArguments.apply( this, arguments ), + mode = args.mode, + queue = args.queue, + effectMethod = $.effects.effect[ args.effect ]; + + if ( $.fx.off || !effectMethod ) { + // delegate to the original method (e.g., .show()) if possible + if ( mode ) { + return this[ mode ]( args.duration, args.complete ); + } else { + return this.each( function() { + if ( args.complete ) { + args.complete.call( this ); + } + }); + } + } + + function run( next ) { + var elem = $( this ), + complete = args.complete, + mode = args.mode; + + function done() { + if ( $.isFunction( complete ) ) { + complete.call( elem[0] ); + } + if ( $.isFunction( next ) ) { + next(); + } + } + + // If the element already has the correct final state, delegate to + // the core methods so the internal tracking of "olddisplay" works. + if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) { + elem[ mode ](); + done(); + } else { + effectMethod.call( elem[0], args, done ); + } + } + + return queue === false ? this.each( run ) : this.queue( queue || "fx", run ); + }, + + show: (function( orig ) { + return function( option ) { + if ( standardAnimationOption( option ) ) { + return orig.apply( this, arguments ); + } else { + var args = _normalizeArguments.apply( this, arguments ); + args.mode = "show"; + return this.effect.call( this, args ); + } + }; + })( $.fn.show ), + + hide: (function( orig ) { + return function( option ) { + if ( standardAnimationOption( option ) ) { + return orig.apply( this, arguments ); + } else { + var args = _normalizeArguments.apply( this, arguments ); + args.mode = "hide"; + return this.effect.call( this, args ); + } + }; + })( $.fn.hide ), + + toggle: (function( orig ) { + return function( option ) { + if ( standardAnimationOption( option ) || typeof option === "boolean" ) { + return orig.apply( this, arguments ); + } else { + var args = _normalizeArguments.apply( this, arguments ); + args.mode = "toggle"; + return this.effect.call( this, args ); + } + }; + })( $.fn.toggle ), + + // helper functions + cssUnit: function(key) { + var style = this.css( key ), + val = []; + + $.each( [ "em", "px", "%", "pt" ], function( i, unit ) { + if ( style.indexOf( unit ) > 0 ) { + val = [ parseFloat( style ), unit ]; + } + }); + return val; + } +}); + +})(); + +/******************************************************************************/ +/*********************************** EASING ***********************************/ +/******************************************************************************/ + +(function() { + +// based on easing equations from Robert Penner (http://www.robertpenner.com/easing) + +var baseEasings = {}; + +$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) { + baseEasings[ name ] = function( p ) { + return Math.pow( p, i + 2 ); + }; +}); + +$.extend( baseEasings, { + Sine: function ( p ) { + return 1 - Math.cos( p * Math.PI / 2 ); + }, + Circ: function ( p ) { + return 1 - Math.sqrt( 1 - p * p ); + }, + Elastic: function( p ) { + return p === 0 || p === 1 ? p : + -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 ); + }, + Back: function( p ) { + return p * p * ( 3 * p - 2 ); + }, + Bounce: function ( p ) { + var pow2, + bounce = 4; + + while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {} + return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 ); + } +}); + +$.each( baseEasings, function( name, easeIn ) { + $.easing[ "easeIn" + name ] = easeIn; + $.easing[ "easeOut" + name ] = function( p ) { + return 1 - easeIn( 1 - p ); + }; + $.easing[ "easeInOut" + name ] = function( p ) { + return p < 0.5 ? + easeIn( p * 2 ) / 2 : + 1 - easeIn( p * -2 + 2 ) / 2; + }; +}); + +})(); + +})(jQuery); +(function( $, undefined ) { + +$.effects.effect.highlight = function( o, done ) { + var elem = $( this ), + props = [ "backgroundImage", "backgroundColor", "opacity" ], + mode = $.effects.setMode( elem, o.mode || "show" ), + animation = { + backgroundColor: elem.css( "backgroundColor" ) + }; + + if (mode === "hide") { + animation.opacity = 0; + } + + $.effects.save( elem, props ); + + elem + .show() + .css({ + backgroundImage: "none", + backgroundColor: o.color || "#ffff99" + }) + .animate( animation, { + queue: false, + duration: o.duration, + easing: o.easing, + complete: function() { + if ( mode === "hide" ) { + elem.hide(); + } + $.effects.restore( elem, props ); + done(); + } + }); +}; + +})(jQuery); diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/jquery.js b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/jquery.js new file mode 100644 index 0000000..7971fea --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/jquery.js @@ -0,0 +1,9184 @@ +/*! + * jQuery JavaScript Library v2.1.1-rc2 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-04-21T20:27Z + */ + +(function( global, factory ) { + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper window is present, + // execute the factory and get jQuery + // For environments that do not inherently posses a window with a document + // (such as Node.js), expose a jQuery-making factory as module.exports + // This accentuates the need for the creation of a real window + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +// + +var arr = []; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + +var + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + version = "2.1.1-rc2", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android<4.1 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; + }, + + isPlainObject: function( obj ) { + // Not plain objects: + // - Any object or value whose internal [[Class]] property is not "[object Object]" + // - DOM nodes + // - window + if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.constructor && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + + // If the function hasn't returned already, we're confident that + // |obj| is a plain object, created by {} or constructed with new Object + return true; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + // Support: Android < 4.0, iOS < 6 (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + var script, + indirect = eval; + + code = jQuery.trim( code ); + + if ( code ) { + // If the code includes a valid, prologue position + // strict mode pragma, execute code by injecting a + // script tag into the document. + if ( code.indexOf("use strict") === 1 ) { + script = document.createElement("script"); + script.text = code; + document.head.appendChild( script ).parentNode.removeChild( script ); + } else { + // Otherwise, avoid the DOM node creation, insertion + // and removal by using an indirect global eval + indirect( code ); + } + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Support: Android<4.1 + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v1.10.19 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-04-18 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== strundefined && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, + doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", function() { + setDocument(); + }, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", function() { + setDocument(); + }); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { + div.innerHTML = "
"; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowclip^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (oldCache = outerCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + outerCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context !== document && context; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is no seed and only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome<14 +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) >= 0 ) !== not; + }); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); +}; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + len = this.length, + ret = [], + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +}); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof rootjQuery.ready !== "undefined" ? + rootjQuery.ready( selector ) : + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.extend({ + dir: function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; + }, + + sibling: function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; + } +}); + +jQuery.fn.extend({ + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter(function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.unique( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return elem.contentDocument || jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.unique( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +}); +var rnotwhite = (/\S+/g); + + + +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend({ + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +}); + +/** + * The ready event handler and self cleanup method + */ +function completed() { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + jQuery.ready(); +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + } else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + len ? fn( elems[0], key ) : emptyGet; +}; + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( owner ) { + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + /* jshint -W018 */ + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + +function Data() { + // Support: Android < 4, + // Old WebKit does not have Object.preventExtensions/freeze method, + // return new empty object instead with no [[set]] accessor + Object.defineProperty( this.cache = {}, 0, { + get: function() { + return {}; + } + }); + + this.expando = jQuery.expando + Math.random(); +} + +Data.uid = 1; +Data.accepts = jQuery.acceptData; + +Data.prototype = { + key: function( owner ) { + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return the key for a frozen object. + if ( !Data.accepts( owner ) ) { + return 0; + } + + var descriptor = {}, + // Check if the owner object already has a cache key + unlock = owner[ this.expando ]; + + // If not, create one + if ( !unlock ) { + unlock = Data.uid++; + + // Secure it in a non-enumerable, non-writable property + try { + descriptor[ this.expando ] = { value: unlock }; + Object.defineProperties( owner, descriptor ); + + // Support: Android < 4 + // Fallback to a less secure definition + } catch ( e ) { + descriptor[ this.expando ] = unlock; + jQuery.extend( owner, descriptor ); + } + } + + // Ensure the cache object + if ( !this.cache[ unlock ] ) { + this.cache[ unlock ] = {}; + } + + return unlock; + }, + set: function( owner, data, value ) { + var prop, + // There may be an unlock assigned to this node, + // if there is no entry for this "owner", create one inline + // and set the unlock as though an owner entry had always existed + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + // Handle: [ owner, key, value ] args + if ( typeof data === "string" ) { + cache[ data ] = value; + + // Handle: [ owner, { properties } ] args + } else { + // Fresh assignments by object are shallow copied + if ( jQuery.isEmptyObject( cache ) ) { + jQuery.extend( this.cache[ unlock ], data ); + // Otherwise, copy the properties one-by-one to the cache object + } else { + for ( prop in data ) { + cache[ prop ] = data[ prop ]; + } + } + } + return cache; + }, + get: function( owner, key ) { + // Either a valid cache is found, or will be created. + // New caches will be created and the unlock returned, + // allowing direct access to the newly created + // empty data object. A valid owner object must be provided. + var cache = this.cache[ this.key( owner ) ]; + + return key === undefined ? + cache : cache[ key ]; + }, + access: function( owner, key, value ) { + var stored; + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ((key && typeof key === "string") && value === undefined) ) { + + stored = this.get( owner, key ); + + return stored !== undefined ? + stored : this.get( owner, jQuery.camelCase(key) ); + } + + // [*]When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, name, camel, + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + if ( key === undefined ) { + this.cache[ unlock ] = {}; + + } else { + // Support array or space separated string of keys + if ( jQuery.isArray( key ) ) { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = key.concat( key.map( jQuery.camelCase ) ); + } else { + camel = jQuery.camelCase( key ); + // Try the string as a key before any manipulation + if ( key in cache ) { + name = [ key, camel ]; + } else { + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + name = camel; + name = name in cache ? + [ name ] : ( name.match( rnotwhite ) || [] ); + } + } + + i = name.length; + while ( i-- ) { + delete cache[ name[ i ] ]; + } + } + }, + hasData: function( owner ) { + return !jQuery.isEmptyObject( + this.cache[ owner[ this.expando ] ] || {} + ); + }, + discard: function( owner ) { + if ( owner[ this.expando ] ) { + delete this.cache[ owner[ this.expando ] ]; + } + } +}; +var data_priv = new Data(); + +var data_user = new Data(); + + + +/* + Implementation Summary + + 1. Enforce API surface and semantic compatibility with 1.9.x branch + 2. Improve the module's maintainability by reducing the storage + paths to a single mechanism. + 3. Use the same single mechanism to support "private" and "user" data. + 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) + 5. Avoid exposing implementation details on user objects (eg. expando properties) + 6. Provide a clear path for implementation upgrade to WeakMap in 2014 +*/ +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + data_user.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend({ + hasData: function( elem ) { + return data_user.hasData( elem ) || data_priv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return data_user.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + data_user.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to data_priv methods, these can be deprecated. + _data: function( elem, name, data ) { + return data_priv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + data_priv.remove( elem, name ); + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = data_user.get( elem ); + + if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + name = attrs[ i ].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + dataAttr( elem, name, data[ name ] ); + } + } + data_priv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + data_user.set( this, key ); + }); + } + + return access( this, function( value ) { + var data, + camelKey = jQuery.camelCase( key ); + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + // Attempt to get data from the cache + // with the key as-is + data = data_user.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to get data from the cache + // with the key camelized + data = data_user.get( elem, camelKey ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, camelKey, undefined ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each(function() { + // First, attempt to store a copy or reference of any + // data that might've been store with a camelCased key. + var data = data_user.get( this, camelKey ); + + // For HTML5 data-* attribute interop, we have to + // store property names with dashes in a camelCase form. + // This might not apply to all properties...* + data_user.set( this, camelKey, value ); + + // *... In the case of properties that might _actually_ + // have dashes, we need to also store a copy of that + // unchanged property. + if ( key.indexOf("-") !== -1 && data !== undefined ) { + data_user.set( this, key, value ); + } + }); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each(function() { + data_user.remove( this, key ); + }); + } +}); + + +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = data_priv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = data_priv.access( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return data_priv.get( elem, key ) || data_priv.access( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + data_priv.remove( elem, [ type + "queue", key ] ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = data_priv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); + }; + +var rcheckableType = (/^(?:checkbox|radio)$/i); + + + +(function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // #11217 - WebKit loses check when the name is after the checked attribute + // Support: Windows Web Apps (WWA) + // `name` and `type` need .setAttribute for WWA + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 + // old WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE9-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +})(); +var strundefined = typeof undefined; + + + +support.focusinBubbles = "onfocusin" in window; + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.hasData( elem ) && data_priv.get( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + data_priv.remove( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && jQuery.acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.disabled !== true || event.type !== "click" ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: Cordova 2.5 (WebKit) (#13255) + // All events should have a target; Cordova deviceready doesn't + if ( !event.target ) { + event.target = document; + } + + // Support: Safari 6.0+, Chrome < 28 + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } +}; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + // Support: Android < 4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && e.preventDefault ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && e.stopPropagation ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +// Support: Chrome 15+ +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// Create "bubbling" focus and blur events +// Support: Firefox, Chrome, Safari +if ( !support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + data_priv.remove( doc, fix ); + + } else { + data_priv.access( doc, fix, attaches ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); + + +var + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rhtml = /<|&#?\w+;/, + rnoInnerhtml = /<(?:script|style|link)/i, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /^$|\/(?:java|ecma)script/i, + rscriptTypeMasked = /^true\/(.*)/, + rcleanScript = /^\s*\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + + // Support: IE 9 + option: [ 1, "" ], + + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] + }; + +// Support: IE 9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: 1.x compatibility +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute("type"); + } + + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + data_priv.set( + elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) + ); + } +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( data_priv.hasData( src ) ) { + pdataOld = data_priv.access( src ); + pdataCur = data_priv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( data_user.hasData( src ) ) { + udataOld = data_user.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + data_user.set( dest, udataCur ); + } +} + +function getAll( context, tag ) { + var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : + context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : + []; + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; +} + +// Support: IE >= 9 +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Support: IE >= 9 + // Fix Cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Fixes #12346 + // Support: Webkit, IE + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; + }, + + cleanData: function( elems ) { + var data, elem, type, key, + special = jQuery.event.special, + i = 0; + + for ( ; (elem = elems[ i ]) !== undefined; i++ ) { + if ( jQuery.acceptData( elem ) ) { + key = elem[ data_priv.expando ]; + + if ( key && (data = data_priv.cache[ key ]) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + if ( data_priv.cache[ key ] ) { + // Discard any remaining `private` data + delete data_priv.cache[ key ]; + } + } + } + // Discard any remaining `user` data + delete data_user.cache[ elem[ data_user.expando ] ]; + } + } +}); + +jQuery.fn.extend({ + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each(function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + }); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map(function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + self.domManip( args, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); + } + } + } + } + } + } + + return this; + } +}); + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: QtWebKit + // .get() because push.apply(_, arraylike) throws + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + + +var iframe, + elemdisplay = {}; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? + + // Use of this method is a temporary fix (more like optmization) until something better comes along, + // since it was removed from specification and supported only in FF + style.display : jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = (iframe || jQuery( "'; + _iframe = temp.firstChild; + container.appendChild(_iframe); + + /* _iframe.onreadystatechange = function() { + console.info(_iframe.readyState); + };*/ + + Events.addEvent(_iframe, 'load', function() { // _iframe.onload doesn't work in IE lte 8 + var el; + + try { + el = _iframe.contentWindow.document || _iframe.contentDocument || window.frames[_iframe.id].document; + + // try to detect some standard error pages + if (/^4(0[0-9]|1[0-7]|2[2346])\s/.test(el.title)) { // test if title starts with 4xx HTTP error + _status = el.title.replace(/^(\d+).*$/, '$1'); + } else { + _status = 200; + // get result + _response = Basic.trim(el.body.innerHTML); + + // we need to fire these at least once + target.trigger({ + type: 'progress', + loaded: _response.length, + total: _response.length + }); + + if (blob) { // if we were uploading a file + target.trigger({ + type: 'uploadprogress', + loaded: blob.size || 1025, + total: blob.size || 1025 + }); + } + } + } catch (ex) { + if (Url.hasSameOrigin(meta.url)) { + // if response is sent with error code, iframe in IE gets redirected to res://ieframe.dll/http_x.htm + // which obviously results to cross domain error (wtf?) + _status = 404; + } else { + cleanup.call(target, function() { + target.trigger('error'); + }); + return; + } + } + + cleanup.call(target, function() { + target.trigger('load'); + }); + }, target.uid); + } // end createIframe + + // prepare data to be sent and convert if required + if (data instanceof FormData && data.hasBlob()) { + blob = data.getBlob(); + uid = blob.uid; + input = Dom.get(uid); + form = Dom.get(uid + '_form'); + if (!form) { + throw new x.DOMException(x.DOMException.NOT_FOUND_ERR); + } + } else { + uid = Basic.guid('uid_'); + + form = document.createElement('form'); + form.setAttribute('id', uid + '_form'); + form.setAttribute('method', meta.method); + form.setAttribute('enctype', 'multipart/form-data'); + form.setAttribute('encoding', 'multipart/form-data'); + form.setAttribute('target', uid + '_iframe'); + + I.getShimContainer().appendChild(form); + } + + if (data instanceof FormData) { + data.each(function(value, name) { + if (value instanceof Blob) { + if (input) { + input.setAttribute('name', name); + } + } else { + var hidden = document.createElement('input'); + + Basic.extend(hidden, { + type : 'hidden', + name : name, + value : value + }); + + // make sure that input[type="file"], if it's there, comes last + if (input) { + form.insertBefore(hidden, input); + } else { + form.appendChild(hidden); + } + } + }); + } + + // set destination url + form.setAttribute("action", meta.url); + + createIframe(); + form.submit(); + target.trigger('loadstart'); + }, + + getStatus: function() { + return _status; + }, + + getResponse: function(responseType) { + if ('json' === responseType) { + // strip off
..
tags that might be enclosing the response + if (Basic.typeOf(_response) === 'string' && !!window.JSON) { + try { + return JSON.parse(_response.replace(/^\s*]*>/, '').replace(/<\/pre>\s*$/, '')); + } catch (ex) { + return null; + } + } + } else if ('document' === responseType) { + + } + return _response; + }, + + abort: function() { + var target = this; + + if (_iframe && _iframe.contentWindow) { + if (_iframe.contentWindow.stop) { // FireFox/Safari/Chrome + _iframe.contentWindow.stop(); + } else if (_iframe.contentWindow.document.execCommand) { // IE + _iframe.contentWindow.document.execCommand('Stop'); + } else { + _iframe.src = "about:blank"; + } + } + + cleanup.call(this, function() { + // target.dispatchEvent('readystatechange'); + target.dispatchEvent('abort'); + }); + } + }); + } + + return (extensions.XMLHttpRequest = XMLHttpRequest); +}); + +// Included from: src/javascript/runtime/html4/image/Image.js + +/** + * Image.js + * + * Copyright 2013, Moxiecode Systems AB + * Released under GPL License. + * + * License: http://www.plupload.com/license + * Contributing: http://www.plupload.com/contributing + */ + +/** +@class moxie/runtime/html4/image/Image +@private +*/ +define("moxie/runtime/html4/image/Image", [ + "moxie/runtime/html4/Runtime", + "moxie/runtime/html5/image/Image" +], function(extensions, Image) { + return (extensions.Image = Image); +}); + +expose(["moxie/core/utils/Basic","moxie/core/I18n","moxie/core/utils/Mime","moxie/core/utils/Env","moxie/core/utils/Dom","moxie/core/Exceptions","moxie/core/EventTarget","moxie/core/utils/Encode","moxie/runtime/Runtime","moxie/runtime/RuntimeClient","moxie/file/Blob","moxie/file/File","moxie/file/FileInput","moxie/file/FileDrop","moxie/runtime/RuntimeTarget","moxie/file/FileReader","moxie/core/utils/Url","moxie/file/FileReaderSync","moxie/xhr/FormData","moxie/xhr/XMLHttpRequest","moxie/runtime/Transporter","moxie/image/Image","moxie/core/utils/Events"]); +})(this);/** + * o.js + * + * Copyright 2013, Moxiecode Systems AB + * Released under GPL License. + * + * License: http://www.plupload.com/license + * Contributing: http://www.plupload.com/contributing + */ + +/*global moxie:true */ + +/** +Globally exposed namespace with the most frequently used public classes and handy methods. + +@class o +@static +@private +*/ +(function() { + "use strict"; + + var o = {}, inArray = moxie.core.utils.Basic.inArray; + + // directly add some public classes + // (we do it dynamically here, since for custom builds we cannot know beforehand what modules were included) + (function addAlias(ns) { + var name, itemType; + for (name in ns) { + itemType = typeof(ns[name]); + if (itemType === 'object' && !~inArray(name, ['Exceptions', 'Env', 'Mime'])) { + addAlias(ns[name]); + } else if (itemType === 'function') { + o[name] = ns[name]; + } + } + })(window.moxie); + + // add some manually + o.Env = window.moxie.core.utils.Env; + o.Mime = window.moxie.core.utils.Mime; + o.Exceptions = window.moxie.core.Exceptions; + + // expose globally + window.mOxie = o; + if (!window.o) { + window.o = o; + } + return o; +})(); diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/pagedown.js b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/pagedown.js new file mode 100644 index 0000000..180c1d7 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/pagedown.js @@ -0,0 +1,2644 @@ +var Markdown; + +if (typeof exports === "object" && typeof require === "function") // we're in a CommonJS (e.g. Node.js) module + Markdown = exports; +else + Markdown = {}; + +// The following text is included for historical reasons, but should +// be taken with a pinch of salt; it's not all true anymore. + +// +// Wherever possible, Showdown is a straight, line-by-line port +// of the Perl version of Markdown. +// +// This is not a normal parser design; it's basically just a +// series of string substitutions. It's hard to read and +// maintain this way, but keeping Showdown close to the original +// design makes it easier to port new features. +// +// More importantly, Showdown behaves like markdown.pl in most +// edge cases. So web applications can do client-side preview +// in Javascript, and then build identical HTML on the server. +// +// This port needs the new RegExp functionality of ECMA 262, +// 3rd Edition (i.e. Javascript 1.5). Most modern web browsers +// should do fine. Even with the new regular expression features, +// We do a lot of work to emulate Perl's regex functionality. +// The tricky changes in this file mostly have the "attacklab:" +// label. Major or self-explanatory changes don't. +// +// Smart diff tools like Araxis Merge will be able to match up +// this file with markdown.pl in a useful way. A little tweaking +// helps: in a copy of markdown.pl, replace "#" with "//" and +// replace "$text" with "text". Be sure to ignore whitespace +// and line endings. +// + + +// +// Usage: +// +// var text = "Markdown *rocks*."; +// +// var converter = new Markdown.Converter(); +// var html = converter.makeHtml(text); +// +// alert(html); +// +// Note: move the sample code to the bottom of this +// file before uncommenting it. +// + +(function () { + + function identity(x) { return x; } + function returnFalse(x) { return false; } + + function HookCollection() { } + + HookCollection.prototype = { + + chain: function (hookname, func) { + var original = this[hookname]; + if (!original) + throw new Error("unknown hook " + hookname); + + if (original === identity) + this[hookname] = func; + else + this[hookname] = function (text) { + var args = Array.prototype.slice.call(arguments, 0); + args[0] = original.apply(null, args); + return func.apply(null, args); + }; + }, + set: function (hookname, func) { + if (!this[hookname]) + throw new Error("unknown hook " + hookname); + this[hookname] = func; + }, + addNoop: function (hookname) { + this[hookname] = identity; + }, + addFalse: function (hookname) { + this[hookname] = returnFalse; + } + }; + + Markdown.HookCollection = HookCollection; + + // g_urls and g_titles allow arbitrary user-entered strings as keys. This + // caused an exception (and hence stopped the rendering) when the user entered + // e.g. [push] or [__proto__]. Adding a prefix to the actual key prevents this + // (since no builtin property starts with "s_"). See + // http://meta.stackoverflow.com/questions/64655/strange-wmd-bug + // (granted, switching from Array() to Object() alone would have left only __proto__ + // to be a problem) + function SaveHash() { } + SaveHash.prototype = { + set: function (key, value) { + this["s_" + key] = value; + }, + get: function (key) { + return this["s_" + key]; + } + }; +})(); + +(function () { + + var util = {}, + position = {}, + ui = {}, + doc = window.document, + re = window.RegExp, + nav = window.navigator, + SETTINGS = { lineLength: 72 }, + + // Used to work around some browser bugs where we can't use feature testing. + uaSniffed = { + isIE: /msie/.test(nav.userAgent.toLowerCase()), + isIE_5or6: /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase()), + isOpera: /opera/.test(nav.userAgent.toLowerCase()) + }; + + var defaultsStrings = { + bold: "Strong Ctrl+B", + boldexample: "strong text", + + italic: "Emphasis Ctrl+I", + italicexample: "emphasized text", + + link: "Hyperlink Ctrl+L", + linkdescription: "enter link description here", + linkdialog: "

Insert Hyperlink

http://example.com/ \"optional title\"

", + linkname: null, + + quote: "Blockquote
Ctrl+Q", + quoteexample: "Blockquote", + + code: "Code Sample
 Ctrl+K",
+        codeexample: "enter code here",
+
+        image: "Image  Ctrl+G",
+        imagedescription: "enter image description here",
+        imagedialog: "

Insert Image

http://example.com/images/diagram.jpg \"optional title\"
Need
free image hosting?

", + imagename: null, + + olist: "Numbered List
    Ctrl+O", + ulist: "Bulleted List
      Ctrl+U", + litem: "List item", + + heading: "Heading

      /

      Ctrl+H", + headingexample: "Heading", + more: "More contents Ctrl+M", + + fullscreen: 'FullScreen Ctrl+J', + exitFullscreen: 'Exit FullScreen Ctrl+E', + fullscreenUnsupport: 'Sorry, the browser dont support fullscreen api', + + hr: "Horizontal Rule
      Ctrl+R", + + undo: "Undo - Ctrl+Z", + redo: "Redo - Ctrl+Y", + redomac: "Redo - Ctrl+Shift+Z", + + ok: "OK", + cancel: "Cancel", + + help: "Markdown Editing Help" + }; + + + // ------------------------------------------------------------------- + // YOUR CHANGES GO HERE + // + // I've tried to localize the things you are likely to change to + // this area. + // ------------------------------------------------------------------- + + // The default text that appears in the dialog input box when entering + // links. + var imageDefaultText = "http://"; + var linkDefaultText = "http://"; + + // ------------------------------------------------------------------- + // END OF YOUR CHANGES + // ------------------------------------------------------------------- + + // options, if given, can have the following properties: + // options.helpButton = { handler: yourEventHandler } + // options.strings = { italicexample: "slanted text" } + // `yourEventHandler` is the click handler for the help button. + // If `options.helpButton` isn't given, not help button is created. + // `options.strings` can have any or all of the same properties as + // `defaultStrings` above, so you can just override some string displayed + // to the user on a case-by-case basis, or translate all strings to + // a different language. + // + // For backwards compatibility reasons, the `options` argument can also + // be just the `helpButton` object, and `strings.help` can also be set via + // `helpButton.title`. This should be considered legacy. + // + // The constructed editor object has the methods: + // - getConverter() returns the markdown converter object that was passed to the constructor + // - run() actually starts the editor; should be called after all necessary plugins are registered. Calling this more than once is a no-op. + // - refreshPreview() forces the preview to be updated. This method is only available after run() was called. + Markdown.Editor = function (markdownConverter, idPostfix, options) { + + options = options || {}; + + if (typeof options.handler === "function") { //backwards compatible behavior + options = { helpButton: options }; + } + options.strings = options.strings || {}; + if (options.helpButton) { + options.strings.help = options.strings.help || options.helpButton.title; + } + var getString = function (identifier) { + var string = options.strings[identifier] || defaultsStrings[identifier]; + + if ('imagename' == identifier || 'linkname' == identifier) { + options.strings[identifier] = null; + } + + return string; + } + + idPostfix = idPostfix || ""; + + var hooks = this.hooks = new Markdown.HookCollection(); + hooks.addNoop("onPreviewRefresh"); // called with no arguments after the preview has been refreshed + hooks.addNoop("postBlockquoteCreation"); // called with the user's selection *after* the blockquote was created; should return the actual to-be-inserted text + hooks.addFalse("insertImageDialog"); /* called with one parameter: a callback to be called with the URL of the image. If the application creates + * its own image insertion dialog, this hook should return true, and the callback should be called with the chosen + * image url (or null if the user cancelled). If this hook returns false, the default dialog will be used. + */ + hooks.addFalse("insertLinkDialog"); /* called with one parameter: a callback to be called with the URL of the image. If the application creates + * its own image insertion dialog, this hook should return true, and the callback should be called with the chosen + * image url (or null if the user cancelled). If this hook returns false, the default dialog will be used. + */ + + hooks.addNoop("makeButton"); + + hooks.addNoop("enterFullScreen"); + hooks.addNoop("enterFakeFullScreen"); + hooks.addNoop("exitFullScreen"); + + this.getConverter = function () { return markdownConverter; } + + var that = this, + panels; + + this.run = function () { + if (panels) + return; // already initialized + + panels = new PanelCollection(idPostfix); + var commandManager = new CommandManager(hooks, getString); + var previewManager = new PreviewManager(markdownConverter, panels, function () { hooks.onPreviewRefresh(); }); + var undoManager, uiManager; + + if (!/\?noundo/.test(doc.location.href)) { + undoManager = new UndoManager(function () { + previewManager.refresh(); + if (uiManager) // not available on the first call + uiManager.setUndoRedoButtonStates(); + }, panels); + this.textOperation = function (f) { + undoManager.setCommandMode(); + f(); + that.refreshPreview(); + } + } + + fullScreenManager = new FullScreenManager(hooks, getString); + uiManager = new UIManager(idPostfix, panels, hooks, undoManager, previewManager, commandManager, fullScreenManager, options.helpButton, getString); + uiManager.setUndoRedoButtonStates(); + + var forceRefresh = that.refreshPreview = function () { previewManager.refresh(true); }; + + forceRefresh(); + }; + + } + + // before: contains all the text in the input box BEFORE the selection. + // after: contains all the text in the input box AFTER the selection. + function Chunks() { } + + // startRegex: a regular expression to find the start tag + // endRegex: a regular expresssion to find the end tag + Chunks.prototype.findTags = function (startRegex, endRegex) { + + var chunkObj = this; + var regex; + + if (startRegex) { + + regex = util.extendRegExp(startRegex, "", "$"); + + this.before = this.before.replace(regex, + function (match) { + chunkObj.startTag = chunkObj.startTag + match; + return ""; + }); + + regex = util.extendRegExp(startRegex, "^", ""); + + this.selection = this.selection.replace(regex, + function (match) { + chunkObj.startTag = chunkObj.startTag + match; + return ""; + }); + } + + if (endRegex) { + + regex = util.extendRegExp(endRegex, "", "$"); + + this.selection = this.selection.replace(regex, + function (match) { + chunkObj.endTag = match + chunkObj.endTag; + return ""; + }); + + regex = util.extendRegExp(endRegex, "^", ""); + + this.after = this.after.replace(regex, + function (match) { + chunkObj.endTag = match + chunkObj.endTag; + return ""; + }); + } + }; + + // If remove is false, the whitespace is transferred + // to the before/after regions. + // + // If remove is true, the whitespace disappears. + Chunks.prototype.trimWhitespace = function (remove) { + var beforeReplacer, afterReplacer, that = this; + if (remove) { + beforeReplacer = afterReplacer = ""; + } else { + beforeReplacer = function (s) { that.before += s; return ""; } + afterReplacer = function (s) { that.after = s + that.after; return ""; } + } + + this.selection = this.selection.replace(/^(\s*)/, beforeReplacer).replace(/(\s*)$/, afterReplacer); + }; + + + Chunks.prototype.skipLines = function (nLinesBefore, nLinesAfter, findExtraNewlines) { + + if (nLinesBefore === undefined) { + nLinesBefore = 1; + } + + if (nLinesAfter === undefined) { + nLinesAfter = 1; + } + + nLinesBefore++; + nLinesAfter++; + + var regexText; + var replacementText; + + // chrome bug ... documented at: http://meta.stackexchange.com/questions/63307/blockquote-glitch-in-editor-in-chrome-6-and-7/65985#65985 + if (navigator.userAgent.match(/Chrome/)) { + "X".match(/()./); + } + + this.selection = this.selection.replace(/(^\n*)/, ""); + + this.startTag = this.startTag + re.$1; + + this.selection = this.selection.replace(/(\n*$)/, ""); + this.endTag = this.endTag + re.$1; + this.startTag = this.startTag.replace(/(^\n*)/, ""); + this.before = this.before + re.$1; + this.endTag = this.endTag.replace(/(\n*$)/, ""); + this.after = this.after + re.$1; + + if (this.before) { + + regexText = replacementText = ""; + + while (nLinesBefore--) { + regexText += "\\n?"; + replacementText += "\n"; + } + + if (findExtraNewlines) { + regexText = "\\n*"; + } + this.before = this.before.replace(new re(regexText + "$", ""), replacementText); + } + + if (this.after) { + + regexText = replacementText = ""; + + while (nLinesAfter--) { + regexText += "\\n?"; + replacementText += "\n"; + } + if (findExtraNewlines) { + regexText = "\\n*"; + } + + this.after = this.after.replace(new re(regexText, ""), replacementText); + } + }; + + // end of Chunks + + // A collection of the important regions on the page. + // Cached so we don't have to keep traversing the DOM. + // Also holds ieCachedRange and ieCachedScrollTop, where necessary; working around + // this issue: + // Internet explorer has problems with CSS sprite buttons that use HTML + // lists. When you click on the background image "button", IE will + // select the non-existent link text and discard the selection in the + // textarea. The solution to this is to cache the textarea selection + // on the button's mousedown event and set a flag. In the part of the + // code where we need to grab the selection, we check for the flag + // and, if it's set, use the cached area instead of querying the + // textarea. + // + // This ONLY affects Internet Explorer (tested on versions 6, 7 + // and 8) and ONLY on button clicks. Keyboard shortcuts work + // normally since the focus never leaves the textarea. + function PanelCollection(postfix) { + this.buttonBar = doc.getElementById("wmd-button-bar" + postfix); + this.preview = doc.getElementById("wmd-preview" + postfix); + this.input = doc.getElementById("text"); + }; + + // Returns true if the DOM element is visible, false if it's hidden. + // Checks if display is anything other than none. + util.isVisible = function (elem) { + + if (window.getComputedStyle) { + // Most browsers + return window.getComputedStyle(elem, null).getPropertyValue("display") !== "none"; + } + else if (elem.currentStyle) { + // IE + return elem.currentStyle["display"] !== "none"; + } + }; + + + // Adds a listener callback to a DOM element which is fired on a specified + // event. + util.addEvent = function (elem, event, listener) { + if (elem.attachEvent) { + // IE only. The "on" is mandatory. + elem.attachEvent("on" + event, listener); + } + else { + // Other browsers. + elem.addEventListener(event, listener, false); + } + }; + + + // Removes a listener callback from a DOM element which is fired on a specified + // event. + util.removeEvent = function (elem, event, listener) { + if (elem.detachEvent) { + // IE only. The "on" is mandatory. + elem.detachEvent("on" + event, listener); + } + else { + // Other browsers. + elem.removeEventListener(event, listener, false); + } + }; + + // Converts \r\n and \r to \n. + util.fixEolChars = function (text) { + text = text.replace(/\r\n/g, "\n"); + text = text.replace(/\r/g, "\n"); + return text; + }; + + // Extends a regular expression. Returns a new RegExp + // using pre + regex + post as the expression. + // Used in a few functions where we have a base + // expression and we want to pre- or append some + // conditions to it (e.g. adding "$" to the end). + // The flags are unchanged. + // + // regex is a RegExp, pre and post are strings. + util.extendRegExp = function (regex, pre, post) { + + if (pre === null || pre === undefined) { + pre = ""; + } + if (post === null || post === undefined) { + post = ""; + } + + var pattern = regex.toString(); + var flags; + + // Replace the flags with empty space and store them. + pattern = pattern.replace(/\/([gim]*)$/, function (wholeMatch, flagsPart) { + flags = flagsPart; + return ""; + }); + + // Remove the slash delimiters on the regular expression. + pattern = pattern.replace(/(^\/|\/$)/g, ""); + pattern = pre + pattern + post; + + return new re(pattern, flags); + } + + // UNFINISHED + // The assignment in the while loop makes jslint cranky. + // I'll change it to a better loop later. + position.getTop = function (elem, isInner) { + var result = elem.offsetTop; + if (!isInner) { + while (elem = elem.offsetParent) { + result += elem.offsetTop; + } + } + return result; + }; + + position.getHeight = function (elem) { + return elem.offsetHeight || elem.scrollHeight; + }; + + position.getWidth = function (elem) { + return elem.offsetWidth || elem.scrollWidth; + }; + + position.getPageSize = function () { + + var scrollWidth, scrollHeight; + var innerWidth, innerHeight; + + // It's not very clear which blocks work with which browsers. + if (self.innerHeight && self.scrollMaxY) { + scrollWidth = doc.body.scrollWidth; + scrollHeight = self.innerHeight + self.scrollMaxY; + } + else if (doc.body.scrollHeight > doc.body.offsetHeight) { + scrollWidth = doc.body.scrollWidth; + scrollHeight = doc.body.scrollHeight; + } + else { + scrollWidth = doc.body.offsetWidth; + scrollHeight = doc.body.offsetHeight; + } + + if (self.innerHeight) { + // Non-IE browser + innerWidth = self.innerWidth; + innerHeight = self.innerHeight; + } + else if (doc.documentElement && doc.documentElement.clientHeight) { + // Some versions of IE (IE 6 w/ a DOCTYPE declaration) + innerWidth = doc.documentElement.clientWidth; + innerHeight = doc.documentElement.clientHeight; + } + else if (doc.body) { + // Other versions of IE + innerWidth = doc.body.clientWidth; + innerHeight = doc.body.clientHeight; + } + + var maxWidth = Math.max(scrollWidth, innerWidth); + var maxHeight = Math.max(scrollHeight, innerHeight); + return [maxWidth, maxHeight, innerWidth, innerHeight]; + }; + + // Handles pushing and popping TextareaStates for undo/redo commands. + // I should rename the stack variables to list. + function UndoManager(callback, panels) { + + var undoObj = this; + var undoStack = []; // A stack of undo states + var stackPtr = 0; // The index of the current state + var mode = "none"; + var lastState; // The last state + var timer; // The setTimeout handle for cancelling the timer + var inputStateObj; + + // Set the mode for later logic steps. + var setMode = function (newMode, noSave) { + if (mode != newMode) { + mode = newMode; + if (!noSave) { + saveState(); + } + } + + if (!uaSniffed.isIE || mode != "moving") { + timer = setTimeout(refreshState, 1); + } + else { + inputStateObj = null; + } + }; + + var refreshState = function (isInitialState) { + inputStateObj = new TextareaState(panels, isInitialState); + timer = undefined; + }; + + this.setCommandMode = function () { + mode = "command"; + saveState(); + timer = setTimeout(refreshState, 0); + }; + + this.canUndo = function () { + return stackPtr > 1; + }; + + this.canRedo = function () { + if (undoStack[stackPtr + 1]) { + return true; + } + return false; + }; + + // Removes the last state and restores it. + this.undo = function () { + + if (undoObj.canUndo()) { + if (lastState) { + // What about setting state -1 to null or checking for undefined? + lastState.restore(); + lastState = null; + } + else { + undoStack[stackPtr] = new TextareaState(panels); + undoStack[--stackPtr].restore(); + + if (callback) { + callback(); + } + } + } + + mode = "none"; + panels.input.focus(); + refreshState(); + }; + + // Redo an action. + this.redo = function () { + + if (undoObj.canRedo()) { + + undoStack[++stackPtr].restore(); + + if (callback) { + callback(); + } + } + + mode = "none"; + panels.input.focus(); + refreshState(); + }; + + // Push the input area state to the stack. + var saveState = function () { + var currState = inputStateObj || new TextareaState(panels); + + if (!currState) { + return false; + } + if (mode == "moving") { + if (!lastState) { + lastState = currState; + } + return; + } + if (lastState) { + if (undoStack[stackPtr - 1].text != lastState.text) { + undoStack[stackPtr++] = lastState; + } + lastState = null; + } + undoStack[stackPtr++] = currState; + undoStack[stackPtr + 1] = null; + if (callback) { + callback(); + } + }; + + var handleCtrlYZ = function (event) { + + var handled = false; + + if ((event.ctrlKey || event.metaKey) && !event.altKey) { + + // IE and Opera do not support charCode. + var keyCode = event.charCode || event.keyCode; + var keyCodeChar = String.fromCharCode(keyCode); + + switch (keyCodeChar.toLowerCase()) { + + case "y": + undoObj.redo(); + handled = true; + break; + + case "z": + if (!event.shiftKey) { + undoObj.undo(); + } + else { + undoObj.redo(); + } + handled = true; + break; + } + } + + if (handled) { + if (event.preventDefault) { + event.preventDefault(); + } + if (window.event) { + window.event.returnValue = false; + } + return; + } + }; + + // Set the mode depending on what is going on in the input area. + var handleModeChange = function (event) { + + if (!event.ctrlKey && !event.metaKey) { + + var keyCode = event.keyCode; + + if ((keyCode >= 33 && keyCode <= 40) || (keyCode >= 63232 && keyCode <= 63235)) { + // 33 - 40: page up/dn and arrow keys + // 63232 - 63235: page up/dn and arrow keys on safari + setMode("moving"); + } + else if (keyCode == 8 || keyCode == 46 || keyCode == 127) { + // 8: backspace + // 46: delete + // 127: delete + setMode("deleting"); + } + else if (keyCode == 13) { + // 13: Enter + setMode("newlines"); + } + else if (keyCode == 27) { + // 27: escape + setMode("escape"); + } + else if ((keyCode < 16 || keyCode > 20) && keyCode != 91) { + // 16-20 are shift, etc. + // 91: left window key + // I think this might be a little messed up since there are + // a lot of nonprinting keys above 20. + setMode("typing"); + } + } + }; + + var setEventHandlers = function () { + util.addEvent(panels.input, "keypress", function (event) { + // keyCode 89: y + // keyCode 90: z + if ((event.ctrlKey || event.metaKey) && !event.altKey && (event.keyCode == 89 || event.keyCode == 90)) { + event.preventDefault(); + } + }); + + var handlePaste = function () { + if (uaSniffed.isIE || (inputStateObj && inputStateObj.text != panels.input.value)) { + if (timer == undefined) { + mode = "paste"; + saveState(); + refreshState(); + } + } + }; + + util.addEvent(panels.input, "keydown", handleCtrlYZ); + util.addEvent(panels.input, "keydown", handleModeChange); + util.addEvent(panels.input, "mousedown", function () { + setMode("moving"); + }); + + panels.input.onpaste = handlePaste; + panels.input.ondrop = handlePaste; + }; + + var init = function () { + setEventHandlers(); + refreshState(true); + saveState(); + }; + + init(); + } + + // end of UndoManager + + // The input textarea state/contents. + // This is used to implement undo/redo by the undo manager. + function TextareaState(panels, isInitialState) { + + // Aliases + var stateObj = this; + var inputArea = panels.input; + this.init = function () { + if (!util.isVisible(inputArea)) { + return; + } + if (!isInitialState && doc.activeElement && doc.activeElement !== inputArea) { // this happens when tabbing out of the input box + return; + } + + this.setInputAreaSelectionStartEnd(); + this.scrollTop = inputArea.scrollTop; + if (!this.text && inputArea.selectionStart || inputArea.selectionStart === 0) { + this.text = inputArea.value; + } + + } + + // Sets the selected text in the input box after we've performed an + // operation. + this.setInputAreaSelection = function () { + + if (!util.isVisible(inputArea)) { + return; + } + + if (inputArea.selectionStart !== undefined && !uaSniffed.isOpera) { + + inputArea.focus(); + inputArea.selectionStart = stateObj.start; + inputArea.selectionEnd = stateObj.end; + inputArea.scrollTop = stateObj.scrollTop; + } + else if (doc.selection) { + + if (doc.activeElement && doc.activeElement !== inputArea) { + return; + } + + inputArea.focus(); + var range = inputArea.createTextRange(); + range.moveStart("character", -inputArea.value.length); + range.moveEnd("character", -inputArea.value.length); + range.moveEnd("character", stateObj.end); + range.moveStart("character", stateObj.start); + range.select(); + } + }; + + this.setInputAreaSelectionStartEnd = function () { + + if (!panels.ieCachedRange && (inputArea.selectionStart || inputArea.selectionStart === 0)) { + + stateObj.start = inputArea.selectionStart; + stateObj.end = inputArea.selectionEnd; + } + else if (doc.selection) { + + stateObj.text = util.fixEolChars(inputArea.value); + + // IE loses the selection in the textarea when buttons are + // clicked. On IE we cache the selection. Here, if something is cached, + // we take it. + var range = panels.ieCachedRange || doc.selection.createRange(); + + var fixedRange = util.fixEolChars(range.text); + var marker = "\x07"; + var markedRange = marker + fixedRange + marker; + range.text = markedRange; + var inputText = util.fixEolChars(inputArea.value); + + range.moveStart("character", -markedRange.length); + range.text = fixedRange; + + stateObj.start = inputText.indexOf(marker); + stateObj.end = inputText.lastIndexOf(marker) - marker.length; + + var len = stateObj.text.length - util.fixEolChars(inputArea.value).length; + + if (len) { + range.moveStart("character", -fixedRange.length); + while (len--) { + fixedRange += "\n"; + stateObj.end += 1; + } + range.text = fixedRange; + } + + if (panels.ieCachedRange) + stateObj.scrollTop = panels.ieCachedScrollTop; // this is set alongside with ieCachedRange + + panels.ieCachedRange = null; + + this.setInputAreaSelection(); + } + }; + + // Restore this state into the input area. + this.restore = function () { + + if (stateObj.text != undefined && stateObj.text != inputArea.value) { + inputArea.value = stateObj.text; + } + this.setInputAreaSelection(); + inputArea.scrollTop = stateObj.scrollTop; + }; + + // Gets a collection of HTML chunks from the inptut textarea. + this.getChunks = function () { + + var chunk = new Chunks(); + chunk.before = util.fixEolChars(stateObj.text.substring(0, stateObj.start)); + chunk.startTag = ""; + chunk.selection = util.fixEolChars(stateObj.text.substring(stateObj.start, stateObj.end)); + chunk.endTag = ""; + chunk.after = util.fixEolChars(stateObj.text.substring(stateObj.end)); + chunk.scrollTop = stateObj.scrollTop; + + return chunk; + }; + + // Sets the TextareaState properties given a chunk of markdown. + this.setChunks = function (chunk) { + + chunk.before = chunk.before + chunk.startTag; + chunk.after = chunk.endTag + chunk.after; + + this.start = chunk.before.length; + this.end = chunk.before.length + chunk.selection.length; + this.text = chunk.before + chunk.selection + chunk.after; + this.scrollTop = chunk.scrollTop; + }; + this.init(); + }; + + function PreviewManager(converter, panels, previewRefreshCallback) { + + var managerObj = this; + var timeout; + var elapsedTime; + var oldInputText; + var maxDelay = 3000; + var startType = "delayed"; // The other legal value is "manual" + + // Adds event listeners to elements + var setupEvents = function (inputElem, listener) { + + util.addEvent(inputElem, "input", listener); + inputElem.onpaste = listener; + inputElem.ondrop = listener; + + util.addEvent(inputElem, "keypress", listener); + util.addEvent(inputElem, "keydown", listener); + }; + + var getDocScrollTop = function () { + + var result = 0; + + if (window.innerHeight) { + result = window.pageYOffset; + } + else + if (doc.documentElement && doc.documentElement.scrollTop) { + result = doc.documentElement.scrollTop; + } + else + if (doc.body) { + result = doc.body.scrollTop; + } + + return result; + }; + + var makePreviewHtml = function () { + + // If there is no registered preview panel + // there is nothing to do. + if (!panels.preview) + return; + + + var text = panels.input.value; + if (text && text == oldInputText) { + return; // Input text hasn't changed. + } + else { + oldInputText = text; + } + + var prevTime = new Date().getTime(); + + text = converter.makeHtml(text); + + // Calculate the processing time of the HTML creation. + // It's used as the delay time in the event listener. + var currTime = new Date().getTime(); + elapsedTime = currTime - prevTime; + + pushPreviewHtml(text); + }; + + // setTimeout is already used. Used as an event listener. + var applyTimeout = function () { + + if (timeout) { + clearTimeout(timeout); + timeout = undefined; + } + + if (startType !== "manual") { + + var delay = 0; + + if (startType === "delayed") { + delay = elapsedTime; + } + + if (delay > maxDelay) { + delay = maxDelay; + } + timeout = setTimeout(makePreviewHtml, delay); + } + }; + + var getScaleFactor = function (panel) { + if (panel.scrollHeight <= panel.clientHeight) { + return 1; + } + return panel.scrollTop / (panel.scrollHeight - panel.clientHeight); + }; + + var setPanelScrollTops = function () { + if (panels.preview) { + panels.preview.scrollTop = (panels.preview.scrollHeight - panels.preview.clientHeight) * getScaleFactor(panels.preview); + } + }; + + this.refresh = function (requiresRefresh) { + + if (requiresRefresh) { + oldInputText = ""; + makePreviewHtml(); + } + else { + applyTimeout(); + } + }; + + this.processingTime = function () { + return elapsedTime; + }; + + var isFirstTimeFilled = true; + + // IE doesn't let you use innerHTML if the element is contained somewhere in a table + // (which is the case for inline editing) -- in that case, detach the element, set the + // value, and reattach. Yes, that *is* ridiculous. + var ieSafePreviewSet = function (text) { + var preview = panels.preview; + var parent = preview.parentNode; + var sibling = preview.nextSibling; + parent.removeChild(preview); + preview.innerHTML = text; + if (!sibling) + parent.appendChild(preview); + else + parent.insertBefore(preview, sibling); + } + + var nonSuckyBrowserPreviewSet = function (text) { + panels.preview.innerHTML = text; + } + + var previewSetter; + + var previewSet = function (text) { + if (previewSetter) + return previewSetter(text); + + try { + nonSuckyBrowserPreviewSet(text); + previewSetter = nonSuckyBrowserPreviewSet; + } catch (e) { + previewSetter = ieSafePreviewSet; + previewSetter(text); + } + }; + + var pushPreviewHtml = function (text) { + + var emptyTop = position.getTop(panels.input) - getDocScrollTop(); + + if (panels.preview) { + previewSet(text); + previewRefreshCallback(); + } + + setPanelScrollTops(); + + if (isFirstTimeFilled) { + isFirstTimeFilled = false; + return; + } + + var fullTop = position.getTop(panels.input) - getDocScrollTop(); + + if (uaSniffed.isIE) { + setTimeout(function () { + window.scrollBy(0, fullTop - emptyTop); + }, 0); + } + else { + window.scrollBy(0, fullTop - emptyTop); + } + }; + + var init = function () { + + setupEvents(panels.input, applyTimeout); + makePreviewHtml(); + + if (panels.preview) { + panels.preview.scrollTop = 0; + } + }; + + init(); + }; + + // Creates the background behind the hyperlink text entry box. + // And download dialog + // Most of this has been moved to CSS but the div creation and + // browser-specific hacks remain here. + ui.createBackground = function () { + + var background = doc.createElement("div"), + style = background.style; + + background.className = "wmd-prompt-background"; + + style.position = "absolute"; + style.top = "0"; + + style.zIndex = "1000"; + + if (uaSniffed.isIE) { + style.filter = "alpha(opacity=50)"; + } + else { + style.opacity = "0.5"; + } + + var pageSize = position.getPageSize(); + style.height = pageSize[1] + "px"; + + if (uaSniffed.isIE) { + style.left = doc.documentElement.scrollLeft; + style.width = doc.documentElement.clientWidth; + } + else { + style.left = "0"; + style.width = "100%"; + } + + doc.body.appendChild(background); + return background; + }; + + // 扩展了原来ui的功能 + // 允许创建一个自定义html的对话框 + ui.dialog = function (html, callback, ok, cancel) { + + // These variables need to be declared at this level since they are used + // in multiple functions. + var dialog; // The dialog box. + + // Used as a keydown event handler. Esc dismisses the prompt. + // Key code 27 is ESC. + var checkEscape = function (key) { + var code = (key.charCode || key.keyCode); + if (code === 27) { + close(true); + } + }; + + // Dismisses the hyperlink input box. + // isCancel is true if we don't care about the input text. + // isCancel is false if we are going to keep the text. + var close = function (isCancel) { + util.removeEvent(doc.body, "keydown", checkEscape); + dialog.parentNode.removeChild(dialog); + + callback(isCancel); + return false; + }; + + + + // Create the text input box form/window. + var createDialog = function () { + + // The main dialog box. + dialog = doc.createElement("div"); + dialog.className = "wmd-prompt-dialog"; + dialog.setAttribute("role", "dialog"); + /* + dialog.style.padding = "10px;"; + dialog.style.position = "fixed"; + dialog.style.width = "400px"; + dialog.style.zIndex = "1001"; + */ + + // The dialog text. + var question = doc.createElement("div"); + + // The web form container for the text box and buttons. + var form = doc.createElement("form"), + style = form.style; + form.onsubmit = function () { return close(false); }; + /* + style.padding = "0"; + style.margin = "0"; + style.cssFloat = "left"; + style.width = "100%"; + style.textAlign = "center"; + style.position = "relative"; + */ + dialog.appendChild(form); + form.appendChild(question); + + if ('function' == typeof(html)) { + html.call(this, question); + } else { + question.innerHTML = html; + } + + // The ok button + var okButton = doc.createElement("button"); + okButton.type = "button"; + okButton.className = "btn btn-s primary"; + okButton.onclick = function () { return close(false); }; + okButton.innerHTML = ok; + /* + style = okButton.style; + style.margin = "10px"; + style.display = "inline"; + style.width = "7em"; + */ + + // The cancel button + var cancelButton = doc.createElement("button"); + cancelButton.type = "button"; + cancelButton.className = "btn btn-s"; + cancelButton.onclick = function () { return close(true); }; + cancelButton.innerHTML = cancel; + /* + style = cancelButton.style; + style.margin = "10px"; + style.display = "inline"; + style.width = "7em"; + */ + + form.appendChild(okButton); + form.appendChild(cancelButton); + + util.addEvent(doc.body, "keydown", checkEscape); + /* + dialog.style.top = "50%"; + dialog.style.left = "50%"; + dialog.style.display = "block"; + if (uaSniffed.isIE_5or6) { + dialog.style.position = "absolute"; + dialog.style.top = doc.documentElement.scrollTop + 200 + "px"; + dialog.style.left = "50%"; + } + */ + doc.body.appendChild(dialog); + + // This has to be done AFTER adding the dialog to the form if you + // want it to be centered. + /* + dialog.style.marginTop = -(position.getHeight(dialog) / 2) + "px"; + dialog.style.marginLeft = -(position.getWidth(dialog) / 2) + "px"; + */ + + }; + + // Why is this in a zero-length timeout? + // Is it working around a browser bug? + setTimeout(function () { + createDialog(); + }, 0); + } + + // This simulates a modal dialog box and asks for the URL when you + // click the hyperlink or image buttons. + // + // text: The html for the input box. + // defaultInputText: The default value that appears in the input box. + // callback: The function which is executed when the prompt is dismissed, either via OK or Cancel. + // It receives a single argument; either the entered text (if OK was chosen) or null (if Cancel + // was chosen). + ui.prompt = function (text, defaultInputText, callback, ok, cancel) { + + // These variables need to be declared at this level since they are used + // in multiple functions. + var dialog; // The dialog box. + var input; // The text box where you enter the hyperlink. + + + if (defaultInputText === undefined) { + defaultInputText = ""; + } + + // Used as a keydown event handler. Esc dismisses the prompt. + // Key code 27 is ESC. + var checkEscape = function (key) { + var code = (key.charCode || key.keyCode); + if (code === 27) { + close(true); + } + }; + + // Dismisses the hyperlink input box. + // isCancel is true if we don't care about the input text. + // isCancel is false if we are going to keep the text. + var close = function (isCancel) { + util.removeEvent(doc.body, "keydown", checkEscape); + var text = input.value; + + if (isCancel) { + text = null; + } + else { + // Fixes common pasting errors. + text = text.replace(/^http:\/\/(https?|ftp):\/\//, '$1://'); + if (!/^(?:https?|ftp):\/\//.test(text)) + text = 'http://' + text; + } + + dialog.parentNode.removeChild(dialog); + + callback(text); + return false; + }; + + + + // Create the text input box form/window. + var createDialog = function () { + + // The main dialog box. + dialog = doc.createElement("div"); + dialog.className = "wmd-prompt-dialog"; + dialog.setAttribute("role", "dialog"); + /* + dialog.style.padding = "10px;"; + dialog.style.position = "fixed"; + dialog.style.width = "400px"; + dialog.style.zIndex = "1001"; + */ + + // The dialog text. + var question = doc.createElement("div"); + question.innerHTML = text; + // question.style.padding = "5px"; + dialog.appendChild(question); + + // The web form container for the text box and buttons. + var form = doc.createElement("form"), + style = form.style; + form.onsubmit = function () { return close(false); }; + /* + style.padding = "0"; + style.margin = "0"; + style.cssFloat = "left"; + style.width = "100%"; + style.textAlign = "center"; + style.position = "relative"; + */ + dialog.appendChild(form); + + // The input text box + input = doc.createElement("input"); + input.type = "text"; + input.value = defaultInputText; + /* + style = input.style; + style.display = "block"; + style.width = "80%"; + style.marginLeft = style.marginRight = "auto"; + */ + form.appendChild(input); + + // The ok button + var okButton = doc.createElement("button"); + okButton.type = "button"; + okButton.className = "btn btn-s primary"; + okButton.onclick = function () { return close(false); }; + okButton.innerHTML = ok; + /* + style = okButton.style; + style.margin = "10px"; + style.display = "inline"; + style.width = "7em"; + */ + + // The cancel button + var cancelButton = doc.createElement("button"); + cancelButton.type = "button"; + cancelButton.className = "btn btn-s"; + cancelButton.onclick = function () { return close(true); }; + cancelButton.innerHTML = cancel; + /* + style = cancelButton.style; + style.margin = "10px"; + style.display = "inline"; + style.width = "7em"; + */ + + form.appendChild(okButton); + form.appendChild(cancelButton); + + util.addEvent(doc.body, "keydown", checkEscape); + /* + dialog.style.top = "50%"; + dialog.style.left = "50%"; + dialog.style.display = "block"; + if (uaSniffed.isIE_5or6) { + dialog.style.position = "absolute"; + dialog.style.top = doc.documentElement.scrollTop + 200 + "px"; + dialog.style.left = "50%"; + } + */ + doc.body.appendChild(dialog); + + // This has to be done AFTER adding the dialog to the form if you + // want it to be centered. + /* + dialog.style.marginTop = -(position.getHeight(dialog) / 2) + "px"; + dialog.style.marginLeft = -(position.getWidth(dialog) / 2) + "px"; + */ + + }; + + // Why is this in a zero-length timeout? + // Is it working around a browser bug? + setTimeout(function () { + + createDialog(); + + var defTextLen = defaultInputText.length; + if (input.selectionStart !== undefined) { + input.selectionStart = 0; + input.selectionEnd = defTextLen; + } + else if (input.createTextRange) { + var range = input.createTextRange(); + range.collapse(false); + range.moveStart("character", -defTextLen); + range.moveEnd("character", defTextLen); + range.select(); + } + + input.focus(); + }, 0); + }; + + function UIManager(postfix, panels, hooks, undoManager, previewManager, commandManager, fullScreenManager, helpOptions, getString) { + + var inputBox = panels.input, + buttons = {}; // buttons.undo, buttons.link, etc. The actual DOM elements. + + makeSpritedButtonRow(); + + var keyEvent = "keydown"; + if (uaSniffed.isOpera) { + keyEvent = "keypress"; + } + + util.addEvent(inputBox, keyEvent, function (key) { + // Check to see if we have a button key and, if so execute the callback. + if ((key.ctrlKey || key.metaKey) && !key.altKey && !key.shiftKey) { + + var keyCode = key.charCode || key.keyCode; + var keyCodeStr = String.fromCharCode(keyCode).toLowerCase(); + + switch (keyCodeStr) { + case "b": + doClick(buttons.bold); + break; + case "i": + doClick(buttons.italic); + break; + case "l": + doClick(buttons.link); + break; + case "q": + doClick(buttons.quote); + break; + case "k": + doClick(buttons.code); + break; + case "g": + doClick(buttons.image); + break; + case "o": + doClick(buttons.olist); + break; + case "u": + doClick(buttons.ulist); + break; + case 'm': + doClick(buttons.more); + break; + case 'j': + doClick(buttons.fullscreen); + break; + case 'e': + doClick(buttons.exitFullscreen); + break; + case "h": + doClick(buttons.heading); + break; + case "r": + doClick(buttons.hr); + break; + case "y": + doClick(buttons.redo); + break; + case "z": + if (key.shiftKey) { + doClick(buttons.redo); + } + else { + doClick(buttons.undo); + } + break; + default: + return; + } + + + if (key.preventDefault) { + key.preventDefault(); + } + + if (window.event) { + window.event.returnValue = false; + } + }else if(key.keyCode==9){ + var tab = {}; + tab.textOp = bindCommand("doTab"); + doClick(tab); + + if (key.preventDefault) { + key.preventDefault(); + } + if (window.event) { + window.event.returnValue = false; + } + } + }); + + // Auto-indent on shift-enter + util.addEvent(inputBox, "keyup", function (key) { + if (key.shiftKey && !key.ctrlKey && !key.metaKey) { + var keyCode = key.charCode || key.keyCode; + // Character 13 is Enter + if (keyCode === 13) { + var fakeButton = {}; + fakeButton.textOp = bindCommand("doAutoindent"); + doClick(fakeButton); + } + } + }); + + // special handler because IE clears the context of the textbox on ESC + if (uaSniffed.isIE) { + util.addEvent(inputBox, "keydown", function (key) { + var code = key.keyCode; + if (code === 27) { + return false; + } + }); + } + + + // Perform the button's action. + function doClick(button) { + + inputBox.focus(); + + if (button.textOp) { + + if (undoManager) { + undoManager.setCommandMode(); + } + + var state = new TextareaState(panels); + + if (!state) { + return; + } + + var chunks = state.getChunks(); + + // Some commands launch a "modal" prompt dialog. Javascript + // can't really make a modal dialog box and the WMD code + // will continue to execute while the dialog is displayed. + // This prevents the dialog pattern I'm used to and means + // I can't do something like this: + // + // var link = CreateLinkDialog(); + // makeMarkdownLink(link); + // + // Instead of this straightforward method of handling a + // dialog I have to pass any code which would execute + // after the dialog is dismissed (e.g. link creation) + // in a function parameter. + // + // Yes this is awkward and I think it sucks, but there's + // no real workaround. Only the image and link code + // create dialogs and require the function pointers. + var fixupInputArea = function () { + + inputBox.focus(); + + if (chunks) { + state.setChunks(chunks); + } + + state.restore(); + previewManager.refresh(); + }; + + var noCleanup = button.textOp(chunks, fixupInputArea); + + if (!noCleanup) { + fixupInputArea(); + } + + } + + if (button.execute) { + button.execute(undoManager); + } + }; + + function setupButton(button, isEnabled) { + + var normalYShift = "0px"; + var disabledYShift = "-20px"; + var highlightYShift = "-40px"; + var image = button.getElementsByTagName("span")[0]; + if (isEnabled) { + image.style.backgroundPosition = button.XShift + " " + normalYShift; + button.onmouseover = function () { + image.style.backgroundPosition = this.XShift + " " + highlightYShift; + }; + + button.onmouseout = function () { + image.style.backgroundPosition = this.XShift + " " + normalYShift; + }; + + // IE tries to select the background image "button" text (it's + // implemented in a list item) so we have to cache the selection + // on mousedown. + if (uaSniffed.isIE) { + button.onmousedown = function () { + if (doc.activeElement && doc.activeElement !== panels.input) { // we're not even in the input box, so there's no selection + return; + } + panels.ieCachedRange = document.selection.createRange(); + panels.ieCachedScrollTop = panels.input.scrollTop; + }; + } + + if (!button.isHelp) { + button.onclick = function () { + if (this.onmouseout) { + this.onmouseout(); + } + doClick(this); + return false; + } + } + } + else { + image.style.backgroundPosition = button.XShift + " " + disabledYShift; + button.onmouseover = button.onmouseout = button.onclick = function () { }; + } + } + + function bindCommand(method) { + if (typeof method === "string") + method = commandManager[method]; + return function () { method.apply(commandManager, arguments); } + } + + function makeSpritedButtonRow() { + + var buttonBar = panels.buttonBar; + + var normalYShift = "0px"; + var disabledYShift = "-20px"; + var highlightYShift = "-40px"; + + var buttonRow = document.createElement("ul"); + buttonRow.id = "wmd-button-row" + postfix; + buttonRow.className = 'wmd-button-row'; + buttonRow = buttonBar.appendChild(buttonRow); + var xPosition = 0; + var makeButton = function (id, title, XShift, textOp) { + var button = document.createElement("li"); + button.className = "wmd-button"; + button.style.left = xPosition + "px"; + xPosition += 25; + var buttonImage = document.createElement("span"); + button.id = id + postfix; + button.appendChild(buttonImage); + button.title = title; + button.XShift = XShift; + if (textOp) + button.textOp = textOp; + setupButton(button, true); + buttonRow.appendChild(button); + return button; + }; + var makeSpacer = function (num) { + var spacer = document.createElement("li"); + spacer.className = "wmd-spacer wmd-spacer" + num; + spacer.id = "wmd-spacer" + num + postfix; + buttonRow.appendChild(spacer); + xPosition += 25; + } + + buttons.bold = makeButton("wmd-bold-button", getString("bold"), "0px", bindCommand("doBold")); + buttons.italic = makeButton("wmd-italic-button", getString("italic"), "-20px", bindCommand("doItalic")); + makeSpacer(1); + buttons.link = makeButton("wmd-link-button", getString("link"), "-40px", bindCommand(function (chunk, postProcessing) { + return this.doLinkOrImage(chunk, postProcessing, false); + })); + buttons.quote = makeButton("wmd-quote-button", getString("quote"), "-60px", bindCommand("doBlockquote")); + buttons.code = makeButton("wmd-code-button", getString("code"), "-80px", bindCommand("doCode")); + buttons.image = makeButton("wmd-image-button", getString("image"), "-100px", bindCommand(function (chunk, postProcessing) { + return this.doLinkOrImage(chunk, postProcessing, true); + })); + makeSpacer(2); + buttons.olist = makeButton("wmd-olist-button", getString("olist"), "-120px", bindCommand(function (chunk, postProcessing) { + this.doList(chunk, postProcessing, true); + })); + buttons.ulist = makeButton("wmd-ulist-button", getString("ulist"), "-140px", bindCommand(function (chunk, postProcessing) { + this.doList(chunk, postProcessing, false); + })); + buttons.heading = makeButton("wmd-heading-button", getString("heading"), "-160px", bindCommand("doHeading")); + buttons.hr = makeButton("wmd-hr-button", getString("hr"), "-180px", bindCommand("doHorizontalRule")); + buttons.more = makeButton("wmd-more-button", getString("more"), "-280px", bindCommand("doMore")); + makeSpacer(3); + buttons.undo = makeButton("wmd-undo-button", getString("undo"), "-200px", null); + buttons.undo.execute = function (manager) { if (manager) manager.undo(); }; + + var redoTitle = /win/.test(nav.platform.toLowerCase()) ? + getString("redo") : + getString("redomac"); // mac and other non-Windows platforms + + buttons.redo = makeButton("wmd-redo-button", redoTitle, "-220px", null); + buttons.redo.execute = function (manager) { if (manager) manager.redo(); }; + makeSpacer(4); + buttons.fullscreen = makeButton("wmd-fullscreen-button", getString("fullscreen"), "-240px", null); + buttons.fullscreen.execute = function () { fullScreenManager.doFullScreen(buttons, true); }; + buttons.exitFullscreen = makeButton("wmd-exit-fullscreen-button", getString("exitFullscreen"), "-260px", null); + buttons.exitFullscreen.style.display = 'none'; + buttons.exitFullscreen.execute = function () { fullScreenManager.doFullScreen(buttons, false); }; + + // button hooks + hooks.makeButton(buttons, makeButton, bindCommand, ui); + + if (helpOptions) { + var helpButton = document.createElement("li"); + var helpButtonImage = document.createElement("span"); + helpButton.appendChild(helpButtonImage); + helpButton.className = "wmd-button wmd-help-button"; + helpButton.id = "wmd-help-button" + postfix; + helpButton.XShift = "-300px"; + helpButton.isHelp = true; + helpButton.style.right = "0px"; + helpButton.title = getString("help"); + helpButton.onclick = helpOptions.handler; + + setupButton(helpButton, true); + buttonRow.appendChild(helpButton); + buttons.help = helpButton; + } + + setUndoRedoButtonStates(); + } + + function setUndoRedoButtonStates() { + if (undoManager) { + setupButton(buttons.undo, undoManager.canUndo()); + setupButton(buttons.redo, undoManager.canRedo()); + } + }; + + this.setUndoRedoButtonStates = setUndoRedoButtonStates; + + } + + function CommandManager(pluginHooks, getString) { + this.hooks = pluginHooks; + this.getString = getString; + } + + var commandProto = CommandManager.prototype; + + // The markdown symbols - 4 spaces = code, > = blockquote, etc. + commandProto.prefixes = "(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)"; + + // Remove markdown symbols from the chunk selection. + commandProto.unwrap = function (chunk) { + var txt = new re("([^\\n])\\n(?!(\\n|" + this.prefixes + "))", "g"); + chunk.selection = chunk.selection.replace(txt, "$1 $2"); + }; + + commandProto.wrap = function (chunk, len) { + this.unwrap(chunk); + var regex = new re("(.{1," + len + "})( +|$\\n?)", "gm"), + that = this; + + chunk.selection = chunk.selection.replace(regex, function (line, marked) { + if (new re("^" + that.prefixes, "").test(line)) { + return line; + } + return marked + "\n"; + }); + + chunk.selection = chunk.selection.replace(/\s+$/, ""); + }; + + commandProto.doBold = function (chunk, postProcessing) { + return this.doBorI(chunk, postProcessing, 2, this.getString("boldexample")); + }; + + commandProto.doItalic = function (chunk, postProcessing) { + return this.doBorI(chunk, postProcessing, 1, this.getString("italicexample")); + }; + + // chunk: The selected region that will be enclosed with */** + // nStars: 1 for italics, 2 for bold + // insertText: If you just click the button without highlighting text, this gets inserted + commandProto.doBorI = function (chunk, postProcessing, nStars, insertText) { + + // Get rid of whitespace and fixup newlines. + chunk.trimWhitespace(); + chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n"); + + // Look for stars before and after. Is the chunk already marked up? + // note that these regex matches cannot fail + var starsBefore = /(\**$)/.exec(chunk.before)[0]; + var starsAfter = /(^\**)/.exec(chunk.after)[0]; + + var prevStars = Math.min(starsBefore.length, starsAfter.length); + + // Remove stars if we have to since the button acts as a toggle. + if ((prevStars >= nStars) && (prevStars != 2 || nStars != 1)) { + chunk.before = chunk.before.replace(re("[*]{" + nStars + "}$", ""), ""); + chunk.after = chunk.after.replace(re("^[*]{" + nStars + "}", ""), ""); + } + else if (!chunk.selection && starsAfter) { + // It's not really clear why this code is necessary. It just moves + // some arbitrary stuff around. + chunk.after = chunk.after.replace(/^([*_]*)/, ""); + chunk.before = chunk.before.replace(/(\s?)$/, ""); + var whitespace = re.$1; + chunk.before = chunk.before + starsAfter + whitespace; + } + else { + + // In most cases, if you don't have any selected text and click the button + // you'll get a selected, marked up region with the default text inserted. + if (!chunk.selection && !starsAfter) { + chunk.selection = insertText; + } + + // Add the true markup. + var markup = nStars <= 1 ? "*" : "**"; // shouldn't the test be = ? + chunk.before = chunk.before + markup; + chunk.after = markup + chunk.after; + } + + return; + }; + + commandProto.stripLinkDefs = function (text, defsToAdd) { + + text = text.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm, + function (totalMatch, id, link, newlines, title) { + defsToAdd[id] = totalMatch.replace(/\s*$/, ""); + if (newlines) { + // Strip the title and return that separately. + defsToAdd[id] = totalMatch.replace(/["(](.+?)[")]$/, ""); + return newlines + title; + } + return ""; + }); + + return text; + }; + + commandProto.addLinkDef = function (chunk, linkDef) { + + var refNumber = 0; // The current reference number + var defsToAdd = {}; // + // Start with a clean slate by removing all previous link definitions. + chunk.before = this.stripLinkDefs(chunk.before, defsToAdd); + chunk.selection = this.stripLinkDefs(chunk.selection, defsToAdd); + chunk.after = this.stripLinkDefs(chunk.after, defsToAdd); + + var defs = ""; + var regex = /(\[)((?:\[[^\]]*\]|[^\[\]])*)(\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g; + + var addDefNumber = function (def) { + refNumber++; + def = def.replace(/^[ ]{0,3}\[(\d+)\]:/, " [" + refNumber + "]:"); + defs += "\n" + def; + }; + + // note that + // a) the recursive call to getLink cannot go infinite, because by definition + // of regex, inner is always a proper substring of wholeMatch, and + // b) more than one level of nesting is neither supported by the regex + // nor making a lot of sense (the only use case for nesting is a linked image) + var getLink = function (wholeMatch, before, inner, afterInner, id, end) { + inner = inner.replace(regex, getLink); + if (defsToAdd[id]) { + addDefNumber(defsToAdd[id]); + return before + inner + afterInner + refNumber + end; + } + return wholeMatch; + }; + + chunk.before = chunk.before.replace(regex, getLink); + + if (linkDef) { + addDefNumber(linkDef); + } + else { + chunk.selection = chunk.selection.replace(regex, getLink); + } + + var refOut = refNumber; + + chunk.after = chunk.after.replace(regex, getLink); + + if (chunk.after) { + chunk.after = chunk.after.replace(/\n*$/, ""); + } + if (!chunk.after) { + chunk.selection = chunk.selection.replace(/\n*$/, ""); + } + + chunk.after += "\n\n" + defs; + + return refOut; + }; + + // takes the line as entered into the add link/as image dialog and makes + // sure the URL and the optinal title are "nice". + function properlyEncoded(linkdef) { + return linkdef.replace(/^\s*(.*?)(?:\s+"(.+)")?\s*$/, function (wholematch, link, title) { + + var inQueryString = false; + + // The last alternative, `[^\w\d-./]`, is just a shortcut that lets us skip + // the most common characters in URLs. Replacing it with `.` would not change + // the result, because encodeURI returns those characters unchanged, but it + // would mean lots of unnecessary replacement calls + link = link.replace(/%(?:[\da-fA-F]{2})|\?|\+|[^\w\d-./]/g, function (match) { + // Valid percent encoding. Could just return it as is, but we follow RFC3986 + // Section 2.1 which says "For consistency, URI producers and normalizers + // should use uppercase hexadecimal digits for all percent-encodings." + // Note that we also handle (illegal) stand-alone percent characters by + // replacing them with "%25" + if (match.length === 3 && match.charAt(0) == "%") { + return match.toUpperCase(); + } + switch (match) { + case "?": + inQueryString = true; + return "?"; + break; + + // In the query string, a plus and a space are identical -- normalize. + // Not strictly necessary, but identical behavior to the previous version + // of this function. + case "+": + if (inQueryString) + return "%20"; + break; + } + return encodeURI(match); + }) + + if (title) { + title = title.trim ? title.trim() : title.replace(/^\s*/, "").replace(/\s*$/, ""); + title = title.replace(/"/g, "quot;").replace(/\(/g, "(").replace(/\)/g, ")").replace(//g, ">"); + } + return title ? link + ' "' + title + '"' : link; + }); + } + + commandProto.doLinkOrImage = function (chunk, postProcessing, isImage) { + + chunk.trimWhitespace(); + chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/); + var background; + + if (chunk.endTag.length > 1 && chunk.startTag.length > 0) { + + chunk.startTag = chunk.startTag.replace(/!?\[/, ""); + chunk.endTag = ""; + this.addLinkDef(chunk, null); + + } + else { + + // We're moving start and end tag back into the selection, since (as we're in the else block) we're not + // *removing* a link, but *adding* one, so whatever findTags() found is now back to being part of the + // link text. linkEnteredCallback takes care of escaping any brackets. + chunk.selection = chunk.startTag + chunk.selection + chunk.endTag; + chunk.startTag = chunk.endTag = ""; + + if (/\n\n/.test(chunk.selection)) { + this.addLinkDef(chunk, null); + return; + } + var that = this; + // The function to be executed when you enter a link and press OK or Cancel. + // Marks up the link and adds the ref. + var linkEnteredCallback = function (link) { + + background.parentNode.removeChild(background); + + if (link !== null) { + // ( $1 + // [^\\] anything that's not a backslash + // (?:\\\\)* an even number (this includes zero) of backslashes + // ) + // (?= followed by + // [[\]] an opening or closing bracket + // ) + // + // In other words, a non-escaped bracket. These have to be escaped now to make sure they + // don't count as the end of the link or similar. + // Note that the actual bracket has to be a lookahead, because (in case of to subsequent brackets), + // the bracket in one match may be the "not a backslash" character in the next match, so it + // should not be consumed by the first match. + // The "prepend a space and finally remove it" steps makes sure there is a "not a backslash" at the + // start of the string, so this also works if the selection begins with a bracket. We cannot solve + // this by anchoring with ^, because in the case that the selection starts with two brackets, this + // would mean a zero-width match at the start. Since zero-width matches advance the string position, + // the first bracket could then not act as the "not a backslash" for the second. + chunk.selection = (" " + chunk.selection).replace(/([^\\](?:\\\\)*)(?=[[\]])/g, "$1\\").substr(1); + + var linkDef = " [999]: " + properlyEncoded(link); + + var num = that.addLinkDef(chunk, linkDef); + chunk.startTag = isImage ? "![" : "["; + chunk.endTag = "][" + num + "]"; + + if (!chunk.selection) { + if (isImage) { + var imagename = that.getString("imagename"); + chunk.selection = imagename || that.getString("imagedescription"); + } + else { + var linkname = that.getString("linkname"); + chunk.selection = linkname || that.getString("linkdescription"); + } + } + } + postProcessing(); + }; + + background = ui.createBackground(); + + if (isImage) { + if (!this.hooks.insertImageDialog(linkEnteredCallback)) + ui.prompt(this.getString("imagedialog"), imageDefaultText, linkEnteredCallback, this.getString("ok"), this.getString("cancel")); + } + else { + if (!this.hooks.insertLinkDialog(linkEnteredCallback)) + ui.prompt(this.getString("linkdialog"), linkDefaultText, linkEnteredCallback, this.getString("ok"), this.getString("cancel")); + } + return true; + } + }; + + // When making a list, hitting shift-enter will put your cursor on the next line + // at the current indent level. + commandProto.doAutoindent = function (chunk, postProcessing) { + + var commandMgr = this, + fakeSelection = false; + + chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/, "\n\n"); + chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/, "\n\n"); + chunk.before = chunk.before.replace(/(\n|^)[ \t]+\n$/, "\n\n"); + + // There's no selection, end the cursor wasn't at the end of the line: + // The user wants to split the current list item / code line / blockquote line + // (for the latter it doesn't really matter) in two. Temporarily select the + // (rest of the) line to achieve this. + if (!chunk.selection && !/^[ \t]*(?:\n|$)/.test(chunk.after)) { + chunk.after = chunk.after.replace(/^[^\n]*/, function (wholeMatch) { + chunk.selection = wholeMatch; + return ""; + }); + fakeSelection = true; + } + + if (/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]+.*\n$/.test(chunk.before)) { + if (commandMgr.doList) { + commandMgr.doList(chunk); + } + } + if (/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(chunk.before)) { + if (commandMgr.doBlockquote) { + commandMgr.doBlockquote(chunk); + } + } + if (/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)) { + if (commandMgr.doCode) { + commandMgr.doCode(chunk); + } + } + + if (fakeSelection) { + chunk.after = chunk.selection + chunk.after; + chunk.selection = ""; + } + }; + + commandProto.doBlockquote = function (chunk, postProcessing) { + + chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/, + function (totalMatch, newlinesBefore, text, newlinesAfter) { + chunk.before += newlinesBefore; + chunk.after = newlinesAfter + chunk.after; + return text; + }); + + chunk.before = chunk.before.replace(/(>[ \t]*)$/, + function (totalMatch, blankLine) { + chunk.selection = blankLine + chunk.selection; + return ""; + }); + + chunk.selection = chunk.selection.replace(/^(\s|>)+$/, ""); + chunk.selection = chunk.selection || this.getString("quoteexample"); + + // The original code uses a regular expression to find out how much of the + // text *directly before* the selection already was a blockquote: + + /* + if (chunk.before) { + chunk.before = chunk.before.replace(/\n?$/, "\n"); + } + chunk.before = chunk.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/, + function (totalMatch) { + chunk.startTag = totalMatch; + return ""; + }); + */ + + // This comes down to: + // Go backwards as many lines a possible, such that each line + // a) starts with ">", or + // b) is almost empty, except for whitespace, or + // c) is preceeded by an unbroken chain of non-empty lines + // leading up to a line that starts with ">" and at least one more character + // and in addition + // d) at least one line fulfills a) + // + // Since this is essentially a backwards-moving regex, it's susceptible to + // catstrophic backtracking and can cause the browser to hang; + // see e.g. http://meta.stackexchange.com/questions/9807. + // + // Hence we replaced this by a simple state machine that just goes through the + // lines and checks for a), b), and c). + + var match = "", + leftOver = "", + line; + if (chunk.before) { + var lines = chunk.before.replace(/\n$/, "").split("\n"); + var inChain = false; + for (var i = 0; i < lines.length; i++) { + var good = false; + line = lines[i]; + inChain = inChain && line.length > 0; // c) any non-empty line continues the chain + if (/^>/.test(line)) { // a) + good = true; + if (!inChain && line.length > 1) // c) any line that starts with ">" and has at least one more character starts the chain + inChain = true; + } else if (/^[ \t]*$/.test(line)) { // b) + good = true; + } else { + good = inChain; // c) the line is not empty and does not start with ">", so it matches if and only if we're in the chain + } + if (good) { + match += line + "\n"; + } else { + leftOver += match + line; + match = "\n"; + } + } + if (!/(^|\n)>/.test(match)) { // d) + leftOver += match; + match = ""; + } + } + + chunk.startTag = match; + chunk.before = leftOver; + + // end of change + + if (chunk.after) { + chunk.after = chunk.after.replace(/^\n?/, "\n"); + } + + chunk.after = chunk.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/, + function (totalMatch) { + chunk.endTag = totalMatch; + return ""; + } + ); + + var replaceBlanksInTags = function (useBracket) { + + var replacement = useBracket ? "> " : ""; + + if (chunk.startTag) { + chunk.startTag = chunk.startTag.replace(/\n((>|\s)*)\n$/, + function (totalMatch, markdown) { + return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n"; + }); + } + if (chunk.endTag) { + chunk.endTag = chunk.endTag.replace(/^\n((>|\s)*)\n/, + function (totalMatch, markdown) { + return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n"; + }); + } + }; + + if (/^(?![ ]{0,3}>)/m.test(chunk.selection)) { + this.wrap(chunk, SETTINGS.lineLength - 2); + chunk.selection = chunk.selection.replace(/^/gm, "> "); + replaceBlanksInTags(true); + chunk.skipLines(); + } else { + chunk.selection = chunk.selection.replace(/^[ ]{0,3}> ?/gm, ""); + this.unwrap(chunk); + replaceBlanksInTags(false); + + if (!/^(\n|^)[ ]{0,3}>/.test(chunk.selection) && chunk.startTag) { + chunk.startTag = chunk.startTag.replace(/\n{0,2}$/, "\n\n"); + } + + if (!/(\n|^)[ ]{0,3}>.*$/.test(chunk.selection) && chunk.endTag) { + chunk.endTag = chunk.endTag.replace(/^\n{0,2}/, "\n\n"); + } + } + + chunk.selection = this.hooks.postBlockquoteCreation(chunk.selection); + + if (!/\n/.test(chunk.selection)) { + chunk.selection = chunk.selection.replace(/^(> *)/, + function (wholeMatch, blanks) { + chunk.startTag += blanks; + return ""; + }); + } + }; + + commandProto.doCode = function (chunk, postProcessing) { + + var hasTextBefore = /\S[ ]*$/.test(chunk.before); + var hasTextAfter = /^[ ]*\S/.test(chunk.after); + + // Use 'four space' markdown if the selection is on its own + // line or is multiline. + if ((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)) { + + chunk.before = chunk.before.replace(/[ ]{4}$/, + function (totalMatch) { + chunk.selection = totalMatch + chunk.selection; + return ""; + }); + + var nLinesBack = 1; + var nLinesForward = 1; + + if (/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)) { + nLinesBack = 0; + } + if (/^\n(\t|[ ]{4,})/.test(chunk.after)) { + nLinesForward = 0; + } + + chunk.skipLines(nLinesBack, nLinesForward); + + if (!chunk.selection) { + chunk.startTag = " "; + chunk.selection = this.getString("codeexample"); + } + else { + if (/^[ ]{0,3}\S/m.test(chunk.selection)) { + if (/\n/.test(chunk.selection)) + chunk.selection = chunk.selection.replace(/^/gm, " "); + else // if it's not multiline, do not select the four added spaces; this is more consistent with the doList behavior + chunk.before += " "; + } + else { + chunk.selection = chunk.selection.replace(/^(?:[ ]{4}|[ ]{0,3}\t)/gm, ""); + } + } + } + else { + // Use backticks (`) to delimit the code block. + + chunk.trimWhitespace(); + chunk.findTags(/`/, /`/); + + if (!chunk.startTag && !chunk.endTag) { + chunk.startTag = chunk.endTag = "`"; + if (!chunk.selection) { + chunk.selection = this.getString("codeexample"); + } + } + else if (chunk.endTag && !chunk.startTag) { + chunk.before += chunk.endTag; + chunk.endTag = ""; + } + else { + chunk.startTag = chunk.endTag = ""; + } + } + }; + + commandProto.doList = function (chunk, postProcessing, isNumberedList) { + + // These are identical except at the very beginning and end. + // Should probably use the regex extension function to make this clearer. + var previousItemsRegex = /(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/; + var nextItemsRegex = /^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/; + + // The default bullet is a dash but others are possible. + // This has nothing to do with the particular HTML bullet, + // it's just a markdown bullet. + var bullet = "-"; + + // The number in a numbered list. + var num = 1; + + // Get the item prefix - e.g. " 1. " for a numbered list, " - " for a bulleted list. + var getItemPrefix = function () { + var prefix; + if (isNumberedList) { + prefix = " " + num + ". "; + num++; + } + else { + prefix = " " + bullet + " "; + } + return prefix; + }; + + // Fixes the prefixes of the other list items. + var getPrefixedItem = function (itemText) { + + // The numbering flag is unset when called by autoindent. + if (isNumberedList === undefined) { + isNumberedList = /^\s*\d/.test(itemText); + } + + // Renumber/bullet the list element. + itemText = itemText.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm, + function (_) { + return getItemPrefix(); + }); + + return itemText; + }; + + chunk.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/, null); + + if (chunk.before && !/\n$/.test(chunk.before) && !/^\n/.test(chunk.startTag)) { + chunk.before += chunk.startTag; + chunk.startTag = ""; + } + + if (chunk.startTag) { + + var hasDigits = /\d+[.]/.test(chunk.startTag); + chunk.startTag = ""; + chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, "\n"); + this.unwrap(chunk); + chunk.skipLines(); + + if (hasDigits) { + // Have to renumber the bullet points if this is a numbered list. + chunk.after = chunk.after.replace(nextItemsRegex, getPrefixedItem); + } + if (isNumberedList == hasDigits) { + return; + } + } + + var nLinesUp = 1; + + chunk.before = chunk.before.replace(previousItemsRegex, + function (itemText) { + if (/^\s*([*+-])/.test(itemText)) { + bullet = re.$1; + } + nLinesUp = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; + return getPrefixedItem(itemText); + }); + + if (!chunk.selection) { + chunk.selection = this.getString("litem"); + } + + var prefix = getItemPrefix(); + + var nLinesDown = 1; + + chunk.after = chunk.after.replace(nextItemsRegex, + function (itemText) { + nLinesDown = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; + return getPrefixedItem(itemText); + }); + + chunk.trimWhitespace(true); + chunk.skipLines(nLinesUp, nLinesDown, true); + chunk.startTag = prefix; + var spaces = prefix.replace(/./g, " "); + this.wrap(chunk, SETTINGS.lineLength - spaces.length); + chunk.selection = chunk.selection.replace(/\n/g, "\n" + spaces); + + }; + + commandProto.doHeading = function (chunk, postProcessing) { + + // Remove leading/trailing whitespace and reduce internal spaces to single spaces. + chunk.selection = chunk.selection.replace(/\s+/g, " "); + chunk.selection = chunk.selection.replace(/(^\s+|\s+$)/g, ""); + + // If we clicked the button with no selected text, we just + // make a level 2 hash header around some default text. + if (!chunk.selection) { + chunk.startTag = "## "; + chunk.selection = this.getString("headingexample"); + chunk.endTag = " ##"; + return; + } + + var headerLevel = 0; // The existing header level of the selected text. + + // Remove any existing hash heading markdown and save the header level. + chunk.findTags(/#+[ ]*/, /[ ]*#+/); + if (/#+/.test(chunk.startTag)) { + headerLevel = re.lastMatch.length; + } + chunk.startTag = chunk.endTag = ""; + + // Try to get the current header level by looking for - and = in the line + // below the selection. + chunk.findTags(null, /\s?(-+|=+)/); + if (/=+/.test(chunk.endTag)) { + headerLevel = 1; + } + if (/-+/.test(chunk.endTag)) { + headerLevel = 2; + } + + // Skip to the next line so we can create the header markdown. + chunk.startTag = chunk.endTag = ""; + chunk.skipLines(1, 1); + + // We make a level 2 header if there is no current header. + // If there is a header level, we substract one from the header level. + // If it's already a level 1 header, it's removed. + var headerLevelToCreate = headerLevel == 0 ? 2 : headerLevel - 1; + + if (headerLevelToCreate > 0) { + + // The button only creates level 1 and 2 underline headers. + // Why not have it iterate over hash header levels? Wouldn't that be easier and cleaner? + var headerChar = headerLevelToCreate >= 2 ? "-" : "="; + var len = chunk.selection.length; + if (len > SETTINGS.lineLength) { + len = SETTINGS.lineLength; + } + chunk.endTag = "\n"; + while (len--) { + chunk.endTag += headerChar; + } + } + }; + + commandProto.doHorizontalRule = function (chunk, postProcessing) { + chunk.startTag = "----------\n"; + chunk.selection = ""; + chunk.skipLines(2, 1, true); + } + + commandProto.doMore = function (chunk, postProcessing) { + chunk.startTag = "\n\n"; + chunk.selection = ""; + chunk.skipLines(2, 0, true); + } + + commandProto.doTab = function (chunk, postProcessing) { + chunk.startTag = " "; + chunk.selection = ""; + } + + function FullScreenManager (hooks, getString) { + this.fullScreenBind = false; + this.hooks = hooks; + this.getString = getString; + this.isFakeFullScreen = false; + } + + function getFullScreenAdapter () { + var selector = { + fullScreenChange : ['onfullscreenchange', 'onwebkitfullscreenchange', 'onmozfullscreenchange', 'onmsfullscreenchange'], + requestFullscreen : ['requestFullscreen', 'webkitRequestFullScreen', 'mozRequestFullScreen', 'msRequestFullScreen'], + cancelFullscreen : ['cancelFullscreen', 'exitFullScreen', 'webkitCancelFullScreen', 'mozCancelFullScreen', 'msCancelFullScreen'] + }, adapter = {}; + + for (var name in selector) { + var len = selector[name].length, found = false; + + for (var i = 0; i < len; i ++) { + var method = selector[name][i]; + + if ('undefined' != typeof(document[method]) || 'undefined' != typeof(document.body[method])) { + adapter[name] = method; + found = true; + break; + } + } + + if (!found) { + return false; + } + } + + return adapter; + }; + + function isFullScreen () { + return document.fullScreen || + document.mozFullScreen || + document.webkitIsFullScreen || + document.msIsFullScreen; + }; + + // fullscreen + FullScreenManager.prototype.doFullScreen = function (buttons, enter) { + var adapter = getFullScreenAdapter(), self = this; + + if (!adapter) { + alert(self.getString('fullscreenUnsupport')); + return false; + } + + if (!this.fullScreenBind) { + util.addEvent(document, adapter.fullScreenChange.substring(2), function () { + if (!isFullScreen()) { + buttons.fullscreen.style.display = ''; + buttons.exitFullscreen.style.display = 'none'; + self.hooks.exitFullScreen(); + } else { + buttons.fullscreen.style.display = 'none'; + buttons.exitFullscreen.style.display = ''; + self.hooks.enterFullScreen(); + } + }); + + this.fullScreenBind = true; + } + + if (enter) { + if (self.isFakeFullScreen) { + document.body[adapter.requestFullscreen]('webkitRequestFullScreen' == adapter.requestFullscreen + ? Element.ALLOW_KEYBOARD_INPUT : null); + self.isFakeFullScreen = false; + } else if (!isFullScreen()) { + buttons.exitFullscreen.style.display = ''; + self.hooks.enterFakeFullScreen(); + self.isFakeFullScreen = true; + } + } else { + if (self.isFakeFullScreen) { + buttons.exitFullscreen.style.display = 'none'; + self.hooks.exitFullScreen(); + } else if (isFullScreen()) { + document[adapter.cancelFullscreen](); + } + + self.isFakeFullScreen = false; + } + }; +})(); diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/plupload.js b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/plupload.js new file mode 100644 index 0000000..9531989 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/plupload.js @@ -0,0 +1,2273 @@ +/** + * Plupload - multi-runtime File Uploader + * v2.1.1 + * + * Copyright 2013, Moxiecode Systems AB + * Released under GPL License. + * + * License: http://www.plupload.com/license + * Contributing: http://www.plupload.com/contributing + * + * Date: 2014-01-16 + */ +/** + * Plupload.js + * + * Copyright 2013, Moxiecode Systems AB + * Released under GPL License. + * + * License: http://www.plupload.com/license + * Contributing: http://www.plupload.com/contributing + */ + +/*global mOxie:true */ + +;(function(window, o, undef) { + +var delay = window.setTimeout +, fileFilters = {} +; + +// convert plupload features to caps acceptable by mOxie +function normalizeCaps(settings) { + var features = settings.required_features, caps = {}; + + function resolve(feature, value, strict) { + // Feature notation is deprecated, use caps (this thing here is required for backward compatibility) + var map = { + chunks: 'slice_blob', + jpgresize: 'send_binary_string', + pngresize: 'send_binary_string', + progress: 'report_upload_progress', + multi_selection: 'select_multiple', + dragdrop: 'drag_and_drop', + drop_element: 'drag_and_drop', + headers: 'send_custom_headers', + canSendBinary: 'send_binary', + triggerDialog: 'summon_file_dialog' + }; + + if (map[feature]) { + caps[map[feature]] = value; + } else if (!strict) { + caps[feature] = value; + } + } + + if (typeof(features) === 'string') { + plupload.each(features.split(/\s*,\s*/), function(feature) { + resolve(feature, true); + }); + } else if (typeof(features) === 'object') { + plupload.each(features, function(value, feature) { + resolve(feature, value); + }); + } else if (features === true) { + // check settings for required features + if (!settings.multipart) { // special care for multipart: false + caps.send_binary_string = true; + } + + if (settings.chunk_size > 0) { + caps.slice_blob = true; + } + + if (settings.resize.enabled) { + caps.send_binary_string = true; + } + + plupload.each(settings, function(value, feature) { + resolve(feature, !!value, true); // strict check + }); + } + + return caps; +} + +/** + * @module plupload + * @static + */ +var plupload = { + /** + * Plupload version will be replaced on build. + * + * @property VERSION + * @for Plupload + * @static + * @final + */ + VERSION : '2.1.1', + + /** + * Inital state of the queue and also the state ones it's finished all it's uploads. + * + * @property STOPPED + * @static + * @final + */ + STOPPED : 1, + + /** + * Upload process is running + * + * @property STARTED + * @static + * @final + */ + STARTED : 2, + + /** + * File is queued for upload + * + * @property QUEUED + * @static + * @final + */ + QUEUED : 1, + + /** + * File is being uploaded + * + * @property UPLOADING + * @static + * @final + */ + UPLOADING : 2, + + /** + * File has failed to be uploaded + * + * @property FAILED + * @static + * @final + */ + FAILED : 4, + + /** + * File has been uploaded successfully + * + * @property DONE + * @static + * @final + */ + DONE : 5, + + // Error constants used by the Error event + + /** + * Generic error for example if an exception is thrown inside Silverlight. + * + * @property GENERIC_ERROR + * @static + * @final + */ + GENERIC_ERROR : -100, + + /** + * HTTP transport error. For example if the server produces a HTTP status other than 200. + * + * @property HTTP_ERROR + * @static + * @final + */ + HTTP_ERROR : -200, + + /** + * Generic I/O error. For exampe if it wasn't possible to open the file stream on local machine. + * + * @property IO_ERROR + * @static + * @final + */ + IO_ERROR : -300, + + /** + * Generic I/O error. For exampe if it wasn't possible to open the file stream on local machine. + * + * @property SECURITY_ERROR + * @static + * @final + */ + SECURITY_ERROR : -400, + + /** + * Initialization error. Will be triggered if no runtime was initialized. + * + * @property INIT_ERROR + * @static + * @final + */ + INIT_ERROR : -500, + + /** + * File size error. If the user selects a file that is too large it will be blocked and an error of this type will be triggered. + * + * @property FILE_SIZE_ERROR + * @static + * @final + */ + FILE_SIZE_ERROR : -600, + + /** + * File extension error. If the user selects a file that isn't valid according to the filters setting. + * + * @property FILE_EXTENSION_ERROR + * @static + * @final + */ + FILE_EXTENSION_ERROR : -601, + + /** + * Duplicate file error. If prevent_duplicates is set to true and user selects the same file again. + * + * @property FILE_DUPLICATE_ERROR + * @static + * @final + */ + FILE_DUPLICATE_ERROR : -602, + + /** + * Runtime will try to detect if image is proper one. Otherwise will throw this error. + * + * @property IMAGE_FORMAT_ERROR + * @static + * @final + */ + IMAGE_FORMAT_ERROR : -700, + + /** + * While working on the image runtime will try to detect if the operation may potentially run out of memeory and will throw this error. + * + * @property IMAGE_MEMORY_ERROR + * @static + * @final + */ + IMAGE_MEMORY_ERROR : -701, + + /** + * Each runtime has an upper limit on a dimension of the image it can handle. If bigger, will throw this error. + * + * @property IMAGE_DIMENSIONS_ERROR + * @static + * @final + */ + IMAGE_DIMENSIONS_ERROR : -702, + + /** + * Mime type lookup table. + * + * @property mimeTypes + * @type Object + * @final + */ + mimeTypes : o.mimes, + + /** + * In some cases sniffing is the only way around :( + */ + ua: o.ua, + + /** + * Gets the true type of the built-in object (better version of typeof). + * @credits Angus Croll (http://javascriptweblog.wordpress.com/) + * + * @method typeOf + * @static + * @param {Object} o Object to check. + * @return {String} Object [[Class]] + */ + typeOf: o.typeOf, + + /** + * Extends the specified object with another object. + * + * @method extend + * @static + * @param {Object} target Object to extend. + * @param {Object..} obj Multiple objects to extend with. + * @return {Object} Same as target, the extended object. + */ + extend : o.extend, + + /** + * Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers. + * The only way a user would be able to get the same ID is if the two persons at the same exact milisecond manages + * to get 5 the same random numbers between 0-65535 it also uses a counter so each call will be guaranteed to be page unique. + * It's more probable for the earth to be hit with an ansteriod. You can also if you want to be 100% sure set the plupload.guidPrefix property + * to an user unique key. + * + * @method guid + * @static + * @return {String} Virtually unique id. + */ + guid : o.guid, + + /** + * Get array of DOM Elements by their ids. + * + * @method get + * @for Utils + * @param {String} id Identifier of the DOM Element + * @return {Array} + */ + get : function get(ids) { + var els = [], el; + + if (o.typeOf(ids) !== 'array') { + ids = [ids]; + } + + var i = ids.length; + while (i--) { + el = o.get(ids[i]); + if (el) { + els.push(el); + } + } + + return els.length ? els : null; + }, + + /** + * Executes the callback function for each item in array/object. If you return false in the + * callback it will break the loop. + * + * @method each + * @static + * @param {Object} obj Object to iterate. + * @param {function} callback Callback function to execute for each item. + */ + each : o.each, + + /** + * Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields. + * + * @method getPos + * @static + * @param {Element} node HTML element or element id to get x, y position from. + * @param {Element} root Optional root element to stop calculations at. + * @return {object} Absolute position of the specified element object with x, y fields. + */ + getPos : o.getPos, + + /** + * Returns the size of the specified node in pixels. + * + * @method getSize + * @static + * @param {Node} node Node to get the size of. + * @return {Object} Object with a w and h property. + */ + getSize : o.getSize, + + /** + * Encodes the specified string. + * + * @method xmlEncode + * @static + * @param {String} s String to encode. + * @return {String} Encoded string. + */ + xmlEncode : function(str) { + var xmlEncodeChars = {'<' : 'lt', '>' : 'gt', '&' : 'amp', '"' : 'quot', '\'' : '#39'}, xmlEncodeRegExp = /[<>&\"\']/g; + + return str ? ('' + str).replace(xmlEncodeRegExp, function(chr) { + return xmlEncodeChars[chr] ? '&' + xmlEncodeChars[chr] + ';' : chr; + }) : str; + }, + + /** + * Forces anything into an array. + * + * @method toArray + * @static + * @param {Object} obj Object with length field. + * @return {Array} Array object containing all items. + */ + toArray : o.toArray, + + /** + * Find an element in array and return it's index if present, otherwise return -1. + * + * @method inArray + * @static + * @param {mixed} needle Element to find + * @param {Array} array + * @return {Int} Index of the element, or -1 if not found + */ + inArray : o.inArray, + + /** + * Extends the language pack object with new items. + * + * @method addI18n + * @static + * @param {Object} pack Language pack items to add. + * @return {Object} Extended language pack object. + */ + addI18n : o.addI18n, + + /** + * Translates the specified string by checking for the english string in the language pack lookup. + * + * @method translate + * @static + * @param {String} str String to look for. + * @return {String} Translated string or the input string if it wasn't found. + */ + translate : o.translate, + + /** + * Checks if object is empty. + * + * @method isEmptyObj + * @static + * @param {Object} obj Object to check. + * @return {Boolean} + */ + isEmptyObj : o.isEmptyObj, + + /** + * Checks if specified DOM element has specified class. + * + * @method hasClass + * @static + * @param {Object} obj DOM element like object to add handler to. + * @param {String} name Class name + */ + hasClass : o.hasClass, + + /** + * Adds specified className to specified DOM element. + * + * @method addClass + * @static + * @param {Object} obj DOM element like object to add handler to. + * @param {String} name Class name + */ + addClass : o.addClass, + + /** + * Removes specified className from specified DOM element. + * + * @method removeClass + * @static + * @param {Object} obj DOM element like object to add handler to. + * @param {String} name Class name + */ + removeClass : o.removeClass, + + /** + * Returns a given computed style of a DOM element. + * + * @method getStyle + * @static + * @param {Object} obj DOM element like object. + * @param {String} name Style you want to get from the DOM element + */ + getStyle : o.getStyle, + + /** + * Adds an event handler to the specified object and store reference to the handler + * in objects internal Plupload registry (@see removeEvent). + * + * @method addEvent + * @static + * @param {Object} obj DOM element like object to add handler to. + * @param {String} name Name to add event listener to. + * @param {Function} callback Function to call when event occurs. + * @param {String} (optional) key that might be used to add specifity to the event record. + */ + addEvent : o.addEvent, + + /** + * Remove event handler from the specified object. If third argument (callback) + * is not specified remove all events with the specified name. + * + * @method removeEvent + * @static + * @param {Object} obj DOM element to remove event listener(s) from. + * @param {String} name Name of event listener to remove. + * @param {Function|String} (optional) might be a callback or unique key to match. + */ + removeEvent: o.removeEvent, + + /** + * Remove all kind of events from the specified object + * + * @method removeAllEvents + * @static + * @param {Object} obj DOM element to remove event listeners from. + * @param {String} (optional) unique key to match, when removing events. + */ + removeAllEvents: o.removeAllEvents, + + /** + * Cleans the specified name from national characters (diacritics). The result will be a name with only a-z, 0-9 and _. + * + * @method cleanName + * @static + * @param {String} s String to clean up. + * @return {String} Cleaned string. + */ + cleanName : function(name) { + var i, lookup; + + // Replace diacritics + lookup = [ + /[\300-\306]/g, 'A', /[\340-\346]/g, 'a', + /\307/g, 'C', /\347/g, 'c', + /[\310-\313]/g, 'E', /[\350-\353]/g, 'e', + /[\314-\317]/g, 'I', /[\354-\357]/g, 'i', + /\321/g, 'N', /\361/g, 'n', + /[\322-\330]/g, 'O', /[\362-\370]/g, 'o', + /[\331-\334]/g, 'U', /[\371-\374]/g, 'u' + ]; + + for (i = 0; i < lookup.length; i += 2) { + name = name.replace(lookup[i], lookup[i + 1]); + } + + // Replace whitespace + name = name.replace(/\s+/g, '_'); + + // Remove anything else + name = name.replace(/[^a-z0-9_\-\.]+/gi, ''); + + return name; + }, + + /** + * Builds a full url out of a base URL and an object with items to append as query string items. + * + * @method buildUrl + * @static + * @param {String} url Base URL to append query string items to. + * @param {Object} items Name/value object to serialize as a querystring. + * @return {String} String with url + serialized query string items. + */ + buildUrl : function(url, items) { + var query = ''; + + plupload.each(items, function(value, name) { + query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value); + }); + + if (query) { + url += (url.indexOf('?') > 0 ? '&' : '?') + query; + } + + return url; + }, + + /** + * Formats the specified number as a size string for example 1024 becomes 1 KB. + * + * @method formatSize + * @static + * @param {Number} size Size to format as string. + * @return {String} Formatted size string. + */ + formatSize : function(size) { + + if (size === undef || /\D/.test(size)) { + return plupload.translate('N/A'); + } + + function round(num, precision) { + return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision); + } + + var boundary = Math.pow(1024, 4); + + // TB + if (size > boundary) { + return round(size / boundary, 1) + " " + plupload.translate('tb'); + } + + // GB + if (size > (boundary/=1024)) { + return round(size / boundary, 1) + " " + plupload.translate('gb'); + } + + // MB + if (size > (boundary/=1024)) { + return round(size / boundary, 1) + " " + plupload.translate('mb'); + } + + // KB + if (size > 1024) { + return Math.round(size / 1024) + " " + plupload.translate('kb'); + } + + return size + " " + plupload.translate('b'); + }, + + + /** + * Parses the specified size string into a byte value. For example 10kb becomes 10240. + * + * @method parseSize + * @static + * @param {String|Number} size String to parse or number to just pass through. + * @return {Number} Size in bytes. + */ + parseSize : o.parseSizeStr, + + + /** + * A way to predict what runtime will be choosen in the current environment with the + * specified settings. + * + * @method predictRuntime + * @static + * @param {Object|String} config Plupload settings to check + * @param {String} [runtimes] Comma-separated list of runtimes to check against + * @return {String} Type of compatible runtime + */ + predictRuntime : function(config, runtimes) { + var up, runtime; + + up = new plupload.Uploader(config); + runtime = o.Runtime.thatCan(up.getOption().required_features, runtimes || config.runtimes); + up.destroy(); + return runtime; + }, + + /** + * Registers a filter that will be executed for each file added to the queue. + * If callback returns false, file will not be added. + * + * Callback receives two arguments: a value for the filter as it was specified in settings.filters + * and a file to be filtered. Callback is executed in the context of uploader instance. + * + * @method addFileFilter + * @static + * @param {String} name Name of the filter by which it can be referenced in settings.filters + * @param {String} cb Callback - the actual routine that every added file must pass + */ + addFileFilter: function(name, cb) { + fileFilters[name] = cb; + } +}; + + +plupload.addFileFilter('mime_types', function(filters, file, cb) { + if (filters.length && !filters.regexp.test(file.name)) { + this.trigger('Error', { + code : plupload.FILE_EXTENSION_ERROR, + message : plupload.translate('File extension error.'), + file : file + }); + cb(false); + } else { + cb(true); + } +}); + + +plupload.addFileFilter('max_file_size', function(maxSize, file, cb) { + var undef; + + maxSize = plupload.parseSize(maxSize); + + // Invalid file size + if (file.size !== undef && maxSize && file.size > maxSize) { + this.trigger('Error', { + code : plupload.FILE_SIZE_ERROR, + message : plupload.translate('File size error.'), + file : file + }); + cb(false); + } else { + cb(true); + } +}); + + +plupload.addFileFilter('prevent_duplicates', function(value, file, cb) { + if (value) { + var ii = this.files.length; + while (ii--) { + // Compare by name and size (size might be 0 or undefined, but still equivalent for both) + if (file.name === this.files[ii].name && file.size === this.files[ii].size) { + this.trigger('Error', { + code : plupload.FILE_DUPLICATE_ERROR, + message : plupload.translate('Duplicate file error.'), + file : file + }); + cb(false); + return; + } + } + } + cb(true); +}); + + +/** +@class Uploader +@constructor + +@param {Object} settings For detailed information about each option check documentation. + @param {String|DOMElement} settings.browse_button id of the DOM element or DOM element itself to use as file dialog trigger. + @param {String} settings.url URL of the server-side upload handler. + @param {Number|String} [settings.chunk_size=0] Chunk size in bytes to slice the file into. Shorcuts with b, kb, mb, gb, tb suffixes also supported. `e.g. 204800 or "204800b" or "200kb"`. By default - disabled. + @param {String} [settings.container] id of the DOM element to use as a container for uploader structures. Defaults to document.body. + @param {String|DOMElement} [settings.drop_element] id of the DOM element or DOM element itself to use as a drop zone for Drag-n-Drop. + @param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message. + @param {Object} [settings.filters={}] Set of file type filters. + @param {Array} [settings.filters.mime_types=[]] List of file types to accept, each one defined by title and list of extensions. `e.g. {title : "Image files", extensions : "jpg,jpeg,gif,png"}`. Dispatches `plupload.FILE_EXTENSION_ERROR` + @param {String|Number} [settings.filters.max_file_size=0] Maximum file size that the user can pick, in bytes. Optionally supports b, kb, mb, gb, tb suffixes. `e.g. "10mb" or "1gb"`. By default - not set. Dispatches `plupload.FILE_SIZE_ERROR`. + @param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`. + @param {String} [settings.flash_swf_url] URL of the Flash swf. + @param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs. + @param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event. + @param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message. + @param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload. + @param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog. + @param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess. + @param {Object} [settings.resize] Enable resizng of images on client-side. Applies to `image/jpeg` and `image/png` only. `e.g. {width : 200, height : 200, quality : 90, crop: true}` + @param {Number} [settings.resize.width] If image is bigger, it will be resized. + @param {Number} [settings.resize.height] If image is bigger, it will be resized. + @param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100). + @param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally. + @param {String} [settings.runtimes="html5,flash,silverlight,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails. + @param {String} [settings.silverlight_xap_url] URL of the Silverlight xap. + @param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files. +*/ +plupload.Uploader = function(options) { + /** + * Fires when the current RunTime has been initialized. + * + * @event Init + * @param {plupload.Uploader} uploader Uploader instance sending the event. + */ + + /** + * Fires after the init event incase you need to perform actions there. + * + * @event PostInit + * @param {plupload.Uploader} uploader Uploader instance sending the event. + */ + + /** + * Fires when the option is changed in via uploader.setOption(). + * + * @event OptionChanged + * @since 2.1 + * @param {plupload.Uploader} uploader Uploader instance sending the event. + * @param {String} name Name of the option that was changed + * @param {Mixed} value New value for the specified option + * @param {Mixed} oldValue Previous value of the option + */ + + /** + * Fires when the silverlight/flash or other shim needs to move. + * + * @event Refresh + * @param {plupload.Uploader} uploader Uploader instance sending the event. + */ + + /** + * Fires when the overall state is being changed for the upload queue. + * + * @event StateChanged + * @param {plupload.Uploader} uploader Uploader instance sending the event. + */ + + /** + * Fires when a file is to be uploaded by the runtime. + * + * @event UploadFile + * @param {plupload.Uploader} uploader Uploader instance sending the event. + * @param {plupload.File} file File to be uploaded. + */ + + /** + * Fires when just before a file is uploaded. This event enables you to override settings + * on the uploader instance before the file is uploaded. + * + * @event BeforeUpload + * @param {plupload.Uploader} uploader Uploader instance sending the event. + * @param {plupload.File} file File to be uploaded. + */ + + /** + * Fires when the file queue is changed. In other words when files are added/removed to the files array of the uploader instance. + * + * @event QueueChanged + * @param {plupload.Uploader} uploader Uploader instance sending the event. + */ + + /** + * Fires while a file is being uploaded. Use this event to update the current file upload progress. + * + * @event UploadProgress + * @param {plupload.Uploader} uploader Uploader instance sending the event. + * @param {plupload.File} file File that is currently being uploaded. + */ + + /** + * Fires when file is removed from the queue. + * + * @event FilesRemoved + * @param {plupload.Uploader} uploader Uploader instance sending the event. + * @param {Array} files Array of files that got removed. + */ + + /** + * Fires for every filtered file before it is added to the queue. + * + * @event FileFiltered + * @since 2.1 + * @param {plupload.Uploader} uploader Uploader instance sending the event. + * @param {plupload.File} file Another file that has to be added to the queue. + */ + + /** + * Fires after files were filtered and added to the queue. + * + * @event FilesAdded + * @param {plupload.Uploader} uploader Uploader instance sending the event. + * @param {Array} files Array of file objects that were added to queue by the user. + */ + + /** + * Fires when a file is successfully uploaded. + * + * @event FileUploaded + * @param {plupload.Uploader} uploader Uploader instance sending the event. + * @param {plupload.File} file File that was uploaded. + * @param {Object} response Object with response properties. + */ + + /** + * Fires when file chunk is uploaded. + * + * @event ChunkUploaded + * @param {plupload.Uploader} uploader Uploader instance sending the event. + * @param {plupload.File} file File that the chunk was uploaded for. + * @param {Object} response Object with response properties. + */ + + /** + * Fires when all files in a queue are uploaded. + * + * @event UploadComplete + * @param {plupload.Uploader} uploader Uploader instance sending the event. + * @param {Array} files Array of file objects that was added to queue/selected by the user. + */ + + /** + * Fires when a error occurs. + * + * @event Error + * @param {plupload.Uploader} uploader Uploader instance sending the event. + * @param {Object} error Contains code, message and sometimes file and other details. + */ + + /** + * Fires when destroy method is called. + * + * @event Destroy + * @param {plupload.Uploader} uploader Uploader instance sending the event. + */ + var uid = plupload.guid() + , settings + , files = [] + , preferred_caps = {} + , fileInputs = [] + , fileDrops = [] + , startTime + , total + , disabled = false + , xhr + ; + + + // Private methods + function uploadNext() { + var file, count = 0, i; + + if (this.state == plupload.STARTED) { + // Find first QUEUED file + for (i = 0; i < files.length; i++) { + if (!file && files[i].status == plupload.QUEUED) { + file = files[i]; + if (this.trigger("BeforeUpload", file)) { + file.status = plupload.UPLOADING; + this.trigger("UploadFile", file); + } + } else { + count++; + } + } + + // All files are DONE or FAILED + if (count == files.length) { + if (this.state !== plupload.STOPPED) { + this.state = plupload.STOPPED; + this.trigger("StateChanged"); + } + this.trigger("UploadComplete", files); + } + } + } + + + function calcFile(file) { + file.percent = file.size > 0 ? Math.ceil(file.loaded / file.size * 100) : 100; + calc(); + } + + + function calc() { + var i, file; + + // Reset stats + total.reset(); + + // Check status, size, loaded etc on all files + for (i = 0; i < files.length; i++) { + file = files[i]; + + if (file.size !== undef) { + // We calculate totals based on original file size + total.size += file.origSize; + + // Since we cannot predict file size after resize, we do opposite and + // interpolate loaded amount to match magnitude of total + total.loaded += file.loaded * file.origSize / file.size; + } else { + total.size = undef; + } + + if (file.status == plupload.DONE) { + total.uploaded++; + } else if (file.status == plupload.FAILED) { + total.failed++; + } else { + total.queued++; + } + } + + // If we couldn't calculate a total file size then use the number of files to calc percent + if (total.size === undef) { + total.percent = files.length > 0 ? Math.ceil(total.uploaded / files.length * 100) : 0; + } else { + total.bytesPerSec = Math.ceil(total.loaded / ((+new Date() - startTime || 1) / 1000.0)); + total.percent = total.size > 0 ? Math.ceil(total.loaded / total.size * 100) : 0; + } + } + + + function getRUID() { + var ctrl = fileInputs[0] || fileDrops[0]; + if (ctrl) { + return ctrl.getRuntime().uid; + } + return false; + } + + + function runtimeCan(file, cap) { + if (file.ruid) { + var info = o.Runtime.getInfo(file.ruid); + if (info) { + return info.can(cap); + } + } + return false; + } + + + function bindEventListeners() { + this.bind('FilesAdded', onFilesAdded); + + this.bind('CancelUpload', onCancelUpload); + + this.bind('BeforeUpload', onBeforeUpload); + + this.bind('UploadFile', onUploadFile); + + this.bind('UploadProgress', onUploadProgress); + + this.bind('StateChanged', onStateChanged); + + this.bind('QueueChanged', calc); + + this.bind('Error', onError); + + this.bind('FileUploaded', onFileUploaded); + + this.bind('Destroy', onDestroy); + } + + + function initControls(settings, cb) { + var self = this, inited = 0, queue = []; + + // common settings + var options = { + accept: settings.filters.mime_types, + runtime_order: settings.runtimes, + required_caps: settings.required_features, + preferred_caps: preferred_caps, + swf_url: settings.flash_swf_url, + xap_url: settings.silverlight_xap_url + }; + + // add runtime specific options if any + plupload.each(settings.runtimes.split(/\s*,\s*/), function(runtime) { + if (settings[runtime]) { + options[runtime] = settings[runtime]; + } + }); + + // initialize file pickers - there can be many + if (settings.browse_button) { + plupload.each(settings.browse_button, function(el) { + queue.push(function(cb) { + var fileInput = new o.FileInput(plupload.extend({}, options, { + name: settings.file_data_name, + multiple: settings.multi_selection, + container: settings.container, + browse_button: el + })); + + fileInput.onready = function() { + var info = o.Runtime.getInfo(this.ruid); + + // for backward compatibility + o.extend(self.features, { + chunks: info.can('slice_blob'), + multipart: info.can('send_multipart'), + multi_selection: info.can('select_multiple') + }); + + inited++; + fileInputs.push(this); + cb(); + }; + + fileInput.onchange = function() { + self.addFile(this.files); + }; + + fileInput.bind('mouseenter mouseleave mousedown mouseup', function(e) { + if (!disabled) { + if (settings.browse_button_hover) { + if ('mouseenter' === e.type) { + o.addClass(el, settings.browse_button_hover); + } else if ('mouseleave' === e.type) { + o.removeClass(el, settings.browse_button_hover); + } + } + + if (settings.browse_button_active) { + if ('mousedown' === e.type) { + o.addClass(el, settings.browse_button_active); + } else if ('mouseup' === e.type) { + o.removeClass(el, settings.browse_button_active); + } + } + } + }); + + fileInput.bind('error runtimeerror', function() { + fileInput = null; + cb(); + }); + + fileInput.init(); + }); + }); + } + + // initialize drop zones + if (settings.drop_element) { + plupload.each(settings.drop_element, function(el) { + queue.push(function(cb) { + var fileDrop = new o.FileDrop(plupload.extend({}, options, { + drop_zone: el + })); + + fileDrop.onready = function() { + var info = o.Runtime.getInfo(this.ruid); + + self.features.dragdrop = info.can('drag_and_drop'); // for backward compatibility + + inited++; + fileDrops.push(this); + cb(); + }; + + fileDrop.ondrop = function() { + self.addFile(this.files); + }; + + fileDrop.bind('error runtimeerror', function() { + fileDrop = null; + cb(); + }); + + fileDrop.init(); + }); + }); + } + + + o.inSeries(queue, function() { + if (typeof(cb) === 'function') { + cb(inited); + } + }); + } + + + function resizeImage(blob, params, cb) { + var img = new o.Image(); + + try { + img.onload = function() { + img.downsize(params.width, params.height, params.crop, params.preserve_headers); + }; + + img.onresize = function() { + cb(this.getAsBlob(blob.type, params.quality)); + this.destroy(); + }; + + img.onerror = function() { + cb(blob); + }; + + img.load(blob); + } catch(ex) { + cb(blob); + } + } + + + function setOption(option, value, init) { + var self = this, reinitRequired = false; + + function _setOption(option, value, init) { + var oldValue = settings[option]; + + switch (option) { + case 'max_file_size': + if (option === 'max_file_size') { + settings.max_file_size = settings.filters.max_file_size = value; + } + break; + + case 'chunk_size': + if (value = plupload.parseSize(value)) { + settings[option] = value; + } + break; + + case 'filters': + // for sake of backward compatibility + if (plupload.typeOf(value) === 'array') { + value = { + mime_types: value + }; + } + + if (init) { + plupload.extend(settings.filters, value); + } else { + settings.filters = value; + } + + // if file format filters are being updated, regenerate the matching expressions + if (value.mime_types) { + settings.filters.mime_types.regexp = (function(filters) { + var extensionsRegExp = []; + + plupload.each(filters, function(filter) { + plupload.each(filter.extensions.split(/,/), function(ext) { + if (/^\s*\*\s*$/.test(ext)) { + extensionsRegExp.push('\\.*'); + } else { + extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&')); + } + }); + }); + + return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i'); + }(settings.filters.mime_types)); + } + break; + + case 'resize': + if (init) { + plupload.extend(settings.resize, value, { + enabled: true + }); + } else { + settings.resize = value; + } + break; + + case 'prevent_duplicates': + settings.prevent_duplicates = settings.filters.prevent_duplicates = !!value; + break; + + case 'browse_button': + case 'drop_element': + value = plupload.get(value); + + case 'container': + case 'runtimes': + case 'multi_selection': + case 'flash_swf_url': + case 'silverlight_xap_url': + settings[option] = value; + if (!init) { + reinitRequired = true; + } + break; + + default: + settings[option] = value; + } + + if (!init) { + self.trigger('OptionChanged', option, value, oldValue); + } + } + + if (typeof(option) === 'object') { + plupload.each(option, function(value, option) { + _setOption(option, value, init); + }); + } else { + _setOption(option, value, init); + } + + if (init) { + // Normalize the list of required capabilities + settings.required_features = normalizeCaps(plupload.extend({}, settings)); + + // Come up with the list of capabilities that can affect default mode in a multi-mode runtimes + preferred_caps = normalizeCaps(plupload.extend({}, settings, { + required_features: true + })); + } else if (reinitRequired) { + self.trigger('Destroy'); + + initControls.call(self, settings, function(inited) { + if (inited) { + self.runtime = o.Runtime.getInfo(getRUID()).type; + self.trigger('Init', { runtime: self.runtime }); + self.trigger('PostInit'); + } else { + self.trigger('Error', { + code : plupload.INIT_ERROR, + message : plupload.translate('Init error.') + }); + } + }); + } + } + + + // Internal event handlers + function onFilesAdded(up, filteredFiles) { + // Add files to queue + [].push.apply(files, filteredFiles); + + up.trigger('QueueChanged'); + up.refresh(); + } + + + function onBeforeUpload(up, file) { + // Generate unique target filenames + if (settings.unique_names) { + var matches = file.name.match(/\.([^.]+)$/), ext = "part"; + if (matches) { + ext = matches[1]; + } + file.target_name = file.id + '.' + ext; + } + } + + + function onUploadFile(up, file) { + var url = up.settings.url + , chunkSize = up.settings.chunk_size + , retries = up.settings.max_retries + , features = up.features + , offset = 0 + , blob + ; + + // make sure we start at a predictable offset + if (file.loaded) { + offset = file.loaded = chunkSize * Math.floor(file.loaded / chunkSize); + } + + function handleError() { + if (retries-- > 0) { + delay(uploadNextChunk, 1000); + } else { + file.loaded = offset; // reset all progress + + up.trigger('Error', { + code : plupload.HTTP_ERROR, + message : plupload.translate('HTTP Error.'), + file : file, + response : xhr.responseText, + status : xhr.status, + responseHeaders: xhr.getAllResponseHeaders() + }); + } + } + + function uploadNextChunk() { + var chunkBlob, formData, args, curChunkSize; + + // File upload finished + if (file.status == plupload.DONE || file.status == plupload.FAILED || up.state == plupload.STOPPED) { + return; + } + + // Standard arguments + args = {name : file.target_name || file.name}; + + if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory + curChunkSize = Math.min(chunkSize, blob.size - offset); + chunkBlob = blob.slice(offset, offset + curChunkSize); + } else { + curChunkSize = blob.size; + chunkBlob = blob; + } + + // If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller + if (chunkSize && features.chunks) { + // Setup query string arguments + if (up.settings.send_chunk_number) { + args.chunk = Math.ceil(offset / chunkSize); + args.chunks = Math.ceil(blob.size / chunkSize); + } else { // keep support for experimental chunk format, just in case + args.offset = offset; + args.total = blob.size; + } + } + + xhr = new o.XMLHttpRequest(); + + // Do we have upload progress support + if (xhr.upload) { + xhr.upload.onprogress = function(e) { + file.loaded = Math.min(file.size, offset + e.loaded); + up.trigger('UploadProgress', file); + }; + } + + xhr.onload = function() { + // check if upload made itself through + if (xhr.status >= 400) { + handleError(); + return; + } + + retries = up.settings.max_retries; // reset the counter + + // Handle chunk response + if (curChunkSize < blob.size) { + chunkBlob.destroy(); + + offset += curChunkSize; + file.loaded = Math.min(offset, blob.size); + + up.trigger('ChunkUploaded', file, { + offset : file.loaded, + total : blob.size, + response : xhr.responseText, + status : xhr.status, + responseHeaders: xhr.getAllResponseHeaders() + }); + + // stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them + if (o.Env.browser === 'Android Browser') { + // doesn't harm in general, but is not required anywhere else + up.trigger('UploadProgress', file); + } + } else { + file.loaded = file.size; + } + + chunkBlob = formData = null; // Free memory + + // Check if file is uploaded + if (!offset || offset >= blob.size) { + // If file was modified, destory the copy + if (file.size != file.origSize) { + blob.destroy(); + blob = null; + } + + up.trigger('UploadProgress', file); + + file.status = plupload.DONE; + + up.trigger('FileUploaded', file, { + response : xhr.responseText, + status : xhr.status, + responseHeaders: xhr.getAllResponseHeaders() + }); + } else { + // Still chunks left + delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere + } + }; + + xhr.onerror = function() { + handleError(); + }; + + xhr.onloadend = function() { + this.destroy(); + xhr = null; + }; + + // Build multipart request + if (up.settings.multipart && features.multipart) { + + args.name = file.target_name || file.name; + + xhr.open("post", url, true); + + // Set custom headers + plupload.each(up.settings.headers, function(value, name) { + xhr.setRequestHeader(name, value); + }); + + formData = new o.FormData(); + + // Add multipart params + plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) { + formData.append(name, value); + }); + + // Add file and send it + formData.append(up.settings.file_data_name, chunkBlob); + xhr.send(formData, { + runtime_order: up.settings.runtimes, + required_caps: up.settings.required_features, + preferred_caps: preferred_caps, + swf_url: up.settings.flash_swf_url, + xap_url: up.settings.silverlight_xap_url + }); + } else { + // if no multipart, send as binary stream + url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params)); + + xhr.open("post", url, true); + + xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header + + // Set custom headers + plupload.each(up.settings.headers, function(value, name) { + xhr.setRequestHeader(name, value); + }); + + xhr.send(chunkBlob, { + runtime_order: up.settings.runtimes, + required_caps: up.settings.required_features, + preferred_caps: preferred_caps, + swf_url: up.settings.flash_swf_url, + xap_url: up.settings.silverlight_xap_url + }); + } + } + + blob = file.getSource(); + + // Start uploading chunks + if (up.settings.resize.enabled && runtimeCan(blob, 'send_binary_string') && !!~o.inArray(blob.type, ['image/jpeg', 'image/png'])) { + // Resize if required + resizeImage.call(this, blob, up.settings.resize, function(resizedBlob) { + blob = resizedBlob; + file.size = resizedBlob.size; + uploadNextChunk(); + }); + } else { + uploadNextChunk(); + } + } + + + function onUploadProgress(up, file) { + calcFile(file); + } + + + function onStateChanged(up) { + if (up.state == plupload.STARTED) { + // Get start time to calculate bps + startTime = (+new Date()); + } else if (up.state == plupload.STOPPED) { + // Reset currently uploading files + for (var i = up.files.length - 1; i >= 0; i--) { + if (up.files[i].status == plupload.UPLOADING) { + up.files[i].status = plupload.QUEUED; + calc(); + } + } + } + } + + + function onCancelUpload() { + if (xhr) { + xhr.abort(); + } + } + + + function onFileUploaded(up) { + calc(); + + // Upload next file but detach it from the error event + // since other custom listeners might want to stop the queue + delay(function() { + uploadNext.call(up); + }, 1); + } + + + function onError(up, err) { + // Set failed status if an error occured on a file + if (err.file) { + err.file.status = plupload.FAILED; + calcFile(err.file); + + // Upload next file but detach it from the error event + // since other custom listeners might want to stop the queue + if (up.state == plupload.STARTED) { // upload in progress + up.trigger('CancelUpload'); + delay(function() { + uploadNext.call(up); + }, 1); + } + } + } + + + function onDestroy(up) { + up.stop(); + + // Purge the queue + plupload.each(files, function(file) { + file.destroy(); + }); + files = []; + + if (fileInputs.length) { + plupload.each(fileInputs, function(fileInput) { + fileInput.destroy(); + }); + fileInputs = []; + } + + if (fileDrops.length) { + plupload.each(fileDrops, function(fileDrop) { + fileDrop.destroy(); + }); + fileDrops = []; + } + + preferred_caps = {}; + disabled = false; + startTime = xhr = null; + total.reset(); + } + + + // Default settings + settings = { + runtimes: o.Runtime.order, + max_retries: 0, + chunk_size: 0, + multipart: true, + multi_selection: true, + file_data_name: 'file', + flash_swf_url: 'js/Moxie.swf', + silverlight_xap_url: 'js/Moxie.xap', + filters: { + mime_types: [], + prevent_duplicates: false, + max_file_size: 0 + }, + resize: { + enabled: false, + preserve_headers: true, + crop: false + }, + send_chunk_number: true // whether to send chunks and chunk numbers, or total and offset bytes + }; + + + setOption.call(this, options, null, true); + + // Inital total state + total = new plupload.QueueProgress(); + + // Add public methods + plupload.extend(this, { + + /** + * Unique id for the Uploader instance. + * + * @property id + * @type String + */ + id : uid, + uid : uid, // mOxie uses this to differentiate between event targets + + /** + * Current state of the total uploading progress. This one can either be plupload.STARTED or plupload.STOPPED. + * These states are controlled by the stop/start methods. The default value is STOPPED. + * + * @property state + * @type Number + */ + state : plupload.STOPPED, + + /** + * Map of features that are available for the uploader runtime. Features will be filled + * before the init event is called, these features can then be used to alter the UI for the end user. + * Some of the current features that might be in this map is: dragdrop, chunks, jpgresize, pngresize. + * + * @property features + * @type Object + */ + features : {}, + + /** + * Current runtime name. + * + * @property runtime + * @type String + */ + runtime : null, + + /** + * Current upload queue, an array of File instances. + * + * @property files + * @type Array + * @see plupload.File + */ + files : files, + + /** + * Object with name/value settings. + * + * @property settings + * @type Object + */ + settings : settings, + + /** + * Total progess information. How many files has been uploaded, total percent etc. + * + * @property total + * @type plupload.QueueProgress + */ + total : total, + + + /** + * Initializes the Uploader instance and adds internal event listeners. + * + * @method init + */ + init : function() { + var self = this; + + if (typeof(settings.preinit) == "function") { + settings.preinit(self); + } else { + plupload.each(settings.preinit, function(func, name) { + self.bind(name, func); + }); + } + + // Check for required options + if (!settings.browse_button || !settings.url) { + this.trigger('Error', { + code : plupload.INIT_ERROR, + message : plupload.translate('Init error.') + }); + return; + } + + bindEventListeners.call(this); + + initControls.call(this, settings, function(inited) { + if (typeof(settings.init) == "function") { + settings.init(self); + } else { + plupload.each(settings.init, function(func, name) { + self.bind(name, func); + }); + } + + if (inited) { + self.runtime = o.Runtime.getInfo(getRUID()).type; + self.trigger('Init', { runtime: self.runtime }); + self.trigger('PostInit'); + } else { + self.trigger('Error', { + code : plupload.INIT_ERROR, + message : plupload.translate('Init error.') + }); + } + }); + }, + + /** + * Set the value for the specified option(s). + * + * @method setOption + * @since 2.1 + * @param {String|Object} option Name of the option to change or the set of key/value pairs + * @param {Mixed} [value] Value for the option (is ignored, if first argument is object) + */ + setOption: function(option, value) { + setOption.call(this, option, value, !this.runtime); // until runtime not set we do not need to reinitialize + }, + + /** + * Get the value for the specified option or the whole configuration, if not specified. + * + * @method getOption + * @since 2.1 + * @param {String} [option] Name of the option to get + * @return {Mixed} Value for the option or the whole set + */ + getOption: function(option) { + if (!option) { + return settings; + } + return settings[option]; + }, + + /** + * Refreshes the upload instance by dispatching out a refresh event to all runtimes. + * This would for example reposition flash/silverlight shims on the page. + * + * @method refresh + */ + refresh : function() { + if (fileInputs.length) { + plupload.each(fileInputs, function(fileInput) { + fileInput.trigger('Refresh'); + }); + } + this.trigger('Refresh'); + }, + + /** + * Starts uploading the queued files. + * + * @method start + */ + start : function() { + if (this.state != plupload.STARTED) { + this.state = plupload.STARTED; + this.trigger('StateChanged'); + + uploadNext.call(this); + } + }, + + /** + * Stops the upload of the queued files. + * + * @method stop + */ + stop : function() { + if (this.state != plupload.STOPPED) { + this.state = plupload.STOPPED; + this.trigger('StateChanged'); + this.trigger('CancelUpload'); + } + }, + + + /** + * Disables/enables browse button on request. + * + * @method disableBrowse + * @param {Boolean} disable Whether to disable or enable (default: true) + */ + disableBrowse : function() { + disabled = arguments[0] !== undef ? arguments[0] : true; + + if (fileInputs.length) { + plupload.each(fileInputs, function(fileInput) { + fileInput.disable(disabled); + }); + } + + this.trigger('DisableBrowse', disabled); + }, + + /** + * Returns the specified file object by id. + * + * @method getFile + * @param {String} id File id to look for. + * @return {plupload.File} File object or undefined if it wasn't found; + */ + getFile : function(id) { + var i; + for (i = files.length - 1; i >= 0; i--) { + if (files[i].id === id) { + return files[i]; + } + } + }, + + /** + * Adds file to the queue programmatically. Can be native file, instance of Plupload.File, + * instance of mOxie.File, input[type="file"] element, or array of these. Fires FilesAdded, + * if any files were added to the queue. Otherwise nothing happens. + * + * @method addFile + * @since 2.0 + * @param {plupload.File|mOxie.File|File|Node|Array} file File or files to add to the queue. + * @param {String} [fileName] If specified, will be used as a name for the file + */ + addFile : function(file, fileName) { + var self = this + , queue = [] + , files = [] + , ruid + ; + + function filterFile(file, cb) { + var queue = []; + o.each(self.settings.filters, function(rule, name) { + if (fileFilters[name]) { + queue.push(function(cb) { + fileFilters[name].call(self, rule, file, function(res) { + cb(!res); + }); + }); + } + }); + o.inSeries(queue, cb); + } + + /** + * @method resolveFile + * @private + * @param {o.File|o.Blob|plupload.File|File|Blob|input[type="file"]} file + */ + function resolveFile(file) { + var type = o.typeOf(file); + + // o.File + if (file instanceof o.File) { + if (!file.ruid && !file.isDetached()) { + if (!ruid) { // weird case + return false; + } + file.ruid = ruid; + file.connectRuntime(ruid); + } + resolveFile(new plupload.File(file)); + } + // o.Blob + else if (file instanceof o.Blob) { + resolveFile(file.getSource()); + file.destroy(); + } + // plupload.File - final step for other branches + else if (file instanceof plupload.File) { + if (fileName) { + file.name = fileName; + } + + queue.push(function(cb) { + // run through the internal and user-defined filters, if any + filterFile(file, function(err) { + if (!err) { + files.push(file); + self.trigger("FileFiltered", file); + } + delay(cb, 1); // do not build up recursions or eventually we might hit the limits + }); + }); + } + // native File or blob + else if (o.inArray(type, ['file', 'blob']) !== -1) { + resolveFile(new o.File(null, file)); + } + // input[type="file"] + else if (type === 'node' && o.typeOf(file.files) === 'filelist') { + // if we are dealing with input[type="file"] + o.each(file.files, resolveFile); + } + // mixed array of any supported types (see above) + else if (type === 'array') { + fileName = null; // should never happen, but unset anyway to avoid funny situations + o.each(file, resolveFile); + } + } + + ruid = getRUID(); + + resolveFile(file); + + if (queue.length) { + o.inSeries(queue, function() { + // if any files left after filtration, trigger FilesAdded + if (files.length) { + self.trigger("FilesAdded", files); + } + }); + } + }, + + /** + * Removes a specific file. + * + * @method removeFile + * @param {plupload.File|String} file File to remove from queue. + */ + removeFile : function(file) { + var id = typeof(file) === 'string' ? file : file.id; + + for (var i = files.length - 1; i >= 0; i--) { + if (files[i].id === id) { + return this.splice(i, 1)[0]; + } + } + }, + + /** + * Removes part of the queue and returns the files removed. This will also trigger the FilesRemoved and QueueChanged events. + * + * @method splice + * @param {Number} start (Optional) Start index to remove from. + * @param {Number} length (Optional) Lengh of items to remove. + * @return {Array} Array of files that was removed. + */ + splice : function(start, length) { + // Splice and trigger events + var removed = files.splice(start === undef ? 0 : start, length === undef ? files.length : length); + + // if upload is in progress we need to stop it and restart after files are removed + var restartRequired = false; + if (this.state == plupload.STARTED) { // upload in progress + restartRequired = true; + this.stop(); + } + + this.trigger("FilesRemoved", removed); + + // Dispose any resources allocated by those files + plupload.each(removed, function(file) { + file.destroy(); + }); + + this.trigger("QueueChanged"); + this.refresh(); + + if (restartRequired) { + this.start(); + } + + return removed; + }, + + /** + * Dispatches the specified event name and it's arguments to all listeners. + * + * + * @method trigger + * @param {String} name Event name to fire. + * @param {Object..} Multiple arguments to pass along to the listener functions. + */ + + /** + * Check whether uploader has any listeners to the specified event. + * + * @method hasEventListener + * @param {String} name Event name to check for. + */ + + + /** + * Adds an event listener by name. + * + * @method bind + * @param {String} name Event name to listen for. + * @param {function} func Function to call ones the event gets fired. + * @param {Object} scope Optional scope to execute the specified function in. + */ + bind : function(name, func, scope) { + var self = this; + // adapt moxie EventTarget style to Plupload-like + plupload.Uploader.prototype.bind.call(this, name, function() { + var args = [].slice.call(arguments); + args.splice(0, 1, self); // replace event object with uploader instance + return func.apply(this, args); + }, 0, scope); + }, + + /** + * Removes the specified event listener. + * + * @method unbind + * @param {String} name Name of event to remove. + * @param {function} func Function to remove from listener. + */ + + /** + * Removes all event listeners. + * + * @method unbindAll + */ + + + /** + * Destroys Plupload instance and cleans after itself. + * + * @method destroy + */ + destroy : function() { + this.trigger('Destroy'); + settings = total = null; // purge these exclusively + this.unbindAll(); + } + }); +}; + +plupload.Uploader.prototype = o.EventTarget.instance; + +/** + * Constructs a new file instance. + * + * @class File + * @constructor + * + * @param {Object} file Object containing file properties + * @param {String} file.name Name of the file. + * @param {Number} file.size File size. + */ +plupload.File = (function() { + var filepool = {}; + + function PluploadFile(file) { + + plupload.extend(this, { + + /** + * File id this is a globally unique id for the specific file. + * + * @property id + * @type String + */ + id: plupload.guid(), + + /** + * File name for example "myfile.gif". + * + * @property name + * @type String + */ + name: file.name || file.fileName, + + /** + * File type, `e.g image/jpeg` + * + * @property type + * @type String + */ + type: file.type || '', + + /** + * File size in bytes (may change after client-side manupilation). + * + * @property size + * @type Number + */ + size: file.size || file.fileSize, + + /** + * Original file size in bytes. + * + * @property origSize + * @type Number + */ + origSize: file.size || file.fileSize, + + /** + * Number of bytes uploaded of the files total size. + * + * @property loaded + * @type Number + */ + loaded: 0, + + /** + * Number of percentage uploaded of the file. + * + * @property percent + * @type Number + */ + percent: 0, + + /** + * Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE. + * + * @property status + * @type Number + * @see plupload + */ + status: plupload.QUEUED, + + /** + * Date of last modification. + * + * @property lastModifiedDate + * @type {String} + */ + lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString(), // Thu Aug 23 2012 19:40:00 GMT+0400 (GET) + + /** + * Returns native window.File object, when it's available. + * + * @method getNative + * @return {window.File} or null, if plupload.File is of different origin + */ + getNative: function() { + var file = this.getSource().getSource(); + return o.inArray(o.typeOf(file), ['blob', 'file']) !== -1 ? file : null; + }, + + /** + * Returns mOxie.File - unified wrapper object that can be used across runtimes. + * + * @method getSource + * @return {mOxie.File} or null + */ + getSource: function() { + if (!filepool[this.id]) { + return null; + } + return filepool[this.id]; + }, + + /** + * Destroys plupload.File object. + * + * @method destroy + */ + destroy: function() { + var src = this.getSource(); + if (src) { + src.destroy(); + delete filepool[this.id]; + } + } + }); + + filepool[this.id] = file; + } + + return PluploadFile; +}()); + + +/** + * Constructs a queue progress. + * + * @class QueueProgress + * @constructor + */ + plupload.QueueProgress = function() { + var self = this; // Setup alias for self to reduce code size when it's compressed + + /** + * Total queue file size. + * + * @property size + * @type Number + */ + self.size = 0; + + /** + * Total bytes uploaded. + * + * @property loaded + * @type Number + */ + self.loaded = 0; + + /** + * Number of files uploaded. + * + * @property uploaded + * @type Number + */ + self.uploaded = 0; + + /** + * Number of files failed to upload. + * + * @property failed + * @type Number + */ + self.failed = 0; + + /** + * Number of files yet to be uploaded. + * + * @property queued + * @type Number + */ + self.queued = 0; + + /** + * Total percent of the uploaded bytes. + * + * @property percent + * @type Number + */ + self.percent = 0; + + /** + * Bytes uploaded per second. + * + * @property bytesPerSec + * @type Number + */ + self.bytesPerSec = 0; + + /** + * Resets the progress to it's initial values. + * + * @method reset + */ + self.reset = function() { + self.size = self.loaded = self.uploaded = self.failed = self.queued = self.percent = self.bytesPerSec = 0; + }; +}; + +window.plupload = plupload; + +}(window, mOxie)); diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/respond.js b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/respond.js new file mode 100644 index 0000000..7b2b759 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/respond.js @@ -0,0 +1,237 @@ +/*! Respond.js v1.4.2: min/max-width media query polyfill + * Copyright 2013 Scott Jehl + * Licensed under MIT + * http://j.mp/respondjs */ + +/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ +/*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ +(function(w) { + "use strict"; + w.matchMedia = w.matchMedia || function(doc, undefined) { + var bool, docElem = doc.documentElement, refNode = docElem.firstElementChild || docElem.firstChild, fakeBody = doc.createElement("body"), div = doc.createElement("div"); + div.id = "mq-test-1"; + div.style.cssText = "position:absolute;top:-100em"; + fakeBody.style.background = "none"; + fakeBody.appendChild(div); + return function(q) { + div.innerHTML = '­'; + docElem.insertBefore(fakeBody, refNode); + bool = div.offsetWidth === 42; + docElem.removeChild(fakeBody); + return { + matches: bool, + media: q + }; + }; + }(w.document); +})(this); + +(function(w) { + "use strict"; + var respond = {}; + w.respond = respond; + respond.update = function() {}; + var requestQueue = [], xmlHttp = function() { + var xmlhttpmethod = false; + try { + xmlhttpmethod = new w.XMLHttpRequest(); + } catch (e) { + xmlhttpmethod = new w.ActiveXObject("Microsoft.XMLHTTP"); + } + return function() { + return xmlhttpmethod; + }; + }(), ajax = function(url, callback) { + var req = xmlHttp(); + if (!req) { + return; + } + req.open("GET", url, true); + req.onreadystatechange = function() { + if (req.readyState !== 4 || req.status !== 200 && req.status !== 304) { + return; + } + callback(req.responseText); + }; + if (req.readyState === 4) { + return; + } + req.send(null); + }, isUnsupportedMediaQuery = function(query) { + return query.replace(respond.regex.minmaxwh, "").match(respond.regex.other); + }; + respond.ajax = ajax; + respond.queue = requestQueue; + respond.unsupportedmq = isUnsupportedMediaQuery; + respond.regex = { + media: /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi, + keyframes: /@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi, + comments: /\/\*[^*]*\*+([^/][^*]*\*+)*\//gi, + urls: /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, + findStyles: /@media *([^\{]+)\{([\S\s]+?)$/, + only: /(only\s+)?([a-zA-Z]+)\s?/, + minw: /\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/, + maxw: /\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/, + minmaxwh: /\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi, + other: /\([^\)]*\)/g + }; + respond.mediaQueriesSupported = w.matchMedia && w.matchMedia("only all") !== null && w.matchMedia("only all").matches; + if (respond.mediaQueriesSupported) { + return; + } + var doc = w.document, docElem = doc.documentElement, mediastyles = [], rules = [], appendedEls = [], parsedSheets = {}, resizeThrottle = 30, head = doc.getElementsByTagName("head")[0] || docElem, base = doc.getElementsByTagName("base")[0], links = head.getElementsByTagName("link"), lastCall, resizeDefer, eminpx, getEmValue = function() { + var ret, div = doc.createElement("div"), body = doc.body, originalHTMLFontSize = docElem.style.fontSize, originalBodyFontSize = body && body.style.fontSize, fakeUsed = false; + div.style.cssText = "position:absolute;font-size:1em;width:1em"; + if (!body) { + body = fakeUsed = doc.createElement("body"); + body.style.background = "none"; + } + docElem.style.fontSize = "100%"; + body.style.fontSize = "100%"; + body.appendChild(div); + if (fakeUsed) { + docElem.insertBefore(body, docElem.firstChild); + } + ret = div.offsetWidth; + if (fakeUsed) { + docElem.removeChild(body); + } else { + body.removeChild(div); + } + docElem.style.fontSize = originalHTMLFontSize; + if (originalBodyFontSize) { + body.style.fontSize = originalBodyFontSize; + } + ret = eminpx = parseFloat(ret); + return ret; + }, applyMedia = function(fromResize) { + var name = "clientWidth", docElemProp = docElem[name], currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[name] || docElemProp, styleBlocks = {}, lastLink = links[links.length - 1], now = new Date().getTime(); + if (fromResize && lastCall && now - lastCall < resizeThrottle) { + w.clearTimeout(resizeDefer); + resizeDefer = w.setTimeout(applyMedia, resizeThrottle); + return; + } else { + lastCall = now; + } + for (var i in mediastyles) { + if (mediastyles.hasOwnProperty(i)) { + var thisstyle = mediastyles[i], min = thisstyle.minw, max = thisstyle.maxw, minnull = min === null, maxnull = max === null, em = "em"; + if (!!min) { + min = parseFloat(min) * (min.indexOf(em) > -1 ? eminpx || getEmValue() : 1); + } + if (!!max) { + max = parseFloat(max) * (max.indexOf(em) > -1 ? eminpx || getEmValue() : 1); + } + if (!thisstyle.hasquery || (!minnull || !maxnull) && (minnull || currWidth >= min) && (maxnull || currWidth <= max)) { + if (!styleBlocks[thisstyle.media]) { + styleBlocks[thisstyle.media] = []; + } + styleBlocks[thisstyle.media].push(rules[thisstyle.rules]); + } + } + } + for (var j in appendedEls) { + if (appendedEls.hasOwnProperty(j)) { + if (appendedEls[j] && appendedEls[j].parentNode === head) { + head.removeChild(appendedEls[j]); + } + } + } + appendedEls.length = 0; + for (var k in styleBlocks) { + if (styleBlocks.hasOwnProperty(k)) { + var ss = doc.createElement("style"), css = styleBlocks[k].join("\n"); + ss.type = "text/css"; + ss.media = k; + head.insertBefore(ss, lastLink.nextSibling); + if (ss.styleSheet) { + ss.styleSheet.cssText = css; + } else { + ss.appendChild(doc.createTextNode(css)); + } + appendedEls.push(ss); + } + } + }, translate = function(styles, href, media) { + var qs = styles.replace(respond.regex.comments, "").replace(respond.regex.keyframes, "").match(respond.regex.media), ql = qs && qs.length || 0; + href = href.substring(0, href.lastIndexOf("/")); + var repUrls = function(css) { + return css.replace(respond.regex.urls, "$1" + href + "$2$3"); + }, useMedia = !ql && media; + if (href.length) { + href += "/"; + } + if (useMedia) { + ql = 1; + } + for (var i = 0; i < ql; i++) { + var fullq, thisq, eachq, eql; + if (useMedia) { + fullq = media; + rules.push(repUrls(styles)); + } else { + fullq = qs[i].match(respond.regex.findStyles) && RegExp.$1; + rules.push(RegExp.$2 && repUrls(RegExp.$2)); + } + eachq = fullq.split(","); + eql = eachq.length; + for (var j = 0; j < eql; j++) { + thisq = eachq[j]; + if (isUnsupportedMediaQuery(thisq)) { + continue; + } + mediastyles.push({ + media: thisq.split("(")[0].match(respond.regex.only) && RegExp.$2 || "all", + rules: rules.length - 1, + hasquery: thisq.indexOf("(") > -1, + minw: thisq.match(respond.regex.minw) && parseFloat(RegExp.$1) + (RegExp.$2 || ""), + maxw: thisq.match(respond.regex.maxw) && parseFloat(RegExp.$1) + (RegExp.$2 || "") + }); + } + } + applyMedia(); + }, makeRequests = function() { + if (requestQueue.length) { + var thisRequest = requestQueue.shift(); + ajax(thisRequest.href, function(styles) { + translate(styles, thisRequest.href, thisRequest.media); + parsedSheets[thisRequest.href] = true; + w.setTimeout(function() { + makeRequests(); + }, 0); + }); + } + }, ripCSS = function() { + for (var i = 0; i < links.length; i++) { + var sheet = links[i], href = sheet.href, media = sheet.media, isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet"; + if (!!href && isCSS && !parsedSheets[href]) { + if (sheet.styleSheet && sheet.styleSheet.rawCssText) { + translate(sheet.styleSheet.rawCssText, href, media); + parsedSheets[href] = true; + } else { + if (!/^([a-zA-Z:]*\/\/)/.test(href) && !base || href.replace(RegExp.$1, "").split("/")[0] === w.location.host) { + if (href.substring(0, 2) === "//") { + href = w.location.protocol + href; + } + requestQueue.push({ + href: href, + media: media + }); + } + } + } + } + makeRequests(); + }; + ripCSS(); + respond.update = ripCSS; + respond.getEmValue = getEmValue; + function callMedia() { + applyMedia(true); + } + if (w.addEventListener) { + w.addEventListener("resize", callMedia, false); + } else if (w.attachEvent) { + w.attachEvent("onresize", callMedia); + } +})(this); \ No newline at end of file diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/stmd.js b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/stmd.js new file mode 100644 index 0000000..f7f48fd --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/stmd.js @@ -0,0 +1,1547 @@ +// stmd.js - CommonMark in javascript +// Copyright (C) 2014 John MacFarlane +// License: BSD3. + +// Basic usage: +// +// var stmd = require('stmd'); +// var parser = new stmd.DocParser(); +// var renderer = new stmd.HtmlRenderer(); +// console.log(renderer.render(parser.parse('Hello *world*'))); + +(function(exports) { + +// Some regexps used in inline parser: + +var ESCAPABLE = '[!"#$%&\'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]'; +var ESCAPED_CHAR = '\\\\' + ESCAPABLE; +var IN_DOUBLE_QUOTES = '"(' + ESCAPED_CHAR + '|[^"\\x00])*"'; +var IN_SINGLE_QUOTES = '\'(' + ESCAPED_CHAR + '|[^\'\\x00])*\''; +var IN_PARENS = '\\((' + ESCAPED_CHAR + '|[^)\\x00])*\\)'; +var REG_CHAR = '[^\\\\()\\x00-\\x20]'; +var IN_PARENS_NOSP = '\\((' + REG_CHAR + '|' + ESCAPED_CHAR + ')*\\)'; +var TAGNAME = '[A-Za-z][A-Za-z0-9]*'; +var BLOCKTAGNAME = '(?:article|header|aside|hgroup|iframe|blockquote|hr|body|li|map|button|object|canvas|ol|caption|output|col|p|colgroup|pre|dd|progress|div|section|dl|table|td|dt|tbody|embed|textarea|fieldset|tfoot|figcaption|th|figure|thead|footer|footer|tr|form|ul|h1|h2|h3|h4|h5|h6|video|script|style)'; +var ATTRIBUTENAME = '[a-zA-Z_:][a-zA-Z0-9:._-]*'; +var UNQUOTEDVALUE = "[^\"'=<>`\\x00-\\x20]+"; +var SINGLEQUOTEDVALUE = "'[^']*'"; +var DOUBLEQUOTEDVALUE = '"[^"]*"'; +var ATTRIBUTEVALUE = "(?:" + UNQUOTEDVALUE + "|" + SINGLEQUOTEDVALUE + "|" + DOUBLEQUOTEDVALUE + ")"; +var ATTRIBUTEVALUESPEC = "(?:" + "\\s*=" + "\\s*" + ATTRIBUTEVALUE + ")"; +var ATTRIBUTE = "(?:" + "\\s+" + ATTRIBUTENAME + ATTRIBUTEVALUESPEC + "?)"; +var OPENTAG = "<" + TAGNAME + ATTRIBUTE + "*" + "\\s*/?>"; +var CLOSETAG = "]"; +var OPENBLOCKTAG = "<" + BLOCKTAGNAME + ATTRIBUTE + "*" + "\\s*/?>"; +var CLOSEBLOCKTAG = "]"; +var HTMLCOMMENT = ""; +var PROCESSINGINSTRUCTION = "[<][?].*?[?][>]"; +var DECLARATION = "]*>"; +var CDATA = "])*\\]\\]>"; +var HTMLTAG = "(?:" + OPENTAG + "|" + CLOSETAG + "|" + HTMLCOMMENT + "|" + + PROCESSINGINSTRUCTION + "|" + DECLARATION + "|" + CDATA + ")"; +var HTMLBLOCKOPEN = "<(?:" + BLOCKTAGNAME + "[\\s/>]" + "|" + + "/" + BLOCKTAGNAME + "[\\s>]" + "|" + "[?!])"; + +var reHtmlTag = new RegExp('^' + HTMLTAG, 'i'); + +var reHtmlBlockOpen = new RegExp('^' + HTMLBLOCKOPEN, 'i'); + +var reLinkTitle = new RegExp( + '^(?:"(' + ESCAPED_CHAR + '|[^"\\x00])*"' + + '|' + + '\'(' + ESCAPED_CHAR + '|[^\'\\x00])*\'' + + '|' + + '\\((' + ESCAPED_CHAR + '|[^)\\x00])*\\))'); + +var reLinkDestinationBraces = new RegExp( + '^(?:[<](?:[^<>\\n\\\\\\x00]' + '|' + ESCAPED_CHAR + '|' + '\\\\)*[>])'); + +var reLinkDestination = new RegExp( + '^(?:' + REG_CHAR + '+|' + ESCAPED_CHAR + '|' + IN_PARENS_NOSP + ')*'); + +var reEscapable = new RegExp(ESCAPABLE); + +var reAllEscapedChar = new RegExp('\\\\(' + ESCAPABLE + ')', 'g'); + +var reEscapedChar = new RegExp('^\\\\(' + ESCAPABLE + ')'); + +var reAllTab = /\t/g; + +var reHrule = /^(?:(?:\* *){3,}|(?:_ *){3,}|(?:- *){3,}) *$/; + +// Matches a character with a special meaning in markdown, +// or a string of non-special characters. +var reMain = /^(?:[\n`\[\]\\!<&*_]|[^\n`\[\]\\!<&*_]+)/m; + +// UTILITY FUNCTIONS + +// Replace backslash escapes with literal characters. +var unescape = function(s) { + return s.replace(reAllEscapedChar, '$1'); +}; + +// Returns true if string contains only space characters. +var isBlank = function(s) { + return /^\s*$/.test(s); +}; + +// Normalize reference label: collapse internal whitespace +// to single space, remove leading/trailing whitespace, case fold. +var normalizeReference = function(s) { + return s.trim() + .replace(/\s+/,' ') + .toUpperCase(); +}; + +// Attempt to match a regex in string s at offset offset. +// Return index of match or null. +var matchAt = function(re, s, offset) { + var res = s.slice(offset).match(re); + if (res) { + return offset + res.index; + } else { + return null; + } +}; + +// Convert tabs to spaces on each line using a 4-space tab stop. +var detabLine = function(text) { + if (text.indexOf('\t') == -1) { + return text; + } else { + var lastStop = 0; + return text.replace(reAllTab, function(match, offset) { + var result = ' '.slice((offset - lastStop) % 4); + lastStop = offset + 1; + return result; + }); + } +}; + +// INLINE PARSER + +// These are methods of an InlineParser object, defined below. +// An InlineParser keeps track of a subject (a string to be +// parsed) and a position in that subject. + +// If re matches at current position in the subject, advance +// position in subject and return the match; otherwise return null. +var match = function(re) { + var match = re.exec(this.subject.slice(this.pos)); + if (match) { + this.pos += match.index + match[0].length; + return match[0]; + } else { + return null; + } +}; + +// Returns the character at the current subject position, or null if +// there are no more characters. +var peek = function() { + return this.subject.charAt(this.pos) || null; +}; + +// Parse zero or more space characters, including at most one newline +var spnl = function() { + this.match(/^ *(?:\n *)?/); + return 1; +}; + +// All of the parsers below try to match something at the current position +// in the subject. If they succeed in matching anything, they +// push an inline element onto the 'inlines' list. They return the +// number of characters parsed (possibly 0). + +// Attempt to parse backticks, adding either a backtick code span or a +// literal sequence of backticks to the 'inlines' list. +var parseBackticks = function(inlines) { + var startpos = this.pos; + var ticks = this.match(/^`+/); + if (!ticks) { + return 0; + } + var afterOpenTicks = this.pos; + var foundCode = false; + var match; + while (!foundCode && (match = this.match(/`+/m))) { + if (match == ticks) { + inlines.push({ t: 'Code', c: this.subject.slice(afterOpenTicks, + this.pos - ticks.length) + .replace(/[ \n]+/g,' ') + .trim() }); + return (this.pos - startpos); + } + } + // If we got here, we didn't match a closing backtick sequence. + inlines.push({ t: 'Str', c: ticks }); + this.pos = afterOpenTicks; + return (this.pos - startpos); +}; + +// Parse a backslash-escaped special character, adding either the escaped +// character, a hard line break (if the backslash is followed by a newline), +// or a literal backslash to the 'inlines' list. +var parseEscaped = function(inlines) { + var subj = this.subject, + pos = this.pos; + if (subj.charAt(pos) === '\\') { + if (subj.charAt(pos + 1) === '\n') { + inlines.push({ t: 'Hardbreak' }); + this.pos = this.pos + 2; + return 2; + } else if (reEscapable.test(subj.charAt(pos + 1))) { + inlines.push({ t: 'Str', c: subj.charAt(pos + 1) }); + this.pos = this.pos + 2; + return 2; + } else { + this.pos++; + inlines.push({t: 'Str', c: '\\'}); + return 1; + } + } else { + return 0; + } +}; + +// Attempt to parse an autolink (URL or email in pointy brackets). +var parseAutolink = function(inlines) { + var m; + var dest; + if ((m = this.match(/^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/))) { // email autolink + dest = m.slice(1,-1); + inlines.push({ t: 'Link', label: [{ t: 'Str', c: dest }], + destination: 'mailto:' + dest }); + return m.length; + } else if ((m = this.match(/^<(?:coap|doi|javascript|aaa|aaas|about|acap|cap|cid|crid|data|dav|dict|dns|file|ftp|geo|go|gopher|h323|http|https|iax|icap|im|imap|info|ipp|iris|iris.beep|iris.xpc|iris.xpcs|iris.lwz|ldap|mailto|mid|msrp|msrps|mtqp|mupdate|news|nfs|ni|nih|nntp|opaquelocktoken|pop|pres|rtsp|service|session|shttp|sieve|sip|sips|sms|snmp|soap.beep|soap.beeps|tag|tel|telnet|tftp|thismessage|tn3270|tip|tv|urn|vemmi|ws|wss|xcon|xcon-userid|xmlrpc.beep|xmlrpc.beeps|xmpp|z39.50r|z39.50s|adiumxtra|afp|afs|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|chrome|chrome-extension|com-eventbrite-attendee|content|cvs|dlna-playsingle|dlna-playcontainer|dtn|dvb|ed2k|facetime|feed|finger|fish|gg|git|gizmoproject|gtalk|hcp|icon|ipn|irc|irc6|ircs|itms|jar|jms|keyparc|lastfm|ldaps|magnet|maps|market|message|mms|ms-help|msnim|mumble|mvn|notes|oid|palm|paparazzi|platform|proxy|psyc|query|res|resource|rmi|rsync|rtmp|secondlife|sftp|sgn|skype|smb|soldat|spotify|ssh|steam|svn|teamspeak|things|udp|unreal|ut2004|ventrilo|view-source|webcal|wtai|wyciwyg|xfire|xri|ymsgr):[^<>\x00-\x20]*>/i))) { + dest = m.slice(1,-1); + inlines.push({ t: 'Link', label: [{ t: 'Str', c: dest }], + destination: dest }); + return m.length; + } else { + return 0; + } +}; + +// Attempt to parse a raw HTML tag. +var parseHtmlTag = function(inlines) { + var m = this.match(reHtmlTag); + if (m) { + inlines.push({ t: 'Html', c: m }); + return m.length; + } else { + return 0; + } +}; + +// Scan a sequence of characters == c, and return information about +// the number of delimiters and whether they are positioned such that +// they can open and/or close emphasis or strong emphasis. A utility +// function for strong/emph parsing. +var scanDelims = function(c) { + var numdelims = 0; + var first_close_delims = 0; + var char_before, char_after; + var startpos = this.pos; + + char_before = this.pos === 0 ? '\n' : + this.subject.charAt(this.pos - 1); + + while (this.peek() === c) { + numdelims++; + this.pos++; + } + + char_after = this.peek() || '\n'; + + var can_open = numdelims > 0 && numdelims <= 3 && !(/\s/.test(char_after)); + var can_close = numdelims > 0 && numdelims <= 3 && !(/\s/.test(char_before)); + if (c === '_') { + can_open = can_open && !((/[a-z0-9]/i).test(char_before)); + can_close = can_close && !((/[a-z0-9]/i).test(char_after)); + } + this.pos = startpos; + return { numdelims: numdelims, + can_open: can_open, + can_close: can_close }; +}; + +// Attempt to parse emphasis or strong emphasis in an efficient way, +// with no backtracking. +var parseEmphasis = function(inlines) { + var startpos = this.pos; + var c ; + var first_close = 0; + var nxt = this.peek(); + if (nxt == '*' || nxt == '_') { + c = nxt; + } else { + return 0; + } + + var numdelims; + var delimpos; + + // Get opening delimiters. + res = this.scanDelims(c); + numdelims = res.numdelims; + this.pos += numdelims; + // We provisionally add a literal string. If we match appropriate + // closing delimiters, we'll change this to Strong or Emph. + inlines.push({t: 'Str', + c: this.subject.substr(this.pos - numdelims, numdelims)}); + // Record the position of this opening delimiter: + delimpos = inlines.length - 1; + + if (!res.can_open || numdelims === 0) { + return 0; + } + + var first_close_delims = 0; + + switch (numdelims) { + case 1: // we started with * or _ + while (true) { + res = this.scanDelims(c); + if (res.numdelims >= 1 && res.can_close) { + this.pos += 1; + // Convert the inline at delimpos, currently a string with the delim, + // into an Emph whose contents are the succeeding inlines + inlines[delimpos].t = 'Emph'; + inlines[delimpos].c = inlines.slice(delimpos + 1); + inlines.splice(delimpos + 1, inlines.length - delimpos - 1); + break; + } else { + if (this.parseInline(inlines) === 0) { + break; + } + } + } + return (this.pos - startpos); + + case 2: // We started with ** or __ + while (true) { + res = this.scanDelims(c); + if (res.numdelims >= 2 && res.can_close) { + this.pos += 2; + inlines[delimpos].t = 'Strong'; + inlines[delimpos].c = inlines.slice(delimpos + 1); + inlines.splice(delimpos + 1, inlines.length - delimpos - 1); + break; + } else { + if (this.parseInline(inlines) === 0) { + break; + } + } + } + return (this.pos - startpos); + + case 3: // We started with *** or ___ + while (true) { + res = this.scanDelims(c); + if (res.numdelims >= 1 && res.numdelims <= 3 && res.can_close && + res.numdelims != first_close_delims) { + + if (first_close_delims === 1 && numdelims > 2) { + res.numdelims = 2; + } else if (first_close_delims === 2) { + res.numdelims = 1; + } else if (res.numdelims === 3) { + // If we opened with ***, then we interpret *** as ** followed by * + // giving us + res.numdelims = 1; + } + + this.pos += res.numdelims; + + if (first_close > 0) { // if we've already passed the first closer: + inlines[delimpos].t = first_close_delims === 1 ? 'Strong' : 'Emph'; + inlines[delimpos].c = [ + { t: first_close_delims === 1 ? 'Emph' : 'Strong', + c: inlines.slice(delimpos + 1, first_close)} + ].concat(inlines.slice(first_close + 1)); + inlines.splice(delimpos + 1); + break; + } else { // this is the first closer; for now, add literal string; + // we'll change this when he hit the second closer + inlines.push({t: 'Str', + c: this.subject.slice(this.pos - res.numdelims, + this.pos) }); + first_close = inlines.length - 1; + first_close_delims = res.numdelims; + } + } else { // parse another inline element, til we hit the end + if (this.parseInline(inlines) === 0) { + break; + } + } + } + return (this.pos - startpos); + + default: + return res; + } + + return 0; +}; + +// Attempt to parse link title (sans quotes), returning the string +// or null if no match. +var parseLinkTitle = function() { + var title = this.match(reLinkTitle); + if (title) { + // chop off quotes from title and unescape: + return unescape(title.substr(1, title.length - 2)); + } else { + return null; + } +}; + +// Attempt to parse link destination, returning the string or +// null if no match. +var parseLinkDestination = function() { + var res = this.match(reLinkDestinationBraces); + if (res) { // chop off surrounding <..>: + return unescape(res.substr(1, res.length - 2)); + } else { + res = this.match(reLinkDestination); + if (res !== null) { + return unescape(res); + } else { + return null; + } + } +}; + +// Attempt to parse a link label, returning number of characters parsed. +var parseLinkLabel = function() { + if (this.peek() != '[') { + return 0; + } + var startpos = this.pos; + var nest_level = 0; + if (this.label_nest_level > 0) { + // If we've already checked to the end of this subject + // for a label, even with a different starting [, we + // know we won't find one here and we can just return. + // This avoids lots of backtracking. + // Note: nest level 1 would be: [foo [bar] + // nest level 2 would be: [foo [bar [baz] + this.label_nest_level--; + return 0; + } + this.pos++; // advance past [ + var c; + while ((c = this.peek()) && (c != ']' || nest_level > 0)) { + switch (c) { + case '`': + this.parseBackticks([]); + break; + case '<': + this.parseAutolink([]) || this.parseHtmlTag([]) || this.parseString([]); + break; + case '[': // nested [] + nest_level++; + this.pos++; + break; + case ']': // nested [] + nest_level--; + this.pos++; + break; + case '\\': + this.parseEscaped([]); + break; + default: + this.parseString([]); + } + } + if (c === ']') { + this.label_nest_level = 0; + this.pos++; // advance past ] + return this.pos - startpos; + } else { + if (!c) { + this.label_nest_level = nest_level; + } + this.pos = startpos; + return 0; + } +}; + +// Parse raw link label, including surrounding [], and return +// inline contents. (Note: this is not a method of InlineParser.) +var parseRawLabel = function(s) { + // note: parse without a refmap; we don't want links to resolve + // in nested brackets! + return new InlineParser().parse(s.substr(1, s.length - 2), {}); +}; + +// Attempt to parse a link. If successful, add the link to +// inlines. +var parseLink = function(inlines) { + var startpos = this.pos; + var reflabel; + var n; + var dest; + var title; + + n = this.parseLinkLabel(); + if (n === 0) { + return 0; + } + var afterlabel = this.pos; + var rawlabel = this.subject.substr(startpos, n); + + // if we got this far, we've parsed a label. + // Try to parse an explicit link: [label](url "title") + if (this.peek() == '(') { + this.pos++; + if (this.spnl() && + ((dest = this.parseLinkDestination()) !== null) && + this.spnl() && + // make sure there's a space before the title: + (/^\s/.test(this.subject.charAt(this.pos - 1)) && + (title = this.parseLinkTitle() || '') || true) && + this.spnl() && + this.match(/^\)/)) { + inlines.push({ t: 'Link', + destination: dest, + title: title, + label: parseRawLabel(rawlabel) }); + return this.pos - startpos; + } else { + this.pos = startpos; + return 0; + } + } + // If we're here, it wasn't an explicit link. Try to parse a reference link. + // first, see if there's another label + var savepos = this.pos; + this.spnl(); + var beforelabel = this.pos; + n = this.parseLinkLabel(); + if (n == 2) { + // empty second label + reflabel = rawlabel; + } else if (n > 0) { + reflabel = this.subject.slice(beforelabel, beforelabel + n); + } else { + this.pos = savepos; + reflabel = rawlabel; + } + // lookup rawlabel in refmap + var link = this.refmap[normalizeReference(reflabel)]; + if (link) { + inlines.push({t: 'Link', + destination: link.destination, + title: link.title, + label: parseRawLabel(rawlabel) }); + return this.pos - startpos; + } else { + this.pos = startpos; + return 0; + } + // Nothing worked, rewind: + this.pos = startpos; + return 0; +}; + +// Attempt to parse an entity, adding to inlines if successful. +var parseEntity = function(inlines) { + var m; + if ((m = this.match(/^&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});/i))) { + inlines.push({ t: 'Entity', c: m }); + return m.length; + } else { + return 0; + } +}; + +// Parse a run of ordinary characters, or a single character with +// a special meaning in markdown, as a plain string, adding to inlines. +var parseString = function(inlines) { + var m; + if ((m = this.match(reMain))) { + inlines.push({ t: 'Str', c: m }); + return m.length; + } else { + return 0; + } +}; + +// Parse a newline. If it was preceded by two spaces, return a hard +// line break; otherwise a soft line break. +var parseNewline = function(inlines) { + if (this.peek() == '\n') { + this.pos++; + var last = inlines[inlines.length - 1]; + if (last && last.t == 'Str' && last.c.slice(-2) == ' ') { + last.c = last.c.replace(/ *$/,''); + inlines.push({ t: 'Hardbreak' }); + } else { + if (last && last.t == 'Str' && last.c.slice(-1) == ' ') { + last.c = last.c.slice(0, -1); + } + inlines.push({ t: 'Softbreak' }); + } + return 1; + } else { + return 0; + } +}; + +// Attempt to parse an image. If the opening '!' is not followed +// by a link, add a literal '!' to inlines. +var parseImage = function(inlines) { + if (this.match(/^!/)) { + var n = this.parseLink(inlines); + if (n === 0) { + inlines.push({ t: 'Str', c: '!' }); + return 1; + } else if (inlines[inlines.length - 1] && + inlines[inlines.length - 1].t == 'Link') { + inlines[inlines.length - 1].t = 'Image'; + return n+1; + } else { + throw "Shouldn't happen"; + } + } else { + return 0; + } +}; + +// Attempt to parse a link reference, modifying refmap. +var parseReference = function(s, refmap) { + this.subject = s; + this.pos = 0; + var rawlabel; + var dest; + var title; + var matchChars; + var startpos = this.pos; + var match; + + // label: + matchChars = this.parseLinkLabel(); + if (matchChars === 0) { + return 0; + } else { + rawlabel = this.subject.substr(0, matchChars); + } + + // colon: + if (this.peek() === ':') { + this.pos++; + } else { + this.pos = startpos; + return 0; + } + + // link url + this.spnl(); + + dest = this.parseLinkDestination(); + if (dest === null || dest.length === 0) { + this.pos = startpos; + return 0; + } + + var beforetitle = this.pos; + this.spnl(); + title = this.parseLinkTitle(); + if (title === null) { + title = ''; + // rewind before spaces + this.pos = beforetitle; + } + + // make sure we're at line end: + if (this.match(/^ *(?:\n|$)/) === null) { + this.pos = startpos; + return 0; + } + + var normlabel = normalizeReference(rawlabel); + + if (!refmap[normlabel]) { + refmap[normlabel] = { destination: dest, title: title }; + } + return this.pos - startpos; +}; + +// Parse the next inline element in subject, advancing subject position +// and adding the result to 'inlines'. +var parseInline = function(inlines) { + var c = this.peek(); + var res; + switch(c) { + case '\n': + res = this.parseNewline(inlines); + break; + case '\\': + res = this.parseEscaped(inlines); + break; + case '`': + res = this.parseBackticks(inlines); + break; + case '*': + case '_': + res = this.parseEmphasis(inlines); + break; + case '[': + res = this.parseLink(inlines); + break; + case '!': + res = this.parseImage(inlines); + break; + case '<': + res = this.parseAutolink(inlines) || + this.parseHtmlTag(inlines); + break; + case '&': + res = this.parseEntity(inlines); + break; + default: + } + return res || this.parseString(inlines); +}; + +// Parse s as a list of inlines, using refmap to resolve references. +var parseInlines = function(s, refmap) { + this.subject = s; + this.pos = 0; + this.refmap = refmap || {}; + var inlines = []; + while (this.parseInline(inlines)) ; + return inlines; +}; + +// The InlineParser object. +function InlineParser(){ + return { + subject: '', + label_nest_level: 0, // used by parseLinkLabel method + pos: 0, + refmap: {}, + match: match, + peek: peek, + spnl: spnl, + parseBackticks: parseBackticks, + parseEscaped: parseEscaped, + parseAutolink: parseAutolink, + parseHtmlTag: parseHtmlTag, + scanDelims: scanDelims, + parseEmphasis: parseEmphasis, + parseLinkTitle: parseLinkTitle, + parseLinkDestination: parseLinkDestination, + parseLinkLabel: parseLinkLabel, + parseLink: parseLink, + parseEntity: parseEntity, + parseString: parseString, + parseNewline: parseNewline, + parseImage: parseImage, + parseReference: parseReference, + parseInline: parseInline, + parse: parseInlines + }; +} + +// DOC PARSER + +// These are methods of a DocParser object, defined below. + +var makeBlock = function(tag, start_line, start_column) { + return { t: tag, + open: true, + last_line_blank: false, + start_line: start_line, + start_column: start_column, + end_line: start_line, + children: [], + parent: null, + // string_content is formed by concatenating strings, in finalize: + string_content: "", + strings: [], + inline_content: [] + }; +}; + +// Returns true if parent block can contain child block. +var canContain = function(parent_type, child_type) { + return ( parent_type == 'Document' || + parent_type == 'BlockQuote' || + parent_type == 'ListItem' || + (parent_type == 'List' && child_type == 'ListItem') ); +}; + +// Returns true if block type can accept lines of text. +var acceptsLines = function(block_type) { + return ( block_type == 'Paragraph' || + block_type == 'IndentedCode' || + block_type == 'FencedCode' ); +}; + +// Returns true if block ends with a blank line, descending if needed +// into lists and sublists. +var endsWithBlankLine = function(block) { + if (block.last_line_blank) { + return true; + } + if ((block.t == 'List' || block.t == 'ListItem') && block.children.length > 0) { + return endsWithBlankLine(block.children[block.children.length - 1]); + } else { + return false; + } +}; + +// Break out of all containing lists, resetting the tip of the +// document to the parent of the highest list, and finalizing +// all the lists. (This is used to implement the "two blank lines +// break of of all lists" feature.) +var breakOutOfLists = function(block, line_number) { + var b = block; + var last_list = null; + do { + if (b.t === 'List') { + last_list = b; + } + b = b.parent; + } while (b); + + if (last_list) { + while (block != last_list) { + this.finalize(block, line_number); + block = block.parent; + } + this.finalize(last_list, line_number); + this.tip = last_list.parent; + } +}; + +// Add a line to the block at the tip. We assume the tip +// can accept lines -- that check should be done before calling this. +var addLine = function(ln, offset) { + var s = ln.slice(offset); + if (!(this.tip.open)) { + throw({ msg: "Attempted to add line (" + ln + ") to closed container." }); + } + this.tip.strings.push(s); +}; + +// Add block of type tag as a child of the tip. If the tip can't +// accept children, close and finalize it and try its parent, +// and so on til we find a block that can accept children. +var addChild = function(tag, line_number, offset) { + while (!canContain(this.tip.t, tag)) { + this.finalize(this.tip, line_number); + } + + var column_number = offset + 1; // offset 0 = column 1 + var newBlock = makeBlock(tag, line_number, column_number); + this.tip.children.push(newBlock); + newBlock.parent = this.tip; + this.tip = newBlock; + return newBlock; +}; + +// Parse a list marker and return data on the marker (type, +// start, delimiter, bullet character, padding) or null. +var parseListMarker = function(ln, offset) { + var rest = ln.slice(offset); + var match; + var spaces_after_marker; + var data = {}; + if (rest.match(reHrule)) { + return null; + } + if ((match = rest.match(/^[*+-]( +|$)/))) { + spaces_after_marker = match[1].length; + data.type = 'Bullet'; + data.bullet_char = match[0].charAt(0); + + } else if ((match = rest.match(/^(\d+)([.)])( +|$)/))) { + spaces_after_marker = match[3].length; + data.type = 'Ordered'; + data.start = parseInt(match[1]); + data.delimiter = match[2]; + } else { + return null; + } + var blank_item = match[0].length === rest.length; + if (spaces_after_marker >= 5 || + spaces_after_marker < 1 || + blank_item) { + data.padding = match[0].length - spaces_after_marker + 1; + } else { + data.padding = match[0].length; + } + return data; +}; + +// Returns true if the two list items are of the same type, +// with the same delimiter and bullet character. This is used +// in agglomerating list items into lists. +var listsMatch = function(list_data, item_data) { + return (list_data.type === item_data.type && + list_data.delimiter === item_data.delimiter && + list_data.bullet_char === item_data.bullet_char); +}; + +// Analyze a line of text and update the document appropriately. +// We parse markdown text by calling this on each line of input, +// then finalizing the document. +var incorporateLine = function(ln, line_number) { + + var all_matched = true; + var last_child; + var first_nonspace; + var offset = 0; + var match; + var data; + var blank; + var indent; + var last_matched_container; + var i; + var CODE_INDENT = 4; + + var container = this.doc; + var oldtip = this.tip; + + // Convert tabs to spaces: + ln = detabLine(ln); + + // For each containing block, try to parse the associated line start. + // Bail out on failure: container will point to the last matching block. + // Set all_matched to false if not all containers match. + while (container.children.length > 0) { + last_child = container.children[container.children.length - 1]; + if (!last_child.open) { + break; + } + container = last_child; + + match = matchAt(/[^ ]/, ln, offset); + if (match === null) { + first_nonspace = ln.length; + blank = true; + } else { + first_nonspace = match; + blank = false; + } + indent = first_nonspace - offset; + + switch (container.t) { + case 'BlockQuote': + var matched = indent <= 3 && ln.charAt(first_nonspace) === '>'; + if (matched) { + offset = first_nonspace + 1; + if (ln.charAt(offset) === ' ') { + offset++; + } + } else { + all_matched = false; + } + break; + + case 'ListItem': + if (indent >= container.list_data.marker_offset + + container.list_data.padding) { + offset += container.list_data.marker_offset + + container.list_data.padding; + } else if (blank) { + offset = first_nonspace; + } else { + all_matched = false; + } + break; + + case 'IndentedCode': + if (indent >= CODE_INDENT) { + offset += CODE_INDENT; + } else if (blank) { + offset = first_nonspace; + } else { + all_matched = false; + } + break; + + case 'ATXHeader': + case 'SetextHeader': + case 'HorizontalRule': + // a header can never container > 1 line, so fail to match: + all_matched = false; + break; + + case 'FencedCode': + // skip optional spaces of fence offset + i = container.fence_offset; + while (i > 0 && ln.charAt(offset) === ' ') { + offset++; + i--; + } + break; + + case 'HtmlBlock': + if (blank) { + all_matched = false; + } + break; + + case 'Paragraph': + if (blank) { + container.last_line_blank = true; + all_matched = false; + } + break; + + default: + } + + if (!all_matched) { + container = container.parent; // back up to last matching block + break; + } + } + + last_matched_container = container; + + // This function is used to finalize and close any unmatched + // blocks. We aren't ready to do this now, because we might + // have a lazy paragraph continuation, in which case we don't + // want to close unmatched blocks. So we store this closure for + // use later, when we have more information. + var closeUnmatchedBlocks = function(mythis) { + // finalize any blocks not matched + while (!already_done && oldtip != last_matched_container) { + mythis.finalize(oldtip, line_number); + oldtip = oldtip.parent; + } + var already_done = true; + }; + + // Check to see if we've hit 2nd blank line; if so break out of list: + if (blank && container.last_line_blank) { + this.breakOutOfLists(container, line_number); + } + + // Unless last matched container is a code block, try new container starts, + // adding children to the last matched container: + while (container.t != 'FencedCode' && + container.t != 'IndentedCode' && + container.t != 'HtmlBlock' && + // this is a little performance optimization: + matchAt(/^[ #`~*+_=<>0-9-]/,ln,offset) !== null) { + + match = matchAt(/[^ ]/, ln, offset); + if (match === null) { + first_nonspace = ln.length; + blank = true; + } else { + first_nonspace = match; + blank = false; + } + indent = first_nonspace - offset; + + if (indent >= CODE_INDENT) { + // indented code + if (this.tip.t != 'Paragraph' && !blank) { + offset += CODE_INDENT; + closeUnmatchedBlocks(this); + container = this.addChild('IndentedCode', line_number, offset); + } else { // indent > 4 in a lazy paragraph continuation + break; + } + + } else if (ln.charAt(first_nonspace) === '>') { + // blockquote + offset = first_nonspace + 1; + // optional following space + if (ln.charAt(offset) === ' ') { + offset++; + } + closeUnmatchedBlocks(this); + container = this.addChild('BlockQuote', line_number, offset); + + } else if ((match = ln.slice(first_nonspace).match(/^#{1,6}(?: +|$)/))) { + // ATX header + offset = first_nonspace + match[0].length; + closeUnmatchedBlocks(this); + container = this.addChild('ATXHeader', line_number, first_nonspace); + container.level = match[0].trim().length; // number of #s + // remove trailing ###s: + container.strings = + [ln.slice(offset).replace(/(?:(\\#) *#*| *#+) *$/,'$1')]; + break; + + } else if ((match = ln.slice(first_nonspace).match(/^`{3,}(?!.*`)|^~{3,}(?!.*~)/))) { + // fenced code block + var fence_length = match[0].length; + closeUnmatchedBlocks(this); + container = this.addChild('FencedCode', line_number, first_nonspace); + container.fence_length = fence_length; + container.fence_char = match[0].charAt(0); + container.fence_offset = first_nonspace - offset; + offset = first_nonspace + fence_length; + break; + + } else if (matchAt(reHtmlBlockOpen, ln, first_nonspace) !== null) { + // html block + closeUnmatchedBlocks(this); + container = this.addChild('HtmlBlock', line_number, first_nonspace); + // note, we don't adjust offset because the tag is part of the text + break; + + } else if (container.t == 'Paragraph' && + container.strings.length === 1 && + ((match = ln.slice(first_nonspace).match(/^(?:=+|-+) *$/)))) { + // setext header line + closeUnmatchedBlocks(this); + container.t = 'SetextHeader'; // convert Paragraph to SetextHeader + container.level = match[0].charAt(0) === '=' ? 1 : 2; + offset = ln.length; + + } else if (matchAt(reHrule, ln, first_nonspace) !== null) { + // hrule + closeUnmatchedBlocks(this); + container = this.addChild('HorizontalRule', line_number, first_nonspace); + offset = ln.length - 1; + break; + + } else if ((data = parseListMarker(ln, first_nonspace))) { + // list item + closeUnmatchedBlocks(this); + data.marker_offset = indent; + offset = first_nonspace + data.padding; + + // add the list if needed + if (container.t !== 'List' || + !(listsMatch(container.list_data, data))) { + container = this.addChild('List', line_number, first_nonspace); + container.list_data = data; + } + + // add the list item + container = this.addChild('ListItem', line_number, first_nonspace); + container.list_data = data; + + } else { + break; + + } + + if (acceptsLines(container.t)) { + // if it's a line container, it can't contain other containers + break; + } + } + + // What remains at the offset is a text line. Add the text to the + // appropriate container. + + match = matchAt(/[^ ]/, ln, offset); + if (match === null) { + first_nonspace = ln.length; + blank = true; + } else { + first_nonspace = match; + blank = false; + } + indent = first_nonspace - offset; + + // First check for a lazy paragraph continuation: + if (this.tip !== last_matched_container && + !blank && + this.tip.t == 'Paragraph' && + this.tip.strings.length > 0) { + // lazy paragraph continuation + + this.last_line_blank = false; + this.addLine(ln, offset); + + } else { // not a lazy continuation + + // finalize any blocks not matched + closeUnmatchedBlocks(this); + + // Block quote lines are never blank as they start with > + // and we don't count blanks in fenced code for purposes of tight/loose + // lists or breaking out of lists. We also don't set last_line_blank + // on an empty list item. + container.last_line_blank = blank && + !(container.t == 'BlockQuote' || + container.t == 'FencedCode' || + (container.t == 'ListItem' && + container.children.length === 0 && + container.start_line == line_number)); + + var cont = container; + while (cont.parent) { + cont.parent.last_line_blank = false; + cont = cont.parent; + } + + switch (container.t) { + case 'IndentedCode': + case 'HtmlBlock': + this.addLine(ln, offset); + break; + + case 'FencedCode': + // check for closing code fence: + match = (indent <= 3 && + ln.charAt(first_nonspace) == container.fence_char && + ln.slice(first_nonspace).match(/^(?:`{3,}|~{3,})(?= *$)/)); + if (match && match[0].length >= container.fence_length) { + // don't add closing fence to container; instead, close it: + this.finalize(container, line_number); + } else { + this.addLine(ln, offset); + } + break; + + case 'ATXHeader': + case 'SetextHeader': + case 'HorizontalRule': + // nothing to do; we already added the contents. + break; + + default: + if (acceptsLines(container.t)) { + this.addLine(ln, first_nonspace); + } else if (blank) { + // do nothing + } else if (container.t != 'HorizontalRule' && + container.t != 'SetextHeader') { + // create paragraph container for line + container = this.addChild('Paragraph', line_number, first_nonspace); + this.addLine(ln, first_nonspace); + } else { + console.log("Line " + line_number.toString() + + " with container type " + container.t + + " did not match any condition."); + + } + } + } +}; + +// Finalize a block. Close it and do any necessary postprocessing, +// e.g. creating string_content from strings, setting the 'tight' +// or 'loose' status of a list, and parsing the beginnings +// of paragraphs for reference definitions. Reset the tip to the +// parent of the closed block. +var finalize = function(block, line_number) { + var pos; + // don't do anything if the block is already closed + if (!block.open) { + return 0; + } + block.open = false; + if (line_number > block.start_line) { + block.end_line = line_number - 1; + } else { + block.end_line = line_number; + } + + switch (block.t) { + case 'Paragraph': + block.string_content = block.strings.join('\n').replace(/^ */m,''); + + // try parsing the beginning as link reference definitions: + while (block.string_content.charAt(0) === '[' && + (pos = this.inlineParser.parseReference(block.string_content, + this.refmap))) { + block.string_content = block.string_content.slice(pos); + if (isBlank(block.string_content)) { + block.t = 'ReferenceDef'; + break; + } + } + break; + + case 'ATXHeader': + case 'SetextHeader': + case 'HtmlBlock': + block.string_content = block.strings.join('\n'); + break; + + case 'IndentedCode': + block.string_content = block.strings.join('\n').replace(/(\n *)*$/,'\n'); + break; + + case 'FencedCode': + // first line becomes info string + block.info = unescape(block.strings[0].trim()); + if (block.strings.length == 1) { + block.string_content = ''; + } else { + block.string_content = block.strings.slice(1).join('\n') + '\n'; + } + break; + + case 'List': + block.tight = true; // tight by default + + var numitems = block.children.length; + var i = 0; + while (i < numitems) { + var item = block.children[i]; + // check for non-final list item ending with blank line: + var last_item = i == numitems - 1; + if (endsWithBlankLine(item) && !last_item) { + block.tight = false; + break; + } + // recurse into children of list item, to see if there are + // spaces between any of them: + var numsubitems = item.children.length; + var j = 0; + while (j < numsubitems) { + var subitem = item.children[j]; + var last_subitem = j == numsubitems - 1; + if (endsWithBlankLine(subitem) && !(last_item && last_subitem)) { + block.tight = false; + break; + } + j++; + } + i++; + } + break; + + default: + break; + } + + this.tip = block.parent || this.top; +}; + +// Walk through a block & children recursively, parsing string content +// into inline content where appropriate. +var processInlines = function(block) { + switch(block.t) { + case 'Paragraph': + case 'SetextHeader': + case 'ATXHeader': + block.inline_content = + this.inlineParser.parse(block.string_content.trim(), this.refmap); + block.string_content = ""; + break; + default: + break; + } + + if (block.children) { + for (var i = 0; i < block.children.length; i++) { + this.processInlines(block.children[i]); + } + } + +}; + +// The main parsing function. Returns a parsed document AST. +var parse = function(input) { + this.doc = makeBlock('Document', 1, 1); + this.tip = this.doc; + this.refmap = {}; + var lines = input.replace(/\n$/,'').split(/\r\n|\n|\r/); + var len = lines.length; + for (var i = 0; i < len; i++) { + this.incorporateLine(lines[i], i+1); + } + while (this.tip) { + this.finalize(this.tip, len - 1); + } + this.processInlines(this.doc); + return this.doc; +}; + + +// The DocParser object. +function DocParser(){ + return { + doc: makeBlock('Document', 1, 1), + tip: this.doc, + refmap: {}, + inlineParser: new InlineParser(), + breakOutOfLists: breakOutOfLists, + addLine: addLine, + addChild: addChild, + incorporateLine: incorporateLine, + finalize: finalize, + processInlines: processInlines, + parse: parse + }; +} + +// HTML RENDERER + +// Helper function to produce content in a pair of HTML tags. +var inTags = function(tag, attribs, contents, selfclosing) { + var result = '<' + tag; + if (attribs) { + var i = 0; + var attrib; + while ((attrib = attribs[i]) !== undefined) { + result = result.concat(' ', attrib[0], '="', attrib[1], '"'); + i++; + } + } + if (contents) { + result = result.concat('>', contents, ''); + } else if (selfclosing) { + result = result + ' />'; + } else { + result = result.concat('>'); + } + return result; +}; + +// Render an inline element as HTML. +var renderInline = function(inline) { + var attrs; + switch (inline.t) { + case 'Str': + return this.escape(inline.c); + case 'Softbreak': + return this.softbreak; + case 'Hardbreak': + return inTags('br',[],"",true) + '\n'; + case 'Emph': + return inTags('em', [], this.renderInlines(inline.c)); + case 'Strong': + return inTags('strong', [], this.renderInlines(inline.c)); + case 'Html': + return inline.c; + case 'Entity': + return inline.c; + case 'Link': + attrs = [['href', this.escape(inline.destination, true)]]; + if (inline.title) { + attrs.push(['title', this.escape(inline.title, true)]); + } + return inTags('a', attrs, this.renderInlines(inline.label)); + case 'Image': + attrs = [['src', this.escape(inline.destination, true)], + ['alt', this.escape(this.renderInlines(inline.label))]]; + if (inline.title) { + attrs.push(['title', this.escape(inline.title, true)]); + } + return inTags('img', attrs, "", true); + case 'Code': + return inTags('code', [], this.escape(inline.c)); + default: + console.log("Uknown inline type " + inline.t); + return ""; + } +}; + +// Render a list of inlines. +var renderInlines = function(inlines) { + var result = ''; + for (var i=0; i < inlines.length; i++) { + result = result + this.renderInline(inlines[i]); + } + return result; +}; + +// Render a single block element. +var renderBlock = function(block, in_tight_list) { + var tag; + var attr; + var info_words; + switch (block.t) { + case 'Document': + var whole_doc = this.renderBlocks(block.children); + return (whole_doc === '' ? '' : whole_doc + '\n'); + case 'Paragraph': + if (in_tight_list) { + return this.renderInlines(block.inline_content); + } else { + return inTags('p', [], this.renderInlines(block.inline_content)); + } + break; + case 'BlockQuote': + var filling = this.renderBlocks(block.children); + return inTags('blockquote', [], filling === '' ? this.innersep : + this.innersep + this.renderBlocks(block.children) + this.innersep); + case 'ListItem': + return inTags('li', [], this.renderBlocks(block.children, in_tight_list).trim()); + case 'List': + tag = block.list_data.type == 'Bullet' ? 'ul' : 'ol'; + attr = (!block.list_data.start || block.list_data.start == 1) ? + [] : [['start', block.list_data.start.toString()]]; + return inTags(tag, attr, this.innersep + + this.renderBlocks(block.children, block.tight) + + this.innersep); + case 'ATXHeader': + case 'SetextHeader': + tag = 'h' + block.level; + return inTags(tag, [], this.renderInlines(block.inline_content)); + case 'IndentedCode': + return inTags('pre', [], + inTags('code', [], this.escape(block.string_content))); + case 'FencedCode': + info_words = block.info.split(/ +/); + attr = info_words.length === 0 || info_words[0].length === 0 ? + [] : [['class','language-' + + this.escape(info_words[0],true)]]; + return inTags('pre', [], + inTags('code', attr, this.escape(block.string_content))); + case 'HtmlBlock': + return block.string_content; + case 'ReferenceDef': + return ""; + case 'HorizontalRule': + return inTags('hr',[],"",true); + default: + console.log("Uknown block type " + block.t); + return ""; + } +}; + +// Render a list of block elements, separated by this.blocksep. +var renderBlocks = function(blocks, in_tight_list) { + var result = []; + for (var i=0; i < blocks.length; i++) { + if (blocks[i].t !== 'ReferenceDef') { + result.push(this.renderBlock(blocks[i], in_tight_list)); + } + } + return result.join(this.blocksep); +}; + +// The HtmlRenderer object. +function HtmlRenderer(){ + return { + // default options: + blocksep: '\n', // space between blocks + innersep: '\n', // space between block container tag and contents + softbreak: '\n', // by default, soft breaks are rendered as newlines in HTML + // set to "
      " to make them hard breaks + // set to " " if you want to ignore line wrapping in source + escape: function(s, preserve_entities) { + if (preserve_entities) { + return s.replace(/[&](?![#](x[a-f0-9]{1,8}|[0-9]{1,8});|[a-z][a-z0-9]{1,31};)/gi,'&') + .replace(/[<]/g,'<') + .replace(/[>]/g,'>') + .replace(/["]/g,'"'); + } else { + return s.replace(/[&]/g,'&') + .replace(/[<]/g,'<') + .replace(/[>]/g,'>') + .replace(/["]/g,'"'); + } + }, + renderInline: renderInline, + renderInlines: renderInlines, + renderBlock: renderBlock, + renderBlocks: renderBlocks, + render: renderBlock + }; +} + +exports.DocParser = DocParser; +exports.HtmlRenderer = HtmlRenderer; + +})(typeof exports === 'undefined' ? this.stmd = {} : exports); diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/timepicker.js b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/timepicker.js new file mode 100644 index 0000000..644703f --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/timepicker.js @@ -0,0 +1,2134 @@ +/*! jQuery Timepicker Addon - v1.4 - 2013-08-11 +* http://trentrichardson.com/examples/timepicker +* Copyright (c) 2013 Trent Richardson; Licensed MIT */ +(function ($) { + + /* + * Lets not redefine timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded" + */ + $.ui.timepicker = $.ui.timepicker || {}; + if ($.ui.timepicker.version) { + return; + } + + /* + * Extend jQueryUI, get it started with our version number + */ + $.extend($.ui, { + timepicker: { + version: "1.4" + } + }); + + /* + * Timepicker manager. + * Use the singleton instance of this class, $.timepicker, to interact with the time picker. + * Settings for (groups of) time pickers are maintained in an instance object, + * allowing multiple different settings on the same page. + */ + var Timepicker = function () { + this.regional = []; // Available regional settings, indexed by language code + this.regional[''] = { // Default regional settings + currentText: 'Now', + closeText: 'Done', + amNames: ['AM', 'A'], + pmNames: ['PM', 'P'], + timeFormat: 'HH:mm', + timeSuffix: '', + timeOnlyTitle: 'Choose Time', + timeText: 'Time', + hourText: 'Hour', + minuteText: 'Minute', + secondText: 'Second', + millisecText: 'Millisecond', + microsecText: 'Microsecond', + timezoneText: 'Time Zone', + isRTL: false + }; + this._defaults = { // Global defaults for all the datetime picker instances + showButtonPanel: true, + timeOnly: false, + showHour: null, + showMinute: null, + showSecond: null, + showMillisec: null, + showMicrosec: null, + showTimezone: null, + showTime: true, + stepHour: 1, + stepMinute: 1, + stepSecond: 1, + stepMillisec: 1, + stepMicrosec: 1, + hour: 0, + minute: 0, + second: 0, + millisec: 0, + microsec: 0, + timezone: null, + hourMin: 0, + minuteMin: 0, + secondMin: 0, + millisecMin: 0, + microsecMin: 0, + hourMax: 23, + minuteMax: 59, + secondMax: 59, + millisecMax: 999, + microsecMax: 999, + minDateTime: null, + maxDateTime: null, + onSelect: null, + hourGrid: 0, + minuteGrid: 0, + secondGrid: 0, + millisecGrid: 0, + microsecGrid: 0, + alwaysSetTime: true, + separator: ' ', + altFieldTimeOnly: true, + altTimeFormat: null, + altSeparator: null, + altTimeSuffix: null, + pickerTimeFormat: null, + pickerTimeSuffix: null, + showTimepicker: true, + timezoneList: null, + addSliderAccess: false, + sliderAccessArgs: null, + controlType: 'slider', + defaultValue: null, + parse: 'strict' + }; + $.extend(this._defaults, this.regional['']); + }; + + $.extend(Timepicker.prototype, { + $input: null, + $altInput: null, + $timeObj: null, + inst: null, + hour_slider: null, + minute_slider: null, + second_slider: null, + millisec_slider: null, + microsec_slider: null, + timezone_select: null, + hour: 0, + minute: 0, + second: 0, + millisec: 0, + microsec: 0, + timezone: null, + hourMinOriginal: null, + minuteMinOriginal: null, + secondMinOriginal: null, + millisecMinOriginal: null, + microsecMinOriginal: null, + hourMaxOriginal: null, + minuteMaxOriginal: null, + secondMaxOriginal: null, + millisecMaxOriginal: null, + microsecMaxOriginal: null, + ampm: '', + formattedDate: '', + formattedTime: '', + formattedDateTime: '', + timezoneList: null, + units: ['hour', 'minute', 'second', 'millisec', 'microsec'], + support: {}, + control: null, + + /* + * Override the default settings for all instances of the time picker. + * @param {Object} settings object - the new settings to use as defaults (anonymous object) + * @return {Object} the manager object + */ + setDefaults: function (settings) { + extendRemove(this._defaults, settings || {}); + return this; + }, + + /* + * Create a new Timepicker instance + */ + _newInst: function ($input, opts) { + var tp_inst = new Timepicker(), + inlineSettings = {}, + fns = {}, + overrides, i; + + for (var attrName in this._defaults) { + if (this._defaults.hasOwnProperty(attrName)) { + var attrValue = $input.attr('time:' + attrName); + if (attrValue) { + try { + inlineSettings[attrName] = eval(attrValue); + } catch (err) { + inlineSettings[attrName] = attrValue; + } + } + } + } + + overrides = { + beforeShow: function (input, dp_inst) { + if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) { + return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst); + } + }, + onChangeMonthYear: function (year, month, dp_inst) { + // Update the time as well : this prevents the time from disappearing from the $input field. + tp_inst._updateDateTime(dp_inst); + if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) { + tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst); + } + }, + onClose: function (dateText, dp_inst) { + if (tp_inst.timeDefined === true && $input.val() !== '') { + tp_inst._updateDateTime(dp_inst); + } + if ($.isFunction(tp_inst._defaults.evnts.onClose)) { + tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst); + } + } + }; + for (i in overrides) { + if (overrides.hasOwnProperty(i)) { + fns[i] = opts[i] || null; + } + } + + tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, opts, overrides, { + evnts: fns, + timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker'); + }); + tp_inst.amNames = $.map(tp_inst._defaults.amNames, function (val) { + return val.toUpperCase(); + }); + tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function (val) { + return val.toUpperCase(); + }); + + // detect which units are supported + tp_inst.support = detectSupport( + tp_inst._defaults.timeFormat + + (tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '') + + (tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : '')); + + // controlType is string - key to our this._controls + if (typeof(tp_inst._defaults.controlType) === 'string') { + if (tp_inst._defaults.controlType === 'slider' && typeof($.ui.slider) === 'undefined') { + tp_inst._defaults.controlType = 'select'; + } + tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType]; + } + // controlType is an object and must implement create, options, value methods + else { + tp_inst.control = tp_inst._defaults.controlType; + } + + // prep the timezone options + var timezoneList = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60, + 0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840]; + if (tp_inst._defaults.timezoneList !== null) { + timezoneList = tp_inst._defaults.timezoneList; + } + var tzl = timezoneList.length, tzi = 0, tzv = null; + if (tzl > 0 && typeof timezoneList[0] !== 'object') { + for (; tzi < tzl; tzi++) { + tzv = timezoneList[tzi]; + timezoneList[tzi] = { value: tzv, label: $.timepicker.timezoneOffsetString(tzv, tp_inst.support.iso8601) }; + } + } + tp_inst._defaults.timezoneList = timezoneList; + + // set the default units + tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone) : + ((new Date()).getTimezoneOffset() * -1); + tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin : + tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour; + tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin : + tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute; + tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin : + tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second; + tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin : + tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec; + tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin : + tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec; + tp_inst.ampm = ''; + tp_inst.$input = $input; + + if (tp_inst._defaults.altField) { + tp_inst.$altInput = $(tp_inst._defaults.altField).css({ + cursor: 'pointer' + }).focus(function () { + $input.trigger("focus"); + }); + } + + if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) { + tp_inst._defaults.minDate = new Date(); + } + if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) { + tp_inst._defaults.maxDate = new Date(); + } + + // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime.. + if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) { + tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime()); + } + if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) { + tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime()); + } + if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) { + tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime()); + } + if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) { + tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime()); + } + tp_inst.$input.bind('focus', function () { + tp_inst._onFocus(); + }); + + return tp_inst; + }, + + /* + * add our sliders to the calendar + */ + _addTimePicker: function (dp_inst) { + var currDT = (this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val(); + + this.timeDefined = this._parseTime(currDT); + this._limitMinMaxDateTime(dp_inst, false); + this._injectTimePicker(); + }, + + /* + * parse the time string from input value or _setTime + */ + _parseTime: function (timeString, withDate) { + if (!this.inst) { + this.inst = $.datepicker._getInst(this.$input[0]); + } + + if (withDate || !this._defaults.timeOnly) { + var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat'); + try { + var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults); + if (!parseRes.timeObj) { + return false; + } + $.extend(this, parseRes.timeObj); + } catch (err) { + $.timepicker.log("Error parsing the date/time string: " + err + + "\ndate/time string = " + timeString + + "\ntimeFormat = " + this._defaults.timeFormat + + "\ndateFormat = " + dp_dateFormat); + return false; + } + return true; + } else { + var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults); + if (!timeObj) { + return false; + } + $.extend(this, timeObj); + return true; + } + }, + + /* + * generate and inject html for timepicker into ui datepicker + */ + _injectTimePicker: function () { + var $dp = this.inst.dpDiv, + o = this.inst.settings, + tp_inst = this, + litem = '', + uitem = '', + show = null, + max = {}, + gridSize = {}, + size = null, + i = 0, + l = 0; + + // Prevent displaying twice + if ($dp.find("div.ui-timepicker-div").length === 0 && o.showTimepicker) { + var noDisplay = ' style="display:none;"', + html = '
      ' + '
      ' + o.timeText + '
      ' + + '
      '; + + // Create the markup + for (i = 0, l = this.units.length; i < l; i++) { + litem = this.units[i]; + uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1); + show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem]; + + // Added by Peter Medeiros: + // - Figure out what the hour/minute/second max should be based on the step values. + // - Example: if stepMinute is 15, then minMax is 45. + max[litem] = parseInt((o[litem + 'Max'] - ((o[litem + 'Max'] - o[litem + 'Min']) % o['step' + uitem])), 10); + gridSize[litem] = 0; + + html += '
      ' + o[litem + 'Text'] + '
      ' + + '
      '; + + if (show && o[litem + 'Grid'] > 0) { + html += '
      '; + + if (litem === 'hour') { + for (var h = o[litem + 'Min']; h <= max[litem]; h += parseInt(o[litem + 'Grid'], 10)) { + gridSize[litem]++; + var tmph = $.datepicker.formatTime(this.support.ampm ? 'hht' : 'HH', {hour: h}, o); + html += ''; + } + } + else { + for (var m = o[litem + 'Min']; m <= max[litem]; m += parseInt(o[litem + 'Grid'], 10)) { + gridSize[litem]++; + html += ''; + } + } + + html += '
      ' + tmph + '' + ((m < 10) ? '0' : '') + m + '
      '; + } + html += '
      '; + } + + // Timezone + var showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone; + html += '
      ' + o.timezoneText + '
      '; + html += '
      '; + + // Create the elements from string + html += '
      '; + var $tp = $(html); + + // if we only want time picker... + if (o.timeOnly === true) { + $tp.prepend('
      ' + '
      ' + o.timeOnlyTitle + '
      ' + '
      '); + $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide(); + } + + // add sliders, adjust grids, add events + for (i = 0, l = tp_inst.units.length; i < l; i++) { + litem = tp_inst.units[i]; + uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1); + show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem]; + + // add the slider + tp_inst[litem + '_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_' + litem + '_slider'), litem, tp_inst[litem], o[litem + 'Min'], max[litem], o['step' + uitem]); + + // adjust the grid and add click event + if (show && o[litem + 'Grid'] > 0) { + size = 100 * gridSize[litem] * o[litem + 'Grid'] / (max[litem] - o[litem + 'Min']); + $tp.find('.ui_tpicker_' + litem + ' table').css({ + width: size + "%", + marginLeft: o.isRTL ? '0' : ((size / (-2 * gridSize[litem])) + "%"), + marginRight: o.isRTL ? ((size / (-2 * gridSize[litem])) + "%") : '0', + borderCollapse: 'collapse' + }).find("td").click(function (e) { + var $t = $(this), + h = $t.html(), + n = parseInt(h.replace(/[^0-9]/g), 10), + ap = h.replace(/[^apm]/ig), + f = $t.data('for'); // loses scope, so we use data-for + + if (f === 'hour') { + if (ap.indexOf('p') !== -1 && n < 12) { + n += 12; + } + else { + if (ap.indexOf('a') !== -1 && n === 12) { + n = 0; + } + } + } + + tp_inst.control.value(tp_inst, tp_inst[f + '_slider'], litem, n); + + tp_inst._onTimeChange(); + tp_inst._onSelectHandler(); + }).css({ + cursor: 'pointer', + width: (100 / gridSize[litem]) + '%', + textAlign: 'center', + overflow: 'hidden' + }); + } // end if grid > 0 + } // end for loop + + // Add timezone options + this.timezone_select = $tp.find('.ui_tpicker_timezone').append('').find("select"); + $.fn.append.apply(this.timezone_select, + $.map(o.timezoneList, function (val, idx) { + return $("
        ") + .addClass(settings.classes.tokenList) + .click(function (event) { + var li = $(event.target).closest("li"); + if(li && li.get(0) && $.data(li.get(0), "tokeninput")) { + toggle_select_token(li); + } else { + // Deselect selected token + if(selected_token) { + deselect_token($(selected_token), POSITION.END); + } + + // Focus input box + input_box.focus(); + } + }) + .mouseover(function (event) { + var li = $(event.target).closest("li"); + if(li && selected_token !== this) { + li.addClass(settings.classes.highlightedToken); + } + }) + .mouseout(function (event) { + var li = $(event.target).closest("li"); + if(li && selected_token !== this) { + li.removeClass(settings.classes.highlightedToken); + } + }) + .insertBefore(hidden_input); + + // The token holding the input box + var input_token = $("
      • ") + .addClass(settings.classes.inputToken) + .appendTo(token_list) + .append(input_box); + + // The list to store the dropdown items in + var dropdown = $("
        ") + .addClass(settings.classes.dropdown) + .appendTo("body") + .hide(); + + // Magic element to help us resize the text input + var input_resizer = $("") + .insertAfter(input_box) + .css({ + position: "absolute", + top: -9999, + left: -9999, + width: "auto", + fontSize: input_box.css("fontSize"), + fontFamily: input_box.css("fontFamily"), + fontWeight: input_box.css("fontWeight"), + letterSpacing: input_box.css("letterSpacing"), + whiteSpace: "nowrap" + }); + + // Pre-populate list if items exist + hidden_input.val(""); + var li_data = settings.prePopulate || hidden_input.data("pre"); + if(settings.processPrePopulate && $.isFunction(settings.onResult)) { + li_data = settings.onResult.call(hidden_input, li_data); + } + if(li_data && li_data.length) { + $.each(li_data, function (index, value) { + insert_token(value); + checkTokenLimit(); + }); + } + + // Initialization is done + if($.isFunction(settings.onReady)) { + settings.onReady.call(); + } + + // + // Public functions + // + + this.clear = function() { + token_list.children("li").each(function() { + if ($(this).children("input").length === 0) { + delete_token($(this)); + } + }); + } + + this.add = function(item) { + add_token(item); + } + + this.remove = function(item) { + token_list.children("li").each(function() { + if ($(this).children("input").length === 0) { + var currToken = $(this).data("tokeninput"); + var match = true; + for (var prop in item) { + if (item[prop] !== currToken[prop]) { + match = false; + break; + } + } + if (match) { + delete_token($(this)); + } + } + }); + } + + this.getTokens = function() { + return saved_tokens; + } + + // + // Private functions + // + + function checkTokenLimit() { + if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { + input_box.hide(); + hide_dropdown(); + return; + } + } + + function resize_input() { + if(input_val === (input_val = input_box.val())) {return;} + + // Enter new content into resizer and resize input accordingly + var escaped = input_val.replace(/&/g, '&').replace(/\s/g,' ').replace(//g, '>'); + input_resizer.html(escaped); + input_box.width(input_resizer.width() + 30); + } + + function is_printable_character(keycode) { + return ((keycode >= 48 && keycode <= 90) || // 0-1a-z + (keycode >= 96 && keycode <= 111) || // numpad 0-9 + - / * . + (keycode >= 186 && keycode <= 192) || // ; = , - . / ^ + (keycode >= 219 && keycode <= 222)); // ( \ ) ' + } + + // Inner function to a token to the list + function insert_token(item) { + var this_token = settings.tokenFormatter(item); + this_token = $(this_token) + .addClass(settings.classes.token) + .insertBefore(input_token); + + // The 'delete token' button + $("" + settings.deleteText + "") + .addClass(settings.classes.tokenDelete) + .appendTo(this_token) + .click(function () { + delete_token($(this).parent()); + hidden_input.change(); + return false; + }); + + // Store data on the token + var token_data = {"id": item.id}; + token_data[settings.propertyToSearch] = item[settings.propertyToSearch]; + $.data(this_token.get(0), "tokeninput", item); + + // Save this token for duplicate checking + saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index)); + selected_token_index++; + + // Update the hidden input + update_hidden_input(saved_tokens, hidden_input); + + token_count += 1; + + // Check the token limit + if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { + input_box.hide(); + hide_dropdown(); + } + + return this_token; + } + + // Add a token to the token list based on user input + function add_token (item) { + var callback = settings.onAdd; + + // fix null bug + if (!item && input_box.val().length > 0) { + item = { + id : input_box.val() + }; + + item[settings.propertyToSearch] = input_box.val(); + } + + if (!item) { + return false; + } + + // See if the token already exists and select it if we don't want duplicates + if(token_count > 0 && settings.preventDuplicates) { + var found_existing_token = null; + token_list.children().each(function () { + var existing_token = $(this); + var existing_data = $.data(existing_token.get(0), "tokeninput"); + if(existing_data && existing_data.id === item.id) { + found_existing_token = existing_token; + return false; + } + }); + + if(found_existing_token) { + select_token(found_existing_token); + input_token.insertAfter(found_existing_token); + input_box.focus(); + return; + } + } + + // Insert the new tokens + if(settings.tokenLimit == null || token_count < settings.tokenLimit) { + insert_token(item); + checkTokenLimit(); + } + + // Clear input box + input_box.val(""); + + // Don't show the help dropdown, they've got the idea + hide_dropdown(); + + // Execute the onAdd callback if defined + if($.isFunction(callback)) { + callback.call(hidden_input,item); + } + } + + // Select a token in the token list + function select_token (token) { + token.addClass(settings.classes.selectedToken); + selected_token = token.get(0); + + // Hide input box + input_box.val(""); + + // Hide dropdown if it is visible (eg if we clicked to select token) + hide_dropdown(); + } + + // Deselect a token in the token list + function deselect_token (token, position) { + token.removeClass(settings.classes.selectedToken); + selected_token = null; + + if(position === POSITION.BEFORE) { + input_token.insertBefore(token); + selected_token_index--; + } else if(position === POSITION.AFTER) { + input_token.insertAfter(token); + selected_token_index++; + } else { + input_token.appendTo(token_list); + selected_token_index = token_count; + } + + // Show the input box and give it focus again + input_box.focus(); + } + + // Toggle selection of a token in the token list + function toggle_select_token(token) { + var previous_selected_token = selected_token; + + if(selected_token) { + deselect_token($(selected_token), POSITION.END); + } + + if(previous_selected_token === token.get(0)) { + deselect_token(token, POSITION.END); + } else { + select_token(token); + } + } + + // Delete a token from the token list + function delete_token (token) { + // Remove the id from the saved list + var token_data = $.data(token.get(0), "tokeninput"); + var callback = settings.onDelete; + + var index = token.prevAll().length; + if(index > selected_token_index) index--; + + // Delete the token + token.remove(); + selected_token = null; + + // Show the input box and give it focus again + input_box.focus(); + + // Remove this token from the saved list + saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1)); + if(index < selected_token_index) selected_token_index--; + + // Update the hidden input + update_hidden_input(saved_tokens, hidden_input); + + token_count -= 1; + + if(settings.tokenLimit !== null) { + input_box + .show() + .val("") + .focus(); + } + + // Execute the onDelete callback if defined + if($.isFunction(callback)) { + callback.call(hidden_input,token_data); + } + } + + // Update the hidden input box value + function update_hidden_input(saved_tokens, hidden_input) { + var token_values = $.map(saved_tokens, function (el) { + return el[settings.tokenValue]; + }); + hidden_input.val(token_values.join(settings.tokenDelimiter)); + + } + + // Hide and clear the results dropdown + function hide_dropdown () { + dropdown.hide().empty(); + selected_dropdown_item = null; + } + + function show_dropdown() { + dropdown + .css({ + position: "absolute", + top: $(token_list).offset().top + $(token_list).outerHeight(), + left: $(token_list).offset().left, + zindex: 999 + }) + .show(); + } + + function show_dropdown_searching () { + if(settings.searchingText) { + dropdown.html("

        "+settings.searchingText+"

        "); + show_dropdown(); + } + } + + function show_dropdown_hint () { + if(settings.hintText) { + dropdown.html("

        "+settings.hintText+"

        "); + show_dropdown(); + } + } + + // Highlight the query part of the search term + function highlight_term(value, term) { + return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); + } + + function find_value_and_highlight_term(template, value, term) { + return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + value + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term)); + } + + // Populate the results dropdown with some results + function populate_dropdown (query, results) { + if(results && results.length) { + dropdown.empty(); + var dropdown_ul = $("
          ") + .appendTo(dropdown) + .mouseover(function (event) { + select_dropdown_item($(event.target).closest("li")); + }) + .mousedown(function (event) { + add_token($(event.target).closest("li").data("tokeninput")); + hidden_input.change(); + return false; + }) + .hide(); + + $.each(results, function(index, value) { + var this_li = settings.resultsFormatter(value); + + this_li = find_value_and_highlight_term(this_li ,value[settings.propertyToSearch], query); + + this_li = $(this_li).appendTo(dropdown_ul); + + if(index % 2) { + this_li.addClass(settings.classes.dropdownItem); + } else { + this_li.addClass(settings.classes.dropdownItem2); + } + + if(index === 0) { + select_dropdown_item(this_li); + } + + $.data(this_li.get(0), "tokeninput", value); + }); + + show_dropdown(); + + if(settings.animateDropdown) { + dropdown_ul.slideDown("fast"); + } else { + dropdown_ul.show(); + } + } else { + if(settings.noResultsText) { + dropdown.html("

          "+settings.noResultsText+"

          "); + show_dropdown(); + } + } + } + + // Highlight an item in the results dropdown + function select_dropdown_item (item) { + if(item) { + if(selected_dropdown_item) { + deselect_dropdown_item($(selected_dropdown_item)); + } + + item.addClass(settings.classes.selectedDropdownItem); + selected_dropdown_item = item.get(0); + } + } + + // Remove highlighting from an item in the results dropdown + function deselect_dropdown_item (item) { + item.removeClass(settings.classes.selectedDropdownItem); + selected_dropdown_item = null; + } + + // Do a search and show the "searching" dropdown if the input is longer + // than settings.minChars + function do_search() { + var query = input_box.val().toLowerCase(); + + if(query && query.length) { + if(selected_token) { + deselect_token($(selected_token), POSITION.AFTER); + } + + if(query.length >= settings.minChars) { + show_dropdown_searching(); + clearTimeout(timeout); + + timeout = setTimeout(function(){ + run_search(query); + }, settings.searchDelay); + } else { + hide_dropdown(); + } + } + } + + // Do the actual search + function run_search(query) { + var cache_key = query + computeURL(); + var cached_results = cache.get(cache_key); + if(cached_results) { + populate_dropdown(query, cached_results); + } else { + // Are we doing an ajax search or local data search? + if(settings.url) { + var url = computeURL(); + // Extract exisiting get params + var ajax_params = {}; + ajax_params.data = {}; + if(url.indexOf("?") > -1) { + var parts = url.split("?"); + ajax_params.url = parts[0]; + + var param_array = parts[1].split("&"); + $.each(param_array, function (index, value) { + var kv = value.split("="); + ajax_params.data[kv[0]] = kv[1]; + }); + } else { + ajax_params.url = url; + } + + // Prepare the request + ajax_params.data[settings.queryParam] = query; + ajax_params.type = settings.method; + ajax_params.dataType = settings.contentType; + if(settings.crossDomain) { + ajax_params.dataType = "jsonp"; + } + + // Attach the success callback + ajax_params.success = function(results) { + if($.isFunction(settings.onResult)) { + results = settings.onResult.call(hidden_input, results, query); + } + cache.add(cache_key, settings.jsonContainer ? results[settings.jsonContainer] : results); + + // only populate the dropdown if the results are associated with the active search query + if(input_box.val().toLowerCase() === query) { + populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results); + } + }; + + // Make the request + $.ajax(ajax_params); + } else if(settings.local_data) { + // Do the search through local data + var results = $.grep(settings.local_data, function (row) { + return row[settings.propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1; + }); + + if($.isFunction(settings.onResult)) { + results = settings.onResult.call(hidden_input, results, query); + } + cache.add(cache_key, results); + populate_dropdown(query, results); + } + } + } + + // compute the dynamic URL + function computeURL() { + var url = settings.url; + if(typeof settings.url == 'function') { + url = settings.url.call(); + } + return url; + } +}; + +// Really basic cache for the results +$.TokenList.Cache = function (options) { + var settings = $.extend({ + max_size: 500 + }, options); + + var data = {}; + var size = 0; + + var flush = function () { + data = {}; + size = 0; + }; + + this.add = function (query, results) { + if(size > settings.max_size) { + flush(); + } + + if(!data[query]) { + size += 1; + } + + data[query] = results; + }; + + this.get = function (query) { + return data[query]; + }; +}; +}(jQuery)); diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/typecho.js b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/typecho.js new file mode 100644 index 0000000..d27e43d --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/js/typecho.js @@ -0,0 +1,1257 @@ +(function (w) { + w.Typecho = { + insertFileToEditor : function (file, url, isImage) {}, + editorResize : function (id, url) { + $('#' + id).resizeable({ + minHeight : 100, + afterResize : function (h) { + $.post(url, {size : h}); + } + }) + }, + uploadComplete : function (file) {} + }; +})(window); + +(function ($) { + // 下拉菜单插件 + $.fn.dropdownMenu = function (options) { + this.each(function () { + var menu = this, s = $.extend({ + menuEl : null, + btnEl : null + }, options); + + $(s.btnEl, menu).click(function () { + var t = $(this); + + t.toggleClass('active'); + $(s.menuEl, menu).toggle(); + return false; + }); + }); + }; + + $.fn.resizeable = function (options) { + var s = $.extend({ + minHeight : 100, + afterResize : null + }, options); + + return this.each(function () { + var r = $('').insertAfter(this), + staticOffset, iLastMousePos = 0, iMin = s.minHeight, t = this; + + function startDrag(e) { + textarea = $(e.data.el); + textarea.blur(); + iLastMousePos = mousePosition(e).y; + + staticOffset = textarea.height() - iLastMousePos; + textarea.css('opacity', 0.25); + + $(document).mousemove(performDrag).mouseup(endDrag); + return false; + } + + function performDrag(e) { + var iThisMousePos = mousePosition(e).y, + iMousePos = staticOffset + iThisMousePos; + if (iLastMousePos >= (iThisMousePos)) { + iMousePos -= 5; + } + + iLastMousePos = iThisMousePos; + iMousePos = Math.max(iMin, iMousePos); + textarea.height(iMousePos + 'px'); + + if (iMousePos < iMin) { + endDrag(e); + } + return false; + } + + function endDrag(e) { + var h = textarea.outerHeight(); + $(document).unbind('mousemove', performDrag).unbind('mouseup', endDrag); + + textarea.css('opacity', 1); + textarea.focus(); + textarea = null; + + staticOffset = null; + iLastMousePos = 0; + + if (s.afterResize) { + s.afterResize.call(t, h); + } + } + + function mousePosition(e) { + return { x: e.clientX + document.documentElement.scrollLeft, y: e.clientY + document.documentElement.scrollTop }; + } + + r.bind('mousedown', {el : this}, startDrag); + }); + }; + + // 表格选择插件 + $.fn.tableSelectable = function (options) { + var table = this, s = $.extend({ + checkEl : null, + rowEl : null, + selectAllEl : null, + actionEl : null + }, options); + + function clickRow (t) { + var t = $(t), check = $(s.checkEl, t), checked = check.prop('checked'); + + if (!check.length) { + return; + } + + check.prop('checked', !checked); + + if (checked) { + t.removeClass('checked'); + } else { + t.addClass('checked'); + } + } + + $(s.rowEl, this).each(function () { + $(s.checkEl, this).click(function (e) { + clickRow($(this).parents(s.rowEl)); + }); + }).click(function (e) { + var target = $(e.toElement ? e.toElement : e.target), + tagName = target.prop('tagName').toLowerCase(); + + if ($.inArray(tagName, ['input', 'textarea', 'a', 'button']) >= 0 + && 'checkbox' != target.attr('type')) { + e.stopPropagation(); + } else { + clickRow(this); + } + }); + + $(s.selectAllEl).click(function () { + var t = $(this), checked = t.prop('checked'); + + if (checked) { + $(s.rowEl, table).each(function () { + var t = $(this), el = $(s.checkEl, this).prop('checked', true); + if (el.length > 0) { + t.addClass('checked'); + } + }); + } else { + $(s.rowEl, table).each(function () { + var t = $(this), el = $(s.checkEl, this).prop('checked', false); + if (el.length > 0) { + t.removeClass('checked'); + } + }); + } + }); + + $(s.actionEl).click(function () { + var t = $(this), lang = t.attr('lang'); + + if (!lang || confirm(lang)) { + table.parents('form').attr('action', t.attr('href')).submit(); + } + + return false; + }); + }; +})($); + +(function () { + + Typecho.Markdown = function () { + this.writer = new stmd.HtmlRenderer(); + this.reader = new stmd.DocParser(); + + + /* begin hardbreak hack */ + var w = this.writer, r = w.renderInline; + w.renderInline = function (inline) { + if (inline.t == 'Softbreak') inline.t = 'Hardbreak'; + return r.call(w, inline); + }; + /* end hardbreak hack */ + + this.hooks = new Markdown.HookCollection() + this.hooks.addNoop('postConversion'); + }; + + Typecho.Markdown.prototype.makeHtml = function (text) { + var doc = this.reader.parse(text), + html = this.writer.renderBlock(doc); + + return this.hooks.postConversion(html); + }; + +})(); + + +/** + * TableDnD plug-in for JQuery, allows you to drag and drop table rows + * You can set up various options to control how the system will work + * Copyright © Denis Howlett + * Licensed like jQuery, see http://docs.jquery.com/License. + * + * Configuration options: + * + * onDragStyle + * This is the style that is assigned to the row during drag. There are limitations to the styles that can be + * associated with a row (such as you can't assign a border—well you can, but it won't be + * displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as + * a map (as used in the jQuery css(...) function). + * onDropStyle + * This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations + * to what you can do. Also this replaces the original style, so again consider using onDragClass which + * is simply added and then removed on drop. + * onDragClass + * This class is added for the duration of the drag and then removed when the row is dropped. It is more + * flexible than using onDragStyle since it can be inherited by the row cells and other content. The default + * is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your + * stylesheet. + * onDrop + * Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table + * and the row that was dropped. You can work out the new order of the rows by using + * table.rows. + * onDragStart + * Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the + * table and the row which the user has started to drag. + * onAllowDrop + * Pass a function that will be called as a row is over another row. If the function returns true, allow + * dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under + * the cursor. It returns a boolean: true allows the drop, false doesn't allow it. + * scrollAmount + * This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the + * window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2, + * FF3 beta) + * + * Other ways to control behaviour: + * + * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows + * that you don't want to be draggable. + * + * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form + * []=&[]= so that you can send this back to the server. The table must have + * an ID as must all the rows. + * + * Known problems: + * - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0 + * + * Version 0.2: 2008-02-20 First public version + * Version 0.3: 2008-02-07 Added onDragStart option + * Made the scroll amount configurable (default is 5 as before) + * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes + * Added onAllowDrop to control dropping + * Fixed a bug which meant that you couldn't set the scroll amount in both directions + * Added serialise method + */ +(function (jQuery) { +jQuery.tableDnD = { + /** Keep hold of the current table being dragged */ + currentTable : null, + /** Keep hold of the current drag object if any */ + dragObject: null, + /** The current mouse offset */ + mouseOffset: null, + /** Remember the old value of Y so that we don't do too much processing */ + oldY: 0, + + /** Actually build the structure */ + build: function(options) { + // Make sure options exists + options = options || {}; + // Set up the defaults if any + + this.each(function() { + // Remember the options + this.tableDnDConfig = { + onDragStyle: options.onDragStyle, + onDropStyle: options.onDropStyle, + // Add in the default class for whileDragging + onDragClass: options.onDragClass ? options.onDragClass : "tDnD_whileDrag", + onDrop: options.onDrop, + onDragStart: options.onDragStart, + scrollAmount: options.scrollAmount ? options.scrollAmount : 5 + }; + // Now make the rows draggable + jQuery.tableDnD.makeDraggable(this); + + // fix chrome border bug + if (0 == $('tfoot', this).length + && 0 < $('thead', this).length) { + var h = $('thead', this), count = $('th', h).length, + f = $('').insertAfter(h), + l = $('tr:last', this); + + if (l.parent().prop('tagName').toLowerCase() != 'tfoot') { + var td = $('td', l), dh = td.height(); + td.height(dh - f.outerHeight()); + } + } + }); + + // Now we need to capture the mouse up and mouse move event + // We can use bind so that we don't interfere with other event handlers + jQuery(document) + .bind('mousemove', jQuery.tableDnD.mousemove) + .bind('mouseup', jQuery.tableDnD.mouseup); + + // Don't break the chain + return this; + }, + + /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */ + makeDraggable: function(table) { + // Now initialise the rows + var rows = table.rows; //getElementsByTagName("tr") + var config = table.tableDnDConfig; + for (var i=0; i jQuery.tableDnD.oldY; + // update the old value + jQuery.tableDnD.oldY = y; + // update the style to show we're dragging + if (config.onDragClass) { + dragObj.addClass(config.onDragClass); + } else { + dragObj.css(config.onDragStyle); + } + // If we're over a row then move the dragged row to there so that the user sees the + // effect dynamically + var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y); + if (currentRow) { + // TODO worry about what happens when there are multiple TBODIES + if (movingDown && jQuery.tableDnD.dragObject != currentRow) { + jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling); + } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) { + jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow); + } + } + } + + return false; + }, + + /** We're only worried about the y position really, because we can only move rows up and down */ + findDropTargetRow: function(draggedRow, y) { + var rows = jQuery.tableDnD.currentTable.rows; + for (var i=0; i rowY - rowHeight) && (y < (rowY + rowHeight))) { + // that's the row we're over + // If it's the same as the current row, ignore it + if (row == draggedRow) {return null;} + var config = jQuery.tableDnD.currentTable.tableDnDConfig; + if (config.onAllowDrop) { + if (config.onAllowDrop(draggedRow, row)) { + return row; + } else { + return null; + } + } else { + // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic) + var nodrop = $(row).hasClass("nodrop"); + if (! nodrop) { + return row; + } else { + return null; + } + } + return row; + } + } + return null; + }, + + mouseup: function(e) { + if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) { + var droppedRow = jQuery.tableDnD.dragObject; + var config = jQuery.tableDnD.currentTable.tableDnDConfig; + // If we have a dragObject, then we need to release it, + // The row will already have been moved to the right place so we just reset stuff + if (config.onDragClass) { + jQuery(droppedRow).removeClass(config.onDragClass); + } else { + jQuery(droppedRow).css(config.onDropStyle); + } + jQuery.tableDnD.dragObject = null; + if (config.onDrop) { + // Call the onDrop method if there is one + config.onDrop(jQuery.tableDnD.currentTable, droppedRow); + } + jQuery.tableDnD.currentTable = null; // let go of the table too + } + }, + + serialize: function() { + if (jQuery.tableDnD.currentTable) { + var result = ""; + var tableId = jQuery.tableDnD.currentTable.id; + var rows = jQuery.tableDnD.currentTable.rows; + for (var i=0; i 0) result += "&"; + result += tableId + '[]=' + rows[i].id; + } + return result; + } else { + return "Error: No Table id set, you need to set an id on your table and every row"; + } + } +} + +jQuery.fn.extend( + { + tableDnD : jQuery.tableDnD.build + } +); +})($); + +/* + Masked Input plugin for jQuery + Copyright (c) 2007-2013 Josh Bush (digitalbush.com) + Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license) + Version: 1.3.1 +*/ +(function($) { + function getPasteEvent() { + var el = document.createElement('input'), + name = 'onpaste'; + el.setAttribute(name, ''); + return (typeof el[name] === 'function')?'paste':'input'; +} + +var pasteEventName = getPasteEvent() + ".mask", + ua = navigator.userAgent, + iPhone = /iphone/i.test(ua), + android=/android/i.test(ua), + caretTimeoutId; + +$.mask = { + //Predefined character definitions + definitions: { + '9': "[0-9]", + 'a': "[A-Za-z]", + '*': "[A-Za-z0-9]" + }, + dataName: "rawMaskFn", + placeholder: '_', +}; + +$.fn.extend({ + //Helper Function for Caret positioning + caret: function(begin, end) { + var range; + + if (this.length === 0 || this.is(":hidden")) { + return; + } + + if (typeof begin == 'number') { + end = (typeof end === 'number') ? end : begin; + return this.each(function() { + if (this.setSelectionRange) { + this.setSelectionRange(begin, end); + } else if (this.createTextRange) { + range = this.createTextRange(); + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', begin); + range.select(); + } + }); + } else { + if (this[0].setSelectionRange) { + begin = this[0].selectionStart; + end = this[0].selectionEnd; + } else if (document.selection && document.selection.createRange) { + range = document.selection.createRange(); + begin = 0 - range.duplicate().moveStart('character', -100000); + end = begin + range.text.length; + } + return { begin: begin, end: end }; + } + }, + unmask: function() { + return this.trigger("unmask"); + }, + mask: function(mask, settings) { + var input, + defs, + tests, + partialPosition, + firstNonMaskPos, + len; + + if (!mask && this.length > 0) { + input = $(this[0]); + return input.data($.mask.dataName)(); + } + settings = $.extend({ + placeholder: $.mask.placeholder, // Load default placeholder + completed: null + }, settings); + + + defs = $.mask.definitions; + tests = []; + partialPosition = len = mask.length; + firstNonMaskPos = null; + + $.each(mask.split(""), function(i, c) { + if (c == '?') { + len--; + partialPosition = i; + } else if (defs[c]) { + tests.push(new RegExp(defs[c])); + if (firstNonMaskPos === null) { + firstNonMaskPos = tests.length - 1; + } + } else { + tests.push(null); + } + }); + + return this.trigger("unmask").each(function() { + var input = $(this), + buffer = $.map( + mask.split(""), + function(c, i) { + if (c != '?') { + return defs[c] ? settings.placeholder : c; + } + }), + focusText = input.val(); + + function seekNext(pos) { + while (++pos < len && !tests[pos]); + return pos; + } + + function seekPrev(pos) { + while (--pos >= 0 && !tests[pos]); + return pos; + } + + function shiftL(begin,end) { + var i, + j; + + if (begin<0) { + return; + } + + for (i = begin, j = seekNext(end); i < len; i++) { + if (tests[i]) { + if (j < len && tests[i].test(buffer[j])) { + buffer[i] = buffer[j]; + buffer[j] = settings.placeholder; + } else { + break; + } + + j = seekNext(j); + } + } + writeBuffer(); + input.caret(Math.max(firstNonMaskPos, begin)); + } + + function shiftR(pos) { + var i, + c, + j, + t; + + for (i = pos, c = settings.placeholder; i < len; i++) { + if (tests[i]) { + j = seekNext(i); + t = buffer[i]; + buffer[i] = c; + if (j < len && tests[j].test(t)) { + c = t; + } else { + break; + } + } + } + } + + function keydownEvent(e) { + var k = e.which, + pos, + begin, + end; + + //backspace, delete, and escape get special treatment + if (k === 8 || k === 46 || (iPhone && k === 127)) { + pos = input.caret(); + begin = pos.begin; + end = pos.end; + + if (end - begin === 0) { + begin=k!==46?seekPrev(begin):(end=seekNext(begin-1)); + end=k===46?seekNext(end):end; + } + clearBuffer(begin, end); + shiftL(begin, end - 1); + + e.preventDefault(); + } else if (k == 27) {//escape + input.val(focusText); + input.caret(0, checkVal()); + e.preventDefault(); + } + } + + function keypressEvent(e) { + var k = e.which, + pos = input.caret(), + p, + c, + next; + + if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore + return; + } else if (k) { + if (pos.end - pos.begin !== 0){ + clearBuffer(pos.begin, pos.end); + shiftL(pos.begin, pos.end-1); + } + + p = seekNext(pos.begin - 1); + if (p < len) { + c = String.fromCharCode(k); + if (tests[p].test(c)) { + shiftR(p); + + buffer[p] = c; + writeBuffer(); + next = seekNext(p); + + if(android){ + setTimeout($.proxy($.fn.caret,input,next),0); + }else{ + input.caret(next); + } + + if (settings.completed && next >= len) { + settings.completed.call(input); + } + } + } + e.preventDefault(); + } + } + + function clearBuffer(start, end) { + var i; + for (i = start; i < end && i < len; i++) { + if (tests[i]) { + buffer[i] = settings.placeholder; + } + } + } + + function writeBuffer() { input.val(buffer.join('')); } + + function checkVal(allow) { + //try to place characters where they belong + var test = input.val(), + lastMatch = -1, + i, + c; + + for (i = 0, pos = 0; i < len; i++) { + if (tests[i]) { + buffer[i] = settings.placeholder; + while (pos++ < test.length) { + c = test.charAt(pos - 1); + if (tests[i].test(c)) { + buffer[i] = c; + lastMatch = i; + break; + } + } + if (pos > test.length) { + break; + } + } else if (buffer[i] === test.charAt(pos) && i !== partialPosition) { + pos++; + lastMatch = i; + } + } + if (allow) { + writeBuffer(); + } else if (lastMatch + 1 < partialPosition) { + input.val(""); + clearBuffer(0, len); + } else { + writeBuffer(); + input.val(input.val().substring(0, lastMatch + 1)); + } + return (partialPosition ? i : firstNonMaskPos); + } + + input.data($.mask.dataName,function(){ + return $.map(buffer, function(c, i) { + return tests[i]&&c!=settings.placeholder ? c : null; + }).join(''); + }); + + if (!input.attr("readonly")) + input + .one("unmask", function() { + input + .unbind(".mask") + .removeData($.mask.dataName); + }) + .bind("focus.mask", function() { + clearTimeout(caretTimeoutId); + var pos, + moveCaret; + + focusText = input.val(); + pos = checkVal(); + + caretTimeoutId = setTimeout(function(){ + writeBuffer(); + if (pos == mask.length) { + input.caret(0, pos); + } else { + input.caret(pos); + } + }, 10); + }) + .bind("blur.mask", function() { + checkVal(); + if (input.val() != focusText) + input.change(); + }) + .bind("keydown.mask", keydownEvent) + .bind("keypress.mask", keypressEvent) + .bind(pasteEventName, function() { + setTimeout(function() { + var pos=checkVal(true); + input.caret(pos); + if (settings.completed && pos == input.val().length) + settings.completed.call(input); + }, 0); + }); + checkVal(); //Perform initial check for existing values + }); + } +}); + + +})(jQuery); + +/* + * jQuery plugin: fieldSelection - v0.1.1 - last change: 2006-12-16 + * (c) 2006 Alex Brem - http://blog.0xab.cd + */ + +jQuery.fn.extend({ + getSelection: function () { + + var e = this.get(0); + if (!e) { + return null; + } + + return ( + + /* mozilla / dom 3.0 */ + ('selectionStart' in e && function() { + var l = e.selectionEnd - e.selectionStart; + return { start: e.selectionStart, end: e.selectionEnd, length: l, text: e.value.substr(e.selectionStart, l) }; + }) || + + /* other */ + (window.getSelection() && function () { + var selection = window.getSelection(), range = selection.getRangeAt(0); + + return { start: range.startOffset, end: range.endOffset, length: range.endOffset - range.startOffset, text: range.toString()}; + + }) || + + /* exploder */ + (document.selection && function() { + + e.focus(); + + var r = document.selection.createRange(); + if (r === null) { + return { start: 0, end: e.value.length, length: 0 } + } + + var re = e.createTextRange(); + var rc = re.duplicate(); + re.moveToBookmark(r.getBookmark()); + rc.setEndPoint('EndToStart', re); + + return { start: rc.text.length, end: rc.text.length + r.text.length, length: r.text.length, text: r.text }; + }) || + + /* browser not supported */ + function() { return null; } + + )(); + }, + + setSelection: function (start, end) { + var e = this.get(0); + if (!e) { + return; + } + + if (e.setSelectionRange) { + e.focus(); + e.setSelectionRange(start, end); + } else if (e.createTextRange) { + var range = e.createTextRange(); + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', start); + range.select(); + } + }, + + replaceSelection: function () { + + var e = this.get(0); + if (!e) { + return null; + } + + var text = arguments[0] || ''; + + return ( + + /* mozilla / dom 3.0 */ + ('selectionStart' in e && function() { + e.value = e.value.substr(0, e.selectionStart) + text + e.value.substr(e.selectionEnd, e.value.length); + return this; + }) || + + /* exploder */ + (document.selection && function() { + e.focus(); + document.selection.createRange().text = text; + return this; + }) || + + /* browser not supported */ + function() { + e.value += text; + return jQuery(e); + } + + )(); + } +}); + +/** + * jQuery Cookie plugin + * + * Copyright (c) 2010 Klaus Hartl (stilbuero.de) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ +jQuery.cookie = function (key, value, options) { + + // key and at least value given, set cookie... + if (arguments.length > 1 && String(value) !== "[object Object]") { + options = jQuery.extend({}, options); + + if (value === null || value === undefined) { + options.expires = -1; + } + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setDate(t.getDate() + days); + } + + value = String(value); + + return (document.cookie = [ + encodeURIComponent(key), '=', + options.raw ? value : encodeURIComponent(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // key and possibly options given, get cookie... + options = value || {}; + var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent; + return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null; +}; + +/*! + * jQuery.ScrollTo + * Copyright (c) 2007-2012 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Dual licensed under MIT and GPL. + * Date: 4/09/2012 + * + * @projectDescription Easy element scrolling using jQuery. + * http://flesler.blogspot.com/2007/10/jqueryscrollto.html + * @author Ariel Flesler + * @version 1.4.3.1 + * + * @id jQuery.scrollTo + * @id jQuery.fn.scrollTo + * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements. + * The different options for target are: + * - A number position (will be applied to all axes). + * - A string position ('44', '100px', '+=90', etc ) will be applied to all axes + * - A jQuery/DOM element ( logically, child of the element to scroll ) + * - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc ) + * - A hash { top:x, left:y }, x and y can be any kind of number/string like above. + * - A percentage of the container's dimension/s, for example: 50% to go to the middle. + * - The string 'max' for go-to-end. + * @param {Number, Function} duration The OVERALL length of the animation, this argument can be the settings object instead. + * @param {Object,Function} settings Optional set of settings or the onAfter callback. + * @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'. + * @option {Number, Function} duration The OVERALL length of the animation. + * @option {String} easing The easing method for the animation. + * @option {Boolean} margin If true, the margin of the target element will be deducted from the final position. + * @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }. + * @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes. + * @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends. + * @option {Function} onAfter Function to be called after the scrolling ends. + * @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends. + * @return {jQuery} Returns the same jQuery object, for chaining. + * + * @desc Scroll to a fixed position + * @example $('div').scrollTo( 340 ); + * + * @desc Scroll relatively to the actual position + * @example $('div').scrollTo( '+=340px', { axis:'y' } ); + * + * @desc Scroll using a selector (relative to the scrolled element) + * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } ); + * + * @desc Scroll to a DOM element (same for jQuery object) + * @example var second_child = document.getElementById('container').firstChild.nextSibling; + * $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){ + * alert('scrolled!!'); + * }}); + * + * @desc Scroll on both axes, to different values + * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } ); + */ + +;(function( $ ){ + + var $scrollTo = $.scrollTo = function( target, duration, settings ){ + $(window).scrollTo( target, duration, settings ); + }; + + $scrollTo.defaults = { + axis:'xy', + duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1, + limit:true + }; + + // Returns the element that needs to be animated to scroll the window. + // Kept for backwards compatibility (specially for localScroll & serialScroll) + $scrollTo.window = function( scope ){ + return $(window)._scrollable(); + }; + + // Hack, hack, hack :) + // Returns the real elements to scroll (supports window/iframes, documents and regular nodes) + $.fn._scrollable = function(){ + return this.map(function(){ + var elem = this, + isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1; + + if( !isWin ) + return elem; + + var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem; + + return /webkit/i.test(navigator.userAgent) || doc.compatMode == 'BackCompat' ? + doc.body : + doc.documentElement; + }); + }; + + $.fn.scrollTo = function( target, duration, settings ){ + if( typeof duration == 'object' ){ + settings = duration; + duration = 0; + } + if( typeof settings == 'function' ) + settings = { onAfter:settings }; + + if( target == 'max' ) + target = 9e9; + + settings = $.extend( {}, $scrollTo.defaults, settings ); + // Speed is still recognized for backwards compatibility + duration = duration || settings.duration; + // Make sure the settings are given right + settings.queue = settings.queue && settings.axis.length > 1; + + if( settings.queue ) + // Let's keep the overall duration + duration /= 2; + settings.offset = both( settings.offset ); + settings.over = both( settings.over ); + + return this._scrollable().each(function(){ + // Null target yields nothing, just like jQuery does + if (target == null) return; + + var elem = this, + $elem = $(elem), + targ = target, toff, attr = {}, + win = $elem.is('html,body'); + + switch( typeof targ ){ + // A number will pass the regex + case 'number': + case 'string': + if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){ + targ = both( targ ); + // We are done + break; + } + // Relative selector, no break! + targ = $(targ,this); + if (!targ.length) return; + case 'object': + // DOMElement / jQuery + if( targ.is || targ.style ) + // Get the real position of the target + toff = (targ = $(targ)).offset(); + } + $.each( settings.axis.split(''), function( i, axis ){ + var Pos = axis == 'x' ? 'Left' : 'Top', + pos = Pos.toLowerCase(), + key = 'scroll' + Pos, + old = elem[key], + max = $scrollTo.max(elem, axis); + + if( toff ){// jQuery / DOMElement + attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] ); + + // If it's a dom element, reduce the margin + if( settings.margin ){ + attr[key] -= parseInt(targ.css('margin'+Pos)) || 0; + attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0; + } + + attr[key] += settings.offset[pos] || 0; + + if( settings.over[pos] ) + // Scroll to a fraction of its width/height + attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos]; + }else{ + var val = targ[pos]; + // Handle percentage values + attr[key] = val.slice && val.slice(-1) == '%' ? + parseFloat(val) / 100 * max + : val; + } + + // Number or 'number' + if( settings.limit && /^\d+$/.test(attr[key]) ) + // Check the limits + attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max ); + + // Queueing axes + if( !i && settings.queue ){ + // Don't waste time animating, if there's no need. + if( old != attr[key] ) + // Intermediate animation + animate( settings.onAfterFirst ); + // Don't animate this axis again in the next iteration. + delete attr[key]; + } + }); + + animate( settings.onAfter ); + + function animate( callback ){ + $elem.animate( attr, duration, settings.easing, callback && function(){ + callback.call(this, target, settings); + }); + }; + + }).end(); + }; + + // Max scrolling position, works on quirks mode + // It only fails (not too badly) on IE, quirks mode. + $scrollTo.max = function( elem, axis ){ + var Dim = axis == 'x' ? 'Width' : 'Height', + scroll = 'scroll'+Dim; + + if( !$(elem).is('html,body') ) + return elem[scroll] - $(elem)[Dim.toLowerCase()](); + + var size = 'client' + Dim, + html = elem.ownerDocument.documentElement, + body = elem.ownerDocument.body; + + return Math.max( html[scroll], body[scroll] ) + - Math.min( html[size] , body[size] ); + }; + + function both( val ){ + return typeof val == 'object' ? val : { top:val, left:val }; + }; + +})( jQuery ); + +jQuery.fn.css2 = jQuery.fn.css; +jQuery.fn.css = function() { + if (arguments.length) return jQuery.fn.css2.apply(this, arguments); + var attr = ['font-family','font-size','font-weight','font-style','color', 'box-sizing', + 'text-transform','text-decoration','letter-spacing', 'box-shadow', + 'line-height','text-align','vertical-align','direction','background-color', + 'background-image','background-repeat','background-position', + 'background-attachment','opacity','width','height','top','right','bottom', + 'left','margin-top','margin-right','margin-bottom','margin-left', + 'padding-top','padding-right','padding-bottom','padding-left', + 'border-top-width','border-right-width','border-bottom-width', + 'border-left-width','border-top-color','border-right-color', + 'border-bottom-color','border-left-color','border-top-style', + 'border-right-style','border-bottom-style','border-left-style','position', + 'display','visibility','z-index','overflow-x','overflow-y','white-space', + 'clip','float','clear','cursor','list-style-image','list-style-position', + 'list-style-type','marker-offset']; + var len = attr.length, obj = {}; + for (var i = 0; i < len; i++) + obj[attr[i]] = jQuery.fn.css2.call(this, attr[i]); + return obj; +}; diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/login.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/login.php new file mode 100644 index 0000000..119f674 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/login.php @@ -0,0 +1,54 @@ +hasLogin()) { + $response->redirect($options->adminUrl); +} +$rememberName = htmlspecialchars(Typecho_Cookie::get('__typecho_remember_name')); +Typecho_Cookie::delete('__typecho_remember_name'); + +$bodyClass = 'body-100'; + +include 'header.php'; +?> + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-categories.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-categories.php new file mode 100644 index 0000000..8124eac --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-categories.php @@ -0,0 +1,154 @@ +to($categories); +?> + +
          +
          + +
          + +
          + +
          +
          +
          + +
          + + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + have()): ?> + next()): ?> + + + + + + + + + + + + + + + +
          name(); ?> + + + + children) > 0): ?> + children)); ?> + + + + slug(); ?> + defaultCategory == $categories->mid): ?> + + + + + count(); ?>
          +
          +
          + +
          +
          +
          +
          + + + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-comments.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-comments.php new file mode 100644 index 0000000..9dfeb46 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-comments.php @@ -0,0 +1,373 @@ +get('__typecho_all_comments') || 'on' == Typecho_Cookie::get('__typecho_all_comments')); +?> +
          +
          + +
          +
          + + +
          +
          +
          + +
          + + + get('status')): ?> + + +
          +
          + +
          +
          + +
          +
          + + + + + + + + + + + + + + + + + + have()): ?> + next()): ?> + + + + + + + + + + + + + +
          + + +
          + type): ?> + gravatar(40); ?> + + type): ?> + + +
          +
          +
          + author(true); ?> + mail): ?> +
          mail(); ?> + + ip): ?> +
          ip(); ?> + +
          +
          +
          dateWord(); ?> 于 title(); ?>
          +
          + content(); ?> +
          +
          + status): ?> + + + + + + status): ?> + + + + + + status): ?> + + + + + + + + status && 'comment' == $comments->type): ?> + + + + +
          +
          +
          + + cid)): ?> + + +
          + +
          +
          +
          + +
          + + + get('status')): ?> + + +
          +
          + have()): ?> +
            + pageNav(); ?> +
          + +
          +
          +
          +
          +
          +
          + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-medias.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-medias.php new file mode 100644 index 0000000..1c08f79 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-medias.php @@ -0,0 +1,122 @@ + + +to($attachments); ?> +
          +
          + +
          +
          + +
          +
          +
          + +
          + + + +
          +
          + +
          +
          + +
          +
          + + + + + + + + + + + + + + + + + + + + + have()): ?> + next()): ?> + attachment->mime); ?> + + + + + + + + + + + + + + + +
          commentsNum(); ?> + + title(); ?> + + author(); ?> + parentPost->cid): ?> + parentPost->title(); ?> + + + + dateWord(); ?>
          +
          +
          + +
          +
          +
          + +
          + + +
          + +
          + have()): ?> +
            + pageNav(); ?> +
          + +
          +
          + +
          +
          +
          +
          + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-pages.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-pages.php new file mode 100644 index 0000000..423770f --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-pages.php @@ -0,0 +1,131 @@ + +
          +
          + +
          +
          +
          +
          +
          + +
          + + +
          +
          + + +
          +
          + +
          +
          + + + + + + + + + + + + + + + + + + + + + to($pages); ?> + have()): ?> + next()): ?> + + + + + + + + + + + + + + + +
          commentsNum(); ?> + title(); ?> + hasSaved || 'page_draft' == $pages->type) { + echo '' . _t('草稿') . ''; + } else if ('hidden' == $pages->status) { + echo '' . _t('隐藏') . ''; + } + ?> + type): ?> + + + slug(); ?>author(); ?> + hasSaved): ?> + + modified); ?> + word()); ?> + + + dateWord(); ?> + +
          +
          +
          +
          +
          +
          +
          + + + +status) || 'publish' == $request->get('status')): ?> + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-posts.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-posts.php new file mode 100644 index 0000000..c8f2f4a --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-posts.php @@ -0,0 +1,152 @@ + +
          +
          + +
          +
          +
          +
          +
          + +
          + + +
          +
          + +
          +
          + +
          +
          + + + + + + + + + + + + + + + + + + + + + to($posts); ?> + have()): ?> + next()): ?> + + + + + + + + + + + + + + + +
          commentsNum(); ?> + title(); ?> + hasSaved || 'post_draft' == $posts->type) { + echo '' . _t('草稿') . ''; + } else if ('hidden' == $posts->status) { + echo '' . _t('隐藏') . ''; + } else if ('waiting' == $posts->status) { + echo '' . _t('待审核') . ''; + } else if ('private' == $posts->status) { + echo '' . _t('私密') . ''; + } else if ($posts->password) { + echo '' . _t('密码保护') . ''; + } + ?> + type): ?> + + + author(); ?>categories; $length = count($categories); ?> + $val): ?> + adminUrl('manage-posts.php?category=' . $val['mid'] + . (isset($request->uid) ? '&uid=' . $request->uid : '') + . (isset($request->status) ? '&status=' . $request->status : '')); + echo '">' . $val['name'] . '' . ($key < $length - 1 ? ', ' : ''); ?> + + + hasSaved): ?> + + modified); ?> + word()); ?> + + + dateWord(); ?> + +
          +
          +
          + +
          +
          +
          + +
          + + +
          +
          + + have()): ?> +
            + pageNav(); ?> +
          + +
          +
          +
          +
          +
          +
          + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-tags.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-tags.php new file mode 100644 index 0000000..b0f6ffd --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-tags.php @@ -0,0 +1,91 @@ +to($tags); +?> + +
          +
          + +
          + +
          + +
          +
          +
          + +
          + + +
          +
          +
          + +
            + have()): ?> + next()): ?> +
          • + + name(); ?> + +
          • + + +
            + +
          + +
          + +
          +
          + form()->render(); ?> +
          +
          +
          +
          + + + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-users.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-users.php new file mode 100644 index 0000000..5bfe7bf --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/manage-users.php @@ -0,0 +1,118 @@ + +
          +
          + +
          +
          +
          +
          +
          + +
          + + +
          +
          + +
          +
          + +
          +
          + + + + + + + + + + + + + + + + + + + + + to($users); ?> + next()): ?> + + + + + + + + + + +
          postsNum(); ?>name(); ?> + + screenName(); ?>mail): ?>mail(); ?>group) { + case 'administrator': + _e('管理员'); + break; + case 'editor': + _e('编辑'); + break; + case 'contributor': + _e('贡献者'); + break; + case 'subscriber': + _e('关注者'); + break; + case 'visitor': + _e('访问者'); + break; + default: + break; + } ?>
          +
          +
          + +
          +
          +
          + +
          + + +
          +
          + have()): ?> +
            + pageNav(); ?> +
          + +
          +
          +
          +
          +
          +
          + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/media.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/media.php new file mode 100644 index 0000000..34b6de8 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/media.php @@ -0,0 +1,194 @@ +to($attachment); +?> + +
          +
          + +
          +
          + attachment->isImage): ?> +

          <?php $attachment->attachment->name(); ?>

          + + +

          + attachment->mime); ?> + + attachment->name(); ?> + attachment->size / 1024)); ?> Kb +

          + +

          + +

          + +
          +
          或者 %s选择文件上传%s', '', ''); ?>
          +
            +
            +
            +
            + form()->render(); ?> +
            +
            +
            +
            + + + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/menu.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/menu.php new file mode 100644 index 0000000..88376da --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/menu.php @@ -0,0 +1,15 @@ + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-discussion.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-discussion.php new file mode 100644 index 0000000..5468985 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-discussion.php @@ -0,0 +1,23 @@ + + +
            +
            + +
            +
            + form()->render(); ?> +
            +
            +
            +
            + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-general.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-general.php new file mode 100644 index 0000000..062018a --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-general.php @@ -0,0 +1,23 @@ + + +
            +
            + +
            +
            + form()->render(); ?> +
            +
            +
            +
            + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-permalink.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-permalink.php new file mode 100644 index 0000000..b715d6d --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-permalink.php @@ -0,0 +1,24 @@ + + +
            +
            + +
            +
            + form()->render(); ?> +
            +
            +
            +
            + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-plugin.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-plugin.php new file mode 100644 index 0000000..f74c249 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-plugin.php @@ -0,0 +1,23 @@ + + +
            +
            + +
            +
            + config()->render(); ?> +
            +
            +
            +
            + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-reading.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-reading.php new file mode 100644 index 0000000..6c871bd --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-reading.php @@ -0,0 +1,37 @@ + + +
            +
            + +
            +
            + form()->render(); ?> +
            +
            +
            +
            + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-theme.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-theme.php new file mode 100644 index 0000000..d5fdb46 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/options-theme.php @@ -0,0 +1,32 @@ + + +
            +
            + +
            +
            +
              +
            • + +
            • + +
            • +
            +
            +
            + config()->render(); ?> +
            +
            +
            +
            + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/page-title.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/page-title.php new file mode 100644 index 0000000..af25b8d --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/page-title.php @@ -0,0 +1,8 @@ + +
            +

            title; ?>addLink)) { + echo "addLink}\">" . _t("新增") . ""; + } + ?>

            +
            diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/plugins.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/plugins.php new file mode 100644 index 0000000..e77d203 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/plugins.php @@ -0,0 +1,127 @@ + +
            +
            + +
            +
            + to($activatedPlugins); ?> + have() || !empty($activatedPlugins->activatedPlugins)): ?> +

            +
            + + + + + + + + + + + + + + + + + + + next()): ?> + + + + + + + + + + activatedPlugins)): ?> + activatedPlugins as $key => $val): ?> + + + + + + + + + +
            title(); ?> + dependence): ?> + <?php _e('%s 无法在此版本的typecho下正常工作', $activatedPlugins->title); ?> + + description(); ?>version(); ?>homepage) ? $activatedPlugins->author : '' . $activatedPlugins->author . ''; ?> + activate || $activatedPlugins->deactivate || $activatedPlugins->config || $activatedPlugins->personalConfig): ?> + config): ?> + + • + + + + + +
            +
            + + + to($deactivatedPlugins); ?> + have() || !$activatedPlugins->have()): ?> +

            +
            + + + + + + + + + + + + + + + + + + + have()): ?> + next()): ?> + + + + + + + + + + + + + + +
            title(); ?>description(); ?>version(); ?>homepage) ? $deactivatedPlugins->author : '' . $deactivatedPlugins->author . ''; ?> + +
            +
            + + +
            +
            +
            +
            + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/profile.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/profile.php new file mode 100644 index 0000000..d65eb03 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/profile.php @@ -0,0 +1,58 @@ + + +
            +
            + +
            +
            +

            mail, 220, 'X', 'mm', $request->isSecure()) . '" alt="' . $user->screenName . '" />'; ?>

            +

            screenName(); ?>

            +

            name(); ?>

            +

            %s 篇日志, 并有 %s 条关于你的评论在 %s 个分类中.', + $stat->myPublishedPostsNum, $stat->myPublishedCommentsNum, $stat->categoriesNum); ?>

            +

            logged > 0) { + _e('最后登录: %s', Typecho_I18n::dateWord($user->logged + $options->timezone, $options->gmtTime + $options->timezone)); + } + ?>

            +
            + +
            +
            +

            + profileForm()->render(); ?> +
            + + pass('contributor', true)): ?> +
            +
            +

            + optionsForm()->render(); ?> +
            + + +
            + +
            +

            + personalFormList(); ?> + passwordForm()->render(); ?> +
            +
            +
            +
            +
            + +bottom(); +include 'footer.php'; +?> diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/register.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/register.php new file mode 100644 index 0000000..5c8a11c --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/register.php @@ -0,0 +1,50 @@ +hasLogin() || !$options->allowRegister) { + $response->redirect($options->siteUrl); +} +$rememberName = htmlspecialchars(Typecho_Cookie::get('__typecho_remember_name')); +$rememberMail = htmlspecialchars(Typecho_Cookie::get('__typecho_remember_mail')); +Typecho_Cookie::delete('__typecho_remember_name'); +Typecho_Cookie::delete('__typecho_remember_mail'); + +$bodyClass = 'body-100'; + +include 'header.php'; +?> + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_buttons.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_buttons.scss new file mode 100644 index 0000000..3c7292f --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_buttons.scss @@ -0,0 +1,113 @@ +@import "compass"; + +/** +* Buttons +*/ + +@mixin button($color) { + border: none; + background-color: $color; + cursor: pointer; + + @include border-radius(2px); + // @include transition-property(background-color); + + &:hover { + @include transition-duration(.4s); + background-color: darken($color, 6%); + } + &:active, &.active { + background-color: darken($color, 8%); + } + &:disabled { + background-color: lighten($color, 6%); + cursor: default; + } +} + +.btn { + @include button(#E9E9E6); + + display: inline-block; + padding: 0 12px; + height: 32px; + color: #666; + vertical-align: middle; + zoom: 1; + + &:disabled { + color: #999; + } +} + +.btn-xs { + padding: 0 10px; + height: 25px; + font-size: 13px; +} +.btn-s { height: 28px; } +.btn-l { + height: 40px; + font-size: 1.14286em; + font-weight: bold; +} + +.primary { + @include button(#467B96); + color: #FFF; +} + +.btn-group { + display: inline-block; +} + +.btn-warn { + @include button(#B94A48); + color: #FFF; +} + +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active, +.btn-link.active { + background-color: transparent; +} + +/* 下拉菜单 */ +.btn-drop { + position: relative; +} +.dropdown-toggle { + padding-right: 8px; +} +.dropdown-menu { + list-style: none; + position: absolute; + z-index: 2; + left: 0; + margin: 0; + padding: 0; + border: 1px solid #D9D9D6; + background: #FFF; + text-align: left; + min-width: 108px; + display: none; + + li { + white-space: nowrap; + &.multiline { + padding: 5px 12px 12px; + } + } + + a { + display: block; + padding: 5px 12px; + color: #666; + &:hover { + background: #F6F6F3; + text-decoration: none !important; + } + } +} diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_components.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_components.scss new file mode 100644 index 0000000..d27c1d8 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_components.scss @@ -0,0 +1,3 @@ +@import "components/editor"; +@import "components/timepicker"; +@import "components/tokeninput"; \ No newline at end of file diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_footer.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_footer.scss new file mode 100644 index 0000000..9e31a9a --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_footer.scss @@ -0,0 +1,20 @@ +/** +* 注脚 +*/ +.typecho-foot { + padding: 4em 0 3em; + color: #999; + line-height: 1.8; + text-align: center; + + .copyright p { + margin: 10px 0 0; + } + .resource { + color: #CCC; + } + .resource a { + margin: 0 3px; + color: #999; + } +} diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_forms.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_forms.scss new file mode 100644 index 0000000..efdd524 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_forms.scss @@ -0,0 +1,47 @@ +/** +* Forms +*/ + +input[type=text], input[type=password], input[type=email], +textarea { + background: #FFF; + border: 1px solid #D9D9D6; + padding: 7px; + + border-radius: 2px; + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +textarea { + resize: vertical; + line-height: 1.5; +} + +input[type="radio"], input[type="checkbox"] { margin-right: 3px; } + +input, textarea { + &.text-s { padding: 5px; } + &.text-l { + padding: 10px; + font-size: 1.14286em; + } +} + +.w-10 { width: 10%; } +.w-20 { width: 20%; } +.w-30 { width: 30%; } +.w-40 { width: 40%;} +.w-50 { width: 50%;} +.w-60 { width: 60%;} +.w-70 { width: 70%;} +.w-80 { width: 80%;} +.w-90 { width: 90%;} +.w-100 { width: 100%;} + +select { + border: 1px solid #CCC; + height: 28px; +} \ No newline at end of file diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_header.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_header.scss new file mode 100644 index 0000000..d72380c --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_header.scss @@ -0,0 +1,109 @@ +/** +* 后台头部导航 +*/ +.typecho-head-nav { + padding: 0 10px; + background: #292D33; +} + +.typecho-head-nav a { + color: #BBB; +} +.typecho-head-nav a:hover, +.typecho-head-nav a:focus { + color: #FFF; + text-decoration: none; +} + +#typecho-nav-list { + float: left; + ul { + list-style: none; + margin: 0; + padding: 0; + } +} + +#typecho-nav-list ul:first-child { + border-left: 1px solid #383D45; +} + +#typecho-nav-list .root { + position: relative; + float: left; +} + +#typecho-nav-list .parent a { + display: block; + float: left; + padding: 0 20px; + border-right: 1px solid #383D45; + height: 36px; + line-height: 36px; + color: #BBB; +} + +#typecho-nav-list .parent a:hover, +#typecho-nav-list .focus .parent a, +#typecho-nav-list .root:hover .parent a { + background: #202328; + color: #FFF; + text-decoration: none; +} + +#typecho-nav-list .focus .parent a { + font-weight: bold; +} + +#typecho-nav-list .child { + position: absolute; + top: 36px; + display: none; + margin: 0; + min-width: 160px; + max-width: 240px; + background: #202328; + z-index: 250; +} + +#typecho-nav-list .root:hover .child { + display: block; +} + +#typecho-nav-list .child li a { + color: #BBB; + display: block; + padding: 0 20px; + overflow: hidden; + text-overflow : ellipsis; + white-space: nowrap; + height: 36px; + line-height: 36px; +} + +#typecho-nav-list .child li a:hover, +#typecho-nav-list .child li a:focus { + background: #292D33; + color: #FFF; +} +#typecho-nav-list .child li.focus a { + color: #6DA1BB; + font-weight: bold; +} + +.typecho-head-nav .operate { + float: right; +} +.typecho-head-nav .operate a { + display: inline-block; + margin-left: -1px; + padding: 0 20px; + border: 1px solid #383D45; + border-width: 0 1px; + line-height: 36px; + color: #BBB; +} +.typecho-head-nav .operate a:hover { + background-color: #202328; + color: #FFF; +} \ No newline at end of file diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_hidden.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_hidden.scss new file mode 100644 index 0000000..0f5ac4a --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_hidden.scss @@ -0,0 +1,46 @@ +/* +* Hide from both screenreaders and browsers: h5bp.com/u +*/ + +.hidden { + display: none; + // display: none !important; + // visibility: hidden; +} + +/* +* Hide only visually, but have it available for screenreaders: h5bp.com/v +*/ + +.sr-only { + border: 0; + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +/* +* Extends the .sr-only class to allow the element to be focusable +* when navigated to via the keyboard: h5bp.com/p +*/ + +.sr-only.focusable:active, +.sr-only.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; +} + +/* +* Hide visually and from screenreaders, but maintain layout +*/ + +.invisible { + visibility: hidden; +} \ No newline at end of file diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_icons.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_icons.scss new file mode 100644 index 0000000..1992149 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_icons.scss @@ -0,0 +1,178 @@ +/** + * icons + */ +$sprites: sprite-map("icons/*.png"); +$sprites-retina: sprite-map("icons-2x/*.png"); + +@mixin sprite-background($name) { + // background-image: sprite-url($sprites); + background-position: sprite-position($sprites, $name); + // background-repeat: no-repeat; + // display: block; + // height: image-height(sprite-file($sprites, $name)); + // width: image-width(sprite-file($sprites, $name)); + @media + (-webkit-min-device-pixel-ratio: 2), + (min-resolution: 192dpi) { + // Workaround for https://gist.github.com/2140082 + @if (sprite-position($sprites, $name) != sprite-position($sprites-retina, $name)) { + $ypos: round(nth(sprite-position($sprites-retina, $name), 2) / 2); + background-position: 0 $ypos; + } + // Hard coded width of the normal sprite image. There must be a smarter way to do this. + // @include background-size(auto 256px); + // background-image: sprite-url($sprites-retina); + } +} + + +%i-base { + display: inline-block; + vertical-align: text-bottom; + text-indent: -9999em; + background-image: sprite-url($sprites); + background-repeat: no-repeat; + &:hover { + @include opacity(0.75); + } + @media + (-webkit-min-device-pixel-ratio: 2), + (min-resolution: 192dpi) { + // Hard coded width of the normal sprite image. There must be a smarter way to do this. + @include background-size(auto 256px); + background-image: sprite-url($sprites-retina); + } +} + +%i-16 { + @extend %i-base; + width: 16px; + height: 16px; +} + +%i-24 { + @extend %i-base; + width: 24px; + height: 24px; +} + +.i-edit { + @extend %i-16; + @include sprite-background(icon-edit); +} +.i-delete { + @extend %i-16; + @include sprite-background(icon-delete); +} + + +// 大号上传按钮 + +.i-upload { + @extend %i-24; + @include sprite-background(icon-upload); +} + +.i-upload-active { + @extend %i-24; + @include sprite-background(icon-upload-active); +} + +// 小箭头 +.i-caret-up, .i-caret-down, .i-caret-left, .i-caret-right { + display: inline-block; + border-style: solid; + border-color: transparent transparent #BBB transparent; + border-width: 3px 4px 5px; +} +.i-caret-down { + border-color: #BBB transparent transparent transparent; + border-width: 5px 4px 3px; +} +.i-caret-left { + border-color: transparent #BBB transparent transparent; + border-width: 4px 5px 4px 3px; +} +.i-caret-right { + border-color: transparent transparent transparent #BBB; + border-width: 4px 3px 4px 5px; +} + +.i-exlink { + @extend %i-16; + @include sprite-background(icon-exlink); +} + + +/* 文件类型图标 */ + +.mime-office { + @extend %i-16; + @include sprite-background(mime-office); +} + +.mime-text { + @extend %i-16; + @include sprite-background(mime-text); +} + +.mime-image { + @extend %i-16; + @include sprite-background(mime-image); +} + +.mime-html { + @extend %i-16; + @include sprite-background(mime-html); +} + +.mime-archive { + @extend %i-16; + @include sprite-background(mime-archive); +} + +.mime-application { + @extend %i-16; + @include sprite-background(mime-application); +} + +.mime-audio { + @extend %i-16; + @include sprite-background(mime-audio); +} + +.mime-script { + @extend %i-16; + @include sprite-background(mime-script); +} + +.mime-video { + @extend %i-16; + @include sprite-background(mime-video); +} + +.mime-unknow { + @extend %i-16; + @include sprite-background(mime-unknow); +} + + +/* Logo 图标 */ +.i-logo, .i-logo-s { + width: 169px; + height: 40px; + display: inline-block; + background: url("../img/typecho-logo.svg") no-repeat; + text-indent: -9999em; + @include background-size(auto 40px); + @include opacity(.15); + &:hover { + @include opacity(.2); + } +} +.i-logo-s { + width: 26px; + height: 26px; + @include background-size(auto 26px); +} + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_messages.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_messages.scss new file mode 100644 index 0000000..c241a21 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_messages.scss @@ -0,0 +1,51 @@ +@import "compass"; + +/** +* 提示信息框 +*/ +.message { + padding: 8px 10px; + @include border-radius(2px); +} + +.message a { + font-weight: bold; + text-decoration: underline; +} + +.error { + background: #FBE3E4; + color: #8A1F11; +} +.error a { color: #8A1F11; } + +.notice { + background: #FFF6BF; + color: #8A6D3B; +} +.notice a { color: #8A6D3B; } + +.success { + background: #E6EFC2; + color: #264409; +} +.success a { color: #264409; } + + +// 气泡 +.balloon { + display: inline-block; + padding: 0 4px; + min-width: 10px; + height: 14px; + line-height: 14px; + background: #B9B9B6; + vertical-align: text-top; + text-align: center; + font-size: 12px; + color: #FFF; + + @include border-radius(20px); + +} + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_pagenavi.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_pagenavi.scss new file mode 100644 index 0000000..30e7589 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/_pagenavi.scss @@ -0,0 +1,36 @@ +/** +* 后台分页 +*/ + +.typecho-pager { + list-style: none; + float: right; + margin: 0; + padding: 0; + line-height: 1; + text-align: center; + zoom: 1; +} + +.typecho-pager li { + display: inline-block; + margin: 0 3px; + height: 28px; + line-height: 28px; +} + +.typecho-pager a { + display: block; + padding: 0 10px; + @include border-radius(2px); +} + +.typecho-pager a:hover { + text-decoration: none; + background: #E9E9E6; +} + +.typecho-pager li.current a { + background: #E9E9E6; + color: #444; +} \ No newline at end of file diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_editor.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_editor.scss new file mode 100644 index 0000000..3d2ce29 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_editor.scss @@ -0,0 +1,248 @@ +/* +* Editor +*/ +.editor { + margin-bottom: -0.5em; +} + +.wmd-button-row { + list-style: none; + margin: 0; + padding: 0; + height: 26px; + line-height: 1; + + li { + display: inline-block; + margin-right: 4px; + padding: 3px; + cursor: pointer; + vertical-align: middle; + @include border-radius(2px); + &:hover { + background-color: #E9E9E6; + } + &.wmd-spacer { + height: 20px; + margin: 0 10px 0 6px; + padding: 0; + width: 1px; + background: #E9E9E6; + cursor: default; + } + } +} + +#wmd-button-row span { + display: block; + width: 20px; + height: 20px; + background: transparent url(../img/editor.png) no-repeat; +} + +@media +(-webkit-min-device-pixel-ratio: 2), +(min-resolution: 192dpi) { + #wmd-button-row span { + background-image: url(../img/editor@2x.png); + @include background-size(320px auto); + } +} + +// 撰写预览切换 tab +.wmd-edittab { + float: right; + margin-top: 3px; + font-size: .92857em; + a { + display: inline-block; + padding: 0 8px; + margin-left: 5px; + height: 20px; + line-height: 20px; + &:hover { + text-decoration: none; + } + &.active { + background: #E9E9E6; + color: #999; + } + } +} + +// 控制被隐藏的 tab,全屏时显示 +.wmd-hidetab { + display: none; +} +// 隐藏编辑器,但占位 +.wmd-visualhide { + visibility: hidden; +} + +/* 对话框 */ +.wmd-prompt-background { + background-color: #000; +} +.wmd-prompt-dialog { + position: fixed; + z-index: 1001; + top: 50%; + left: 50%; + margin-top: -95px; + margin-left: -200px; + padding: 20px; + width: 360px; + background: #F6F6F3; + + p { margin: 0 0 5px; } + form { margin-top: 10px; } + input[type="text"] { + margin-bottom: 10px; + width: 100%; + } + button { margin-right: 10px; } +} + +/* 预览 */ +#wmd-preview { + background: #FFF; + margin: 1em 0; + padding: 0 15px; + word-wrap: break-word; + overflow: auto; + @include border-radius(2px); + img { max-width: 100%; } + code, pre { + padding: 2px 4px; + background: #F3F3F0; + font-size: .92857em; + } + code { color: #C13; } + pre { + padding: 1em; + code { + padding: 0; + color: #444; + } + } + blockquote { + margin: 1em 1.5em; + padding-left: 1.5em; + border-left: 4px solid #E9E9E6; + color: #777; + } + hr { + margin: 2em auto; + width: 100px; + border: 1px solid #E9E9E6; + border-width: 2px 0 0 0; + } + .summary:after { + display: block; + margin: 2em 0; + background: #FFF9E8; + color: darken(#FFF9E8, 55%); + font-size: .85714em; + text-align: center; + content: "- more -"; + } +} + +/* 上传面板动画效果 */ +@keyframes fullscreen-upload { + 0% { right: -280px; } + 100% { right: -1px; } +} + +@-moz-keyframes fullscreen-upload { + 0% { right: -280px; } + 100% { right: -1px; } +} + +@-webkit-keyframes fullscreen-upload { + 0% { right: -280px; } + 100% { right: -1px; } +} + +@-o-keyframes fullscreen-upload { + 0% { right: -280px; } + 100% { right: -1px; } +} + +/* 编辑器全屏 */ +.fullscreen { + #wmd-button-bar, #text, #wmd-preview, .submit { + // position: fixed; + position: absolute; + top: 0; + width: 50%; + background: #FFF; + z-index: 999; + @include box-sizing(border-box); + @include border-radius(0); + } + #wmd-button-bar { + left: 0; + padding: 13px 20px; + border-bottom: 1px solid #F3F3F0; + z-index: 1000; + } + #text { + top: 53px; + left: 0; + padding: 20px; + border: none; + outline: none; + } + #wmd-preview { + top: 53px; + right: 0; + margin: 0; + padding: 5px 20px; + border: none; + border-left: 1px solid #F3F3F0; + background: #F6F6F3; + overflow: auto; + code, pre { + background: #F0F0EC; + } + } + .submit { + right: 0; + margin: 0; + padding: 10px 20px; + border-bottom: 1px solid #F3F3F0; + } + #upload-panel { + -webkit-box-shadow: 0 4px 16px rgba(0,0,0,0.225); + box-shadow: 0 4px 16px rgba(0,0,0,0.225); + border-style: solid; + } + + // 全屏附件上传 + #tab-files { + position: absolute; + top: 52px; + right: -1px; + width: 280px; + z-index: 1001; + + animation: fullscreen-upload 0.5s; + -moz-animation: fullscreen-upload 0.5s; + -webkit-animation: fullscreen-upload 0.5s; + -o-animation: fullscreen-upload 0.5s; + } + + .wmd-edittab, + .typecho-post-option, + .title, + .url-slug, + .typecho-page-title, + .typecho-head-nav, + .message { display: none; } + .wmd-hidetab { display: block; } + .wmd-visualhide, + #btn-fullscreen-upload { + visibility: visible; + } +} diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_retina.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_retina.scss new file mode 100644 index 0000000..76f848e --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_retina.scss @@ -0,0 +1,12 @@ +@import "compass/css3/background-size"; + +@mixin image-2x($image, $width, $height) { + @media (-webkit-min-device-pixel-ratio: 1.3), + (min-resolution: 124.8dpi), + (min-resolution: 1.3dppx) { + & { + background-image: url($image); + @include background-size($width $height); + } + } +} diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_timepicker.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_timepicker.scss new file mode 100644 index 0000000..3e088a6 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_timepicker.scss @@ -0,0 +1,84 @@ +/** +* Jquery Timepicker +*/ + +#ui-datepicker-div { + display: none; + margin-top: -1px; + padding: 10px; + border: 1px solid #D9D9D6; + background: #FFF; +} +.ui-timepicker-div .ui-widget-header { margin-bottom: 8px; } +.ui-timepicker-div dl { text-align: left; } +.ui-timepicker-div dl dt { float: left; clear:left; } +.ui-timepicker-div dl dd { margin: 0 0 10px 40%; } +.ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; } + +#ui-datepicker-div .ui-datepicker-header { + margin-bottom: 10px; + padding-bottom: 10px; + border-bottom: 1px solid #EEE; +} +#ui-datepicker-div .ui-datepicker-prev { float: left; cursor: pointer; } +#ui-datepicker-div .ui-datepicker-next { float: right; cursor: pointer; } +#ui-datepicker-div .ui-datepicker-title { + font-weight: bold; + text-align: center; +} +#ui-datepicker-div .ui-datepicker-calendar th { line-height: 24px; } +#ui-datepicker-div .ui-datepicker-calendar a { + display: block; + width: 30px; + background-color: #F3F3F0; + line-height: 24px; + text-align: center; +} +#ui-datepicker-div .ui-datepicker-calendar a:hover { + background-color: #E9E9E6; + text-decoration: none; +} +#ui-datepicker-div .ui-datepicker-today a { + background-color: #E9E9E6; + color: #444; +} +#ui-datepicker-div .ui-datepicker-current-day a { + background-color: #467B96 !important; + color: #FFF; +} +#ui-datepicker-div .ui-timepicker-div { + margin-top: 20px; + border-top: 1px solid #EEE; +} +#ui-datepicker-div .ui-slider { + position: relative; + margin-top: 18px; + border: 1px solid #E9E9E6; + background-color: #F6F6F3; + height: 4px; +} +#ui-datepicker-div .ui-slider .ui-slider-handle { + position: absolute; + top: -7px; + margin-left: -5px; + z-index: 2; + width: 10px; + height: 16px; + background-color: #467B96; +} + +#ui-datepicker-div .ui-datepicker-buttonpane { + padding-top: 10px; + border-top: 1px solid #EEE; +} +#ui-datepicker-div .ui-datepicker-current, +#ui-datepicker-div .ui-datepicker-close { + float: left; + @extend .btn; + @extend .btn-xs; +} +#ui-datepicker-div .ui-datepicker-close { + float: right; +} + +.ui-effects-transfer { border: 2px dotted #ccc; } \ No newline at end of file diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_tokeninput.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_tokeninput.scss new file mode 100644 index 0000000..b33c5dc --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/components/_tokeninput.scss @@ -0,0 +1,106 @@ +/** +* Jquery Tokeninput +*/ + +ul.token-input-list { + list-style: none; + margin: 0; + padding: 0 4px; + min-height: 32px; + border: 1px solid #D9D9D6; + cursor: text; + z-index: 999; + background-color: #FFF; + clear: left; + + border-radius: 2px; + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + + li { + margin: 4px 0; + } +} + + +ul.token-input-list li input { + padding: 0; + border: 0; + width: 100%; + -webkit-appearance: caret; +} + +li.token-input-token { + padding: 0 6px; + height: 27px; + line-height: 27px; + background-color: #F3F3F0; + cursor: default; + font-size: .92857em; + text-align: right; + white-space: nowrap; + p { + float: left; + display: inline; + margin: 0; + } + span { + color: #BBB; + font-weight: bold; + cursor: pointer; + } +} + + + +li.token-input-selected-token { + background-color: #E9E9E6; +} + +li.token-input-input-token { + padding: 0 4px; +} + +div.token-input-dropdown { + position: absolute; + background-color: #FFF; + overflow: hidden; + border: 1px solid #D9D9D6; + border-top-width: 0; + cursor: default; + z-index: 1; + font-size: .92857em; +} + +div.token-input-dropdown p { + margin: 0; + padding: 5px 10px; + color: #777; + font-weight: bold; +} + +div.token-input-dropdown ul { + list-style: none; + margin: 0; + padding: 0; +} + +div.token-input-dropdown ul li { + padding: 4px 10px; + background-color: #FFF; +} + +div.token-input-dropdown ul li.token-input-dropdown-item { + background-color: #FFF; +} + +div.token-input-dropdown ul li em { + font-style: normal; +} + +div.token-input-dropdown ul li.token-input-selected-dropdown-item { + background-color: #467B96; + color: #FFF; +} \ No newline at end of file diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/grid.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/grid.scss new file mode 100644 index 0000000..a9363ba --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/grid.scss @@ -0,0 +1,226 @@ +/* + * Bento Grid System + * Source: https://github.com/fenbox/bento + * Version: 1.2.8 + * Update: 2013.11.25 + */ + +// +// Config +// +$columns: 12; +$column-width: 100% / $columns; +$gutter-width: 20px; + + +// Break point +$screen-tablet: 768px; +$screen-desktop: 992px; +$screen-wide: 1200px; + + +// Mixins +%box-sizing { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + + +// Container +.container { + margin-left: auto; + margin-right: auto; + padding-left: $gutter-width / 2; + padding-right: $gutter-width / 2; + @extend %box-sizing; +} + +// Column group +.row { + margin-right: $gutter-width / -2; + margin-left: $gutter-width / -2; + @extend .clearfix; +} + +.row [class*="col-"] { + float: left; + min-height: 1px; + padding-right: $gutter-width / 2; + padding-left: $gutter-width / 2; + @extend %box-sizing; +} + +.row [class*="-push-"], +.row [class*="-pull-"] { + position: relative; +} + + +/* + * Mobile and up + */ + +@for $index from 1 through $columns { + .col-mb-#{$index} { + width: $column-width * $index; + } +} + + +/* + * Tablet and up + */ + +@media (min-width: $screen-tablet) { + .container { + max-width: $screen-tablet - ($gutter-width * 2); + } + + // Colunms + @for $index from 1 through $columns { + .col-tb-#{$index} { + width: $column-width * $index; + } + } + + // Offset + @for $index from 0 through $columns { + .col-tb-offset-#{$index} { + margin-left: $column-width * $index; + } + } + + // Pull + @for $index from 0 through $columns { + .col-tb-pull-#{$index} { + right: $column-width * $index; + } + } + + // Push + @for $index from 0 through $columns { + .col-tb-push-#{$index} { + left: $column-width * $index; + } + } +} + + +/* + * Desktop and up + */ + +@media (min-width: $screen-desktop) { + .container { + max-width: $screen-desktop - ($gutter-width * 2); + } + + // Colunms + @for $index from 1 through $columns { + .col-#{$index} { + width: $column-width * $index; + } + } + + // Offset + @for $index from 0 through $columns { + .col-offset-#{$index} { + margin-left: $column-width * $index; + } + } + + // Pull + @for $index from 0 through $columns { + .col-pull-#{$index} { + right: $column-width * $index; + } + } + + // Push + @for $index from 0 through $columns { + .col-push-#{$index} { + left: $column-width * $index; + } + } +} + + +/* + * Widescreen and up + */ + +@media (min-width: $screen-wide) { + .container { + max-width: $screen-wide - ($gutter-width * 2); + } + + // Colunms + @for $index from 1 through $columns { + .col-wd-#{$index} { + width: $column-width * $index; + } + } + + // Offset + @for $index from 0 through $columns { + .col-wd-offset-#{$index} { + margin-left: $column-width * $index; + } + } + + // Pull + @for $index from 0 through $columns { + .col-wd-pull-#{$index} { + right: $column-width * $index; + } + } + + // Push + @for $index from 0 through $columns { + .col-wd-push-#{$index} { + left: $column-width * $index; + } + } +} + + +/* + * Responsive kit + */ + +// Hidden in mobile and down +@media (max-width: $screen-tablet - 1px) { + .kit-hidden-mb { + display: none; + } +} + +// Hidden in tablet and down +@media (max-width: $screen-desktop - 1px) { + .kit-hidden-tb { + display: none; + } +} + +// Hidden in descktop and down +@media (max-width: $screen-wide - 1px) { + .kit-hidden { + display: none; + } +} + + +/* + * Clearfix + */ +.clearfix { + zoom: 1; + &:before, &:after { + content: " "; + display: table; + } + &:after { + clear: both; + } +} diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/style.scss b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/style.scss new file mode 100644 index 0000000..254cee5 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/scss/style.scss @@ -0,0 +1,947 @@ +/* vim: set et sw=2 ts=2 sts=2 fdm=marker ff=unix fenc=utf8 */ +/** +* Typecho 后台样式 +* +* @author Typecho Team +* @since 2008-09-26 +* @update 2013-11-02 +* @link http://www.typecho.org/ +* @version 0.9 +*/ + +@import "compass"; + +/** +* Typecho 全局样式 +*/ + +html { + height: 100%; +} + +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + background: #F6F6F3; + color: #444; + font-size: 87.5%; + line-height: 1.5; +} + +a { + color: #467B96; + text-decoration: none; + &:hover { + color: #499BC3; + text-decoration: underline; + } +} + +code, pre, .mono { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} + +.p { margin: 1em 0; } + +.body-100 { + height: 100%; +} + +a.balloon-button { + display: inline-block; + padding: 0 6px; + min-width: 12px; + height: 18px; + line-height: 18px; + background: #D8E7EE; + font-size: .85714em; + text-align: center; + text-decoration: none; + + /** 修正ie中文不对齐 */ + zoom:1; + + -moz-border-radius: 30px; + -webkit-border-radius: 30px; + border-radius: 30px; +} + +a.button:hover, a.balloon-button:hover { + background-color: #A5CADC; + color: #FFF; + text-decoration: none; +} + + +@import "forms"; +@import "buttons"; +@import "messages"; +@import "pagenavi"; + +@import "header"; +@import "footer"; + +/* 低版本浏览器升级提示 */ +.browsehappy { + border: none; + text-align: center; +} + +/** 顶部消息样式 by 70 */ +.popup { + display: none; + position: absolute; + top: 0; + left: 0; + margin: 0; + padding: 8px 0; + border: none; + width: 100%; + z-index: 10; + text-align: center; + + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; +} + +.popup ul { + list-style: none; + margin: 0; + padding: 0; + text-align: center; +} + +.popup ul li { + display: inline-block; + margin-right: 10px; +} + +/** +* logo 的样式 +*/ +.logo { +} + +/** +* 载入状态 +*/ +.loading { + padding-left: 20px !important; + background: transparent url(../img/ajax-loader.gif) no-repeat left center; +} + + +/** +* 典型配置选项 +*/ +.typecho-option { + list-style: none; + margin: 1em 0; + padding: 0; +} + +.typecho-option li { +} + +.typecho-option-submit li { + border-bottom: none; +} + +.typecho-option label.typecho-label { + display: block; + margin-bottom: .5em; + font-weight: bold; +} +.typecho-option label.required:after { + content: " *"; + color: #B94A48; +} +.typecho-option label.typecho-label+input { +} +.typecho-option span { margin-right: 15px; } +.typecho-option .description { + margin: .5em 0 0; + color: #999; + font-size: .92857em; +} + +.front-archive { + padding-left: 1.5em; +} + +.profile-avatar { + border: 1px dashed #D9D9D6; + max-width: 100%; +} + +/** 增加配置面板内部的错误样式 by 70 */ + + +/** +* 安装样式 +* +* @author mingcheng +* @date 2008-09-06 +*/ + +/** +* 安装向导 +*/ +.typecho-install { + padding-bottom: 2em; +} +.typecho-install-patch { + margin-bottom: 2em; + padding: 2em 0; + background-color: #292D33; + color: #FFF; + text-align: center; +} + +.typecho-install-patch ol { + list-style: none; + margin: 3em 0 1em; + padding: 0; + color: #999; +} +.typecho-install-patch li { + display: inline-block; + margin: 0 .8em; +} +.typecho-install-patch span { + display: inline-block; + margin-right: 5px; + width: 20px; + height: 20px; + line-height: 20px; + border: 2px solid #999; + text-align: center; + border-radius: 2em; +} +.typecho-install-patch li.current { + color: #FFF; + font-weight: bold; +} +.typecho-install-patch li.current span { + border-color: #FFF; +} + + +/** +* 安装主体内容 +*/ + +.typecho-install .typecho-install-body input { + width: 100%; +} +.typecho-install-body .typecho-option li { + margin: 1em 0; +} + + +/** +* 欢迎界面 +*/ + +#typecho-welcome { + margin: 1em 0; + padding: 1em 2em; + background-color: #E9E9E6; +} + +.welcome-board { + color: #999; + font-size: 1.15em; + em { + color: #444; + font-size: 2em; + font-style: normal; + font-family: Georgia, serif; + } +} + +#start-link { + margin-bottom: 25px; + padding: 0 0 35px; + border-bottom: 1px solid #ECECEC; + li { + float: left; + margin-right: 1.5em; + } + .balloon { + margin-top: 2px; + } +} + +.latest-link { + li { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + span { + display: inline-block; + margin-right: 4px; + padding-right: 8px; + border-right: 1px solid #ECECEC; + width: 37px; + text-align: right; + color: #999; + } +} + +.update-check { + font-size: 14px; +} + +/** +* 登录框 +*/ +.typecho-login-wrap { + display: table; + margin: 0 auto; + height: 100%; +} +.typecho-login { + display: table-cell; + padding: 30px 0 100px; + width: 280px; + text-align: center; + vertical-align: middle; + h1 { + margin: 0 0 1em; + } +} + +.typecho-login .more-link { + margin-top: 2em; + color: #CCC; +} +.typecho-login .more-link a { margin: 0 3px; } + +/** +* 标题 +*/ +.typecho-page-title { +} +.typecho-page-title h2 { + margin: 25px 0 10px; + font-size: 1.28571em; +} +.typecho-page-title h2 a { + margin-left: 10px; + padding: 3px 8px; + background: #E9E9E6; + font-size: .8em; + + border-radius: 2px; +} +.typecho-page-title h2 a:hover { + text-decoration: none; +} + +/** +* 后台页面主体 +*/ + + +/** +* 主页主体 +*/ +.typecho-dashboard { +} +.typecho-dashboard ul { + list-style: none; + padding: 0; +} +.typecho-dashboard li { + margin-bottom: 5px; +} + + +/** +* 标签页 +*/ +.typecho-option-tabs { + list-style: none; + margin: 1em 0 0; + padding: 0; + font-size: 13px; + text-align: center; + &.fix-tabs { + margin-bottom: 1em; + } +} + +.typecho-option-tabs a { + display: block; + margin-right: -1px; + border: 1px solid #D9D9D6; + padding: 0 15px; + height: 26px; + line-height: 26px; + color: #666; + @include box-sizing(border-box); + +} +.typecho-option-tabs a:hover { + background-color: #E9E9E6; + color: #666; + text-decoration: none; +} + +.typecho-option-tabs li { + float: left; + &:first-child a { + @include border-radius(2px 0 0 2px); + } + &:last-child a { + @include border-radius(0 2px 2px 0); + } +} + +.typecho-option-tabs.right { + float: right; +} + +.typecho-option-tabs li.current a, +.typecho-option-tabs li.active a { + background-color: #E9E9E6; +} + + +/** +* 表格列表页 +*/ + +/** +* 列表页选项 +*/ +.typecho-list .typecho-pager { +} + +.typecho-list-operate { + margin: 1em 0; +} + +.typecho-list-operate input, +.typecho-list-operate button, +.typecho-list-operate select { + vertical-align: bottom; +} + +.typecho-list-operate input[type="checkbox"] { + vertical-align: text-top; +} + +.typecho-list-operate .operate { + float: left; +} + +.typecho-list-operate .search { + float: right; +} + +.typecho-list-operate span.operate-delete, a.operate-delete, +.typecho-list-operate span.operate-button-delete, a.operate-button-delete { + color: #B94A48; +} + +a.operate-edit { + color: #007700; +} + +a.operate-reply { + color: #545c30; +} + +.typecho-list-operate a:hover { + text-decoration: none; +} + +/** +* 列表表格 +*/ +/** 增加表格标题 by 70 */ +.typecho-list-table-title { + margin: 1em 0; + color: #999; + text-align: center; +} +.typecho-table-wrap { + padding: 30px; + background: #FFF; +} +.typecho-list-table { + width: 100%; +} + +.typecho-list-table.deactivate { + color: #999; +} + +.typecho-list-table .right { + text-align: right; +} + +.typecho-list-table th { + padding: 0 10px 10px; + border-bottom: 2px solid #F0F0EC; + text-align: left; +} + +.typecho-list-table td { + padding: 10px; + border-top: 1px solid #F0F0EC; + word-break: break-all; +} +.typecho-list-table .status { + margin-left: 5px; + color: #999; + font-size: .92857em; + font-style: normal; +} +.typecho-list-table tbody tr:hover td { + background-color: #F6F6F3; +} + +.typecho-list-table tbody tr.checked td { + background-color: #FFF9E8; +} + +.warning { + color: #B94A48; +} + + +.typecho-list-table tr td .hidden-by-mouse { + @include opacity(0); +} + +.typecho-list-table tr:hover td .hidden-by-mouse { + @include opacity(1); +} + + +/** +* 评论管理 +*/ + +.comment-reply-content { + position: relative; + margin: 1em 0; + padding: 0 1em; + border: 1px solid transparent; + background-color: #F0F0EC; +} +.comment-reply-content:after { + position: absolute; + right: 1em; + border: 8px solid #F0F0EC; + border-color: #F0F0EC #F0F0EC transparent transparent; + content: " "; +} + +.comment-meta span, +.comment-date { + font-size: .92857em; + color: #999; +} + +.comment-action a, .comment-action span { margin-right: 4px; } + +.comment-edit label { + display: block; +} + + +/** +* 评论回复 +*/ +#typecho-respond { + padding: 10px; + display: none; +} + + +/** +* 模板列表 +*/ +.typecho-theme-list { +} + +.typecho-theme-list .theme-item { +} + +.typecho-theme-list td { +} + +.typecho-theme-list img { + margin: 1em 0; + max-width: 100%; + max-height: 240px; +} + +.typecho-theme-list cite { + font-style: normal; + color: #999; +} + +.typecho-theme-list tbody tr.current td { + background-color: #FFF9E8; +} + + +/** +* 后台配置项 +*/ + +.typecho-page-main .typecho-option input.text { + width: 100%; +} + +.typecho-page-main .typecho-option input.num { + width: 40px; +} + +.typecho-page-main .typecho-option textarea { + width: 100%; + height: 100px; +} + +.typecho-page-main .typecho-option .multiline { + display: block; + margin: .3em 0; + &.hidden { + display: none; + } +} + + +/** +* 编辑模板 +*/ +.typecho-select-theme { + height: 25px; + line-height: 25px; + margin: 15px 0px; +} + +.typecho-select-theme h5 { + color: #E47E00; + font-weight: bold; + float: left; + font-size: 14px; + width: 120px; + margin-right: 10px; +} + +.typecho-select-theme select { + width: 150px; +} + +/** +* 编辑模板(编辑详情) +*/ + +.typecho-edit-theme ul { + list-style: none; + margin: 0; + padding: 0; +} + +.typecho-edit-theme li { + padding: 3px 10px; +} + +.typecho-edit-theme .current { + background-color: #E6E6E3; +} +.typecho-edit-theme .current a { + color: #444; +} + +.typecho-edit-theme textarea { + font-size: .92857em; + line-height: 1.2; + height: 500px; +} + +/** +* 编写页面 +*/ + +.typecho-post-area .edit-draft-notice { + color: #999; + font-size: .92857em; +} +.typecho-post-area .edit-draft-notice a { color: #B94A48; } + +.typecho-post-area .typecho-label { + display: block; + margin: 1em 0 -0.5em; + font-weight: bold; +} + +.typecho-post-area #auto-save-message { + display: block; + margin-top: 0.5em; + color: #999; + font-size: .92857em; +} + +.typecho-post-area .submit .right button { + margin-left: 5px; +} + +.typecho-post-area .right { + float: right; + padding-left: 24px; +} + +.typecho-post-area .left { + float: left; +} + +.typecho-post-area input.text { +} + +.typecho-post-area .out-date { + border: 1px solid #D3DBB3; + padding: 3px; + background: #fff; +} + +.typecho-post-area input.title { + font-size: 1.17em; + font-weight: bold; +} +.typecho-post-area .url-slug { + margin-top: -0.5em; + color: #AAA; + font-size: .92857em; + word-break: break-word; +} +.typecho-post-area #slug { + padding: 2px; + border: none; + background: #FFFBCC; + color: #666; +} + +.typecho-post-area #text { + resize: none; +} + +#advance-panel { + display: none; +} + +#custom-field { + margin: 1em 0; + padding: 10px 15px; + background: #FFF; + &.fold { + table, .description { display: none; } + } + + .description { + margin-top: 10px; + text-align: right; + button { + float: left; + } + } + + p.description { + text-align: left; + } + + .typecho-label { + margin: 0; + a { + display: block; + color: #444; + &:hover { + color: #467B96; + text-decoration: none; + } + } + } + table { + margin-top: 10px; + } + td { + padding: 10px 5px; + font-size: .92857em; + border-bottom: 1px solid #F0F0EC; + vertical-align: top; + label { + font-size: 1em; + font-weight: normal; + } + } + select { height: 27px; } +} + +.typecho-post-area .is-draft { +background: #FFF1A8; +} + +.typecho-post-option .description { + margin-top: -0.5em; + color: #999; + font-size: .92857em; +} + +.category-option ul { + list-style: none; + border: 1px solid #D9D9D6; + padding: 6px 12px; + max-height: 240px; + overflow: auto; + background-color: #FFF; + border-radius: 2px; +} +.category-option li { + margin: 3px 0; +} + +.visibility-option ul, +.allow-option ul { + list-style: none; + padding: 0; +} + + +/** +* 标签列表 +*/ + +.typecho-page-main ul.tag-list { + list-style: none; + margin: 0; + padding: 20px; + background-color: #FFF; +} + +.typecho-page-main ul.tag-list li { + display: inline-block; + margin: 0 0 5px 0; + padding: 5px 5px 5px 10px; + cursor: pointer; +} +.typecho-page-main ul.tag-list li:hover { + background-color: #E9E9E6; +} + +.typecho-page-main ul.tag-list li input { + display: none; +} + + +.typecho-page-main ul.tag-list li.checked { + background-color: #FFFBCC; +} + +.typecho-page-main ul.tag-list li.size-5 { font-size: 1em; } +.typecho-page-main ul.tag-list li.size-10 { font-size: 1.2em; } +.typecho-page-main ul.tag-list li.size-20 { font-size: 1.4em; } +.typecho-page-main ul.tag-list li.size-30 { font-size: 1.6em; } +.typecho-page-main ul.tag-list li.size-0 { font-size: 1.8em; } + +.typecho-page-main .tag-edit-link { visibility: hidden; } +.typecho-page-main li:hover .tag-edit-link { visibility: visible; } + +.typecho-attachment-photo { + border: 1px solid #E6E6E3; + max-width: 100%; +} + + +/* +* Upload +*/ +#upload-panel { + border: 1px dashed #D9D9D6; + background-color: #FFF; + color: #999; + font-size: .92857em; + &.drag { + background-color: #FFFBCC; + } +} + +.upload-area { + padding: 15px; + text-align: center; +} + +#file-list { + list-style: none; + margin: 0 10px; + padding: 0; + max-height: 450px; + overflow: auto; + word-break: break-all; + li, + .insert { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + li { + padding: 8px 0; + border-top: 1px dashed #D9D9D6; + } + .insert { + display: block; + max-width: 100%; + } + .file { + margin-left: 5px; + } + .info { + text-transform: uppercase; + } +} + +#btn-fullscreen-upload { + visibility: hidden; +} + + +/** +* 附件管理 +*/ +.edit-media button { margin-right: 6px; } + +/* 拖动调整 textarea 大小 */ +.resize { + display: block; + margin: 2px auto 0; + padding: 2px 0; + border: 1px solid #D9D9D6; + border-width: 1px 0; + width: 60px; + cursor: row-resize; + i { + display: block; + height: 1px; + background-color: #D9D9D6; + } +} + +/* 拖动排序 */ +.tDnD_whileDrag { + background-color: #FFFBCC; +} + + +/** +* 导入扩展样式 +*/ + +@import "icons"; +@import "components"; +@import "hidden"; diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/table-js.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/table-js.php new file mode 100644 index 0000000..13ac1c3 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/table-js.php @@ -0,0 +1,18 @@ + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/theme-editor.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/theme-editor.php new file mode 100644 index 0000000..a042d00 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/theme-editor.php @@ -0,0 +1,62 @@ +to($files); +?> + +
            +
            + +
            + + +
            +
            +
            + + +

            + currentIsWriteable()): ?> + + + + + + +

            +
            +
            +
              +
            • 模板文件
            • + next()): ?> + current): ?> class="current"> + file(); ?> + +
            +
            +
            +
            +
            + +bottom($files); +include 'footer.php'; +?> diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/themes.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/themes.php new file mode 100644 index 0000000..a2d0a00 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/themes.php @@ -0,0 +1,69 @@ + + +
            +
            + +
            +
            +
              +
            • + +
            • + + +
            • + +
            + +
            + + + + + + + + + + + + + to($themes); ?> + next()): ?> + + + + + + +
            截图详情
            <?php $themes->name(); ?> +

            title ? $themes->title() : $themes->name(); ?>

            + + author): ?>: homepage): ?>author(); ?>homepage): ?>    + version): ?>: version() ?> + +

            description); ?>

            + theme != $themes->name): ?> +

            + +   + + +

            + +
            +
            +
            +
            +
            +
            + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/upgrade.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/upgrade.php new file mode 100644 index 0000000..718d47a --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/upgrade.php @@ -0,0 +1,41 @@ + + +
            +
            + +
            +
            +
            +
            +

            +
              +
            • +
            • %s 升级到 %s', $options->version, Typecho_Common::VERSION); ?>
            • +
            • +
            +

            +
            +
            +
            +
            +
            +
            + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/user.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/user.php new file mode 100644 index 0000000..f6cb957 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/user.php @@ -0,0 +1,23 @@ + + +
            +
            + +
            +
            + form()->render(); ?> +
            +
            +
            +
            + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/welcome.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/welcome.php new file mode 100644 index 0000000..6c26136 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/welcome.php @@ -0,0 +1,36 @@ + + +
            +
            + +
            +
            +
            +
            +

            title); ?>

            +
              +
            1. + pass('contributor', true)): ?> +
            2. +
            3. + +
            4. + +
            +

            +
            +
            +
            +
            +
            +
            + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/write-js.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/write-js.php new file mode 100644 index 0000000..958635f --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/write-js.php @@ -0,0 +1,260 @@ + +write(); ?> +to($tags); ?> + + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/write-page.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/write-page.php new file mode 100644 index 0000000..2b50bf2 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/write-page.php @@ -0,0 +1,151 @@ +to($page); +?> +
            +
            + +
            +
            +
            + draft && $page->draft['cid'] != $page->cid): ?> + draft['modified']); ?> + 删除它', $pageModifyDate->word(), + $security->getIndex('/action/contents-page-edit?do=deleteDraft&cid=' . $page->cid)); ?> + + +

            + + +

            + routingTable['page']['url'], $options->index); + list ($scheme, $permalink) = explode(':', $permalink, 2); + $permalink = ltrim($permalink, '/'); + $permalink = preg_replace("/\[([_a-z0-9-]+)[^\]]*\]/i", "{\\1}", $permalink); + if ($page->have()) { + $permalink = str_replace('{cid}', $page->cid, $permalink); + } + $input = ''; + ?> +

            + + +

            +

            + + +

            + + +

            + + + + + markdown && (!$page->have() || $page->isMarkdown)): ?> + + + +

            + + content($page); ?> +
            + +
            +
            +
            +
            + +trigger($plugged)->richEditor($page); +if (!$plugged) { + include 'editor-js.php'; +} + +include 'file-upload-js.php'; +include 'custom-fields-js.php'; +Typecho_Plugin::factory('admin/write-page.php')->bottom($page); +include 'footer.php'; +?> diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/write-post.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/write-post.php new file mode 100644 index 0000000..d8ad69f --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/admin/write-post.php @@ -0,0 +1,180 @@ +to($post); +?> +
            +
            + +
            +
            +
            + draft && $post->draft['cid'] != $post->cid): ?> + draft['modified']); ?> + 删除它', $postModifyDate->word(), + $security->getIndex('/action/contents-post-edit?do=deleteDraft&cid=' . $post->cid)); ?> + + +

            + + +

            + routingTable['post']['url'], $options->index); + list ($scheme, $permalink) = explode(':', $permalink, 2); + $permalink = ltrim($permalink, '/'); + $permalink = preg_replace("/\[([_a-z0-9-]+)[^\]]*\]/i", "{\\1}", $permalink); + if ($post->have()) { + $permalink = str_replace(array( + '{cid}', '{category}', '{year}', '{month}', '{day}' + ), array( + $post->cid, $post->category, $post->year, $post->month, $post->day + ), $permalink); + } + $input = ''; + ?> +

            + + +

            +

            + + +

            + + + +

            + + + + + markdown && (!$post->have() || $post->isMarkdown)): ?> + + + +

            + + content($post); ?> +
            + + +
            +
            +
            +
            + +trigger($plugged)->richEditor($post); +if (!$plugged) { + include 'editor-js.php'; +} + +include 'file-upload-js.php'; +include 'custom-fields-js.php'; +Typecho_Plugin::factory('admin/write-post.php')->bottom($post); +include 'footer.php'; +?> diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/changelog.txt b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/changelog.txt new file mode 100644 index 0000000..769e526 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/changelog.txt @@ -0,0 +1,52 @@ +Version 0.8.1/12.4.1 + +修复同级子目录下安装多个站点引起的登录失效 +增加评论黑名单插件 +修复评论接口重用导致启用评论验证插件后,后台无法评论的bug +修正年份路由的bug +修正由于flash的cookie丢失bug导致的文件无法上传bug +增加对ini_get的判断 +提交对Sina App Engine的兼容性判断 +增加对Sina App Engine环境的支持 +增加对ini_get的判断 +修正新注册用户登录后跳转错误bug +修正对ssl的支持 +fix issue 510 +修复使用多层代理时, 获取ip地址错误 +修正修改文章时上传控件无法载入的问题 +修改后台错别字 +修正错别字 +兼容server不支持http 1.1的情况,典型问题如 sae环境404页面乱码 +修正由于SAE更改常用导致的数据库信息无法自动读入的问题 +增加上传插件接口 +提交Sina App Engine专用的文件上传插件, +使用SAE的Storage做持久化存储。 +修正SaeUpload插件的说明地址 +new version library +只是一些小修正 +fix bug report on segmentfault +fix issue 536 +fix Issue 541 +fix Issue 544 +fix Issue 544 +fix Issue 540 +fix Issue 537 +fix Issue 529 +fix Issue 532 +fix issue 526 +文章增加待审核功能,下一步给文章增加private属性 +更新后台表单样式, 新增文章预览功能 +css细节微调 +文章增加private属性显示 +修正插件显示空白问题, 预览框box修正 +修改预览内容样式,修改预览选项位置,高级选项->权限控制增加“允许游客访问”选项,用于私密浏览 +fix Issue 545 +实现文章公开度:公开、密码保护、私有、未审核 +little change +实现功能:1,当用户之前有审核通过的评论,再次发评论会直接为通过审核(可关闭);2,未通过审核的评论,评论作者可以在前台看到(他人不可见);3,隐藏功能; +修正后台修改或添加页面状态错误的问题;修正保存私密、审核日志时会新增一篇同样日志的问题 +简单增强搜索功能 Issue 480 +添加有密码和未发布图标 +fix Issue 551 +将文章管理页的公开度草稿等信息改为文字 +接受插件返回的header diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/index.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/index.php new file mode 100644 index 0000000..79efa8b --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/index.php @@ -0,0 +1,26 @@ +begin(); + +/** 开始路由分发 */ +Typecho_Router::dispatch(); + +/** 注册一个结束插件 */ +Typecho_Plugin::factory('index.php')->end(); diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install.php new file mode 100644 index 0000000..ce361a3 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install.php @@ -0,0 +1,666 @@ +generator = 'Typecho ' . Typecho_Common::VERSION; +list($soft, $currentVersion) = explode(' ', $options->generator); + +$options->software = $soft; +$options->version = $currentVersion; + +list($prefixVersion, $suffixVersion) = explode('/', $currentVersion); + +/** 获取语言 */ +$lang = _r('lang', Typecho_Cookie::get('__typecho_lang')); +$langs = Widget_Options_General::getLangs(); + +if (empty($lang) && count($langs) > 1) { + foreach ($langs as $lang) { + if ('zh_CN' != $lang) { + break; + } + } +} + +if (empty($lang)) { + $lang = 'zh_CN'; +} + +if ('zh_CN' != $lang) { + $dir = defined('__TYPECHO_LANG_DIR__') ? __TYPECHO_LANG_DIR__ : __TYPECHO_ROOT_DIR__ . '/usr/langs'; + Typecho_I18n::setLang($dir . '/' . $lang . '.mo'); +} + +Typecho_Cookie::set('__typecho_lang', $lang); + +?> + + + + <?php _e('Typecho 安装程序'); ?> + + + + + +
            +

            Typecho

            +
              + class="current">1 + class="current">2 + class="current">3 + class="current">4 +
            +
            +
            +
            +
            +
            + + +

            +
            +
            +

            +
            +
            + +

            +
            +
            +

            +
            +
            + + addServer($config, Typecho_Db::READ | Typecho_Db::WRITE); + Typecho_Db::set($db); + ?> +

            +
            +
            + + + + + :
            + : + + +
            + + + +
            +

            :

            +
              + getTokenUrl($loginUrl); + } else { + $loginUrl = _u() . '/admin/index.php'; + } + ?> +
            • +
            • +
            +
            + +

            +
            + + + +

            +
            +
            +

            +
            +
            + + addServer($config, Typecho_Db::READ | Typecho_Db::WRITE); + + /** 初始化数据库结构 */ + $scripts = file_get_contents ('./install/' . $type . '.sql'); + $scripts = str_replace('typecho_', $config['prefix'], $scripts); + + if (isset($config['charset'])) { + $scripts = str_replace('%charset%', $config['charset'], $scripts); + } + + $scripts = explode(';', $scripts); + foreach ($scripts as $script) { + $script = trim($script); + if ($script) { + $installDb->query($script, Typecho_Db::WRITE); + } + } + + /** 全局变量 */ + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'theme', 'user' => 0, 'value' => 'default'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'theme:default', 'user' => 0, 'value' => 'a:2:{s:7:"logoUrl";N;s:12:"sidebarBlock";a:5:{i:0;s:15:"ShowRecentPosts";i:1;s:18:"ShowRecentComments";i:2;s:12:"ShowCategory";i:3;s:11:"ShowArchive";i:4;s:9:"ShowOther";}}'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'timezone', 'user' => 0, 'value' => _t('28800')))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'lang', 'user' => 0, 'value' => $lang))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'charset', 'user' => 0, 'value' => _t('UTF-8')))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'contentType', 'user' => 0, 'value' => 'text/html'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'gzip', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'generator', 'user' => 0, 'value' => $options->generator))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'title', 'user' => 0, 'value' => 'Hello World'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'description', 'user' => 0, 'value' => 'Just So So ...'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'keywords', 'user' => 0, 'value' => 'typecho,php,blog'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'rewrite', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'frontPage', 'user' => 0, 'value' => 'recent'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'frontArchive', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsRequireMail', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsWhitelist', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsRequireURL', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsRequireModeration', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'plugins', 'user' => 0, 'value' => 'a:0:{}'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentDateFormat', 'user' => 0, 'value' => 'F jS, Y \a\t h:i a'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'siteUrl', 'user' => 0, 'value' => $config['siteUrl']))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'defaultCategory', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'allowRegister', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'defaultAllowComment', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'defaultAllowPing', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'defaultAllowFeed', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'pageSize', 'user' => 0, 'value' => 5))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'postsListSize', 'user' => 0, 'value' => 10))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsListSize', 'user' => 0, 'value' => 10))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsHTMLTagAllowed', 'user' => 0, 'value' => NULL))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'postDateFormat', 'user' => 0, 'value' => 'Y-m-d'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'feedFullText', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'editorSize', 'user' => 0, 'value' => 350))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'autoSave', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'markdown', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsMaxNestingLevels', 'user' => 0, 'value' => 5))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPostTimeout', 'user' => 0, 'value' => 24 * 3600 * 30))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsUrlNofollow', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsShowUrl', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsMarkdown', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPageBreak', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsThreaded', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPageSize', 'user' => 0, 'value' => 20))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPageDisplay', 'user' => 0, 'value' => 'last'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsOrder', 'user' => 0, 'value' => 'ASC'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsCheckReferer', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsAutoClose', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPostIntervalEnable', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPostInterval', 'user' => 0, 'value' => 60))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsShowCommentOnly', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsAvatar', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsAvatarRating', 'user' => 0, 'value' => 'G'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsAntiSpam', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'routingTable', 'user' => 0, 'value' => 'a:25:{s:5:"index";a:3:{s:3:"url";s:1:"/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:7:"archive";a:3:{s:3:"url";s:6:"/blog/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:2:"do";a:3:{s:3:"url";s:22:"/action/[action:alpha]";s:6:"widget";s:9:"Widget_Do";s:6:"action";s:6:"action";}s:4:"post";a:3:{s:3:"url";s:24:"/archives/[cid:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:10:"attachment";a:3:{s:3:"url";s:26:"/attachment/[cid:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:8:"category";a:3:{s:3:"url";s:17:"/category/[slug]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:3:"tag";a:3:{s:3:"url";s:12:"/tag/[slug]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:6:"author";a:3:{s:3:"url";s:22:"/author/[uid:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:6:"search";a:3:{s:3:"url";s:19:"/search/[keywords]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:10:"index_page";a:3:{s:3:"url";s:21:"/page/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:12:"archive_page";a:3:{s:3:"url";s:26:"/blog/page/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:13:"category_page";a:3:{s:3:"url";s:32:"/category/[slug]/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:8:"tag_page";a:3:{s:3:"url";s:27:"/tag/[slug]/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:11:"author_page";a:3:{s:3:"url";s:37:"/author/[uid:digital]/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:11:"search_page";a:3:{s:3:"url";s:34:"/search/[keywords]/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:12:"archive_year";a:3:{s:3:"url";s:18:"/[year:digital:4]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:13:"archive_month";a:3:{s:3:"url";s:36:"/[year:digital:4]/[month:digital:2]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:11:"archive_day";a:3:{s:3:"url";s:52:"/[year:digital:4]/[month:digital:2]/[day:digital:2]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:17:"archive_year_page";a:3:{s:3:"url";s:38:"/[year:digital:4]/page/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:18:"archive_month_page";a:3:{s:3:"url";s:56:"/[year:digital:4]/[month:digital:2]/page/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:16:"archive_day_page";a:3:{s:3:"url";s:72:"/[year:digital:4]/[month:digital:2]/[day:digital:2]/page/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:12:"comment_page";a:3:{s:3:"url";s:53:"[permalink:string]/comment-page-[commentPage:digital]";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:4:"feed";a:3:{s:3:"url";s:20:"/feed[feed:string:0]";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:4:"feed";}s:8:"feedback";a:3:{s:3:"url";s:31:"[permalink:string]/[type:alpha]";s:6:"widget";s:15:"Widget_Feedback";s:6:"action";s:6:"action";}s:4:"page";a:3:{s:3:"url";s:12:"/[slug].html";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}}'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'actionTable', 'user' => 0, 'value' => 'a:0:{}'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'panelTable', 'user' => 0, 'value' => 'a:0:{}'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'attachmentTypes', 'user' => 0, 'value' => '@image@'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'secret', 'user' => 0, 'value' => Typecho_Common::randString(32, true)))); + + /** 初始分类 */ + $installDb->query($installDb->insert('table.metas')->rows(array('name' => _t('默认分类'), 'slug' => 'default', 'type' => 'category', 'description' => _t('只是一个默认分类'), + 'count' => 1, 'order' => 1))); + + /** 初始关系 */ + $installDb->query($installDb->insert('table.relationships')->rows(array('cid' => 1, 'mid' => 1))); + + /** 初始内容 */ + $installDb->query($installDb->insert('table.contents')->rows(array('title' => _t('欢迎使用 Typecho'), 'slug' => 'start', 'created' => Typecho_Date::gmtTime(), 'modified' => Typecho_Date::gmtTime(), + 'text' => '' . _t('如果您看到这篇文章,表示您的 blog 已经安装成功.'), 'authorId' => 1, 'type' => 'post', 'status' => 'publish', 'commentsNum' => 1, 'allowComment' => 1, + 'allowPing' => 1, 'allowFeed' => 1, 'parent' => 0))); + + $installDb->query($installDb->insert('table.contents')->rows(array('title' => _t('关于'), 'slug' => 'start-page', 'created' => Typecho_Date::gmtTime(), 'modified' => Typecho_Date::gmtTime(), + 'text' => '' . _t('本页面由 Typecho 创建, 这只是个测试页面.'), 'authorId' => 1, 'order' => 0, 'type' => 'page', 'status' => 'publish', 'commentsNum' => 0, 'allowComment' => 1, + 'allowPing' => 1, 'allowFeed' => 1, 'parent' => 0))); + + /** 初始评论 */ + $installDb->query($installDb->insert('table.comments')->rows(array('cid' => 1, 'created' => Typecho_Date::gmtTime(), 'author' => 'Typecho', 'ownerId' => 1, 'url' => 'http://typecho.org', + 'ip' => '127.0.0.1', 'agent' => $options->generator, 'text' => '欢迎加入 Typecho 大家族', 'type' => 'comment', 'status' => 'approved', 'parent' => 0))); + + /** 初始用户 */ + $password = empty($config['userPassword']) ? substr(uniqid(), 7) : $config['userPassword']; + $hasher = new PasswordHash(8, true); + + $installDb->query($installDb->insert('table.users')->rows(array('name' => $config['userName'], 'password' => $hasher->HashPassword($password), 'mail' => $config['userMail'], + 'url' => 'http://www.typecho.org', 'screenName' => $config['userName'], 'group' => 'administrator', 'created' => Typecho_Date::gmtTime()))); + + unset($_SESSION['typecho']); + header('Location: ./install.php?finish&user=' . urlencode($config['userName']) + . '&password=' . urlencode($password)); + } catch (Typecho_Db_Exception $e) { + $success = false; + $code = $e->getCode(); +?> +

            +
            +
            +query("DROP TABLE IF EXISTS `{$table}`"); + } elseif($type == 'Pgsql') { + $installDb->query("DROP TABLE {$table}"); + } elseif($type == 'SQLite') { + $installDb->query("DROP TABLE {$table}"); + } + } + echo '

            ' . _t('已经删除完原有数据') . '

            '; + } elseif (_r('goahead')) { + //使用原有数据 + //但是要更新用户网站 + $installDb->query($installDb->update('table.options')->rows(array('value' => $config['siteUrl']))->where('name = ?', 'siteUrl')); + unset($_SESSION['typecho']); + header('Location: ./install.php?finish&use_old'); + exit; + } else { + echo '

            ' . _t('安装程序检查到原有数据表已经存在.') + . '

            ' . ' ' + . _t('或者') . '

            '; + } + } else { + echo '

            ' . _t('安装程序捕捉到以下错误: "%s". 程序被终止, 请检查您的配置信息.',$e->getMessage()) . '

            '; + } + ?> +
            +
            + + + + +
            +

            +
            +

            + ' . _t('没有检测到您手动创建的配置文件, 请检查后再次创建') . '

            '; + $success = false; + } else { + if (NULL == _r('userUrl')) { + $success = false; + echo '

            ' . _t('请填写您的网站地址') . '

            '; + } else if (NULL == _r('userName')) { + $success = false; + echo '

            ' . _t('请填写您的用户名') . '

            '; + } else if (NULL == _r('userMail')) { + $success = false; + echo '

            ' . _t('请填写您的邮箱地址') . '

            '; + } else if (32 < strlen(_r('userName'))) { + $success = false; + echo '

            ' . _t('用户名长度超过限制, 请不要超过 32 个字符') . '

            '; + } else if (200 < strlen(_r('userMail'))) { + $success = false; + echo '

            ' . _t('邮箱长度超过限制, 请不要超过 200 个字符') . '

            '; + } + } + + $_dbConfig = _rFrom('dbHost', 'dbUser', 'dbPassword', 'dbCharset', 'dbPort', 'dbDatabase', 'dbFile', 'dbDsn'); + + $_dbConfig = array_filter($_dbConfig); + $dbConfig = array(); + foreach ($_dbConfig as $key => $val) { + $dbConfig[strtolower (substr($key, 2))] = $val; + } + + // 在特殊服务器上的特殊安装过程处理 + if (_r('config')) { + $replace = array_keys($dbConfig); + foreach ($replace as &$key) { + $key = '{' . $key . '}'; + } + + if (!empty($_dbConfig['dbDsn'])) { + $dbConfig['dsn'] = str_replace($replace, array_values($dbConfig), $dbConfig['dsn']); + } + $config = str_replace($replace, array_values($dbConfig), _r('config')); + } + + if (!isset($config) && $success && !_r('created')) { + $installDb = new Typecho_Db($adapter, _r('dbPrefix')); + $installDb->addServer($dbConfig, Typecho_Db::READ | Typecho_Db::WRITE); + + + /** 检测数据库配置 */ + try { + $installDb->query('SELECT 1=1'); + } catch (Typecho_Db_Adapter_Exception $e) { + $success = false; + echo '

            ' + . _t('对不起,无法连接数据库,请先检查数据库配置再继续进行安装') . '

            '; + } catch (Typecho_Db_Exception $e) { + $success = false; + echo '

            ' + . _t('安装程序捕捉到以下错误: " %s ". 程序被终止, 请检查您的配置信息.',$e->getMessage()) . '

            '; + } + } + + if($success) { + Typecho_Cookie::set('__typecho_config', base64_encode(serialize(array_merge(array( + 'prefix' => _r('dbPrefix'), + 'userName' => _r('userName'), + 'userPassword' => _r('userPassword'), + 'userMail' => _r('userMail'), + 'adapter' => $adapter, + 'siteUrl' => _r('userUrl') + ), $dbConfig)))); + + if (_r('created')) { + header('Location: ./install.php?start'); + exit; + } + + /** 初始化配置文件 */ + $lines = array_slice(file(__FILE__), 0, 52); + $lines[] = " +/** 定义数据库参数 */ +\$db = new Typecho_Db('{$adapter}', '" . _r('dbPrefix') . "'); +\$db->addServer(" . (empty($config) ? var_export($dbConfig, true) : $config) . ", Typecho_Db::READ | Typecho_Db::WRITE); +Typecho_Db::set(\$db); +"; + $contents = implode('', $lines); + if (!Typecho_Common::isAppEngine()) { + @file_put_contents('./config.inc.php', $contents); + } + + // 创建一个用于标识的临时文件 + $_SESSION['typecho'] = 1; + + if (!file_exists('./config.inc.php')) { + ?> +

            config.inc.php 文件'); ?>
            +config.inc.php
            文件, 并复制如下代码至其中'); ?>

            +

            +

            + +
              +
            • + + +

              +
            • + +
            • + + +

              +
            • +
            + + + +

            +
              +
            • + + +

              +
            • +
            • + + +

              +
            • +
            • + + +

              +
            • +
            • + + +

              +
            • +
            +
            + +

            +
            + +
            +

            +
            +

            +

            +

            +

            GPL 协议发布, 我们允许用户在 GPL 协议许可的范围内使用, 拷贝, 修改和分发此程序.'); ?> +

            +

            + +

            +
            +

            + + + 1): ?> + + +

            +
            + + +
            +
            +
            +
            + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Mysql.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Mysql.php new file mode 100644 index 0000000..dac6899 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Mysql.php @@ -0,0 +1,175 @@ + + + + + +

            + + + + + + + + + + + + + +
          • + + +
          • + + + + + +
          • + + +
          • + + + + +
          • + + +

            +
          • + + + + + + +
          • + + +

            +
          • +
          • + + +
          • +
          • + + +
          • +
          • + + +
          • +
          • + + +
          • + + + +

            + +
          • + + +

            /cloudsql/typecho-gae:typecho'); ?>

            +
          • + +
          • + + +

            :/cloudsql/typecho-gae:typecho'); ?>

            +
          • + + +
          • + + +
          • +
          • + + +
          • +
          • + + +

            +
          • + + + + + + + + + + +
          • + + +

            +
          • +
          • + + +

            +
          • +
          • + + +

            +
          • +
          • + + +
          • +
          • + + +

            +
          • + + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Mysql.sql b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Mysql.sql new file mode 100644 index 0000000..78f2ad6 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Mysql.sql @@ -0,0 +1,152 @@ +-- phpMyAdmin SQL Dump +-- version 2.11.5 +-- http://www.phpmyadmin.net +-- +-- 主机: localhost +-- 生成日期: 2008 年 07 月 06 日 18:00 +-- 服务器版本: 5.0.51 +-- PHP 版本: 5.2.5 + +-- +-- 数据库: `typecho` +-- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_comments` +-- + +CREATE TABLE `typecho_comments` ( + `coid` int(10) unsigned NOT NULL auto_increment, + `cid` int(10) unsigned default '0', + `created` int(10) unsigned default '0', + `author` varchar(200) default NULL, + `authorId` int(10) unsigned default '0', + `ownerId` int(10) unsigned default '0', + `mail` varchar(200) default NULL, + `url` varchar(200) default NULL, + `ip` varchar(64) default NULL, + `agent` varchar(200) default NULL, + `text` text, + `type` varchar(16) default 'comment', + `status` varchar(16) default 'approved', + `parent` int(10) unsigned default '0', + PRIMARY KEY (`coid`), + KEY `cid` (`cid`), + KEY `created` (`created`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_contents` +-- + +CREATE TABLE `typecho_contents` ( + `cid` int(10) unsigned NOT NULL auto_increment, + `title` varchar(200) default NULL, + `slug` varchar(200) default NULL, + `created` int(10) unsigned default '0', + `modified` int(10) unsigned default '0', + `text` text, + `order` int(10) unsigned default '0', + `authorId` int(10) unsigned default '0', + `template` varchar(32) default NULL, + `type` varchar(16) default 'post', + `status` varchar(16) default 'publish', + `password` varchar(32) default NULL, + `commentsNum` int(10) unsigned default '0', + `allowComment` char(1) default '0', + `allowPing` char(1) default '0', + `allowFeed` char(1) default '0', + `parent` int(10) unsigned default '0', + PRIMARY KEY (`cid`), + UNIQUE KEY `slug` (`slug`), + KEY `created` (`created`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_fields` +-- + +CREATE TABLE `typecho_fields` ( + `cid` int(10) unsigned NOT NULL, + `name` varchar(200) NOT NULL, + `type` varchar(8) default 'str', + `str_value` text, + `int_value` int(10) default '0', + `float_value` float default '0', + PRIMARY KEY (`cid`,`name`), + KEY `int_value` (`int_value`), + KEY `float_value` (`float_value`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_metas` +-- + +CREATE TABLE `typecho_metas` ( + `mid` int(10) unsigned NOT NULL auto_increment, + `name` varchar(200) default NULL, + `slug` varchar(200) default NULL, + `type` varchar(32) NOT NULL, + `description` varchar(200) default NULL, + `count` int(10) unsigned default '0', + `order` int(10) unsigned default '0', + `parent` int(10) unsigned default '0', + PRIMARY KEY (`mid`), + KEY `slug` (`slug`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_options` +-- + +CREATE TABLE `typecho_options` ( + `name` varchar(32) NOT NULL, + `user` int(10) unsigned NOT NULL default '0', + `value` text, + PRIMARY KEY (`name`,`user`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_relationships` +-- + +CREATE TABLE `typecho_relationships` ( + `cid` int(10) unsigned NOT NULL, + `mid` int(10) unsigned NOT NULL, + PRIMARY KEY (`cid`,`mid`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_users` +-- + +CREATE TABLE `typecho_users` ( + `uid` int(10) unsigned NOT NULL auto_increment, + `name` varchar(32) default NULL, + `password` varchar(64) default NULL, + `mail` varchar(200) default NULL, + `url` varchar(200) default NULL, + `screenName` varchar(32) default NULL, + `created` int(10) unsigned default '0', + `activated` int(10) unsigned default '0', + `logged` int(10) unsigned default '0', + `group` varchar(16) default 'visitor', + `authCode` varchar(64) default NULL, + PRIMARY KEY (`uid`), + UNIQUE KEY `name` (`name`), + UNIQUE KEY `mail` (`mail`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Pgsql.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Pgsql.php new file mode 100644 index 0000000..b6fc203 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Pgsql.php @@ -0,0 +1,26 @@ + +
          • + + +

            +
          • +
          • + + +

            +
          • +
          • + + +

            +
          • +
          • + + +
          • +
          • + + +

            +
          • + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Pgsql.sql b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Pgsql.sql new file mode 100644 index 0000000..d612bd6 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/Pgsql.sql @@ -0,0 +1,130 @@ +-- +-- Table structure for table "typecho_comments" +-- +CREATE SEQUENCE "typecho_comments_seq"; + +CREATE TABLE "typecho_comments" ( "coid" INT NOT NULL DEFAULT nextval('typecho_comments_seq'), + "cid" INT NULL DEFAULT '0', + "created" INT NULL DEFAULT '0', + "author" VARCHAR(200) NULL DEFAULT NULL, + "authorId" INT NULL DEFAULT '0', + "ownerId" INT NULL DEFAULT '0', + "mail" VARCHAR(200) NULL DEFAULT NULL, + "url" VARCHAR(200) NULL DEFAULT NULL, + "ip" VARCHAR(64) NULL DEFAULT NULL, + "agent" VARCHAR(200) NULL DEFAULT NULL, + "text" TEXT NULL DEFAULT NULL, + "type" VARCHAR(16) NULL DEFAULT 'comment', + "status" VARCHAR(16) NULL DEFAULT 'approved', + "parent" INT NULL DEFAULT '0', + PRIMARY KEY ("coid") +); + +CREATE INDEX "typecho_comments_cid" ON "typecho_comments" ("cid"); +CREATE INDEX "typecho_comments_created" ON "typecho_comments" ("created"); + + +-- +-- Table structure for table "typecho_contents" +-- + +CREATE SEQUENCE "typecho_contents_seq"; + +CREATE TABLE "typecho_contents" ( "cid" INT NOT NULL DEFAULT nextval('typecho_contents_seq'), + "title" VARCHAR(200) NULL DEFAULT NULL, + "slug" VARCHAR(200) NULL DEFAULT NULL, + "created" INT NULL DEFAULT '0', + "modified" INT NULL DEFAULT '0', + "text" TEXT NULL DEFAULT NULL, + "order" INT NULL DEFAULT '0', + "authorId" INT NULL DEFAULT '0', + "template" VARCHAR(32) NULL DEFAULT NULL, + "type" VARCHAR(16) NULL DEFAULT 'post', + "status" VARCHAR(16) NULL DEFAULT 'publish', + "password" VARCHAR(32) NULL DEFAULT NULL, + "commentsNum" INT NULL DEFAULT '0', + "allowComment" CHAR(1) NULL DEFAULT '0', + "allowPing" CHAR(1) NULL DEFAULT '0', + "allowFeed" CHAR(1) NULL DEFAULT '0', + "parent" INT NULL DEFAULT '0', + PRIMARY KEY ("cid"), + UNIQUE ("slug") +); + +CREATE INDEX "typecho_contents_created" ON "typecho_contents" ("created"); + +-- +-- Table structure for table "typecho_fields" +-- + +CREATE TABLE "typecho_fields" ("cid" INT NOT NULL, + "name" VARCHAR(200) NOT NULL, + "type" VARCHAR(8) NULL DEFAULT 'str', + "str_value" TEXT NULL DEFAULT NULL, + "int_value" INT NULL DEFAULT '0', + "float_value" REAL NULL DEFAULT '0', + PRIMARY KEY ("cid","name") +); + +CREATE INDEX "typecho_fields_int_value" ON "typecho_fields" ("int_value"); +CREATE INDEX "typecho_fields_float_value" ON "typecho_fields" ("float_value"); + +-- +-- Table structure for table "typecho_metas" +-- + +CREATE SEQUENCE "typecho_metas_seq"; + +CREATE TABLE "typecho_metas" ( "mid" INT NOT NULL DEFAULT nextval('typecho_metas_seq'), + "name" VARCHAR(200) NULL DEFAULT NULL, + "slug" VARCHAR(200) NULL DEFAULT NULL, + "type" VARCHAR(16) NOT NULL DEFAULT '', + "description" VARCHAR(200) NULL DEFAULT NULL, + "count" INT NULL DEFAULT '0', + "order" INT NULL DEFAULT '0', + "parent" INT NULL DEFAULT '0', + PRIMARY KEY ("mid") +); + +CREATE INDEX "typecho_metas_slug" ON "typecho_metas" ("slug"); + + +-- +-- Table structure for table "typecho_options" +-- + +CREATE TABLE "typecho_options" ( "name" VARCHAR(32) NOT NULL DEFAULT '', + "user" INT NOT NULL DEFAULT '0', + "value" TEXT NULL DEFAULT NULL, + PRIMARY KEY ("name","user") +); + +-- +-- Table structure for table "typecho_relationships" +-- + +CREATE TABLE "typecho_relationships" ( "cid" INT NOT NULL DEFAULT '0', + "mid" INT NOT NULL DEFAULT '0', + PRIMARY KEY ("cid","mid") +); + +-- +-- Table structure for table "typecho_users" +-- +CREATE SEQUENCE "typecho_users_seq"; + +CREATE TABLE "typecho_users" ( "uid" INT NOT NULL DEFAULT nextval('typecho_users_seq') , + "name" VARCHAR(32) NULL DEFAULT NULL, + "password" VARCHAR(64) NULL DEFAULT NULL, + "mail" VARCHAR(200) NULL DEFAULT NULL, + "url" VARCHAR(200) NULL DEFAULT NULL, + "screenName" VARCHAR(32) NULL DEFAULT NULL, + "created" INT NULL DEFAULT '0', + "activated" INT NULL DEFAULT '0', + "logged" INT NULL DEFAULT '0', + "group" VARCHAR(16) NULL DEFAULT 'visitor', + "authCode" VARCHAR(64) NULL DEFAULT NULL, + PRIMARY KEY ("uid"), + UNIQUE ("name"), + UNIQUE ("mail") +); diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/SQLite.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/SQLite.php new file mode 100644 index 0000000..ec0fa66 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/SQLite.php @@ -0,0 +1,7 @@ + + +
          • + + +

            +
          • diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/SQLite.sql b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/SQLite.sql new file mode 100644 index 0000000..80d53d6 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/install/SQLite.sql @@ -0,0 +1,87 @@ +CREATE TABLE typecho_comments ( "coid" INTEGER NOT NULL PRIMARY KEY, +"cid" int(10) default '0' , +"created" int(10) default '0' , +"author" varchar(200) default NULL , +"authorId" int(10) default '0' , +"ownerId" int(10) default '0' , +"mail" varchar(200) default NULL , +"url" varchar(200) default NULL , +"ip" varchar(64) default NULL , +"agent" varchar(200) default NULL , +"text" text , +"type" varchar(16) default 'comment' , +"status" varchar(16) default 'approved' , +"parent" int(10) default '0' ); + +CREATE INDEX typecho_comments_cid ON typecho_comments ("cid"); +CREATE INDEX typecho_comments_created ON typecho_comments ("created"); + +CREATE TABLE typecho_contents ( "cid" INTEGER NOT NULL PRIMARY KEY, +"title" varchar(200) default NULL , +"slug" varchar(200) default NULL , +"created" int(10) default '0' , +"modified" int(10) default '0' , +"text" text , +"order" int(10) default '0' , +"authorId" int(10) default '0' , +"template" varchar(32) default NULL , +"type" varchar(16) default 'post' , +"status" varchar(16) default 'publish' , +"password" varchar(32) default NULL , +"commentsNum" int(10) default '0' , +"allowComment" char(1) default '0' , +"allowPing" char(1) default '0' , +"allowFeed" char(1) default '0' , +"parent" int(10) default '0' ); + +CREATE UNIQUE INDEX typecho_contents_slug ON typecho_contents ("slug"); +CREATE INDEX typecho_contents_created ON typecho_contents ("created"); + +CREATE TABLE "typecho_fields" ("cid" INTEGER NOT NULL, + "name" varchar(200) NOT NULL, + "type" varchar(8) default 'str', + "str_value" text, + "int_value" int(10) default '0', + "float_value" real default '0' +); + +CREATE UNIQUE INDEX typecho_fields_cid_name ON typecho_fields ("cid", "name"); +CREATE INDEX typecho_fields_int_value ON typecho_fields ("int_value"); +CREATE INDEX typecho_fields_float_value ON typecho_fields ("float_value"); + +CREATE TABLE typecho_metas ( "mid" INTEGER NOT NULL PRIMARY KEY, +"name" varchar(200) default NULL , +"slug" varchar(200) default NULL , +"type" varchar(32) NOT NULL , +"description" varchar(200) default NULL , +"count" int(10) default '0' , +"order" int(10) default '0' , +"parent" int(10) default '0'); + +CREATE INDEX typecho_metas_slug ON typecho_metas ("slug"); + +CREATE TABLE typecho_options ( "name" varchar(32) NOT NULL , +"user" int(10) NOT NULL default '0' , +"value" text ); + +CREATE UNIQUE INDEX typecho_options_name_user ON typecho_options ("name", "user"); + +CREATE TABLE typecho_relationships ( "cid" int(10) NOT NULL , +"mid" int(10) NOT NULL ); + +CREATE UNIQUE INDEX typecho_relationships_cid_mid ON typecho_relationships ("cid", "mid"); + +CREATE TABLE typecho_users ( "uid" INTEGER NOT NULL PRIMARY KEY, +"name" varchar(32) default NULL , +"password" varchar(64) default NULL , +"mail" varchar(200) default NULL , +"url" varchar(200) default NULL , +"screenName" varchar(32) default NULL , +"created" int(10) default '0' , +"activated" int(10) default '0' , +"logged" int(10) default '0' , +"group" varchar(16) default 'visitor' , +"authCode" varchar(64) default NULL); + +CREATE UNIQUE INDEX typecho_users_name ON typecho_users ("name"); +CREATE UNIQUE INDEX typecho_users_mail ON typecho_users ("mail"); diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/license.txt b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/license.txt new file mode 100644 index 0000000..0e77e74 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/license.txt @@ -0,0 +1,282 @@ +The GNU General Public License (GPL) +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. + +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies + +of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/todo.txt b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/todo.txt new file mode 100644 index 0000000..7949f9c --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/todo.txt @@ -0,0 +1,7 @@ +0.9 版 + +1. 干掉mootools,上jquery +2. 后台UI更新 +3. 撰写页面更新,加入markdown支持 +4. 增加bae支持 + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/Makefile b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/Makefile new file mode 100644 index 0000000..f9bf98e --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/Makefile @@ -0,0 +1,115 @@ + + +#define root directory +DIR=../ + +#update subversion +update: + @echo 'git update' + rm -Rf build/ + git clone https://github.com/typecho/typecho.git build + rm -Rf build/.git + rm -f build/.gitignore + rm -f build/.gitattributes + for i in `find build/ -name '*.css'`; do echo $$i && java -Xmx32m -jar yuicompressor-2.4.2.jar $$i --charset UTF-8 -o $$i; done; + for i in `find build/admin/js/ -name '*.js'`; do echo $$i && java -Xmx32m -jar yuicompressor-2.4.2.jar $$i --charset UTF-8 -o $$i; done; + for i in `find build/ -name '*.php'`; do php -l $$i; done; + + +package: + @echo 'package' + rm -Rf build/tools/ + rm -f build/todo.txt + rm -f build/changelog.txt + rm -f build/.travis.yml + rm -f build/README.md + rm -Rf build/admin/scss + rm -Rf build/admin/img/editor + rm -Rf build/admin/img/icons + rm -Rf build/admin/img/icons-2x + rm -Rf build/usr/langs/* + mkdir build/usr/uploads/ + chmod 777 build/usr/uploads/ + tar -cvvzf build.tar.gz build/ + + +commonmark-php: + rm -rf ../var/CommonMark + git clone https://github.com/joyqi/commonmark-php.git commonmark + php transfer.php commonmark/src CommonMark ColinODell/CommonMark + rm -f commonmark/src/CommonMarkConverter.php + mv commonmark/src ../var/CommonMark + rm -rf commonmark + + +commonmark-js: + rm -rf ../admin/js/stmd.js + git clone https://github.com/jgm/stmd.git + cp stmd/js/stmd.js ../admin/js/ + rm -rf stmd + + +clear: + rm -Rf build/ + + +upgrade: + make update + rm -Rf ${DIR}/admin/ + cp -Rf build/admin/ ${DIR} + rm -Rf ${DIR}/var/ + cp -Rf build/var/ ${DIR} + rm -Rf ${DIR}/index.php + cp build/index.php ${DIR} + make clear + +langs: + rm -rf ../usr/langs + mkdir ../usr/langs + git clone https://github.com/typecho/languages.git + #for i in `find . -name '*.po'`; do echo $$i && php msgfmt.php $$i; done; + cd languages/ && for i in `find . -name '*.po'`; do echo `basename $$i` && msgfmt -o `basename $$i .po`.mo `basename $$i`; done; + cp languages/*.mo ../usr/langs/ + rm -rf languages + + +theme: + make update + rm -Rf ${DIR}/usr/themes/default/ + cp -Rf build/usr/themes/default/ ${DIR}/usr/themes/ + make clear + + +install: + make update + rm -Rf build/tools/ + rm -f build/todo.txt + rm -f build/changelog.txt + rm -f build/.travis.yml + rm -f build/README.md + rm -Rf build/admin/scss + rm -Rf build/admin/img/editor + rm -Rf build/admin/img/icons + rm -Rf build/admin/img/icons-2x + mkdir build/usr/uploads/ + chmod 777 build/usr/uploads/ + cp -Rf build/* ${DIR} + make clear + + +pot: + cd ../ && php tools/list.php ./ > tools/files.txt + cd ../ && xgettext --files-from=tools/files.txt -Lphp --from-code=UTF-8 --keyword=_t --keyword=_e --keyword=_n:1,2 --no-location --copyright-holder=Typecho --package-name=Typecho --package-version=`grep -E "VERSION = '(.*)'" ./var/Typecho/Common.php | cut -d "'" -f 2` --no-wrap --output=tools/messages.pot + rm -f files.txt + + +test: + for i in `find ../var/ -name '*.php'`; do php -l $$i || exit 1; done; + for i in `find ../usr/ -name '*.php'`; do php -l $$i || exit 1; done; + for i in `find ../admin/ -name '*.php'`; do php -l $$i || exit 1; done; + + +all: + make update + make package + make clear diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/list.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/list.php new file mode 100644 index 0000000..35f7b71 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/list.php @@ -0,0 +1,36 @@ +getPathname(); + $file = $file->getFilename(); + $lists[] = $path; + + $dir = dirname($path); + + if ($file[0] == '.') { + continue; + } + + $path = ltrim(substr($path, $offset), '/\\'); + list($class) = explode('.', $path); + + $name = str_replace(['/', '\\'], '\\', $fake . '\\' . $class); + $class = str_replace(['/', '\\'], '_', $ns . '_' . $class); + + $map[$name] = $class; +} + +foreach ($lists as $file) { + $dir = dirname($file); + $source = file_get_contents($file); + $replace = []; + $current = ''; + + $source = preg_replace_callback("/\nnamespace\s*([a-z_\\\]+);/is", function ($matches) use ($map, $file, &$replace, &$current) { + $matches[1] .= '\\' . pathinfo($file, PATHINFO_FILENAME); + $parts = explode('\\', $matches[1]); + $last = array_pop($parts); + + if (isset($map[$matches[1]])) { + $replace[$last] = $map[$matches[1]]; + $current = $matches[1]; + } + + return "if (!defined('__TYPECHO_ROOT_DIR__')) exit;"; + }, $source); + + $source = preg_replace_callback("/\nuse\s*([a-z_\\\]+)(?:\s+as\s+([a-z_\\\]+))?;/is", function ($matches) use ($map, &$replace) { + $parts = explode('\\', $matches[1]); + $last = array_pop($parts); + + if (isset($map[$matches[1]])) { + $replace[$last] = $map[$matches[1]]; + } + + return ''; + }, $source); + + foreach ($map as $key => $val) { + if (count(explode('\\', $key)) == count(explode('\\', $current))) { + $parts = explode('_', $val); + $last = array_pop($parts); + + if (!isset($replace[$last])) { + $replace[$last] = $val; + } + } + } + + $source = str_replace(array('mb_strtoupper', 'mb_strlen'), + array('Typecho_Common::strToUpper', 'Typecho_Common::strLen'), $source); + + $tokens = token_get_all($source); + $source = ''; + + $last = false; + foreach ($tokens as $key => $token) { + if (!is_array($token)) { + $source .= $token; + $last = false; + continue; + } + + list ($name, $str) = $token; + + if (T_STRING == $name) { + $str = isset($replace[$str]) ? $replace[$str] : $str; + } else if (T_NS_SEPARATOR == $name) { + if (T_STRING == $last) { + $source = substr($source, 0, - strlen($tokens[$key - 1][1])); + } + + $str = ''; + } + + $last = $name; + $source .= $str; + } + + file_put_contents($file, $source); +} + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/updateForkTypecho.sh b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/updateForkTypecho.sh new file mode 100644 index 0000000..ee45275 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/updateForkTypecho.sh @@ -0,0 +1,20 @@ +#!/bin/bash +#更新fork之后的Typecho至最新代码 + +#切换到主目录 +cd .. + +#增加Typecho源分支地址到本地项目远程分支列表 +git remote add official https://github.com/typecho/typecho.git + +#查看远程分支列表 +#git remote -v + +#更新Typecho源分支的新版本到本地 +git fetch official + +#合并Typecho源分支的代码 +git merge official/master + +#将合并后的代码push到你自己fork的Tyepcho项目上去 +git push origin master diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/yuicompressor-2.4.2.jar b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/yuicompressor-2.4.2.jar new file mode 100644 index 0000000..c29470b Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/tools/yuicompressor-2.4.2.jar differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/plugins/HelloWorld/Plugin.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/plugins/HelloWorld/Plugin.php new file mode 100644 index 0000000..6425858 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/plugins/HelloWorld/Plugin.php @@ -0,0 +1,70 @@ +navBar = array('HelloWorld_Plugin', 'render'); + } + + /** + * 禁用插件方法,如果禁用失败,直接抛出异常 + * + * @static + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function deactivate(){} + + /** + * 获取插件配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form 配置面板 + * @return void + */ + public static function config(Typecho_Widget_Helper_Form $form) + { + /** 分类名称 */ + $name = new Typecho_Widget_Helper_Form_Element_Text('word', NULL, 'Hello World', _t('说点什么')); + $form->addInput($name); + } + + /** + * 个人用户的配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form + * @return void + */ + public static function personalConfig(Typecho_Widget_Helper_Form $form){} + + /** + * 插件实现方法 + * + * @access public + * @return void + */ + public static function render() + { + echo '' + . htmlspecialchars(Typecho_Widget::widget('Widget_Options')->plugin('HelloWorld')->word) + . ''; + } +} diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/404.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/404.php new file mode 100644 index 0000000..6a2a210 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/404.php @@ -0,0 +1,16 @@ + +need('header.php'); ?> + +
            + +
            +

            404 -

            +

            +
            +

            +

            +
            +
            + +
            + need('footer.php'); ?> diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/archive.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/archive.php new file mode 100644 index 0000000..cc5f991 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/archive.php @@ -0,0 +1,36 @@ + +need('header.php'); ?> + +
            +

            archiveTitle(array( + 'category' => _t('分类 %s 下的文章'), + 'search' => _t('包含关键字 %s 的文章'), + 'tag' => _t('标签 %s 下的文章'), + 'author' => _t('%s 发布的文章') + ), '', ''); ?>

            + have()): ?> + next()): ?> + + + +
            +

            +
            + + + pageNav('« 前一页', '后一页 »'); ?> +
            + + need('sidebar.php'); ?> + need('footer.php'); ?> diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/comments.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/comments.php new file mode 100644 index 0000000..709d0b9 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/comments.php @@ -0,0 +1,49 @@ + +
            + comments()->to($comments); ?> + have()): ?> +

            commentsNum(_t('暂无评论'), _t('仅有一条评论'), _t('已有 %d 条评论')); ?>

            + + listComments(); ?> + + pageNav('« 前一页', '后一页 »'); ?> + + + + allow('comment')): ?> +
            +
            + cancelReply(); ?> +
            + +

            +
            + user->hasLogin()): ?> +

            user->screenName(); ?>. »

            + +

            + + +

            +

            + + options->commentsRequireMail): ?> required /> +

            +

            + + options->commentsRequireURL): ?> required /> +

            + +

            + + +

            +

            + +

            +
            +
            + +

            + +
            diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/footer.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/footer.php new file mode 100644 index 0000000..347cd19 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/footer.php @@ -0,0 +1,14 @@ + + +
          +

    + + + + +footer(); ?> + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/functions.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/functions.php new file mode 100644 index 0000000..77b4855 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/functions.php @@ -0,0 +1,26 @@ +addInput($logoUrl); + + $sidebarBlock = new Typecho_Widget_Helper_Form_Element_Checkbox('sidebarBlock', + array('ShowRecentPosts' => _t('显示最新文章'), + 'ShowRecentComments' => _t('显示最近回复'), + 'ShowCategory' => _t('显示分类'), + 'ShowArchive' => _t('显示归档'), + 'ShowOther' => _t('显示其它杂项')), + array('ShowRecentPosts', 'ShowRecentComments', 'ShowCategory', 'ShowArchive', 'ShowOther'), _t('侧边栏显示')); + + $form->addInput($sidebarBlock->multiMode()); +} + + +/* +function themeFields($layout) { + $logoUrl = new Typecho_Widget_Helper_Form_Element_Text('logoUrl', NULL, NULL, _t('站点LOGO地址'), _t('在这里填入一个图片URL地址, 以在网站标题前加上一个LOGO')); + $layout->addItem($logoUrl); +} +*/ + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/grid.css b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/grid.css new file mode 100644 index 0000000..e8c959c --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/grid.css @@ -0,0 +1,569 @@ +/* + * Bento Grid System + * Source: https://github.com/fenbox/bento + * Version: 1.2.8 + * Update: 2013.11.25 + */ +.container, .row [class*="col-"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } + +.container { + margin-left: auto; + margin-right: auto; + padding-left: 10px; + padding-right: 10px; } + +.row { + margin-right: -10px; + margin-left: -10px; } + +.row [class*="col-"] { + float: left; + min-height: 1px; + padding-right: 10px; + padding-left: 10px; } + +.row [class*="-push-"], +.row [class*="-pull-"] { + position: relative; } + +/* + * Mobile and up + */ +.col-mb-1 { + width: 8.33333%; } + +.col-mb-2 { + width: 16.66667%; } + +.col-mb-3 { + width: 25%; } + +.col-mb-4 { + width: 33.33333%; } + +.col-mb-5 { + width: 41.66667%; } + +.col-mb-6 { + width: 50%; } + +.col-mb-7 { + width: 58.33333%; } + +.col-mb-8 { + width: 66.66667%; } + +.col-mb-9 { + width: 75%; } + +.col-mb-10 { + width: 83.33333%; } + +.col-mb-11 { + width: 91.66667%; } + +.col-mb-12 { + width: 100%; } + +/* + * Tablet and up + */ +@media (min-width: 768px) { + .container { + max-width: 728px; } + + .col-tb-1 { + width: 8.33333%; } + + .col-tb-2 { + width: 16.66667%; } + + .col-tb-3 { + width: 25%; } + + .col-tb-4 { + width: 33.33333%; } + + .col-tb-5 { + width: 41.66667%; } + + .col-tb-6 { + width: 50%; } + + .col-tb-7 { + width: 58.33333%; } + + .col-tb-8 { + width: 66.66667%; } + + .col-tb-9 { + width: 75%; } + + .col-tb-10 { + width: 83.33333%; } + + .col-tb-11 { + width: 91.66667%; } + + .col-tb-12 { + width: 100%; } + + .col-tb-offset-0 { + margin-left: 0%; } + + .col-tb-offset-1 { + margin-left: 8.33333%; } + + .col-tb-offset-2 { + margin-left: 16.66667%; } + + .col-tb-offset-3 { + margin-left: 25%; } + + .col-tb-offset-4 { + margin-left: 33.33333%; } + + .col-tb-offset-5 { + margin-left: 41.66667%; } + + .col-tb-offset-6 { + margin-left: 50%; } + + .col-tb-offset-7 { + margin-left: 58.33333%; } + + .col-tb-offset-8 { + margin-left: 66.66667%; } + + .col-tb-offset-9 { + margin-left: 75%; } + + .col-tb-offset-10 { + margin-left: 83.33333%; } + + .col-tb-offset-11 { + margin-left: 91.66667%; } + + .col-tb-offset-12 { + margin-left: 100%; } + + .col-tb-pull-0 { + right: 0%; } + + .col-tb-pull-1 { + right: 8.33333%; } + + .col-tb-pull-2 { + right: 16.66667%; } + + .col-tb-pull-3 { + right: 25%; } + + .col-tb-pull-4 { + right: 33.33333%; } + + .col-tb-pull-5 { + right: 41.66667%; } + + .col-tb-pull-6 { + right: 50%; } + + .col-tb-pull-7 { + right: 58.33333%; } + + .col-tb-pull-8 { + right: 66.66667%; } + + .col-tb-pull-9 { + right: 75%; } + + .col-tb-pull-10 { + right: 83.33333%; } + + .col-tb-pull-11 { + right: 91.66667%; } + + .col-tb-pull-12 { + right: 100%; } + + .col-tb-push-0 { + left: 0%; } + + .col-tb-push-1 { + left: 8.33333%; } + + .col-tb-push-2 { + left: 16.66667%; } + + .col-tb-push-3 { + left: 25%; } + + .col-tb-push-4 { + left: 33.33333%; } + + .col-tb-push-5 { + left: 41.66667%; } + + .col-tb-push-6 { + left: 50%; } + + .col-tb-push-7 { + left: 58.33333%; } + + .col-tb-push-8 { + left: 66.66667%; } + + .col-tb-push-9 { + left: 75%; } + + .col-tb-push-10 { + left: 83.33333%; } + + .col-tb-push-11 { + left: 91.66667%; } + + .col-tb-push-12 { + left: 100%; } } +/* + * Desktop and up + */ +@media (min-width: 992px) { + .container { + max-width: 952px; } + + .col-1 { + width: 8.33333%; } + + .col-2 { + width: 16.66667%; } + + .col-3 { + width: 25%; } + + .col-4 { + width: 33.33333%; } + + .col-5 { + width: 41.66667%; } + + .col-6 { + width: 50%; } + + .col-7 { + width: 58.33333%; } + + .col-8 { + width: 66.66667%; } + + .col-9 { + width: 75%; } + + .col-10 { + width: 83.33333%; } + + .col-11 { + width: 91.66667%; } + + .col-12 { + width: 100%; } + + .col-offset-0 { + margin-left: 0%; } + + .col-offset-1 { + margin-left: 8.33333%; } + + .col-offset-2 { + margin-left: 16.66667%; } + + .col-offset-3 { + margin-left: 25%; } + + .col-offset-4 { + margin-left: 33.33333%; } + + .col-offset-5 { + margin-left: 41.66667%; } + + .col-offset-6 { + margin-left: 50%; } + + .col-offset-7 { + margin-left: 58.33333%; } + + .col-offset-8 { + margin-left: 66.66667%; } + + .col-offset-9 { + margin-left: 75%; } + + .col-offset-10 { + margin-left: 83.33333%; } + + .col-offset-11 { + margin-left: 91.66667%; } + + .col-offset-12 { + margin-left: 100%; } + + .col-pull-0 { + right: 0%; } + + .col-pull-1 { + right: 8.33333%; } + + .col-pull-2 { + right: 16.66667%; } + + .col-pull-3 { + right: 25%; } + + .col-pull-4 { + right: 33.33333%; } + + .col-pull-5 { + right: 41.66667%; } + + .col-pull-6 { + right: 50%; } + + .col-pull-7 { + right: 58.33333%; } + + .col-pull-8 { + right: 66.66667%; } + + .col-pull-9 { + right: 75%; } + + .col-pull-10 { + right: 83.33333%; } + + .col-pull-11 { + right: 91.66667%; } + + .col-pull-12 { + right: 100%; } + + .col-push-0 { + left: 0%; } + + .col-push-1 { + left: 8.33333%; } + + .col-push-2 { + left: 16.66667%; } + + .col-push-3 { + left: 25%; } + + .col-push-4 { + left: 33.33333%; } + + .col-push-5 { + left: 41.66667%; } + + .col-push-6 { + left: 50%; } + + .col-push-7 { + left: 58.33333%; } + + .col-push-8 { + left: 66.66667%; } + + .col-push-9 { + left: 75%; } + + .col-push-10 { + left: 83.33333%; } + + .col-push-11 { + left: 91.66667%; } + + .col-push-12 { + left: 100%; } } +/* + * Widescreen and up + */ +@media (min-width: 1200px) { + .container { + max-width: 1160px; } + + .col-wd-1 { + width: 8.33333%; } + + .col-wd-2 { + width: 16.66667%; } + + .col-wd-3 { + width: 25%; } + + .col-wd-4 { + width: 33.33333%; } + + .col-wd-5 { + width: 41.66667%; } + + .col-wd-6 { + width: 50%; } + + .col-wd-7 { + width: 58.33333%; } + + .col-wd-8 { + width: 66.66667%; } + + .col-wd-9 { + width: 75%; } + + .col-wd-10 { + width: 83.33333%; } + + .col-wd-11 { + width: 91.66667%; } + + .col-wd-12 { + width: 100%; } + + .col-wd-offset-0 { + margin-left: 0%; } + + .col-wd-offset-1 { + margin-left: 8.33333%; } + + .col-wd-offset-2 { + margin-left: 16.66667%; } + + .col-wd-offset-3 { + margin-left: 25%; } + + .col-wd-offset-4 { + margin-left: 33.33333%; } + + .col-wd-offset-5 { + margin-left: 41.66667%; } + + .col-wd-offset-6 { + margin-left: 50%; } + + .col-wd-offset-7 { + margin-left: 58.33333%; } + + .col-wd-offset-8 { + margin-left: 66.66667%; } + + .col-wd-offset-9 { + margin-left: 75%; } + + .col-wd-offset-10 { + margin-left: 83.33333%; } + + .col-wd-offset-11 { + margin-left: 91.66667%; } + + .col-wd-offset-12 { + margin-left: 100%; } + + .col-wd-pull-0 { + right: 0%; } + + .col-wd-pull-1 { + right: 8.33333%; } + + .col-wd-pull-2 { + right: 16.66667%; } + + .col-wd-pull-3 { + right: 25%; } + + .col-wd-pull-4 { + right: 33.33333%; } + + .col-wd-pull-5 { + right: 41.66667%; } + + .col-wd-pull-6 { + right: 50%; } + + .col-wd-pull-7 { + right: 58.33333%; } + + .col-wd-pull-8 { + right: 66.66667%; } + + .col-wd-pull-9 { + right: 75%; } + + .col-wd-pull-10 { + right: 83.33333%; } + + .col-wd-pull-11 { + right: 91.66667%; } + + .col-wd-pull-12 { + right: 100%; } + + .col-wd-push-0 { + left: 0%; } + + .col-wd-push-1 { + left: 8.33333%; } + + .col-wd-push-2 { + left: 16.66667%; } + + .col-wd-push-3 { + left: 25%; } + + .col-wd-push-4 { + left: 33.33333%; } + + .col-wd-push-5 { + left: 41.66667%; } + + .col-wd-push-6 { + left: 50%; } + + .col-wd-push-7 { + left: 58.33333%; } + + .col-wd-push-8 { + left: 66.66667%; } + + .col-wd-push-9 { + left: 75%; } + + .col-wd-push-10 { + left: 83.33333%; } + + .col-wd-push-11 { + left: 91.66667%; } + + .col-wd-push-12 { + left: 100%; } } +/* + * Responsive kit + */ +@media (max-width: 767px) { + .kit-hidden-mb { + display: none; } } +@media (max-width: 991px) { + .kit-hidden-tb { + display: none; } } +@media (max-width: 1199px) { + .kit-hidden { + display: none; } } +/* + * Clearfix + */ +.clearfix, .row { + zoom: 1; } + .clearfix:before, .row:before, .clearfix:after, .row:after { + content: " "; + display: table; } + .clearfix:after, .row:after { + clear: both; } diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/header.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/header.php new file mode 100644 index 0000000..efabbed --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/header.php @@ -0,0 +1,70 @@ + + + + + + + + + <?php $this->archiveTitle(array( + 'category' => _t('分类 %s 下的文章'), + 'search' => _t('包含关键字 %s 的文章'), + 'tag' => _t('标签 %s 下的文章'), + 'author' => _t('%s 发布的文章') + ), '', ' - '); ?><?php $this->options->title(); ?> + + + + + + + + + + header(); ?> + + + + + +
    +
    +
    + + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/img/icon-search.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/img/icon-search.png new file mode 100644 index 0000000..6ab0619 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/img/icon-search.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/img/icon-search@2x.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/img/icon-search@2x.png new file mode 100644 index 0000000..243b408 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/img/icon-search@2x.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/index.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/index.php new file mode 100644 index 0000000..495d426 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/index.php @@ -0,0 +1,35 @@ +need('header.php'); + ?> + +
    + next()): ?> + + + + pageNav('« 前一页', '后一页 »'); ?> +
    + +need('sidebar.php'); ?> +need('footer.php'); ?> diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/page.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/page.php new file mode 100644 index 0000000..8f1cd6f --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/page.php @@ -0,0 +1,15 @@ + +need('header.php'); ?> + +
    + + need('comments.php'); ?> +
    + +need('sidebar.php'); ?> +need('footer.php'); ?> diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/post.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/post.php new file mode 100644 index 0000000..3732c9f --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/post.php @@ -0,0 +1,27 @@ + +need('header.php'); ?> + +
    +
    +

    title() ?>

    + +
    + content(); ?> +
    +

    tags(', ', true, 'none'); ?>

    +
    + + need('comments.php'); ?> + +
      +
    • 上一篇: thePrev('%s','没有了'); ?>
    • +
    • 下一篇: theNext('%s','没有了'); ?>
    • +
    +
    + +need('sidebar.php'); ?> +need('footer.php'); ?> diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/screenshot.png b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/screenshot.png new file mode 100644 index 0000000..8aa2987 Binary files /dev/null and b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/screenshot.png differ diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/sidebar.php b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/sidebar.php new file mode 100644 index 0000000..d546eb6 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/sidebar.php @@ -0,0 +1,59 @@ + + diff --git a/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/style.css b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/style.css new file mode 100644 index 0000000..8f775b3 --- /dev/null +++ b/PHP安全/PHP反序列化漏洞复现/Typecho-1.0-14.10.10/源码/typecho/usr/themes/default/style.css @@ -0,0 +1,499 @@ +/* ------------------------------------ + * Typecho Default Theme + * + * @author Typecho Team + * @link http: //typecho.org/ + * @update 2013-10-28 + * --------------------------------- */ + +/* ------------------ + * Global style + * --------------- */ +body { + background-color: #FFF; + color: #444; + /*font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;*/ + font-family: "Droid Serif", Georgia, "Times New Roman", STHeiti, serif; + font-size: 87.5%; +} + +a { + color: #3354AA; + text-decoration: none; +} +a:hover, a:active { + color: #444; +} +pre, code { + background: #F3F3F0; + font-family: Menlo, Monaco, Consolas, "Lucida Console", "Courier New", monospace; + font-size: .92857em; +} +code { padding: 2px 4px; color: #B94A48; } +pre { + padding: 0; + border: 1px solid #ccc; + overflow: auto; + max-height: 400px; +} +pre code { + padding: 3px; + color: #444; +} + +blockquote { + margin: 1em 1.5em; + padding-left: 1.5em; + border-left: 4px solid #F3F3F0; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +input[type="text"], +input[type="email"], +input[type="url"], +input[type="password"], +textarea { + padding: 5px; + border: 1px solid #E9E9E9; + width: 100%; + + border-radius: 2px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +textarea { + resize: vertical; +} + + +/* Special link style */ +.post-meta a, +.post-content a, +.widget a, +.comment-content a { + border-bottom: 1px solid #EEE; +} + +.post-meta a:hover, +.post-content a:hover, +.widget a:hover, +.comment-content a:hover { + border-bottom-color: transparent; +} + +.browsehappy { + padding: 8px 0; + background: #FBE3E4; + color: #8A1F11; + text-align: center; +} +.browsehappy a { + color: #8A1F11; + text-decoration: underline; + font-weight: bold; +} + +/* ------------------ + * Header + * --------------- */ + +#header { + padding-top: 35px; + border-bottom: 1px solid #EEE; +} + +#logo { + color: #333; + font-size: 2.5em; +} +.description { + margin: .5em 0 0; + color: #999; + font-style: italic; +} + +/* Navigation menu */ +#nav-menu { + margin: 25px 0 0; + padding: 0; +} +#nav-menu a { + display: block; + margin-right: -1px; + padding: 0 20px; + border: 1px solid #EEE; + border-bottom: none; + height: 32px; + line-height: 32px; + color: #444; + float: left; +} +#nav-menu a:hover, +#nav-menu .current { + background: #F6F6F6; +} + +/* Search */ +#search { + position: relative; + margin-top: 15px; +} +#search input { + padding-right: 30px; +} +#search button { + position: absolute; + right: 4px; + top: 2px; + border: none; + padding: 0; + width: 24px; + height: 24px; + background: transparent url(img/icon-search.png) no-repeat center center; + direction: ltr; /* fix RTL language */ + text-indent: -9999em; +} + +@media +(-webkit-min-device-pixel-ratio: 2), +(min-resolution: 192dpi) { + #search button { + background-image: url(img/icon-search@2x.png); + -webkit-background-size: 24px 24px; + -moz-background-size: 24px 24px; + -o-background-size: 24px 24px; + background-size: 24px 24px; + } +} + + +/* ------------------ + * Main + * --------------- */ + +.post { + padding: 15px 0 20px; + border-bottom: 1px solid #EEE; +} +.post-title { + margin: .83em 0; + font-size: 1.4em; +} +.post-meta { + margin-top: -0.5em; + padding: 0; + color: #999; + font-size: .92857em; +} +.post-meta li { + display: inline-block; + margin: 0 8px 0 0; + padding-left: 12px; + border-left: 1px solid #EEE; +} +.post-meta li:first-child { + margin-left: 0; + padding-left: 0; + border: none; +} +.post-content { + line-height: 1.5; +} +.post .tags { + clear: both; +} + +.post-near { + list-style: none; + margin: 30px 0; + padding: 0; + color: #999; +} +.post-near li { + margin: 10px 0; +} + +.archive-title { + margin: 1em 0 -1em; + padding-top: 20px; + color: #999; + font-size: 1em; +} +.more { + text-align: center; +} +.more a { + border: none; +} +.protected .text { + width: 50%; +} + +/* Page nav */ + +.page-navigator { + list-style: none; + margin: 25px 0; + padding: 0; + text-align: center; +} +.page-navigator li { + display: inline-block; + margin: 0 4px; +} +.page-navigator a { + display: inline-block; + padding: 0 10px; + height: 30px; + line-height: 30px; +} +.page-navigator a:hover { + background: #EEE; + text-decoration: none; +} + +.page-navigator .current a { + color: #444; + background: #EEE; +} + +/* ------------------ + * Comment list + * --------------- */ +#comments { + padding-top: 15px; +} +.comment-list, .comment-list ol { + list-style: none; + margin: 0; + padding: 0; +} +.comment-list li { + padding: 14px; + margin-top: 10px; + border: 1px solid #EEE; +} +.comment-list li.comment-level-odd { + background: #F6F6F3; +} +.comment-list li.comment-level-even { + background: #FFF; +} +.comment-list li.comment-by-author { + background: #FFF9E8; +} +.comment-list li .comment-reply { + text-align: right; + font-size: .92857em; +} +.comment-meta a { + color: #999; + font-size: .92857em; +} +.comment-author { + display: block; + margin-bottom: 3px; + color: #444; +} +.comment-author .avatar { + float: left; + margin-right: 10px; +} +.comment-author cite { + font-weight: bold; + font-style: normal; +} + +/* Comment reply */ +.comment-list .respond { + margin-top: 15px; + border-top: 1px solid #EEE; +} +.respond .cancel-comment-reply { + float: right; + margin-top: 15px; + font-size: .92857em; +} +#comment-form label { + display: block; + margin-bottom: .5em; + font-weight: bold; +} +#comment-form .required:after { + content: " *"; + color: #C00; +} + +/* ------------------ + * secondary + * --------------- */ +#secondary { + padding-top: 15px; + word-wrap: break-word; +} +.widget { + margin-bottom: 30px; +} +.widget-title { +} +.widget-list { + list-style: none; + padding: 0; +} +.widget-list li { + margin: 5px 0; + line-height: 1.5; +} + +.widget-list li ul { + margin-left: 15px; +} + +.widget-list a:hover { +} + + +/* ------------------ + * Footer + * --------------- */ +#footer { + padding: 3em 0; + line-height: 1.5; + text-align: center; + color: #999; +} + + +/* ----------------- + * Error page + * -------------- */ +.error-page { + margin-top: 100px; + margin-bottom: 100px; +} + + +/* ----------------- + * Content format + *--------------- */ +.post-content, .comment-content { + line-height: 1.5; + word-wrap: break-word; +} +.post-content h2, .comment-content h2 { + font-size: 1.28571em; +} +.post-content img, .comment-content img, +.post-content video, .comment-content video { + max-width: 100%; +} +.post-content a img, +.comment-content a img { + background: #FFF; + position: relative; + bottom: -4px; /* hidden img parent link border */ +} +.post-content hr, .comment-content hr { + margin: 2em auto; + width: 100px; + border: 1px solid #E9E9E9; + border-width: 2px 0 0 0; +} + + +/* ----------------- + * Misc + *--------------- */ +.aligncenter, div.aligncenter { + display: block; + margin-left: auto; + margin-right: auto; +} +.alignleft { + float: left; +} +.alignright { + float: right; +} +img.alignleft { + margin: 0 15px 0 0; +} +img.alignright { + margin: 0 0 0 15px; +} + + +/* ----------------- + * Responsive + *--------------- */ +@media (max-width: 767px) { + body { + font-size: 81.25%; + } + #nav-menu a { + float: none; + display: inline-block; + margin: 0 -2px; + } +} + +@media (max-width: 768px) { + #header, + .post-title, + .post-meta { + text-align: center; + } +} + +@media (min-width: 992px) { + +} + +@media (min-width: 1200px) { + .container { + max-width: 952px; + } +} + + +/* +* Hide from both screenreaders and browsers: h5bp.com/u +*/ +.hidden { + display: none !important; + visibility: hidden; } + +/* +* Hide only visually, but have it available for screenreaders: h5bp.com/v +*/ +.sr-only { + border: 0; + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; } + +/* +* Extends the .sr-only class to allow the element to be focusable +* when navigated to via the keyboard: h5bp.com/p +*/ +.sr-only.focusable:active, +.sr-only.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; } + +/* +* Hide visually and from screenreaders, but maintain layout +*/ +.invisible { + visibility: hidden; }