diff --git a/.babelrc b/.babelrc
new file mode 100644
index 00000000..55754d07
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,3 @@
+{
+ "compact": false
+}
diff --git a/.editorconfig b/.editorconfig
index d9c3abd5..d72a75ea 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,29 +1,25 @@
-# EditorConfig helps developers define and maintain consistent
-# coding styles between different editors and IDEs
-# http://editorconfig.org
-# 所有文件换行使用 Unix like 风格(LF),bat 文件使用 win 风格(CRLF)
-# 缩进 java 4 个空格,其他所有文件 2 个空格
+# EditorConfig 用于在 IDE 中检查代码的基本 Code Style
+# @see: https://editorconfig.org/
+
+# 配置说明:
+# 所有文件换行使用 Unix 风格(LF),*.bat 文件使用 Windows 风格(CRLF)
+# java / sh 文件缩进 4 个空格,其他所有文件缩进 2 个空格
root = true
[*]
-# Unix-style newlines with a newline ending every file
end_of_line = lf
-
-# Change these settings to your own preference
indent_size = 2
indent_style = space
max_line_length = 120
-
-# We recommend you to keep these unchanged
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
-[*.bat]
+[*.{bat, cmd}]
end_of_line = crlf
-[*.java]
+[*.{java, gradle, groovy, kt, sh}]
indent_size = 4
[*.md]
diff --git a/.gitattributes b/.gitattributes
index e2a6e5e0..eaae227f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -22,6 +22,7 @@
*.less text
*.sql text
*.properties text
+*.md text
# unix style
*.sh text eol=lf
@@ -50,26 +51,28 @@
*.bin binary
*.exe binary
-# 图片
+# images
*.png binary
*.jpg binary
*.ico binary
*.gif binary
-# 音视频
+# medias
*.mp3 binary
*.swf binary
-# 字体
+# fonts
*.eot binary
*.svg binary
*.ttf binary
*.woff binary
-# other doc
+# others
*.pdf binary
*.doc binary
*.docx binary
+*.ppt binary
+*.pptx binary
*.xls binary
*.xlsx binary
*.xmind binary
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 00000000..04010943
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,36 @@
+name: CI
+
+# 在master分支发生push事件时触发。
+on:
+ push:
+ branches:
+ - master
+
+env: # 设置环境变量
+ TZ: Asia/Shanghai # 时区(设置时区可使页面中的`最近更新时间`使用时区时间)
+
+jobs:
+ build: # 自定义名称
+ runs-on: ubuntu-latest # 运行在虚拟机环境ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [16.x]
+
+ steps:
+ # 使用的动作。格式:userName/repoName。作用:检出仓库,获取源码。 官方actions库:https://github.com/actions
+ - name: Checkout
+ uses: actions/checkout@master
+
+ # 指定 nodejs 版本
+ - name: Use Nodejs ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+
+ # 部署
+ - name: Deploy
+ env: # 设置环境变量
+ GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
+ GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }}
+ run: npm install && npm run deploy
diff --git a/.gitignore b/.gitignore
index cb2d5824..7d98dac9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,24 +1,47 @@
-################ JAVA ################
-# temp folders
+# ---------------------------------------------------------------------
+# more gitignore templates see https://github.com/github/gitignore
+# ---------------------------------------------------------------------
+
+# ------------------------------- java -------------------------------
+# compiled folders
classes
target
logs
-work
+.mtj.tmp/
-# temp files
+# compiled files
*.class
+
+# bluej files
+*.ctxt
+
+# package files #
*.jar
*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs
+hs_err_pid*
+
+# maven plugin temp files
+.flattened-pom.xml
-################ JAVASCRIPT ################
+# ------------------------------- javascript -------------------------------
# dependencies
node_modules
# temp folders
+build
dist
_book
_jsdoc
+.temp
+.deploy*/
# temp files
*.log
@@ -26,13 +49,18 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
bundle*.js
+.DS_Store
+Thumbs.db
+db.json
+book.pdf
+package-lock.json
-################ IDEA ################
+# ------------------------------- intellij -------------------------------
.idea
*.iml
-################ Eclipse ################
+# ------------------------------- eclipse -------------------------------
.classpath
.project
diff --git a/LICENSE b/LICENSE
index 261eeb9e..3b7b82d0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,201 +1,427 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- 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.
+Attribution-ShareAlike 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+ Considerations for licensors: Our public licenses are
+ intended for use by those authorized to give the public
+ permission to use material in ways otherwise restricted by
+ copyright and certain other rights. Our licenses are
+ irrevocable. Licensors should read and understand the terms
+ and conditions of the license they choose before applying it.
+ Licensors should also secure all rights necessary before
+ applying our licenses so that the public can reuse the
+ material as expected. Licensors should clearly mark any
+ material not subject to the license. This includes other CC-
+ licensed material, or material used under an exception or
+ limitation to copyright. More considerations for licensors:
+ wiki.creativecommons.org/Considerations_for_licensors
+
+ Considerations for the public: By using one of our public
+ licenses, a licensor grants the public permission to use the
+ licensed material under specified terms and conditions. If
+ the licensor's permission is not necessary for any reason--for
+ example, because of any applicable exception or limitation to
+ copyright--then that use is not regulated by the license. Our
+ licenses grant only permissions under copyright and certain
+ other rights that a licensor has authority to grant. Use of
+ the licensed material may still be restricted for other
+ reasons, including because others have copyright or other
+ rights in the material. A licensor may make special requests,
+ such as asking that all changes be marked or described.
+ Although not required by our licenses, you are encouraged to
+ respect those requests where reasonable. More_considerations
+ for the public:
+ wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-ShareAlike 4.0 International Public
+License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-ShareAlike 4.0 International Public License ("Public
+License"). To the extent this Public License may be interpreted as a
+contract, You are granted the Licensed Rights in consideration of Your
+acceptance of these terms and conditions, and the Licensor grants You
+such rights in consideration of benefits the Licensor receives from
+making the Licensed Material available under these terms and
+conditions.
+
+
+Section 1 -- Definitions.
+
+ a. Adapted Material means material subject to Copyright and Similar
+ Rights that is derived from or based upon the Licensed Material
+ and in which the Licensed Material is translated, altered,
+ arranged, transformed, or otherwise modified in a manner requiring
+ permission under the Copyright and Similar Rights held by the
+ Licensor. For purposes of this Public License, where the Licensed
+ Material is a musical work, performance, or sound recording,
+ Adapted Material is always produced where the Licensed Material is
+ synched in timed relation with a moving image.
+
+ b. Adapter's License means the license You apply to Your Copyright
+ and Similar Rights in Your contributions to Adapted Material in
+ accordance with the terms and conditions of this Public License.
+
+ c. BY-SA Compatible License means a license listed at
+ creativecommons.org/compatiblelicenses, approved by Creative
+ Commons as essentially the equivalent of this Public License.
+
+ d. Copyright and Similar Rights means copyright and/or similar rights
+ closely related to copyright including, without limitation,
+ performance, broadcast, sound recording, and Sui Generis Database
+ Rights, without regard to how the rights are labeled or
+ categorized. For purposes of this Public License, the rights
+ specified in Section 2(b)(1)-(2) are not Copyright and Similar
+ Rights.
+
+ e. Effective Technological Measures means those measures that, in the
+ absence of proper authority, may not be circumvented under laws
+ fulfilling obligations under Article 11 of the WIPO Copyright
+ Treaty adopted on December 20, 1996, and/or similar international
+ agreements.
+
+ f. Exceptions and Limitations means fair use, fair dealing, and/or
+ any other exception or limitation to Copyright and Similar Rights
+ that applies to Your use of the Licensed Material.
+
+ g. License Elements means the license attributes listed in the name
+ of a Creative Commons Public License. The License Elements of this
+ Public License are Attribution and ShareAlike.
+
+ h. Licensed Material means the artistic or literary work, database,
+ or other material to which the Licensor applied this Public
+ License.
+
+ i. Licensed Rights means the rights granted to You subject to the
+ terms and conditions of this Public License, which are limited to
+ all Copyright and Similar Rights that apply to Your use of the
+ Licensed Material and that the Licensor has authority to license.
+
+ j. Licensor means the individual(s) or entity(ies) granting rights
+ under this Public License.
+
+ k. Share means to provide material to the public by any means or
+ process that requires permission under the Licensed Rights, such
+ as reproduction, public display, public performance, distribution,
+ dissemination, communication, or importation, and to make material
+ available to the public including in ways that members of the
+ public may access the material from a place and at a time
+ individually chosen by them.
+
+ l. Sui Generis Database Rights means rights other than copyright
+ resulting from Directive 96/9/EC of the European Parliament and of
+ the Council of 11 March 1996 on the legal protection of databases,
+ as amended and/or succeeded, as well as other essentially
+ equivalent rights anywhere in the world.
+
+ m. You means the individual or entity exercising the Licensed Rights
+ under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+ a. License grant.
+
+ 1. Subject to the terms and conditions of this Public License,
+ the Licensor hereby grants You a worldwide, royalty-free,
+ non-sublicensable, non-exclusive, irrevocable license to
+ exercise the Licensed Rights in the Licensed Material to:
+
+ a. reproduce and Share the Licensed Material, in whole or
+ in part; and
+
+ b. produce, reproduce, and Share Adapted Material.
+
+ 2. Exceptions and Limitations. For the avoidance of doubt, where
+ Exceptions and Limitations apply to Your use, this Public
+ License does not apply, and You do not need to comply with
+ its terms and conditions.
+
+ 3. Term. The term of this Public License is specified in Section
+ 6(a).
+
+ 4. Media and formats; technical modifications allowed. The
+ Licensor authorizes You to exercise the Licensed Rights in
+ all media and formats whether now known or hereafter created,
+ and to make technical modifications necessary to do so. The
+ Licensor waives and/or agrees not to assert any right or
+ authority to forbid You from making technical modifications
+ necessary to exercise the Licensed Rights, including
+ technical modifications necessary to circumvent Effective
+ Technological Measures. For purposes of this Public License,
+ simply making modifications authorized by this Section 2(a)
+ (4) never produces Adapted Material.
+
+ 5. Downstream recipients.
+
+ a. Offer from the Licensor -- Licensed Material. Every
+ recipient of the Licensed Material automatically
+ receives an offer from the Licensor to exercise the
+ Licensed Rights under the terms and conditions of this
+ Public License.
+
+ b. Additional offer from the Licensor -- Adapted Material.
+ Every recipient of Adapted Material from You
+ automatically receives an offer from the Licensor to
+ exercise the Licensed Rights in the Adapted Material
+ under the conditions of the Adapter's License You apply.
+
+ c. No downstream restrictions. You may not offer or impose
+ any additional or different terms or conditions on, or
+ apply any Effective Technological Measures to, the
+ Licensed Material if doing so restricts exercise of the
+ Licensed Rights by any recipient of the Licensed
+ Material.
+
+ 6. No endorsement. Nothing in this Public License constitutes or
+ may be construed as permission to assert or imply that You
+ are, or that Your use of the Licensed Material is, connected
+ with, or sponsored, endorsed, or granted official status by,
+ the Licensor or others designated to receive attribution as
+ provided in Section 3(a)(1)(A)(i).
+
+ b. Other rights.
+
+ 1. Moral rights, such as the right of integrity, are not
+ licensed under this Public License, nor are publicity,
+ privacy, and/or other similar personality rights; however, to
+ the extent possible, the Licensor waives and/or agrees not to
+ assert any such rights held by the Licensor to the limited
+ extent necessary to allow You to exercise the Licensed
+ Rights, but not otherwise.
+
+ 2. Patent and trademark rights are not licensed under this
+ Public License.
+
+ 3. To the extent possible, the Licensor waives any right to
+ collect royalties from You for the exercise of the Licensed
+ Rights, whether directly or through a collecting society
+ under any voluntary or waivable statutory or compulsory
+ licensing scheme. In all other cases the Licensor expressly
+ reserves any right to collect such royalties.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+ a. Attribution.
+
+ 1. If You Share the Licensed Material (including in modified
+ form), You must:
+
+ a. retain the following if it is supplied by the Licensor
+ with the Licensed Material:
+
+ i. identification of the creator(s) of the Licensed
+ Material and any others designated to receive
+ attribution, in any reasonable manner requested by
+ the Licensor (including by pseudonym if
+ designated);
+
+ ii. a copyright notice;
+
+ iii. a notice that refers to this Public License;
+
+ iv. a notice that refers to the disclaimer of
+ warranties;
+
+ v. a URI or hyperlink to the Licensed Material to the
+ extent reasonably practicable;
+
+ b. indicate if You modified the Licensed Material and
+ retain an indication of any previous modifications; and
+
+ c. indicate the Licensed Material is licensed under this
+ Public License, and include the text of, or the URI or
+ hyperlink to, this Public License.
+
+ 2. You may satisfy the conditions in Section 3(a)(1) in any
+ reasonable manner based on the medium, means, and context in
+ which You Share the Licensed Material. For example, it may be
+ reasonable to satisfy the conditions by providing a URI or
+ hyperlink to a resource that includes the required
+ information.
+
+ 3. If requested by the Licensor, You must remove any of the
+ information required by Section 3(a)(1)(A) to the extent
+ reasonably practicable.
+
+ b. ShareAlike.
+
+ In addition to the conditions in Section 3(a), if You Share
+ Adapted Material You produce, the following conditions also apply.
+
+ 1. The Adapter's License You apply must be a Creative Commons
+ license with the same License Elements, this version or
+ later, or a BY-SA Compatible License.
+
+ 2. You must include the text of, or the URI or hyperlink to, the
+ Adapter's License You apply. You may satisfy this condition
+ in any reasonable manner based on the medium, means, and
+ context in which You Share Adapted Material.
+
+ 3. You may not offer or impose any additional or different terms
+ or conditions on, or apply any Effective Technological
+ Measures to, Adapted Material that restrict exercise of the
+ rights granted under the Adapter's License You apply.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+ a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+ to extract, reuse, reproduce, and Share all or a substantial
+ portion of the contents of the database;
+
+ b. if You include all or a substantial portion of the database
+ contents in a database in which You have Sui Generis Database
+ Rights, then the database in which You have Sui Generis Database
+ Rights (but not its individual contents) is Adapted Material,
+
+ including for purposes of Section 3(b); and
+ c. You must comply with the conditions in Section 3(a) if You Share
+ all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+ a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+ EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+ AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+ ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+ IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+ WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+ ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+ KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+ ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+ b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+ TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+ NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+ INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+ COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+ USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+ DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+ IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+ c. The disclaimer of warranties and limitation of liability provided
+ above shall be interpreted in a manner that, to the extent
+ possible, most closely approximates an absolute disclaimer and
+ waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+ a. This Public License applies for the term of the Copyright and
+ Similar Rights licensed here. However, if You fail to comply with
+ this Public License, then Your rights under this Public License
+ terminate automatically.
+
+ b. Where Your right to use the Licensed Material has terminated under
+ Section 6(a), it reinstates:
+
+ 1. automatically as of the date the violation is cured, provided
+ it is cured within 30 days of Your discovery of the
+ violation; or
+
+ 2. upon express reinstatement by the Licensor.
+
+ For the avoidance of doubt, this Section 6(b) does not affect any
+ right the Licensor may have to seek remedies for Your violations
+ of this Public License.
+
+ c. For the avoidance of doubt, the Licensor may also offer the
+ Licensed Material under separate terms or conditions or stop
+ distributing the Licensed Material at any time; however, doing so
+ will not terminate this Public License.
+
+ d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+ License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+ a. The Licensor shall not be bound by any additional or different
+ terms or conditions communicated by You unless expressly agreed.
+
+ b. Any arrangements, understandings, or agreements regarding the
+ Licensed Material not stated herein are separate from and
+ independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+ a. For the avoidance of doubt, this Public License does not, and
+ shall not be interpreted to, reduce, limit, restrict, or impose
+ conditions on any use of the Licensed Material that could lawfully
+ be made without permission under this Public License.
+
+ b. To the extent possible, if any provision of this Public License is
+ deemed unenforceable, it shall be automatically reformed to the
+ minimum extent necessary to make it enforceable. If the provision
+ cannot be reformed, it shall be severed from this Public License
+ without affecting the enforceability of the remaining terms and
+ conditions.
+
+ c. No term or condition of this Public License will be waived and no
+ failure to comply consented to unless expressly agreed to by the
+ Licensor.
+
+ d. Nothing in this Public License constitutes or may be interpreted
+ as a limitation upon, or waiver of, any privileges and immunities
+ that apply to the Licensor or You, including from the legal
+ processes of any jurisdiction or authority.
+
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the “Licensor.” The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
diff --git a/README.md b/README.md
index bde44267..7d3dbdd3 100644
--- a/README.md
+++ b/README.md
@@ -1,41 +1,253 @@
-# JavaStack
+
-| :one: | :two: | :three: | :four: | :five: | :six: |
-| :-----------------------: | :-----------------------: | :-----------------------: | :------------------------: | :--------------------------: | :-----------: |
-| [JavaCore](#one-javacore) | [JavaTool](#two-javatool) | [JavaWeb](#three-javaweb) | [Database](#four-database) | [Algorithm](#five-algorithm) | [OS](#six-os) |
+
+
+
-## :one: JavaCore
+
+
+
-> [JavaCore](https://github.com/dunwu/JavaCore) 是对 Java 核心技术的总结。
+
+
+
+
+
+
+
+
+
+
+ data = new ArrayList<>();
+
+ public void oom() {
+ data.add(IntStream.rangeClosed(1, 100000)
+ .mapToObj(__ -> "a")
+ .collect(Collectors.joining("")));
+ }
+
+}
diff --git a/codes/deadloop/src/main/java/io/github/dunwu/trouble/OOMApplication.java b/codes/deadloop/src/main/java/io/github/dunwu/trouble/OOMApplication.java
new file mode 100644
index 00000000..a39833ea
--- /dev/null
+++ b/codes/deadloop/src/main/java/io/github/dunwu/trouble/OOMApplication.java
@@ -0,0 +1,56 @@
+package io.github.dunwu.trouble;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import java.lang.management.ManagementFactory;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * 启动参数:
+ *
+ * java -verbose:gc -Xms128M -Xmx256M -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps
+ * -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -Dcom.sun.management.jmxremote=true
+ * -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
+ * -Djava.rmi.server.hostname=172.22.6.43 -Dcom.sun.management.jmxremote.port=18888 -Xdebug -Xnoagent
+ * -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=28888,server=y,suspend=n -jar deadloop.war
+ */
+@Slf4j
+@SpringBootApplication
+public class OOMApplication implements CommandLineRunner {
+
+ private final FooService fooService;
+
+ public OOMApplication(FooService fooService) {
+ this.fooService = fooService;
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(OOMApplication.class, args);
+ }
+
+ @Override
+ public void run(String... args) throws Exception {
+ log.info("VM options");
+ log.info(ManagementFactory.getRuntimeMXBean()
+ .getInputArguments()
+ .stream()
+ .collect(Collectors.joining(System.lineSeparator())));
+ log.info("Program arguments");
+ log.info(Arrays.stream(args).collect(Collectors.joining(System.lineSeparator())));
+
+ while (true) {
+ fooService.oom();
+ try {
+ TimeUnit.SECONDS.sleep(3);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/codes/deadloop/src/main/resources/application.properties b/codes/deadloop/src/main/resources/application.properties
new file mode 100644
index 00000000..8acb1a51
--- /dev/null
+++ b/codes/deadloop/src/main/resources/application.properties
@@ -0,0 +1,11 @@
+server.port = 8888
+#server.port = ${random.int[1024,65536]}
+spring.datasource.url = jdbc:mysql://172.22.6.9:3316/trouble_shooting?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
+#spring.datasource.url = jdbc:mysql://172.22.6.9:3316/trouble_shooting?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
+spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
+spring.datasource.username = root
+spring.datasource.password = 604330436
+# 强制每次启动使用 sql 初始化数据,本项目仅为了演示方便,真实环境应避免这种模式
+spring.datasource.initialization-mode = ALWAYS
+spring.datasource.schema = classpath:sql/schema.sql
+spring.datasource.data = classpath:sql/data.sql
diff --git a/codes/deadloop/src/main/resources/banner.txt b/codes/deadloop/src/main/resources/banner.txt
new file mode 100644
index 00000000..449413d5
--- /dev/null
+++ b/codes/deadloop/src/main/resources/banner.txt
@@ -0,0 +1,12 @@
+${AnsiColor.BRIGHT_YELLOW}${AnsiStyle.BOLD}
+ ________ ___ ___ ________ ___ __ ___ ___
+|\ ___ \|\ \|\ \|\ ___ \|\ \ |\ \|\ \|\ \
+\ \ \_|\ \ \ \\\ \ \ \\ \ \ \ \ \ \ \ \ \\\ \
+ \ \ \ \\ \ \ \\\ \ \ \\ \ \ \ \ __\ \ \ \ \\\ \
+ \ \ \_\\ \ \ \\\ \ \ \\ \ \ \ \|\__\_\ \ \ \\\ \
+ \ \_______\ \_______\ \__\\ \__\ \____________\ \_______\
+ \|_______|\|_______|\|__| \|__|\|____________|\|_______|
+${AnsiColor.CYAN}${AnsiStyle.BOLD}
+:: Java :: (v${java.version})
+:: Spring Boot :: (v${spring-boot.version})
+${AnsiStyle.NORMAL}
diff --git a/codes/deadloop/src/main/resources/logback.xml b/codes/deadloop/src/main/resources/logback.xml
new file mode 100644
index 00000000..7667f240
--- /dev/null
+++ b/codes/deadloop/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%boldYellow(%thread)] [%highlight(%-5level)] %boldGreen(%c{36}.%M) - %boldBlue(%m%n)
+
+
+
+
+
+
+
+
diff --git a/codes/deadloop/src/main/resources/sql/data.sql b/codes/deadloop/src/main/resources/sql/data.sql
new file mode 100644
index 00000000..733abe41
--- /dev/null
+++ b/codes/deadloop/src/main/resources/sql/data.sql
@@ -0,0 +1,11 @@
+-- -------------------------------------------------------------------
+-- 运行本项目的初始化 DML 脚本
+-- Mysql 知识点可以参考:
+-- https://dunwu.github.io/db-tutorial/#/sql/mysql/README
+-- -------------------------------------------------------------------
+
+# INSERT INTO user (username, password, email)
+# VALUES ('admin', '$2a$10$Y9uV9YjFuNlATDGz5MeTZeuo8LbebbpP6jRgtZYQcgiCZRlf8rJYG', 'admin@xxx.com');
+#
+# INSERT INTO user (username, password, email)
+# VALUES ('user', '$2a$10$Y9uV9YjFuNlATDGz5MeTZeuo8LbebbpP6jRgtZYQcgiCZRlf8rJYG', 'user@xxx.com');
diff --git a/codes/deadloop/src/main/resources/sql/schema.sql b/codes/deadloop/src/main/resources/sql/schema.sql
new file mode 100644
index 00000000..13e82ae2
--- /dev/null
+++ b/codes/deadloop/src/main/resources/sql/schema.sql
@@ -0,0 +1,15 @@
+-- -------------------------------------------------------------------
+-- 运行本项目的初始化 DDL 脚本
+-- Mysql 知识点可以参考:
+-- https://dunwu.github.io/db-tutorial/#/sql/mysql/README
+-- -------------------------------------------------------------------
+
+-- 强制新建用户表
+DROP TABLE IF EXISTS `testuser`;
+CREATE TABLE `testuser` (
+ `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+ `name` VARCHAR(255) NOT NULL,
+ PRIMARY KEY (`id`)
+)
+ ENGINE = InnoDB
+ DEFAULT CHARSET = utf8mb4;
diff --git a/codes/java-distributed/README.md b/codes/java-distributed/README.md
new file mode 100644
index 00000000..a96b29fe
--- /dev/null
+++ b/codes/java-distributed/README.md
@@ -0,0 +1,3 @@
+# Java 和分布式
+
+> 分布式系统中常用算法的 Java 实现方案
diff --git a/codes/java-distributed/java-distributed-id/pom.xml b/codes/java-distributed/java-distributed-id/pom.xml
new file mode 100644
index 00000000..5f72fc0d
--- /dev/null
+++ b/codes/java-distributed/java-distributed-id/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+
+
+ io.github.dunwu.distributed
+ java-distributed
+ 1.0.0
+
+
+ io.github.dunwu.javatech
+ java-distributed-id
+ 1.0.0
+ jar
+ ${project.artifactId}
+
+
+ UTF-8
+ 1.8
+ ${java.version}
+ ${java.version}
+
+
+
+
+ org.apache.zookeeper
+ zookeeper
+
+
+ org.apache.curator
+ curator-recipes
+
+
+ redis.clients
+ jedis
+
+
+ cn.hutool
+ hutool-all
+
+
+ org.projectlombok
+ lombok
+
+
+ ch.qos.logback
+ logback-classic
+ true
+
+
+
diff --git a/codes/java-distributed/java-distributed-id/src/main/java/io/github/dunwu/distributed/id/ZookeeperDistributedId.java b/codes/java-distributed/java-distributed-id/src/main/java/io/github/dunwu/distributed/id/ZookeeperDistributedId.java
new file mode 100644
index 00000000..06d7092c
--- /dev/null
+++ b/codes/java-distributed/java-distributed-id/src/main/java/io/github/dunwu/distributed/id/ZookeeperDistributedId.java
@@ -0,0 +1,55 @@
+package io.github.dunwu.distributed.id;
+
+import cn.hutool.core.collection.CollectionUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.curator.RetryPolicy;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.zookeeper.CreateMode;
+
+import java.util.List;
+
+/**
+ * ZK 分布式 ID
+ *
+ * @author Zhang Peng
+ * @date 2024-12-20
+ */
+@Slf4j
+public class ZookeeperDistributedId {
+
+ public static void main(String[] args) throws Exception {
+
+ // 获取客户端
+ RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
+ CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", retryPolicy);
+
+ // 开启会话
+ client.start();
+
+ String id1 = client.create()
+ .creatingParentsIfNeeded()
+ .withMode(CreateMode.PERSISTENT_SEQUENTIAL)
+ .forPath("/zkid/id_");
+ log.info("id: {}", id1);
+
+ String id2 = client.create()
+ .creatingParentsIfNeeded()
+ .withMode(CreateMode.PERSISTENT_SEQUENTIAL)
+ .forPath("/zkid/id_");
+ log.info("id: {}", id2);
+
+ List children = client.getChildren().forPath("/zkid");
+ if (CollectionUtil.isNotEmpty(children)) {
+ for (String child : children) {
+ client.delete().forPath("/zkid/" + child);
+ }
+ }
+ client.delete().forPath("/zkid");
+
+ // 关闭客户端
+ client.close();
+ }
+
+}
diff --git a/codes/java-distributed/java-distributed-id/src/main/java/io/github/dunwu/distributed/id/ZookeeperDistributedId2.java b/codes/java-distributed/java-distributed-id/src/main/java/io/github/dunwu/distributed/id/ZookeeperDistributedId2.java
new file mode 100644
index 00000000..69d5d543
--- /dev/null
+++ b/codes/java-distributed/java-distributed-id/src/main/java/io/github/dunwu/distributed/id/ZookeeperDistributedId2.java
@@ -0,0 +1,46 @@
+package io.github.dunwu.distributed.id;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.curator.RetryPolicy;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.recipes.atomic.AtomicValue;
+import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+
+/**
+ * ZK 分布式 ID
+ *
+ * 基于原子计数器生成 ID
+ *
+ * @author Zhang Peng
+ * @date 2024-12-20
+ */
+@Slf4j
+public class ZookeeperDistributedId2 {
+
+ public static void main(String[] args) throws Exception {
+
+ // 获取客户端
+ RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
+ CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", retryPolicy);
+ DistributedAtomicLong atomicLong = new DistributedAtomicLong(client, "/zkid", retryPolicy);
+
+ // 开启会话
+ client.start();
+
+ // 基于原子计数器生成 ID
+ AtomicValue id1 = atomicLong.increment();
+ log.info("id: {}", id1.postValue());
+
+ AtomicValue id2 = atomicLong.increment();
+ log.info("id: {}", id2.postValue());
+
+ // 清理节点
+ client.delete().forPath("/zkid");
+
+ // 关闭客户端
+ client.close();
+ }
+
+}
diff --git a/codes/java-distributed/java-distributed-id/src/main/resources/scripts/fixed_window_rate_limit.lua b/codes/java-distributed/java-distributed-id/src/main/resources/scripts/fixed_window_rate_limit.lua
new file mode 100644
index 00000000..e0c9ad00
--- /dev/null
+++ b/codes/java-distributed/java-distributed-id/src/main/resources/scripts/fixed_window_rate_limit.lua
@@ -0,0 +1,21 @@
+-- 缓存 Key
+local key = KEYS[1]
+-- 访问请求数
+local permits = tonumber(ARGV[1])
+-- 过期时间
+local seconds = tonumber(ARGV[2])
+-- 限流阈值
+local limit = tonumber(ARGV[3])
+
+-- 获取统计值
+local count = tonumber(redis.call('GET', key) or "0")
+
+if count + permits > limit then
+ -- 请求拒绝
+ return -1
+else
+ -- 请求通过
+ redis.call('INCRBY', key, permits)
+ redis.call('EXPIRE', key, seconds)
+ return count + permits
+end
\ No newline at end of file
diff --git a/codes/java-distributed/java-distributed-id/src/main/resources/scripts/token_bucket_rate_limit.lua b/codes/java-distributed/java-distributed-id/src/main/resources/scripts/token_bucket_rate_limit.lua
new file mode 100644
index 00000000..541d70c9
--- /dev/null
+++ b/codes/java-distributed/java-distributed-id/src/main/resources/scripts/token_bucket_rate_limit.lua
@@ -0,0 +1,39 @@
+local tokenKey = KEYS[1]
+local timeKey = KEYS[2]
+
+-- 申请令牌数
+local permits = tonumber(ARGV[1])
+-- QPS
+local qps = tonumber(ARGV[2])
+-- 桶的容量
+local capacity = tonumber(ARGV[3])
+-- 当前时间(单位:毫秒)
+local nowMillis = tonumber(ARGV[4])
+-- 填满令牌桶所需要的时间
+local fillTime = capacity / qps
+local ttl = math.min(capacity, math.floor(fillTime * 2))
+
+local currentTokenNum = tonumber(redis.call("GET", tokenKey))
+if currentTokenNum == nil then
+ currentTokenNum = capacity
+end
+
+local endTimeMillis = tonumber(redis.call("GET", timeKey))
+if endTimeMillis == nil then
+ endTimeMillis = 0
+end
+
+local gap = nowMillis - endTimeMillis
+local newTokenNum = math.max(0, gap * qps / 1000)
+local currentTokenNum = math.min(capacity, currentTokenNum + newTokenNum)
+
+if currentTokenNum < permits then
+ -- 请求拒绝
+ return -1
+else
+ -- 请求通过
+ local finalTokenNum = currentTokenNum - permits
+ redis.call("SETEX", tokenKey, ttl, finalTokenNum)
+ redis.call("SETEX", timeKey, ttl, nowMillis)
+ return finalTokenNum
+end
diff --git a/codes/java-distributed/java-load-balance/pom.xml b/codes/java-distributed/java-load-balance/pom.xml
new file mode 100644
index 00000000..b4e73f0f
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/pom.xml
@@ -0,0 +1,36 @@
+
+
+ 4.0.0
+
+ io.github.dunwu.distributed
+ java-load-balance
+ 1.0.0
+ jar
+
+
+ UTF-8
+ 1.8
+ ${java.version}
+ ${java.version}
+
+
+
+
+ cn.hutool
+ hutool-all
+ 5.4.1
+
+
+ org.projectlombok
+ lombok
+ 1.18.12
+
+
+ junit
+ junit
+ 4.13
+ test
+
+
+
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/BaseLoadBalance.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/BaseLoadBalance.java
new file mode 100644
index 00000000..ebb317a8
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/BaseLoadBalance.java
@@ -0,0 +1,37 @@
+package io.github.dunwu.distributed;
+
+import cn.hutool.core.collection.CollectionUtil;
+
+import java.util.List;
+
+/**
+ * @author Zhang Peng
+ * @since 2021-01-18
+ */
+public abstract class BaseLoadBalance implements LoadBalance {
+
+ @Override
+ public N select(List nodes, String ip) {
+ // nodes 列表为空,返回 null
+ if (CollectionUtil.isEmpty(nodes)) {
+ return null;
+ }
+
+ // 如果 nodes 列表中仅有一个 node,直接返回即可
+ if (nodes.size() == 1) {
+ return nodes.get(0);
+ }
+
+ return doSelect(nodes, ip);
+ }
+
+ /**
+ * 负载均衡算法抽象方法,各个算法需要自行实现
+ *
+ * @param nodes 节点列表
+ * @param ip 请求方 IP
+ * @return 被选中的节点
+ */
+ protected abstract N doSelect(List nodes, String ip);
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/ConsistentHashLoadBalance.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/ConsistentHashLoadBalance.java
new file mode 100644
index 00000000..c67558a2
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/ConsistentHashLoadBalance.java
@@ -0,0 +1,129 @@
+package io.github.dunwu.distributed;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class ConsistentHashLoadBalance extends BaseLoadBalance implements LoadBalance {
+
+ private final ConcurrentMap> selectors = new ConcurrentHashMap<>();
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected N doSelect(List nodes, String ip) {
+ // 分片数,这里设为节点数的 4 倍
+ Integer replicaNum = nodes.size() * 4;
+ // 获取 nodes 原始的 hashcode
+ int identityHashCode = System.identityHashCode(nodes);
+
+ // 如果 nodes 是一个新的 List 对象,意味着节点数量发生了变化
+ // 此时 selector.identityHashCode != identityHashCode 条件成立
+ ConsistentHashSelector selector = (ConsistentHashSelector) selectors.get(ip);
+ if (selector == null || selector.identityHashCode != identityHashCode) {
+ // 创建新的 ConsistentHashSelector
+ selectors.put(ip, new ConsistentHashSelector<>(nodes, identityHashCode, replicaNum));
+ selector = (ConsistentHashSelector) selectors.get(ip);
+ }
+ // 调用 ConsistentHashSelector 的 select 方法选择 Node
+ return selector.select(ip);
+ }
+
+ /**
+ * 一致性哈希选择器
+ */
+ private static final class ConsistentHashSelector {
+
+ /**
+ * 存储虚拟节点
+ */
+ private final TreeMap virtualNodes;
+
+ private final int identityHashCode;
+
+ /**
+ * 构造器
+ *
+ * @param nodes 节点列表
+ * @param identityHashCode hashcode
+ * @param replicaNum 分片数
+ */
+ ConsistentHashSelector(List nodes, int identityHashCode, Integer replicaNum) {
+ this.virtualNodes = new TreeMap<>();
+ this.identityHashCode = identityHashCode;
+ // 获取虚拟节点数,默认为 100
+ if (replicaNum == null) {
+ replicaNum = 100;
+ }
+ for (N node : nodes) {
+ for (int i = 0; i < replicaNum / 4; i++) {
+ // 对 url 进行 md5 运算,得到一个长度为16的字节数组
+ byte[] digest = md5(node.getUrl());
+ // 对 digest 部分字节进行 4 次 hash 运算,得到四个不同的 long 型正整数
+ for (int j = 0; j < 4; j++) {
+ // h = 0 时,取 digest 中下标为 0 ~ 3 的4个字节进行位运算
+ // h = 1 时,取 digest 中下标为 4 ~ 7 的4个字节进行位运算
+ // h = 2, h = 3 时过程同上
+ long m = hash(digest, j);
+ // 将 hash 到 node 的映射关系存储到 virtualNodes 中,
+ // virtualNodes 需要提供高效的查询操作,因此选用 TreeMap 作为存储结构
+ virtualNodes.put(m, node);
+ }
+ }
+ }
+ }
+
+ public N select(String key) {
+ // 对参数 key 进行 md5 运算
+ byte[] digest = md5(key);
+ // 取 digest 数组的前四个字节进行 hash 运算,再将 hash 值传给 selectForKey 方法,
+ // 寻找合适的 Node
+ return selectForKey(hash(digest, 0));
+ }
+
+ private N selectForKey(long hash) {
+ // 查找第一个大于或等于当前 hash 的节点
+ Map.Entry entry = virtualNodes.ceilingEntry(hash);
+ // 如果 hash 大于 Node 在哈希环上最大的位置,此时 entry = null,
+ // 需要将 TreeMap 的头节点赋值给 entry
+ if (entry == null) {
+ entry = virtualNodes.firstEntry();
+ }
+ // 返回 Node
+ return entry.getValue();
+ }
+
+ }
+
+ /**
+ * 计算 hash 值
+ */
+ public static long hash(byte[] digest, int number) {
+ return (((long) (digest[3 + number * 4] & 0xFF) << 24)
+ | ((long) (digest[2 + number * 4] & 0xFF) << 16)
+ | ((long) (digest[1 + number * 4] & 0xFF) << 8)
+ | (digest[number * 4] & 0xFF))
+ & 0xFFFFFFFFL;
+ }
+
+ /**
+ * 计算 MD5 值
+ */
+ public static byte[] md5(String value) {
+ MessageDigest md5;
+ try {
+ md5 = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ md5.reset();
+ byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
+ md5.update(bytes);
+ return md5.digest();
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/IpHashLoadBalance.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/IpHashLoadBalance.java
new file mode 100644
index 00000000..3d71cbb7
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/IpHashLoadBalance.java
@@ -0,0 +1,29 @@
+package io.github.dunwu.distributed;
+
+import cn.hutool.core.util.HashUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.util.List;
+
+/**
+ * @author peng.zhang
+ * @date 2021/1/19
+ */
+public class IpHashLoadBalance extends BaseLoadBalance implements LoadBalance {
+
+ @Override
+ protected N doSelect(List nodes, String ip) {
+ if (StrUtil.isBlank(ip)) {
+ ip = "127.0.0.1";
+ }
+
+ int length = nodes.size();
+ int index = hash(ip) % length;
+ return nodes.get(index);
+ }
+
+ public int hash(String text) {
+ return HashUtil.fnvHash(text);
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/LeastActiveLoadBalance.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/LeastActiveLoadBalance.java
new file mode 100644
index 00000000..23a7f03c
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/LeastActiveLoadBalance.java
@@ -0,0 +1,83 @@
+package io.github.dunwu.distributed;
+
+import java.util.List;
+import java.util.Random;
+
+/**
+ * @author peng.zhang
+ * @date 2021/1/18
+ */
+public class LeastActiveLoadBalance extends BaseLoadBalance implements LoadBalance {
+
+ private final Random random = new Random();
+
+ @Override
+ protected N doSelect(List nodes, String ip) {
+ int length = nodes.size();
+ // 最小的活跃数
+ int leastActive = -1;
+ // 具有相同“最小活跃数”的服务者提供者(以下用 Node 代称)数量
+ int leastCount = 0;
+ // leastIndexs 用于记录具有相同“最小活跃数”的 Node 在 nodes 列表中的下标信息
+ int[] leastIndexs = new int[length];
+ int totalWeight = 0;
+ // 第一个最小活跃数的 Node 权重值,用于与其他具有相同最小活跃数的 Node 的权重进行对比,
+ // 以检测是否“所有具有相同最小活跃数的 Node 的权重”均相等
+ int firstWeight = 0;
+ boolean sameWeight = true;
+
+ // 遍历 nodes 列表
+ for (int i = 0; i < length; i++) {
+ N node = nodes.get(i);
+ // 发现更小的活跃数,重新开始
+ if (leastActive == -1 || node.getActive() < leastActive) {
+ // 使用当前活跃数更新最小活跃数 leastActive
+ leastActive = node.getActive();
+ // 更新 leastCount 为 1
+ leastCount = 1;
+ // 记录当前下标值到 leastIndexs 中
+ leastIndexs[0] = i;
+ totalWeight = node.getWeight();
+ firstWeight = node.getWeight();
+ sameWeight = true;
+
+ // 当前 Node 的活跃数 node.getActive() 与最小活跃数 leastActive 相同
+ } else if (node.getActive() == leastActive) {
+ // 在 leastIndexs 中记录下当前 Node 在 nodes 集合中的下标
+ leastIndexs[leastCount++] = i;
+ // 累加权重
+ totalWeight += node.getWeight();
+ // 检测当前 Node 的权重与 firstWeight 是否相等,
+ // 不相等则将 sameWeight 置为 false
+ if (sameWeight && i > 0
+ && node.getWeight() != firstWeight) {
+ sameWeight = false;
+ }
+ }
+ }
+
+ // 当只有一个 Node 具有最小活跃数,此时直接返回该 Node 即可
+ if (leastCount == 1) {
+ return nodes.get(leastIndexs[0]);
+ }
+
+ // 有多个 Node 具有相同的最小活跃数,但它们之间的权重不同
+ if (!sameWeight && totalWeight > 0) {
+ // 随机生成一个 [0, totalWeight) 之间的数字
+ int offsetWeight = random.nextInt(totalWeight);
+ // 循环让随机数减去具有最小活跃数的 Node 的权重值,
+ // 当 offset 小于等于0时,返回相应的 Node
+ for (int i = 0; i < leastCount; i++) {
+ int leastIndex = leastIndexs[i];
+ // 获取权重值,并让随机数减去权重值
+ offsetWeight -= nodes.get(leastIndex).getWeight();
+ if (offsetWeight <= 0) {
+ return nodes.get(leastIndex);
+ }
+ }
+ }
+ // 如果权重相同或权重为0时,随机返回一个 Node
+ return nodes.get(leastIndexs[random.nextInt(leastCount)]);
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/LoadBalance.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/LoadBalance.java
new file mode 100644
index 00000000..f2d0c561
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/LoadBalance.java
@@ -0,0 +1,15 @@
+package io.github.dunwu.distributed;
+
+import java.util.List;
+
+/**
+ * 负载均衡策略接口
+ *
+ * @author Zhang Peng
+ * @since 2020-01-21
+ */
+public interface LoadBalance {
+
+ N select(List nodes, String ip);
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/LoadBalanceDemo.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/LoadBalanceDemo.java
new file mode 100644
index 00000000..57dc61f1
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/LoadBalanceDemo.java
@@ -0,0 +1,146 @@
+package io.github.dunwu.distributed;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.TreeMap;
+
+/**
+ * 负载均衡算法测试例
+ *
+ * @author peng.zhang
+ * @date 2021/1/19
+ */
+public class LoadBalanceDemo {
+
+ private static final Random random = new Random();
+
+ public static String randomIpv4() {
+ int[][] range = { { 607649792, 608174079 }, // 36.56.0.0-36.63.255.255
+ { 1038614528, 1039007743 }, // 61.232.0.0-61.237.255.255
+ { 1783627776, 1784676351 }, // 106.80.0.0-106.95.255.255
+ { 2035023872, 2035154943 }, // 121.76.0.0-121.77.255.255
+ { 2078801920, 2079064063 }, // 123.232.0.0-123.235.255.255
+ { -1950089216, -1948778497 }, // 139.196.0.0-139.215.255.255
+ { -1425539072, -1425014785 }, // 171.8.0.0-171.15.255.255
+ { -1236271104, -1235419137 }, // 182.80.0.0-182.92.255.255
+ { -770113536, -768606209 }, // 210.25.0.0-210.47.255.255
+ { -569376768, -564133889 }, // 222.16.0.0-222.95.255.255
+ };
+
+ Random rdint = new Random();
+ int index = rdint.nextInt(10);
+ String ip = num2ip(range[index][0]
+ + new Random().nextInt(range[index][1] - range[index][0]));
+ return ip;
+ }
+
+ private static String num2ip(final int ip) {
+ int[] b = new int[4];
+ String result = "";
+ b[0] = (ip >> 24) & 0xff;
+ b[1] = ((ip >> 16) & 0xff);
+ b[2] = ((ip >> 8) & 0xff);
+ b[3] = (ip & 0xff);
+ result = Integer.toString(b[0]) + "." + Integer.toString(b[1]) + "."
+ + Integer.toString(b[2]) + "." + Integer.toString(b[3]);
+ return result;
+ }
+
+ /**
+ * 生成 num 个随机 IP 地址
+ */
+ private static List initRandomIpList(int num) {
+ List list = new ArrayList<>();
+ for (int i = 1; i <= num; i++) {
+ list.add(randomIpv4());
+ }
+ return list;
+ }
+
+ /**
+ * 生成 num 个样本节点
+ *
+ * @param num 节点数
+ * @param sameWeight 各节点权重是否相同
+ * @param sameActive 各节点活跃数是否相同
+ */
+ private static List initNodeList(Integer num, boolean sameWeight, boolean sameActive) {
+
+ List nodes = new ArrayList<>();
+ for (int i = 1; i <= num; i++) {
+ Node node = new Node("192.168.0." + i);
+ if (!sameWeight) {
+ node.setWeight(random.nextInt(10));
+ }
+ if (!sameActive) {
+ node.setActive(random.nextInt(10));
+ }
+ nodes.add(node);
+ }
+ return nodes;
+ }
+
+ /**
+ * 统计负载均衡命中次数,样本数为 10000 次访问
+ */
+ private static Map loadBalance10000(LoadBalance algorithm, List nodes,
+ List ipList) {
+ Map staticMap = new TreeMap<>();
+
+ int ipLength = ipList.size();
+ for (int i = 0; i < 10000; i++) {
+ String ip = ipList.get(random.nextInt(ipLength));
+ Node node = algorithm.select(nodes, ip);
+ // 打印每一次负载均衡的选择结果
+ // System.out.println(StrUtil.format("ip = {}, node url = {}", ip, node.getUrl()));
+ if (staticMap.containsKey(node)) {
+ Long value = staticMap.get(node);
+ staticMap.put(node, ++value);
+ } else {
+ staticMap.put(node, 1L);
+ }
+ }
+
+ System.out.println("======================= 统计数据 =======================");
+ staticMap.forEach((key, value) -> {
+ System.out.printf("key = %s, value = %s\n", key, value);
+ });
+ System.out.printf("方差:%s, ", StatisticsUtil.variance(staticMap.values().toArray(new Long[0])));
+ System.out.printf("标准差:%s\n", StatisticsUtil.standardDeviation(staticMap.values().toArray(new Long[] {})));
+ return staticMap;
+ }
+
+ public static void main(String[] args) {
+ // 构造 100 个候选服务器节点
+ List nodes = initNodeList(100, false, false);
+ // 构造 100 个随机IP
+ List ipList = initRandomIpList(100);
+
+ // ============================================================================
+ // 基于以上构造数据,对每种算法都 负载均衡选择 10000 次,然后统计方差、标准差,查看负载均衡效果。
+
+ System.out.println("======================= 随机负载均衡 =======================");
+ loadBalance10000(new RandomLoadBalance<>(), nodes, ipList);
+
+ System.out.println("======================= 加权随机负载均衡 =======================");
+ loadBalance10000(new WeightRandomLoadBalance<>(), nodes, ipList);
+
+ System.out.println("======================= 轮询负载均衡 =======================");
+ loadBalance10000(new RoundRobinLoadBalance<>(), nodes, ipList);
+
+ System.out.println("======================= 加权轮询负载均衡 =======================");
+ loadBalance10000(new WeightRoundRobinLoadBalance<>(), nodes, ipList);
+
+ System.out.println("======================= 源地址哈希负载均衡 =======================");
+ loadBalance10000(new IpHashLoadBalance<>(), nodes, ipList);
+
+ System.out.println("======================= 最小活跃数负载均衡 =======================");
+ loadBalance10000(new LeastActiveLoadBalance<>(), nodes, ipList);
+
+ System.out.println("======================= 一致性哈希负载均衡 =======================");
+ loadBalance10000(new ConsistentHashLoadBalance<>(), nodes, ipList);
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/Node.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/Node.java
new file mode 100644
index 00000000..2fc9c712
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/Node.java
@@ -0,0 +1,87 @@
+package io.github.dunwu.distributed;
+
+import java.util.Objects;
+
+/**
+ * 负载均衡节点
+ *
+ * @author Zhang Peng
+ * @since 2020-01-23
+ */
+public class Node implements Comparable {
+
+ public static final Integer DEFAULT_WEIGHT = 1;
+ public static final Integer DEFAULT_ACTIVE = 0;
+
+ protected String url;
+
+ protected Integer weight;
+
+ protected Integer active;
+
+ public Node(String url) {
+ this(url, DEFAULT_WEIGHT, DEFAULT_ACTIVE);
+ }
+
+ public Node(String url, Integer weight, Integer active) {
+ this.url = url;
+ this.weight = weight;
+ this.active = active;
+ }
+
+ @Override
+ public int compareTo(Node o) {
+ return url.compareTo(o.url);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Node)) {
+ return false;
+ }
+ Node node = (Node) o;
+ return url.equals(node.url);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(url);
+ }
+
+ @Override
+ public String toString() {
+ return "Node{" +
+ "url='" + url + '\'' +
+ ", weight=" + weight +
+ ", active=" + active +
+ '}';
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public Integer getWeight() {
+ return weight;
+ }
+
+ public void setWeight(Integer weight) {
+ this.weight = weight;
+ }
+
+ public Integer getActive() {
+ return active;
+ }
+
+ public void setActive(Integer active) {
+ this.active = active;
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/RandomLoadBalance.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/RandomLoadBalance.java
new file mode 100644
index 00000000..5b775dd2
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/RandomLoadBalance.java
@@ -0,0 +1,23 @@
+package io.github.dunwu.distributed;
+
+import java.util.List;
+import java.util.Random;
+
+/**
+ * (加权)随机负载均衡策略
+ *
+ * @author Zhang Peng
+ * @see Zhang Peng
+ * @since 2020-01-20
+ */
+public class RandomLoadBalance extends BaseLoadBalance implements LoadBalance {
+
+ private final Random random = new Random();
+
+ @Override
+ protected N doSelect(List nodes, String ip) {
+ int index = random.nextInt(nodes.size());
+ return nodes.get(index);
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/RoundRobinLoadBalance.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/RoundRobinLoadBalance.java
new file mode 100644
index 00000000..c0858152
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/RoundRobinLoadBalance.java
@@ -0,0 +1,26 @@
+package io.github.dunwu.distributed;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * (加权)轮询负载均衡策略
+ *
+ * @author Zhang Peng
+ * @since 2020-01-20
+ */
+public class RoundRobinLoadBalance extends BaseLoadBalance implements LoadBalance {
+
+ private final AtomicInteger position = new AtomicInteger(0);
+
+ @Override
+ protected N doSelect(List nodes, String ip) {
+ int length = nodes.size();
+ // 如果位置值已经等于节点数,重置为 0
+ position.compareAndSet(length, 0);
+ N node = nodes.get(position.get());
+ position.getAndIncrement();
+ return node;
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/StatisticsUtil.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/StatisticsUtil.java
new file mode 100644
index 00000000..cbb66d13
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/StatisticsUtil.java
@@ -0,0 +1,35 @@
+package io.github.dunwu.distributed;
+
+public class StatisticsUtil {
+
+ private StatisticsUtil() {}
+
+ /**
+ * 方差计算
+ * 公式:s^2 = [(x1-x)^2 +...(xn-x)^2]/n
+ */
+ public static double variance(Long[] array) {
+ int m = array.length;
+ double sum = 0;
+ for (Long item : array) {// 求和
+ sum += item;
+ }
+ double avg = sum / m;// 求平均值
+ double value = 0;
+ for (Long item : array) {// 求方差
+ value += (item - avg) * (item - avg);
+ }
+ return value / m;
+ }
+
+ /**
+ * 标准差
+ * 公式 result = sqrt(s^2),即 sqrt(variance(array))
+ */
+ public static double standardDeviation(Long[] array) {
+ int m = array.length;
+ double value = variance(array);
+ return Math.sqrt(value / m);
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/WeightRandomLoadBalance.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/WeightRandomLoadBalance.java
new file mode 100644
index 00000000..c7135667
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/WeightRandomLoadBalance.java
@@ -0,0 +1,42 @@
+package io.github.dunwu.distributed;
+
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author peng.zhang
+ * @date 2021/1/19
+ */
+public class WeightRandomLoadBalance extends BaseLoadBalance implements LoadBalance {
+
+ private final Random random = ThreadLocalRandom.current();
+
+ @Override
+ protected N doSelect(List nodes, String ip) {
+
+ int length = nodes.size();
+ AtomicInteger totalWeight = new AtomicInteger(0);
+ for (N node : nodes) {
+ Integer weight = node.getWeight();
+ totalWeight.getAndAdd(weight);
+ }
+
+ if (totalWeight.get() > 0) {
+ int offset = random.nextInt(totalWeight.get());
+ for (N node : nodes) {
+ // 让随机值 offset 减去权重值
+ offset -= node.getWeight();
+ if (offset < 0) {
+ // 返回相应的 Node
+ return node;
+ }
+ }
+ }
+
+ // 直接随机返回一个
+ return nodes.get(random.nextInt(length));
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/WeightRoundRobinLoadBalance.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/WeightRoundRobinLoadBalance.java
new file mode 100644
index 00000000..3f71a573
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/WeightRoundRobinLoadBalance.java
@@ -0,0 +1,160 @@
+package io.github.dunwu.distributed;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author peng.zhang
+ * @date 2021/1/19
+ */
+public class WeightRoundRobinLoadBalance extends BaseLoadBalance implements LoadBalance {
+
+ /**
+ * 60秒
+ */
+ private static final int RECYCLE_PERIOD = 60000;
+
+ /**
+ * Node hashcode 到 WeightedRoundRobin 的映射关系
+ */
+ private ConcurrentMap weightMap = new ConcurrentHashMap<>();
+
+ /**
+ * 原子更新锁
+ */
+ private AtomicBoolean updateLock = new AtomicBoolean();
+
+ @Override
+ protected N doSelect(List nodes, String ip) {
+
+ int totalWeight = 0;
+ long maxCurrent = Long.MIN_VALUE;
+
+ // 获取当前时间
+ long now = System.currentTimeMillis();
+ N selectedNode = null;
+ WeightedRoundRobin selectedWRR = null;
+
+ // 下面这个循环主要做了这样几件事情:
+ // 1. 遍历 Node 列表,检测当前 Node 是否有相应的 WeightedRoundRobin,没有则创建
+ // 2. 检测 Node 权重是否发生了变化,若变化了,则更新 WeightedRoundRobin 的 weight 字段
+ // 3. 让 current 字段加上自身权重,等价于 current += weight
+ // 4. 设置 lastUpdate 字段,即 lastUpdate = now
+ // 5. 寻找具有最大 current 的 Node,以及 Node 对应的 WeightedRoundRobin,
+ // 暂存起来,留作后用
+ // 6. 计算权重总和
+ for (N node : nodes) {
+ int hashCode = node.hashCode();
+ WeightedRoundRobin weightedRoundRobin = weightMap.get(hashCode);
+ int weight = node.getWeight();
+ if (weight < 0) {
+ weight = 0;
+ }
+
+ // 检测当前 Node 是否有对应的 WeightedRoundRobin,没有则创建
+ if (weightedRoundRobin == null) {
+ weightedRoundRobin = new WeightedRoundRobin();
+ // 设置 Node 权重
+ weightedRoundRobin.setWeight(weight);
+ // 存储 url 唯一标识 identifyString 到 weightedRoundRobin 的映射关系
+ weightMap.putIfAbsent(hashCode, weightedRoundRobin);
+ weightedRoundRobin = weightMap.get(hashCode);
+ }
+ // Node 权重不等于 WeightedRoundRobin 中保存的权重,说明权重变化了,此时进行更新
+ if (weight != weightedRoundRobin.getWeight()) {
+ weightedRoundRobin.setWeight(weight);
+ }
+
+ // 让 current 加上自身权重,等价于 current += weight
+ long current = weightedRoundRobin.increaseCurrent();
+ // 设置 lastUpdate,表示近期更新过
+ weightedRoundRobin.setLastUpdate(now);
+ // 找出最大的 current
+ if (current > maxCurrent) {
+ maxCurrent = current;
+ // 将具有最大 current 权重的 Node 赋值给 selectedNode
+ selectedNode = node;
+ // 将 Node 对应的 weightedRoundRobin 赋值给 selectedWRR,留作后用
+ selectedWRR = weightedRoundRobin;
+ }
+
+ // 计算权重总和
+ totalWeight += weight;
+ }
+
+ // 对 weightMap 进行检查,过滤掉长时间未被更新的节点。
+ // 该节点可能挂了,nodes 中不包含该节点,所以该节点的 lastUpdate 长时间无法被更新。
+ // 若未更新时长超过阈值后,就会被移除掉,默认阈值为60秒。
+ if (!updateLock.get() && nodes.size() != weightMap.size()) {
+ if (updateLock.compareAndSet(false, true)) {
+ try {
+ // 遍历修改,即移除过期记录
+ weightMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
+ } finally {
+ updateLock.set(false);
+ }
+ }
+ }
+
+ if (selectedNode != null) {
+ // 让 current 减去权重总和,等价于 current -= totalWeight
+ selectedWRR.decreaseCurrent(totalWeight);
+ // 返回具有最大 current 的 Node
+ return selectedNode;
+ }
+
+ // should not happen here
+ return nodes.get(0);
+ }
+
+ protected static class WeightedRoundRobin {
+
+ // 服务提供者权重
+ private int weight;
+ // 当前权重
+ private AtomicLong current = new AtomicLong(0);
+ // 最后一次更新时间
+ private long lastUpdate;
+
+ public long increaseCurrent() {
+ // current = current + weight;
+ return current.addAndGet(weight);
+ }
+
+ public long decreaseCurrent(int total) {
+ // current = current - total;
+ return current.addAndGet(-1 * total);
+ }
+
+ public int getWeight() {
+ return weight;
+ }
+
+ public void setWeight(int weight) {
+ this.weight = weight;
+ // 初始情况下,current = 0
+ current.set(0);
+ }
+
+ public AtomicLong getCurrent() {
+ return current;
+ }
+
+ public void setCurrent(AtomicLong current) {
+ this.current = current;
+ }
+
+ public long getLastUpdate() {
+ return lastUpdate;
+ }
+
+ public void setLastUpdate(long lastUpdate) {
+ this.lastUpdate = lastUpdate;
+ }
+
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/package-info.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/package-info.java
new file mode 100644
index 00000000..4d8b7a26
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * 负载均衡算法实现
+ *
+ * @author Zhang Peng
+ * @since 2020-01-22
+ */
+package io.github.dunwu.distributed;
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/CRCHashStrategy.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/CRCHashStrategy.java
new file mode 100644
index 00000000..5c732a5b
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/CRCHashStrategy.java
@@ -0,0 +1,68 @@
+package io.github.dunwu.distributed.support;
+
+import java.nio.charset.StandardCharsets;
+
+public class CRCHashStrategy implements HashStrategy {
+
+ private static final int LOOKUP_TABLE[] = { 0x0000, 0x1021, 0x2042, 0x3063,
+ 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B,
+ 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252,
+ 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A,
+ 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
+ 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509,
+ 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630,
+ 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738,
+ 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7,
+ 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF,
+ 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96,
+ 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E,
+ 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5,
+ 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
+ 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4,
+ 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC,
+ 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB,
+ 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3,
+ 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA,
+ 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2,
+ 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589,
+ 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481,
+ 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
+ 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0,
+ 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F,
+ 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827,
+ 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E,
+ 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16,
+ 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D,
+ 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45,
+ 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C,
+ 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
+ 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, };
+
+ /**
+ * Create a CRC16 checksum from the bytes. implementation is from mp911de/lettuce, modified with some more
+ * optimizations
+ *
+ * @param bytes
+ * @return CRC16 as integer value
+ */
+ public static int getCRC16(byte[] bytes) {
+ int crc = 0x0000;
+
+ for (byte b : bytes) {
+ crc = ((crc << 8) ^ LOOKUP_TABLE[((crc >>> 8) ^ (b & 0xFF)) & 0xFF]);
+ }
+ return crc & 0xFFFF;
+ }
+
+ public static int getCRC16(String key) {
+ return getCRC16(key.getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Override
+ public int hashCode(String key) {
+ // optimization with modulo operator with power of 2
+ // equivalent to getCRC16(key) % 16384
+ return getCRC16(key) & (16384 - 1);
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/FnvHashStrategy.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/FnvHashStrategy.java
new file mode 100644
index 00000000..fc7cea13
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/FnvHashStrategy.java
@@ -0,0 +1,24 @@
+package io.github.dunwu.distributed.support;
+
+public class FnvHashStrategy implements HashStrategy {
+
+ private static final long FNV_32_INIT = 2166136261L;
+
+ private static final int FNV_32_PRIME = 16777619;
+
+ @Override
+ public int hashCode(String key) {
+ final int p = FNV_32_PRIME;
+ int hash = (int) FNV_32_INIT;
+ for (int i = 0; i < key.length(); i++)
+ hash = (hash ^ key.charAt(i)) * p;
+ hash += hash << 13;
+ hash ^= hash >> 7;
+ hash += hash << 3;
+ hash ^= hash >> 17;
+ hash += hash << 5;
+ hash = Math.abs(hash);
+ return hash;
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/HashStrategy.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/HashStrategy.java
new file mode 100644
index 00000000..f574c86e
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/HashStrategy.java
@@ -0,0 +1,7 @@
+package io.github.dunwu.distributed.support;
+
+public interface HashStrategy {
+
+ int hashCode(String key);
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/JdkHashCodeStrategy.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/JdkHashCodeStrategy.java
new file mode 100644
index 00000000..6541e2a4
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/JdkHashCodeStrategy.java
@@ -0,0 +1,10 @@
+package io.github.dunwu.distributed.support;
+
+public class JdkHashCodeStrategy implements HashStrategy {
+
+ @Override
+ public int hashCode(String key) {
+ return key.hashCode();
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/KetamaHashStrategy.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/KetamaHashStrategy.java
new file mode 100644
index 00000000..ef479aaf
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/KetamaHashStrategy.java
@@ -0,0 +1,42 @@
+package io.github.dunwu.distributed.support;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class KetamaHashStrategy implements HashStrategy {
+
+ private static MessageDigest md5Digest;
+
+ static {
+ try {
+ md5Digest = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("MD5 not supported", e);
+ }
+ }
+
+ @Override
+ public int hashCode(String key) {
+ byte[] bKey = computeMd5(key);
+ long rv = ((long) (bKey[3] & 0xFF) << 24)
+ | ((long) (bKey[2] & 0xFF) << 16)
+ | ((long) (bKey[1] & 0xFF) << 8)
+ | (bKey[0] & 0xFF);
+ return (int) (rv & 0xffffffffL);
+ }
+
+ /**
+ * Get the md5 of the given key.
+ */
+ public static byte[] computeMd5(String k) {
+ MessageDigest md5;
+ try {
+ md5 = (MessageDigest) md5Digest.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException("clone of MD5 not supported", e);
+ }
+ md5.update(k.getBytes());
+ return md5.digest();
+ }
+
+}
diff --git a/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/MurmurHashStrategy.java b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/MurmurHashStrategy.java
new file mode 100644
index 00000000..82ee1620
--- /dev/null
+++ b/codes/java-distributed/java-load-balance/src/main/java/io/github/dunwu/distributed/support/MurmurHashStrategy.java
@@ -0,0 +1,51 @@
+package io.github.dunwu.distributed.support;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+public class MurmurHashStrategy implements HashStrategy {
+
+ @Override
+ public int hashCode(String key) {
+
+ ByteBuffer buf = ByteBuffer.wrap(key.getBytes());
+ int seed = 0x1234ABCD;
+
+ ByteOrder byteOrder = buf.order();
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+
+ long m = 0xc6a4a7935bd1e995L;
+ int r = 47;
+
+ long h = seed ^ (buf.remaining() * m);
+
+ long k;
+ while (buf.remaining() >= 8) {
+ k = buf.getLong();
+
+ k *= m;
+ k ^= k >>> r;
+ k *= m;
+
+ h ^= k;
+ h *= m;
+ }
+
+ if (buf.remaining() > 0) {
+ ByteBuffer finish = ByteBuffer.allocate(8).order(
+ ByteOrder.LITTLE_ENDIAN);
+ // for big-endian version, do this first:
+ // finish.position(8-buf.remaining());
+ finish.put(buf).rewind();
+ h ^= finish.getLong();
+ h *= m;
+ }
+ h ^= h >>> r;
+ h *= m;
+ h ^= h >>> r;
+
+ buf.order(byteOrder);
+ return (int) (h & 0xffffffffL);
+ }
+
+}
diff --git a/codes/java-distributed/java-rate-limit/pom.xml b/codes/java-distributed/java-rate-limit/pom.xml
new file mode 100644
index 00000000..312d7f21
--- /dev/null
+++ b/codes/java-distributed/java-rate-limit/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ io.github.dunwu.distributed
+ java-rate-limit
+ 1.0.0
+ jar
+
+
+ UTF-8
+ 1.8
+ ${java.version}
+ ${java.version}
+
+
+
+
+ redis.clients
+ jedis
+ 5.1.0
+
+
+ cn.hutool
+ hutool-all
+ 5.8.25
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+ true
+
+
+
diff --git a/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/FixedWindowRateLimiter.java b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/FixedWindowRateLimiter.java
new file mode 100644
index 00000000..0af8d142
--- /dev/null
+++ b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/FixedWindowRateLimiter.java
@@ -0,0 +1,59 @@
+package io.github.dunwu.distributed.ratelimit;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * 固定时间窗口限流算法
+ *
+ * @author Zhang Peng
+ * @date 2024-01-18
+ */
+public class FixedWindowRateLimiter implements RateLimiter {
+
+ /**
+ * 允许的最大请求数
+ */
+ private final long maxPermits;
+
+ /**
+ * 窗口期时长
+ */
+ private final long periodMillis;
+
+ /**
+ * 窗口期截止时间
+ */
+ private long lastPeriodMillis;
+
+ /**
+ * 请求计数
+ */
+ private AtomicLong count = new AtomicLong(0);
+
+ public FixedWindowRateLimiter(long qps) {
+ this(qps, 1000, TimeUnit.MILLISECONDS);
+ }
+
+ public FixedWindowRateLimiter(long maxPermits, long period, TimeUnit timeUnit) {
+ this.maxPermits = maxPermits;
+ this.periodMillis = timeUnit.toMillis(period);
+ this.lastPeriodMillis = System.currentTimeMillis() + this.periodMillis;
+ }
+
+ @Override
+ public synchronized boolean tryAcquire(int permits) {
+ long now = System.currentTimeMillis();
+ if (lastPeriodMillis <= now) {
+ this.lastPeriodMillis = now + this.periodMillis;
+ count = new AtomicLong(0);
+ }
+ if (count.get() + permits <= maxPermits) {
+ count.addAndGet(permits);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/LeakyBucketRateLimiter.java b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/LeakyBucketRateLimiter.java
new file mode 100644
index 00000000..0d99a227
--- /dev/null
+++ b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/LeakyBucketRateLimiter.java
@@ -0,0 +1,64 @@
+package io.github.dunwu.distributed.ratelimit;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * 漏桶限流算法
+ *
+ * @author Zhang Peng
+ * @date 2024-01-18
+ */
+public class LeakyBucketRateLimiter implements RateLimiter {
+
+ /**
+ * QPS
+ */
+ private final int qps;
+
+ /**
+ * 桶的容量
+ */
+ private final long capacity;
+
+ /**
+ * 计算的起始时间
+ */
+ private long beginTimeMillis;
+
+ /**
+ * 桶中当前的水量
+ */
+ private final AtomicLong waterNum = new AtomicLong(0);
+
+ public LeakyBucketRateLimiter(int qps, int capacity) {
+ this.qps = qps;
+ this.capacity = capacity;
+ }
+
+ @Override
+ public synchronized boolean tryAcquire(int permits) {
+
+ // 如果桶中没有水,直接放行
+ if (waterNum.get() == 0) {
+ beginTimeMillis = System.currentTimeMillis();
+ waterNum.addAndGet(permits);
+ return true;
+ }
+
+ // 计算水量
+ long leakedWaterNum = ((System.currentTimeMillis() - beginTimeMillis) / 1000) * qps;
+ long currentWaterNum = waterNum.get() - leakedWaterNum;
+ waterNum.set(Math.max(0, currentWaterNum));
+
+ // 重置时间
+ beginTimeMillis = System.currentTimeMillis();
+
+ if (waterNum.get() + permits < capacity) {
+ waterNum.addAndGet(permits);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RateLimiter.java b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RateLimiter.java
new file mode 100644
index 00000000..4fbc9646
--- /dev/null
+++ b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RateLimiter.java
@@ -0,0 +1,13 @@
+package io.github.dunwu.distributed.ratelimit;
+
+/**
+ * 限流器
+ *
+ * @author Zhang Peng
+ * @date 2024-01-18
+ */
+public interface RateLimiter {
+
+ boolean tryAcquire(int permits);
+
+}
diff --git a/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RateLimiterDemo.java b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RateLimiterDemo.java
new file mode 100644
index 00000000..e4a50641
--- /dev/null
+++ b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RateLimiterDemo.java
@@ -0,0 +1,95 @@
+package io.github.dunwu.distributed.ratelimit;
+
+import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.core.util.RandomUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * 限流器示例
+ *
+ * @author Zhang Peng
+ * @date 2024-01-18
+ */
+@Slf4j
+public class RateLimiterDemo {
+
+ public static void main(String[] args) {
+
+ // ============================================================================
+
+ int qps = 20;
+
+ System.out.println("======================= 固定时间窗口限流算法 =======================");
+ FixedWindowRateLimiter fixedWindowRateLimiter = new FixedWindowRateLimiter(qps);
+ testRateLimit(fixedWindowRateLimiter, qps);
+
+ System.out.println("======================= 滑动时间窗口限流算法 =======================");
+ SlidingWindowRateLimiter slidingWindowRateLimiter = new SlidingWindowRateLimiter(qps, 10);
+ testRateLimit(slidingWindowRateLimiter, qps);
+
+ System.out.println("======================= 漏桶限流算法 =======================");
+ LeakyBucketRateLimiter leakyBucketRateLimiter = new LeakyBucketRateLimiter(qps, 100);
+ testRateLimit(leakyBucketRateLimiter, qps);
+
+ System.out.println("======================= 令牌桶限流算法 =======================");
+ TokenBucketRateLimiter tokenBucketRateLimiter = new TokenBucketRateLimiter(qps, 100);
+ testRateLimit(tokenBucketRateLimiter, qps);
+ }
+
+ private static void testRateLimit(RateLimiter rateLimiter, int qps) {
+
+ AtomicInteger okNum = new AtomicInteger(0);
+ AtomicInteger limitNum = new AtomicInteger(0);
+ ExecutorService executorService = ThreadUtil.newFixedExecutor(10, "限流测试", true);
+ long beginTime = System.currentTimeMillis();
+
+ int threadNum = 4;
+ final CountDownLatch latch = new CountDownLatch(threadNum);
+ for (int i = 0; i < threadNum; i++) {
+ executorService.submit(() -> {
+ try {
+ batchRequest(rateLimiter, okNum, limitNum, 1000);
+ } catch (Exception e) {
+ log.error("发生异常!", e);
+ } finally {
+ latch.countDown();
+ }
+ });
+ }
+
+ try {
+ latch.await(10, TimeUnit.SECONDS);
+ long endTime = System.currentTimeMillis();
+ long gap = endTime - beginTime;
+ log.info("限流 QPS: {} -> 实际结果:耗时 {} ms,{} 次请求成功,{} 次请求被限流,实际 QPS: {}",
+ qps, gap, okNum.get(), limitNum.get(), okNum.get() * 1000 / gap);
+ if (okNum.get() == qps) {
+ log.info("限流符合预期");
+ }
+ } catch (Exception e) {
+ log.error("发生异常!", e);
+ } finally {
+ executorService.shutdown();
+ }
+ }
+
+ private static void batchRequest(RateLimiter rateLimiter, AtomicInteger okNum, AtomicInteger limitNum, int num)
+ throws InterruptedException {
+ for (int j = 0; j < num; j++) {
+ if (rateLimiter.tryAcquire(1)) {
+ log.info("请求成功");
+ okNum.getAndIncrement();
+ } else {
+ log.info("请求限流");
+ limitNum.getAndIncrement();
+ }
+ TimeUnit.MILLISECONDS.sleep(RandomUtil.randomInt(0, 10));
+ }
+ }
+
+}
diff --git a/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RedisFixedWindowRateLimiter.java b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RedisFixedWindowRateLimiter.java
new file mode 100644
index 00000000..ec5d77d9
--- /dev/null
+++ b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RedisFixedWindowRateLimiter.java
@@ -0,0 +1,100 @@
+package io.github.dunwu.distributed.ratelimit;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.resource.ResourceUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.exceptions.JedisConnectionException;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 基于 Redis + Lua 实现的固定时间窗口限流算法
+ *
+ * @author Zhang Peng
+ * @date 2024-01-23
+ */
+public class RedisFixedWindowRateLimiter implements RateLimiter {
+
+ private static final String REDIS_HOST = "localhost";
+
+ private static final int REDIS_PORT = 6379;
+
+ private static final Jedis JEDIS;
+
+ public static final String SCRIPT;
+
+ static {
+ // Jedis 有多种构造方法,这里选用最简单的一种情况
+ JEDIS = new Jedis(REDIS_HOST, REDIS_PORT);
+
+ // 触发 ping 命令
+ try {
+ JEDIS.ping();
+ System.out.println("jedis 连接成功");
+ } catch (JedisConnectionException e) {
+ e.printStackTrace();
+ }
+
+ SCRIPT = FileUtil.readString(ResourceUtil.getResource("scripts/fixed_window_rate_limit.lua"),
+ StandardCharsets.UTF_8);
+ }
+
+ private final long maxPermits;
+ private final long periodSeconds;
+ private final String key;
+
+ public RedisFixedWindowRateLimiter(long qps, String key) {
+ this(qps * 60, 60, TimeUnit.SECONDS, key);
+ }
+
+ public RedisFixedWindowRateLimiter(long maxPermits, long period, TimeUnit timeUnit, String key) {
+ this.maxPermits = maxPermits;
+ this.periodSeconds = timeUnit.toSeconds(period);
+ this.key = key;
+ }
+
+ @Override
+ public boolean tryAcquire(int permits) {
+ List keys = Collections.singletonList(key);
+ List args = CollectionUtil.newLinkedList(String.valueOf(permits), String.valueOf(periodSeconds),
+ String.valueOf(maxPermits));
+ Object eval = JEDIS.eval(SCRIPT, keys, args);
+ long value = (long) eval;
+ return value != -1;
+ }
+
+ public static void main(String[] args) throws InterruptedException {
+
+ int qps = 20;
+ RateLimiter jedisFixedWindowRateLimiter = new RedisFixedWindowRateLimiter(qps, "rate:limit:20240122210000");
+
+ // 模拟在一分钟内,不断收到请求,限流是否有效
+ int seconds = 60;
+ long okNum = 0L;
+ long total = 0L;
+ long beginTime = System.currentTimeMillis();
+ int num = RandomUtil.randomInt(qps, 100);
+ for (int second = 0; second < seconds; second++) {
+ for (int i = 0; i < num; i++) {
+ total++;
+ if (jedisFixedWindowRateLimiter.tryAcquire(1)) {
+ okNum++;
+ System.out.println("请求成功");
+ } else {
+ System.out.println("请求限流");
+ }
+ }
+ TimeUnit.SECONDS.sleep(1);
+ }
+ long endTime = System.currentTimeMillis();
+ long time = (endTime - beginTime) / 1000;
+ System.out.println(StrUtil.format("请求通过数:{},总请求数:{},实际 QPS:{}", okNum, total, okNum / time));
+ }
+
+}
diff --git a/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RedisTokenBucketRateLimiter.java b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RedisTokenBucketRateLimiter.java
new file mode 100644
index 00000000..9dd219df
--- /dev/null
+++ b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/RedisTokenBucketRateLimiter.java
@@ -0,0 +1,104 @@
+package io.github.dunwu.distributed.ratelimit;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.resource.ResourceUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.exceptions.JedisConnectionException;
+
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 基于 Redis + Lua 实现的令牌桶限流算法
+ *
+ * @author Zhang Peng
+ * @date 2024-01-23
+ */
+public class RedisTokenBucketRateLimiter implements RateLimiter {
+
+ private static final String REDIS_HOST = "localhost";
+
+ private static final int REDIS_PORT = 6379;
+
+ private static final Jedis JEDIS;
+
+ public static final String SCRIPT;
+
+ static {
+ // Jedis 有多种构造方法,这里选用最简单的一种情况
+ JEDIS = new Jedis(REDIS_HOST, REDIS_PORT);
+
+ // 触发 ping 命令
+ try {
+ JEDIS.ping();
+ System.out.println("jedis 连接成功");
+ } catch (JedisConnectionException e) {
+ e.printStackTrace();
+ }
+
+ SCRIPT = FileUtil.readString(ResourceUtil.getResource("scripts/token_bucket_rate_limit.lua"),
+ StandardCharsets.UTF_8);
+ }
+
+ private final long qps;
+ private final long capacity;
+ private final String tokenKey;
+ private final String timeKey;
+
+ public RedisTokenBucketRateLimiter(long qps, long capacity, String tokenKey, String timeKey) {
+ this.qps = qps;
+ this.capacity = capacity;
+ this.tokenKey = tokenKey;
+ this.timeKey = timeKey;
+ }
+
+ @Override
+ public boolean tryAcquire(int permits) {
+ long now = System.currentTimeMillis();
+ List keys = CollectionUtil.newLinkedList(tokenKey, timeKey);
+ List args = CollectionUtil.newLinkedList(String.valueOf(permits), String.valueOf(qps),
+ String.valueOf(capacity), String.valueOf(now));
+ Object eval = JEDIS.eval(SCRIPT, keys, args);
+ long value = (long) eval;
+ return value != -1;
+ }
+
+ public static void main(String[] args) throws InterruptedException {
+
+ int qps = 20;
+ int bucket = 100;
+ RedisTokenBucketRateLimiter redisTokenBucketRateLimiter =
+ new RedisTokenBucketRateLimiter(qps, bucket, "token:rate:limit", "token:rate:limit:time");
+
+ // 先将令牌桶预热令牌申请完,后续才能真实反映限流 QPS
+ redisTokenBucketRateLimiter.tryAcquire(bucket);
+ TimeUnit.SECONDS.sleep(1);
+
+ // 模拟在一分钟内,不断收到请求,限流是否有效
+ int seconds = 60;
+ long okNum = 0L;
+ long total = 0L;
+ long beginTime = System.currentTimeMillis();
+ for (int second = 0; second < seconds; second++) {
+ int num = RandomUtil.randomInt(qps, 100);
+ for (int i = 0; i < num; i++) {
+ total++;
+ if (redisTokenBucketRateLimiter.tryAcquire(1)) {
+ okNum++;
+ System.out.println("请求成功");
+ } else {
+ System.out.println("请求限流");
+ }
+ }
+ TimeUnit.SECONDS.sleep(1);
+ }
+ long endTime = System.currentTimeMillis();
+ long time = (endTime - beginTime) / 1000;
+ System.out.println(StrUtil.format("请求通过数:{},总请求数:{},实际 QPS:{}", okNum, total, okNum / time));
+ }
+
+}
diff --git a/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/SlidingWindowRateLimiter.java b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/SlidingWindowRateLimiter.java
new file mode 100644
index 00000000..a93613a2
--- /dev/null
+++ b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/SlidingWindowRateLimiter.java
@@ -0,0 +1,87 @@
+package io.github.dunwu.distributed.ratelimit;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * 滑动时间窗口限流算法
+ *
+ * @author Zhang Peng
+ * @date 2024-01-18
+ */
+public class SlidingWindowRateLimiter implements RateLimiter {
+
+ /**
+ * 允许的最大请求数
+ */
+ private final long maxPermits;
+
+ /**
+ * 窗口期时长
+ */
+ private final long periodMillis;
+
+ /**
+ * 分片窗口期时长
+ */
+ private final long shardPeriodMillis;
+
+ /**
+ * 窗口期截止时间
+ */
+ private long lastPeriodMillis;
+
+ /**
+ * 分片窗口数
+ */
+ private final int shardNum;
+
+ /**
+ * 请求总计数
+ */
+ private final AtomicLong totalCount = new AtomicLong(0);
+
+ /**
+ * 分片窗口计数列表
+ */
+ private final List countList = new LinkedList<>();
+
+ public SlidingWindowRateLimiter(long qps, int shardNum) {
+ this(qps, 1000, TimeUnit.MILLISECONDS, shardNum);
+ }
+
+ public SlidingWindowRateLimiter(long maxPermits, long period, TimeUnit timeUnit, int shardNum) {
+ this.maxPermits = maxPermits;
+ this.periodMillis = timeUnit.toMillis(period);
+ this.lastPeriodMillis = System.currentTimeMillis();
+ this.shardPeriodMillis = timeUnit.toMillis(period) / shardNum;
+ this.shardNum = shardNum;
+ for (int i = 0; i < shardNum; i++) {
+ countList.add(new AtomicLong(0));
+ }
+ }
+
+ @Override
+ public synchronized boolean tryAcquire(int permits) {
+ long now = System.currentTimeMillis();
+ if (now > lastPeriodMillis) {
+ for (int shardId = 0; shardId < shardNum; shardId++) {
+ long shardCount = countList.get(shardId).get();
+ totalCount.addAndGet(-shardCount);
+ countList.set(shardId, new AtomicLong(0));
+ lastPeriodMillis += shardPeriodMillis;
+ }
+ }
+ int shardId = (int) (now % periodMillis / shardPeriodMillis);
+ if (totalCount.get() + permits <= maxPermits) {
+ countList.get(shardId).addAndGet(permits);
+ totalCount.addAndGet(permits);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/TokenBucketRateLimiter.java b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/TokenBucketRateLimiter.java
new file mode 100644
index 00000000..e03e4c7d
--- /dev/null
+++ b/codes/java-distributed/java-rate-limit/src/main/java/io/github/dunwu/distributed/ratelimit/TokenBucketRateLimiter.java
@@ -0,0 +1,59 @@
+package io.github.dunwu.distributed.ratelimit;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * 令牌桶限流算法
+ *
+ * @author Zhang Peng
+ * @date 2024-01-18
+ */
+public class TokenBucketRateLimiter implements RateLimiter {
+
+ /**
+ * QPS
+ */
+ private final long qps;
+
+ /**
+ * 桶的容量
+ */
+ private final long capacity;
+
+ /**
+ * 上一次令牌发放时间
+ */
+ private long endTimeMillis;
+
+ /**
+ * 桶中当前的令牌数量
+ */
+ private final AtomicLong tokenNum = new AtomicLong(0);
+
+ public TokenBucketRateLimiter(long qps, long capacity) {
+ this.qps = qps;
+ this.capacity = capacity;
+ this.endTimeMillis = System.currentTimeMillis();
+ }
+
+ @Override
+ public synchronized boolean tryAcquire(int permits) {
+
+ long now = System.currentTimeMillis();
+ long gap = now - endTimeMillis;
+
+ // 计算令牌数
+ long newTokenNum = (gap * qps / 1000);
+ long currentTokenNum = tokenNum.get() + newTokenNum;
+ tokenNum.set(Math.min(capacity, currentTokenNum));
+
+ if (tokenNum.get() < permits) {
+ return false;
+ } else {
+ tokenNum.addAndGet(-permits);
+ endTimeMillis = now;
+ return true;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/codes/java-distributed/java-rate-limit/src/main/resources/scripts/fixed_window_rate_limit.lua b/codes/java-distributed/java-rate-limit/src/main/resources/scripts/fixed_window_rate_limit.lua
new file mode 100644
index 00000000..e0c9ad00
--- /dev/null
+++ b/codes/java-distributed/java-rate-limit/src/main/resources/scripts/fixed_window_rate_limit.lua
@@ -0,0 +1,21 @@
+-- 缓存 Key
+local key = KEYS[1]
+-- 访问请求数
+local permits = tonumber(ARGV[1])
+-- 过期时间
+local seconds = tonumber(ARGV[2])
+-- 限流阈值
+local limit = tonumber(ARGV[3])
+
+-- 获取统计值
+local count = tonumber(redis.call('GET', key) or "0")
+
+if count + permits > limit then
+ -- 请求拒绝
+ return -1
+else
+ -- 请求通过
+ redis.call('INCRBY', key, permits)
+ redis.call('EXPIRE', key, seconds)
+ return count + permits
+end
\ No newline at end of file
diff --git a/codes/java-distributed/java-rate-limit/src/main/resources/scripts/token_bucket_rate_limit.lua b/codes/java-distributed/java-rate-limit/src/main/resources/scripts/token_bucket_rate_limit.lua
new file mode 100644
index 00000000..541d70c9
--- /dev/null
+++ b/codes/java-distributed/java-rate-limit/src/main/resources/scripts/token_bucket_rate_limit.lua
@@ -0,0 +1,39 @@
+local tokenKey = KEYS[1]
+local timeKey = KEYS[2]
+
+-- 申请令牌数
+local permits = tonumber(ARGV[1])
+-- QPS
+local qps = tonumber(ARGV[2])
+-- 桶的容量
+local capacity = tonumber(ARGV[3])
+-- 当前时间(单位:毫秒)
+local nowMillis = tonumber(ARGV[4])
+-- 填满令牌桶所需要的时间
+local fillTime = capacity / qps
+local ttl = math.min(capacity, math.floor(fillTime * 2))
+
+local currentTokenNum = tonumber(redis.call("GET", tokenKey))
+if currentTokenNum == nil then
+ currentTokenNum = capacity
+end
+
+local endTimeMillis = tonumber(redis.call("GET", timeKey))
+if endTimeMillis == nil then
+ endTimeMillis = 0
+end
+
+local gap = nowMillis - endTimeMillis
+local newTokenNum = math.max(0, gap * qps / 1000)
+local currentTokenNum = math.min(capacity, currentTokenNum + newTokenNum)
+
+if currentTokenNum < permits then
+ -- 请求拒绝
+ return -1
+else
+ -- 请求通过
+ local finalTokenNum = currentTokenNum - permits
+ redis.call("SETEX", tokenKey, ttl, finalTokenNum)
+ redis.call("SETEX", timeKey, ttl, nowMillis)
+ return finalTokenNum
+end
diff --git a/codes/java-distributed/java-task/pom.xml b/codes/java-distributed/java-task/pom.xml
new file mode 100644
index 00000000..8d7cc462
--- /dev/null
+++ b/codes/java-distributed/java-task/pom.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+
+
+ io.github.dunwu.distributed
+ java-distributed
+ 1.0.0
+
+
+ io.github.dunwu.distributed
+ java-task
+ 1.0.0
+ jar
+
+
+ UTF-8
+ 1.8
+ ${java.version}
+ ${java.version}
+
+
+
+
+ cn.hutool
+ hutool-all
+
+
+ org.projectlombok
+ lombok
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+ true
+
+
+
diff --git a/codes/java-distributed/java-task/src/main/java/io/github/dunwu/local/task/DelayQueueExample.java b/codes/java-distributed/java-task/src/main/java/io/github/dunwu/local/task/DelayQueueExample.java
new file mode 100644
index 00000000..d510eed1
--- /dev/null
+++ b/codes/java-distributed/java-task/src/main/java/io/github/dunwu/local/task/DelayQueueExample.java
@@ -0,0 +1,52 @@
+package io.github.dunwu.local.task;
+
+import cn.hutool.core.date.DateUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Date;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+public class DelayQueueExample {
+
+ public static void main(String[] args) throws InterruptedException {
+ BlockingQueue delayQueue = new DelayQueue<>();
+ long now = System.currentTimeMillis();
+ delayQueue.put(new SampleTask(now + 1000));
+ delayQueue.put(new SampleTask(now + 2000));
+ delayQueue.put(new SampleTask(now + 3000));
+ for (int i = 0; i < 3; i++) {
+ log.info("task 执行时间:{}", DateUtil.format(new Date(delayQueue.take().getTime()), "yyyy-MM-dd HH:mm:ss"));
+ }
+ }
+
+ static class SampleTask implements Delayed {
+
+ long time;
+
+ public SampleTask(long time) {
+ this.time = time;
+ }
+
+ public long getTime() {
+ return time;
+ }
+
+ @Override
+ public int compareTo(Delayed o) {
+ return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
+ }
+
+ @Override
+ public long getDelay(TimeUnit unit) {
+ return unit.convert(time - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
+ }
+
+ }
+
+}
+
+
diff --git a/codes/java-distributed/java-task/src/main/java/io/github/dunwu/local/task/ScheduledExecutorServiceExample.java b/codes/java-distributed/java-task/src/main/java/io/github/dunwu/local/task/ScheduledExecutorServiceExample.java
new file mode 100644
index 00000000..78e8f5bd
--- /dev/null
+++ b/codes/java-distributed/java-task/src/main/java/io/github/dunwu/local/task/ScheduledExecutorServiceExample.java
@@ -0,0 +1,37 @@
+package io.github.dunwu.local.task;
+
+import cn.hutool.core.date.DateUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Date;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+public class ScheduledExecutorServiceExample {
+
+ public static void main(String[] args) {
+ // 创建一个 ScheduledExecutorService 对象,它将使用一个线程池来执行任务
+ ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+
+ // 创建一个 Runnable 对象,这个任务将在 2 秒后执行,并且每 1 秒重复执行一次
+ Runnable task = () -> {
+ log.info("task 执行时间:{}", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
+ };
+
+ // 安排任务在 2 秒后执行,并且每 1 秒重复执行一次
+ executor.scheduleAtFixedRate(task, 2, 1, TimeUnit.SECONDS);
+
+ // 主线程等待 10 秒后结束
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ // 关闭 executor,这将停止所有正在执行的任务,并拒绝新任务的提交
+ executor.shutdown();
+ }
+
+}
diff --git a/codes/java-distributed/java-task/src/main/java/io/github/dunwu/local/task/TimerExample.java b/codes/java-distributed/java-task/src/main/java/io/github/dunwu/local/task/TimerExample.java
new file mode 100644
index 00000000..ce1d7756
--- /dev/null
+++ b/codes/java-distributed/java-task/src/main/java/io/github/dunwu/local/task/TimerExample.java
@@ -0,0 +1,39 @@
+package io.github.dunwu.local.task;
+
+import cn.hutool.core.date.DateUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Date;
+import java.util.Timer;
+import java.util.TimerTask;
+
+@Slf4j
+public class TimerExample {
+
+ public static void main(String[] args) {
+ // 创建一个 Timer 对象
+ Timer timer = new Timer();
+
+ // 创建一个 TimerTask 对象,这个任务将在 2 秒后执行,并且每 1 秒重复执行一次
+ TimerTask task = new TimerTask() {
+ @Override
+ public void run() {
+ log.info("task 执行时间:{}", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
+ }
+ };
+
+ // 安排任务在 2 秒后执行,并且每 1 秒重复执行一次
+ timer.schedule(task, 2000, 1000);
+
+ // 主线程等待 10 秒后结束
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ // 取消定时器和所有已安排的任务
+ timer.cancel();
+ }
+
+}
diff --git a/codes/java-distributed/pom.xml b/codes/java-distributed/pom.xml
new file mode 100644
index 00000000..aa88d15d
--- /dev/null
+++ b/codes/java-distributed/pom.xml
@@ -0,0 +1,54 @@
+
+
+ 4.0.0
+
+ io.github.dunwu.distributed
+ java-distributed
+ 1.0.0
+ pom
+
+
+ java-load-balance
+ java-rate-limit
+ java-distributed-id
+ java-task
+
+
+
+
+
+ org.apache.zookeeper
+ zookeeper
+ 3.9.2
+
+
+ org.apache.curator
+ curator-recipes
+ 4.3.0
+
+
+ redis.clients
+ jedis
+ 5.1.0
+
+
+ cn.hutool
+ hutool-all
+ 5.8.34
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+
+
+ ch.qos.logback
+ logback-classic
+ 1.4.12
+ true
+
+
+
+
+
diff --git a/codes/javaee/README.md b/codes/javaee/README.md
new file mode 100644
index 00000000..7587bd07
--- /dev/null
+++ b/codes/javaee/README.md
@@ -0,0 +1 @@
+# JavaEE 示例代码
\ No newline at end of file
diff --git a/codes/javaee/javaee-filter/pom.xml b/codes/javaee/javaee-filter/pom.xml
new file mode 100644
index 00000000..a0f8f7e9
--- /dev/null
+++ b/codes/javaee/javaee-filter/pom.xml
@@ -0,0 +1,107 @@
+
+ 4.0.0
+
+
+ io.github.dunwu.javaee
+ javaee
+ 1.0.0
+
+
+ javaee-filter
+ 1.0.0
+ war
+ javaee-filter
+ JavaEE 学习笔记之 Filter
+
+
+ UTF-8
+ 1.7
+ ${java.version}
+ ${java.version}
+
+
+
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ ch.qos.logback
+ logback-core
+
+
+ org.slf4j
+ jcl-over-slf4j
+
+
+
+
+
+ net.coobird
+ thumbnailator
+
+
+
+
+
+ commons-fileupload
+ commons-fileupload
+
+
+ commons-io
+ commons-io
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+
+
+
+ org.eclipse.jetty
+ jetty-webapp
+ test
+
+
+ org.eclipse.jetty
+ jetty-server
+ test
+
+
+ org.eclipse.jetty
+ jetty-annotations
+ test
+
+
+ org.eclipse.jetty
+ apache-jsp
+ test
+
+
+
+
+
+ junit
+ junit
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/CacheFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/CacheFilter.java
new file mode 100644
index 00000000..b7bb001a
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/CacheFilter.java
@@ -0,0 +1,97 @@
+package io.github.dunwu.javaee.filter;
+
+import io.github.dunwu.javaee.filter.wrapper.CacheResponseWrapper;
+
+import java.io.*;
+import java.net.URLEncoder;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class CacheFilter extends MyFilter {
+
+ private ServletContext servletContext;
+
+ // 缓存文件夹,使用Tomcat工作目录
+ private File temporalDir;
+
+ // 缓存时间,配置在Filter初始化参数中
+ private long cacheTime = Long.MAX_VALUE;
+
+ @Override
+ public void init(FilterConfig filterConfig) {
+ super.init(filterConfig);
+ temporalDir = (File) filterConfig.getServletContext().getAttribute("javax.servlet.context.tempdir");
+ servletContext = filterConfig.getServletContext();
+ cacheTime = new Long(filterConfig.getInitParameter("cacheTime"));
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ logger.info("{} 开始做过滤处理", this.getClass().getName());
+
+ HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+
+ // 如果为 POST, 则不经过缓存
+ if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) {
+ chain.doFilter(httpServletRequest, httpServletResponse);
+ return;
+ }
+
+ // 请求的 URI
+ String uri = httpServletRequest.getRequestURI();
+ if (uri == null) {
+ uri = "";
+ }
+ uri = uri.replace(httpServletRequest.getContextPath() + "/", "");
+ uri = uri.trim().length() == 0 ? "index.jsp" : uri;
+ uri = httpServletRequest.getQueryString() == null ? uri : (uri + "?" + httpServletRequest.getQueryString());
+
+ // 对应的缓存文件
+ File cacheFile = new File(temporalDir, URLEncoder.encode(uri, "UTF-8"));
+ System.out.println(cacheFile);
+
+ // 如果缓存文件不存在 或者已经超出缓存时间 则请求 Servlet
+ if (!cacheFile.exists() || cacheFile.length() == 0
+ || cacheFile.lastModified() < System.currentTimeMillis() - cacheTime) {
+
+ CacheResponseWrapper cacheResponse = new CacheResponseWrapper(httpServletResponse);
+
+ chain.doFilter(httpServletRequest, cacheResponse);
+
+ // 将内容写入缓存文件
+ char[] content = cacheResponse.getCacheWriter().toCharArray();
+
+ temporalDir.mkdirs();
+ cacheFile.createNewFile();
+
+ Writer writer = new OutputStreamWriter(new FileOutputStream(cacheFile), "UTF-8");
+ writer.write(content);
+ writer.close();
+ }
+
+ // 请求的ContentType
+ String mimeType = servletContext.getMimeType(httpServletRequest.getRequestURI());
+ httpServletResponse.setContentType(mimeType);
+
+ // 读取缓存文件的内容,写入客户端浏览器
+ Reader ins = new InputStreamReader(new FileInputStream(cacheFile), "UTF-8");
+ StringBuffer buffer = new StringBuffer();
+ char[] cbuf = new char[1024];
+ int len;
+ while ((len = ins.read(cbuf)) > -1) {
+ buffer.append(cbuf, 0, len);
+ }
+ ins.close();
+ // 输出到客户端
+ httpServletResponse.getWriter().write(buffer.toString());
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/CharacterEncodingFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/CharacterEncodingFilter.java
new file mode 100644
index 00000000..7b90de06
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/CharacterEncodingFilter.java
@@ -0,0 +1,65 @@
+package io.github.dunwu.javaee.filter;
+
+import io.github.dunwu.javaee.filter.wrapper.UploadRequestWrapper;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.IOException;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class CharacterEncodingFilter extends MyFilter {
+
+ private String characterEncoding;
+
+ private boolean enabled;
+
+ @Override
+ public void init(FilterConfig config) {
+ super.init(config);
+
+ characterEncoding = config.getInitParameter("characterEncoding");
+ enabled = "true".equalsIgnoreCase(characterEncoding.trim()) || "1".equalsIgnoreCase(characterEncoding.trim());
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ logger.info("{} 开始做过滤处理", this.getClass().getName());
+
+ if (enabled || StringUtils.isNotBlank(characterEncoding)) {
+ request.setCharacterEncoding(characterEncoding);
+ response.setCharacterEncoding(characterEncoding);
+ }
+
+ logger.info("系统设置HTTP请求和应答的默认编码为 {}", characterEncoding);
+ chain.doFilter(request, response);
+ }
+
+ public static class UploadFilter implements Filter {
+
+ @Override
+ public void destroy() {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ UploadRequestWrapper uploadRequest = new UploadRequestWrapper((HttpServletRequest) request);
+
+ chain.doFilter(uploadRequest, response);
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+
+ }
+
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/ExceptionHandlerFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/ExceptionHandlerFilter.java
new file mode 100644
index 00000000..aba7ca1a
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/ExceptionHandlerFilter.java
@@ -0,0 +1,48 @@
+package io.github.dunwu.javaee.filter;
+
+import io.github.dunwu.javaee.filter.exception.AccountException;
+import io.github.dunwu.javaee.filter.exception.BusinessException;
+import java.io.IOException;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class ExceptionHandlerFilter extends MyFilter {
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ try {
+ chain.doFilter(request, response);
+ } catch (Exception e) {
+ logger.info("{} 捕捉到异常", this.getClass().getName());
+ Throwable rootCause = e;
+
+ while (rootCause.getCause() != null) {
+ rootCause = rootCause.getCause();
+ }
+
+ String message = rootCause.getMessage();
+
+ message = message == null ? "异常:" + rootCause.getClass().getName() : message;
+
+ request.setAttribute("message", message);
+ request.setAttribute("e", e);
+
+ if (rootCause instanceof AccountException) {
+ request.getRequestDispatcher("/views/jsp/accountException.jsp").forward(request, response);
+ } else if (rootCause instanceof BusinessException) {
+ request.getRequestDispatcher("/views/jsp/businessException.jsp").forward(request, response);
+ } else {
+ request.getRequestDispatcher("/views/jsp/exception.jsp").forward(request, response);
+ }
+ }
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/FilterImpl.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/FilterImpl.java
new file mode 100644
index 00000000..7424572d
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/FilterImpl.java
@@ -0,0 +1,36 @@
+package io.github.dunwu.javaee.filter;
+
+import java.io.IOException;
+import javax.servlet.*;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class FilterImpl implements Filter {
+
+ private boolean enable;
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ // 初始化代码
+ enable = "true".equals(filterConfig.getInitParameter("enable"));
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ System.out.println("befor doFilter(). ");
+
+ chain.doFilter(request, response);
+
+ System.out.println("after doFitler(). ");
+ }
+
+ @Override
+ public void destroy() {
+ // 资源销毁代码
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/GZipFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/GZipFilter.java
new file mode 100644
index 00000000..96a39a07
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/GZipFilter.java
@@ -0,0 +1,44 @@
+package io.github.dunwu.javaee.filter;
+
+import io.github.dunwu.javaee.filter.wrapper.GZipResponseWrapper;
+import java.io.IOException;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class GZipFilter extends MyFilter {
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ logger.info("{} 开始做过滤处理", this.getClass().getName());
+
+ HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+
+ String acceptEncoding = httpServletRequest.getHeader("Accept-Encoding");
+ System.out.println("Accept-Encoding: " + acceptEncoding);
+
+ if (acceptEncoding != null && acceptEncoding.toLowerCase().indexOf("gzip") != -1) {
+
+ // 如果客户浏览器支持 GZIP 格式, 则使用 GZIP 压缩数据
+ GZipResponseWrapper gzipResponse = new GZipResponseWrapper(httpServletResponse);
+ chain.doFilter(httpServletRequest, gzipResponse);
+
+ // 输出压缩数据
+ gzipResponse.finishResponse();
+ } else {
+ // 否则, 不压缩
+ chain.doFilter(httpServletRequest, httpServletResponse);
+ }
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/ImageRedirectFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/ImageRedirectFilter.java
new file mode 100644
index 00000000..9fca074a
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/ImageRedirectFilter.java
@@ -0,0 +1,44 @@
+package io.github.dunwu.javaee.filter;
+
+import java.io.IOException;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class ImageRedirectFilter extends MyFilter {
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ logger.info("{} 开始做过滤处理", this.getClass().getName());
+
+ HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+
+ // 禁止缓存
+ httpServletResponse.setHeader("Cache-Control", "no-store");
+ httpServletResponse.setHeader("Pragrma", "no-cache");
+ httpServletResponse.setDateHeader("Expires", 0);
+
+ // 链接来源地址
+ String referer = httpServletRequest.getHeader("referer");
+
+ if (referer == null || !referer.contains(httpServletRequest.getServerName())) {
+ // 如果 链接地址来自其他网站,则返回错误图片
+ httpServletRequest.getRequestDispatcher("/views/images/error.gif").forward(httpServletRequest,
+ httpServletResponse);
+ } else {
+ // 图片正常显示
+ chain.doFilter(httpServletRequest, httpServletResponse);
+ }
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/LogFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/LogFilter.java
new file mode 100644
index 00000000..c57eb31e
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/LogFilter.java
@@ -0,0 +1,36 @@
+package io.github.dunwu.javaee.filter;
+
+import java.io.IOException;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class LogFilter extends MyFilter {
+
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
+ throws IOException, ServletException {
+
+ HttpServletRequest request = (HttpServletRequest) req;
+ HttpServletResponse response = (HttpServletResponse) res;
+
+ long startTime = System.currentTimeMillis();
+ String requestURI = request.getRequestURI();
+
+ requestURI = request.getQueryString() == null ? requestURI : (requestURI + "?" + request.getQueryString());
+
+ chain.doFilter(request, response);
+
+ long endTime = System.currentTimeMillis();
+
+ logger.info("{} 访问了 {},总用时 {} 毫秒", request.getRemoteAddr(), requestURI, (endTime - startTime));
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/MyFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/MyFilter.java
new file mode 100644
index 00000000..5c6f76a3
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/MyFilter.java
@@ -0,0 +1,36 @@
+package io.github.dunwu.javaee.filter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import javax.servlet.*;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public abstract class MyFilter implements Filter {
+
+ protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private String filterName;
+
+ @Override
+ public void init(FilterConfig filterConfig) {
+ // 获取 Filter 的 name,配置在 web.xml 中
+ filterName = filterConfig.getFilterName();
+ logger.info("启动 Filter: {}", filterName);
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ }
+
+ @Override
+ public void destroy() {
+ logger.info("关闭 Filter: {}", filterName);
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/OutputReplaceFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/OutputReplaceFilter.java
new file mode 100644
index 00000000..96e6996f
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/OutputReplaceFilter.java
@@ -0,0 +1,58 @@
+package io.github.dunwu.javaee.filter;
+
+import io.github.dunwu.javaee.filter.wrapper.HttpCharacterResponseWrapper;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class OutputReplaceFilter extends MyFilter {
+
+ private Properties properties = new Properties();
+
+ @Override
+ public void init(FilterConfig filterConfig) {
+ super.init(filterConfig);
+ String file = filterConfig.getInitParameter("file");
+ String realPath = filterConfig.getServletContext().getRealPath(file);
+ try {
+ properties.load(new FileInputStream(realPath));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ logger.info("{} 开始做过滤处理", this.getClass().getName());
+
+ // 自定义的 response
+ HttpCharacterResponseWrapper wrapper = new HttpCharacterResponseWrapper((HttpServletResponse) response);
+
+ // 提交给 Servlet 或者下一个 Filter
+ chain.doFilter(request, wrapper);
+
+ // 得到缓存在自定义 response 中的输出内容
+ String output = wrapper.getCharArrayWriter().toString();
+
+ // 修改,替换
+ for (Object obj : properties.keySet()) {
+ String key = (String) obj;
+ output = output.replace(key, properties.getProperty(key));
+ }
+
+ // 输出
+ PrintWriter out = response.getWriter();
+ out.write(output);
+ out.println("");
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/PrivilegeFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/PrivilegeFilter.java
new file mode 100644
index 00000000..37ef77b8
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/PrivilegeFilter.java
@@ -0,0 +1,79 @@
+package io.github.dunwu.javaee.filter;
+
+import io.github.dunwu.javaee.filter.exception.AccountException;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class PrivilegeFilter extends MyFilter {
+
+ private Properties pp = new Properties();
+
+ @Override
+ public void init(FilterConfig config) {
+
+ // 从 初始化参数 中获取权 限配置文件 的位置
+ String file = config.getInitParameter("file");
+ String realPath = config.getServletContext().getRealPath(file);
+ try {
+ pp.load(new FileInputStream(realPath));
+ } catch (Exception e) {
+ config.getServletContext().log("读取权限控制文件失败。", e);
+ }
+ }
+
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
+ throws IOException, ServletException {
+
+ logger.info("{} 开始做过滤处理", this.getClass().getName());
+
+ HttpServletRequest request = (HttpServletRequest) req;
+
+ // 获取访问的路径,例如:admin.jsp
+ String requestURI = request.getRequestURI().replace(request.getContextPath() + "/", "");
+
+ // 获取 action 参数,例如:add
+ String action = req.getParameter("action");
+ action = action == null ? "" : action;
+
+ // 拼接成 URI。例如:log.do?action=list
+ String uri = requestURI + "?action=" + action;
+
+ // 从 session 中获取用户权限角色。
+ String role = (String) request.getSession(true).getAttribute("role");
+ role = role == null ? "guest" : role;
+
+ boolean authentificated = false;
+ // 开始检查该用户角色是否有权限访问 uri
+ for (Object obj : pp.keySet()) {
+ String key = ((String) obj);
+ // 使用正则表达式验证 需要将 ? . 替换一下,并将通配符 * 处理一下
+ if (uri.matches(key.replace("?", "\\?").replace(".", "\\.").replace("*", ".*"))) {
+ // 如果 role 匹配
+ if (role.equals(pp.get(key))) {
+ authentificated = true;
+ break;
+ }
+ }
+ }
+ if (!authentificated) {
+ throw new RuntimeException(new AccountException("您无权访问该页面。请以合适的身份登陆后查看。"));
+ }
+ // 继续运行
+ chain.doFilter(req, res);
+ }
+
+ @Override
+ public void destroy() {
+ pp = null;
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/UploadFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/UploadFilter.java
new file mode 100644
index 00000000..3ae65193
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/UploadFilter.java
@@ -0,0 +1,24 @@
+package io.github.dunwu.javaee.filter;
+
+import io.github.dunwu.javaee.filter.wrapper.UploadRequestWrapper;
+import java.io.IOException;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author Zhang Peng
+ * @since 2017-04-04
+ */
+public class UploadFilter extends MyFilter {
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ UploadRequestWrapper uploadRequest = new UploadRequestWrapper((HttpServletRequest) request);
+ chain.doFilter(uploadRequest, response);
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/WaterMarkFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/WaterMarkFilter.java
new file mode 100644
index 00000000..836b2065
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/WaterMarkFilter.java
@@ -0,0 +1,49 @@
+package io.github.dunwu.javaee.filter;
+
+import io.github.dunwu.javaee.filter.wrapper.WaterMarkResponseWrapper;
+
+import java.io.IOException;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class WaterMarkFilter extends MyFilter {
+
+ // 水印图片,配置在初始化参数中
+ private String waterMarkFile;
+
+ @Override
+ public void init(FilterConfig filterConfig) {
+ super.init(filterConfig);
+ String file = filterConfig.getInitParameter("waterMarkFile");
+ waterMarkFile = filterConfig.getServletContext().getRealPath(file);
+ }
+
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
+ throws IOException, ServletException {
+ logger.info("{} 开始做过滤处理", this.getClass().getName());
+
+ HttpServletRequest request = (HttpServletRequest) req;
+ HttpServletResponse response = (HttpServletResponse) res;
+
+ String requestURI = request.getRequestURI();
+
+ String originImageFile = request.getServletContext().getRealPath("/") + requestURI;
+
+ // 自定义的response
+ WaterMarkResponseWrapper waterMarkRes = new WaterMarkResponseWrapper(response, originImageFile, waterMarkFile);
+
+ chain.doFilter(request, waterMarkRes);
+
+ // 打水印,输出到客户端浏览器
+ waterMarkRes.finishResponse();
+
+ logger.info("图片 {} 已添加水印图片 {}", originImageFile, waterMarkFile);
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/XSLTFilter.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/XSLTFilter.java
new file mode 100644
index 00000000..2e013440
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/XSLTFilter.java
@@ -0,0 +1,69 @@
+package io.github.dunwu.javaee.filter;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/28.
+ */
+public class XSLTFilter extends MyFilter {
+
+ private ServletContext servletContext;
+
+ @Override
+ public void init(FilterConfig filterConfig) {
+ super.init(filterConfig);
+ servletContext = filterConfig.getServletContext();
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ logger.info("{} 开始做过滤处理", this.getClass().getName());
+
+ HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+
+ // 格式样本文件:/book.xsl
+ Source styleSource = new StreamSource(servletContext.getRealPath("/views/xml/messageLog.xsl"));
+
+ // 请求的 xml 文件
+ Source xmlSource = new StreamSource(servletContext
+ .getRealPath(httpServletRequest.getRequestURI().replace(httpServletRequest.getContextPath() + "", "")));
+ try {
+
+ // 转换器工厂
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+
+ // 转换器
+ Transformer transformer = transformerFactory.newTransformer(styleSource);
+
+ // 将转换的结果保存到该对象中
+ CharArrayWriter charArrayWriter = new CharArrayWriter();
+ StreamResult result = new StreamResult(charArrayWriter);
+
+ // 转换
+ transformer.transform(xmlSource, result);
+
+ // 输出转换后的结果
+ httpServletResponse.setContentType("text/html");
+ httpServletResponse.setContentLength(charArrayWriter.toString().length());
+ PrintWriter out = httpServletResponse.getWriter();
+ out.write(charArrayWriter.toString());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/exception/AccountException.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/exception/AccountException.java
new file mode 100644
index 00000000..96a6d42b
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/exception/AccountException.java
@@ -0,0 +1,11 @@
+package io.github.dunwu.javaee.filter.exception;
+
+public class AccountException extends Exception {
+
+ private static final long serialVersionUID = -3040955562136599570L;
+
+ public AccountException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/exception/BusinessException.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/exception/BusinessException.java
new file mode 100644
index 00000000..c3662556
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/exception/BusinessException.java
@@ -0,0 +1,11 @@
+package io.github.dunwu.javaee.filter.exception;
+
+public class BusinessException extends Exception {
+
+ private static final long serialVersionUID = -3040955562136599570L;
+
+ public BusinessException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/test/Download.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/test/Download.java
new file mode 100644
index 00000000..26c115bf
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/test/Download.java
@@ -0,0 +1,36 @@
+package io.github.dunwu.javaee.filter.test;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+
+public class Download {
+
+ public static void main(String[] args) throws Exception {
+ System.out.println(getContent("http://localhost:8080/filter/book/thinkInJava.xml"));
+ }
+
+ public static String getContent(String url) throws Exception {
+
+ URL r = new URL(url);
+
+ r.openConnection();
+
+ InputStream ins = r.openStream();
+
+ Reader reader = new InputStreamReader(ins);
+
+ int len = 0;
+ char[] tmp = new char[1024];
+
+ StringBuffer buffer = new StringBuffer();
+
+ while ((len = reader.read(tmp)) != -1) {
+ buffer.append(tmp, 0, len);
+ }
+
+ return buffer.toString();
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/test/GZipTest.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/test/GZipTest.java
new file mode 100644
index 00000000..9b4899aa
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/test/GZipTest.java
@@ -0,0 +1,34 @@
+package io.github.dunwu.javaee.filter.test;
+
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.NumberFormat;
+
+public class GZipTest {
+
+ public static void main(String[] args) throws Exception {
+ test("http://localhost:8080/filter/dojo/dojo.js");
+ test("http://localhost:8080/filter/image.jsp");
+ test("http://localhost:8080/filter/winter.jpg");
+ }
+
+ public static void test(String url) throws Exception {
+
+ /** 支持 GZIP 的连接 */
+ URLConnection connGzip = new URL(url).openConnection();
+ connGzip.setRequestProperty("Accept-Encoding", "gzip");
+ int lengthGzip = connGzip.getContentLength();
+
+ /** 不支持 GZIP 的连接 */
+ URLConnection connCommon = new URL(url).openConnection();
+ int lengthCommon = connCommon.getContentLength();
+
+ double rate = new Double(lengthGzip) / lengthCommon;
+
+ System.out.println("网址: " + url);
+ System.out.println("压缩后: " + lengthGzip + " byte, \t压缩前: " + lengthCommon + " byte, \t比率: "
+ + NumberFormat.getPercentInstance().format(rate));
+ System.out.println();
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/CacheResponseWrapper.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/CacheResponseWrapper.java
new file mode 100644
index 00000000..79d8bf0c
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/CacheResponseWrapper.java
@@ -0,0 +1,44 @@
+package io.github.dunwu.javaee.filter.wrapper;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class CacheResponseWrapper extends HttpServletResponseWrapper {
+
+ // 缓存字符类输出
+ private CharArrayWriter cacheWriter = new CharArrayWriter();
+
+ public CacheResponseWrapper(HttpServletResponse response) throws IOException {
+ super(response);
+ }
+
+ @Override
+ public PrintWriter getWriter() throws IOException {
+ return new PrintWriter(cacheWriter);
+ }
+
+ @Override
+ public void flushBuffer() throws IOException {
+ cacheWriter.flush();
+ }
+
+ public void finishResponse() throws IOException {
+ cacheWriter.close();
+ }
+
+ public CharArrayWriter getCacheWriter() {
+ return cacheWriter;
+ }
+
+ public void setCacheWriter(CharArrayWriter cacheWriter) {
+ this.cacheWriter = cacheWriter;
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/GZipOutputStream.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/GZipOutputStream.java
new file mode 100644
index 00000000..48e1adc1
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/GZipOutputStream.java
@@ -0,0 +1,74 @@
+package io.github.dunwu.javaee.filter.wrapper;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.GZIPOutputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class GZipOutputStream extends ServletOutputStream {
+
+ private HttpServletResponse response;
+
+ // JDK 自带的压缩数据的类
+ private GZIPOutputStream gzipOutputStream;
+
+ // 将压缩后的数据存放到 ByteArrayOutputStream 对象中
+ private ByteArrayOutputStream byteArrayOutputStream;
+
+ public GZipOutputStream(HttpServletResponse response) throws IOException {
+ this.response = response;
+ byteArrayOutputStream = new ByteArrayOutputStream();
+ gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
+ }
+
+ public void write(int b) throws IOException {
+ gzipOutputStream.write(b);
+ }
+
+ public void close() throws IOException {
+
+ // 压缩完毕 一定要调用该方法
+ gzipOutputStream.finish();
+
+ // 将压缩后的数据输出到客户端
+ byte[] content = byteArrayOutputStream.toByteArray();
+
+ // 设定压缩方式为 GZIP, 客户端浏览器会自动将数据解压
+ response.addHeader("Content-Encoding", "gzip");
+ response.addHeader("Content-Length", Integer.toString(content.length));
+
+ // 输出
+ ServletOutputStream out = response.getOutputStream();
+ out.write(content);
+ out.close();
+ }
+
+ public void flush() throws IOException {
+ gzipOutputStream.flush();
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ gzipOutputStream.write(b, off, len);
+ }
+
+ public void write(byte[] b) throws IOException {
+ gzipOutputStream.write(b);
+ }
+
+ @Override
+ public boolean isReady() {
+ return false;
+ }
+
+ @Override
+ public void setWriteListener(WriteListener writeListener) {
+
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/GZipResponseWrapper.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/GZipResponseWrapper.java
new file mode 100644
index 00000000..f230b860
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/GZipResponseWrapper.java
@@ -0,0 +1,61 @@
+package io.github.dunwu.javaee.filter.wrapper;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class GZipResponseWrapper extends HttpServletResponseWrapper {
+
+ // 默认的 response
+ private HttpServletResponse response;
+
+ // 自定义的 outputStream, 执行close()的时候对数据压缩,并输出
+ private GZipOutputStream gzipOutputStream;
+
+ // 自定义 printWriter,将内容输出到 GZipOutputStream 中
+ private PrintWriter writer;
+
+ public GZipResponseWrapper(HttpServletResponse response) throws IOException {
+ super(response);
+ this.response = response;
+ }
+
+ public ServletOutputStream getOutputStream() throws IOException {
+ if (gzipOutputStream == null) {
+ gzipOutputStream = new GZipOutputStream(response);
+ }
+ return gzipOutputStream;
+ }
+
+ public PrintWriter getWriter() throws IOException {
+ if (writer == null) {
+ writer = new PrintWriter(new OutputStreamWriter(new GZipOutputStream(response), "UTF-8"));
+ }
+ return writer;
+ }
+
+ // 压缩后数据长度会发生变化 因此将该方法内容置空
+ public void setContentLength(int contentLength) {
+ }
+
+ public void flushBuffer() throws IOException {
+ gzipOutputStream.flush();
+ }
+
+ public void finishResponse() throws IOException {
+ if (gzipOutputStream != null) {
+ gzipOutputStream.close();
+ }
+ if (writer != null) {
+ writer.close();
+ }
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/HttpCharacterResponseWrapper.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/HttpCharacterResponseWrapper.java
new file mode 100644
index 00000000..d9798ee2
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/HttpCharacterResponseWrapper.java
@@ -0,0 +1,30 @@
+package io.github.dunwu.javaee.filter.wrapper;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class HttpCharacterResponseWrapper extends HttpServletResponseWrapper {
+
+ private CharArrayWriter charArrayWriter = new CharArrayWriter();
+
+ public HttpCharacterResponseWrapper(HttpServletResponse response) {
+ super(response);
+ }
+
+ @Override
+ public PrintWriter getWriter() throws IOException {
+ return new PrintWriter(charArrayWriter);
+ }
+
+ public CharArrayWriter getCharArrayWriter() {
+ return charArrayWriter;
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/UploadRequestWrapper.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/UploadRequestWrapper.java
new file mode 100644
index 00000000..612bf7c2
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/UploadRequestWrapper.java
@@ -0,0 +1,105 @@
+package io.github.dunwu.javaee.filter.wrapper;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import org.apache.commons.fileupload.DiskFileUpload;
+import org.apache.commons.fileupload.FileItem;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class UploadRequestWrapper extends HttpServletRequestWrapper {
+
+ private static final String MULTIPART_HEADER = "Content-type";
+
+ // 是否是上传文件
+ private boolean multipart;
+
+ // map,保存所有的域
+ private Map params = new HashMap();
+
+ @SuppressWarnings("all")
+ public UploadRequestWrapper(HttpServletRequest request) {
+
+ super(request);
+
+ // 判断是否为上传文件
+ multipart = request.getHeader(MULTIPART_HEADER) != null
+ && request.getHeader(MULTIPART_HEADER).startsWith("multipart/form-data");
+
+ if (multipart) {
+
+ try {
+ // 使用apache的工具解析
+ DiskFileUpload upload = new DiskFileUpload();
+ upload.setHeaderEncoding("utf8");
+
+ // 解析,获得所有的文本域与文件域
+ List fileItems = upload.parseRequest(request);
+
+ for (Iterator it = fileItems.iterator(); it.hasNext(); ) {
+
+ // 遍历
+ FileItem item = it.next();
+ if (item.isFormField()) {
+
+ // 如果是文本域,直接放到map里
+ params.put(item.getFieldName(), item.getString("utf8"));
+ } else {
+
+ // 否则,为文件,先获取文件名称
+ String filename = item.getName().replace("\\", "/");
+ filename = filename.substring(filename.lastIndexOf("/") + 1);
+
+ // 保存到系统临时文件夹中
+ File file = new File(System.getProperty("java.io.tmpdir"), filename);
+
+ // 保存文件内容
+ OutputStream ous = new FileOutputStream(file);
+ ous.write(item.get());
+ ous.close();
+
+ // 放到map中
+ params.put(item.getFieldName(), file);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+
+ System.out.println(System.getProperties().toString().replace(", ", "\r\n"));
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+
+ // 如果为上传文件,则从map中取值
+ if (multipart && params.containsKey(name)) {
+ return params.get(name);
+ }
+ return super.getAttribute(name);
+ }
+
+ @Override
+ public String getParameter(String name) {
+
+ // 如果为上传文件,则从map中取值
+ if (multipart && params.containsKey(name)) {
+ return params.get(name).toString();
+ }
+ return super.getParameter(name);
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/WaterMarkOutputStream.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/WaterMarkOutputStream.java
new file mode 100644
index 00000000..b94322c4
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/WaterMarkOutputStream.java
@@ -0,0 +1,55 @@
+package io.github.dunwu.javaee.filter.wrapper;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class WaterMarkOutputStream extends ServletOutputStream {
+
+ // 缓冲图片数据
+ private ByteArrayOutputStream byteArrayOutputStream;
+
+ public WaterMarkOutputStream() throws IOException {
+ byteArrayOutputStream = new ByteArrayOutputStream();
+ }
+
+ @Override
+ public boolean isReady() {
+ return false;
+ }
+
+ @Override
+ public void setWriteListener(WriteListener writeListener) {
+
+ }
+
+ public void write(int b) throws IOException {
+ byteArrayOutputStream.write(b);
+ }
+
+ public void close() throws IOException {
+ byteArrayOutputStream.close();
+ }
+
+ public void flush() throws IOException {
+ byteArrayOutputStream.flush();
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ byteArrayOutputStream.write(b, off, len);
+ }
+
+ public void write(byte[] b) throws IOException {
+ byteArrayOutputStream.write(b);
+ }
+
+ public ByteArrayOutputStream getByteArrayOutputStream() {
+ return byteArrayOutputStream;
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/WaterMarkResponseWrapper.java b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/WaterMarkResponseWrapper.java
new file mode 100644
index 00000000..5f57cca4
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/java/io/github/dunwu/javaee/filter/wrapper/WaterMarkResponseWrapper.java
@@ -0,0 +1,60 @@
+package io.github.dunwu.javaee.filter.wrapper;
+
+import java.awt.image.BufferedImage;
+import java.io.FileInputStream;
+import java.io.IOException;
+import javax.imageio.ImageIO;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import net.coobird.thumbnailator.Thumbnails;
+import net.coobird.thumbnailator.geometry.Positions;
+
+/**
+ * @author Zhang Peng
+ * @since 2017/3/27.
+ */
+public class WaterMarkResponseWrapper extends HttpServletResponseWrapper {
+
+ private String originFile;
+
+ // 水印图片位置
+ private String waterMarkFile;
+
+ // 原response
+ private HttpServletResponse response;
+
+ // 自定义servletOutputStream,用于缓冲图像数据
+ private WaterMarkOutputStream waterMarkOutputStream;
+
+ public WaterMarkResponseWrapper(HttpServletResponse response, String originFile, String waterMarkFile)
+ throws IOException {
+ super(response);
+ this.response = response;
+ this.originFile = originFile;
+ this.waterMarkFile = waterMarkFile;
+ this.waterMarkOutputStream = new WaterMarkOutputStream();
+ }
+
+ // 覆盖getOutputStream(),返回自定义的waterMarkOutputStream
+ public ServletOutputStream getOutputStream() throws IOException {
+ return waterMarkOutputStream;
+ }
+
+ public void flushBuffer() throws IOException {
+ waterMarkOutputStream.flush();
+ }
+
+ // 将图像数据打水印,并输出到客户端浏览器
+ public void finishResponse() throws IOException {
+ FileInputStream fileInputStream = new FileInputStream(waterMarkFile);
+ BufferedImage wartermarkImage = ImageIO.read(fileInputStream);
+ Thumbnails.Builder builder = Thumbnails.of(this.originFile);
+ builder.scale(1.0, 1.0);
+ builder.watermark(Positions.BOTTOM_RIGHT, wartermarkImage, 0.5f);
+
+ // 打水印后的图片数据
+ builder.toOutputStream(response.getOutputStream());
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/main/resources/logback.xml b/codes/javaee/javaee-filter/src/main/resources/logback.xml
new file mode 100644
index 00000000..5f9e7061
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/resources/logback.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5p] %c.%M - %m%n
+
+
+
+
+
+
+
+ logs/${FILE_NAME}-all.%d{yyyy-MM-dd}.log
+ 30
+
+
+
+
+ 30MB
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5p] %c.%M - %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/codes/javatool/server/src/main/webapp/META-INF/MANIFEST.MF b/codes/javaee/javaee-filter/src/main/webapp/META-INF/MANIFEST.MF
similarity index 100%
rename from codes/javatool/server/src/main/webapp/META-INF/MANIFEST.MF
rename to codes/javaee/javaee-filter/src/main/webapp/META-INF/MANIFEST.MF
diff --git a/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/logo.png b/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/logo.png
new file mode 100644
index 00000000..39ac67c3
Binary files /dev/null and b/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/logo.png differ
diff --git a/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/privilege.properties b/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/privilege.properties
new file mode 100644
index 00000000..ab78f73c
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/privilege.properties
@@ -0,0 +1,8 @@
+# Privilege Settings
+admin.do?action\=*=administrator
+log.do?action\=*=administrator
+list.do?action\=add=member
+list.do?action\=delete=member
+list.do?action\=save=member
+list.do?action\=view=guest
+list.do?action\=list=guest
diff --git a/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/sensitive.properties b/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/sensitive.properties
new file mode 100644
index 00000000..cdc23204
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/sensitive.properties
@@ -0,0 +1,7 @@
+# \u81ea\u52a8\u66f4\u6b63
+Chna=China
+www.baidu.com.cn=www.baidu.com
+# \u81ea\u52a8\u66ff\u6362
+\u8272\u60c5=**
+\u60c5\u8272=**
+\u8d4c\u535a=**
diff --git a/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/web.xml b/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..4a7c507e
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,182 @@
+
+
+
+ javaee-filter
+
+
+
+ index.jsp
+ /views/jsp/index.jsp
+
+
+ index.jsp
+ /
+
+
+
+ dispatcherServlet
+ /views/jsp/dispatcher.jsp
+
+
+ dispatcherServlet
+ *.do
+
+
+
+
+
+ CacheFilter
+
+ io.github.dunwu.javaee.filter.CacheFilter
+
+
+ cache
+ true
+
+
+ cacheTime
+ 1000000
+
+
+
+ CacheFilter
+ *.jsp
+ *.html
+ *.do
+ REQUEST
+
+
+
+ characterEncodingFilter
+
+ io.github.dunwu.javaee.filter.CharacterEncodingFilter
+
+
+ characterEncoding
+ UTF-8
+
+
+ enable
+ true
+
+
+
+ characterEncodingFilter
+ /*
+
+
+
+ ExceptionHandlerFilter
+
+ io.github.dunwu.javaee.filter.ExceptionHandlerFilter
+
+
+
+ ExceptionHandlerFilter
+ /*
+
+
+
+ GZipFilter
+ io.github.dunwu.javaee.filter.GZipFilter
+
+
+ GZipFilter
+ /*
+
+
+
+ ImageRedirectFilter
+
+ io.github.dunwu.javaee.filter.ImageRedirectFilter
+
+
+
+ ImageRedirectFilter
+ /views/images/*
+
+
+
+ LogFilter
+ io.github.dunwu.javaee.filter.LogFilter
+
+
+ LogFilter
+ /*
+
+
+
+ OutputReplaceFilter
+
+ io.github.dunwu.javaee.filter.OutputReplaceFilter
+
+
+ file
+ /WEB-INF/sensitive.properties
+
+
+
+ OutputReplaceFilter
+ *.jsp
+
+
+
+ UploadFilter
+ io.github.dunwu.javaee.filter.UploadFilter
+
+
+ UploadFilter
+ /*
+
+
+
+ PrivilegeFilter
+
+ io.github.dunwu.javaee.filter.PrivilegeFilter
+
+
+ file
+ /WEB-INF/privilege.properties
+
+
+
+ PrivilegeFilter
+ *.do
+
+
+
+ WaterMarkFilter
+
+ io.github.dunwu.javaee.filter.WaterMarkFilter
+
+
+ waterMarkFile
+ /WEB-INF/logo.png
+
+
+
+ WaterMarkFilter
+ *.jpg
+ *.png
+ *.bmp
+
+
+
+ XSLTFilter
+ io.github.dunwu.javaee.filter.XSLTFilter
+
+
+ XSLTFilter
+ /views/xml/*
+
+
+
+
+ /views/jsp/index.html
+ /views/jsp/index.htm
+ /views/jsp/index.jsp
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/images/error.gif b/codes/javaee/javaee-filter/src/main/webapp/views/images/error.gif
new file mode 100644
index 00000000..b6922ac1
Binary files /dev/null and b/codes/javaee/javaee-filter/src/main/webapp/views/images/error.gif differ
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/images/mm.jpg b/codes/javaee/javaee-filter/src/main/webapp/views/images/mm.jpg
new file mode 100644
index 00000000..4c6de35c
Binary files /dev/null and b/codes/javaee/javaee-filter/src/main/webapp/views/images/mm.jpg differ
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/images/sunset.jpg b/codes/javaee/javaee-filter/src/main/webapp/views/images/sunset.jpg
new file mode 100644
index 00000000..860f6eec
Binary files /dev/null and b/codes/javaee/javaee-filter/src/main/webapp/views/images/sunset.jpg differ
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/images/winter.jpg b/codes/javaee/javaee-filter/src/main/webapp/views/images/winter.jpg
new file mode 100644
index 00000000..6db32ca1
Binary files /dev/null and b/codes/javaee/javaee-filter/src/main/webapp/views/images/winter.jpg differ
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/js/dojo.js b/codes/javaee/javaee-filter/src/main/webapp/views/js/dojo.js
new file mode 100644
index 00000000..d5a0fc94
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/js/dojo.js
@@ -0,0 +1,6812 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+ */
+
+/*
+ This is a compiled version of Dojo, built for deployment and not for
+ development. To get an editable version, please visit:
+
+ http://dojotoolkit.org
+
+ for documentation and information on getting the source.
+ */
+
+if (typeof dojo == 'undefined') {
+ var dj_global = this;
+ var dj_currentContext = this;
+
+ function dj_undef(_1, _2) {
+ return (typeof (_2 || dj_currentContext)[_1] == 'undefined')
+ }
+
+ if (dj_undef('djConfig', this)) {
+ var djConfig = {}
+ }
+ if (dj_undef('dojo', this)) {
+ var dojo = {}
+ }
+ dojo.global = function () {
+ return dj_currentContext
+ };
+ dojo.locale = djConfig.locale;
+ dojo.version = {
+ major: 0,
+ minor: 4,
+ patch: 3,
+ flag: '-20070622',
+ revision: Number('$Rev: 8617 $'.match(/[0-9]+/)[0]),
+ toString: function () {
+ with (dojo.version) {
+ return major + '.' + minor + '.' + patch + flag + ' (' + revision + ')'
+ }
+ }
+ };
+ dojo.evalProp = function (_3, _4, _5) {
+ if ((!_4) || (!_3)) {
+ return undefined
+ }
+ if (!dj_undef(_3, _4)) {
+ return _4[_3]
+ }
+ return (_5 ? (_4[_3] = {}) : undefined)
+ };
+ dojo.parseObjPath = function (_6, _7, _8) {
+ var _9 = (_7 || dojo.global());
+ var _a = _6.split('.');
+ var _b = _a.pop();
+ for (var i = 0, l = _a.length; i < l && _9; i++) {
+ _9 = dojo.evalProp(_a[i], _9, _8)
+ }
+ return {obj: _9, prop: _b}
+ };
+ dojo.evalObjPath = function (_e, _f) {
+ if (typeof _e != 'string') {
+ return dojo.global()
+ }
+ if (_e.indexOf('.') == -1) {
+ return dojo.evalProp(_e, dojo.global(), _f)
+ }
+ var ref = dojo.parseObjPath(_e, dojo.global(), _f);
+ if (ref) {
+ return dojo.evalProp(ref.prop, ref.obj, _f)
+ }
+ return null
+ };
+ dojo.errorToString = function (_11) {
+ if (!dj_undef('message', _11)) {
+ return _11.message
+ } else {
+ if (!dj_undef('description', _11)) {
+ return _11.description
+ } else {
+ return _11
+ }
+ }
+ };
+ dojo.raise = function (_12, _13) {
+ if (_13) {
+ _12 = _12 + ': ' + dojo.errorToString(_13)
+ } else {
+ _12 = dojo.errorToString(_12)
+ }
+ try {
+ if (djConfig.isDebug) {
+ dojo.hostenv.println('FATAL exception raised: ' + _12)
+ }
+ } catch (e) {
+ }
+ throw _13 || Error(_12)
+ };
+ dojo.debug = function () {
+ };
+ dojo.debugShallow = function (obj) {
+ };
+ dojo.profile = {
+ start: function () {
+ }, end: function () {
+ }, stop: function () {
+ }, dump: function () {
+ }
+ };
+
+ function dj_eval(_15) {
+ return dj_global.eval ? dj_global.eval(_15) : eval(_15)
+ }
+
+ dojo.unimplemented = function (_16, _17) {
+ var _18 = '\'' + _16 + '\' not implemented';
+ if (_17 != null) {
+ _18 += ' ' + _17
+ }
+ dojo.raise(_18)
+ };
+ dojo.deprecated = function (_19, _1a, _1b) {
+ var _1c = 'DEPRECATED: ' + _19;
+ if (_1a) {
+ _1c += ' ' + _1a
+ }
+ if (_1b) {
+ _1c += ' -- will be removed in version: ' + _1b
+ }
+ dojo.debug(_1c)
+ };
+ dojo.render = (function () {
+ function vscaffold(_1d, _1e) {
+ var tmp = {capable: false, support: {builtin: false, plugin: false}, prefixes: _1d};
+ for (var i = 0; i < _1e.length; i++) {
+ tmp[_1e[i]] = false
+ }
+ return tmp
+ }
+
+ return {
+ name: '',
+ ver: dojo.version,
+ os: {win: false, linux: false, osx: false},
+ html: vscaffold(['html'], ['ie', 'opera', 'khtml', 'safari', 'moz']),
+ svg: vscaffold(['svg'], ['corel', 'adobe', 'batik']),
+ vml: vscaffold(['vml'], ['ie']),
+ swf: vscaffold(['Swf', 'Flash', 'Mm'], ['mm']),
+ swt: vscaffold(['Swt'], ['ibm'])
+ }
+ })();
+ dojo.hostenv = (function () {
+ var _21 = {
+ isDebug: false,
+ allowQueryConfig: false,
+ baseScriptUri: '',
+ baseRelativePath: '',
+ libraryScriptUri: '',
+ iePreventClobber: false,
+ ieClobberMinimal: true,
+ preventBackButtonFix: true,
+ delayMozLoadingFix: false,
+ searchIds: [],
+ parseWidgets: true
+ };
+ if (typeof djConfig == 'undefined') {
+ djConfig = _21
+ } else {
+ for (var _22 in _21) {
+ if (typeof djConfig[_22] == 'undefined') {
+ djConfig[_22] = _21[_22]
+ }
+ }
+ }
+ return {
+ name_: '(unset)', version_: '(unset)', getName: function () {
+ return this.name_
+ }, getVersion: function () {
+ return this.version_
+ }, getText: function (uri) {
+ dojo.unimplemented('getText', 'uri=' + uri)
+ }
+ }
+ })();
+ dojo.hostenv.getBaseScriptUri = function () {
+ if (djConfig.baseScriptUri.length) {
+ return djConfig.baseScriptUri
+ }
+ var uri = String(djConfig.libraryScriptUri || djConfig.baseRelativePath);
+ if (!uri) {
+ dojo.raise('Nothing returned by getLibraryScriptUri(): ' + uri)
+ }
+ var _25 = uri.lastIndexOf('/');
+ djConfig.baseScriptUri = djConfig.baseRelativePath;
+ return djConfig.baseScriptUri
+ };
+ (function () {
+ var _26 = {
+ pkgFileName: '__package__',
+ loading_modules_: {},
+ loaded_modules_: {},
+ addedToLoadingCount: [],
+ removedFromLoadingCount: [],
+ inFlightCount: 0,
+ modulePrefixes_: {dojo: {name: 'dojo', value: 'src'}},
+ setModulePrefix: function (_27, _28) {
+ this.modulePrefixes_[_27] = {name: _27, value: _28}
+ },
+ moduleHasPrefix: function (_29) {
+ var mp = this.modulePrefixes_;
+ return Boolean(mp[_29] && mp[_29].value)
+ },
+ getModulePrefix: function (_2b) {
+ if (this.moduleHasPrefix(_2b)) {
+ return this.modulePrefixes_[_2b].value
+ }
+ return _2b
+ },
+ getTextStack: [],
+ loadUriStack: [],
+ loadedUris: [],
+ post_load_: false,
+ modulesLoadedListeners: [],
+ unloadListeners: [],
+ loadNotifying: false
+ };
+ for (var _2c in _26) {
+ dojo.hostenv[_2c] = _26[_2c]
+ }
+ })();
+ dojo.hostenv.loadPath = function (_2d, _2e, cb) {
+ var uri;
+ if (_2d.charAt(0) == '/' || _2d.match(/^\w+:/)) {
+ uri = _2d
+ } else {
+ uri = this.getBaseScriptUri() + _2d
+ }
+ if (djConfig.cacheBust && dojo.render.html.capable) {
+ uri += '?' + String(djConfig.cacheBust).replace(/\W+/g, '')
+ }
+ try {
+ return !_2e ? this.loadUri(uri, cb) : this.loadUriAndCheck(uri, _2e, cb)
+ } catch (e) {
+ dojo.debug(e);
+ return false
+ }
+ };
+ dojo.hostenv.loadUri = function (uri, cb) {
+ if (this.loadedUris[uri]) {
+ return true
+ }
+ var _33 = this.getText(uri, null, true);
+ if (!_33) {
+ return false
+ }
+ this.loadedUris[uri] = true;
+ if (cb) {
+ _33 = '(' + _33 + ')'
+ }
+ var _34 = dj_eval(_33);
+ if (cb) {
+ cb(_34)
+ }
+ return true
+ };
+ dojo.hostenv.loadUriAndCheck = function (uri, _36, cb) {
+ var ok = true;
+ try {
+ ok = this.loadUri(uri, cb)
+ } catch (e) {
+ dojo.debug('failed loading ', uri, ' with error: ', e)
+ }
+ return Boolean(ok && this.findModule(_36, false))
+ };
+ dojo.loaded = function () {
+ };
+ dojo.unloaded = function () {
+ };
+ dojo.hostenv.loaded = function () {
+ this.loadNotifying = true;
+ this.post_load_ = true;
+ var mll = this.modulesLoadedListeners;
+ for (var x = 0; x < mll.length; x++) {
+ mll[x]()
+ }
+ this.modulesLoadedListeners = [];
+ this.loadNotifying = false;
+ dojo.loaded()
+ };
+ dojo.hostenv.unloaded = function () {
+ var mll = this.unloadListeners;
+ while (mll.length) {
+ (mll.pop())()
+ }
+ dojo.unloaded()
+ };
+ dojo.addOnLoad = function (obj, _3d) {
+ var dh = dojo.hostenv;
+ if (arguments.length == 1) {
+ dh.modulesLoadedListeners.push(obj)
+ } else {
+ if (arguments.length > 1) {
+ dh.modulesLoadedListeners.push(function () {
+ obj[_3d]()
+ })
+ }
+ }
+ if (dh.post_load_ && dh.inFlightCount == 0 && !dh.loadNotifying) {
+ dh.callLoaded()
+ }
+ };
+ dojo.addOnUnload = function (obj, _40) {
+ var dh = dojo.hostenv;
+ if (arguments.length == 1) {
+ dh.unloadListeners.push(obj)
+ } else {
+ if (arguments.length > 1) {
+ dh.unloadListeners.push(function () {
+ obj[_40]()
+ })
+ }
+ }
+ };
+ dojo.hostenv.modulesLoaded = function () {
+ if (this.post_load_) {
+ return
+ }
+ if (this.loadUriStack.length == 0 && this.getTextStack.length == 0) {
+ if (this.inFlightCount > 0) {
+ dojo.debug('files still in flight!');
+ return
+ }
+ dojo.hostenv.callLoaded()
+ }
+ };
+ dojo.hostenv.callLoaded = function () {
+ if (typeof setTimeout == 'object' || (djConfig['useXDomain'] && dojo.render.html.opera)) {
+ setTimeout('dojo.hostenv.loaded();', 0)
+ } else {
+ dojo.hostenv.loaded()
+ }
+ };
+ dojo.hostenv.getModuleSymbols = function (_42) {
+ var _43 = _42.split('.');
+ for (var i = _43.length; i > 0; i--) {
+ var _45 = _43.slice(0, i).join('.');
+ if ((i == 1) && !this.moduleHasPrefix(_45)) {
+ _43[0] = '../' + _43[0]
+ } else {
+ var _46 = this.getModulePrefix(_45);
+ if (_46 != _45) {
+ _43.splice(0, i, _46);
+ break
+ }
+ }
+ }
+ return _43
+ };
+ dojo.hostenv._global_omit_module_check = false;
+ dojo.hostenv.loadModule = function (_47, _48, _49) {
+ if (!_47) {
+ return
+ }
+ _49 = this._global_omit_module_check || _49;
+ var _4a = this.findModule(_47, false);
+ if (_4a) {
+ return _4a
+ }
+ if (dj_undef(_47, this.loading_modules_)) {
+ this.addedToLoadingCount.push(_47)
+ }
+ this.loading_modules_[_47] = 1;
+ var _4b = _47.replace(/\./g, '/') + '.js';
+ var _4c = _47.split('.');
+ var _4d = this.getModuleSymbols(_47);
+ var _4e = ((_4d[0].charAt(0) != '/') && !_4d[0].match(/^\w+:/));
+ var _4f = _4d[_4d.length - 1];
+ var ok;
+ if (_4f == '*') {
+ _47 = _4c.slice(0, -1).join('.');
+ while (_4d.length) {
+ _4d.pop();
+ _4d.push(this.pkgFileName);
+ _4b = _4d.join('/') + '.js';
+ if (_4e && _4b.charAt(0) == '/') {
+ _4b = _4b.slice(1)
+ }
+ ok = this.loadPath(_4b, !_49 ? _47 : null);
+ if (ok) {
+ break
+ }
+ _4d.pop()
+ }
+ } else {
+ _4b = _4d.join('/') + '.js';
+ _47 = _4c.join('.');
+ var _51 = !_49 ? _47 : null;
+ ok = this.loadPath(_4b, _51);
+ if (!ok && !_48) {
+ _4d.pop();
+ while (_4d.length) {
+ _4b = _4d.join('/') + '.js';
+ ok = this.loadPath(_4b, _51);
+ if (ok) {
+ break
+ }
+ _4d.pop();
+ _4b = _4d.join('/') + '/' + this.pkgFileName + '.js';
+ if (_4e && _4b.charAt(0) == '/') {
+ _4b = _4b.slice(1)
+ }
+ ok = this.loadPath(_4b, _51);
+ if (ok) {
+ break
+ }
+ }
+ }
+ if (!ok && !_49) {
+ dojo.raise('Could not load \'' + _47 + '\'; last tried \'' + _4b + '\'')
+ }
+ }
+ if (!_49 && !this['isXDomain']) {
+ _4a = this.findModule(_47, false);
+ if (!_4a) {
+ dojo.raise('symbol \'' + _47 + '\' is not defined after loading \'' + _4b + '\'')
+ }
+ }
+ return _4a
+ };
+ dojo.hostenv.startPackage = function (_52) {
+ var _53 = String(_52);
+ var _54 = _53;
+ var _55 = _52.split(/\./);
+ if (_55[_55.length - 1] == '*') {
+ _55.pop();
+ _54 = _55.join('.')
+ }
+ var _56 = dojo.evalObjPath(_54, true);
+ this.loaded_modules_[_53] = _56;
+ this.loaded_modules_[_54] = _56;
+ return _56
+ };
+ dojo.hostenv.findModule = function (_57, _58) {
+ var lmn = String(_57);
+ if (this.loaded_modules_[lmn]) {
+ return this.loaded_modules_[lmn]
+ }
+ if (_58) {
+ dojo.raise('no loaded module named \'' + _57 + '\'')
+ }
+ return null
+ };
+ dojo.kwCompoundRequire = function (_5a) {
+ var _5b = _5a['common'] || [];
+ var _5c = _5a[dojo.hostenv.name_] ? _5b.concat(_5a[dojo.hostenv.name_] || []) : _5b.concat(_5a['default'] || []);
+ for (var x = 0; x < _5c.length; x++) {
+ var _5e = _5c[x];
+ if (_5e.constructor == Array) {
+ dojo.hostenv.loadModule.apply(dojo.hostenv, _5e)
+ } else {
+ dojo.hostenv.loadModule(_5e)
+ }
+ }
+ };
+ dojo.require = function (_5f) {
+ dojo.hostenv.loadModule.apply(dojo.hostenv, arguments)
+ };
+ dojo.requireIf = function (_60, _61) {
+ var _62 = arguments[0];
+ if ((_62 === true) || (_62 == 'common') || (_62 && dojo.render[_62].capable)) {
+ var _63 = [];
+ for (var i = 1; i < arguments.length; i++) {
+ _63.push(arguments[i])
+ }
+ dojo.require.apply(dojo, _63)
+ }
+ };
+ dojo.requireAfterIf = dojo.requireIf;
+ dojo.provide = function (_65) {
+ return dojo.hostenv.startPackage.apply(dojo.hostenv, arguments)
+ };
+ dojo.registerModulePath = function (_66, _67) {
+ return dojo.hostenv.setModulePrefix(_66, _67)
+ };
+ if (djConfig['modulePaths']) {
+ for (var param in djConfig['modulePaths']) {
+ dojo.registerModulePath(param, djConfig['modulePaths'][param])
+ }
+ }
+ dojo.setModulePrefix = function (_68, _69) {
+ dojo.deprecated('dojo.setModulePrefix("' + _68 + '", "' + _69 + '")', 'replaced by dojo.registerModulePath', '0.5');
+ return dojo.registerModulePath(_68, _69)
+ };
+ dojo.exists = function (obj, _6b) {
+ var p = _6b.split('.');
+ for (var i = 0; i < p.length; i++) {
+ if (!obj[p[i]]) {
+ return false
+ }
+ obj = obj[p[i]]
+ }
+ return true
+ };
+ dojo.hostenv.normalizeLocale = function (_6e) {
+ var _6f = _6e ? _6e.toLowerCase() : dojo.locale;
+ if (_6f == 'root') {
+ _6f = 'ROOT'
+ }
+ return _6f
+ };
+ dojo.hostenv.searchLocalePath = function (_70, _71, _72) {
+ _70 = dojo.hostenv.normalizeLocale(_70);
+ var _73 = _70.split('-');
+ var _74 = [];
+ for (var i = _73.length; i > 0; i--) {
+ _74.push(_73.slice(0, i).join('-'))
+ }
+ _74.push(false);
+ if (_71) {
+ _74.reverse()
+ }
+ for (var j = _74.length - 1; j >= 0; j--) {
+ var loc = _74[j] || 'ROOT';
+ var _78 = _72(loc);
+ if (_78) {
+ break
+ }
+ }
+ };
+ dojo.hostenv.localesGenerated;
+ dojo.hostenv.registerNlsPrefix = function () {
+ dojo.registerModulePath('nls', 'nls')
+ };
+ dojo.hostenv.preloadLocalizations = function () {
+ if (dojo.hostenv.localesGenerated) {
+ dojo.hostenv.registerNlsPrefix();
+
+ function preload(_79) {
+ _79 = dojo.hostenv.normalizeLocale(_79);
+ dojo.hostenv.searchLocalePath(_79, true, function (loc) {
+ for (var i = 0; i < dojo.hostenv.localesGenerated.length; i++) {
+ if (dojo.hostenv.localesGenerated[i] == loc) {
+ dojo['require']('nls.dojo_' + loc);
+ return true
+ }
+ }
+ return false
+ })
+ }
+
+ preload();
+ var _7c = djConfig.extraLocale || [];
+ for (var i = 0; i < _7c.length; i++) {
+ preload(_7c[i])
+ }
+ }
+ dojo.hostenv.preloadLocalizations = function () {
+ }
+ };
+ dojo.requireLocalization = function (_7e, _7f, _80, _81) {
+ dojo.hostenv.preloadLocalizations();
+ var _82 = dojo.hostenv.normalizeLocale(_80);
+ var _83 = [_7e, 'nls', _7f].join('.');
+ var _84 = '';
+ if (_81) {
+ var _85 = _81.split(',');
+ for (var i = 0; i < _85.length; i++) {
+ if (_82.indexOf(_85[i]) == 0) {
+ if (_85[i].length > _84.length) {
+ _84 = _85[i]
+ }
+ }
+ }
+ if (!_84) {
+ _84 = 'ROOT'
+ }
+ }
+ var _87 = _81 ? _84 : _82;
+ var _88 = dojo.hostenv.findModule(_83);
+ var _89 = null;
+ if (_88) {
+ if (djConfig.localizationComplete && _88._built) {
+ return
+ }
+ var _8a = _87.replace('-', '_');
+ var _8b = _83 + '.' + _8a;
+ _89 = dojo.hostenv.findModule(_8b)
+ }
+ if (!_89) {
+ _88 = dojo.hostenv.startPackage(_83);
+ var _8c = dojo.hostenv.getModuleSymbols(_7e);
+ var _8d = _8c.concat('nls').join('/');
+ var _8e;
+ dojo.hostenv.searchLocalePath(_87, _81, function (loc) {
+ var _90 = loc.replace('-', '_');
+ var _91 = _83 + '.' + _90;
+ var _92 = false;
+ if (!dojo.hostenv.findModule(_91)) {
+ dojo.hostenv.startPackage(_91);
+ var _93 = [_8d];
+ if (loc != 'ROOT') {
+ _93.push(loc)
+ }
+ _93.push(_7f);
+ var _94 = _93.join('/') + '.js';
+ _92 = dojo.hostenv.loadPath(_94, null, function (_95) {
+ var _96 = function () {
+ };
+ _96.prototype = _8e;
+ _88[_90] = new _96();
+ for (var j in _95) {
+ _88[_90][j] = _95[j]
+ }
+ })
+ } else {
+ _92 = true
+ }
+ if (_92 && _88[_90]) {
+ _8e = _88[_90]
+ } else {
+ _88[_90] = _8e
+ }
+ if (_81) {
+ return true
+ }
+ })
+ }
+ if (_81 && _82 != _84) {
+ _88[_82.replace('-', '_')] = _88[_84.replace('-', '_')]
+ }
+ };
+ (function () {
+ var _98 = djConfig.extraLocale;
+ if (_98) {
+ if (!_98 instanceof Array) {
+ _98 = [_98]
+ }
+ var req = dojo.requireLocalization;
+ dojo.requireLocalization = function (m, b, _9c, _9d) {
+ req(m, b, _9c, _9d);
+ if (_9c) {
+ return
+ }
+ for (var i = 0; i < _98.length; i++) {
+ req(m, b, _98[i], _9d)
+ }
+ }
+ }
+ })()
+}
+if (typeof window != 'undefined') {
+ (function () {
+ if (djConfig.allowQueryConfig) {
+ var _9f = document.location.toString();
+ var _a0 = _9f.split('?', 2);
+ if (_a0.length > 1) {
+ var _a1 = _a0[1];
+ var _a2 = _a1.split('&');
+ for (var x in _a2) {
+ var sp = _a2[x].split('=');
+ if ((sp[0].length > 9) && (sp[0].substr(0, 9) == 'djConfig.')) {
+ var opt = sp[0].substr(9);
+ try {
+ djConfig[opt] = eval(sp[1])
+ } catch (e) {
+ djConfig[opt] = sp[1]
+ }
+ }
+ }
+ }
+ }
+ if (((djConfig['baseScriptUri'] == '') || (djConfig['baseRelativePath'] == '')) && (document
+ && document.getElementsByTagName)) {
+ var _a6 = document.getElementsByTagName('script');
+ var _a7 = /(__package__|dojo|bootstrap1)\.js([\?\.]|$)/i;
+ for (var i = 0; i < _a6.length; i++) {
+ var src = _a6[i].getAttribute('src');
+ if (!src) {
+ continue
+ }
+ var m = src.match(_a7);
+ if (m) {
+ var _ab = src.substring(0, m.index);
+ if (src.indexOf('bootstrap1') > -1) {
+ _ab += '../'
+ }
+ if (!this['djConfig']) {
+ djConfig = {}
+ }
+ if (djConfig['baseScriptUri'] == '') {
+ djConfig['baseScriptUri'] = _ab
+ }
+ if (djConfig['baseRelativePath'] == '') {
+ djConfig['baseRelativePath'] = _ab
+ }
+ break
+ }
+ }
+ }
+ var dr = dojo.render;
+ var drh = dojo.render.html;
+ var drs = dojo.render.svg;
+ var dua = (drh.UA = navigator.userAgent);
+ var dav = (drh.AV = navigator.appVersion);
+ var t = true;
+ var f = false;
+ drh.capable = t;
+ drh.support.builtin = t;
+ dr.ver = parseFloat(drh.AV);
+ dr.os.mac = dav.indexOf('Macintosh') >= 0;
+ dr.os.win = dav.indexOf('Windows') >= 0;
+ dr.os.linux = dav.indexOf('X11') >= 0;
+ drh.opera = dua.indexOf('Opera') >= 0;
+ drh.khtml = (dav.indexOf('Konqueror') >= 0) || (dav.indexOf('Safari') >= 0);
+ drh.safari = dav.indexOf('Safari') >= 0;
+ var _b3 = dua.indexOf('Gecko');
+ drh.mozilla = drh.moz = (_b3 >= 0) && (!drh.khtml);
+ if (drh.mozilla) {
+ drh.geckoVersion = dua.substring(_b3 + 6, _b3 + 14)
+ }
+ drh.ie = (document.all) && (!drh.opera);
+ drh.ie50 = drh.ie && dav.indexOf('MSIE 5.0') >= 0;
+ drh.ie55 = drh.ie && dav.indexOf('MSIE 5.5') >= 0;
+ drh.ie60 = drh.ie && dav.indexOf('MSIE 6.0') >= 0;
+ drh.ie70 = drh.ie && dav.indexOf('MSIE 7.0') >= 0;
+ var cm = document['compatMode'];
+ drh.quirks = (cm == 'BackCompat') || (cm == 'QuirksMode') || drh.ie55 || drh.ie50;
+ dojo.locale = dojo.locale || (drh.ie ? navigator.userLanguage : navigator.language).toLowerCase();
+ dr.vml.capable = drh.ie;
+ drs.capable = f;
+ drs.support.plugin = f;
+ drs.support.builtin = f;
+ var _b5 = window['document'];
+ var tdi = _b5['implementation'];
+ if (drh.ie && (window.location.protocol == 'file:')) {
+ djConfig.ieForceActiveXXhr = true
+ }
+ if ((tdi) && (tdi['hasFeature']) && (tdi.hasFeature('org.w3c.dom.svg', '1.0'))) {
+ drs.capable = t;
+ drs.support.builtin = t;
+ drs.support.plugin = f
+ }
+ if (drh.safari) {
+ var tmp = dua.split('AppleWebKit/')[1];
+ var ver = parseFloat(tmp.split(' ')[0]);
+ if (ver >= 420) {
+ drs.capable = t;
+ drs.support.builtin = t;
+ drs.support.plugin = f
+ }
+ } else {
+ }
+ })();
+ dojo.hostenv.startPackage('dojo.hostenv');
+ dojo.render.name = dojo.hostenv.name_ = 'browser';
+ dojo.hostenv.searchIds = [];
+ dojo.hostenv._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
+ dojo.hostenv.getXmlhttpObject = function () {
+ var _b9 = null;
+ var _ba = null;
+ if (!dojo.render.html.ie || !djConfig.ieForceActiveXXhr) {
+ try {
+ _b9 = new XMLHttpRequest()
+ } catch (e) {
+ }
+ }
+ if (!_b9) {
+ for (var i = 0; i < 3; ++i) {
+ var _bc = dojo.hostenv._XMLHTTP_PROGIDS[i];
+ try {
+ _b9 = new ActiveXObject(_bc)
+ } catch (e) {
+ _ba = e
+ }
+ if (_b9) {
+ dojo.hostenv._XMLHTTP_PROGIDS = [_bc];
+ break
+ }
+ }
+ }
+ if (!_b9) {
+ return dojo.raise('XMLHTTP not available', _ba)
+ }
+ return _b9
+ };
+ dojo.hostenv._blockAsync = false;
+ dojo.hostenv.getText = function (uri, _be, _bf) {
+ if (!_be) {
+ this._blockAsync = true
+ }
+ var _c0 = this.getXmlhttpObject();
+
+ function isDocumentOk(_c1) {
+ var _c2 = _c1['status'];
+ return Boolean((!_c2) || ((200 <= _c2) && (300 > _c2)) || (_c2 == 304))
+ }
+
+ if (_be) {
+ var _c3 = this, _c4 = null, gbl = dojo.global();
+ var xhr = dojo.evalObjPath('dojo.io.XMLHTTPTransport');
+ _c0.onreadystatechange = function () {
+ if (_c4) {
+ gbl.clearTimeout(_c4);
+ _c4 = null
+ }
+ if (_c3._blockAsync || (xhr && xhr._blockAsync)) {
+ _c4 = gbl.setTimeout(function () {
+ _c0.onreadystatechange.apply(this)
+ }, 10)
+ } else {
+ if (4 == _c0.readyState) {
+ if (isDocumentOk(_c0)) {
+ _be(_c0.responseText)
+ }
+ }
+ }
+ }
+ }
+ _c0.open('GET', uri, _be ? true : false);
+ try {
+ _c0.send(null);
+ if (_be) {
+ return null
+ }
+ if (!isDocumentOk(_c0)) {
+ var err = Error('Unable to load ' + uri + ' status:' + _c0.status);
+ err.status = _c0.status;
+ err.responseText = _c0.responseText;
+ throw err
+ }
+ } catch (e) {
+ this._blockAsync = false;
+ if ((_bf) && (!_be)) {
+ return null
+ } else {
+ throw e
+ }
+ }
+ this._blockAsync = false;
+ return _c0.responseText
+ };
+ dojo.hostenv.defaultDebugContainerId = 'dojoDebug';
+ dojo.hostenv._println_buffer = [];
+ dojo.hostenv._println_safe = false;
+ dojo.hostenv.println = function (_c8) {
+ if (!dojo.hostenv._println_safe) {
+ dojo.hostenv._println_buffer.push(_c8)
+ } else {
+ try {
+ var _c9 = document.getElementById(djConfig.debugContainerId ? djConfig.debugContainerId : dojo.hostenv.defaultDebugContainerId);
+ if (!_c9) {
+ _c9 = dojo.body()
+ }
+ var div = document.createElement('div');
+ div.appendChild(document.createTextNode(_c8));
+ _c9.appendChild(div)
+ } catch (e) {
+ try {
+ document.write('' + _c8 + '
')
+ } catch (e2) {
+ window.status = _c8
+ }
+ }
+ }
+ };
+ dojo.addOnLoad(function () {
+ dojo.hostenv._println_safe = true;
+ while (dojo.hostenv._println_buffer.length > 0) {
+ dojo.hostenv.println(dojo.hostenv._println_buffer.shift())
+ }
+ });
+
+ function dj_addNodeEvtHdlr(_cb, _cc, fp) {
+ var _ce = _cb['on' + _cc] || function () {
+ };
+ _cb['on' + _cc] = function () {
+ fp.apply(_cb, arguments);
+ _ce.apply(_cb, arguments)
+ };
+ return true
+ }
+
+ dojo.hostenv._djInitFired = false;
+
+ function dj_load_init(e) {
+ dojo.hostenv._djInitFired = true;
+ var _d0 = (e && e.type) ? e.type.toLowerCase() : 'load';
+ if (arguments.callee.initialized || (_d0 != 'domcontentloaded' && _d0 != 'load')) {
+ return
+ }
+ arguments.callee.initialized = true;
+ if (typeof (_timer) != 'undefined') {
+ clearInterval(_timer);
+ delete _timer
+ }
+ var _d1 = function () {
+ if (dojo.render.html.ie) {
+ dojo.hostenv.makeWidgets()
+ }
+ };
+ if (dojo.hostenv.inFlightCount == 0) {
+ _d1();
+ dojo.hostenv.modulesLoaded()
+ } else {
+ dojo.hostenv.modulesLoadedListeners.unshift(_d1)
+ }
+ }
+
+ if (document.addEventListener) {
+ if (dojo.render.html.opera || (dojo.render.html.moz && (djConfig['enableMozDomContentLoaded'] === true))) {
+ document.addEventListener('DOMContentLoaded', dj_load_init, null)
+ }
+ window.addEventListener('load', dj_load_init, null)
+ }
+ if (dojo.render.html.ie && dojo.render.os.win) {
+ document.attachEvent('onreadystatechange', function (e) {
+ if (document.readyState == 'complete') {
+ dj_load_init()
+ }
+ })
+ }
+ if (/(WebKit|khtml)/i.test(navigator.userAgent)) {
+ var _timer = setInterval(function () {
+ if (/loaded|complete/.test(document.readyState)) {
+ dj_load_init()
+ }
+ }, 10)
+ }
+ if (dojo.render.html.ie) {
+ dj_addNodeEvtHdlr(window, 'beforeunload', function () {
+ dojo.hostenv._unloading = true;
+ window.setTimeout(function () {
+ dojo.hostenv._unloading = false
+ }, 0)
+ })
+ }
+ dj_addNodeEvtHdlr(window, 'unload', function () {
+ dojo.hostenv.unloaded();
+ if ((!dojo.render.html.ie) || (dojo.render.html.ie && dojo.hostenv._unloading)) {
+ dojo.hostenv.unloaded()
+ }
+ });
+ dojo.hostenv.makeWidgets = function () {
+ var _d3 = [];
+ if (djConfig.searchIds && djConfig.searchIds.length > 0) {
+ _d3 = _d3.concat(djConfig.searchIds)
+ }
+ if (dojo.hostenv.searchIds && dojo.hostenv.searchIds.length > 0) {
+ _d3 = _d3.concat(dojo.hostenv.searchIds)
+ }
+ if ((djConfig.parseWidgets) || (_d3.length > 0)) {
+ if (dojo.evalObjPath('dojo.widget.Parse')) {
+ var _d4 = new dojo.xml.Parse();
+ if (_d3.length > 0) {
+ for (var x = 0; x < _d3.length; x++) {
+ var _d6 = document.getElementById(_d3[x]);
+ if (!_d6) {
+ continue
+ }
+ var _d7 = _d4.parseElement(_d6, null, true);
+ dojo.widget.getParser().createComponents(_d7)
+ }
+ } else {
+ if (djConfig.parseWidgets) {
+ var _d7 = _d4.parseElement(dojo.body(), null, true);
+ dojo.widget.getParser().createComponents(_d7)
+ }
+ }
+ }
+ }
+ };
+ dojo.addOnLoad(function () {
+ if (!dojo.render.html.ie) {
+ dojo.hostenv.makeWidgets()
+ }
+ });
+ try {
+ if (dojo.render.html.ie) {
+ document.namespaces.add('v', 'urn:schemas-microsoft-com:vml');
+ document.createStyleSheet().addRule('v\\:*', 'behavior:url(#default#VML)')
+ }
+ } catch (e) {
+ }
+ dojo.hostenv.writeIncludes = function () {
+ };
+ if (!dj_undef('document', this)) {
+ dj_currentDocument = this.document
+ }
+ dojo.doc = function () {
+ return dj_currentDocument
+ };
+ dojo.body = function () {
+ return dojo.doc().body || dojo.doc().getElementsByTagName('body')[0]
+ };
+ dojo.byId = function (id, doc) {
+ if ((id) && ((typeof id == 'string') || (id instanceof String))) {
+ if (!doc) {
+ doc = dj_currentDocument
+ }
+ var ele = doc.getElementById(id);
+ if (ele && (ele.id != id) && doc.all) {
+ ele = null;
+ eles = doc.all[id];
+ if (eles) {
+ if (eles.length) {
+ for (var i = 0; i < eles.length; i++) {
+ if (eles[i].id == id) {
+ ele = eles[i];
+ break
+ }
+ }
+ } else {
+ ele = eles
+ }
+ }
+ }
+ return ele
+ }
+ return id
+ };
+ dojo.setContext = function (_dc, _dd) {
+ dj_currentContext = _dc;
+ dj_currentDocument = _dd
+ };
+ dojo._fireCallback = function (_de, _df, _e0) {
+ if ((_df) && ((typeof _de == 'string') || (_de instanceof String))) {
+ _de = _df[_de]
+ }
+ return (_df ? _de.apply(_df, _e0 || []) : _de())
+ };
+ dojo.withGlobal = function (_e1, _e2, _e3, _e4) {
+ var _e5;
+ var _e6 = dj_currentContext;
+ var _e7 = dj_currentDocument;
+ try {
+ dojo.setContext(_e1, _e1.document);
+ _e5 = dojo._fireCallback(_e2, _e3, _e4)
+ } finally {
+ dojo.setContext(_e6, _e7)
+ }
+ return _e5
+ };
+ dojo.withDoc = function (_e8, _e9, _ea, _eb) {
+ var _ec;
+ var _ed = dj_currentDocument;
+ try {
+ dj_currentDocument = _e8;
+ _ec = dojo._fireCallback(_e9, _ea, _eb)
+ } finally {
+ dj_currentDocument = _ed
+ }
+ return _ec
+ }
+}
+dojo.requireIf((djConfig['isDebug'] || djConfig['debugAtAllCosts']), 'dojo.debug');
+dojo.requireIf(djConfig['debugAtAllCosts'] && !window.widget && !djConfig['useXDomain'], 'dojo.browser_debug');
+dojo.requireIf(djConfig['debugAtAllCosts'] && !window.widget && djConfig['useXDomain'], 'dojo.browser_debug_xd');
+dojo.provide('dojo.string.common');
+dojo.string.trim = function (str, wh) {
+ if (!str.replace) {
+ return str
+ }
+ if (!str.length) {
+ return str
+ }
+ var re = (wh > 0) ? (/^\s+/) : (wh < 0) ? (/\s+$/) : (/^\s+|\s+$/g);
+ return str.replace(re, '')
+};
+dojo.string.trimStart = function (str) {
+ return dojo.string.trim(str, 1)
+};
+dojo.string.trimEnd = function (str) {
+ return dojo.string.trim(str, -1)
+};
+dojo.string.repeat = function (str, _f4, _f5) {
+ var out = '';
+ for (var i = 0; i < _f4; i++) {
+ out += str;
+ if (_f5 && i < _f4 - 1) {
+ out += _f5
+ }
+ }
+ return out
+};
+dojo.string.pad = function (str, len, c, dir) {
+ var out = String(str);
+ if (!c) {
+ c = '0'
+ }
+ if (!dir) {
+ dir = 1
+ }
+ while (out.length < len) {
+ if (dir > 0) {
+ out = c + out
+ } else {
+ out += c
+ }
+ }
+ return out
+};
+dojo.string.padLeft = function (str, len, c) {
+ return dojo.string.pad(str, len, c, 1)
+};
+dojo.string.padRight = function (str, len, c) {
+ return dojo.string.pad(str, len, c, -1)
+};
+dojo.provide('dojo.string');
+dojo.provide('dojo.lang.common');
+dojo.lang.inherits = function (_103, _104) {
+ if (!dojo.lang.isFunction(_104)) {
+ dojo.raise('dojo.inherits: superclass argument [' + _104 + '] must be a function (subclass: [' + _103 + '\']')
+ }
+ _103.prototype = new _104();
+ _103.prototype.constructor = _103;
+ _103.superclass = _104.prototype;
+ _103['super'] = _104.prototype
+};
+dojo.lang._mixin = function (obj, _106) {
+ var tobj = {};
+ for (var x in _106) {
+ if ((typeof tobj[x] == 'undefined') || (tobj[x] != _106[x])) {
+ obj[x] = _106[x]
+ }
+ }
+ if (dojo.render.html.ie
+ && (typeof (_106['toString']) == 'function')
+ && (_106['toString'] != obj['toString'])
+ && (_106['toString'] != tobj['toString'])) {
+ obj.toString = _106.toString
+ }
+ return obj
+};
+dojo.lang.mixin = function (obj, _10a) {
+ for (var i = 1, l = arguments.length; i < l; i++) {
+ dojo.lang._mixin(obj, arguments[i])
+ }
+ return obj
+};
+dojo.lang.extend = function (_10d, _10e) {
+ for (var i = 1, l = arguments.length; i < l; i++) {
+ dojo.lang._mixin(_10d.prototype, arguments[i])
+ }
+ return _10d
+};
+dojo.inherits = dojo.lang.inherits;
+dojo.mixin = dojo.lang.mixin;
+dojo.extend = dojo.lang.extend;
+dojo.lang.find = function (_111, _112, _113, _114) {
+ if (!dojo.lang.isArrayLike(_111) && dojo.lang.isArrayLike(_112)) {
+ dojo.deprecated('dojo.lang.find(value, array)', 'use dojo.lang.find(array, value) instead', '0.5');
+ var temp = _111;
+ _111 = _112;
+ _112 = temp
+ }
+ var _116 = dojo.lang.isString(_111);
+ if (_116) {
+ _111 = _111.split('')
+ }
+ if (_114) {
+ var step = -1;
+ var i = _111.length - 1;
+ var end = -1
+ } else {
+ var step = 1;
+ var i = 0;
+ var end = _111.length
+ }
+ if (_113) {
+ while (i != end) {
+ if (_111[i] === _112) {
+ return i
+ }
+ i += step
+ }
+ } else {
+ while (i != end) {
+ if (_111[i] == _112) {
+ return i
+ }
+ i += step
+ }
+ }
+ return -1
+};
+dojo.lang.indexOf = dojo.lang.find;
+dojo.lang.findLast = function (_11a, _11b, _11c) {
+ return dojo.lang.find(_11a, _11b, _11c, true)
+};
+dojo.lang.lastIndexOf = dojo.lang.findLast;
+dojo.lang.inArray = function (_11d, _11e) {
+ return dojo.lang.find(_11d, _11e) > -1
+};
+dojo.lang.isObject = function (it) {
+ if (typeof it == 'undefined') {
+ return false
+ }
+ return (typeof it == 'object' || it === null || dojo.lang.isArray(it) || dojo.lang.isFunction(it))
+};
+dojo.lang.isArray = function (it) {
+ return (it && it instanceof Array || typeof it == 'array')
+};
+dojo.lang.isArrayLike = function (it) {
+ if ((!it) || (dojo.lang.isUndefined(it))) {
+ return false
+ }
+ if (dojo.lang.isString(it)) {
+ return false
+ }
+ if (dojo.lang.isFunction(it)) {
+ return false
+ }
+ if (dojo.lang.isArray(it)) {
+ return true
+ }
+ if ((it.tagName) && (it.tagName.toLowerCase() == 'form')) {
+ return false
+ }
+ if (dojo.lang.isNumber(it.length) && isFinite(it.length)) {
+ return true
+ }
+ return false
+};
+dojo.lang.isFunction = function (it) {
+ return (it instanceof Function || typeof it == 'function')
+};
+(function () {
+ if ((dojo.render.html.capable) && (dojo.render.html['safari'])) {
+ dojo.lang.isFunction = function (it) {
+ if ((typeof (it) == 'function') && (it == '[object NodeList]')) {
+ return false
+ }
+ return (it instanceof Function || typeof it == 'function')
+ }
+ }
+})();
+dojo.lang.isString = function (it) {
+ return (typeof it == 'string' || it instanceof String)
+};
+dojo.lang.isAlien = function (it) {
+ if (!it) {
+ return false
+ }
+ return !dojo.lang.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it))
+};
+dojo.lang.isBoolean = function (it) {
+ return (it instanceof Boolean || typeof it == 'boolean')
+};
+dojo.lang.isNumber = function (it) {
+ return (it instanceof Number || typeof it == 'number')
+};
+dojo.lang.isUndefined = function (it) {
+ return ((typeof (it) == 'undefined') && (it == undefined))
+};
+dojo.provide('dojo.lang.extras');
+dojo.lang.setTimeout = function (func, _12a) {
+ var _12b = window, _12c = 2;
+ if (!dojo.lang.isFunction(func)) {
+ _12b = func;
+ func = _12a;
+ _12a = arguments[2];
+ _12c++
+ }
+ if (dojo.lang.isString(func)) {
+ func = _12b[func]
+ }
+ var args = [];
+ for (var i = _12c; i < arguments.length; i++) {
+ args.push(arguments[i])
+ }
+ return dojo.global().setTimeout(function () {
+ func.apply(_12b, args)
+ }, _12a)
+};
+dojo.lang.clearTimeout = function (_12f) {
+ dojo.global().clearTimeout(_12f)
+};
+dojo.lang.getNameInObj = function (ns, item) {
+ if (!ns) {
+ ns = dj_global
+ }
+ for (var x in ns) {
+ if (ns[x] === item) {
+ return String(x)
+ }
+ }
+ return null
+};
+dojo.lang.shallowCopy = function (obj, deep) {
+ var i, ret;
+ if (obj === null) {
+ return null
+ }
+ if (dojo.lang.isObject(obj)) {
+ ret = new obj.constructor();
+ for (i in obj) {
+ if (dojo.lang.isUndefined(ret[i])) {
+ ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i]
+ }
+ }
+ } else {
+ if (dojo.lang.isArray(obj)) {
+ ret = [];
+ for (i = 0; i < obj.length; i++) {
+ ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i]
+ }
+ } else {
+ ret = obj
+ }
+ }
+ return ret
+};
+dojo.lang.firstValued = function () {
+ for (var i = 0; i < arguments.length; i++) {
+ if (typeof arguments[i] != 'undefined') {
+ return arguments[i]
+ }
+ }
+ return undefined
+};
+dojo.lang.getObjPathValue = function (_138, _139, _13a) {
+ with (dojo.parseObjPath(_138, _139, _13a)) {
+ return dojo.evalProp(prop, obj, _13a)
+ }
+};
+dojo.lang.setObjPathValue = function (_13b, _13c, _13d, _13e) {
+ dojo.deprecated('dojo.lang.setObjPathValue', 'use dojo.parseObjPath and the \'=\' operator', '0.6');
+ if (arguments.length < 4) {
+ _13e = true
+ }
+ with (dojo.parseObjPath(_13b, _13d, _13e)) {
+ if (obj && (_13e || (prop in obj))) {
+ obj[prop] = _13c
+ }
+ }
+};
+dojo.provide('dojo.io.common');
+dojo.io.transports = [];
+dojo.io.hdlrFuncNames = ['load', 'error', 'timeout'];
+dojo.io.Request = function (url, _140, _141, _142) {
+ if ((arguments.length == 1) && (arguments[0].constructor == Object)) {
+ this.fromKwArgs(arguments[0])
+ } else {
+ this.url = url;
+ if (_140) {
+ this.mimetype = _140
+ }
+ if (_141) {
+ this.transport = _141
+ }
+ if (arguments.length >= 4) {
+ this.changeUrl = _142
+ }
+ }
+};
+dojo.lang.extend(dojo.io.Request, {
+ url: '',
+ mimetype: 'text/plain',
+ method: 'GET',
+ content: undefined,
+ transport: undefined,
+ changeUrl: undefined,
+ formNode: undefined,
+ sync: false,
+ bindSuccess: false,
+ useCache: false,
+ preventCache: false,
+ jsonFilter: function (_143) {
+ if ((this.mimetype == 'text/json-comment-filtered') || (this.mimetype == 'application/json-comment-filtered')) {
+ var _144 = _143.indexOf('/*');
+ var _145 = _143.lastIndexOf('*/');
+ if ((_144 == -1) || (_145 == -1)) {
+ dojo.debug('your JSON wasn\'t comment filtered!');
+ return ''
+ }
+ return _143.substring(_144 + 2, _145)
+ }
+ dojo.debug('please consider using a mimetype of text/json-comment-filtered to avoid potential security issues with JSON endpoints');
+ return _143
+ },
+ load: function (type, data, _148, _149) {
+ },
+ error: function (type, _14b, _14c, _14d) {
+ },
+ timeout: function (type, _14f, _150, _151) {
+ },
+ handle: function (type, data, _154, _155) {
+ },
+ timeoutSeconds: 0,
+ abort: function () {
+ },
+ fromKwArgs: function (_156) {
+ if (_156['url']) {
+ _156.url = _156.url.toString()
+ }
+ if (_156['formNode']) {
+ _156.formNode = dojo.byId(_156.formNode)
+ }
+ if (!_156['method'] && _156['formNode'] && _156['formNode'].method) {
+ _156.method = _156['formNode'].method
+ }
+ if (!_156['handle'] && _156['handler']) {
+ _156.handle = _156.handler
+ }
+ if (!_156['load'] && _156['loaded']) {
+ _156.load = _156.loaded
+ }
+ if (!_156['changeUrl'] && _156['changeURL']) {
+ _156.changeUrl = _156.changeURL
+ }
+ _156.encoding = dojo.lang.firstValued(_156['encoding'], djConfig['bindEncoding'], '');
+ _156.sendTransport = dojo.lang.firstValued(_156['sendTransport'], djConfig['ioSendTransport'], false);
+ var _157 = dojo.lang.isFunction;
+ for (var x = 0; x < dojo.io.hdlrFuncNames.length; x++) {
+ var fn = dojo.io.hdlrFuncNames[x];
+ if (_156[fn] && _157(_156[fn])) {
+ continue
+ }
+ if (_156['handle'] && _157(_156['handle'])) {
+ _156[fn] = _156.handle
+ }
+ }
+ dojo.lang.mixin(this, _156)
+ }
+});
+dojo.io.Error = function (msg, type, num) {
+ this.message = msg;
+ this.type = type || 'unknown';
+ this.number = num || 0
+};
+dojo.io.transports.addTransport = function (name) {
+ this.push(name);
+ this[name] = dojo.io[name]
+};
+dojo.io.bind = function (_15e) {
+ if (!(_15e instanceof dojo.io.Request)) {
+ try {
+ _15e = new dojo.io.Request(_15e)
+ } catch (e) {
+ dojo.debug(e)
+ }
+ }
+ var _15f = '';
+ if (_15e['transport']) {
+ _15f = _15e['transport'];
+ if (!this[_15f]) {
+ dojo.io.sendBindError(_15e, 'No dojo.io.bind() transport with name \'' + _15e['transport'] + '\'.');
+ return _15e
+ }
+ if (!this[_15f].canHandle(_15e)) {
+ dojo.io.sendBindError(_15e, 'dojo.io.bind() transport with name \''
+ + _15e['transport']
+ + '\' cannot handle this type of request.');
+ return _15e
+ }
+ } else {
+ for (var x = 0; x < dojo.io.transports.length; x++) {
+ var tmp = dojo.io.transports[x];
+ if ((this[tmp]) && (this[tmp].canHandle(_15e))) {
+ _15f = tmp;
+ break
+ }
+ }
+ if (_15f == '') {
+ dojo.io.sendBindError(_15e, 'None of the loaded transports for dojo.io.bind()' + ' can handle the request.');
+ return _15e
+ }
+ }
+ this[_15f].bind(_15e);
+ _15e.bindSuccess = true;
+ return _15e
+};
+dojo.io.sendBindError = function (_162, _163) {
+ if ((typeof _162.error == 'function' || typeof _162.handle == 'function') && (typeof setTimeout
+ == 'function'
+ || typeof setTimeout
+ == 'object')) {
+ var _164 = new dojo.io.Error(_163);
+ setTimeout(function () {
+ _162[(typeof _162.error == 'function') ? 'error' : 'handle']('error', _164, null, _162)
+ }, 50)
+ } else {
+ dojo.raise(_163)
+ }
+};
+dojo.io.queueBind = function (_165) {
+ if (!(_165 instanceof dojo.io.Request)) {
+ try {
+ _165 = new dojo.io.Request(_165)
+ } catch (e) {
+ dojo.debug(e)
+ }
+ }
+ var _166 = _165.load;
+ _165.load = function () {
+ dojo.io._queueBindInFlight = false;
+ var ret = _166.apply(this, arguments);
+ dojo.io._dispatchNextQueueBind();
+ return ret
+ };
+ var _168 = _165.error;
+ _165.error = function () {
+ dojo.io._queueBindInFlight = false;
+ var ret = _168.apply(this, arguments);
+ dojo.io._dispatchNextQueueBind();
+ return ret
+ };
+ dojo.io._bindQueue.push(_165);
+ dojo.io._dispatchNextQueueBind();
+ return _165
+};
+dojo.io._dispatchNextQueueBind = function () {
+ if (!dojo.io._queueBindInFlight) {
+ dojo.io._queueBindInFlight = true;
+ if (dojo.io._bindQueue.length > 0) {
+ dojo.io.bind(dojo.io._bindQueue.shift())
+ } else {
+ dojo.io._queueBindInFlight = false
+ }
+ }
+};
+dojo.io._bindQueue = [];
+dojo.io._queueBindInFlight = false;
+dojo.io.argsFromMap = function (map, _16b, last) {
+ var enc = /utf/i.test(_16b || '') ? encodeURIComponent : dojo.string.encodeAscii;
+ var _16e = [];
+ var _16f = {};
+ for (var name in map) {
+ var _171 = function (elt) {
+ var val = enc(name) + '=' + enc(elt);
+ _16e[(last == name) ? 'push' : 'unshift'](val)
+ };
+ if (!_16f[name]) {
+ var _174 = map[name];
+ if (dojo.lang.isArray(_174)) {
+ dojo.lang.forEach(_174, _171)
+ } else {
+ _171(_174)
+ }
+ }
+ }
+ return _16e.join('&')
+};
+dojo.io.setIFrameSrc = function (_175, src, _177) {
+ try {
+ var r = dojo.render.html;
+ if (!_177) {
+ if (r.safari) {
+ _175.location = src
+ } else {
+ frames[_175.name].location = src
+ }
+ } else {
+ var idoc;
+ if (r.ie) {
+ idoc = _175.contentWindow.document
+ } else {
+ if (r.safari) {
+ idoc = _175.document
+ } else {
+ idoc = _175.contentWindow
+ }
+ }
+ if (!idoc) {
+ _175.location = src;
+
+ } else {
+ idoc.location.replace(src)
+ }
+ }
+ } catch (e) {
+ dojo.debug(e);
+ dojo.debug('setIFrameSrc: ' + e)
+ }
+};
+dojo.provide('dojo.lang.array');
+dojo.lang.mixin(dojo.lang, {
+ has: function (obj, name) {
+ try {
+ return typeof obj[name] != 'undefined'
+ } catch (e) {
+ return false
+ }
+ }, isEmpty: function (obj) {
+ if (dojo.lang.isObject(obj)) {
+ var tmp = {};
+ var _17e = 0;
+ for (var x in obj) {
+ if (obj[x] && (!tmp[x])) {
+ _17e++;
+ break
+ }
+ }
+ return _17e == 0
+ } else {
+ if (dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)) {
+ return obj.length == 0
+ }
+ }
+ }, map: function (arr, obj, _182) {
+ var _183 = dojo.lang.isString(arr);
+ if (_183) {
+ arr = arr.split('')
+ }
+ if (dojo.lang.isFunction(obj) && (!_182)) {
+ _182 = obj;
+ obj = dj_global
+ } else {
+ if (dojo.lang.isFunction(obj) && _182) {
+ var _184 = obj;
+ obj = _182;
+ _182 = _184
+ }
+ }
+ if (Array.map) {
+ var _185 = Array.map(arr, _182, obj)
+ } else {
+ var _185 = [];
+ for (var i = 0; i < arr.length; ++i) {
+ _185.push(_182.call(obj, arr[i]))
+ }
+ }
+ if (_183) {
+ return _185.join('')
+ } else {
+ return _185
+ }
+ }, reduce: function (arr, _188, obj, _18a) {
+ var _18b = _188;
+ if (arguments.length == 2) {
+ _18a = _188;
+ _18b = arr[0];
+ arr = arr.slice(1)
+ } else {
+ if (arguments.length == 3) {
+ if (dojo.lang.isFunction(obj)) {
+ _18a = obj;
+ obj = null
+ }
+ } else {
+ if (dojo.lang.isFunction(obj)) {
+ var tmp = _18a;
+ _18a = obj;
+ obj = tmp
+ }
+ }
+ }
+ var ob = obj || dj_global;
+ dojo.lang.map(arr, function (val) {
+ _18b = _18a.call(ob, _18b, val)
+ });
+ return _18b
+ }, forEach: function (_18f, _190, _191) {
+ if (dojo.lang.isString(_18f)) {
+ _18f = _18f.split('')
+ }
+ if (Array.forEach) {
+ Array.forEach(_18f, _190, _191)
+ } else {
+ if (!_191) {
+ _191 = dj_global
+ }
+ for (var i = 0, l = _18f.length; i < l; i++) {
+ _190.call(_191, _18f[i], i, _18f)
+ }
+ }
+ }, _everyOrSome: function (_194, arr, _196, _197) {
+ if (dojo.lang.isString(arr)) {
+ arr = arr.split('')
+ }
+ if (Array.every) {
+ return Array[_194 ? 'every' : 'some'](arr, _196, _197)
+ } else {
+ if (!_197) {
+ _197 = dj_global
+ }
+ for (var i = 0, l = arr.length; i < l; i++) {
+ var _19a = _196.call(_197, arr[i], i, arr);
+ if (_194 && !_19a) {
+ return false
+ } else {
+ if ((!_194) && (_19a)) {
+ return true
+ }
+ }
+ }
+ return Boolean(_194)
+ }
+ }, every: function (arr, _19c, _19d) {
+ return this._everyOrSome(true, arr, _19c, _19d)
+ }, some: function (arr, _19f, _1a0) {
+ return this._everyOrSome(false, arr, _19f, _1a0)
+ }, filter: function (arr, _1a2, _1a3) {
+ var _1a4 = dojo.lang.isString(arr);
+ if (_1a4) {
+ arr = arr.split('')
+ }
+ var _1a5;
+ if (Array.filter) {
+ _1a5 = Array.filter(arr, _1a2, _1a3)
+ } else {
+ if (!_1a3) {
+ if (arguments.length >= 3) {
+ dojo.raise('thisObject doesn\'t exist!')
+ }
+ _1a3 = dj_global
+ }
+ _1a5 = [];
+ for (var i = 0; i < arr.length; i++) {
+ if (_1a2.call(_1a3, arr[i], i, arr)) {
+ _1a5.push(arr[i])
+ }
+ }
+ }
+ if (_1a4) {
+ return _1a5.join('')
+ } else {
+ return _1a5
+ }
+ }, unnest: function () {
+ var out = [];
+ for (var i = 0; i < arguments.length; i++) {
+ if (dojo.lang.isArrayLike(arguments[i])) {
+ var add = dojo.lang.unnest.apply(this, arguments[i]);
+ out = out.concat(add)
+ } else {
+ out.push(arguments[i])
+ }
+ }
+ return out
+ }, toArray: function (_1aa, _1ab) {
+ var _1ac = [];
+ for (var i = _1ab || 0; i < _1aa.length; i++) {
+ _1ac.push(_1aa[i])
+ }
+ return _1ac
+ }
+});
+dojo.provide('dojo.lang.func');
+dojo.lang.hitch = function (_1ae, _1af) {
+ var args = [];
+ for (var x = 2; x < arguments.length; x++) {
+ args.push(arguments[x])
+ }
+ var fcn = (dojo.lang.isString(_1af) ? _1ae[_1af] : _1af) || function () {
+ };
+ return function () {
+ var ta = args.concat([]);
+ for (var x = 0; x < arguments.length; x++) {
+ ta.push(arguments[x])
+ }
+ return fcn.apply(_1ae, ta)
+ }
+};
+dojo.lang.anonCtr = 0;
+dojo.lang.anon = {};
+dojo.lang.nameAnonFunc = function (_1b5, _1b6, _1b7) {
+ var nso = (_1b6 || dojo.lang.anon);
+ if ((_1b7) || ((dj_global['djConfig']) && (djConfig['slowAnonFuncLookups'] == true))) {
+ for (var x in nso) {
+ try {
+ if (nso[x] === _1b5) {
+ return x
+ }
+ } catch (e) {
+ }
+ }
+ }
+ var ret = '__' + dojo.lang.anonCtr++;
+ while (typeof nso[ret] != 'undefined') {
+ ret = '__' + dojo.lang.anonCtr++
+ }
+ nso[ret] = _1b5;
+ return ret
+};
+dojo.lang.forward = function (_1bb) {
+ return function () {
+ return this[_1bb].apply(this, arguments)
+ }
+};
+dojo.lang.curry = function (_1bc, func) {
+ var _1be = [];
+ _1bc = _1bc || dj_global;
+ if (dojo.lang.isString(func)) {
+ func = _1bc[func]
+ }
+ for (var x = 2; x < arguments.length; x++) {
+ _1be.push(arguments[x])
+ }
+ var _1c0 = (func['__preJoinArity'] || func.length) - _1be.length;
+
+ function gather(_1c1, _1c2, _1c3) {
+ var _1c4 = _1c3;
+ var _1c5 = _1c2.slice(0);
+ for (var x = 0; x < _1c1.length; x++) {
+ _1c5.push(_1c1[x])
+ }
+ _1c3 = _1c3 - _1c1.length;
+ if (_1c3 <= 0) {
+ var res = func.apply(_1bc, _1c5);
+ _1c3 = _1c4;
+ return res
+ } else {
+ return function () {
+ return gather(arguments, _1c5, _1c3)
+ }
+ }
+ }
+
+ return gather([], _1be, _1c0)
+};
+dojo.lang.curryArguments = function (_1c8, func, args, _1cb) {
+ var _1cc = [];
+ var x = _1cb || 0;
+ for (x = _1cb; x < args.length; x++) {
+ _1cc.push(args[x])
+ }
+ return dojo.lang.curry.apply(dojo.lang, [_1c8, func].concat(_1cc))
+};
+dojo.lang.tryThese = function () {
+ for (var x = 0; x < arguments.length; x++) {
+ try {
+ if (typeof arguments[x] == 'function') {
+ var ret = (arguments[x]());
+ if (ret) {
+ return ret
+ }
+ }
+ } catch (e) {
+ dojo.debug(e)
+ }
+ }
+};
+dojo.lang.delayThese = function (farr, cb, _1d2, _1d3) {
+ if (!farr.length) {
+ if (typeof _1d3 == 'function') {
+ _1d3()
+ }
+ return
+ }
+ if ((typeof _1d2 == 'undefined') && (typeof cb == 'number')) {
+ _1d2 = cb;
+ cb = function () {
+ }
+ } else {
+ if (!cb) {
+ cb = function () {
+ };
+ if (!_1d2) {
+ _1d2 = 0
+ }
+ }
+ }
+ setTimeout(function () {
+ (farr.shift())();
+ cb();
+ dojo.lang.delayThese(farr, cb, _1d2, _1d3)
+ }, _1d2)
+};
+dojo.provide('dojo.string.extras');
+dojo.string.substituteParams = function (_1d4, hash) {
+ var map = (typeof hash == 'object') ? hash : dojo.lang.toArray(arguments, 1);
+ return _1d4.replace(/\%\{(\w+)\}/g, function (_1d7, key) {
+ if (typeof (map[key]) != 'undefined' && map[key] != null) {
+ return map[key]
+ }
+ dojo.raise('Substitution not found: ' + key)
+ })
+};
+dojo.string.capitalize = function (str) {
+ if (!dojo.lang.isString(str)) {
+ return ''
+ }
+ if (arguments.length == 0) {
+ str = this
+ }
+ var _1da = str.split(' ');
+ for (var i = 0; i < _1da.length; i++) {
+ _1da[i] = _1da[i].charAt(0).toUpperCase() + _1da[i].substring(1)
+ }
+ return _1da.join(' ')
+};
+dojo.string.isBlank = function (str) {
+ if (!dojo.lang.isString(str)) {
+ return true
+ }
+ return (dojo.string.trim(str).length == 0)
+};
+dojo.string.encodeAscii = function (str) {
+ if (!dojo.lang.isString(str)) {
+ return str
+ }
+ var ret = '';
+ var _1df = escape(str);
+ var _1e0, re = /%u([0-9A-F]{4})/i;
+ while ((_1e0 = _1df.match(re))) {
+ var num = Number('0x' + _1e0[1]);
+ var _1e3 = escape('' + num + ';');
+ ret += _1df.substring(0, _1e0.index) + _1e3;
+ _1df = _1df.substring(_1e0.index + _1e0[0].length)
+ }
+ ret += _1df.replace(/\+/g, '%2B');
+ return ret
+};
+dojo.string.escape = function (type, str) {
+ var args = dojo.lang.toArray(arguments, 1);
+ switch (type.toLowerCase()) {
+ case 'xml':
+ case 'html':
+ case 'xhtml':
+ return dojo.string.escapeXml.apply(this, args);
+ case 'sql':
+ return dojo.string.escapeSql.apply(this, args);
+ case 'regexp':
+ case 'regex':
+ return dojo.string.escapeRegExp.apply(this, args);
+ case 'javascript':
+ case 'jscript':
+ case 'js':
+ return dojo.string.escapeJavaScript.apply(this, args);
+ case 'ascii':
+ return dojo.string.encodeAscii.apply(this, args);
+ default:
+ return str
+ }
+};
+dojo.string.escapeXml = function (str, _1e8) {
+ str = str.replace(/&/gm, '&').replace(//gm, '>').replace(/"/gm, '"');
+ if (!_1e8) {
+ str = str.replace(/'/gm, ''')
+ }
+ return str
+};
+dojo.string.escapeSql = function (str) {
+ return str.replace(/'/gm, '\'\'')
+};
+dojo.string.escapeRegExp = function (str) {
+ return str.replace(/\\/gm, '\\\\').replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, '\\$1')
+};
+dojo.string.escapeJavaScript = function (str) {
+ return str.replace(/(["'\f\b\n\t\r])/gm, '\\$1')
+};
+dojo.string.escapeString = function (str) {
+ return ('"'
+ + str.replace(/(["\\])/g, '\\$1')
+ + '"').replace(/[\f]/g, '\\f').replace(/[\b]/g, '\\b').replace(/[\n]/g, '\\n').replace(/[\t]/g, '\\t').replace(/[\r]/g, '\\r')
+};
+dojo.string.summary = function (str, len) {
+ if (!len || str.length <= len) {
+ return str
+ }
+ return str.substring(0, len).replace(/\.+$/, '') + '...'
+};
+dojo.string.endsWith = function (str, end, _1f1) {
+ if (_1f1) {
+ str = str.toLowerCase();
+ end = end.toLowerCase()
+ }
+ if ((str.length - end.length) < 0) {
+ return false
+ }
+ return str.lastIndexOf(end) == str.length - end.length
+};
+dojo.string.endsWithAny = function (str) {
+ for (var i = 1; i < arguments.length; i++) {
+ if (dojo.string.endsWith(str, arguments[i])) {
+ return true
+ }
+ }
+ return false
+};
+dojo.string.startsWith = function (str, _1f5, _1f6) {
+ if (_1f6) {
+ str = str.toLowerCase();
+ _1f5 = _1f5.toLowerCase()
+ }
+ return str.indexOf(_1f5) == 0
+};
+dojo.string.startsWithAny = function (str) {
+ for (var i = 1; i < arguments.length; i++) {
+ if (dojo.string.startsWith(str, arguments[i])) {
+ return true
+ }
+ }
+ return false
+};
+dojo.string.has = function (str) {
+ for (var i = 1; i < arguments.length; i++) {
+ if (str.indexOf(arguments[i]) > -1) {
+ return true
+ }
+ }
+ return false
+};
+dojo.string.normalizeNewlines = function (text, _1fc) {
+ if (_1fc == '\n') {
+ text = text.replace(/\r\n/g, '\n');
+ text = text.replace(/\r/g, '\n')
+ } else {
+ if (_1fc == '\r') {
+ text = text.replace(/\r\n/g, '\r');
+ text = text.replace(/\n/g, '\r')
+ } else {
+ text = text.replace(/([^\r])\n/g, '$1\r\n').replace(/\r([^\n])/g, '\r\n$1')
+ }
+ }
+ return text
+};
+dojo.string.splitEscaped = function (str, _1fe) {
+ var _1ff = [];
+ for (var i = 0, _201 = 0; i < str.length; i++) {
+ if (str.charAt(i) == '\\') {
+ i++;
+ continue
+ }
+ if (str.charAt(i) == _1fe) {
+ _1ff.push(str.substring(_201, i));
+ _201 = i + 1
+ }
+ }
+ _1ff.push(str.substr(_201));
+ return _1ff
+};
+dojo.provide('dojo.dom');
+dojo.dom.ELEMENT_NODE = 1;
+dojo.dom.ATTRIBUTE_NODE = 2;
+dojo.dom.TEXT_NODE = 3;
+dojo.dom.CDATA_SECTION_NODE = 4;
+dojo.dom.ENTITY_REFERENCE_NODE = 5;
+dojo.dom.ENTITY_NODE = 6;
+dojo.dom.PROCESSING_INSTRUCTION_NODE = 7;
+dojo.dom.COMMENT_NODE = 8;
+dojo.dom.DOCUMENT_NODE = 9;
+dojo.dom.DOCUMENT_TYPE_NODE = 10;
+dojo.dom.DOCUMENT_FRAGMENT_NODE = 11;
+dojo.dom.NOTATION_NODE = 12;
+dojo.dom.dojoml = 'http://www.dojotoolkit.org/2004/dojoml';
+dojo.dom.xmlns = {
+ svg: 'http://www.w3.org/2000/svg',
+ smil: 'http://www.w3.org/2001/SMIL20/',
+ mml: 'http://www.w3.org/1998/Math/MathML',
+ cml: 'http://www.xml-cml.org',
+ xlink: 'http://www.w3.org/1999/xlink',
+ xhtml: 'http://www.w3.org/1999/xhtml',
+ xul: 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul',
+ xbl: 'http://www.mozilla.org/xbl',
+ fo: 'http://www.w3.org/1999/XSL/Format',
+ xsl: 'http://www.w3.org/1999/XSL/Transform',
+ xslt: 'http://www.w3.org/1999/XSL/Transform',
+ xi: 'http://www.w3.org/2001/XInclude',
+ xforms: 'http://www.w3.org/2002/01/xforms',
+ saxon: 'http://icl.com/saxon',
+ xalan: 'http://xml.apache.org/xslt',
+ xsd: 'http://www.w3.org/2001/XMLSchema',
+ dt: 'http://www.w3.org/2001/XMLSchema-datatypes',
+ xsi: 'http://www.w3.org/2001/XMLSchema-instance',
+ rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+ rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
+ dc: 'http://purl.org/dc/elements/1.1/',
+ dcq: 'http://purl.org/dc/qualifiers/1.0',
+ 'soap-env': 'http://schemas.xmlsoap.org/soap/envelope/',
+ wsdl: 'http://schemas.xmlsoap.org/wsdl/',
+ AdobeExtensions: 'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/'
+};
+dojo.dom.isNode = function (wh) {
+ if (typeof Element == 'function') {
+ try {
+ return wh instanceof Element
+ } catch (e) {
+ }
+ } else {
+ return wh && !isNaN(wh.nodeType)
+ }
+};
+dojo.dom.getUniqueId = function () {
+ var _203 = dojo.doc();
+ do {
+ var id = 'dj_unique_' + (++arguments.callee._idIncrement)
+ } while (_203.getElementById(id));
+ return id
+};
+dojo.dom.getUniqueId._idIncrement = 0;
+dojo.dom.firstElement = dojo.dom.getFirstChildElement = function (_205, _206) {
+ var node = _205.firstChild;
+ while (node && node.nodeType != dojo.dom.ELEMENT_NODE) {
+ node = node.nextSibling
+ }
+ if (_206 && node && node.tagName && node.tagName.toLowerCase() != _206.toLowerCase()) {
+ node = dojo.dom.nextElement(node, _206)
+ }
+ return node
+};
+dojo.dom.lastElement = dojo.dom.getLastChildElement = function (_208, _209) {
+ var node = _208.lastChild;
+ while (node && node.nodeType != dojo.dom.ELEMENT_NODE) {
+ node = node.previousSibling
+ }
+ if (_209 && node && node.tagName && node.tagName.toLowerCase() != _209.toLowerCase()) {
+ node = dojo.dom.prevElement(node, _209)
+ }
+ return node
+};
+dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function (node, _20c) {
+ if (!node) {
+ return null
+ }
+ do {
+ node = node.nextSibling
+ } while (node && node.nodeType != dojo.dom.ELEMENT_NODE);
+ if (node && _20c && _20c.toLowerCase() != node.tagName.toLowerCase()) {
+ return dojo.dom.nextElement(node, _20c)
+ }
+ return node
+};
+dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function (node, _20e) {
+ if (!node) {
+ return null
+ }
+ if (_20e) {
+ _20e = _20e.toLowerCase()
+ }
+ do {
+ node = node.previousSibling
+ } while (node && node.nodeType != dojo.dom.ELEMENT_NODE);
+ if (node && _20e && _20e.toLowerCase() != node.tagName.toLowerCase()) {
+ return dojo.dom.prevElement(node, _20e)
+ }
+ return node
+};
+dojo.dom.moveChildren = function (_20f, _210, trim) {
+ var _212 = 0;
+ if (trim) {
+ while (_20f.hasChildNodes() && _20f.firstChild.nodeType == dojo.dom.TEXT_NODE) {
+ _20f.removeChild(_20f.firstChild)
+ }
+ while (_20f.hasChildNodes() && _20f.lastChild.nodeType == dojo.dom.TEXT_NODE) {
+ _20f.removeChild(_20f.lastChild)
+ }
+ }
+ while (_20f.hasChildNodes()) {
+ _210.appendChild(_20f.firstChild);
+ _212++
+ }
+ return _212
+};
+dojo.dom.copyChildren = function (_213, _214, trim) {
+ var _216 = _213.cloneNode(true);
+ return this.moveChildren(_216, _214, trim)
+};
+dojo.dom.replaceChildren = function (node, _218) {
+ var _219 = [];
+ if (dojo.render.html.ie) {
+ for (var i = 0; i < node.childNodes.length; i++) {
+ _219.push(node.childNodes[i])
+ }
+ }
+ dojo.dom.removeChildren(node);
+ node.appendChild(_218);
+ for (var i = 0; i < _219.length; i++) {
+ dojo.dom.destroyNode(_219[i])
+ }
+};
+dojo.dom.removeChildren = function (node) {
+ var _21c = node.childNodes.length;
+ while (node.hasChildNodes()) {
+ dojo.dom.removeNode(node.firstChild)
+ }
+ return _21c
+};
+dojo.dom.replaceNode = function (node, _21e) {
+ return node.parentNode.replaceChild(_21e, node)
+};
+dojo.dom.destroyNode = function (node) {
+ if (node.parentNode) {
+ node = dojo.dom.removeNode(node)
+ }
+ if (node.nodeType != 3) {
+ if (dojo.evalObjPath('dojo.event.browser.clean', false)) {
+ dojo.event.browser.clean(node)
+ }
+ if (dojo.render.html.ie) {
+ node.outerHTML = ''
+ }
+ }
+};
+dojo.dom.removeNode = function (node) {
+ if (node && node.parentNode) {
+ return node.parentNode.removeChild(node)
+ }
+};
+dojo.dom.getAncestors = function (node, _222, _223) {
+ var _224 = [];
+ var _225 = (_222 && (_222 instanceof Function || typeof _222 == 'function'));
+ while (node) {
+ if (!_225 || _222(node)) {
+ _224.push(node)
+ }
+ if (_223 && _224.length > 0) {
+ return _224[0]
+ }
+ node = node.parentNode
+ }
+ if (_223) {
+ return null
+ }
+ return _224
+};
+dojo.dom.getAncestorsByTag = function (node, tag, _228) {
+ tag = tag.toLowerCase();
+ return dojo.dom.getAncestors(node, function (el) {
+ return ((el.tagName) && (el.tagName.toLowerCase() == tag))
+ }, _228)
+};
+dojo.dom.getFirstAncestorByTag = function (node, tag) {
+ return dojo.dom.getAncestorsByTag(node, tag, true)
+};
+dojo.dom.isDescendantOf = function (node, _22d, _22e) {
+ if (_22e && node) {
+ node = node.parentNode
+ }
+ while (node) {
+ if (node == _22d) {
+ return true
+ }
+ node = node.parentNode
+ }
+ return false
+};
+dojo.dom.innerXML = function (node) {
+ if (node.innerXML) {
+ return node.innerXML
+ } else {
+ if (node.xml) {
+ return node.xml
+ } else {
+ if (typeof XMLSerializer != 'undefined') {
+ return (new XMLSerializer()).serializeToString(node)
+ }
+ }
+ }
+};
+dojo.dom.createDocument = function () {
+ var doc = null;
+ var _231 = dojo.doc();
+ if (!dj_undef('ActiveXObject')) {
+ var _232 = ['MSXML2', 'Microsoft', 'MSXML', 'MSXML3'];
+ for (var i = 0; i < _232.length; i++) {
+ try {
+ doc = new ActiveXObject(_232[i] + '.XMLDOM')
+ } catch (e) {
+ }
+ if (doc) {
+ break
+ }
+ }
+ } else {
+ if ((_231.implementation) && (_231.implementation.createDocument)) {
+ doc = _231.implementation.createDocument('', '', null)
+ }
+ }
+ return doc
+};
+dojo.dom.createDocumentFromText = function (str, _235) {
+ if (!_235) {
+ _235 = 'text/xml'
+ }
+ if (!dj_undef('DOMParser')) {
+ var _236 = new DOMParser();
+ return _236.parseFromString(str, _235)
+ } else {
+ if (!dj_undef('ActiveXObject')) {
+ var _237 = dojo.dom.createDocument();
+ if (_237) {
+ _237.async = false;
+ _237.loadXML(str);
+ return _237
+ } else {
+ dojo.debug('toXml didn\'t work?')
+ }
+ } else {
+ var _238 = dojo.doc();
+ if (_238.createElement) {
+ var tmp = _238.createElement('xml');
+ tmp.innerHTML = str;
+ if (_238.implementation && _238.implementation.createDocument) {
+ var _23a = _238.implementation.createDocument('foo', '', null);
+ for (var i = 0; i < tmp.childNodes.length; i++) {
+ _23a.importNode(tmp.childNodes.item(i), true)
+ }
+ return _23a
+ }
+ return ((tmp.document) && (tmp.document.firstChild ? tmp.document.firstChild : tmp))
+ }
+ }
+ }
+ return null
+};
+dojo.dom.prependChild = function (node, _23d) {
+ if (_23d.firstChild) {
+ _23d.insertBefore(node, _23d.firstChild)
+ } else {
+ _23d.appendChild(node)
+ }
+ return true
+};
+dojo.dom.insertBefore = function (node, ref, _240) {
+ if ((_240 != true) && (node === ref || node.nextSibling === ref)) {
+ return false
+ }
+ var _241 = ref.parentNode;
+ _241.insertBefore(node, ref);
+ return true
+};
+dojo.dom.insertAfter = function (node, ref, _244) {
+ var pn = ref.parentNode;
+ if (ref == pn.lastChild) {
+ if ((_244 != true) && (node === ref)) {
+ return false
+ }
+ pn.appendChild(node)
+ } else {
+ return this.insertBefore(node, ref.nextSibling, _244)
+ }
+ return true
+};
+dojo.dom.insertAtPosition = function (node, ref, _248) {
+ if ((!node) || (!ref) || (!_248)) {
+ return false
+ }
+ switch (_248.toLowerCase()) {
+ case 'before':
+ return dojo.dom.insertBefore(node, ref);
+ case 'after':
+ return dojo.dom.insertAfter(node, ref);
+ case 'first':
+ if (ref.firstChild) {
+ return dojo.dom.insertBefore(node, ref.firstChild)
+ } else {
+ ref.appendChild(node);
+ return true
+ }
+ break;
+ default:
+ ref.appendChild(node);
+ return true
+ }
+};
+dojo.dom.insertAtIndex = function (node, _24a, _24b) {
+ var _24c = _24a.childNodes;
+ if (!_24c.length || _24c.length == _24b) {
+ _24a.appendChild(node);
+ return true
+ }
+ if (_24b == 0) {
+ return dojo.dom.prependChild(node, _24a)
+ }
+ return dojo.dom.insertAfter(node, _24c[_24b - 1])
+};
+dojo.dom.textContent = function (node, text) {
+ if (arguments.length > 1) {
+ var _24f = dojo.doc();
+ dojo.dom.replaceChildren(node, _24f.createTextNode(text));
+ return text
+ } else {
+ if (node.textContent != undefined) {
+ return node.textContent
+ }
+ var _250 = '';
+ if (node == null) {
+ return _250
+ }
+ for (var i = 0; i < node.childNodes.length; i++) {
+ switch (node.childNodes[i].nodeType) {
+ case 1:
+ case 5:
+ _250 += dojo.dom.textContent(node.childNodes[i]);
+ break;
+ case 3:
+ case 2:
+ case 4:
+ _250 += node.childNodes[i].nodeValue;
+ break;
+ default:
+ break
+ }
+ }
+ return _250
+ }
+};
+dojo.dom.hasParent = function (node) {
+ return Boolean(node && node.parentNode && dojo.dom.isNode(node.parentNode))
+};
+dojo.dom.isTag = function (node) {
+ if (node && node.tagName) {
+ for (var i = 1; i < arguments.length; i++) {
+ if (node.tagName == String(arguments[i])) {
+ return String(arguments[i])
+ }
+ }
+ }
+ return ''
+};
+dojo.dom.setAttributeNS = function (elem, _256, _257, _258) {
+ if (elem == null || ((elem == undefined) && (typeof elem == 'undefined'))) {
+ dojo.raise('No element given to dojo.dom.setAttributeNS')
+ }
+ if (!((elem.setAttributeNS == undefined) && (typeof elem.setAttributeNS == 'undefined'))) {
+ elem.setAttributeNS(_256, _257, _258)
+ } else {
+ var _259 = elem.ownerDocument;
+ var _25a = _259.createNode(2, _257, _256);
+ _25a.nodeValue = _258;
+ elem.setAttributeNode(_25a)
+ }
+};
+dojo.provide('dojo.undo.browser');
+try {
+ if ((!djConfig['preventBackButtonFix']) && (!dojo.hostenv.post_load_)) {
+ document.write('')
+ }
+} catch (e) {
+}
+if (dojo.render.html.opera) {
+ dojo.debug('Opera is not supported with dojo.undo.browser, so back/forward detection will not work.')
+}
+dojo.undo.browser = {
+ initialHref: (!dj_undef('window')) ? window.location.href : '',
+ initialHash: (!dj_undef('window')) ? window.location.hash : '',
+ moveForward: false,
+ historyStack: [],
+ forwardStack: [],
+ historyIframe: null,
+ bookmarkAnchor: null,
+ locationTimer: null,
+ setInitialState: function (args) {
+ this.initialState = this._createState(this.initialHref, args, this.initialHash)
+ },
+ addToHistory: function (args) {
+ this.forwardStack = [];
+ var hash = null;
+ var url = null;
+ if (!this.historyIframe) {
+ if (djConfig['useXDomain'] && !djConfig['dojoIframeHistoryUrl']) {
+ dojo.debug('dojo.undo.browser: When using cross-domain Dojo builds,'
+ + ' please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl'
+ + ' to the path on your domain to iframe_history.html')
+ }
+ this.historyIframe = window.frames['djhistory']
+ }
+ if (!this.bookmarkAnchor) {
+ this.bookmarkAnchor = document.createElement('a');
+ dojo.body().appendChild(this.bookmarkAnchor);
+ this.bookmarkAnchor.style.display = 'none'
+ }
+ if (args['changeUrl']) {
+ hash = '#' + ((args['changeUrl'] !== true) ? args['changeUrl'] : (new Date()).getTime());
+ if (this.historyStack.length == 0 && this.initialState.urlHash == hash) {
+ this.initialState = this._createState(url, args, hash);
+ return
+ } else {
+ if (this.historyStack.length > 0 && this.historyStack[this.historyStack.length - 1].urlHash == hash) {
+ this.historyStack[this.historyStack.length - 1] = this._createState(url, args, hash);
+ return
+ }
+ }
+ this.changingUrl = true;
+ setTimeout('window.location.href = \'' + hash + '\'; dojo.undo.browser.changingUrl = false;', 1);
+ this.bookmarkAnchor.href = hash;
+ if (dojo.render.html.ie) {
+ url = this._loadIframeHistory();
+ var _25f = args['back'] || args['backButton'] || args['handle'];
+ var tcb = function (_261) {
+ if (window.location.hash != '') {
+ setTimeout('window.location.href = \'' + hash + '\';', 1)
+ }
+ _25f.apply(this, [_261])
+ };
+ if (args['back']) {
+ args.back = tcb
+ } else {
+ if (args['backButton']) {
+ args.backButton = tcb
+ } else {
+ if (args['handle']) {
+ args.handle = tcb
+ }
+ }
+ }
+ var _262 = args['forward'] || args['forwardButton'] || args['handle'];
+ var tfw = function (_264) {
+ if (window.location.hash != '') {
+ window.location.href = hash
+ }
+ if (_262) {
+ _262.apply(this, [_264])
+ }
+ };
+ if (args['forward']) {
+ args.forward = tfw
+ } else {
+ if (args['forwardButton']) {
+ args.forwardButton = tfw
+ } else {
+ if (args['handle']) {
+ args.handle = tfw
+ }
+ }
+ }
+ } else {
+ if (dojo.render.html.moz) {
+ if (!this.locationTimer) {
+ this.locationTimer = setInterval('dojo.undo.browser.checkLocation();', 200)
+ }
+ }
+ }
+ } else {
+ url = this._loadIframeHistory()
+ }
+ this.historyStack.push(this._createState(url, args, hash))
+ },
+ checkLocation: function () {
+ if (!this.changingUrl) {
+ var hsl = this.historyStack.length;
+ if ((window.location.hash == this.initialHash || window.location.href == this.initialHref) && (hsl == 1)) {
+ this.handleBackButton();
+ return
+ }
+ if (this.forwardStack.length > 0) {
+ if (this.forwardStack[this.forwardStack.length - 1].urlHash == window.location.hash) {
+ this.handleForwardButton();
+ return
+ }
+ }
+ if ((hsl >= 2) && (this.historyStack[hsl - 2])) {
+ if (this.historyStack[hsl - 2].urlHash == window.location.hash) {
+ this.handleBackButton();
+
+ }
+ }
+ }
+ },
+ iframeLoaded: function (evt, _267) {
+ if (!dojo.render.html.opera) {
+ var _268 = this._getUrlQuery(_267.href);
+ if (_268 == null) {
+ if (this.historyStack.length == 1) {
+ this.handleBackButton()
+ }
+ return
+ }
+ if (this.moveForward) {
+ this.moveForward = false;
+ return
+ }
+ if (this.historyStack.length >= 2 && _268 == this._getUrlQuery(this.historyStack[this.historyStack.length
+ - 2].url)) {
+ this.handleBackButton()
+ } else {
+ if (this.forwardStack.length > 0 && _268 == this._getUrlQuery(this.forwardStack[this.forwardStack.length
+ - 1].url)) {
+ this.handleForwardButton()
+ }
+ }
+ }
+ },
+ handleBackButton: function () {
+ var _269 = this.historyStack.pop();
+ if (!_269) {
+ return
+ }
+ var last = this.historyStack[this.historyStack.length - 1];
+ if (!last && this.historyStack.length == 0) {
+ last = this.initialState
+ }
+ if (last) {
+ if (last.kwArgs['back']) {
+ last.kwArgs['back']()
+ } else {
+ if (last.kwArgs['backButton']) {
+ last.kwArgs['backButton']()
+ } else {
+ if (last.kwArgs['handle']) {
+ last.kwArgs.handle('back')
+ }
+ }
+ }
+ }
+ this.forwardStack.push(_269)
+ },
+ handleForwardButton: function () {
+ var last = this.forwardStack.pop();
+ if (!last) {
+ return
+ }
+ if (last.kwArgs['forward']) {
+ last.kwArgs.forward()
+ } else {
+ if (last.kwArgs['forwardButton']) {
+ last.kwArgs.forwardButton()
+ } else {
+ if (last.kwArgs['handle']) {
+ last.kwArgs.handle('forward')
+ }
+ }
+ }
+ this.historyStack.push(last)
+ },
+ _createState: function (url, args, hash) {
+ return {'url': url, 'kwArgs': args, 'urlHash': hash}
+ },
+ _getUrlQuery: function (url) {
+ var _270 = url.split('?');
+ if (_270.length < 2) {
+ return null
+ } else {
+ return _270[1]
+ }
+ },
+ _loadIframeHistory: function () {
+ var url = (djConfig['dojoIframeHistoryUrl'] || dojo.hostenv.getBaseScriptUri() + 'iframe_history.html')
+ + '?'
+ + (new Date()).getTime();
+ this.moveForward = true;
+ dojo.io.setIFrameSrc(this.historyIframe, url, false);
+ return url
+ }
+};
+dojo.provide('dojo.io.BrowserIO');
+if (!dj_undef('window')) {
+ dojo.io.checkChildrenForFile = function (node) {
+ var _273 = false;
+ var _274 = node.getElementsByTagName('input');
+ dojo.lang.forEach(_274, function (_275) {
+ if (_273) {
+ return
+ }
+ if (_275.getAttribute('type') == 'file') {
+ _273 = true
+ }
+ });
+ return _273
+ };
+ dojo.io.formHasFile = function (_276) {
+ return dojo.io.checkChildrenForFile(_276)
+ };
+ dojo.io.updateNode = function (node, _278) {
+ node = dojo.byId(node);
+ var args = _278;
+ if (dojo.lang.isString(_278)) {
+ args = {url: _278}
+ }
+ args.mimetype = 'text/html';
+ args.load = function (t, d, e) {
+ while (node.firstChild) {
+ dojo.dom.destroyNode(node.firstChild)
+ }
+ node.innerHTML = d
+ };
+ dojo.io.bind(args)
+ };
+ dojo.io.formFilter = function (node) {
+ var type = (node.type || '').toLowerCase();
+ return !node.disabled && node.name && !dojo.lang.inArray(['file', 'submit', 'image', 'reset', 'button'], type)
+ };
+ dojo.io.encodeForm = function (_27f, _280, _281) {
+ if ((!_27f) || (!_27f.tagName) || (!_27f.tagName.toLowerCase() == 'form')) {
+ dojo.raise('Attempted to encode a non-form element.')
+ }
+ if (!_281) {
+ _281 = dojo.io.formFilter
+ }
+ var enc = /utf/i.test(_280 || '') ? encodeURIComponent : dojo.string.encodeAscii;
+ var _283 = [];
+ for (var i = 0; i < _27f.elements.length; i++) {
+ var elm = _27f.elements[i];
+ if (!elm || elm.tagName.toLowerCase() == 'fieldset' || !_281(elm)) {
+ continue
+ }
+ var name = enc(elm.name);
+ var type = elm.type.toLowerCase();
+ if (type == 'select-multiple') {
+ for (var j = 0; j < elm.options.length; j++) {
+ if (elm.options[j].selected) {
+ _283.push(name + '=' + enc(elm.options[j].value))
+ }
+ }
+ } else {
+ if (dojo.lang.inArray(['radio', 'checkbox'], type)) {
+ if (elm.checked) {
+ _283.push(name + '=' + enc(elm.value))
+ }
+ } else {
+ _283.push(name + '=' + enc(elm.value))
+ }
+ }
+ }
+ var _289 = _27f.getElementsByTagName('input');
+ for (var i = 0; i < _289.length; i++) {
+ var _28a = _289[i];
+ if (_28a.type.toLowerCase() == 'image' && _28a.form == _27f && _281(_28a)) {
+ var name = enc(_28a.name);
+ _283.push(name + '=' + enc(_28a.value));
+ _283.push(name + '.x=0');
+ _283.push(name + '.y=0')
+ }
+ }
+ return _283.join('&') + '&'
+ };
+ dojo.io.FormBind = function (args) {
+ this.bindArgs = {};
+ if (args && args.formNode) {
+ this.init(args)
+ } else {
+ if (args) {
+ this.init({formNode: args})
+ }
+ }
+ };
+ dojo.lang.extend(dojo.io.FormBind, {
+ form: null, bindArgs: null, clickedButton: null, init: function (args) {
+ var form = dojo.byId(args.formNode);
+ if (!form || !form.tagName || form.tagName.toLowerCase() != 'form') {
+ throw new Error('FormBind: Couldn\'t apply, invalid form')
+ } else {
+ if (this.form == form) {
+ return
+ } else {
+ if (this.form) {
+ throw new Error('FormBind: Already applied to a form')
+ }
+ }
+ }
+ dojo.lang.mixin(this.bindArgs, args);
+ this.form = form;
+ this.connect(form, 'onsubmit', 'submit');
+ for (var i = 0; i < form.elements.length; i++) {
+ var node = form.elements[i];
+ if (node && node.type && dojo.lang.inArray(['submit', 'button'], node.type.toLowerCase())) {
+ this.connect(node, 'onclick', 'click')
+ }
+ }
+ var _290 = form.getElementsByTagName('input');
+ for (var i = 0; i < _290.length; i++) {
+ var _291 = _290[i];
+ if (_291.type.toLowerCase() == 'image' && _291.form == form) {
+ this.connect(_291, 'onclick', 'click')
+ }
+ }
+ }, onSubmit: function (form) {
+ return true
+ }, submit: function (e) {
+ e.preventDefault();
+ if (this.onSubmit(this.form)) {
+ dojo.io.bind(dojo.lang.mixin(this.bindArgs, {formFilter: dojo.lang.hitch(this, 'formFilter')}))
+ }
+ }, click: function (e) {
+ var node = e.currentTarget;
+ if (node.disabled) {
+ return
+ }
+ this.clickedButton = node
+ }, formFilter: function (node) {
+ var type = (node.type || '').toLowerCase();
+ var _298 = false;
+ if (node.disabled || !node.name) {
+ _298 = false
+ } else {
+ if (dojo.lang.inArray(['submit', 'button', 'image'], type)) {
+ if (!this.clickedButton) {
+ this.clickedButton = node
+ }
+ _298 = node == this.clickedButton
+ } else {
+ _298 = !dojo.lang.inArray(['file', 'submit', 'reset', 'button'], type)
+ }
+ }
+ return _298
+ }, connect: function (_299, _29a, _29b) {
+ if (dojo.evalObjPath('dojo.event.connect')) {
+ dojo.event.connect(_299, _29a, this, _29b)
+ } else {
+ var fcn = dojo.lang.hitch(this, _29b);
+ _299[_29a] = function (e) {
+ if (!e) {
+ e = window.event
+ }
+ if (!e.currentTarget) {
+ e.currentTarget = e.srcElement
+ }
+ if (!e.preventDefault) {
+ e.preventDefault = function () {
+ window.event.returnValue = false
+ }
+ }
+ fcn(e)
+ }
+ }
+ }
+ });
+ dojo.io.XMLHTTPTransport = new function () {
+ var _29e = this;
+ var _29f = {};
+ this.useCache = false;
+ this.preventCache = false;
+
+ function getCacheKey(url, _2a1, _2a2) {
+ return url + '|' + _2a1 + '|' + _2a2.toLowerCase()
+ }
+
+ function addToCache(url, _2a4, _2a5, http) {
+ _29f[getCacheKey(url, _2a4, _2a5)] = http
+ }
+
+ function getFromCache(url, _2a8, _2a9) {
+ return _29f[getCacheKey(url, _2a8, _2a9)]
+ }
+
+ this.clearCache = function () {
+ _29f = {}
+ };
+
+ function doLoad(_2aa, http, url, _2ad, _2ae) {
+ if (((http.status >= 200) && (http.status < 300))
+ || (http.status == 304)
+ || (http.status == 1223)
+ || (location.protocol == 'file:' && (http.status == 0 || http.status == undefined))
+ || (location.protocol == 'chrome:' && (http.status == 0 || http.status == undefined))) {
+ var ret;
+ if (_2aa.method.toLowerCase() == 'head') {
+ var _2b0 = http.getAllResponseHeaders();
+ ret = {};
+ ret.toString = function () {
+ return _2b0
+ };
+ var _2b1 = _2b0.split(/[\r\n]+/g);
+ for (var i = 0; i < _2b1.length; i++) {
+ var pair = _2b1[i].match(/^([^:]+)\s*:\s*(.+)$/i);
+ if (pair) {
+ ret[pair[1]] = pair[2]
+ }
+ }
+ } else {
+ if (_2aa.mimetype == 'text/javascript') {
+ try {
+ ret = dj_eval(http.responseText)
+ } catch (e) {
+ dojo.debug(e);
+ dojo.debug(http.responseText);
+ ret = null
+ }
+ } else {
+ if (_2aa.mimetype.substr(0, 9) == 'text/json' || _2aa.mimetype.substr(0, 16) == 'application/json') {
+ try {
+ ret = dj_eval('(' + _2aa.jsonFilter(http.responseText) + ')')
+ } catch (e) {
+ dojo.debug(e);
+ dojo.debug(http.responseText);
+ ret = false
+ }
+ } else {
+ if ((_2aa.mimetype == 'application/xml') || (_2aa.mimetype == 'text/xml')) {
+ ret = http.responseXML;
+ if (!ret || typeof ret == 'string' || !http.getResponseHeader('Content-Type')) {
+ ret = dojo.dom.createDocumentFromText(http.responseText)
+ }
+ } else {
+ ret = http.responseText
+ }
+ }
+ }
+ }
+ if (_2ae) {
+ addToCache(url, _2ad, _2aa.method, http)
+ }
+ _2aa[(typeof _2aa.load == 'function') ? 'load' : 'handle']('load', ret, http, _2aa)
+ } else {
+ var _2b4 = new dojo.io.Error('XMLHttpTransport Error: ' + http.status + ' ' + http.statusText);
+ _2aa[(typeof _2aa.error == 'function') ? 'error' : 'handle']('error', _2b4, http, _2aa)
+ }
+ }
+
+ function setHeaders(http, _2b6) {
+ if (_2b6['headers']) {
+ for (var _2b7 in _2b6['headers']) {
+ if (_2b7.toLowerCase() == 'content-type' && !_2b6['contentType']) {
+ _2b6['contentType'] = _2b6['headers'][_2b7]
+ } else {
+ http.setRequestHeader(_2b7, _2b6['headers'][_2b7])
+ }
+ }
+ }
+ }
+
+ this.inFlight = [];
+ this.inFlightTimer = null;
+ this.startWatchingInFlight = function () {
+ if (!this.inFlightTimer) {
+ this.inFlightTimer = setTimeout('dojo.io.XMLHTTPTransport.watchInFlight();', 10)
+ }
+ };
+ this.watchInFlight = function () {
+ var now = null;
+ if (!dojo.hostenv._blockAsync && !_29e._blockAsync) {
+ for (var x = this.inFlight.length - 1; x >= 0; x--) {
+ try {
+ var tif = this.inFlight[x];
+ if (!tif || tif.http._aborted || !tif.http.readyState) {
+ this.inFlight.splice(x, 1);
+ continue
+ }
+ if (4 == tif.http.readyState) {
+ this.inFlight.splice(x, 1);
+ doLoad(tif.req, tif.http, tif.url, tif.query, tif.useCache)
+ } else {
+ if (tif.startTime) {
+ if (!now) {
+ now = (new Date()).getTime()
+ }
+ if (tif.startTime + (tif.req.timeoutSeconds * 1000) < now) {
+ if (typeof tif.http.abort == 'function') {
+ tif.http.abort()
+ }
+ this.inFlight.splice(x, 1);
+ tif.req[(typeof tif.req.timeout
+ == 'function') ? 'timeout' : 'handle']('timeout', null, tif.http, tif.req)
+ }
+ }
+ }
+ } catch (e) {
+ try {
+ var _2bb = new dojo.io.Error('XMLHttpTransport.watchInFlight Error: ' + e);
+ tif.req[(typeof tif.req.error == 'function') ? 'error' : 'handle']('error', _2bb, tif.http, tif.req)
+ } catch (e2) {
+ dojo.debug('XMLHttpTransport error callback failed: ' + e2)
+ }
+ }
+ }
+ }
+ clearTimeout(this.inFlightTimer);
+ if (this.inFlight.length == 0) {
+ this.inFlightTimer = null;
+ return
+ }
+ this.inFlightTimer = setTimeout('dojo.io.XMLHTTPTransport.watchInFlight();', 10)
+ };
+ var _2bc = dojo.hostenv.getXmlhttpObject() ? true : false;
+ this.canHandle = function (_2bd) {
+ var mlc = _2bd['mimetype'].toLowerCase() || '';
+ return _2bc
+ && ((dojo.lang.inArray(['text/plain', 'text/html', 'application/xml', 'text/xml', 'text/javascript'], mlc))
+ || (mlc.substr(0, 9) == 'text/json' || mlc.substr(0, 16) == 'application/json'))
+ && !(_2bd['formNode'] && dojo.io.formHasFile(_2bd['formNode']))
+ };
+ this.multipartBoundary = '45309FFF-BD65-4d50-99C9-36986896A96F';
+ this.bind = function (_2bf) {
+ if (!_2bf['url']) {
+ if (!_2bf['formNode']
+ && (_2bf['backButton'] || _2bf['back'] || _2bf['changeUrl'] || _2bf['watchForURL'])
+ && (!djConfig.preventBackButtonFix)) {
+ dojo.deprecated('Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request', 'Use dojo.undo.browser.addToHistory() instead.', '0.4');
+ dojo.undo.browser.addToHistory(_2bf);
+ return true
+ }
+ }
+ var url = _2bf.url;
+ var _2c1 = '';
+ if (_2bf['formNode']) {
+ var ta = _2bf.formNode.getAttribute('action');
+ if ((ta) && (!_2bf['url'])) {
+ url = ta
+ }
+ var tp = _2bf.formNode.getAttribute('method');
+ if ((tp) && (!_2bf['method'])) {
+ _2bf.method = tp
+ }
+ _2c1 += dojo.io.encodeForm(_2bf.formNode, _2bf.encoding, _2bf['formFilter'])
+ }
+ if (url.indexOf('#') > -1) {
+ dojo.debug('Warning: dojo.io.bind: stripping hash values from url:', url);
+ url = url.split('#')[0]
+ }
+ if (_2bf['file']) {
+ _2bf.method = 'post'
+ }
+ if (!_2bf['method']) {
+ _2bf.method = 'get'
+ }
+ if (_2bf.method.toLowerCase() == 'get') {
+ _2bf.multipart = false
+ } else {
+ if (_2bf['file']) {
+ _2bf.multipart = true
+ } else {
+ if (!_2bf['multipart']) {
+ _2bf.multipart = false
+ }
+ }
+ }
+ if (_2bf['backButton'] || _2bf['back'] || _2bf['changeUrl']) {
+ dojo.undo.browser.addToHistory(_2bf)
+ }
+ var _2c4 = _2bf['content'] || {};
+ if (_2bf.sendTransport) {
+ _2c4['dojo.transport'] = 'xmlhttp'
+ }
+ do {
+ if (_2bf.postContent) {
+ _2c1 = _2bf.postContent;
+ break
+ }
+ if (_2c4) {
+ _2c1 += dojo.io.argsFromMap(_2c4, _2bf.encoding)
+ }
+ if (_2bf.method.toLowerCase() == 'get' || !_2bf.multipart) {
+ break
+ }
+ var t = [];
+ if (_2c1.length) {
+ var q = _2c1.split('&');
+ for (var i = 0; i < q.length; ++i) {
+ if (q[i].length) {
+ var p = q[i].split('=');
+ t.push('--' + this.multipartBoundary, 'Content-Disposition: form-data; name="' + p[0] + '"', '', p[1])
+ }
+ }
+ }
+ if (_2bf.file) {
+ if (dojo.lang.isArray(_2bf.file)) {
+ for (var i = 0; i < _2bf.file.length; ++i) {
+ var o = _2bf.file[i];
+ t.push('--' + this.multipartBoundary, 'Content-Disposition: form-data; name="'
+ + o.name
+ + '"; filename="'
+ + ('fileName' in o ? o.fileName : o.name)
+ + '"', 'Content-Type: ' + ('contentType'
+ in o ? o.contentType : 'application/octet-stream'), '', o.content)
+ }
+ } else {
+ var o = _2bf.file;
+ t.push('--' + this.multipartBoundary, 'Content-Disposition: form-data; name="'
+ + o.name
+ + '"; filename="'
+ + ('fileName' in o ? o.fileName : o.name)
+ + '"', 'Content-Type: ' + ('contentType'
+ in o ? o.contentType : 'application/octet-stream'), '', o.content)
+ }
+ }
+ if (t.length) {
+ t.push('--' + this.multipartBoundary + '--', '');
+ _2c1 = t.join('\r\n')
+ }
+ } while (false);
+ var _2ca = _2bf['sync'] ? false : true;
+ var _2cb = _2bf['preventCache'] || (this.preventCache == true && _2bf['preventCache'] != false);
+ var _2cc = _2bf['useCache'] == true || (this.useCache == true && _2bf['useCache'] != false);
+ if (!_2cb && _2cc) {
+ var _2cd = getFromCache(url, _2c1, _2bf.method);
+ if (_2cd) {
+ doLoad(_2bf, _2cd, url, _2c1, false);
+ return
+ }
+ }
+ var http = dojo.hostenv.getXmlhttpObject(_2bf);
+ var _2cf = false;
+ if (_2ca) {
+ var _2d0 = this.inFlight.push({
+ 'req': _2bf,
+ 'http': http,
+ 'url': url,
+ 'query': _2c1,
+ 'useCache': _2cc,
+ 'startTime': _2bf.timeoutSeconds ? (new Date()).getTime() : 0
+ });
+ this.startWatchingInFlight()
+ } else {
+ _29e._blockAsync = true
+ }
+ if (_2bf.method.toLowerCase() == 'post') {
+ if (!_2bf.user) {
+ http.open('POST', url, _2ca)
+ } else {
+ http.open('POST', url, _2ca, _2bf.user, _2bf.password)
+ }
+ setHeaders(http, _2bf);
+ http.setRequestHeader('Content-Type', _2bf.multipart ? ('multipart/form-data; boundary='
+ + this.multipartBoundary) : (_2bf.contentType || 'application/x-www-form-urlencoded'));
+ try {
+ http.send(_2c1)
+ } catch (e) {
+ if (typeof http.abort == 'function') {
+ http.abort()
+ }
+ doLoad(_2bf, {status: 404}, url, _2c1, _2cc)
+ }
+ } else {
+ var _2d1 = url;
+ if (_2c1 != '') {
+ _2d1 += (_2d1.indexOf('?') > -1 ? '&' : '?') + _2c1
+ }
+ if (_2cb) {
+ _2d1 += (dojo.string.endsWithAny(_2d1, '?', '&') ? '' : (_2d1.indexOf('?') > -1 ? '&' : '?'))
+ + 'dojo.preventCache='
+ + new Date().valueOf()
+ }
+ if (!_2bf.user) {
+ http.open(_2bf.method.toUpperCase(), _2d1, _2ca)
+ } else {
+ http.open(_2bf.method.toUpperCase(), _2d1, _2ca, _2bf.user, _2bf.password)
+ }
+ setHeaders(http, _2bf);
+ try {
+ http.send(null)
+ } catch (e) {
+ if (typeof http.abort == 'function') {
+ http.abort()
+ }
+ doLoad(_2bf, {status: 404}, url, _2c1, _2cc)
+ }
+ }
+ if (!_2ca) {
+ doLoad(_2bf, http, url, _2c1, _2cc);
+ _29e._blockAsync = false
+ }
+ _2bf.abort = function () {
+ try {
+ http._aborted = true
+ } catch (e) {
+ }
+ return http.abort()
+ };
+
+ };
+ dojo.io.transports.addTransport('XMLHTTPTransport')
+ };
+}
+dojo.provide('dojo.io.cookie');
+dojo.io.cookie.setCookie = function (name, _2d3, days, path, _2d6, _2d7) {
+ var _2d8 = -1;
+ if ((typeof days == 'number') && (days >= 0)) {
+ var d = new Date();
+ d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000));
+ _2d8 = d.toGMTString()
+ }
+ _2d3 = escape(_2d3);
+ document.cookie = name + '=' + _2d3 + ';' + (_2d8 != -1 ? ' expires=' + _2d8 + ';' : '') + (path ? 'path='
+ + path : '') + (_2d6 ? '; domain=' + _2d6 : '') + (_2d7 ? '; secure' : '')
+};
+dojo.io.cookie.set = dojo.io.cookie.setCookie;
+dojo.io.cookie.getCookie = function (name) {
+ var idx = document.cookie.lastIndexOf(name + '=');
+ if (idx == -1) {
+ return null
+ }
+ var _2dc = document.cookie.substring(idx + name.length + 1);
+ var end = _2dc.indexOf(';');
+ if (end == -1) {
+ end = _2dc.length
+ }
+ _2dc = _2dc.substring(0, end);
+ _2dc = unescape(_2dc);
+ return _2dc
+};
+dojo.io.cookie.get = dojo.io.cookie.getCookie;
+dojo.io.cookie.deleteCookie = function (name) {
+ dojo.io.cookie.setCookie(name, '-', 0)
+};
+dojo.io.cookie.setObjectCookie = function (name, obj, days, path, _2e3, _2e4, _2e5) {
+ if (arguments.length == 5) {
+ _2e5 = _2e3;
+ _2e3 = null;
+ _2e4 = null
+ }
+ var _2e6 = [], _2e7, _2e8 = '';
+ if (!_2e5) {
+ _2e7 = dojo.io.cookie.getObjectCookie(name)
+ }
+ if (days >= 0) {
+ if (!_2e7) {
+ _2e7 = {}
+ }
+ for (var prop in obj) {
+ if (obj[prop] == null) {
+ delete _2e7[prop]
+ } else {
+ if ((typeof obj[prop] == 'string') || (typeof obj[prop] == 'number')) {
+ _2e7[prop] = obj[prop]
+ }
+ }
+ }
+ prop = null;
+ for (var prop in _2e7) {
+ _2e6.push(escape(prop) + '=' + escape(_2e7[prop]))
+ }
+ _2e8 = _2e6.join('&')
+ }
+ dojo.io.cookie.setCookie(name, _2e8, days, path, _2e3, _2e4)
+};
+dojo.io.cookie.getObjectCookie = function (name) {
+ var _2eb = null, _2ec = dojo.io.cookie.getCookie(name);
+ if (_2ec) {
+ _2eb = {};
+ var _2ed = _2ec.split('&');
+ for (var i = 0; i < _2ed.length; i++) {
+ var pair = _2ed[i].split('=');
+ var _2f0 = pair[1];
+ if (isNaN(_2f0)) {
+ _2f0 = unescape(pair[1])
+ }
+ _2eb[unescape(pair[0])] = _2f0
+ }
+ }
+ return _2eb
+};
+dojo.io.cookie.isSupported = function () {
+ if (typeof navigator.cookieEnabled != 'boolean') {
+ dojo.io.cookie.setCookie('__TestingYourBrowserForCookieSupport__', 'CookiesAllowed', 90, null);
+ var _2f1 = dojo.io.cookie.getCookie('__TestingYourBrowserForCookieSupport__');
+ navigator.cookieEnabled = (_2f1 == 'CookiesAllowed');
+ if (navigator.cookieEnabled) {
+ this.deleteCookie('__TestingYourBrowserForCookieSupport__')
+ }
+ }
+ return navigator.cookieEnabled
+};
+if (!dojo.io.cookies) {
+ dojo.io.cookies = dojo.io.cookie
+}
+dojo.kwCompoundRequire({
+ common: ['dojo.io.common'],
+ rhino: ['dojo.io.RhinoIO'],
+ browser: ['dojo.io.BrowserIO', 'dojo.io.cookie'],
+ dashboard: ['dojo.io.BrowserIO', 'dojo.io.cookie']
+});
+dojo.provide('dojo.io.*');
+dojo.provide('dojo.event.common');
+dojo.event = new function () {
+ this._canTimeout = dojo.lang.isFunction(dj_global['setTimeout']) || dojo.lang.isAlien(dj_global['setTimeout']);
+
+ function interpolateArgs(args, _2f3) {
+ var dl = dojo.lang;
+ var ao = {
+ srcObj: dj_global,
+ srcFunc: null,
+ adviceObj: dj_global,
+ adviceFunc: null,
+ aroundObj: null,
+ aroundFunc: null,
+ adviceType: (args.length > 2) ? args[0] : 'after',
+ precedence: 'last',
+ once: false,
+ delay: null,
+ rate: 0,
+ adviceMsg: false,
+ maxCalls: -1
+ };
+ switch (args.length) {
+ case 0:
+ return;
+ case 1:
+ return;
+ case 2:
+ ao.srcFunc = args[0];
+ ao.adviceFunc = args[1];
+ break;
+ case 3:
+ if ((dl.isObject(args[0])) && (dl.isString(args[1])) && (dl.isString(args[2]))) {
+ ao.adviceType = 'after';
+ ao.srcObj = args[0];
+ ao.srcFunc = args[1];
+ ao.adviceFunc = args[2]
+ } else {
+ if ((dl.isString(args[1])) && (dl.isString(args[2]))) {
+ ao.srcFunc = args[1];
+ ao.adviceFunc = args[2]
+ } else {
+ if ((dl.isObject(args[0])) && (dl.isString(args[1])) && (dl.isFunction(args[2]))) {
+ ao.adviceType = 'after';
+ ao.srcObj = args[0];
+ ao.srcFunc = args[1];
+ var _2f6 = dl.nameAnonFunc(args[2], ao.adviceObj, _2f3);
+ ao.adviceFunc = _2f6
+ } else {
+ if ((dl.isFunction(args[0])) && (dl.isObject(args[1])) && (dl.isString(args[2]))) {
+ ao.adviceType = 'after';
+ ao.srcObj = dj_global;
+ var _2f6 = dl.nameAnonFunc(args[0], ao.srcObj, _2f3);
+ ao.srcFunc = _2f6;
+ ao.adviceObj = args[1];
+ ao.adviceFunc = args[2]
+ }
+ }
+ }
+ }
+ break;
+ case 4:
+ if ((dl.isObject(args[0])) && (dl.isObject(args[2]))) {
+ ao.adviceType = 'after';
+ ao.srcObj = args[0];
+ ao.srcFunc = args[1];
+ ao.adviceObj = args[2];
+ ao.adviceFunc = args[3]
+ } else {
+ if ((dl.isString(args[0])) && (dl.isString(args[1])) && (dl.isObject(args[2]))) {
+ ao.adviceType = args[0];
+ ao.srcObj = dj_global;
+ ao.srcFunc = args[1];
+ ao.adviceObj = args[2];
+ ao.adviceFunc = args[3]
+ } else {
+ if ((dl.isString(args[0])) && (dl.isFunction(args[1])) && (dl.isObject(args[2]))) {
+ ao.adviceType = args[0];
+ ao.srcObj = dj_global;
+ var _2f6 = dl.nameAnonFunc(args[1], dj_global, _2f3);
+ ao.srcFunc = _2f6;
+ ao.adviceObj = args[2];
+ ao.adviceFunc = args[3]
+ } else {
+ if ((dl.isString(args[0]))
+ && (dl.isObject(args[1]))
+ && (dl.isString(args[2]))
+ && (dl.isFunction(args[3]))) {
+ ao.srcObj = args[1];
+ ao.srcFunc = args[2];
+ var _2f6 = dl.nameAnonFunc(args[3], dj_global, _2f3);
+ ao.adviceObj = dj_global;
+ ao.adviceFunc = _2f6
+ } else {
+ if (dl.isObject(args[1])) {
+ ao.srcObj = args[1];
+ ao.srcFunc = args[2];
+ ao.adviceObj = dj_global;
+ ao.adviceFunc = args[3]
+ } else {
+ if (dl.isObject(args[2])) {
+ ao.srcObj = dj_global;
+ ao.srcFunc = args[1];
+ ao.adviceObj = args[2];
+ ao.adviceFunc = args[3]
+ } else {
+ ao.srcObj = ao.adviceObj = ao.aroundObj = dj_global;
+ ao.srcFunc = args[1];
+ ao.adviceFunc = args[2];
+ ao.aroundFunc = args[3]
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 6:
+ ao.srcObj = args[1];
+ ao.srcFunc = args[2];
+ ao.adviceObj = args[3];
+ ao.adviceFunc = args[4];
+ ao.aroundFunc = args[5];
+ ao.aroundObj = dj_global;
+ break;
+ default:
+ ao.srcObj = args[1];
+ ao.srcFunc = args[2];
+ ao.adviceObj = args[3];
+ ao.adviceFunc = args[4];
+ ao.aroundObj = args[5];
+ ao.aroundFunc = args[6];
+ ao.once = args[7];
+ ao.delay = args[8];
+ ao.rate = args[9];
+ ao.adviceMsg = args[10];
+ ao.maxCalls = (!isNaN(parseInt(args[11]))) ? args[11] : -1;
+ break
+ }
+ if (dl.isFunction(ao.aroundFunc)) {
+ var _2f6 = dl.nameAnonFunc(ao.aroundFunc, ao.aroundObj, _2f3);
+ ao.aroundFunc = _2f6
+ }
+ if (dl.isFunction(ao.srcFunc)) {
+ ao.srcFunc = dl.getNameInObj(ao.srcObj, ao.srcFunc)
+ }
+ if (dl.isFunction(ao.adviceFunc)) {
+ ao.adviceFunc = dl.getNameInObj(ao.adviceObj, ao.adviceFunc)
+ }
+ if ((ao.aroundObj) && (dl.isFunction(ao.aroundFunc))) {
+ ao.aroundFunc = dl.getNameInObj(ao.aroundObj, ao.aroundFunc)
+ }
+ if (!ao.srcObj) {
+ dojo.raise('bad srcObj for srcFunc: ' + ao.srcFunc)
+ }
+ if (!ao.adviceObj) {
+ dojo.raise('bad adviceObj for adviceFunc: ' + ao.adviceFunc)
+ }
+ if (!ao.adviceFunc) {
+ dojo.debug('bad adviceFunc for srcFunc: ' + ao.srcFunc);
+ dojo.debugShallow(ao)
+ }
+ return ao
+ }
+
+ this.connect = function () {
+ if (arguments.length == 1) {
+ var ao = arguments[0]
+ } else {
+ var ao = interpolateArgs(arguments, true)
+ }
+ if (dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == 'onkey')) {
+ if (dojo.render.html.ie) {
+ ao.srcFunc = 'onkeydown';
+ this.connect(ao)
+ }
+ ao.srcFunc = 'onkeypress'
+ }
+ if (dojo.lang.isArray(ao.srcObj) && ao.srcObj != '') {
+ var _2f8 = {};
+ for (var x in ao) {
+ _2f8[x] = ao[x]
+ }
+ var mjps = [];
+ dojo.lang.forEach(ao.srcObj, function (src) {
+ if ((dojo.render.html.capable) && (dojo.lang.isString(src))) {
+ src = dojo.byId(src)
+ }
+ _2f8.srcObj = src;
+ mjps.push(dojo.event.connect.call(dojo.event, _2f8))
+ });
+ return mjps
+ }
+ var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
+ if (ao.adviceFunc) {
+ var mjp2 = dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc)
+ }
+ mjp.kwAddAdvice(ao);
+ return mjp
+ };
+ this.log = function (a1, a2) {
+ var _300;
+ if ((arguments.length == 1) && (typeof a1 == 'object')) {
+ _300 = a1
+ } else {
+ _300 = {srcObj: a1, srcFunc: a2}
+ }
+ _300.adviceFunc = function () {
+ var _301 = [];
+ for (var x = 0; x < arguments.length; x++) {
+ _301.push(arguments[x])
+ }
+ dojo.debug('(' + _300.srcObj + ').' + _300.srcFunc, ':', _301.join(', '))
+ };
+ this.kwConnect(_300)
+ };
+ this.connectBefore = function () {
+ var args = ['before'];
+ for (var i = 0; i < arguments.length; i++) {
+ args.push(arguments[i])
+ }
+ return this.connect.apply(this, args)
+ };
+ this.connectAround = function () {
+ var args = ['around'];
+ for (var i = 0; i < arguments.length; i++) {
+ args.push(arguments[i])
+ }
+ return this.connect.apply(this, args)
+ };
+ this.connectOnce = function () {
+ var ao = interpolateArgs(arguments, true);
+ ao.once = true;
+ return this.connect(ao)
+ };
+ this.connectRunOnce = function () {
+ var ao = interpolateArgs(arguments, true);
+ ao.maxCalls = 1;
+ return this.connect(ao)
+ };
+ this._kwConnectImpl = function (_309, _30a) {
+ var fn = (_30a) ? 'disconnect' : 'connect';
+ if (typeof _309['srcFunc'] == 'function') {
+ _309.srcObj = _309['srcObj'] || dj_global;
+ var _30c = dojo.lang.nameAnonFunc(_309.srcFunc, _309.srcObj, true);
+ _309.srcFunc = _30c
+ }
+ if (typeof _309['adviceFunc'] == 'function') {
+ _309.adviceObj = _309['adviceObj'] || dj_global;
+ var _30c = dojo.lang.nameAnonFunc(_309.adviceFunc, _309.adviceObj, true);
+ _309.adviceFunc = _30c
+ }
+ _309.srcObj = _309['srcObj'] || dj_global;
+ _309.adviceObj = _309['adviceObj'] || _309['targetObj'] || dj_global;
+ _309.adviceFunc = _309['adviceFunc'] || _309['targetFunc'];
+ return dojo.event[fn](_309)
+ };
+ this.kwConnect = function (_30d) {
+ return this._kwConnectImpl(_30d, false)
+ };
+ this.disconnect = function () {
+ if (arguments.length == 1) {
+ var ao = arguments[0]
+ } else {
+ var ao = interpolateArgs(arguments, true)
+ }
+ if (!ao.adviceFunc) {
+ return
+ }
+ if (dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == 'onkey')) {
+ if (dojo.render.html.ie) {
+ ao.srcFunc = 'onkeydown';
+ this.disconnect(ao)
+ }
+ ao.srcFunc = 'onkeypress'
+ }
+ if (!ao.srcObj[ao.srcFunc]) {
+ return null
+ }
+ var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc, true);
+ mjp.removeAdvice(ao.adviceObj, ao.adviceFunc, ao.adviceType, ao.once);
+ return mjp
+ };
+ this.kwDisconnect = function (_310) {
+ return this._kwConnectImpl(_310, true)
+ }
+};
+;dojo.event.MethodInvocation = function (_311, obj, args) {
+ this.jp_ = _311;
+ this.object = obj;
+ this.args = [];
+ for (var x = 0; x < args.length; x++) {
+ this.args[x] = args[x]
+ }
+ this.around_index = -1
+};
+dojo.event.MethodInvocation.prototype.proceed = function () {
+ this.around_index++;
+ if (this.around_index >= this.jp_.around.length) {
+ return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args)
+ } else {
+ var ti = this.jp_.around[this.around_index];
+ var mobj = ti[0] || dj_global;
+ var meth = ti[1];
+ return mobj[meth].call(mobj, this)
+ }
+};
+dojo.event.MethodJoinPoint = function (obj, _319) {
+ this.object = obj || dj_global;
+ this.methodname = _319;
+ this.methodfunc = this.object[_319];
+ this.squelch = false
+};
+dojo.event.MethodJoinPoint.getForMethod = function (obj, _31b) {
+ if (!obj) {
+ obj = dj_global
+ }
+ var ofn = obj[_31b];
+ if (!ofn) {
+ ofn = obj[_31b] = function () {
+ };
+ if (!obj[_31b]) {
+ dojo.raise('Cannot set do-nothing method on that object ' + _31b)
+ }
+ } else {
+ if ((typeof ofn != 'function') && (!dojo.lang.isFunction(ofn)) && (!dojo.lang.isAlien(ofn))) {
+ return null
+ }
+ }
+ var _31d = _31b + '$joinpoint';
+ var _31e = _31b + '$joinpoint$method';
+ var _31f = obj[_31d];
+ if (!_31f) {
+ var _320 = false;
+ if (dojo.event['browser']) {
+ if ((obj['attachEvent']) || (obj['nodeType']) || (obj['addEventListener'])) {
+ _320 = true;
+ dojo.event.browser.addClobberNodeAttrs(obj, [_31d, _31e, _31b])
+ }
+ }
+ var _321 = ofn.length;
+ obj[_31e] = ofn;
+ _31f = obj[_31d] = new dojo.event.MethodJoinPoint(obj, _31e);
+ if (!_320) {
+ obj[_31b] = function () {
+ return _31f.run.apply(_31f, arguments)
+ }
+ } else {
+ obj[_31b] = function () {
+ var args = [];
+ if (!arguments.length) {
+ var evt = null;
+ try {
+ if (obj.ownerDocument) {
+ evt = obj.ownerDocument.parentWindow.event
+ } else {
+ if (obj.documentElement) {
+ evt = obj.documentElement.ownerDocument.parentWindow.event
+ } else {
+ if (obj.event) {
+ evt = obj.event
+ } else {
+ evt = window.event
+ }
+ }
+ }
+ } catch (e) {
+ evt = window.event
+ }
+ if (evt) {
+ args.push(dojo.event.browser.fixEvent(evt, this))
+ }
+ } else {
+ for (var x = 0; x < arguments.length; x++) {
+ if ((x == 0) && (dojo.event.browser.isEvent(arguments[x]))) {
+ args.push(dojo.event.browser.fixEvent(arguments[x], this))
+ } else {
+ args.push(arguments[x])
+ }
+ }
+ }
+ return _31f.run.apply(_31f, args)
+ }
+ }
+ obj[_31b].__preJoinArity = _321
+ }
+ return _31f
+};
+dojo.lang.extend(dojo.event.MethodJoinPoint, {
+ squelch: false, unintercept: function () {
+ this.object[this.methodname] = this.methodfunc;
+ this.before = [];
+ this.after = [];
+ this.around = []
+ }, disconnect: dojo.lang.forward('unintercept'), run: function () {
+ var obj = this.object || dj_global;
+ var args = arguments;
+ var _327 = [];
+ for (var x = 0; x < args.length; x++) {
+ _327[x] = args[x]
+ }
+ var _329 = function (marr) {
+ if (!marr) {
+ dojo.debug('Null argument to unrollAdvice()');
+ return
+ }
+ var _32b = marr[0] || dj_global;
+ var _32c = marr[1];
+ if (!_32b[_32c]) {
+ dojo.raise('function "' + _32c + '" does not exist on "' + _32b + '"')
+ }
+ var _32d = marr[2] || dj_global;
+ var _32e = marr[3];
+ var msg = marr[6];
+ var _330 = marr[7];
+ if (_330 > -1) {
+ if (_330 == 0) {
+ return
+ }
+ marr[7]--
+ }
+ var _331;
+ var to = {
+ args: [], jp_: this, object: obj, proceed: function () {
+ return _32b[_32c].apply(_32b, to.args)
+ }
+ };
+ to.args = _327;
+ var _333 = parseInt(marr[4]);
+ var _334 = ((!isNaN(_333)) && (marr[4] !== null) && (typeof marr[4] != 'undefined'));
+ if (marr[5]) {
+ var rate = parseInt(marr[5]);
+ var cur = new Date();
+ var _337 = false;
+ if ((marr['last']) && ((cur - marr.last) <= rate)) {
+ if (dojo.event._canTimeout) {
+ if (marr['delayTimer']) {
+ clearTimeout(marr.delayTimer)
+ }
+ var tod = parseInt(rate * 2);
+ var mcpy = dojo.lang.shallowCopy(marr);
+ marr.delayTimer = setTimeout(function () {
+ mcpy[5] = 0;
+ _329(mcpy)
+ }, tod)
+ }
+ return
+ } else {
+ marr.last = cur
+ }
+ }
+ if (_32e) {
+ _32d[_32e].call(_32d, to)
+ } else {
+ if ((_334) && ((dojo.render.html) || (dojo.render.svg))) {
+ dj_global['setTimeout'](function () {
+ if (msg) {
+ _32b[_32c].call(_32b, to)
+ } else {
+ _32b[_32c].apply(_32b, args)
+ }
+ }, _333)
+ } else {
+ if (msg) {
+ _32b[_32c].call(_32b, to)
+ } else {
+ _32b[_32c].apply(_32b, args)
+ }
+ }
+ }
+ };
+ var _33a = function () {
+ if (this.squelch) {
+ try {
+ return _329.apply(this, arguments)
+ } catch (e) {
+ dojo.debug(e)
+ }
+ } else {
+ return _329.apply(this, arguments)
+ }
+ };
+ if ((this['before']) && (this.before.length > 0)) {
+ dojo.lang.forEach(this.before.concat([]), _33a)
+ }
+ var _33b;
+ try {
+ if ((this['around']) && (this.around.length > 0)) {
+ var mi = new dojo.event.MethodInvocation(this, obj, args);
+ _33b = mi.proceed()
+ } else {
+ if (this.methodfunc) {
+ _33b = this.object[this.methodname].apply(this.object, args)
+ }
+ }
+ } catch (e) {
+ if (!this.squelch) {
+ dojo.debug(e, 'when calling', this.methodname, 'on', this.object, 'with arguments', args);
+ dojo.raise(e)
+ }
+ }
+ if ((this['after']) && (this.after.length > 0)) {
+ dojo.lang.forEach(this.after.concat([]), _33a)
+ }
+ return (this.methodfunc) ? _33b : null
+ }, getArr: function (kind) {
+ var type = 'after';
+ if ((typeof kind == 'string') && (kind.indexOf('before') != -1)) {
+ type = 'before'
+ } else {
+ if (kind == 'around') {
+ type = 'around'
+ }
+ }
+ if (!this[type]) {
+ this[type] = []
+ }
+ return this[type]
+ }, kwAddAdvice: function (args) {
+ this.addAdvice(args['adviceObj'], args['adviceFunc'], args['aroundObj'], args['aroundFunc'], args['adviceType'], args['precedence'], args['once'], args['delay'], args['rate'], args['adviceMsg'], args['maxCalls'])
+ }, addAdvice: function (_340, _341, _342, _343, _344, _345, once, _347, rate, _349, _34a) {
+ var arr = this.getArr(_344);
+ if (!arr) {
+ dojo.raise('bad this: ' + this)
+ }
+ var ao = [_340, _341, _342, _343, _347, rate, _349, _34a];
+ if (once) {
+ if (this.hasAdvice(_340, _341, _344, arr) >= 0) {
+ return
+ }
+ }
+ if (_345 == 'first') {
+ arr.unshift(ao)
+ } else {
+ arr.push(ao)
+ }
+ }, hasAdvice: function (_34d, _34e, _34f, arr) {
+ if (!arr) {
+ arr = this.getArr(_34f)
+ }
+ var ind = -1;
+ for (var x = 0; x < arr.length; x++) {
+ var aao = (typeof _34e == 'object') ? (String(_34e)).toString() : _34e;
+ var a1o = (typeof arr[x][1] == 'object') ? (String(arr[x][1])).toString() : arr[x][1];
+ if ((arr[x][0] == _34d) && (a1o == aao)) {
+ ind = x
+ }
+ }
+ return ind
+ }, removeAdvice: function (_355, _356, _357, once) {
+ var arr = this.getArr(_357);
+ var ind = this.hasAdvice(_355, _356, _357, arr);
+ if (ind == -1) {
+ return false
+ }
+ while (ind != -1) {
+ arr.splice(ind, 1);
+ if (once) {
+ break
+ }
+ ind = this.hasAdvice(_355, _356, _357, arr)
+ }
+ return true
+ }
+});
+dojo.provide('dojo.event.topic');
+dojo.event.topic = new function () {
+ this.topics = {};
+ this.getTopic = function (_35b) {
+ if (!this.topics[_35b]) {
+ this.topics[_35b] = new this.TopicImpl(_35b)
+ }
+ return this.topics[_35b]
+ };
+ this.registerPublisher = function (_35c, obj, _35e) {
+ var _35c = this.getTopic(_35c);
+ _35c.registerPublisher(obj, _35e)
+ };
+ this.subscribe = function (_35f, obj, _361) {
+ var _35f = this.getTopic(_35f);
+ _35f.subscribe(obj, _361)
+ };
+ this.unsubscribe = function (_362, obj, _364) {
+ var _362 = this.getTopic(_362);
+ _362.unsubscribe(obj, _364)
+ };
+ this.destroy = function (_365) {
+ this.getTopic(_365).destroy();
+ delete this.topics[_365]
+ };
+ this.publishApply = function (_366, args) {
+ var _366 = this.getTopic(_366);
+ _366.sendMessage.apply(_366, args)
+ };
+ this.publish = function (_368, _369) {
+ var _368 = this.getTopic(_368);
+ var args = [];
+ for (var x = 1; x < arguments.length; x++) {
+ args.push(arguments[x])
+ }
+ _368.sendMessage.apply(_368, args)
+ }
+};
+;dojo.event.topic.TopicImpl = function (_36c) {
+ this.topicName = _36c;
+ this.subscribe = function (_36d, _36e) {
+ var tf = _36e || _36d;
+ var to = (!_36e) ? dj_global : _36d;
+ return dojo.event.kwConnect({srcObj: this, srcFunc: 'sendMessage', adviceObj: to, adviceFunc: tf})
+ };
+ this.unsubscribe = function (_371, _372) {
+ var tf = (!_372) ? _371 : _372;
+ var to = (!_372) ? null : _371;
+ return dojo.event.kwDisconnect({srcObj: this, srcFunc: 'sendMessage', adviceObj: to, adviceFunc: tf})
+ };
+ this._getJoinPoint = function () {
+ return dojo.event.MethodJoinPoint.getForMethod(this, 'sendMessage')
+ };
+ this.setSquelch = function (_375) {
+ this._getJoinPoint().squelch = _375
+ };
+ this.destroy = function () {
+ this._getJoinPoint().disconnect()
+ };
+ this.registerPublisher = function (_376, _377) {
+ dojo.event.connect(_376, _377, this, 'sendMessage')
+ };
+ this.sendMessage = function (_378) {
+ }
+};
+dojo.provide('dojo.event.browser');
+dojo._ie_clobber = new function () {
+ this.clobberNodes = [];
+
+ function nukeProp(node, prop) {
+ try {
+ node[prop] = null
+ } catch (e) {
+ }
+ try {
+ delete node[prop]
+ } catch (e) {
+ }
+ try {
+ node.removeAttribute(prop)
+ } catch (e) {
+ }
+ }
+
+ this.clobber = function (_37b) {
+ var na;
+ var tna;
+ if (_37b) {
+ tna = _37b.all || _37b.getElementsByTagName('*');
+ na = [_37b];
+ for (var x = 0; x < tna.length; x++) {
+ if (tna[x]['__doClobber__']) {
+ na.push(tna[x])
+ }
+ }
+ } else {
+ try {
+ window.onload = null
+ } catch (e) {
+ }
+ na = (this.clobberNodes.length) ? this.clobberNodes : document.all
+ }
+ tna = null;
+ var _37f = {};
+ for (var i = na.length - 1; i >= 0; i = i - 1) {
+ var el = na[i];
+ try {
+ if (el && el['__clobberAttrs__']) {
+ for (var j = 0; j < el.__clobberAttrs__.length; j++) {
+ nukeProp(el, el.__clobberAttrs__[j])
+ }
+ nukeProp(el, '__clobberAttrs__');
+ nukeProp(el, '__doClobber__')
+ }
+ } catch (e) {
+ }
+ }
+ na = null
+ }
+};
+;
+if (dojo.render.html.ie) {
+ dojo.addOnUnload(function () {
+ dojo._ie_clobber.clobber();
+ try {
+ if ((dojo['widget']) && (dojo.widget['manager'])) {
+ dojo.widget.manager.destroyAll()
+ }
+ } catch (e) {
+ }
+ if (dojo.widget) {
+ for (var name in dojo.widget._templateCache) {
+ if (dojo.widget._templateCache[name].node) {
+ dojo.dom.destroyNode(dojo.widget._templateCache[name].node);
+ dojo.widget._templateCache[name].node = null;
+ delete dojo.widget._templateCache[name].node
+ }
+ }
+ }
+ try {
+ window.onload = null
+ } catch (e) {
+ }
+ try {
+ window.onunload = null
+ } catch (e) {
+ }
+ dojo._ie_clobber.clobberNodes = []
+ })
+}
+dojo.event.browser = new function () {
+ var _384 = 0;
+ this.normalizedEventName = function (_385) {
+ switch (_385) {
+ case 'CheckboxStateChange':
+ case 'DOMAttrModified':
+ case 'DOMMenuItemActive':
+ case 'DOMMenuItemInactive':
+ case 'DOMMouseScroll':
+ case 'DOMNodeInserted':
+ case 'DOMNodeRemoved':
+ case 'RadioStateChange':
+ return _385;
+ break;
+ default:
+ var lcn = _385.toLowerCase();
+ return (lcn.indexOf('on') == 0) ? lcn.substr(2) : lcn;
+ break
+ }
+ };
+ this.clean = function (node) {
+ if (dojo.render.html.ie) {
+ dojo._ie_clobber.clobber(node)
+ }
+ };
+ this.addClobberNode = function (node) {
+ if (!dojo.render.html.ie) {
+ return
+ }
+ if (!node['__doClobber__']) {
+ node.__doClobber__ = true;
+ dojo._ie_clobber.clobberNodes.push(node);
+ node.__clobberAttrs__ = []
+ }
+ };
+ this.addClobberNodeAttrs = function (node, _38a) {
+ if (!dojo.render.html.ie) {
+ return
+ }
+ this.addClobberNode(node);
+ for (var x = 0; x < _38a.length; x++) {
+ node.__clobberAttrs__.push(_38a[x])
+ }
+ };
+ this.removeListener = function (node, _38d, fp, _38f) {
+ if (!_38f) {
+ var _38f = false
+ }
+ _38d = dojo.event.browser.normalizedEventName(_38d);
+ if (_38d == 'key') {
+ if (dojo.render.html.ie) {
+ this.removeListener(node, 'onkeydown', fp, _38f)
+ }
+ _38d = 'keypress'
+ }
+ if (node.removeEventListener) {
+ node.removeEventListener(_38d, fp, _38f)
+ }
+ };
+ this.addListener = function (node, _391, fp, _393, _394) {
+ if (!node) {
+ return
+ }
+ if (!_393) {
+ var _393 = false
+ }
+ _391 = dojo.event.browser.normalizedEventName(_391);
+ if (_391 == 'key') {
+ if (dojo.render.html.ie) {
+ this.addListener(node, 'onkeydown', fp, _393, _394)
+ }
+ _391 = 'keypress'
+ }
+ if (!_394) {
+ var _395 = function (evt) {
+ if (!evt) {
+ evt = window.event
+ }
+ var ret = fp(dojo.event.browser.fixEvent(evt, this));
+ if (_393) {
+ dojo.event.browser.stopEvent(evt)
+ }
+ return ret
+ }
+ } else {
+ _395 = fp
+ }
+ if (node.addEventListener) {
+ node.addEventListener(_391, _395, _393);
+ return _395
+ } else {
+ _391 = 'on' + _391;
+ if (typeof node[_391] == 'function') {
+ var _398 = node[_391];
+ node[_391] = function (e) {
+ _398(e);
+ return _395(e)
+ }
+ } else {
+ node[_391] = _395
+ }
+ if (dojo.render.html.ie) {
+ this.addClobberNodeAttrs(node, [_391])
+ }
+ return _395
+ }
+ };
+ this.isEvent = function (obj) {
+ return (typeof obj != 'undefined') && (obj) && (typeof Event != 'undefined') && (obj.eventPhase)
+ };
+ this.currentEvent = null;
+ this.callListener = function (_39b, _39c) {
+ if (typeof _39b != 'function') {
+ dojo.raise('listener not a function: ' + _39b)
+ }
+ dojo.event.browser.currentEvent.currentTarget = _39c;
+ return _39b.call(_39c, dojo.event.browser.currentEvent)
+ };
+ this._stopPropagation = function () {
+ dojo.event.browser.currentEvent.cancelBubble = true
+ };
+ this._preventDefault = function () {
+ dojo.event.browser.currentEvent.returnValue = false
+ };
+ this.keys = {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_CLEAR: 12,
+ KEY_ENTER: 13,
+ KEY_SHIFT: 16,
+ KEY_CTRL: 17,
+ KEY_ALT: 18,
+ KEY_PAUSE: 19,
+ KEY_CAPS_LOCK: 20,
+ KEY_ESCAPE: 27,
+ KEY_SPACE: 32,
+ KEY_PAGE_UP: 33,
+ KEY_PAGE_DOWN: 34,
+ KEY_END: 35,
+ KEY_HOME: 36,
+ KEY_LEFT_ARROW: 37,
+ KEY_UP_ARROW: 38,
+ KEY_RIGHT_ARROW: 39,
+ KEY_DOWN_ARROW: 40,
+ KEY_INSERT: 45,
+ KEY_DELETE: 46,
+ KEY_HELP: 47,
+ KEY_LEFT_WINDOW: 91,
+ KEY_RIGHT_WINDOW: 92,
+ KEY_SELECT: 93,
+ KEY_NUMPAD_0: 96,
+ KEY_NUMPAD_1: 97,
+ KEY_NUMPAD_2: 98,
+ KEY_NUMPAD_3: 99,
+ KEY_NUMPAD_4: 100,
+ KEY_NUMPAD_5: 101,
+ KEY_NUMPAD_6: 102,
+ KEY_NUMPAD_7: 103,
+ KEY_NUMPAD_8: 104,
+ KEY_NUMPAD_9: 105,
+ KEY_NUMPAD_MULTIPLY: 106,
+ KEY_NUMPAD_PLUS: 107,
+ KEY_NUMPAD_ENTER: 108,
+ KEY_NUMPAD_MINUS: 109,
+ KEY_NUMPAD_PERIOD: 110,
+ KEY_NUMPAD_DIVIDE: 111,
+ KEY_F1: 112,
+ KEY_F2: 113,
+ KEY_F3: 114,
+ KEY_F4: 115,
+ KEY_F5: 116,
+ KEY_F6: 117,
+ KEY_F7: 118,
+ KEY_F8: 119,
+ KEY_F9: 120,
+ KEY_F10: 121,
+ KEY_F11: 122,
+ KEY_F12: 123,
+ KEY_F13: 124,
+ KEY_F14: 125,
+ KEY_F15: 126,
+ KEY_NUM_LOCK: 144,
+ KEY_SCROLL_LOCK: 145
+ };
+ this.revKeys = [];
+ for (var key in this.keys) {
+ this.revKeys[this.keys[key]] = key
+ }
+ this.fixEvent = function (evt, _39f) {
+ if (!evt) {
+ if (window['event']) {
+ evt = window.event
+ }
+ }
+ if ((evt['type']) && (evt['type'].indexOf('key') == 0)) {
+ evt.keys = this.revKeys;
+ for (var key in this.keys) {
+ evt[key] = this.keys[key]
+ }
+ if (evt['type'] == 'keydown' && dojo.render.html.ie) {
+ switch (evt.keyCode) {
+ case evt.KEY_SHIFT:
+ case evt.KEY_CTRL:
+ case evt.KEY_ALT:
+ case evt.KEY_CAPS_LOCK:
+ case evt.KEY_LEFT_WINDOW:
+ case evt.KEY_RIGHT_WINDOW:
+ case evt.KEY_SELECT:
+ case evt.KEY_NUM_LOCK:
+ case evt.KEY_SCROLL_LOCK:
+ case evt.KEY_NUMPAD_0:
+ case evt.KEY_NUMPAD_1:
+ case evt.KEY_NUMPAD_2:
+ case evt.KEY_NUMPAD_3:
+ case evt.KEY_NUMPAD_4:
+ case evt.KEY_NUMPAD_5:
+ case evt.KEY_NUMPAD_6:
+ case evt.KEY_NUMPAD_7:
+ case evt.KEY_NUMPAD_8:
+ case evt.KEY_NUMPAD_9:
+ case evt.KEY_NUMPAD_PERIOD:
+ break;
+ case evt.KEY_NUMPAD_MULTIPLY:
+ case evt.KEY_NUMPAD_PLUS:
+ case evt.KEY_NUMPAD_ENTER:
+ case evt.KEY_NUMPAD_MINUS:
+ case evt.KEY_NUMPAD_DIVIDE:
+ break;
+ case evt.KEY_PAUSE:
+ case evt.KEY_TAB:
+ case evt.KEY_BACKSPACE:
+ case evt.KEY_ENTER:
+ case evt.KEY_ESCAPE:
+ case evt.KEY_PAGE_UP:
+ case evt.KEY_PAGE_DOWN:
+ case evt.KEY_END:
+ case evt.KEY_HOME:
+ case evt.KEY_LEFT_ARROW:
+ case evt.KEY_UP_ARROW:
+ case evt.KEY_RIGHT_ARROW:
+ case evt.KEY_DOWN_ARROW:
+ case evt.KEY_INSERT:
+ case evt.KEY_DELETE:
+ case evt.KEY_F1:
+ case evt.KEY_F2:
+ case evt.KEY_F3:
+ case evt.KEY_F4:
+ case evt.KEY_F5:
+ case evt.KEY_F6:
+ case evt.KEY_F7:
+ case evt.KEY_F8:
+ case evt.KEY_F9:
+ case evt.KEY_F10:
+ case evt.KEY_F11:
+ case evt.KEY_F12:
+ case evt.KEY_F12:
+ case evt.KEY_F13:
+ case evt.KEY_F14:
+ case evt.KEY_F15:
+ case evt.KEY_CLEAR:
+ case evt.KEY_HELP:
+ evt.key = evt.keyCode;
+ break;
+ default:
+ if (evt.ctrlKey || evt.altKey) {
+ var _3a1 = evt.keyCode;
+ if (_3a1 >= 65 && _3a1 <= 90 && evt.shiftKey == false) {
+ _3a1 += 32
+ }
+ if (_3a1 >= 1 && _3a1 <= 26 && evt.ctrlKey) {
+ _3a1 += 96
+ }
+ evt.key = String.fromCharCode(_3a1)
+ }
+ }
+ } else {
+ if (evt['type'] == 'keypress') {
+ if (dojo.render.html.opera) {
+ if (evt.which == 0) {
+ evt.key = evt.keyCode
+ } else {
+ if (evt.which > 0) {
+ switch (evt.which) {
+ case evt.KEY_SHIFT:
+ case evt.KEY_CTRL:
+ case evt.KEY_ALT:
+ case evt.KEY_CAPS_LOCK:
+ case evt.KEY_NUM_LOCK:
+ case evt.KEY_SCROLL_LOCK:
+ break;
+ case evt.KEY_PAUSE:
+ case evt.KEY_TAB:
+ case evt.KEY_BACKSPACE:
+ case evt.KEY_ENTER:
+ case evt.KEY_ESCAPE:
+ evt.key = evt.which;
+ break;
+ default:
+ var _3a1 = evt.which;
+ if ((evt.ctrlKey || evt.altKey || evt.metaKey) && (evt.which
+ >= 65
+ && evt.which
+ <= 90
+ && evt.shiftKey
+ == false)) {
+ _3a1 += 32
+ }
+ evt.key = String.fromCharCode(_3a1)
+ }
+ }
+ }
+ } else {
+ if (dojo.render.html.ie) {
+ if (!evt.ctrlKey && !evt.altKey && evt.keyCode >= evt.KEY_SPACE) {
+ evt.key = String.fromCharCode(evt.keyCode)
+ }
+ } else {
+ if (dojo.render.html.safari) {
+ switch (evt.keyCode) {
+ case 25:
+ evt.key = evt.KEY_TAB;
+ evt.shift = true;
+ break;
+ case 63232:
+ evt.key = evt.KEY_UP_ARROW;
+ break;
+ case 63233:
+ evt.key = evt.KEY_DOWN_ARROW;
+ break;
+ case 63234:
+ evt.key = evt.KEY_LEFT_ARROW;
+ break;
+ case 63235:
+ evt.key = evt.KEY_RIGHT_ARROW;
+ break;
+ case 63236:
+ evt.key = evt.KEY_F1;
+ break;
+ case 63237:
+ evt.key = evt.KEY_F2;
+ break;
+ case 63238:
+ evt.key = evt.KEY_F3;
+ break;
+ case 63239:
+ evt.key = evt.KEY_F4;
+ break;
+ case 63240:
+ evt.key = evt.KEY_F5;
+ break;
+ case 63241:
+ evt.key = evt.KEY_F6;
+ break;
+ case 63242:
+ evt.key = evt.KEY_F7;
+ break;
+ case 63243:
+ evt.key = evt.KEY_F8;
+ break;
+ case 63244:
+ evt.key = evt.KEY_F9;
+ break;
+ case 63245:
+ evt.key = evt.KEY_F10;
+ break;
+ case 63246:
+ evt.key = evt.KEY_F11;
+ break;
+ case 63247:
+ evt.key = evt.KEY_F12;
+ break;
+ case 63250:
+ evt.key = evt.KEY_PAUSE;
+ break;
+ case 63272:
+ evt.key = evt.KEY_DELETE;
+ break;
+ case 63273:
+ evt.key = evt.KEY_HOME;
+ break;
+ case 63275:
+ evt.key = evt.KEY_END;
+ break;
+ case 63276:
+ evt.key = evt.KEY_PAGE_UP;
+ break;
+ case 63277:
+ evt.key = evt.KEY_PAGE_DOWN;
+ break;
+ case 63302:
+ evt.key = evt.KEY_INSERT;
+ break;
+ case 63248:
+ case 63249:
+ case 63289:
+ break;
+ default:
+ evt.key = evt.charCode >= evt.KEY_SPACE ? String.fromCharCode(evt.charCode) : evt.keyCode
+ }
+ } else {
+ evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode
+ }
+ }
+ }
+ }
+ }
+ }
+ if (dojo.render.html.ie) {
+ if (!evt.target) {
+ evt.target = evt.srcElement
+ }
+ if (!evt.currentTarget) {
+ evt.currentTarget = (_39f ? _39f : evt.srcElement)
+ }
+ if (!evt.layerX) {
+ evt.layerX = evt.offsetX
+ }
+ if (!evt.layerY) {
+ evt.layerY = evt.offsetY
+ }
+ var doc = (evt.srcElement && evt.srcElement.ownerDocument) ? evt.srcElement.ownerDocument : document;
+ var _3a3 = ((dojo.render.html.ie55) || (doc['compatMode'] == 'BackCompat')) ? doc.body : doc.documentElement;
+ if (!evt.pageX) {
+ evt.pageX = evt.clientX + (_3a3.scrollLeft || 0)
+ }
+ if (!evt.pageY) {
+ evt.pageY = evt.clientY + (_3a3.scrollTop || 0)
+ }
+ if (evt.type == 'mouseover') {
+ evt.relatedTarget = evt.fromElement
+ }
+ if (evt.type == 'mouseout') {
+ evt.relatedTarget = evt.toElement
+ }
+ this.currentEvent = evt;
+ evt.callListener = this.callListener;
+ evt.stopPropagation = this._stopPropagation;
+ evt.preventDefault = this._preventDefault
+ }
+ return evt
+ };
+ this.stopEvent = function (evt) {
+ if (window.event) {
+ evt.cancelBubble = true;
+ evt.returnValue = false
+ } else {
+ evt.preventDefault();
+ evt.stopPropagation()
+ }
+ }
+};
+;dojo.kwCompoundRequire({
+ common: ['dojo.event.common', 'dojo.event.topic'], browser: ['dojo.event.browser'], dashboard: ['dojo.event.browser']
+});
+dojo.provide('dojo.event.*');
+dojo.provide('dojo.gfx.color');
+dojo.gfx.color.Color = function (r, g, b, a) {
+ if (dojo.lang.isArray(r)) {
+ this.r = r[0];
+ this.g = r[1];
+ this.b = r[2];
+ this.a = r[3] || 1
+ } else {
+ if (dojo.lang.isString(r)) {
+ var rgb = dojo.gfx.color.extractRGB(r);
+ this.r = rgb[0];
+ this.g = rgb[1];
+ this.b = rgb[2];
+ this.a = g || 1
+ } else {
+ if (r instanceof dojo.gfx.color.Color) {
+ this.r = r.r;
+ this.b = r.b;
+ this.g = r.g;
+ this.a = r.a
+ } else {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a
+ }
+ }
+ }
+};
+dojo.gfx.color.Color.fromArray = function (arr) {
+ return new dojo.gfx.color.Color(arr[0], arr[1], arr[2], arr[3])
+};
+dojo.extend(dojo.gfx.color.Color, {
+ toRgb: function (_3ab) {
+ if (_3ab) {
+ return this.toRgba()
+ } else {
+ return [this.r, this.g, this.b]
+ }
+ }, toRgba: function () {
+ return [this.r, this.g, this.b, this.a]
+ }, toHex: function () {
+ return dojo.gfx.color.rgb2hex(this.toRgb())
+ }, toCss: function () {
+ return 'rgb(' + this.toRgb().join() + ')'
+ }, toString: function () {
+ return this.toHex()
+ }, blend: function (_3ac, _3ad) {
+ var rgb = null;
+ if (dojo.lang.isArray(_3ac)) {
+ rgb = _3ac
+ } else {
+ if (_3ac instanceof dojo.gfx.color.Color) {
+ rgb = _3ac.toRgb()
+ } else {
+ rgb = new dojo.gfx.color.Color(_3ac).toRgb()
+ }
+ }
+ return dojo.gfx.color.blend(this.toRgb(), rgb, _3ad)
+ }
+});
+dojo.gfx.color.named = {
+ white: [255, 255, 255],
+ black: [0, 0, 0],
+ red: [255, 0, 0],
+ green: [0, 255, 0],
+ lime: [0, 255, 0],
+ blue: [0, 0, 255],
+ navy: [0, 0, 128],
+ gray: [128, 128, 128],
+ silver: [192, 192, 192]
+};
+dojo.gfx.color.blend = function (a, b, _3b1) {
+ if (typeof a == 'string') {
+ return dojo.gfx.color.blendHex(a, b, _3b1)
+ }
+ if (!_3b1) {
+ _3b1 = 0
+ }
+ _3b1 = Math.min(Math.max(-1, _3b1), 1);
+ _3b1 = ((_3b1 + 1) / 2);
+ var c = [];
+ for (var x = 0; x < 3; x++) {
+ c[x] = parseInt(b[x] + ((a[x] - b[x]) * _3b1))
+ }
+ return c
+};
+dojo.gfx.color.blendHex = function (a, b, _3b6) {
+ return dojo.gfx.color.rgb2hex(dojo.gfx.color.blend(dojo.gfx.color.hex2rgb(a), dojo.gfx.color.hex2rgb(b), _3b6))
+};
+dojo.gfx.color.extractRGB = function (_3b7) {
+ var hex = '0123456789abcdef';
+ _3b7 = _3b7.toLowerCase();
+ if (_3b7.indexOf('rgb') == 0) {
+ var _3b9 = _3b7.match(/rgba*\((\d+), *(\d+), *(\d+)/i);
+ var ret = _3b9.splice(1, 3);
+ return ret
+ } else {
+ var _3bb = dojo.gfx.color.hex2rgb(_3b7);
+ if (_3bb) {
+ return _3bb
+ } else {
+ return dojo.gfx.color.named[_3b7] || [255, 255, 255]
+ }
+ }
+};
+dojo.gfx.color.hex2rgb = function (hex) {
+ var _3bd = '0123456789ABCDEF';
+ var rgb = new Array(3);
+ if (hex.indexOf('#') == 0) {
+ hex = hex.substring(1)
+ }
+ hex = hex.toUpperCase();
+ if (hex.replace(new RegExp('[' + _3bd + ']', 'g'), '') != '') {
+ return null
+ }
+ if (hex.length == 3) {
+ rgb[0] = hex.charAt(0) + hex.charAt(0);
+ rgb[1] = hex.charAt(1) + hex.charAt(1);
+ rgb[2] = hex.charAt(2) + hex.charAt(2)
+ } else {
+ rgb[0] = hex.substring(0, 2);
+ rgb[1] = hex.substring(2, 4);
+ rgb[2] = hex.substring(4)
+ }
+ for (var i = 0; i < rgb.length; i++) {
+ rgb[i] = _3bd.indexOf(rgb[i].charAt(0)) * 16 + _3bd.indexOf(rgb[i].charAt(1))
+ }
+ return rgb
+};
+dojo.gfx.color.rgb2hex = function (r, g, b) {
+ if (dojo.lang.isArray(r)) {
+ g = r[1] || 0;
+ b = r[2] || 0;
+ r = r[0] || 0
+ }
+ var ret = dojo.lang.map([r, g, b], function (x) {
+ x = Number(x);
+ var s = x.toString(16);
+ while (s.length < 2) {
+ s = '0' + s
+ }
+ return s
+ });
+ ret.unshift('#');
+ return ret.join('')
+};
+dojo.provide('dojo.lfx.Animation');
+dojo.lfx.Line = function (_3c6, end) {
+ this.start = _3c6;
+ this.end = end;
+ if (dojo.lang.isArray(_3c6)) {
+ var diff = [];
+ dojo.lang.forEach(this.start, function (s, i) {
+ diff[i] = this.end[i] - s
+ }, this);
+ this.getValue = function (n) {
+ var res = [];
+ dojo.lang.forEach(this.start, function (s, i) {
+ res[i] = (diff[i] * n) + s
+ }, this);
+ return res
+ }
+ } else {
+ var diff = end - _3c6;
+ this.getValue = function (n) {
+ return (diff * n) + this.start
+ }
+ }
+};
+if ((dojo.render.html.khtml) && (!dojo.render.html.safari)) {
+ dojo.lfx.easeDefault = function (n) {
+ return (parseFloat('0.5') + ((Math.sin((n + parseFloat('1.5')) * Math.PI)) / 2))
+ }
+} else {
+ dojo.lfx.easeDefault = function (n) {
+ return (0.5 + ((Math.sin((n + 1.5) * Math.PI)) / 2))
+ }
+}
+dojo.lfx.easeIn = function (n) {
+ return Math.pow(n, 3)
+};
+dojo.lfx.easeOut = function (n) {
+ return (1 - Math.pow(1 - n, 3))
+};
+dojo.lfx.easeInOut = function (n) {
+ return ((3 * Math.pow(n, 2)) - (2 * Math.pow(n, 3)))
+};
+dojo.lfx.IAnimation = function () {
+};
+dojo.lang.extend(dojo.lfx.IAnimation, {
+ curve: null,
+ duration: 1000,
+ easing: null,
+ repeatCount: 0,
+ rate: 10,
+ handler: null,
+ beforeBegin: null,
+ onBegin: null,
+ onAnimate: null,
+ onEnd: null,
+ onPlay: null,
+ onPause: null,
+ onStop: null,
+ play: null,
+ pause: null,
+ stop: null,
+ connect: function (evt, _3d6, _3d7) {
+ if (!_3d7) {
+ _3d7 = _3d6;
+ _3d6 = this
+ }
+ _3d7 = dojo.lang.hitch(_3d6, _3d7);
+ var _3d8 = this[evt] || function () {
+ };
+ this[evt] = function () {
+ var ret = _3d8.apply(this, arguments);
+ _3d7.apply(this, arguments);
+ return ret
+ };
+ return this
+ },
+ fire: function (evt, args) {
+ if (this[evt]) {
+ this[evt].apply(this, (args || []))
+ }
+ return this
+ },
+ repeat: function (_3dc) {
+ this.repeatCount = _3dc;
+ return this
+ },
+ _active: false,
+ _paused: false
+});
+dojo.lfx.Animation = function (_3dd, _3de, _3df, _3e0, _3e1, rate) {
+ dojo.lfx.IAnimation.call(this);
+ if (dojo.lang.isNumber(_3dd) || (!_3dd && _3de.getValue)) {
+ rate = _3e1;
+ _3e1 = _3e0;
+ _3e0 = _3df;
+ _3df = _3de;
+ _3de = _3dd;
+ _3dd = null
+ } else {
+ if (_3dd.getValue || dojo.lang.isArray(_3dd)) {
+ rate = _3e0;
+ _3e1 = _3df;
+ _3e0 = _3de;
+ _3df = _3dd;
+ _3de = null;
+ _3dd = null
+ }
+ }
+ if (dojo.lang.isArray(_3df)) {
+ this.curve = new dojo.lfx.Line(_3df[0], _3df[1])
+ } else {
+ this.curve = _3df
+ }
+ if (_3de != null && _3de > 0) {
+ this.duration = _3de
+ }
+ if (_3e1) {
+ this.repeatCount = _3e1
+ }
+ if (rate) {
+ this.rate = rate
+ }
+ if (_3dd) {
+ dojo.lang.forEach(['handler', 'beforeBegin', 'onBegin', 'onEnd', 'onPlay', 'onStop', 'onAnimate'], function (item) {
+ if (_3dd[item]) {
+ this.connect(item, _3dd[item])
+ }
+ }, this)
+ }
+ if (_3e0 && dojo.lang.isFunction(_3e0)) {
+ this.easing = _3e0
+ }
+};
+dojo.inherits(dojo.lfx.Animation, dojo.lfx.IAnimation);
+dojo.lang.extend(dojo.lfx.Animation, {
+ _startTime: null, _endTime: null, _timer: null, _percent: 0, _startRepeatCount: 0, play: function (_3e4, _3e5) {
+ if (_3e5) {
+ clearTimeout(this._timer);
+ this._active = false;
+ this._paused = false;
+ this._percent = 0
+ } else {
+ if (this._active && !this._paused) {
+ return this
+ }
+ }
+ this.fire('handler', ['beforeBegin']);
+ this.fire('beforeBegin');
+ if (_3e4 > 0) {
+ setTimeout(dojo.lang.hitch(this, function () {
+ this.play(null, _3e5)
+ }), _3e4);
+ return this
+ }
+ this._startTime = new Date().valueOf();
+ if (this._paused) {
+ this._startTime -= (this.duration * this._percent / 100)
+ }
+ this._endTime = this._startTime + this.duration;
+ this._active = true;
+ this._paused = false;
+ var step = this._percent / 100;
+ var _3e7 = this.curve.getValue(step);
+ if (this._percent == 0) {
+ if (!this._startRepeatCount) {
+ this._startRepeatCount = this.repeatCount
+ }
+ this.fire('handler', ['begin', _3e7]);
+ this.fire('onBegin', [_3e7])
+ }
+ this.fire('handler', ['play', _3e7]);
+ this.fire('onPlay', [_3e7]);
+ this._cycle();
+ return this
+ }, pause: function () {
+ clearTimeout(this._timer);
+ if (!this._active) {
+ return this
+ }
+ this._paused = true;
+ var _3e8 = this.curve.getValue(this._percent / 100);
+ this.fire('handler', ['pause', _3e8]);
+ this.fire('onPause', [_3e8]);
+ return this
+ }, gotoPercent: function (pct, _3ea) {
+ clearTimeout(this._timer);
+ this._active = true;
+ this._paused = true;
+ this._percent = pct;
+ if (_3ea) {
+ this.play()
+ }
+ return this
+ }, stop: function (_3eb) {
+ clearTimeout(this._timer);
+ var step = this._percent / 100;
+ if (_3eb) {
+ step = 1
+ }
+ var _3ed = this.curve.getValue(step);
+ this.fire('handler', ['stop', _3ed]);
+ this.fire('onStop', [_3ed]);
+ this._active = false;
+ this._paused = false;
+ return this
+ }, status: function () {
+ if (this._active) {
+ return this._paused ? 'paused' : 'playing'
+ } else {
+ return 'stopped'
+ }
+ return this
+ }, _cycle: function () {
+ clearTimeout(this._timer);
+ if (this._active) {
+ var curr = new Date().valueOf();
+ var step = (curr - this._startTime) / (this._endTime - this._startTime);
+ if (step >= 1) {
+ step = 1;
+ this._percent = 100
+ } else {
+ this._percent = step * 100
+ }
+ if ((this.easing) && (dojo.lang.isFunction(this.easing))) {
+ step = this.easing(step)
+ }
+ var _3f0 = this.curve.getValue(step);
+ this.fire('handler', ['animate', _3f0]);
+ this.fire('onAnimate', [_3f0]);
+ if (step < 1) {
+ this._timer = setTimeout(dojo.lang.hitch(this, '_cycle'), this.rate)
+ } else {
+ this._active = false;
+ this.fire('handler', ['end']);
+ this.fire('onEnd');
+ if (this.repeatCount > 0) {
+ this.repeatCount--;
+ this.play(null, true)
+ } else {
+ if (this.repeatCount == -1) {
+ this.play(null, true)
+ } else {
+ if (this._startRepeatCount) {
+ this.repeatCount = this._startRepeatCount;
+ this._startRepeatCount = 0
+ }
+ }
+ }
+ }
+ }
+ return this
+ }
+});
+dojo.lfx.Combine = function (_3f1) {
+ dojo.lfx.IAnimation.call(this);
+ this._anims = [];
+ this._animsEnded = 0;
+ var _3f2 = arguments;
+ if (_3f2.length == 1 && (dojo.lang.isArray(_3f2[0]) || dojo.lang.isArrayLike(_3f2[0]))) {
+ _3f2 = _3f2[0]
+ }
+ dojo.lang.forEach(_3f2, function (anim) {
+ this._anims.push(anim);
+ anim.connect('onEnd', dojo.lang.hitch(this, '_onAnimsEnded'))
+ }, this)
+};
+dojo.inherits(dojo.lfx.Combine, dojo.lfx.IAnimation);
+dojo.lang.extend(dojo.lfx.Combine, {
+ _animsEnded: 0, play: function (_3f4, _3f5) {
+ if (!this._anims.length) {
+ return this
+ }
+ this.fire('beforeBegin');
+ if (_3f4 > 0) {
+ setTimeout(dojo.lang.hitch(this, function () {
+ this.play(null, _3f5)
+ }), _3f4);
+ return this
+ }
+ if (_3f5 || this._anims[0].percent == 0) {
+ this.fire('onBegin')
+ }
+ this.fire('onPlay');
+ this._animsCall('play', null, _3f5);
+ return this
+ }, pause: function () {
+ this.fire('onPause');
+ this._animsCall('pause');
+ return this
+ }, stop: function (_3f6) {
+ this.fire('onStop');
+ this._animsCall('stop', _3f6);
+ return this
+ }, _onAnimsEnded: function () {
+ this._animsEnded++;
+ if (this._animsEnded >= this._anims.length) {
+ this.fire('onEnd')
+ }
+ return this
+ }, _animsCall: function (_3f7) {
+ var args = [];
+ if (arguments.length > 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args.push(arguments[i])
+ }
+ }
+ var _3fa = this;
+ dojo.lang.forEach(this._anims, function (anim) {
+ anim[_3f7](args)
+ }, _3fa);
+ return this
+ }
+});
+dojo.lfx.Chain = function (_3fc) {
+ dojo.lfx.IAnimation.call(this);
+ this._anims = [];
+ this._currAnim = -1;
+ var _3fd = arguments;
+ if (_3fd.length == 1 && (dojo.lang.isArray(_3fd[0]) || dojo.lang.isArrayLike(_3fd[0]))) {
+ _3fd = _3fd[0]
+ }
+ var _3fe = this;
+ dojo.lang.forEach(_3fd, function (anim, i, _401) {
+ this._anims.push(anim);
+ if (i < _401.length - 1) {
+ anim.connect('onEnd', dojo.lang.hitch(this, '_playNext'))
+ } else {
+ anim.connect('onEnd', dojo.lang.hitch(this, function () {
+ this.fire('onEnd')
+ }))
+ }
+ }, this)
+};
+dojo.inherits(dojo.lfx.Chain, dojo.lfx.IAnimation);
+dojo.lang.extend(dojo.lfx.Chain, {
+ _currAnim: -1, play: function (_402, _403) {
+ if (!this._anims.length) {
+ return this
+ }
+ if (_403 || !this._anims[this._currAnim]) {
+ this._currAnim = 0
+ }
+ var _404 = this._anims[this._currAnim];
+ this.fire('beforeBegin');
+ if (_402 > 0) {
+ setTimeout(dojo.lang.hitch(this, function () {
+ this.play(null, _403)
+ }), _402);
+ return this
+ }
+ if (_404) {
+ if (this._currAnim == 0) {
+ this.fire('handler', ['begin', this._currAnim]);
+ this.fire('onBegin', [this._currAnim])
+ }
+ this.fire('onPlay', [this._currAnim]);
+ _404.play(null, _403)
+ }
+ return this
+ }, pause: function () {
+ if (this._anims[this._currAnim]) {
+ this._anims[this._currAnim].pause();
+ this.fire('onPause', [this._currAnim])
+ }
+ return this
+ }, playPause: function () {
+ if (this._anims.length == 0) {
+ return this
+ }
+ if (this._currAnim == -1) {
+ this._currAnim = 0
+ }
+ var _405 = this._anims[this._currAnim];
+ if (_405) {
+ if (!_405._active || _405._paused) {
+ this.play()
+ } else {
+ this.pause()
+ }
+ }
+ return this
+ }, stop: function () {
+ var _406 = this._anims[this._currAnim];
+ if (_406) {
+ _406.stop();
+ this.fire('onStop', [this._currAnim])
+ }
+ return _406
+ }, _playNext: function () {
+ if (this._currAnim == -1 || this._anims.length == 0) {
+ return this
+ }
+ this._currAnim++;
+ if (this._anims[this._currAnim]) {
+ this._anims[this._currAnim].play(null, true)
+ }
+ return this
+ }
+});
+dojo.lfx.combine = function (_407) {
+ var _408 = arguments;
+ if (dojo.lang.isArray(arguments[0])) {
+ _408 = arguments[0]
+ }
+ if (_408.length == 1) {
+ return _408[0]
+ }
+ return new dojo.lfx.Combine(_408)
+};
+dojo.lfx.chain = function (_409) {
+ var _40a = arguments;
+ if (dojo.lang.isArray(arguments[0])) {
+ _40a = arguments[0]
+ }
+ if (_40a.length == 1) {
+ return _40a[0]
+ }
+ return new dojo.lfx.Chain(_40a)
+};
+dojo.provide('dojo.html.common');
+dojo.lang.mixin(dojo.html, dojo.dom);
+dojo.html.body = function () {
+ dojo.deprecated('dojo.html.body() moved to dojo.body()', '0.5');
+ return dojo.body()
+};
+dojo.html.getEventTarget = function (evt) {
+ if (!evt) {
+ evt = dojo.global().event || {}
+ }
+ var t = (evt.srcElement ? evt.srcElement : (evt.target ? evt.target : null));
+ while ((t) && (t.nodeType != 1)) {
+ t = t.parentNode
+ }
+ return t
+};
+dojo.html.getViewport = function () {
+ var _40d = dojo.global();
+ var _40e = dojo.doc();
+ var w = 0;
+ var h = 0;
+ if (dojo.render.html.mozilla) {
+ w = _40e.documentElement.clientWidth;
+ h = _40d.innerHeight
+ } else {
+ if (!dojo.render.html.opera && _40d.innerWidth) {
+ w = _40d.innerWidth;
+ h = _40d.innerHeight
+ } else {
+ if (!dojo.render.html.opera && dojo.exists(_40e, 'documentElement.clientWidth')) {
+ var w2 = _40e.documentElement.clientWidth;
+ if (!w || w2 && w2 < w) {
+ w = w2
+ }
+ h = _40e.documentElement.clientHeight
+ } else {
+ if (dojo.body().clientWidth) {
+ w = dojo.body().clientWidth;
+ h = dojo.body().clientHeight
+ }
+ }
+ }
+ }
+ return {width: w, height: h}
+};
+dojo.html.getScroll = function () {
+ var _412 = dojo.global();
+ var _413 = dojo.doc();
+ var top = _412.pageYOffset || _413.documentElement.scrollTop || dojo.body().scrollTop || 0;
+ var left = _412.pageXOffset || _413.documentElement.scrollLeft || dojo.body().scrollLeft || 0;
+ return {top: top, left: left, offset: {x: left, y: top}}
+};
+dojo.html.getParentByType = function (node, type) {
+ var _418 = dojo.doc();
+ var _419 = dojo.byId(node);
+ type = type.toLowerCase();
+ while ((_419) && (_419.nodeName.toLowerCase() != type)) {
+ if (_419 == (_418['body'] || _418['documentElement'])) {
+ return null
+ }
+ _419 = _419.parentNode
+ }
+ return _419
+};
+dojo.html.getAttribute = function (node, attr) {
+ node = dojo.byId(node);
+ if ((!node) || (!node.getAttribute)) {
+ return null
+ }
+ var ta = typeof attr == 'string' ? attr : String(attr);
+ var v = node.getAttribute(ta.toUpperCase());
+ if ((v) && (typeof v == 'string') && (v != '')) {
+ return v
+ }
+ if (v && v.value) {
+ return v.value
+ }
+ if ((node.getAttributeNode) && (node.getAttributeNode(ta))) {
+ return (node.getAttributeNode(ta)).value
+ } else {
+ if (node.getAttribute(ta)) {
+ return node.getAttribute(ta)
+ } else {
+ if (node.getAttribute(ta.toLowerCase())) {
+ return node.getAttribute(ta.toLowerCase())
+ }
+ }
+ }
+ return null
+};
+dojo.html.hasAttribute = function (node, attr) {
+ return dojo.html.getAttribute(dojo.byId(node), attr) ? true : false
+};
+dojo.html.getCursorPosition = function (e) {
+ e = e || dojo.global().event;
+ var _421 = {x: 0, y: 0};
+ if (e.pageX || e.pageY) {
+ _421.x = e.pageX;
+ _421.y = e.pageY
+ } else {
+ var de = dojo.doc().documentElement;
+ var db = dojo.body();
+ _421.x = e.clientX + ((de || db)['scrollLeft']) - ((de || db)['clientLeft']);
+ _421.y = e.clientY + ((de || db)['scrollTop']) - ((de || db)['clientTop'])
+ }
+ return _421
+};
+dojo.html.isTag = function (node) {
+ node = dojo.byId(node);
+ if (node && node.tagName) {
+ for (var i = 1; i < arguments.length; i++) {
+ if (node.tagName.toLowerCase() == String(arguments[i]).toLowerCase()) {
+ return String(arguments[i]).toLowerCase()
+ }
+ }
+ }
+ return ''
+};
+if (dojo.render.html.ie && !dojo.render.html.ie70) {
+ if (window.location.href.substr(0, 6).toLowerCase() != 'https:') {
+ (function () {
+ var _426 = dojo.doc().createElement('script');
+ _426.src = 'javascript:\'dojo.html.createExternalElement=function(doc, tag){ return doc.createElement(tag); }\'';
+ dojo.doc().getElementsByTagName('head')[0].appendChild(_426)
+ })()
+ }
+} else {
+ dojo.html.createExternalElement = function (doc, tag) {
+ return doc.createElement(tag)
+ }
+}
+dojo.html._callDeprecated = function (_429, _42a, args, _42c, _42d) {
+ dojo.deprecated('dojo.html.' + _429, 'replaced by dojo.html.' + _42a + '(' + (_42c ? 'node, {'
+ + _42c
+ + ': '
+ + _42c
+ + '}' : '') + ')' + (_42d ? '.' + _42d : ''), '0.5');
+ var _42e = [];
+ if (_42c) {
+ var _42f = {};
+ _42f[_42c] = args[1];
+ _42e.push(args[0]);
+ _42e.push(_42f)
+ } else {
+ _42e = args
+ }
+ var ret = dojo.html[_42a].apply(dojo.html, args);
+ if (_42d) {
+ return ret[_42d]
+ } else {
+ return ret
+ }
+};
+dojo.html.getViewportWidth = function () {
+ return dojo.html._callDeprecated('getViewportWidth', 'getViewport', arguments, null, 'width')
+};
+dojo.html.getViewportHeight = function () {
+ return dojo.html._callDeprecated('getViewportHeight', 'getViewport', arguments, null, 'height')
+};
+dojo.html.getViewportSize = function () {
+ return dojo.html._callDeprecated('getViewportSize', 'getViewport', arguments)
+};
+dojo.html.getScrollTop = function () {
+ return dojo.html._callDeprecated('getScrollTop', 'getScroll', arguments, null, 'top')
+};
+dojo.html.getScrollLeft = function () {
+ return dojo.html._callDeprecated('getScrollLeft', 'getScroll', arguments, null, 'left')
+};
+dojo.html.getScrollOffset = function () {
+ return dojo.html._callDeprecated('getScrollOffset', 'getScroll', arguments, null, 'offset')
+};
+dojo.provide('dojo.uri.Uri');
+dojo.uri = new function () {
+ this.dojoUri = function (uri) {
+ return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri(), uri)
+ };
+ this.moduleUri = function (_432, uri) {
+ var loc = dojo.hostenv.getModuleSymbols(_432).join('/');
+ if (!loc) {
+ return null
+ }
+ if (loc.lastIndexOf('/') != loc.length - 1) {
+ loc += '/'
+ }
+ var _435 = loc.indexOf(':');
+ var _436 = loc.indexOf('/');
+ if (loc.charAt(0) != '/' && (_435 == -1 || _435 > _436)) {
+ loc = dojo.hostenv.getBaseScriptUri() + loc
+ }
+ return new dojo.uri.Uri(loc, uri)
+ };
+ this.Uri = function () {
+ var uri = arguments[0];
+ for (var i = 1; i < arguments.length; i++) {
+ if (!arguments[i]) {
+ continue
+ }
+ var _439 = new dojo.uri.Uri(arguments[i].toString());
+ var _43a = new dojo.uri.Uri(uri.toString());
+ if ((_439.path == '') && (_439.scheme == null) && (_439.authority == null) && (_439.query == null)) {
+ if (_439.fragment != null) {
+ _43a.fragment = _439.fragment
+ }
+ _439 = _43a
+ } else {
+ if (_439.scheme == null) {
+ _439.scheme = _43a.scheme;
+ if (_439.authority == null) {
+ _439.authority = _43a.authority;
+ if (_439.path.charAt(0) != '/') {
+ var path = _43a.path.substring(0, _43a.path.lastIndexOf('/') + 1) + _439.path;
+ var segs = path.split('/');
+ for (var j = 0; j < segs.length; j++) {
+ if (segs[j] == '.') {
+ if (j == segs.length - 1) {
+ segs[j] = ''
+ } else {
+ segs.splice(j, 1);
+ j--
+ }
+ } else {
+ if (j > 0 && !(j == 1 && segs[0] == '') && segs[j] == '..' && segs[j - 1] != '..') {
+ if (j == segs.length - 1) {
+ segs.splice(j, 1);
+ segs[j - 1] = ''
+ } else {
+ segs.splice(j - 1, 2);
+ j -= 2
+ }
+ }
+ }
+ }
+ _439.path = segs.join('/')
+ }
+ }
+ }
+ }
+ uri = '';
+ if (_439.scheme != null) {
+ uri += _439.scheme + ':'
+ }
+ if (_439.authority != null) {
+ uri += '//' + _439.authority
+ }
+ uri += _439.path;
+ if (_439.query != null) {
+ uri += '?' + _439.query
+ }
+ if (_439.fragment != null) {
+ uri += '#' + _439.fragment
+ }
+ }
+ this.uri = uri.toString();
+ var _43e = '^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$';
+ var r = this.uri.match(new RegExp(_43e));
+ this.scheme = r[2] || (r[1] ? '' : null);
+ this.authority = r[4] || (r[3] ? '' : null);
+ this.path = r[5];
+ this.query = r[7] || (r[6] ? '' : null);
+ this.fragment = r[9] || (r[8] ? '' : null);
+ if (this.authority != null) {
+ _43e = '^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$';
+ r = this.authority.match(new RegExp(_43e));
+ this.user = r[3] || null;
+ this.password = r[4] || null;
+ this.host = r[5];
+ this.port = r[7] || null
+ }
+ this.toString = function () {
+ return this.uri
+ }
+ }
+};
+;dojo.provide('dojo.html.style');
+dojo.html.getClass = function (node) {
+ node = dojo.byId(node);
+ if (!node) {
+ return ''
+ }
+ var cs = '';
+ if (node.className) {
+ cs = node.className
+ } else {
+ if (dojo.html.hasAttribute(node, 'class')) {
+ cs = dojo.html.getAttribute(node, 'class')
+ }
+ }
+ return cs.replace(/^\s+|\s+$/g, '')
+};
+dojo.html.getClasses = function (node) {
+ var c = dojo.html.getClass(node);
+ return (c == '') ? [] : c.split(/\s+/g)
+};
+dojo.html.hasClass = function (node, _445) {
+ return (new RegExp('(^|\\s+)' + _445 + '(\\s+|$)')).test(dojo.html.getClass(node))
+};
+dojo.html.prependClass = function (node, _447) {
+ _447 += ' ' + dojo.html.getClass(node);
+ return dojo.html.setClass(node, _447)
+};
+dojo.html.addClass = function (node, _449) {
+ if (dojo.html.hasClass(node, _449)) {
+ return false
+ }
+ _449 = (dojo.html.getClass(node) + ' ' + _449).replace(/^\s+|\s+$/g, '');
+ return dojo.html.setClass(node, _449)
+};
+dojo.html.setClass = function (node, _44b) {
+ node = dojo.byId(node);
+ var cs = String(_44b);
+ try {
+ if (typeof node.className == 'string') {
+ node.className = cs
+ } else {
+ if (node.setAttribute) {
+ node.setAttribute('class', _44b);
+ node.className = cs
+ } else {
+ return false
+ }
+ }
+ } catch (e) {
+ dojo.debug('dojo.html.setClass() failed', e)
+ }
+ return true
+};
+dojo.html.removeClass = function (node, _44e, _44f) {
+ try {
+ if (!_44f) {
+ var _450 = dojo.html.getClass(node).replace(new RegExp('(^|\\s+)' + _44e + '(\\s+|$)'), '$1$2')
+ } else {
+ var _450 = dojo.html.getClass(node).replace(_44e, '')
+ }
+ dojo.html.setClass(node, _450)
+ } catch (e) {
+ dojo.debug('dojo.html.removeClass() failed', e)
+ }
+ return true
+};
+dojo.html.replaceClass = function (node, _452, _453) {
+ dojo.html.removeClass(node, _453);
+ dojo.html.addClass(node, _452)
+};
+dojo.html.classMatchType = {ContainsAll: 0, ContainsAny: 1, IsOnly: 2};
+dojo.html.getElementsByClass = function (_454, _455, _456, _457, _458) {
+ _458 = false;
+ var _459 = dojo.doc();
+ _455 = dojo.byId(_455) || _459;
+ var _45a = _454.split(/\s+/g);
+ var _45b = [];
+ if (_457 != 1 && _457 != 2) {
+ _457 = 0
+ }
+ var _45c = new RegExp('(\\s|^)((' + _45a.join(')|(') + '))(\\s|$)');
+ var _45d = _45a.join(' ').length;
+ var _45e = [];
+ if (!_458 && _459.evaluate) {
+ var _45f = './/' + (_456 || '*') + '[contains(';
+ if (_457 != dojo.html.classMatchType.ContainsAny) {
+ _45f += 'concat(\' \',@class,\' \'), \' '
+ + _45a.join(' \') and contains(concat(\' \',@class,\' \'), \' ')
+ + ' \')';
+ if (_457 == 2) {
+ _45f += ' and string-length(@class)=' + _45d + ']'
+ } else {
+ _45f += ']'
+ }
+ } else {
+ _45f += 'concat(\' \',@class,\' \'), \' '
+ + _45a.join(' \') or contains(concat(\' \',@class,\' \'), \' ')
+ + ' \')]'
+ }
+ var _460 = _459.evaluate(_45f, _455, null, XPathResult.ANY_TYPE, null);
+ var _461 = _460.iterateNext();
+ while (_461) {
+ try {
+ _45e.push(_461);
+ _461 = _460.iterateNext()
+ } catch (e) {
+ break
+ }
+ }
+ return _45e
+ } else {
+ if (!_456) {
+ _456 = '*'
+ }
+ _45e = _455.getElementsByTagName(_456);
+ var node, i = 0;
+ outer:
+ while (node = _45e[i++]) {
+ var _464 = dojo.html.getClasses(node);
+ if (_464.length == 0) {
+ continue;
+ }
+ var _465 = 0;
+ for (var j = 0; j < _464.length; j++) {
+ if (_45c.test(_464[j])) {
+ if (_457 == dojo.html.classMatchType.ContainsAny) {
+ _45b.push(node);
+ continue outer
+ } else {
+ _465++
+ }
+ } else {
+ if (_457 == dojo.html.classMatchType.IsOnly) {
+ continue outer
+ }
+ }
+ }
+ if (_465 == _45a.length) {
+ if ((_457 == dojo.html.classMatchType.IsOnly) && (_465 == _464.length)) {
+ _45b.push(node)
+ } else {
+ if (_457 == dojo.html.classMatchType.ContainsAll) {
+ _45b.push(node)
+ }
+ }
+ }
+ }
+ return _45b
+ }
+};
+dojo.html.getElementsByClassName = dojo.html.getElementsByClass;
+dojo.html.toCamelCase = function (_467) {
+ var arr = _467.split('-'), cc = arr[0];
+ for (var i = 1; i < arr.length; i++) {
+ cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1)
+ }
+ return cc
+};
+dojo.html.toSelectorCase = function (_46b) {
+ return _46b.replace(/([A-Z])/g, '-$1').toLowerCase()
+};
+if (dojo.render.html.ie) {
+ dojo.html.getComputedStyle = function (node, _46d, _46e) {
+ node = dojo.byId(node);
+ if (!node || !node.currentStyle) {
+ return _46e
+ }
+ return node.currentStyle[dojo.html.toCamelCase(_46d)]
+ };
+ dojo.html.getComputedStyles = function (node) {
+ return node.currentStyle
+ }
+} else {
+ dojo.html.getComputedStyle = function (node, _471, _472) {
+ node = dojo.byId(node);
+ if (!node || !node.style) {
+ return _472
+ }
+ var s = document.defaultView.getComputedStyle(node, null);
+ return (s && s[dojo.html.toCamelCase(_471)]) || ''
+ };
+ dojo.html.getComputedStyles = function (node) {
+ return document.defaultView.getComputedStyle(node, null)
+ }
+}
+dojo.html.getStyleProperty = function (node, _476) {
+ node = dojo.byId(node);
+ return (node && node.style ? node.style[dojo.html.toCamelCase(_476)] : undefined)
+};
+dojo.html.getStyle = function (node, _478) {
+ var _479 = dojo.html.getStyleProperty(node, _478);
+ return (_479 ? _479 : dojo.html.getComputedStyle(node, _478))
+};
+dojo.html.setStyle = function (node, _47b, _47c) {
+ node = dojo.byId(node);
+ if (node && node.style) {
+ var _47d = dojo.html.toCamelCase(_47b);
+ node.style[_47d] = _47c
+ }
+};
+dojo.html.setStyleText = function (_47e, text) {
+ try {
+ _47e.style.cssText = text
+ } catch (e) {
+ _47e.setAttribute('style', text)
+ }
+};
+dojo.html.copyStyle = function (_480, _481) {
+ if (!_481.style.cssText) {
+ _480.setAttribute('style', _481.getAttribute('style'))
+ } else {
+ _480.style.cssText = _481.style.cssText
+ }
+ dojo.html.addClass(_480, dojo.html.getClass(_481))
+};
+dojo.html.getUnitValue = function (node, _483, _484) {
+ var s = dojo.html.getComputedStyle(node, _483);
+ if ((!s) || ((s == 'auto') && (_484))) {
+ return {value: 0, units: 'px'}
+ }
+ var _486 = s.match(/(\-?[\d.]+)([a-z%]*)/i);
+ if (!_486) {
+ return dojo.html.getUnitValue.bad
+ }
+ return {value: Number(_486[1]), units: _486[2].toLowerCase()}
+};
+dojo.html.getUnitValue.bad = {value: NaN, units: ''};
+if (dojo.render.html.ie) {
+ dojo.html.toPixelValue = function (_487, _488) {
+ if (!_488) {
+ return 0
+ }
+ if (_488.slice(-2) == 'px') {
+ return parseFloat(_488)
+ }
+ var _489 = 0;
+ with (_487) {
+ var _48a = style.left;
+ var _48b = runtimeStyle.left;
+ runtimeStyle.left = currentStyle.left;
+ try {
+ style.left = _488 || 0;
+ _489 = style.pixelLeft;
+ style.left = _48a;
+ runtimeStyle.left = _48b
+ } catch (e) {
+ }
+ }
+ return _489
+ }
+} else {
+ dojo.html.toPixelValue = function (_48c, _48d) {
+ return (_48d && (_48d.slice(-2) == 'px') ? parseFloat(_48d) : 0)
+ }
+}
+dojo.html.getPixelValue = function (node, _48f, _490) {
+ return dojo.html.toPixelValue(node, dojo.html.getComputedStyle(node, _48f))
+};
+dojo.html.setPositivePixelValue = function (node, _492, _493) {
+ if (isNaN(_493)) {
+ return false
+ }
+ node.style[_492] = Math.max(0, _493) + 'px';
+ return true
+};
+dojo.html.styleSheet = null;
+dojo.html.insertCssRule = function (_494, _495, _496) {
+ if (!dojo.html.styleSheet) {
+ if (document.createStyleSheet) {
+ dojo.html.styleSheet = document.createStyleSheet()
+ } else {
+ if (document.styleSheets[0]) {
+ dojo.html.styleSheet = document.styleSheets[0]
+ } else {
+ return null
+ }
+ }
+ }
+ if (arguments.length < 3) {
+ if (dojo.html.styleSheet.cssRules) {
+ _496 = dojo.html.styleSheet.cssRules.length
+ } else {
+ if (dojo.html.styleSheet.rules) {
+ _496 = dojo.html.styleSheet.rules.length
+ } else {
+ return null
+ }
+ }
+ }
+ if (dojo.html.styleSheet.insertRule) {
+ var rule = _494 + ' { ' + _495 + ' }';
+ return dojo.html.styleSheet.insertRule(rule, _496)
+ } else {
+ if (dojo.html.styleSheet.addRule) {
+ return dojo.html.styleSheet.addRule(_494, _495, _496)
+ } else {
+ return null
+ }
+ }
+};
+dojo.html.removeCssRule = function (_498) {
+ if (!dojo.html.styleSheet) {
+ dojo.debug('no stylesheet defined for removing rules');
+ return false
+ }
+ if (dojo.render.html.ie) {
+ if (!_498) {
+ _498 = dojo.html.styleSheet.rules.length;
+ dojo.html.styleSheet.removeRule(_498)
+ }
+ } else {
+ if (document.styleSheets[0]) {
+ if (!_498) {
+ _498 = dojo.html.styleSheet.cssRules.length
+ }
+ dojo.html.styleSheet.deleteRule(_498)
+ }
+ }
+ return true
+};
+dojo.html._insertedCssFiles = [];
+dojo.html.insertCssFile = function (URI, doc, _49b, _49c) {
+ if (!URI) {
+ return
+ }
+ if (!doc) {
+ doc = document
+ }
+ var _49d = dojo.hostenv.getText(URI, false, _49c);
+ if (_49d === null) {
+ return
+ }
+ _49d = dojo.html.fixPathsInCssText(_49d, URI);
+ if (_49b) {
+ var idx = -1, node, ent = dojo.html._insertedCssFiles;
+ for (var i = 0; i < ent.length; i++) {
+ if ((ent[i].doc == doc) && (ent[i].cssText == _49d)) {
+ idx = i;
+ node = ent[i].nodeRef;
+ break
+ }
+ }
+ if (node) {
+ var _4a2 = doc.getElementsByTagName('style');
+ for (var i = 0; i < _4a2.length; i++) {
+ if (_4a2[i] == node) {
+ return
+ }
+ }
+ dojo.html._insertedCssFiles.shift(idx, 1)
+ }
+ }
+ var _4a3 = dojo.html.insertCssText(_49d, doc);
+ dojo.html._insertedCssFiles.push({'doc': doc, 'cssText': _49d, 'nodeRef': _4a3});
+ if (_4a3 && djConfig.isDebug) {
+ _4a3.setAttribute('dbgHref', URI)
+ }
+ return _4a3
+};
+dojo.html.insertCssText = function (_4a4, doc, URI) {
+ if (!_4a4) {
+ return
+ }
+ if (!doc) {
+ doc = document
+ }
+ if (URI) {
+ _4a4 = dojo.html.fixPathsInCssText(_4a4, URI)
+ }
+ var _4a7 = doc.createElement('style');
+ _4a7.setAttribute('type', 'text/css');
+ var head = doc.getElementsByTagName('head')[0];
+ if (!head) {
+ dojo.debug('No head tag in document, aborting styles');
+ return
+ } else {
+ head.appendChild(_4a7)
+ }
+ if (_4a7.styleSheet) {
+ var _4a9 = function () {
+ try {
+ _4a7.styleSheet.cssText = _4a4
+ } catch (e) {
+ dojo.debug(e)
+ }
+ };
+ if (_4a7.styleSheet.disabled) {
+ setTimeout(_4a9, 10)
+ } else {
+ _4a9()
+ }
+ } else {
+ var _4aa = doc.createTextNode(_4a4);
+ _4a7.appendChild(_4aa)
+ }
+ return _4a7
+};
+dojo.html.fixPathsInCssText = function (_4ab, URI) {
+ if (!_4ab || !URI) {
+ return
+ }
+ var _4ad, str = '', url = '', _4b0 = '[\\t\\s\\w\\(\\)\\/\\.\\\\\'"-:#=&?~]+';
+ var _4b1 = new RegExp('url\\(\\s*(' + _4b0 + ')\\s*\\)');
+ var _4b2 = /(file|https?|ftps?):\/\//;
+ regexTrim = new RegExp('^[\\s]*([\'"]?)(' + _4b0 + ')\\1[\\s]*?$');
+ if (dojo.render.html.ie55 || dojo.render.html.ie60) {
+ var _4b3 = new RegExp('AlphaImageLoader\\((.*)src=[\'"](' + _4b0 + ')[\'"]');
+ while (_4ad = _4b3.exec(_4ab)) {
+ url = _4ad[2].replace(regexTrim, '$2');
+ if (!_4b2.exec(url)) {
+ url = (new dojo.uri.Uri(URI, url).toString())
+ }
+ str += _4ab.substring(0, _4ad.index) + 'AlphaImageLoader(' + _4ad[1] + 'src=\'' + url + '\'';
+ _4ab = _4ab.substr(_4ad.index + _4ad[0].length)
+ }
+ _4ab = str + _4ab;
+ str = ''
+ }
+ while (_4ad = _4b1.exec(_4ab)) {
+ url = _4ad[1].replace(regexTrim, '$2');
+ if (!_4b2.exec(url)) {
+ url = (new dojo.uri.Uri(URI, url).toString())
+ }
+ str += _4ab.substring(0, _4ad.index) + 'url(' + url + ')';
+ _4ab = _4ab.substr(_4ad.index + _4ad[0].length)
+ }
+ return str + _4ab
+};
+dojo.html.setActiveStyleSheet = function (_4b4) {
+ var i = 0, a, els = dojo.doc().getElementsByTagName('link');
+ while (a = els[i++]) {
+ if (a.getAttribute('rel').indexOf('style') != -1 && a.getAttribute('title')) {
+ a.disabled = true;
+ if (a.getAttribute('title') == _4b4) {
+ a.disabled = false
+ }
+ }
+ }
+};
+dojo.html.getActiveStyleSheet = function () {
+ var i = 0, a, els = dojo.doc().getElementsByTagName('link');
+ while (a = els[i++]) {
+ if (a.getAttribute('rel').indexOf('style') != -1 && a.getAttribute('title') && !a.disabled) {
+ return a.getAttribute('title')
+ }
+ }
+ return null
+};
+dojo.html.getPreferredStyleSheet = function () {
+ var i = 0, a, els = dojo.doc().getElementsByTagName('link');
+ while (a = els[i++]) {
+ if (a.getAttribute('rel').indexOf('style')
+ != -1
+ && a.getAttribute('rel').indexOf('alt')
+ == -1
+ && a.getAttribute('title')) {
+ return a.getAttribute('title')
+ }
+ }
+ return null
+};
+dojo.html.applyBrowserClass = function (node) {
+ var drh = dojo.render.html;
+ var _4c0 = {
+ dj_ie: drh.ie,
+ dj_ie55: drh.ie55,
+ dj_ie6: drh.ie60,
+ dj_ie7: drh.ie70,
+ dj_iequirks: drh.ie && drh.quirks,
+ dj_opera: drh.opera,
+ dj_opera8: drh.opera && (Math.floor(dojo.render.version) == 8),
+ dj_opera9: drh.opera && (Math.floor(dojo.render.version) == 9),
+ dj_khtml: drh.khtml,
+ dj_safari: drh.safari,
+ dj_gecko: drh.mozilla
+ };
+ for (var p in _4c0) {
+ if (_4c0[p]) {
+ dojo.html.addClass(node, p)
+ }
+ }
+};
+dojo.provide('dojo.html.display');
+dojo.html._toggle = function (node, _4c3, _4c4) {
+ node = dojo.byId(node);
+ _4c4(node, !_4c3(node));
+ return _4c3(node)
+};
+dojo.html.show = function (node) {
+ node = dojo.byId(node);
+ if (dojo.html.getStyleProperty(node, 'display') == 'none') {
+ dojo.html.setStyle(node, 'display', (node.dojoDisplayCache || ''));
+ node.dojoDisplayCache = undefined
+ }
+};
+dojo.html.hide = function (node) {
+ node = dojo.byId(node);
+ if (typeof node['dojoDisplayCache'] == 'undefined') {
+ var d = dojo.html.getStyleProperty(node, 'display');
+ if (d != 'none') {
+ node.dojoDisplayCache = d
+ }
+ }
+ dojo.html.setStyle(node, 'display', 'none')
+};
+dojo.html.setShowing = function (node, _4c9) {
+ dojo.html[(_4c9 ? 'show' : 'hide')](node)
+};
+dojo.html.isShowing = function (node) {
+ return (dojo.html.getStyleProperty(node, 'display') != 'none')
+};
+dojo.html.toggleShowing = function (node) {
+ return dojo.html._toggle(node, dojo.html.isShowing, dojo.html.setShowing)
+};
+dojo.html.displayMap = {tr: '', td: '', th: '', img: 'inline', span: 'inline', input: 'inline', button: 'inline'};
+dojo.html.suggestDisplayByTagName = function (node) {
+ node = dojo.byId(node);
+ if (node && node.tagName) {
+ var tag = node.tagName.toLowerCase();
+ return (tag in dojo.html.displayMap ? dojo.html.displayMap[tag] : 'block')
+ }
+};
+dojo.html.setDisplay = function (node, _4cf) {
+ dojo.html.setStyle(node, 'display', ((_4cf
+ instanceof String
+ || typeof _4cf
+ == 'string') ? _4cf : (_4cf ? dojo.html.suggestDisplayByTagName(node) : 'none')))
+};
+dojo.html.isDisplayed = function (node) {
+ return (dojo.html.getComputedStyle(node, 'display') != 'none')
+};
+dojo.html.toggleDisplay = function (node) {
+ return dojo.html._toggle(node, dojo.html.isDisplayed, dojo.html.setDisplay)
+};
+dojo.html.setVisibility = function (node, _4d3) {
+ dojo.html.setStyle(node, 'visibility', ((_4d3
+ instanceof String
+ || typeof _4d3
+ == 'string') ? _4d3 : (_4d3 ? 'visible' : 'hidden')))
+};
+dojo.html.isVisible = function (node) {
+ return (dojo.html.getComputedStyle(node, 'visibility') != 'hidden')
+};
+dojo.html.toggleVisibility = function (node) {
+ return dojo.html._toggle(node, dojo.html.isVisible, dojo.html.setVisibility)
+};
+dojo.html.setOpacity = function (node, _4d7, _4d8) {
+ node = dojo.byId(node);
+ var h = dojo.render.html;
+ if (!_4d8) {
+ if (_4d7 >= 1) {
+ if (h.ie) {
+ dojo.html.clearOpacity(node);
+ return
+ } else {
+ _4d7 = 0.999999
+ }
+ } else {
+ if (_4d7 < 0) {
+ _4d7 = 0
+ }
+ }
+ }
+ if (h.ie) {
+ if (node.nodeName.toLowerCase() == 'tr') {
+ var tds = node.getElementsByTagName('td');
+ for (var x = 0; x < tds.length; x++) {
+ tds[x].style.filter = 'Alpha(Opacity=' + _4d7 * 100 + ')'
+ }
+ }
+ node.style.filter = 'Alpha(Opacity=' + _4d7 * 100 + ')'
+ } else {
+ if (h.moz) {
+ node.style.opacity = _4d7;
+ node.style.MozOpacity = _4d7
+ } else {
+ if (h.safari) {
+ node.style.opacity = _4d7;
+ node.style.KhtmlOpacity = _4d7
+ } else {
+ node.style.opacity = _4d7
+ }
+ }
+ }
+};
+dojo.html.clearOpacity = function (node) {
+ node = dojo.byId(node);
+ var ns = node.style;
+ var h = dojo.render.html;
+ if (h.ie) {
+ try {
+ if (node.filters && node.filters.alpha) {
+ ns.filter = ''
+ }
+ } catch (e) {
+ }
+ } else {
+ if (h.moz) {
+ ns.opacity = 1;
+ ns.MozOpacity = 1
+ } else {
+ if (h.safari) {
+ ns.opacity = 1;
+ ns.KhtmlOpacity = 1
+ } else {
+ ns.opacity = 1
+ }
+ }
+ }
+};
+dojo.html.getOpacity = function (node) {
+ node = dojo.byId(node);
+ var h = dojo.render.html;
+ if (h.ie) {
+ var opac = (node.filters
+ && node.filters.alpha
+ && typeof node.filters.alpha.opacity
+ == 'number' ? node.filters.alpha.opacity : 100) / 100
+ } else {
+ var opac = node.style.opacity || node.style.MozOpacity || node.style.KhtmlOpacity || 1
+ }
+ return opac >= 0.999999 ? 1 : Number(opac)
+};
+dojo.provide('dojo.html.color');
+dojo.html.getBackgroundColor = function (node) {
+ node = dojo.byId(node);
+ var _4e3;
+ do {
+ _4e3 = dojo.html.getStyle(node, 'background-color');
+ if (_4e3.toLowerCase() == 'rgba(0, 0, 0, 0)') {
+ _4e3 = 'transparent'
+ }
+ if (node == document.getElementsByTagName('body')[0]) {
+ node = null;
+ break
+ }
+ node = node.parentNode
+ } while (node && dojo.lang.inArray(['transparent', ''], _4e3));
+ if (_4e3 == 'transparent') {
+ _4e3 = [255, 255, 255, 0]
+ } else {
+ _4e3 = dojo.gfx.color.extractRGB(_4e3)
+ }
+ return _4e3
+};
+dojo.provide('dojo.html.layout');
+dojo.html.sumAncestorProperties = function (node, prop) {
+ node = dojo.byId(node);
+ if (!node) {
+ return 0
+ }
+ var _4e6 = 0;
+ while (node) {
+ if (dojo.html.getComputedStyle(node, 'position') == 'fixed') {
+ return 0
+ }
+ var val = node[prop];
+ if (val) {
+ _4e6 += val - 0;
+ if (node == dojo.body()) {
+ break
+ }
+ }
+ node = node.parentNode
+ }
+ return _4e6
+};
+dojo.html.setStyleAttributes = function (node, _4e9) {
+ node = dojo.byId(node);
+ var _4ea = _4e9.replace(/(;)?\s*$/, '').split(';');
+ for (var i = 0; i < _4ea.length; i++) {
+ var _4ec = _4ea[i].split(':');
+ var name = _4ec[0].replace(/\s*$/, '').replace(/^\s*/, '').toLowerCase();
+ var _4ee = _4ec[1].replace(/\s*$/, '').replace(/^\s*/, '');
+ switch (name) {
+ case 'opacity':
+ dojo.html.setOpacity(node, _4ee);
+ break;
+ case 'content-height':
+ dojo.html.setContentBox(node, {height: _4ee});
+ break;
+ case 'content-width':
+ dojo.html.setContentBox(node, {width: _4ee});
+ break;
+ case 'outer-height':
+ dojo.html.setMarginBox(node, {height: _4ee});
+ break;
+ case 'outer-width':
+ dojo.html.setMarginBox(node, {width: _4ee});
+ break;
+ default:
+ node.style[dojo.html.toCamelCase(name)] = _4ee
+ }
+ }
+};
+dojo.html.boxSizing = {
+ MARGIN_BOX: 'margin-box', BORDER_BOX: 'border-box', PADDING_BOX: 'padding-box', CONTENT_BOX: 'content-box'
+};
+dojo.html.getAbsolutePosition = dojo.html.abs = function (node, _4f0, _4f1) {
+ node = dojo.byId(node, node.ownerDocument);
+ var ret = {x: 0, y: 0};
+ var bs = dojo.html.boxSizing;
+ if (!_4f1) {
+ _4f1 = bs.CONTENT_BOX
+ }
+ var _4f4 = 2;
+ var _4f5;
+ switch (_4f1) {
+ case bs.MARGIN_BOX:
+ _4f5 = 3;
+ break;
+ case bs.BORDER_BOX:
+ _4f5 = 2;
+ break;
+ case bs.PADDING_BOX:
+ default:
+ _4f5 = 1;
+ break;
+ case bs.CONTENT_BOX:
+ _4f5 = 0;
+ break
+ }
+ var h = dojo.render.html;
+ var db = document['body'] || document['documentElement'];
+ if (h.ie) {
+ with (node.getBoundingClientRect()) {
+ ret.x = left - 2;
+ ret.y = top - 2
+ }
+ } else {
+ if (document.getBoxObjectFor) {
+ _4f4 = 1;
+ try {
+ var bo = document.getBoxObjectFor(node);
+ ret.x = bo.x - dojo.html.sumAncestorProperties(node, 'scrollLeft');
+ ret.y = bo.y - dojo.html.sumAncestorProperties(node, 'scrollTop')
+ } catch (e) {
+ }
+ } else {
+ if (node['offsetParent']) {
+ var _4f9;
+ if ((h.safari) && (node.style.getPropertyValue('position') == 'absolute') && (node.parentNode == db)) {
+ _4f9 = db
+ } else {
+ _4f9 = db.parentNode
+ }
+ if (node.parentNode != db) {
+ var nd = node;
+ if (dojo.render.html.opera) {
+ nd = db
+ }
+ ret.x -= dojo.html.sumAncestorProperties(nd, 'scrollLeft');
+ ret.y -= dojo.html.sumAncestorProperties(nd, 'scrollTop')
+ }
+ var _4fb = node;
+ do {
+ var n = _4fb['offsetLeft'];
+ if (!h.opera || n > 0) {
+ ret.x += isNaN(n) ? 0 : n
+ }
+ var m = _4fb['offsetTop'];
+ ret.y += isNaN(m) ? 0 : m;
+ _4fb = _4fb.offsetParent
+ } while ((_4fb != _4f9) && (_4fb != null))
+ } else {
+ if (node['x'] && node['y']) {
+ ret.x += isNaN(node.x) ? 0 : node.x;
+ ret.y += isNaN(node.y) ? 0 : node.y
+ }
+ }
+ }
+ }
+ if (_4f0) {
+ var _4fe = dojo.html.getScroll();
+ ret.y += _4fe.top;
+ ret.x += _4fe.left
+ }
+ var _4ff = [dojo.html.getPaddingExtent, dojo.html.getBorderExtent, dojo.html.getMarginExtent];
+ if (_4f4 > _4f5) {
+ for (var i = _4f5; i < _4f4; ++i) {
+ ret.y += _4ff[i](node, 'top');
+ ret.x += _4ff[i](node, 'left')
+ }
+ } else {
+ if (_4f4 < _4f5) {
+ for (var i = _4f5; i > _4f4; --i) {
+ ret.y -= _4ff[i - 1](node, 'top');
+ ret.x -= _4ff[i - 1](node, 'left')
+ }
+ }
+ }
+ ret.top = ret.y;
+ ret.left = ret.x;
+ return ret
+};
+dojo.html.isPositionAbsolute = function (node) {
+ return (dojo.html.getComputedStyle(node, 'position') == 'absolute')
+};
+dojo.html._sumPixelValues = function (node, _503, _504) {
+ var _505 = 0;
+ for (var x = 0; x < _503.length; x++) {
+ _505 += dojo.html.getPixelValue(node, _503[x], _504)
+ }
+ return _505
+};
+dojo.html.getMargin = function (node) {
+ return {
+ width: dojo.html._sumPixelValues(node, ['margin-left', 'margin-right'], (dojo.html.getComputedStyle(node, 'position')
+ == 'absolute')),
+ height: dojo.html._sumPixelValues(node, ['margin-top', 'margin-bottom'], (dojo.html.getComputedStyle(node, 'position')
+ == 'absolute'))
+ }
+};
+dojo.html.getBorder = function (node) {
+ return {
+ width: dojo.html.getBorderExtent(node, 'left') + dojo.html.getBorderExtent(node, 'right'),
+ height: dojo.html.getBorderExtent(node, 'top') + dojo.html.getBorderExtent(node, 'bottom')
+ }
+};
+dojo.html.getBorderExtent = function (node, side) {
+ return (dojo.html.getStyle(node, 'border-' + side + '-style') == 'none' ? 0 : dojo.html.getPixelValue(node, 'border-'
+ + side
+ + '-width'))
+};
+dojo.html.getMarginExtent = function (node, side) {
+ return dojo.html._sumPixelValues(node, ['margin-' + side], dojo.html.isPositionAbsolute(node))
+};
+dojo.html.getPaddingExtent = function (node, side) {
+ return dojo.html._sumPixelValues(node, ['padding-' + side], true)
+};
+dojo.html.getPadding = function (node) {
+ return {
+ width: dojo.html._sumPixelValues(node, ['padding-left', 'padding-right'], true),
+ height: dojo.html._sumPixelValues(node, ['padding-top', 'padding-bottom'], true)
+ }
+};
+dojo.html.getPadBorder = function (node) {
+ var pad = dojo.html.getPadding(node);
+ var _512 = dojo.html.getBorder(node);
+ return {width: pad.width + _512.width, height: pad.height + _512.height}
+};
+dojo.html.getBoxSizing = function (node) {
+ var h = dojo.render.html;
+ var bs = dojo.html.boxSizing;
+ if (((h.ie) || (h.opera)) && node.nodeName.toLowerCase() != 'img') {
+ var cm = document['compatMode'];
+ if ((cm == 'BackCompat') || (cm == 'QuirksMode')) {
+ return bs.BORDER_BOX
+ } else {
+ return bs.CONTENT_BOX
+ }
+ } else {
+ if (arguments.length == 0) {
+ node = document.documentElement
+ }
+ var _517;
+ if (!h.ie) {
+ _517 = dojo.html.getStyle(node, '-moz-box-sizing');
+ if (!_517) {
+ _517 = dojo.html.getStyle(node, 'box-sizing')
+ }
+ }
+ return (_517 ? _517 : bs.CONTENT_BOX)
+ }
+};
+dojo.html.isBorderBox = function (node) {
+ return (dojo.html.getBoxSizing(node) == dojo.html.boxSizing.BORDER_BOX)
+};
+dojo.html.getBorderBox = function (node) {
+ node = dojo.byId(node);
+ return {width: node.offsetWidth, height: node.offsetHeight}
+};
+dojo.html.getPaddingBox = function (node) {
+ var box = dojo.html.getBorderBox(node);
+ var _51c = dojo.html.getBorder(node);
+ return {width: box.width - _51c.width, height: box.height - _51c.height}
+};
+dojo.html.getContentBox = function (node) {
+ node = dojo.byId(node);
+ var _51e = dojo.html.getPadBorder(node);
+ return {width: node.offsetWidth - _51e.width, height: node.offsetHeight - _51e.height}
+};
+dojo.html.setContentBox = function (node, args) {
+ node = dojo.byId(node);
+ var _521 = 0;
+ var _522 = 0;
+ var isbb = dojo.html.isBorderBox(node);
+ var _524 = (isbb ? dojo.html.getPadBorder(node) : {width: 0, height: 0});
+ var ret = {};
+ if (typeof args.width != 'undefined') {
+ _521 = args.width + _524.width;
+ ret.width = dojo.html.setPositivePixelValue(node, 'width', _521)
+ }
+ if (typeof args.height != 'undefined') {
+ _522 = args.height + _524.height;
+ ret.height = dojo.html.setPositivePixelValue(node, 'height', _522)
+ }
+ return ret
+};
+dojo.html.getMarginBox = function (node) {
+ var _527 = dojo.html.getBorderBox(node);
+ var _528 = dojo.html.getMargin(node);
+ return {width: _527.width + _528.width, height: _527.height + _528.height}
+};
+dojo.html.setMarginBox = function (node, args) {
+ node = dojo.byId(node);
+ var _52b = 0;
+ var _52c = 0;
+ var isbb = dojo.html.isBorderBox(node);
+ var _52e = (!isbb ? dojo.html.getPadBorder(node) : {width: 0, height: 0});
+ var _52f = dojo.html.getMargin(node);
+ var ret = {};
+ if (typeof args.width != 'undefined') {
+ _52b = args.width - _52e.width;
+ _52b -= _52f.width;
+ ret.width = dojo.html.setPositivePixelValue(node, 'width', _52b)
+ }
+ if (typeof args.height != 'undefined') {
+ _52c = args.height - _52e.height;
+ _52c -= _52f.height;
+ ret.height = dojo.html.setPositivePixelValue(node, 'height', _52c)
+ }
+ return ret
+};
+dojo.html.getElementBox = function (node, type) {
+ var bs = dojo.html.boxSizing;
+ switch (type) {
+ case bs.MARGIN_BOX:
+ return dojo.html.getMarginBox(node);
+ case bs.BORDER_BOX:
+ return dojo.html.getBorderBox(node);
+ case bs.PADDING_BOX:
+ return dojo.html.getPaddingBox(node);
+ case bs.CONTENT_BOX:
+ default:
+ return dojo.html.getContentBox(node)
+ }
+};
+dojo.html.toCoordinateObject = dojo.html.toCoordinateArray = function (_534, _535, _536) {
+ if (_534 instanceof Array || typeof _534 == 'array') {
+ dojo.deprecated('dojo.html.toCoordinateArray', 'use dojo.html.toCoordinateObject({left: , top: , width: , height: }) instead', '0.5');
+ while (_534.length < 4) {
+ _534.push(0)
+ }
+ while (_534.length > 4) {
+ _534.pop()
+ }
+ var ret = {left: _534[0], top: _534[1], width: _534[2], height: _534[3]}
+ } else {
+ if (!_534.nodeType && !(_534 instanceof String || typeof _534 == 'string') && ('width'
+ in _534
+ || 'height'
+ in _534
+ || 'left'
+ in _534
+ || 'x'
+ in _534
+ || 'top'
+ in _534
+ || 'y'
+ in _534)) {
+ var ret = {
+ left: _534.left || _534.x || 0, top: _534.top || _534.y || 0, width: _534.width || 0, height: _534.height || 0
+ }
+ } else {
+ var node = dojo.byId(_534);
+ var pos = dojo.html.abs(node, _535, _536);
+ var _53a = dojo.html.getMarginBox(node);
+ var ret = {left: pos.left, top: pos.top, width: _53a.width, height: _53a.height}
+ }
+ }
+ ret.x = ret.left;
+ ret.y = ret.top;
+ return ret
+};
+dojo.html.setMarginBoxWidth = dojo.html.setOuterWidth = function (node, _53c) {
+ return dojo.html._callDeprecated('setMarginBoxWidth', 'setMarginBox', arguments, 'width')
+};
+dojo.html.setMarginBoxHeight = dojo.html.setOuterHeight = function () {
+ return dojo.html._callDeprecated('setMarginBoxHeight', 'setMarginBox', arguments, 'height')
+};
+dojo.html.getMarginBoxWidth = dojo.html.getOuterWidth = function () {
+ return dojo.html._callDeprecated('getMarginBoxWidth', 'getMarginBox', arguments, null, 'width')
+};
+dojo.html.getMarginBoxHeight = dojo.html.getOuterHeight = function () {
+ return dojo.html._callDeprecated('getMarginBoxHeight', 'getMarginBox', arguments, null, 'height')
+};
+dojo.html.getTotalOffset = function (node, type, _53f) {
+ return dojo.html._callDeprecated('getTotalOffset', 'getAbsolutePosition', arguments, null, type)
+};
+dojo.html.getAbsoluteX = function (node, _541) {
+ return dojo.html._callDeprecated('getAbsoluteX', 'getAbsolutePosition', arguments, null, 'x')
+};
+dojo.html.getAbsoluteY = function (node, _543) {
+ return dojo.html._callDeprecated('getAbsoluteY', 'getAbsolutePosition', arguments, null, 'y')
+};
+dojo.html.totalOffsetLeft = function (node, _545) {
+ return dojo.html._callDeprecated('totalOffsetLeft', 'getAbsolutePosition', arguments, null, 'left')
+};
+dojo.html.totalOffsetTop = function (node, _547) {
+ return dojo.html._callDeprecated('totalOffsetTop', 'getAbsolutePosition', arguments, null, 'top')
+};
+dojo.html.getMarginWidth = function (node) {
+ return dojo.html._callDeprecated('getMarginWidth', 'getMargin', arguments, null, 'width')
+};
+dojo.html.getMarginHeight = function (node) {
+ return dojo.html._callDeprecated('getMarginHeight', 'getMargin', arguments, null, 'height')
+};
+dojo.html.getBorderWidth = function (node) {
+ return dojo.html._callDeprecated('getBorderWidth', 'getBorder', arguments, null, 'width')
+};
+dojo.html.getBorderHeight = function (node) {
+ return dojo.html._callDeprecated('getBorderHeight', 'getBorder', arguments, null, 'height')
+};
+dojo.html.getPaddingWidth = function (node) {
+ return dojo.html._callDeprecated('getPaddingWidth', 'getPadding', arguments, null, 'width')
+};
+dojo.html.getPaddingHeight = function (node) {
+ return dojo.html._callDeprecated('getPaddingHeight', 'getPadding', arguments, null, 'height')
+};
+dojo.html.getPadBorderWidth = function (node) {
+ return dojo.html._callDeprecated('getPadBorderWidth', 'getPadBorder', arguments, null, 'width')
+};
+dojo.html.getPadBorderHeight = function (node) {
+ return dojo.html._callDeprecated('getPadBorderHeight', 'getPadBorder', arguments, null, 'height')
+};
+dojo.html.getBorderBoxWidth = dojo.html.getInnerWidth = function () {
+ return dojo.html._callDeprecated('getBorderBoxWidth', 'getBorderBox', arguments, null, 'width')
+};
+dojo.html.getBorderBoxHeight = dojo.html.getInnerHeight = function () {
+ return dojo.html._callDeprecated('getBorderBoxHeight', 'getBorderBox', arguments, null, 'height')
+};
+dojo.html.getContentBoxWidth = dojo.html.getContentWidth = function () {
+ return dojo.html._callDeprecated('getContentBoxWidth', 'getContentBox', arguments, null, 'width')
+};
+dojo.html.getContentBoxHeight = dojo.html.getContentHeight = function () {
+ return dojo.html._callDeprecated('getContentBoxHeight', 'getContentBox', arguments, null, 'height')
+};
+dojo.html.setContentBoxWidth = dojo.html.setContentWidth = function (node, _551) {
+ return dojo.html._callDeprecated('setContentBoxWidth', 'setContentBox', arguments, 'width')
+};
+dojo.html.setContentBoxHeight = dojo.html.setContentHeight = function (node, _553) {
+ return dojo.html._callDeprecated('setContentBoxHeight', 'setContentBox', arguments, 'height')
+};
+dojo.provide('dojo.lfx.html');
+dojo.lfx.html._byId = function (_554) {
+ if (!_554) {
+ return []
+ }
+ if (dojo.lang.isArrayLike(_554)) {
+ if (!_554.alreadyChecked) {
+ var n = [];
+ dojo.lang.forEach(_554, function (node) {
+ n.push(dojo.byId(node))
+ });
+ n.alreadyChecked = true;
+ return n
+ } else {
+ return _554
+ }
+ } else {
+ var n = [];
+ n.push(dojo.byId(_554));
+ n.alreadyChecked = true;
+ return n
+ }
+};
+dojo.lfx.html.propertyAnimation = function (_557, _558, _559, _55a, _55b) {
+ _557 = dojo.lfx.html._byId(_557);
+ var _55c = {'propertyMap': _558, 'nodes': _557, 'duration': _559, 'easing': _55a || dojo.lfx.easeDefault};
+ var _55d = function (args) {
+ if (args.nodes.length == 1) {
+ var pm = args.propertyMap;
+ if (!dojo.lang.isArray(args.propertyMap)) {
+ var parr = [];
+ for (var _561 in pm) {
+ pm[_561].property = _561;
+ parr.push(pm[_561])
+ }
+ pm = args.propertyMap = parr
+ }
+ dojo.lang.forEach(pm, function (prop) {
+ if (dj_undef('start', prop)) {
+ if (prop.property != 'opacity') {
+ prop.start = parseInt(dojo.html.getComputedStyle(args.nodes[0], prop.property))
+ } else {
+ prop.start = dojo.html.getOpacity(args.nodes[0])
+ }
+ }
+ })
+ }
+ };
+ var _563 = function (_564) {
+ var _565 = [];
+ dojo.lang.forEach(_564, function (c) {
+ _565.push(Math.round(c))
+ });
+ return _565
+ };
+ var _567 = function (n, _569) {
+ n = dojo.byId(n);
+ if (!n || !n.style) {
+ return
+ }
+ for (var s in _569) {
+ try {
+ if (s == 'opacity') {
+ dojo.html.setOpacity(n, _569[s])
+ } else {
+ n.style[s] = _569[s]
+ }
+ } catch (e) {
+ dojo.debug(e)
+ }
+ }
+ };
+ var _56b = function (_56c) {
+ this._properties = _56c;
+ this.diffs = new Array(_56c.length);
+ dojo.lang.forEach(_56c, function (prop, i) {
+ if (dojo.lang.isFunction(prop.start)) {
+ prop.start = prop.start(prop, i)
+ }
+ if (dojo.lang.isFunction(prop.end)) {
+ prop.end = prop.end(prop, i)
+ }
+ if (dojo.lang.isArray(prop.start)) {
+ this.diffs[i] = null
+ } else {
+ if (prop.start instanceof dojo.gfx.color.Color) {
+ prop.startRgb = prop.start.toRgb();
+ prop.endRgb = prop.end.toRgb()
+ } else {
+ this.diffs[i] = prop.end - prop.start
+ }
+ }
+ }, this);
+ this.getValue = function (n) {
+ var ret = {};
+ dojo.lang.forEach(this._properties, function (prop, i) {
+ var _573 = null;
+ if (dojo.lang.isArray(prop.start)) {
+ } else {
+ if (prop.start instanceof dojo.gfx.color.Color) {
+ _573 = (prop.units || 'rgb') + '(';
+ for (var j = 0; j < prop.startRgb.length; j++) {
+ _573 += Math.round(((prop.endRgb[j] - prop.startRgb[j]) * n) + prop.startRgb[j]) + (j
+ < prop.startRgb.length
+ - 1 ? ',' : '')
+ }
+ _573 += ')'
+ } else {
+ _573 = ((this.diffs[i]) * n) + prop.start + (prop.property != 'opacity' ? prop.units || 'px' : '')
+ }
+ }
+ ret[dojo.html.toCamelCase(prop.property)] = _573
+ }, this);
+ return ret
+ }
+ };
+ var anim = new dojo.lfx.Animation({
+ beforeBegin: function () {
+ _55d(_55c);
+ anim.curve = new _56b(_55c.propertyMap)
+ }, onAnimate: function (_576) {
+ dojo.lang.forEach(_55c.nodes, function (node) {
+ _567(node, _576)
+ })
+ }
+ }, _55c.duration, null, _55c.easing);
+ if (_55b) {
+ for (var x in _55b) {
+ if (dojo.lang.isFunction(_55b[x])) {
+ anim.connect(x, anim, _55b[x])
+ }
+ }
+ }
+ return anim
+};
+dojo.lfx.html._makeFadeable = function (_579) {
+ var _57a = function (node) {
+ if (dojo.render.html.ie) {
+ if ((node.style.zoom.length == 0) && (dojo.html.getStyle(node, 'zoom') == 'normal')) {
+ node.style.zoom = '1'
+ }
+ if ((node.style.width.length == 0) && (dojo.html.getStyle(node, 'width') == 'auto')) {
+ node.style.width = 'auto'
+ }
+ }
+ };
+ if (dojo.lang.isArrayLike(_579)) {
+ dojo.lang.forEach(_579, _57a)
+ } else {
+ _57a(_579)
+ }
+};
+dojo.lfx.html.fade = function (_57c, _57d, _57e, _57f, _580) {
+ _57c = dojo.lfx.html._byId(_57c);
+ var _581 = {property: 'opacity'};
+ if (!dj_undef('start', _57d)) {
+ _581.start = _57d.start
+ } else {
+ _581.start = function () {
+ return dojo.html.getOpacity(_57c[0])
+ }
+ }
+ if (!dj_undef('end', _57d)) {
+ _581.end = _57d.end
+ } else {
+ dojo.raise('dojo.lfx.html.fade needs an end value')
+ }
+ var anim = dojo.lfx.propertyAnimation(_57c, [_581], _57e, _57f);
+ anim.connect('beforeBegin', function () {
+ dojo.lfx.html._makeFadeable(_57c)
+ });
+ if (_580) {
+ anim.connect('onEnd', function () {
+ _580(_57c, anim)
+ })
+ }
+ return anim
+};
+dojo.lfx.html.fadeIn = function (_583, _584, _585, _586) {
+ return dojo.lfx.html.fade(_583, {end: 1}, _584, _585, _586)
+};
+dojo.lfx.html.fadeOut = function (_587, _588, _589, _58a) {
+ return dojo.lfx.html.fade(_587, {end: 0}, _588, _589, _58a)
+};
+dojo.lfx.html.fadeShow = function (_58b, _58c, _58d, _58e) {
+ _58b = dojo.lfx.html._byId(_58b);
+ dojo.lang.forEach(_58b, function (node) {
+ dojo.html.setOpacity(node, 0)
+ });
+ var anim = dojo.lfx.html.fadeIn(_58b, _58c, _58d, _58e);
+ anim.connect('beforeBegin', function () {
+ if (dojo.lang.isArrayLike(_58b)) {
+ dojo.lang.forEach(_58b, dojo.html.show)
+ } else {
+ dojo.html.show(_58b)
+ }
+ });
+ return anim
+};
+dojo.lfx.html.fadeHide = function (_591, _592, _593, _594) {
+ var anim = dojo.lfx.html.fadeOut(_591, _592, _593, function () {
+ if (dojo.lang.isArrayLike(_591)) {
+ dojo.lang.forEach(_591, dojo.html.hide)
+ } else {
+ dojo.html.hide(_591)
+ }
+ if (_594) {
+ _594(_591, anim)
+ }
+ });
+ return anim
+};
+dojo.lfx.html.wipeIn = function (_596, _597, _598, _599) {
+ _596 = dojo.lfx.html._byId(_596);
+ var _59a = [];
+ dojo.lang.forEach(_596, function (node) {
+ var _59c = {};
+ var _59d, _59e, _59f;
+ with (node.style) {
+ _59d = top;
+ _59e = left;
+ _59f = position;
+ top = '-9999px';
+ left = '-9999px';
+ position = 'absolute';
+ display = ''
+ }
+ var _5a0 = dojo.html.getBorderBox(node).height;
+ with (node.style) {
+ top = _59d;
+ left = _59e;
+ position = _59f;
+ display = 'none'
+ }
+ var anim = dojo.lfx.propertyAnimation(node, {
+ 'height': {
+ start: 1, end: function () {
+ return _5a0
+ }
+ }
+ }, _597, _598);
+ anim.connect('beforeBegin', function () {
+ _59c.overflow = node.style.overflow;
+ _59c.height = node.style.height;
+ with (node.style) {
+ overflow = 'hidden';
+ height = '1px'
+ }
+ dojo.html.show(node)
+ });
+ anim.connect('onEnd', function () {
+ with (node.style) {
+ overflow = _59c.overflow;
+ height = _59c.height
+ }
+ if (_599) {
+ _599(node, anim)
+ }
+ });
+ _59a.push(anim)
+ });
+ return dojo.lfx.combine(_59a)
+};
+dojo.lfx.html.wipeOut = function (_5a2, _5a3, _5a4, _5a5) {
+ _5a2 = dojo.lfx.html._byId(_5a2);
+ var _5a6 = [];
+ dojo.lang.forEach(_5a2, function (node) {
+ var _5a8 = {};
+ var anim = dojo.lfx.propertyAnimation(node, {
+ 'height': {
+ start: function () {
+ return dojo.html.getContentBox(node).height
+ }, end: 1
+ }
+ }, _5a3, _5a4, {
+ 'beforeBegin': function () {
+ _5a8.overflow = node.style.overflow;
+ _5a8.height = node.style.height;
+ with (node.style) {
+ overflow = 'hidden'
+ }
+ dojo.html.show(node)
+ }, 'onEnd': function () {
+ dojo.html.hide(node);
+ with (node.style) {
+ overflow = _5a8.overflow;
+ height = _5a8.height
+ }
+ if (_5a5) {
+ _5a5(node, anim)
+ }
+ }
+ });
+ _5a6.push(anim)
+ });
+ return dojo.lfx.combine(_5a6)
+};
+dojo.lfx.html.slideTo = function (_5aa, _5ab, _5ac, _5ad, _5ae) {
+ _5aa = dojo.lfx.html._byId(_5aa);
+ var _5af = [];
+ var _5b0 = dojo.html.getComputedStyle;
+ if (dojo.lang.isArray(_5ab)) {
+ dojo.deprecated('dojo.lfx.html.slideTo(node, array)', 'use dojo.lfx.html.slideTo(node, {top: value, left: value});', '0.5');
+ _5ab = {top: _5ab[0], left: _5ab[1]}
+ }
+ dojo.lang.forEach(_5aa, function (node) {
+ var top = null;
+ var left = null;
+ var init = (function () {
+ var _5b5 = node;
+ return function () {
+ var pos = _5b0(_5b5, 'position');
+ top = (pos == 'absolute' ? node.offsetTop : parseInt(_5b0(node, 'top')) || 0);
+ left = (pos == 'absolute' ? node.offsetLeft : parseInt(_5b0(node, 'left')) || 0);
+ if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
+ var ret = dojo.html.abs(_5b5, true);
+ dojo.html.setStyleAttributes(_5b5, 'position:absolute;top:' + ret.y + 'px;left:' + ret.x + 'px;');
+ top = ret.y;
+ left = ret.x
+ }
+ }
+ })();
+ init();
+ var anim = dojo.lfx.propertyAnimation(node, {
+ 'top': {start: top, end: (_5ab.top || 0)}, 'left': {start: left, end: (_5ab.left || 0)}
+ }, _5ac, _5ad, {'beforeBegin': init});
+ if (_5ae) {
+ anim.connect('onEnd', function () {
+ _5ae(_5aa, anim)
+ })
+ }
+ _5af.push(anim)
+ });
+ return dojo.lfx.combine(_5af)
+};
+dojo.lfx.html.slideBy = function (_5b9, _5ba, _5bb, _5bc, _5bd) {
+ _5b9 = dojo.lfx.html._byId(_5b9);
+ var _5be = [];
+ var _5bf = dojo.html.getComputedStyle;
+ if (dojo.lang.isArray(_5ba)) {
+ dojo.deprecated('dojo.lfx.html.slideBy(node, array)', 'use dojo.lfx.html.slideBy(node, {top: value, left: value});', '0.5');
+ _5ba = {top: _5ba[0], left: _5ba[1]}
+ }
+ dojo.lang.forEach(_5b9, function (node) {
+ var top = null;
+ var left = null;
+ var init = (function () {
+ var _5c4 = node;
+ return function () {
+ var pos = _5bf(_5c4, 'position');
+ top = (pos == 'absolute' ? node.offsetTop : parseInt(_5bf(node, 'top')) || 0);
+ left = (pos == 'absolute' ? node.offsetLeft : parseInt(_5bf(node, 'left')) || 0);
+ if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
+ var ret = dojo.html.abs(_5c4, true);
+ dojo.html.setStyleAttributes(_5c4, 'position:absolute;top:' + ret.y + 'px;left:' + ret.x + 'px;');
+ top = ret.y;
+ left = ret.x
+ }
+ }
+ })();
+ init();
+ var anim = dojo.lfx.propertyAnimation(node, {
+ 'top': {start: top, end: top + (_5ba.top || 0)}, 'left': {start: left, end: left + (_5ba.left || 0)}
+ }, _5bb, _5bc).connect('beforeBegin', init);
+ if (_5bd) {
+ anim.connect('onEnd', function () {
+ _5bd(_5b9, anim)
+ })
+ }
+ _5be.push(anim)
+ });
+ return dojo.lfx.combine(_5be)
+};
+dojo.lfx.html.explode = function (_5c8, _5c9, _5ca, _5cb, _5cc) {
+ var h = dojo.html;
+ _5c8 = dojo.byId(_5c8);
+ _5c9 = dojo.byId(_5c9);
+ var _5ce = h.toCoordinateObject(_5c8, true);
+ var _5cf = document.createElement('div');
+ h.copyStyle(_5cf, _5c9);
+ if (_5c9.explodeClassName) {
+ _5cf.className = _5c9.explodeClassName
+ }
+ with (_5cf.style) {
+ position = 'absolute';
+ display = 'none';
+ var _5d0 = h.getStyle(_5c8, 'background-color');
+ backgroundColor = _5d0 ? _5d0.toLowerCase() : 'transparent';
+ backgroundColor = (backgroundColor == 'transparent') ? 'rgb(221, 221, 221)' : backgroundColor
+ }
+ dojo.body().appendChild(_5cf);
+ with (_5c9.style) {
+ visibility = 'hidden';
+ display = 'block'
+ }
+ var _5d1 = h.toCoordinateObject(_5c9, true);
+ with (_5c9.style) {
+ display = 'none';
+ visibility = 'visible'
+ }
+ var _5d2 = {opacity: {start: 0.5, end: 1}};
+ dojo.lang.forEach(['height', 'width', 'top', 'left'], function (type) {
+ _5d2[type] = {start: _5ce[type], end: _5d1[type]}
+ });
+ var anim = new dojo.lfx.propertyAnimation(_5cf, _5d2, _5ca, _5cb, {
+ 'beforeBegin': function () {
+ h.setDisplay(_5cf, 'block')
+ }, 'onEnd': function () {
+ h.setDisplay(_5c9, 'block');
+ _5cf.parentNode.removeChild(_5cf)
+ }
+ });
+ if (_5cc) {
+ anim.connect('onEnd', function () {
+ _5cc(_5c9, anim)
+ })
+ }
+ return anim
+};
+dojo.lfx.html.implode = function (_5d5, end, _5d7, _5d8, _5d9) {
+ var h = dojo.html;
+ _5d5 = dojo.byId(_5d5);
+ end = dojo.byId(end);
+ var _5db = dojo.html.toCoordinateObject(_5d5, true);
+ var _5dc = dojo.html.toCoordinateObject(end, true);
+ var _5dd = document.createElement('div');
+ dojo.html.copyStyle(_5dd, _5d5);
+ if (_5d5.explodeClassName) {
+ _5dd.className = _5d5.explodeClassName
+ }
+ dojo.html.setOpacity(_5dd, 0.3);
+ with (_5dd.style) {
+ position = 'absolute';
+ display = 'none';
+ backgroundColor = h.getStyle(_5d5, 'background-color').toLowerCase()
+ }
+ dojo.body().appendChild(_5dd);
+ var _5de = {opacity: {start: 1, end: 0.5}};
+ dojo.lang.forEach(['height', 'width', 'top', 'left'], function (type) {
+ _5de[type] = {start: _5db[type], end: _5dc[type]}
+ });
+ var anim = new dojo.lfx.propertyAnimation(_5dd, _5de, _5d7, _5d8, {
+ 'beforeBegin': function () {
+ dojo.html.hide(_5d5);
+ dojo.html.show(_5dd)
+ }, 'onEnd': function () {
+ _5dd.parentNode.removeChild(_5dd)
+ }
+ });
+ if (_5d9) {
+ anim.connect('onEnd', function () {
+ _5d9(_5d5, anim)
+ })
+ }
+ return anim
+};
+dojo.lfx.html.highlight = function (_5e1, _5e2, _5e3, _5e4, _5e5) {
+ _5e1 = dojo.lfx.html._byId(_5e1);
+ var _5e6 = [];
+ dojo.lang.forEach(_5e1, function (node) {
+ var _5e8 = dojo.html.getBackgroundColor(node);
+ var bg = dojo.html.getStyle(node, 'background-color').toLowerCase();
+ var _5ea = dojo.html.getStyle(node, 'background-image');
+ var _5eb = (bg == 'transparent' || bg == 'rgba(0, 0, 0, 0)');
+ while (_5e8.length > 3) {
+ _5e8.pop()
+ }
+ var rgb = new dojo.gfx.color.Color(_5e2);
+ var _5ed = new dojo.gfx.color.Color(_5e8);
+ var anim = dojo.lfx.propertyAnimation(node, {'background-color': {start: rgb, end: _5ed}}, _5e3, _5e4, {
+ 'beforeBegin': function () {
+ if (_5ea) {
+ node.style.backgroundImage = 'none'
+ }
+ node.style.backgroundColor = 'rgb(' + rgb.toRgb().join(',') + ')'
+ }, 'onEnd': function () {
+ if (_5ea) {
+ node.style.backgroundImage = _5ea
+ }
+ if (_5eb) {
+ node.style.backgroundColor = 'transparent'
+ }
+ if (_5e5) {
+ _5e5(node, anim)
+ }
+ }
+ });
+ _5e6.push(anim)
+ });
+ return dojo.lfx.combine(_5e6)
+};
+dojo.lfx.html.unhighlight = function (_5ef, _5f0, _5f1, _5f2, _5f3) {
+ _5ef = dojo.lfx.html._byId(_5ef);
+ var _5f4 = [];
+ dojo.lang.forEach(_5ef, function (node) {
+ var _5f6 = new dojo.gfx.color.Color(dojo.html.getBackgroundColor(node));
+ var rgb = new dojo.gfx.color.Color(_5f0);
+ var _5f8 = dojo.html.getStyle(node, 'background-image');
+ var anim = dojo.lfx.propertyAnimation(node, {'background-color': {start: _5f6, end: rgb}}, _5f1, _5f2, {
+ 'beforeBegin': function () {
+ if (_5f8) {
+ node.style.backgroundImage = 'none'
+ }
+ node.style.backgroundColor = 'rgb(' + _5f6.toRgb().join(',') + ')'
+ }, 'onEnd': function () {
+ if (_5f3) {
+ _5f3(node, anim)
+ }
+ }
+ });
+ _5f4.push(anim)
+ });
+ return dojo.lfx.combine(_5f4)
+};
+dojo.lang.mixin(dojo.lfx, dojo.lfx.html);
+dojo.kwCompoundRequire({browser: ['dojo.lfx.html'], dashboard: ['dojo.lfx.html']});
+dojo.provide('dojo.lfx.*');
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/accountException.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/accountException.jsp
new file mode 100644
index 00000000..308c7052
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/accountException.jsp
@@ -0,0 +1,44 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+
+
+
+
+ accountException
+
+
+
+
+
+ ${ message }
+
+
+
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/businessException.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/businessException.jsp
new file mode 100644
index 00000000..54abdded
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/businessException.jsp
@@ -0,0 +1,24 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+
+
+
+
+ businessException
+
+
+
+
+
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/dispatcher.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/dispatcher.jsp
new file mode 100644
index 00000000..3372bdc7
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/dispatcher.jsp
@@ -0,0 +1,13 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" %>
+
+
+
+
+ ${ pageContext.request.requestURI }
+
+
+
+你正在访问 ${ pageContext.request.requestURI }?${ pageContext.request.queryString }.
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/exception.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/exception.jsp
new file mode 100644
index 00000000..7e581362
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/exception.jsp
@@ -0,0 +1,24 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+
+
+
+
+ exception
+
+
+
+
+
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/image.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/image.jsp
new file mode 100644
index 00000000..b1608a46
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/image.jsp
@@ -0,0 +1,19 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" %>
+
+
+
+
+ Insert title here
+
+
+
+
+刷新
+直接访问
+
+
+request.getHeader("referer"): ${ header['referer'] }
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/index.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/index.jsp
new file mode 100644
index 00000000..ffc11dfa
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/index.jsp
@@ -0,0 +1,52 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+<%
+ String path = request.getContextPath();
+ String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
+%>
+
+
+
+
+ javaee-filter 首页
+
+
+
+javaee-filter 首页
+<%out.print("Server Ip:" + basePath);%>
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testCacheFilter.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testCacheFilter.jsp
new file mode 100644
index 00000000..77d55892
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testCacheFilter.jsp
@@ -0,0 +1,71 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" %>
+
+
+
+
+ Insert title here
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testCharacterEncodingFilter.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testCharacterEncodingFilter.jsp
new file mode 100644
index 00000000..f9da90a4
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testCharacterEncodingFilter.jsp
@@ -0,0 +1,28 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" %>
+
+
+
+
+ Insert title here
+
+
+
+
+您输入了 :
+${ param.text }
+
+
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testExceptionHandlerFilter.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testExceptionHandlerFilter.jsp
new file mode 100644
index 00000000..ef1f3304
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testExceptionHandlerFilter.jsp
@@ -0,0 +1,29 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" %>
+<%@ page import="io.github.dunwu.javaee.filter.exception.AccountException" %>
+<%@ page import="io.github.dunwu.javaee.filter.exception.BusinessException" %>
+<%
+ String action = request.getParameter("action");
+
+ if ("businessException".equals(action)) {
+ throw new BusinessException("业务操作失败. ");
+ } else if ("accountException".equals(action)) {
+ throw new AccountException("您需要登陆后再进行此项操作. ");
+ } else if ("exception".equals(action)) {
+ Integer.parseInt("");
+ }
+
+%>
+
+
+
+
+ exceptionHandler
+
+
+
+test BusinessException
+test AccountException
+test Exception
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testGZipFilter.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testGZipFilter.jsp
new file mode 100644
index 00000000..be254535
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testGZipFilter.jsp
@@ -0,0 +1,66 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" %>
+
+<%@page import="java.net.URL" %>
+<%@page import="java.net.URLConnection" %>
+<%@page import="java.text.NumberFormat" %>
+
+
+
+ Insert title here
+
+
+
+<%!
+ public void test(JspWriter out, String url) throws Exception {
+
+ // 模拟支持 GZIP 的浏览器
+ URLConnection connGzip = new URL(url).openConnection();
+ connGzip.setRequestProperty("Accept-Encoding", "gzip");
+ int lengthGzip = connGzip.getContentLength();
+
+ // 模拟不支持 GZIP 的浏览器
+ URLConnection connCommon = new URL(url).openConnection();
+ int lengthCommon = connCommon.getContentLength();
+
+ double rate = new Double(lengthGzip) / lengthCommon;
+
+ out.println("");
+ out.println(" ");
+ out.println(" 网址: " + url + " ");
+ out.println(" ");
+ out.println(" ");
+ out.println(" 压缩后:" + lengthGzip + " byte ");
+ out.println(" 压缩前:" + lengthCommon + " byte ");
+ out.println(" 比率:" + NumberFormat.getPercentInstance().format(rate) + " ");
+ out.println(" ");
+ out.println("
");
+ }
+%>
+<%
+ String[] urls = {"http://localhost:9899/views/js/dojo.js", "http://localhost:9899/views/images/image.jsp",
+ "http://localhost:9899/views/images/winter.jpg", "http://www.baidu.com", "http://www.google.cn",};
+ for (String url : urls) {
+ test(out, url);
+ }
+%>
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testOutputReplaceFilter.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testOutputReplaceFilter.jsp
new file mode 100644
index 00000000..7517a03a
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testOutputReplaceFilter.jsp
@@ -0,0 +1,19 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" %>
+
+
+
+
+ Insert title here
+
+
+
+Chna
+
+色情
+赌博
+情色
+
+www.baidu.com.cn
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testUploadFilter.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testUploadFilter.jsp
new file mode 100644
index 00000000..d6b602ce
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testUploadFilter.jsp
@@ -0,0 +1,43 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" %>
+
+<%@page import="java.io.File" %>
+
+
+
+ Insert title here
+
+
+
+
+header['Content-type'] = ${ header['Content-type'] }
+
+
+
+ <%= request.getParameter("text1") %>
+
+ <%
+ File file1 = (File) request.getAttribute("file1");
+ if (file1 != null) {
+ out.println(" 文件: " + file1 + ", 大小: " + file1.length());
+ }
+ %>
+
+ <%= request.getParameter("text2") %>
+
+ <%
+ File file2 = (File) request.getAttribute("file2");
+ if (file2 != null) {
+ out.println(" 文件: " + file2 + ", 大小: " + file2.length());
+ }
+ %>
+
+
+
+
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testWaterMarkFilter.jsp b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testWaterMarkFilter.jsp
new file mode 100644
index 00000000..99c5585c
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/jsp/testWaterMarkFilter.jsp
@@ -0,0 +1,16 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" %>
+
+
+
+
+ 图片过滤器示例
+
+
+
+
+刷新
+
+${ header }
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/xml/book.xml b/codes/javaee/javaee-filter/src/main/webapp/views/xml/book.xml
new file mode 100644
index 00000000..ab4bc9fc
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/xml/book.xml
@@ -0,0 +1,6 @@
+
+ Bruce
+ Java 编程思想
+ 计算机经典书籍系列丛书
+ $100.00
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/xml/book.xsl b/codes/javaee/javaee-filter/src/main/webapp/views/xml/book.xsl
new file mode 100644
index 00000000..409e24e3
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/xml/book.xsl
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+ 图书资料:
+
+
+ 作者:
+
+
+
+
+
+ 书名:
+
+
+
+
+
+ 类别:
+
+
+
+
+
+ 定价:
+
+
+
+
+
+
+
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/xml/demo.xml b/codes/javaee/javaee-filter/src/main/webapp/views/xml/demo.xml
new file mode 100644
index 00000000..82020867
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/xml/demo.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+ 你会Ajax吗
+
+
+
+
+
+
+
+
+ 我们用的是dojo框架的
+
+
+
+
+
+
+
+
+ response.setHeader("Content-Disposition",
+ "attachment;filename="
+ + URLEncoder.encode(newFile.getName(),
+ "UTF-8"));
+ response.setHeader("Connection", "close");
+ response.setContentLength((int)newFile.length());
+ resp
+
+
+
+
+
+
+
+
+
+ 先 out.clear()
+
+
+
+
+
+
+
+
+ ok
+
+
+
+
+
+
+
+
+
+ response.setContentLength((int)newFile.length());
+ response.setHeader("Content-Type",
+ "application/octet-stream");
+
+
+
+
+
+
+
+
+
+ application/vnd.ms-excel
+
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/xml/messageLog.xsl b/codes/javaee/javaee-filter/src/main/webapp/views/xml/messageLog.xsl
new file mode 100644
index 00000000..715ce717
Binary files /dev/null and b/codes/javaee/javaee-filter/src/main/webapp/views/xml/messageLog.xsl differ
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/xml/thinkInJava.xml b/codes/javaee/javaee-filter/src/main/webapp/views/xml/thinkInJava.xml
new file mode 100644
index 00000000..e3cb21c7
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/xml/thinkInJava.xml
@@ -0,0 +1,7 @@
+
+
+ Bruce
+ Java 编程思想
+ 计算机经典书籍系列丛书
+ $100.00
+
diff --git a/codes/javaee/javaee-filter/src/main/webapp/views/xml/xml.xsl b/codes/javaee/javaee-filter/src/main/webapp/views/xml/xml.xsl
new file mode 100644
index 00000000..3dd7c355
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/main/webapp/views/xml/xml.xsl
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/codes/javaee/javaee-filter/src/test/java/io/github/dunwu/javaee/server/FilterDemosBootstrap.java b/codes/javaee/javaee-filter/src/test/java/io/github/dunwu/javaee/server/FilterDemosBootstrap.java
new file mode 100644
index 00000000..fe36788e
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/test/java/io/github/dunwu/javaee/server/FilterDemosBootstrap.java
@@ -0,0 +1,35 @@
+package io.github.dunwu.javaee.server;
+
+import org.eclipse.jetty.server.Server;
+
+/**
+ * 快速启动 jetty 服务器,方便测试
+ *
+ * @author Zhang Peng
+ */
+public class FilterDemosBootstrap {
+
+ // private static int STARTUP_TYPE = JettyFactory.IDE_ECLIPSE;
+ private static int STARTUP_TYPE = JettyFactory.IDE_INTELLIJ;
+
+ public static void main(String[] args) throws Exception {
+ Server server = JettyFactory.initServer();
+ JettyFactory.initWebAppContext(server, STARTUP_TYPE);
+
+ try {
+ JettyFactory.startServer(server);
+
+ // 等待用户输入回车重载应用
+ while (true) {
+ char c = (char) System.in.read();
+ if (c == '\n') {
+ JettyFactory.reloadWebAppContext(server);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/test/java/io/github/dunwu/javaee/server/JettyFactory.java b/codes/javaee/javaee-filter/src/test/java/io/github/dunwu/javaee/server/JettyFactory.java
new file mode 100644
index 00000000..35d18b11
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/test/java/io/github/dunwu/javaee/server/JettyFactory.java
@@ -0,0 +1,131 @@
+package io.github.dunwu.javaee.server;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.webapp.WebAppClassLoader;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * JettyFactory 可以工作在 Eclipse 和 Intellij 中,用来启动 jetty 服务。 Intellij 并不支持jetty,所以要想类似eclipse一样的使用jetty,需要配置webdefault.xml。
+ *
+ * @author Zhang Peng
+ */
+@SuppressWarnings("all")
+public class JettyFactory {
+
+ public static final int IDE_ECLIPSE = 0;
+
+ public static final int IDE_INTELLIJ = 1;
+
+ public static final String ACTIVE_PROFILE = "spring.profiles.active";
+
+ public static final String DEFAULT_PROFILE = "spring.profiles.default";
+
+ public static final String DEVELOPMENT = "development";
+
+ private static final int PORT = 9527;
+
+ private static final String CONTEXT = "/";
+
+ private static final String RESOURCE_BASE_PATH = "src/main/webapp";
+
+ private static final String WEB_XML_PATH = "/WEB-INF/web.xml";
+
+ private static final String[] TLD_JAR_NAMES = new String[] { "sitemesh", "spring-webmvc", "tiles" };
+
+ private static final String WINDOWS_WEBDEFAULT_PATH = "jetty/webdefault.xml";
+
+ public static Server initServer() {
+ setProfileAsSystemProperty(DEVELOPMENT);
+ WebAppContext webAppContext = new WebAppContext();
+ Server server = new Server(PORT);
+ server.setHandler(webAppContext);
+ return server;
+ }
+
+ /**
+ * 在Spring启动前,设置profile的环境变量。
+ */
+ public static void setProfileAsSystemProperty(String profile) {
+ System.setProperty(ACTIVE_PROFILE, profile);
+ }
+
+ public static void initWebAppContext(Server server, int type) throws Exception {
+ System.out.println("[INFO] Application loading");
+ WebAppContext webAppContext = (WebAppContext) server.getHandler();
+ webAppContext.setContextPath(CONTEXT);
+ webAppContext.setResourceBase(getAbsolutePath() + RESOURCE_BASE_PATH);
+ webAppContext.setDescriptor(getAbsolutePath() + RESOURCE_BASE_PATH + WEB_XML_PATH);
+
+ if (IDE_INTELLIJ == type) {
+ webAppContext.setDefaultsDescriptor(WINDOWS_WEBDEFAULT_PATH);
+ supportJspAndSetTldJarNames(server, TLD_JAR_NAMES);
+ } else {
+ webAppContext.setParentLoaderPriority(true);
+ }
+
+ System.out.println("[INFO] Application loaded");
+ }
+
+ public static String getAbsolutePath() {
+ String path = null;
+ String folderPath = JettyFactory.class.getProtectionDomain().getCodeSource().getLocation().getPath()
+ .substring(1);
+ if (folderPath.indexOf("target") > 0) {
+ path = folderPath.substring(0, folderPath.indexOf("target"));
+ }
+ return path;
+ }
+
+ public static void supportJspAndSetTldJarNames(Server server, String... jarNames) {
+ WebAppContext context = (WebAppContext) server.getHandler();
+ // This webapp will use jsps and jstl. We need to enable the
+ // AnnotationConfiguration in
+ // order to correctly set up the jsp container
+ org.eclipse.jetty.webapp.Configuration.ClassList classlist = org.eclipse.jetty.webapp.Configuration.ClassList
+ .setServerDefault(server);
+ classlist.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
+ "org.eclipse.jetty.annotations.AnnotationConfiguration");
+ // Set the ContainerIncludeJarPattern so that jetty examines these container-path
+ // jars for
+ // tlds, web-fragments etc.
+ // If you omit the jar that contains the jstl .tlds, the jsp engine will scan for
+ // them
+ // instead.
+
+ List list = new ArrayList<>();
+ list.add(".*/[^/]*servlet-api-[^/]*\\.jar$");
+ list.add(".*/javax.servlet.jsp.jstl-.*\\.jar$");
+ list.add(".*/[^/]*taglibs.*\\.jar$");
+
+ for (String jarName : jarNames) {
+ String str = ".*/" + jarName + "-[^/]*\\.jar$";
+ list.add(str);
+ }
+
+ context.setAttribute("org.eclipse.jetty.io.github.dunwu.javaee.server.webapp.ContainerIncludeJarPattern",
+ StringUtils.join(list, '|'));
+ }
+
+ public static void reloadWebAppContext(Server server) throws Exception {
+ WebAppContext webAppContext = (WebAppContext) server.getHandler();
+ System.out.println("[INFO] Application reloading");
+ webAppContext.stop();
+ WebAppClassLoader classLoader = new WebAppClassLoader(webAppContext);
+ classLoader.addClassPath(getAbsolutePath() + "target/classes");
+ classLoader.addClassPath(getAbsolutePath() + "target/test-classes");
+ webAppContext.setClassLoader(classLoader);
+ webAppContext.start();
+ System.out.println("[INFO] Application reloaded");
+ }
+
+ public static void startServer(Server server) throws Exception {
+ System.out.println("[HINT] Don't forget to set -XX:MaxPermSize=128m");
+ server.start();
+ System.out.println("Server running at http://localhost:" + PORT + CONTEXT);
+ System.out.println("[HINT] Hit Enter to reload the application quickly");
+ }
+
+}
diff --git a/codes/javaee/javaee-filter/src/test/resources/jetty/webdefault.xml b/codes/javaee/javaee-filter/src/test/resources/jetty/webdefault.xml
new file mode 100644
index 00000000..b991d44c
--- /dev/null
+++ b/codes/javaee/javaee-filter/src/test/resources/jetty/webdefault.xml
@@ -0,0 +1,534 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Default web.xml file.
+ This file is applied to a Web application before it's own WEB_INF/web.xml file
+
+
+
+
+
+
+
+ org.eclipse.jetty.servlet.listener.ELContextCleaner
+
+
+
+
+
+
+
+ org.eclipse.jetty.servlet.listener.IntrospectorCleaner
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ default
+ org.eclipse.jetty.servlet.DefaultServlet
+
+ aliases
+ false
+
+
+ acceptRanges
+ true
+
+
+ dirAllowed
+ true
+
+
+ welcomeServlets
+ false
+
+
+ redirectWelcome
+ false
+
+
+ maxCacheSize
+ 256000000
+
+
+ maxCachedFileSize
+ 200000000
+
+
+ maxCachedFiles
+ 2048
+
+
+ gzip
+ false
+
+
+ etags
+ false
+
+
+ useFileMappedBuffer
+ false
+
+
+
+ 0
+
+
+
+ default
+ /
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ jsp
+ org.eclipse.jetty.jsp.JettyJspServlet
+
+ logVerbosityLevel
+ DEBUG
+
+
+ fork
+ false
+
+
+ xpoweredBy
+ false
+
+
+ compilerTargetVM
+ 1.7
+
+
+ compilerSourceVM
+ 1.7
+
+
+ 0
+
+
+
+ jsp
+ *.jsp
+ *.jspf
+ *.jspx
+ *.xsp
+ *.JSP
+ *.JSPF
+ *.JSPX
+ *.XSP
+
+
+
+
+
+
+
+ 30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ index.html
+ index.htm
+ index.jsp
+
+
+
+
+
+
+
+ ar
+ ISO-8859-6
+
+
+ be
+ ISO-8859-5
+
+
+ bg
+ ISO-8859-5
+
+
+ ca
+ ISO-8859-1
+
+
+ cs
+ ISO-8859-2
+
+
+ da
+ ISO-8859-1
+
+
+ de
+ ISO-8859-1
+
+
+ el
+ ISO-8859-7
+
+
+ en
+ ISO-8859-1
+
+
+ es
+ ISO-8859-1
+
+
+ et
+ ISO-8859-1
+
+
+ fi
+ ISO-8859-1
+
+
+ fr
+ ISO-8859-1
+
+
+ hr
+ ISO-8859-2
+
+
+ hu
+ ISO-8859-2
+
+
+ is
+ ISO-8859-1
+
+
+ it
+ ISO-8859-1
+
+
+ iw
+ ISO-8859-8
+
+
+ ja
+ Shift_JIS
+
+
+ ko
+ EUC-KR
+
+
+ lt
+ ISO-8859-2
+
+
+ lv
+ ISO-8859-2
+
+
+ mk
+ ISO-8859-5
+
+
+ nl
+ ISO-8859-1
+
+
+ no
+ ISO-8859-1
+
+
+ pl
+ ISO-8859-2
+
+
+ pt
+ ISO-8859-1
+
+
+ ro
+ ISO-8859-2
+
+
+ ru
+ ISO-8859-5
+
+
+ sh
+ ISO-8859-5
+
+
+ sk
+ ISO-8859-2
+
+
+ sl
+ ISO-8859-2
+
+
+ sq
+ ISO-8859-2
+
+
+ sr
+ ISO-8859-5
+
+
+ sv
+ ISO-8859-1
+
+
+ tr
+ ISO-8859-9
+
+
+ uk
+ ISO-8859-5
+
+
+ zh
+ GB2312
+
+
+ zh_TW
+ Big5
+
+
+
+
+
+
+
+
+ Disable TRACE
+ /
+ TRACE
+
+
+
+
+
+ Enable everything but TRACE
+ /
+ TRACE
+
+
+
+
+
diff --git a/codes/javaee/javaee-jsp/pom.xml b/codes/javaee/javaee-jsp/pom.xml
new file mode 100644
index 00000000..bed08254
--- /dev/null
+++ b/codes/javaee/javaee-jsp/pom.xml
@@ -0,0 +1,91 @@
+
+
+ 4.0.0
+
+
+ io.github.dunwu.javaee
+ javaee
+ 1.0.0
+
+
+ javaee-jsp
+ 1.0.0
+ war
+ javaee-jsp
+ JavaEE 学习笔记之 JSP
+
+
+ UTF-8
+ 1.7
+ ${java.version}
+ ${java.version}
+
+
+
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.slf4j
+ jcl-over-slf4j
+
+
+
+
+ commons-fileupload
+ commons-fileupload
+
+
+ commons-io
+ commons-io
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+ org.eclipse.jetty
+ jetty-webapp
+ test
+
+
+ org.eclipse.jetty
+ jetty-server
+ test
+
+
+ org.eclipse.jetty
+ jetty-annotations
+ test
+
+
+ org.eclipse.jetty
+ apache-jsp
+ test
+
+
+
+
+ junit
+ junit
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+
diff --git a/codes/javaee/javaee-jsp/src/main/java/com/sun/products/applet/demo/Graph.java b/codes/javaee/javaee-jsp/src/main/java/com/sun/products/applet/demo/Graph.java
new file mode 100644
index 00000000..7536980a
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/java/com/sun/products/applet/demo/Graph.java
@@ -0,0 +1,486 @@
+package com.sun.products.applet.demo;
+/*
+ * @(#)Graph.java 1.9 99/08/04
+ *
+ * Copyright (c) 1997, 1998 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
+ * modify and redistribute this software in source and binary code form,
+ * provided that i) this copyright notice and license appear on all copies of
+ * the software; and ii) Licensee does not utilize the software in a manner
+ * which is disparaging to Sun.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
+ * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
+ * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
+ * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
+ * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
+ * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * This software is not designed or intended for use in on-line control of
+ * aircraft, air traffic, aircraft navigation or aircraft communications; or in
+ * the design, construction, operation or maintenance of any nuclear
+ * facility. Licensee represents and warrants that it will not use or
+ * redistribute the Software for such purposes.
+ */
+
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.StringTokenizer;
+
+class Node {
+
+ double x;
+
+ double y;
+
+ double dx;
+
+ double dy;
+
+ boolean fixed;
+
+ String lbl;
+
+}
+
+class Edge {
+
+ int from;
+
+ int to;
+
+ double len;
+
+}
+
+class GraphPanel extends Panel implements Runnable, MouseListener, MouseMotionListener {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -6414075534397495418L;
+
+ final Color fixedColor = Color.red;
+
+ final Color selectColor = Color.pink;
+
+ final Color edgeColor = Color.black;
+
+ final Color nodeColor = new Color(250, 220, 100);
+
+ final Color stressColor = Color.darkGray;
+
+ final Color arcColor1 = Color.black;
+
+ final Color arcColor2 = Color.pink;
+
+ final Color arcColor3 = Color.red;
+
+ Graph graph;
+
+ int nnodes;
+
+ Node nodes[] = new Node[100];
+
+ int nedges;
+
+ Edge edges[] = new Edge[200];
+
+ Thread relaxer;
+
+ boolean stress;
+
+ boolean random;
+
+ Node pick;
+
+ boolean pickfixed;
+
+ Image offscreen;
+
+ Dimension offscreensize;
+
+ Graphics offgraphics;
+
+ GraphPanel(Graph graph) {
+ this.graph = graph;
+ addMouseListener(this);
+ }
+
+ void addEdge(String from, String to, int len) {
+ Edge e = new Edge();
+ e.from = findNode(from);
+ e.to = findNode(to);
+ e.len = len;
+ edges[nedges++] = e;
+ }
+
+ int findNode(String lbl) {
+ for (int i = 0; i < nnodes; i++) {
+ if (nodes[i].lbl.equals(lbl)) {
+ return i;
+ }
+ }
+ return addNode(lbl);
+ }
+
+ int addNode(String lbl) {
+ Node n = new Node();
+ n.x = 10 + 380 * Math.random();
+ n.y = 10 + 380 * Math.random();
+ n.lbl = lbl;
+ nodes[nnodes] = n;
+ return nnodes++;
+ }
+
+ @Override
+ public void run() {
+ Thread me = Thread.currentThread();
+ while (relaxer == me) {
+ relax();
+ if (random && (Math.random() < 0.03)) {
+ Node n = nodes[(int) (Math.random() * nnodes)];
+ if (!n.fixed) {
+ n.x += 100 * Math.random() - 50;
+ n.y += 100 * Math.random() - 50;
+ }
+ graph.play(graph.getCodeBase(), "audio/drip.au");
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+
+ synchronized void relax() {
+ for (int i = 0; i < nedges; i++) {
+ Edge e = edges[i];
+ double vx = nodes[e.to].x - nodes[e.from].x;
+ double vy = nodes[e.to].y - nodes[e.from].y;
+ double len = Math.sqrt(vx * vx + vy * vy);
+ len = (len == 0) ? .0001 : len;
+ double f = (edges[i].len - len) / (len * 3);
+ double dx = f * vx;
+ double dy = f * vy;
+
+ nodes[e.to].dx += dx;
+ nodes[e.to].dy += dy;
+ nodes[e.from].dx += -dx;
+ nodes[e.from].dy += -dy;
+ }
+
+ for (int i = 0; i < nnodes; i++) {
+ Node n1 = nodes[i];
+ double dx = 0;
+ double dy = 0;
+
+ for (int j = 0; j < nnodes; j++) {
+ if (i == j) {
+ continue;
+ }
+ Node n2 = nodes[j];
+ double vx = n1.x - n2.x;
+ double vy = n1.y - n2.y;
+ double len = vx * vx + vy * vy;
+ if (len == 0) {
+ dx += Math.random();
+ dy += Math.random();
+ } else if (len < 100 * 100) {
+ dx += vx / len;
+ dy += vy / len;
+ }
+ }
+ double dlen = dx * dx + dy * dy;
+ if (dlen > 0) {
+ dlen = Math.sqrt(dlen) / 2;
+ n1.dx += dx / dlen;
+ n1.dy += dy / dlen;
+ }
+ }
+
+ Dimension d = getSize();
+ for (int i = 0; i < nnodes; i++) {
+ Node n = nodes[i];
+ if (!n.fixed) {
+ n.x += Math.max(-5, Math.min(5, n.dx));
+ n.y += Math.max(-5, Math.min(5, n.dy));
+ }
+ if (n.x < 0) {
+ n.x = 0;
+ } else if (n.x > d.width) {
+ n.x = d.width;
+ }
+ if (n.y < 0) {
+ n.y = 0;
+ } else if (n.y > d.height) {
+ n.y = d.height;
+ }
+ n.dx /= 2;
+ n.dy /= 2;
+ }
+ repaint();
+ }
+
+ @Override
+ public synchronized void update(Graphics g) {
+ Dimension d = getSize();
+ if ((offscreen == null) || (d.width != offscreensize.width) || (d.height != offscreensize.height)) {
+ offscreen = createImage(d.width, d.height);
+ offscreensize = d;
+ if (offgraphics != null) {
+ offgraphics.dispose();
+ }
+ offgraphics = offscreen.getGraphics();
+ offgraphics.setFont(getFont());
+ }
+
+ offgraphics.setColor(getBackground());
+ offgraphics.fillRect(0, 0, d.width, d.height);
+ for (int i = 0; i < nedges; i++) {
+ Edge e = edges[i];
+ int x1 = (int) nodes[e.from].x;
+ int y1 = (int) nodes[e.from].y;
+ int x2 = (int) nodes[e.to].x;
+ int y2 = (int) nodes[e.to].y;
+ int len = (int) Math.abs(Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) - e.len);
+ offgraphics.setColor((len < 10) ? arcColor1 : (len < 20 ? arcColor2 : arcColor3));
+ offgraphics.drawLine(x1, y1, x2, y2);
+ if (stress) {
+ String lbl = String.valueOf(len);
+ offgraphics.setColor(stressColor);
+ offgraphics.drawString(lbl, x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2);
+ offgraphics.setColor(edgeColor);
+ }
+ }
+
+ FontMetrics fm = offgraphics.getFontMetrics();
+ for (int i = 0; i < nnodes; i++) {
+ paintNode(offgraphics, nodes[i], fm);
+ }
+ g.drawImage(offscreen, 0, 0, null);
+ }
+
+ public void paintNode(Graphics g, Node n, FontMetrics fm) {
+ int x = (int) n.x;
+ int y = (int) n.y;
+ g.setColor((n == pick) ? selectColor : (n.fixed ? fixedColor : nodeColor));
+ int w = fm.stringWidth(n.lbl) + 10;
+ int h = fm.getHeight() + 4;
+ g.fillRect(x - w / 2, y - h / 2, w, h);
+ g.setColor(Color.black);
+ g.drawRect(x - w / 2, y - h / 2, w - 1, h - 1);
+ g.drawString(n.lbl, x - (w - 10) / 2, (y - (h - 4) / 2) + fm.getAscent());
+ }
+
+ // 1.1 event handling
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ addMouseMotionListener(this);
+ double bestdist = Double.MAX_VALUE;
+ int x = e.getX();
+ int y = e.getY();
+ for (int i = 0; i < nnodes; i++) {
+ Node n = nodes[i];
+ double dist = (n.x - x) * (n.x - x) + (n.y - y) * (n.y - y);
+ if (dist < bestdist) {
+ pick = n;
+ bestdist = dist;
+ }
+ }
+ pickfixed = pick.fixed;
+ pick.fixed = true;
+ pick.x = x;
+ pick.y = y;
+ repaint();
+ e.consume();
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ removeMouseMotionListener(this);
+ if (pick != null) {
+ pick.x = e.getX();
+ pick.y = e.getY();
+ pick.fixed = pickfixed;
+ pick = null;
+ }
+ repaint();
+ e.consume();
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDragged(MouseEvent e) {
+ pick.x = e.getX();
+ pick.y = e.getY();
+ repaint();
+ e.consume();
+ }
+
+ @Override
+ public void mouseMoved(MouseEvent e) {
+ }
+
+ public void start() {
+ relaxer = new Thread(this);
+ relaxer.start();
+ }
+
+ public void stop() {
+ relaxer = null;
+ }
+
+}
+
+public class Graph extends Applet implements ActionListener, ItemListener {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 9208137208697128121L;
+
+ GraphPanel panel;
+
+ Panel controlPanel;
+
+ Button scramble = new Button("Scramble");
+
+ Button shake = new Button("Shake");
+
+ Checkbox stress = new Checkbox("Stress");
+
+ Checkbox random = new Checkbox("Random");
+
+ @Override
+ public void init() {
+ setLayout(new BorderLayout());
+
+ panel = new GraphPanel(this);
+ add("Center", panel);
+ controlPanel = new Panel();
+ add("South", controlPanel);
+
+ controlPanel.add(scramble);
+ scramble.addActionListener(this);
+ controlPanel.add(shake);
+ shake.addActionListener(this);
+ controlPanel.add(stress);
+ stress.addItemListener(this);
+ controlPanel.add(random);
+ random.addItemListener(this);
+
+ String edges = getParameter("edges");
+ for (StringTokenizer t = new StringTokenizer(edges, ","); t.hasMoreTokens(); ) {
+ String str = t.nextToken();
+ int i = str.indexOf('-');
+ if (i > 0) {
+ int len = 50;
+ int j = str.indexOf('/');
+ if (j > 0) {
+ len = Integer.valueOf(str.substring(j + 1)).intValue();
+ str = str.substring(0, j);
+ }
+ panel.addEdge(str.substring(0, i), str.substring(i + 1), len);
+ }
+ }
+ Dimension d = getSize();
+ String center = getParameter("center");
+ if (center != null) {
+ Node n = panel.nodes[panel.findNode(center)];
+ n.x = d.width / 2;
+ n.y = d.height / 2;
+ n.fixed = true;
+ }
+ }
+
+ @Override
+ public void destroy() {
+ remove(panel);
+ remove(controlPanel);
+ }
+
+ @Override
+ public void start() {
+ panel.start();
+ }
+
+ @Override
+ public void stop() {
+ panel.stop();
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ Object src = e.getSource();
+
+ if (src == scramble) {
+ play(getCodeBase(), "audio/computer.au");
+ Dimension d = getSize();
+ for (int i = 0; i < panel.nnodes; i++) {
+ Node n = panel.nodes[i];
+ if (!n.fixed) {
+ n.x = 10 + (d.width - 20) * Math.random();
+ n.y = 10 + (d.height - 20) * Math.random();
+ }
+ }
+ return;
+ }
+
+ if (src == shake) {
+ play(getCodeBase(), "audio/gong.au");
+ // Dimension d = getSize();
+ for (int i = 0; i < panel.nnodes; i++) {
+ Node n = panel.nodes[i];
+ if (!n.fixed) {
+ n.x += 80 * Math.random() - 40;
+ n.y += 80 * Math.random() - 40;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ Object src = e.getSource();
+ boolean on = e.getStateChange() == ItemEvent.SELECTED;
+ if (src == stress) {
+ panel.stress = on;
+ } else if (src == random) {
+ panel.random = on;
+ }
+ }
+
+ @Override
+ public String getAppletInfo() {
+ return "Title: GraphLayout \nAuthor: ";
+ }
+
+ @Override
+ public String[][] getParameterInfo() {
+ String[][] info = { { "edges", "delimited string",
+ "A comma-delimited list of all the edges. It takes the form of 'C-N1,C-N2,C-N3,C-NX,N1-N2/M12,N2-N3/M23,N3-NX/M3X,...' where C is the name of center node (see 'center' parameter) and NX is a node attached to the center node. For the edges connecting nodes to eachother (and not to the center node) you may (optionally) specify a length MXY separated from the edge name by a forward slash." },
+ { "center", "string", "The name of the center node." } };
+ return info;
+ }
+
+}
diff --git a/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/bean/Counter.java b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/bean/Counter.java
new file mode 100644
index 00000000..2b4108d5
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/bean/Counter.java
@@ -0,0 +1,15 @@
+package io.github.dunwu.javaee.jsp.bean;
+
+public class Counter {
+
+ private int count;
+
+ public int getCount() {
+ return ++count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+}
diff --git a/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/bean/Message.java b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/bean/Message.java
new file mode 100644
index 00000000..a15011f0
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/bean/Message.java
@@ -0,0 +1,15 @@
+package io.github.dunwu.javaee.jsp.bean;
+
+public class Message {
+
+ private String content;
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+}
diff --git a/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/bean/Person.java b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/bean/Person.java
new file mode 100644
index 00000000..a699495f
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/bean/Person.java
@@ -0,0 +1,35 @@
+package io.github.dunwu.javaee.jsp.bean;
+
+public class Person {
+
+ private String name;
+
+ private int age;
+
+ private String sex;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getSex() {
+ return sex;
+ }
+
+ public void setSex(String sex) {
+ this.sex = sex;
+ }
+
+}
diff --git a/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/IpUtil.java b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/IpUtil.java
new file mode 100644
index 00000000..5b3c47f9
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/IpUtil.java
@@ -0,0 +1,16 @@
+package io.github.dunwu.javaee.jsp.util;
+
+import io.github.dunwu.javaee.jsp.util.ip.IPSeeker;
+
+public class IpUtil {
+
+ public static String getIpAddress(String ip) {
+ try {
+ return IPSeeker.getInstance().getAddress(ip);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "δ֪����";
+ }
+
+}
diff --git a/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/IPEntry.java b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/IPEntry.java
new file mode 100644
index 00000000..d662262e
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/IPEntry.java
@@ -0,0 +1,51 @@
+package io.github.dunwu.javaee.jsp.util.ip;
+
+/*
+ * LumaQQ - Java QQ Client
+ *
+ * Copyright (C) 2004 luma
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ *
+ * һ��IP��Χ��¼�������������Һ�����Ҳ������ʼIP�ͽ���IP
+ *
+ *
+ * @author �����
+ */
+public class IPEntry {
+
+ public String beginIp;
+
+ public String endIp;
+
+ public String country;
+
+ public String area;
+
+ /**
+ * ���캯��
+ */
+ public IPEntry() {
+ beginIp = endIp = country = area = "";
+ }
+
+ public String toString() {
+ return this.area + " " + this.country + " IP��Χ:" + this.beginIp + "-" + this.endIp;
+ }
+
+}
diff --git a/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/IPSeeker.java b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/IPSeeker.java
new file mode 100644
index 00000000..06fee4d4
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/IPSeeker.java
@@ -0,0 +1,709 @@
+package io.github.dunwu.javaee.jsp.util.ip;
+
+/*
+ * LumaQQ - Java QQ Client
+ *
+ * Copyright (C) 2004 luma
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteOrder;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+/**
+ *
+ * ������ȡQQwry.dat�ļ����Ը���ip��ú���λ�ã�QQwry.dat�ĸ�ʽ��
+ * һ. �ļ�ͷ����8�ֽ�
+ * 1. ��һ����ʼIP�ľ���ƫ�ƣ� 4�ֽ�
+ * 2. ���һ����ʼIP�ľ���ƫ�ƣ� 4�ֽ�
+ * ��. "������ַ/����/����"��¼��
+ * ���ֽ�ip��ַ�����ÿһ����¼�ֳ���������
+ * 1. ���Ҽ�¼
+ * 2. ������¼
+ * ���ǵ�����¼�Dz�һ���еġ����ҹ��Ҽ�¼�͵�����¼����������ʽ
+ * 1. ��0�������ַ���
+ * 2. 4���ֽڣ�һ���ֽڿ���Ϊ0x1��0x2
+ * a. Ϊ0x1ʱ����ʾ�ھ���ƫ�ƺ���һ������ļ�¼��ע���Ǿ���ƫ��֮���������ĸ��ֽ�֮��
+ * b. Ϊ0x2ʱ����ʾ�ھ���ƫ�ƺ�û�������¼
+ * ����Ϊ0x1����0x2���������ֽڶ���ʵ�ʹ��������ļ��ھ���ƫ��
+ * ����ǵ�����¼��0x1��0x2�ĺ��岻����������������������ֽڣ�Ҳ�϶��Ǹ���3���ֽ�ƫ�ƣ��������
+ * ��Ϊ0��β�ַ���
+ * ��. "��ʼ��ַ/������ַƫ��"��¼��
+ * 1. ÿ����¼7�ֽڣ�������ʼ��ַ��С��������
+ * a. ��ʼIP��ַ��4�ֽ�
+ * b. ����ip��ַ�ľ���ƫ�ƣ�3�ֽ�
+ *
+ * ע�⣬����ļ����ip��ַ�����е�ƫ����������little-endian��ʽ����java�Dz���
+ * big-endian��ʽ�ģ�Ҫע��ת��
+ *
+ *
+ * @author �����
+ */
+public class IPSeeker {
+
+ private static final File IP_FILE = new File(IPSeeker.class.getResource("").getFile(), "QQWry.dat");
+
+ // һЩ�̶������������¼���ȵȵ�
+ private static final int IP_RECORD_LENGTH = 7;
+
+ private static final byte AREA_FOLLOWED = 0x01;
+
+ private static final byte NO_AREA = 0x2;
+
+ // ��һģʽʵ��
+ private static IPSeeker instance = new IPSeeker();
+
+ // ������Ϊcache����ѯһ��ipʱ���Ȳ鿴cache���Լ��ٲ���Ҫ���ظ�����
+ private Hashtable ipCache;
+
+ // ����������
+ private RandomAccessFile ipFile;
+
+ // �ڴ�ӳ���ļ�
+ private MappedByteBuffer mbb;
+
+ // ��ʼ�����Ŀ�ʼ�ͽ����ľ���ƫ��
+ private long ipBegin, ipEnd;
+
+ // Ϊ���Ч�ʶ����õ���ʱ����
+ private IPLocation loc;
+
+ private byte[] buf;
+
+ private byte[] b4;
+
+ private byte[] b3;
+
+ /**
+ * ˽�й��캯��
+ */
+ private IPSeeker() {
+ ipCache = new Hashtable();
+ loc = new IPLocation();
+ buf = new byte[100];
+ b4 = new byte[4];
+ b3 = new byte[3];
+ try {
+ ipFile = new RandomAccessFile(IP_FILE, "r");
+ } catch (FileNotFoundException e) {
+ System.out.println(IP_FILE.getAbsolutePath());
+ System.out.println(IP_FILE);
+ System.out.println("IP��ַ��Ϣ�ļ�û���ҵ���IP��ʾ���ܽ���ʹ��");
+ ipFile = null;
+ }
+ // ������ļ��ɹ�����ȡ�ļ�ͷ��Ϣ
+ if (ipFile != null) {
+ try {
+ ipBegin = readLong4(0);
+ ipEnd = readLong4(4);
+ if (ipBegin == -1 || ipEnd == -1) {
+ ipFile.close();
+ ipFile = null;
+ }
+ } catch (IOException e) {
+ System.out.println("IP��ַ��Ϣ�ļ���ʽ�д���IP��ʾ���ܽ���ʹ��");
+ ipFile = null;
+ }
+ }
+ }
+
+ /**
+ * ��offsetλ�ö�ȡ4���ֽ�Ϊһ��long����ΪjavaΪbig-endian��ʽ������û�취 ������ôһ����������ת��
+ *
+ * @param offset
+ * @return ��ȡ��longֵ������-1��ʾ��ȡ�ļ�ʧ��
+ */
+ private long readLong4(long offset) {
+ long ret = 0;
+ try {
+ ipFile.seek(offset);
+ ret |= (ipFile.readByte() & 0xFF);
+ ret |= ((ipFile.readByte() << 8) & 0xFF00);
+ ret |= ((ipFile.readByte() << 16) & 0xFF0000);
+ ret |= ((ipFile.readByte() << 24) & 0xFF000000);
+ return ret;
+ } catch (IOException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * @return ��һʵ��
+ */
+ public static IPSeeker getInstance() {
+ return instance;
+ }
+
+ /**
+ * ����һ���ص�IJ���ȫ���֣��õ�һϵ�а���s�Ӵ���IP��Χ��¼
+ *
+ * @param s �ص��Ӵ�
+ * @return ����IPEntry���͵�List
+ */
+ public List getIPEntriesDebug(String s) {
+ List ret = new ArrayList();
+ long endOffset = ipEnd + 4;
+ for (long offset = ipBegin + 4; offset <= endOffset; offset += IP_RECORD_LENGTH) {
+ // ��ȡ����IPƫ��
+ long temp = readLong3(offset);
+ // ���temp������-1����ȡIP�ĵص���Ϣ
+ if (temp != -1) {
+ IPLocation loc = getIPLocation(temp);
+ // �ж��Ƿ�����ص����������s�Ӵ�����������ˣ���������¼��List�У����û�У�����
+ if (loc.country.indexOf(s) != -1 || loc.area.indexOf(s) != -1) {
+ IPEntry entry = new IPEntry();
+ entry.country = loc.country;
+ entry.area = loc.area;
+ // �õ���ʼIP
+ readIP(offset - 4, b4);
+ entry.beginIp = Utils.getIpStringFromBytes(b4);
+ // �õ�����IP
+ readIP(temp, b4);
+ entry.endIp = Utils.getIpStringFromBytes(b4);
+ // ��Ӹü�¼
+ ret.add(entry);
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * ��offsetλ�ö�ȡ3���ֽ�Ϊһ��long����ΪjavaΪbig-endian��ʽ������û�취 ������ôһ����������ת��
+ *
+ * @param offset
+ * @return ��ȡ��longֵ������-1��ʾ��ȡ�ļ�ʧ��
+ */
+ private long readLong3(long offset) {
+ long ret = 0;
+ try {
+ ipFile.seek(offset);
+ ipFile.readFully(b3);
+ ret |= (b3[0] & 0xFF);
+ ret |= ((b3[1] << 8) & 0xFF00);
+ ret |= ((b3[2] << 16) & 0xFF0000);
+ return ret;
+ } catch (IOException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * ����һ��ip���ҵ�����¼��ƫ�ƣ�����һ��IPLocation�ṹ
+ *
+ * @param offset
+ * @return
+ */
+ private IPLocation getIPLocation(long offset) {
+ try {
+ // ����4�ֽ�ip
+ ipFile.seek(offset + 4);
+ // ��ȡ��һ���ֽ��ж��Ƿ��־�ֽ�
+ byte b = ipFile.readByte();
+ if (b == AREA_FOLLOWED) {
+ // ��ȡ����ƫ��
+ long countryOffset = readLong3();
+ // ��ת��ƫ�ƴ�
+ ipFile.seek(countryOffset);
+ // �ټ��һ�α�־�ֽڣ���Ϊ���ʱ������ط���Ȼ�����Ǹ��ض���
+ b = ipFile.readByte();
+ if (b == NO_AREA) {
+ loc.country = readString(readLong3());
+ ipFile.seek(countryOffset + 4);
+ } else {
+ loc.country = readString(countryOffset);
+ }
+ // ��ȡ������־
+ loc.area = readArea(ipFile.getFilePointer());
+ } else if (b == NO_AREA) {
+ loc.country = readString(readLong3());
+ loc.area = readArea(offset + 8);
+ } else {
+ loc.country = readString(ipFile.getFilePointer() - 1);
+ loc.area = readArea(ipFile.getFilePointer());
+ }
+ return loc;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * ��offsetλ�ö�ȡ�ĸ��ֽڵ�ip��ַ����ip�����У���ȡ���ipΪbig-endian��ʽ������ �ļ�����little-endian��ʽ���������ת��
+ *
+ * @param offset
+ * @param ip
+ */
+ private void readIP(long offset, byte[] ip) {
+ try {
+ ipFile.seek(offset);
+ ipFile.readFully(ip);
+ byte temp = ip[0];
+ ip[0] = ip[3];
+ ip[3] = temp;
+ temp = ip[1];
+ ip[1] = ip[2];
+ ip[2] = temp;
+ } catch (IOException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * �ӵ�ǰλ�ö�ȡ3���ֽ�ת����long
+ *
+ * @return
+ */
+ private long readLong3() {
+ long ret = 0;
+ try {
+ ipFile.readFully(b3);
+ ret |= (b3[0] & 0xFF);
+ ret |= ((b3[1] << 8) & 0xFF00);
+ ret |= ((b3[2] << 16) & 0xFF0000);
+ return ret;
+ } catch (IOException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * ��offsetƫ�ƴ���ȡһ����0�������ַ���
+ *
+ * @param offset
+ * @return ��ȡ���ַ����������ؿ��ַ���
+ */
+ private String readString(long offset) {
+ try {
+ ipFile.seek(offset);
+ int i;
+ for (i = 0, buf[i] = ipFile.readByte(); buf[i] != 0; buf[++i] = ipFile.readByte())
+ ;
+ if (i != 0) {
+ return Utils.getString(buf, 0, i, "GBK");
+ }
+ } catch (IOException e) {
+ System.out.println(e.getMessage());
+ }
+ return "";
+ }
+
+ /**
+ * ��offsetƫ�ƿ�ʼ����������ֽڣ�����һ��������
+ *
+ * @param offset
+ * @return �������ַ���
+ * @throws IOException
+ */
+ private String readArea(long offset) throws IOException {
+ ipFile.seek(offset);
+ byte b = ipFile.readByte();
+ if (b == 0x01 || b == 0x02) {
+ long areaOffset = readLong3(offset + 1);
+ if (areaOffset == 0) {
+ return "δ֪����";
+ } else {
+ return readString(areaOffset);
+ }
+ } else {
+ return readString(offset);
+ }
+ }
+
+ /**
+ * ����һ���ص�IJ���ȫ���֣��õ�һϵ�а���s�Ӵ���IP��Χ��¼
+ *
+ * @param s �ص��Ӵ�
+ * @return ����IPEntry���͵�List
+ */
+ public List getIPEntries(String s) {
+ List ret = new ArrayList();
+ try {
+ // ӳ��IP��Ϣ�ļ����ڴ���
+ if (mbb == null) {
+ FileChannel fc = ipFile.getChannel();
+ mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, ipFile.length());
+ mbb.order(ByteOrder.LITTLE_ENDIAN);
+ }
+
+ int endOffset = (int) ipEnd;
+ for (int offset = (int) ipBegin + 4; offset <= endOffset; offset += IP_RECORD_LENGTH) {
+ int temp = readInt3(offset);
+ if (temp != -1) {
+ IPLocation loc = getIPLocation(temp);
+ // �ж��Ƿ�����ص����������s�Ӵ�����������ˣ���������¼��List�У����û�У�����
+ if (loc.country.indexOf(s) != -1 || loc.area.indexOf(s) != -1) {
+ IPEntry entry = new IPEntry();
+ entry.country = loc.country;
+ entry.area = loc.area;
+ // �õ���ʼIP
+ readIP(offset - 4, b4);
+ entry.beginIp = Utils.getIpStringFromBytes(b4);
+ // �õ�����IP
+ readIP(temp, b4);
+ entry.endIp = Utils.getIpStringFromBytes(b4);
+ // ��Ӹü�¼
+ ret.add(entry);
+ }
+ }
+ }
+ } catch (IOException e) {
+ System.out.println(e.getMessage());
+ }
+ return ret;
+ }
+
+ /**
+ * ���ڴ�ӳ���ļ���offsetλ�ÿ�ʼ��3���ֽڶ�ȡһ��int
+ *
+ * @param offset
+ * @return
+ */
+ private int readInt3(int offset) {
+ mbb.position(offset);
+ return mbb.getInt() & 0x00FFFFFF;
+ }
+
+ /**
+ * @param offset
+ * @return
+ */
+ private IPLocation getIPLocation(int offset) {
+ // ����4�ֽ�ip
+ mbb.position(offset + 4);
+ // ��ȡ��һ���ֽ��ж��Ƿ��־�ֽ�
+ byte b = mbb.get();
+ if (b == AREA_FOLLOWED) {
+ // ��ȡ����ƫ��
+ int countryOffset = readInt3();
+ // ��ת��ƫ�ƴ�
+ mbb.position(countryOffset);
+ // �ټ��һ�α�־�ֽڣ���Ϊ���ʱ������ط���Ȼ�����Ǹ��ض���
+ b = mbb.get();
+ if (b == NO_AREA) {
+ loc.country = readString(readInt3());
+ mbb.position(countryOffset + 4);
+ } else {
+ loc.country = readString(countryOffset);
+ }
+ // ��ȡ������־
+ loc.area = readArea(mbb.position());
+ } else if (b == NO_AREA) {
+ loc.country = readString(readInt3());
+ loc.area = readArea(offset + 8);
+ } else {
+ loc.country = readString(mbb.position() - 1);
+ loc.area = readArea(mbb.position());
+ }
+ return loc;
+ }
+
+ /**
+ * ��offsetλ�ö�ȡ�ĸ��ֽڵ�ip��ַ����ip�����У���ȡ���ipΪbig-endian��ʽ������ �ļ�����little-endian��ʽ���������ת��
+ *
+ * @param offset
+ * @param ip
+ */
+ private void readIP(int offset, byte[] ip) {
+ mbb.position(offset);
+ mbb.get(ip);
+ byte temp = ip[0];
+ ip[0] = ip[3];
+ ip[3] = temp;
+ temp = ip[1];
+ ip[1] = ip[2];
+ ip[2] = temp;
+ }
+
+ /**
+ * ���ڴ�ӳ���ļ��ĵ�ǰλ�ÿ�ʼ��3���ֽڶ�ȡһ��int
+ *
+ * @return
+ */
+ private int readInt3() {
+ return mbb.getInt() & 0x00FFFFFF;
+ }
+
+ /**
+ * ���ڴ�ӳ���ļ���offsetλ�õõ�һ��0��β�ַ���
+ *
+ * @param offset
+ * @return
+ */
+ private String readString(int offset) {
+ try {
+ mbb.position(offset);
+ int i;
+ for (i = 0, buf[i] = mbb.get(); buf[i] != 0; buf[++i] = mbb.get())
+ ;
+ if (i != 0) {
+ return Utils.getString(buf, 0, i, "GBK");
+ }
+ } catch (IllegalArgumentException e) {
+ System.out.println(e.getMessage());
+ }
+ return "";
+ }
+
+ /**
+ * @param offset
+ * @return
+ */
+ private String readArea(int offset) {
+ mbb.position(offset);
+ byte b = mbb.get();
+ if (b == 0x01 || b == 0x02) {
+ int areaOffset = readInt3();
+ if (areaOffset == 0) {
+ return "δ֪����";
+ } else {
+ return readString(areaOffset);
+ }
+ } else {
+ return readString(offset);
+ }
+ }
+
+ /**
+ * ����IP�õ�������
+ *
+ * @param ip ip���ֽ�������ʽ
+ * @return �������ַ���
+ */
+ public String getCountry(byte[] ip) {
+ // ���ip��ַ�ļ��Ƿ�����
+ if (ipFile == null) {
+ return "�����IP���ݿ��ļ�";
+ }
+ // ����ip��ת��ip�ֽ�����Ϊ�ַ�����ʽ
+ String ipStr = Utils.getIpStringFromBytes(ip);
+ // �ȼ��cache���Ƿ��Ѿ����������ip�Ľ����û���������ļ�
+ if (ipCache.containsKey(ipStr)) {
+ IPLocation loc = (IPLocation) ipCache.get(ipStr);
+ return loc.country;
+ } else {
+ IPLocation loc = getIPLocation(ip);
+ ipCache.put(ipStr, loc.getCopy());
+ return loc.country;
+ }
+ }
+
+ /**
+ * ����ip����ip��Ϣ�ļ����õ�IPLocation�ṹ����������ip���������Աip�еõ�
+ *
+ * @param ip Ҫ��ѯ��IP
+ * @return IPLocation�ṹ
+ */
+ private IPLocation getIPLocation(byte[] ip) {
+ IPLocation info = null;
+ long offset = locateIP(ip);
+ if (offset != -1) {
+ info = getIPLocation(offset);
+ }
+ if (info == null) {
+ info = new IPLocation();
+ info.country = "δ֪����";
+ info.area = "δ֪����";
+ }
+ return info;
+ }
+
+ /**
+ * �������������ip�����ݣ���λ���������ip���ҵ����ļ�¼��������һ������ƫ�� ����ʹ�ö��ַ����ҡ�
+ *
+ * @param ip Ҫ��ѯ��IP
+ * @return ����ҵ��ˣ����ؽ���IP��ƫ�ƣ����û���ҵ�������-1
+ */
+ private long locateIP(byte[] ip) {
+ long m = 0;
+ int r;
+ // �Ƚϵ�һ��ip��
+ readIP(ipBegin, b4);
+ r = compareIP(ip, b4);
+ if (r == 0) {
+ return ipBegin;
+ } else if (r < 0) {
+ return -1;
+ }
+ // ��ʼ��������
+ for (long i = ipBegin, j = ipEnd; i < j; ) {
+ m = getMiddleOffset(i, j);
+ readIP(m, b4);
+ r = compareIP(ip, b4);
+ // log.debug(Utils.getIpStringFromBytes(b));
+ if (r > 0) {
+ i = m;
+ } else if (r < 0) {
+ if (m == j) {
+ j -= IP_RECORD_LENGTH;
+ m = j;
+ } else {
+ j = m;
+ }
+ } else {
+ return readLong3(m + 4);
+ }
+ }
+ // ���ѭ�������ˣ���ôi��j�ض�����ȵģ������¼Ϊ����ܵļ�¼�����Dz���
+ // �϶����ǣ���Ҫ���һ�£�����ǣ��ͷ��ؽ�����ַ���ľ���ƫ��
+ m = readLong3(m + 4);
+ readIP(m, b4);
+ r = compareIP(ip, b4);
+ if (r <= 0) {
+ return m;
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * �����Աip��beginIp�Ƚϣ�ע�����beginIp��big-endian��
+ *
+ * @param ip Ҫ��ѯ��IP
+ * @param beginIp �ͱ���ѯIP��Ƚϵ�IP
+ * @return ��ȷ���0��ip����beginIp��1��С�ڷ���-1��
+ */
+ private int compareIP(byte[] ip, byte[] beginIp) {
+ for (int i = 0; i < 4; i++) {
+ int r = compareByte(ip[i], beginIp[i]);
+ if (r != 0) {
+ return r;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * �õ�beginƫ�ƺ�endƫ���м�λ�ü�¼��ƫ��
+ *
+ * @param begin
+ * @param end
+ * @return
+ */
+ private long getMiddleOffset(long begin, long end) {
+ long records = (end - begin) / IP_RECORD_LENGTH;
+ records >>= 1;
+ if (records == 0) {
+ records = 1;
+ }
+ return begin + records * IP_RECORD_LENGTH;
+ }
+
+ /**
+ * ������byte�������������бȽ�
+ *
+ * @param b1
+ * @param b2
+ * @return ��b1����b2��1����ȷ���0��С�ڷ���-1
+ */
+ private int compareByte(byte b1, byte b2) {
+ if ((b1 & 0xFF) > (b2 & 0xFF)) // �Ƚ��Ƿ����
+ {
+ return 1;
+ } else if ((b1 ^ b2) == 0)// �ж��Ƿ����
+ {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * ����IP�õ�������
+ *
+ * @param ip ip���ֽ�������ʽ
+ * @return �������ַ���
+ */
+ public String getArea(byte[] ip) {
+ // ���ip��ַ�ļ��Ƿ�����
+ if (ipFile == null) {
+ return "�����IP���ݿ��ļ�";
+ }
+ // ����ip��ת��ip�ֽ�����Ϊ�ַ�����ʽ
+ String ipStr = Utils.getIpStringFromBytes(ip);
+ // �ȼ��cache���Ƿ��Ѿ����������ip�Ľ����û���������ļ�
+ if (ipCache.containsKey(ipStr)) {
+ IPLocation loc = (IPLocation) ipCache.get(ipStr);
+ return loc.area;
+ } else {
+ IPLocation loc = getIPLocation(ip);
+ ipCache.put(ipStr, loc.getCopy());
+ return loc.area;
+ }
+ }
+
+ public String getAddress(String ip) {
+ String country = getCountry(ip).equals(" CZ88.NET") ? "" : getCountry(ip);
+ String area = getArea(ip).equals(" CZ88.NET") ? "" : getArea(ip);
+ String address = country + " " + area;
+ return address.trim();
+ }
+
+ /**
+ * ����IP�õ�������
+ *
+ * @param ip IP���ַ�����ʽ
+ * @return �������ַ���
+ */
+ public String getCountry(String ip) {
+ return getCountry(Utils.getIpByteArrayFromString(ip));
+ }
+
+ /**
+ * ����IP�õ�������
+ *
+ * @param ip IP���ַ�����ʽ
+ * @return �������ַ���
+ */
+ public String getArea(String ip) {
+ return getArea(Utils.getIpByteArrayFromString(ip));
+ }
+
+ /**
+ *
+ * ������װip�����Ϣ��Ŀǰֻ�������ֶΣ�ip���ڵĹ��Һ͵���
+ *
+ *
+ * @author �����
+ */
+ private class IPLocation {
+
+ public String country;
+
+ public String area;
+
+ public IPLocation() {
+ country = area = "";
+ }
+
+ public IPLocation getCopy() {
+ IPLocation ret = new IPLocation();
+ ret.country = country;
+ ret.area = area;
+ return ret;
+ }
+
+ }
+
+}
diff --git a/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/QQWry.dat b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/QQWry.dat
new file mode 100644
index 00000000..a77baf0a
Binary files /dev/null and b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/QQWry.dat differ
diff --git a/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/Test.java b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/Test.java
new file mode 100644
index 00000000..e0ce37ce
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/Test.java
@@ -0,0 +1,35 @@
+package io.github.dunwu.javaee.jsp.util.ip;
+
+import java.util.List;
+
+/**
+ * @author LJ-silver
+ */
+
+public class Test {
+
+ public static void main(String[] args) {
+
+ args = new String[] { "ip", "9.128.2.1" };
+
+ IPSeeker seeker = IPSeeker.getInstance();
+
+ if (args.length == 2) {
+ if ("ip".equals(args[0])) {
+ System.out.println(args[0] + "�����ڵ�ַ��:" + seeker.getAddress(args[1]));
+ System.out.println(args[0] + "�����ڵ�ַ������:" + seeker.getCountry(args[1]));
+ } else if ("address".equals(args[0])) {
+ List a = seeker.getIPEntries(args[1]);
+ System.out.println(args[0] + ":");
+ for (int i = 0; i < a.size(); i++) {
+ System.out.println(a.get(i).toString());
+ }
+ } else {
+ System.out.println("usage:java Test ip/address yourIpString/yourAddressString");
+ }
+ } else {
+ System.out.println("usage:java Test ip/address yourIpString/yourAddressString");
+ }
+ }
+
+}
diff --git a/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/Utils.java b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/Utils.java
new file mode 100644
index 00000000..2fc31152
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/java/io/github/dunwu/javaee/jsp/util/ip/Utils.java
@@ -0,0 +1,106 @@
+package io.github.dunwu.javaee.jsp.util.ip;
+
+/*
+ * Created on 2004-8-4
+ *
+ */
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @author LJ-silver
+ */
+public class Utils {
+
+ public static void main(String args[]) {
+ byte[] a = getIpByteArrayFromString(args[0]);
+ for (int i = 0; i < a.length; i++)
+ System.out.println(a[i]);
+ System.out.println(getIpStringFromBytes(a));
+ }
+
+ /**
+ * ��ip���ַ�����ʽ�õ��ֽ�������ʽ
+ *
+ * @param ip �ַ�����ʽ��ip
+ * @return �ֽ�������ʽ��ip
+ */
+ public static byte[] getIpByteArrayFromString(String ip) {
+ byte[] ret = new byte[4];
+ java.util.StringTokenizer st = new java.util.StringTokenizer(ip, ".");
+ try {
+ ret[0] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
+ ret[1] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
+ ret[2] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
+ ret[3] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ return ret;
+ }
+
+ /**
+ * @param ip ip���ֽ�������ʽ
+ * @return �ַ�����ʽ��ip
+ */
+ public static String getIpStringFromBytes(byte[] ip) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(ip[0] & 0xFF);
+ sb.append('.');
+ sb.append(ip[1] & 0xFF);
+ sb.append('.');
+ sb.append(ip[2] & 0xFF);
+ sb.append('.');
+ sb.append(ip[3] & 0xFF);
+ return sb.toString();
+ }
+
+ /**
+ * ��ԭʼ�ַ������б���ת�������ʧ�ܣ�����ԭʼ���ַ���
+ *
+ * @param s ԭʼ�ַ���
+ * @param srcEncoding Դ���뷽ʽ
+ * @param destEncoding Ŀ����뷽ʽ
+ * @return ת���������ַ�����ʧ�ܷ���ԭʼ�ַ���
+ */
+ public static String getString(String s, String srcEncoding, String destEncoding) {
+ try {
+ return new String(s.getBytes(srcEncoding), destEncoding);
+ } catch (UnsupportedEncodingException e) {
+ return s;
+ }
+ }
+
+ /**
+ * ����ij�ֱ��뷽ʽ���ֽ�����ת�����ַ���
+ *
+ * @param b �ֽ�����
+ * @param encoding ���뷽ʽ
+ * @return ���encoding��֧�֣�����һ��ȱʡ������ַ���
+ */
+ public static String getString(byte[] b, String encoding) {
+ try {
+ return new String(b, encoding);
+ } catch (UnsupportedEncodingException e) {
+ return new String(b);
+ }
+ }
+
+ /**
+ * ����ij�ֱ��뷽ʽ���ֽ�����ת�����ַ���
+ *
+ * @param b �ֽ�����
+ * @param offset Ҫת������ʼλ��
+ * @param len Ҫת���ij���
+ * @param encoding ���뷽ʽ
+ * @return ���encoding��֧�֣�����һ��ȱʡ������ַ���
+ */
+ public static String getString(byte[] b, int offset, int len, String encoding) {
+ try {
+ return new String(b, offset, len, encoding);
+ } catch (UnsupportedEncodingException e) {
+ return new String(b, offset, len);
+ }
+ }
+
+}
diff --git a/codes/javaee/javaee-jsp/src/main/resources/logback.xml b/codes/javaee/javaee-jsp/src/main/resources/logback.xml
new file mode 100644
index 00000000..677498f2
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/resources/logback.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5p] %c.%M - %m%n
+
+
+
+
+
+
+
+ logs/${FILE_NAME}-all.%d{yyyy-MM-dd}.log
+ 30
+
+
+
+
+ 30MB
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5p] %c.%M - %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/META-INF/MANIFEST.MF b/codes/javaee/javaee-jsp/src/main/webapp/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..254272e1
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/WEB-INF/META-INF/MANIFEST.MF b/codes/javaee/javaee-jsp/src/main/webapp/WEB-INF/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..254272e1
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/WEB-INF/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/WEB-INF/resources/jsp/index.jsp b/codes/javaee/javaee-jsp/src/main/webapp/WEB-INF/resources/jsp/index.jsp
new file mode 100644
index 00000000..473a1315
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/WEB-INF/resources/jsp/index.jsp
@@ -0,0 +1,16 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" %>
+
+
+
+
+ My JSP 'index.jsp' starting page
+
+
+
+
+
+
+
+This is my JSP page.
+
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/WEB-INF/web.xml b/codes/javaee/javaee-jsp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..a403cc53
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ HelloServlet
+ /examples/configuration.jsp
+
+ message
+ welcome to jsp
+
+ 1
+
+
+ HelloServlet
+ /config
+ /config.jsp
+
+
+
+
+ /WEB-INF/views/jsp/index.jsp
+
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/break.jsp b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/break.jsp
new file mode 100644
index 00000000..0c39bb74
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/break.jsp
@@ -0,0 +1,26 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" %>
+
+JSP Scriptlets
+
+
+<%
+ for (int i = 0; i < 5; i++) {
+%> break 所在的循环, i = <%= i %>。
+<%
+ if (i == 2) {
+ break;
+ }
+ }
+%> break 循环完毕,
+<%
+ for (int i = 0; i < 5; i++) {
+%> return 所在的循环, i = <%= i %>。
+<%
+ if (i == 2) {
+ return;
+ }
+ }
+%> return 循环完毕,
+
+
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/for.jsp b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/for.jsp
new file mode 100644
index 00000000..7f172314
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/for.jsp
@@ -0,0 +1,54 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" %>
+
+
+ 02.JSP语法 - for示例
+
+
+
+
+<%
+ Object[][] letters = {{true, "恭喜您注册的信息已经生效", "e_inn@163.com", "forbreak@163.com", "2007-8-8"},
+ {true, "JavaEE 7.0 release!", "admin@sun.com", "forbreak@163.com", "2007-6-24"},
+ {false, "来信已经收到,下周来面谈", "foo@bar.com", "forbreak@163.com", "2007-5-20"},
+ {false, "您有新的邮件", "blog@foo.bar.com", "forbreak@163.com", "2007-3-2"},};
+ String[] colors = {"#DDDDDD", "#AAAAAA",};
+%>
+
+
+
+ 标题
+ 发信人
+ 收信人
+ 时间
+
+ <%
+ for (int i = 0; i < letters.length; i++) {
+ Object[] letter = letters[i];
+ %>
+
+
+ <%
+ if (letter[0] == Boolean.TRUE) {
+ %>
+
+ <%
+ } else {
+ out.println(" ");
+ }
+ %>
+
+ <%= letter[1] %>
+
+ <%= letter[2] %>
+
+ <%= letter[3] %>
+
+ <%= letter[4] %>
+
+
+ <%
+ }
+ %>
+
+
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/if.jsp b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/if.jsp
new file mode 100644
index 00000000..5d5d19ff
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/if.jsp
@@ -0,0 +1,68 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+
+
+02.JSP语法 - if...else示例
+
+
+<%
+ String param = request.getParameter("param");
+ if ("1".equals(param)) {
+%>
+《破阵子·为孙同甫赋壮语以寄》
+【宋】辛弃疾
+醉里挑灯看剑,梦回吹角连营。
+八百里分麾下炙,五十弦翻塞外声,沙场秋点兵。
+马作的卢飞快,弓如霹雳弦惊。
+了却君王天下事,赢得生前身后名。可怜白发生!
+<%
+} else if ("2".equals(param)) {
+%>
+《青玉案·元夕》
+【宋】辛弃疾
+东风夜放花千树,更吹落,星如雨。
+宝马雕车香满路,凤箫声动,玉壶光转,一夜鱼龙舞。
+蛾儿雪柳黄金缕,笑语盈盈暗香去。
+众里寻他千百度,蓦然回首,那人却在,灯火阑珊处。
+<%
+} else if ("3".equals(param)) {
+%>
+《丑奴儿》
+【宋】辛弃疾
+少年不识愁滋味,爱上层楼,爱上层楼,为赋新词强说愁。
+而今识得愁滋味,欲说还休,欲说还休,却道天凉好个秋。
+<%
+} else if ("4".equals(param)) {
+%>
+《永遇乐》
+【宋】辛弃疾
+千古江山,英雄无觅,孙仲谋处。
+舞榭歌台,风流总被、雨打风吹去。
+斜阳草树,寻常巷陌,人道寄奴曾住。
+想当年,金戈铁马,气吞万里如虎。
+元嘉草草,封狼居胥,赢得仓皇北顾。
+四十三年,望中犹记,烽火扬州路。
+可堪回首,佛狸祠下,一片神鸦社鼓。
+凭谁问:廉颇老矣,尚能饭否?
+<%
+} else if ("5".equals(param)) {
+%>
+《南乡子》
+【宋】辛弃疾
+何处望神州,满眼风光北固楼。
+千古兴亡多少事,悠悠,不尽长江滚滚流。
+年少万兜鍪,坐断东南战未休。
+天下英雄谁敌手?曹刘,生子当如孙仲谋。
+<%
+} else {
+%>
+请使用参数 param=1,2,3,4,5 选择要显示的诗歌
+if.jsp?param=1
+if.jsp?param=2
+if.jsp?param=3
+if.jsp?param=4
+if.jsp?param=5
+<%
+ }
+%>
+
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/if2.jsp b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/if2.jsp
new file mode 100644
index 00000000..ac691dfe
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/if2.jsp
@@ -0,0 +1,17 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+<%! int day = 1; %>
+
+
+
+
+ 02.JSP语法 - if...else示例
+
+
+if...else示例
+<% if (day == 1 | day == 7) { %>
+今天是周末
+<% } else { %>
+今天不是周末
+<% } %>
+
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/scriptlet.jsp b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/scriptlet.jsp
new file mode 100644
index 00000000..38c13ab0
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/scriptlet.jsp
@@ -0,0 +1,16 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" %>
+
+JSP Scriptlets
+
+
+<%
+ int num = 10;
+ int result = 1;
+ for (int i = 1; i <= num; i++) {
+ result *= i;
+ }
+ out.println(" ");
+ out.println("数字 " + num + " 的阶乘为:" + result);
+%>
+
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/switch.jsp b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/switch.jsp
new file mode 100644
index 00000000..435c36b9
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/switch.jsp
@@ -0,0 +1,37 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8" %>
+<%! int day = 3; %>
+
+
+
+
+ 02.JSP语法 - switch...case示例
+
+
+Sswitch...case示例
+<%
+ switch (day) {
+ case 0:
+ out.println("星期天");
+ break;
+ case 1:
+ out.println("星期一");
+ break;
+ case 2:
+ out.println("星期二");
+ break;
+ case 3:
+ out.println("星期三");
+ break;
+ case 4:
+ out.println("星期四");
+ break;
+ case 5:
+ out.println("星期五");
+ break;
+ default:
+ out.println("星期六");
+ }
+%>
+
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/while.jsp b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/while.jsp
new file mode 100644
index 00000000..c35802eb
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/examples/02.grammar/while.jsp
@@ -0,0 +1,23 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" %>
+
+
+ JSP Scriptlets
+
+
+<%
+ java.util.List list = new java.util.ArrayList();
+
+ list.add("青青子衿");
+ list.add("悠悠我心");
+ list.add("但为君故");
+ list.add("沉吟至今");
+
+ java.util.Iterator it = list.iterator();
+
+ while (it.hasNext()) {
+%> <%= it.next() %>
+<%
+ }
+%>
+
+
diff --git a/codes/javaee/javaee-jsp/src/main/webapp/examples/03.directive/foot.jsp b/codes/javaee/javaee-jsp/src/main/webapp/examples/03.directive/foot.jsp
new file mode 100644
index 00000000..62f768cd
--- /dev/null
+++ b/codes/javaee/javaee-jsp/src/main/webapp/examples/03.directive/foot.jsp
@@ -0,0 +1,13 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" %>
+
+
+
+
+ Copyright 2017 &Zhang Peng
+
+
+
+
+
+