├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── checkstyle.xml ├── deploy_website.sh ├── pom.xml ├── progressbutton-samples ├── pom.xml └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── f2prateek │ │ └── progressbutton │ │ └── samples │ │ └── MainActivity.java │ └── res │ ├── drawable-hdpi │ └── icon.png │ ├── layout │ └── activity_main.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── progressbutton ├── pom.xml └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── f2prateek │ │ │ └── progressbutton │ │ │ └── ProgressButton.java │ └── res │ │ ├── drawable-xhdpi │ │ ├── pin_progress_pinned.png │ │ ├── pin_progress_shadow.png │ │ └── pin_progress_unpinned.png │ │ ├── values-v11 │ │ └── styles.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── f2prateek │ └── progressbutton │ └── ProgressButtonTest.java ├── sample-app-screenshot.png └── website ├── index.html └── static ├── app-theme.css ├── app.css ├── bootstrap-combined.min.css ├── bootstrap.min.js ├── html5shiv.min.js ├── icon-github.png ├── icon-progressbutton.png ├── jquery-maven-artifact.min.js ├── jquery.smooth-scroll.min.js ├── prettify.js ├── sample.png └── states.png /.gitignore: -------------------------------------------------------------------------------- 1 | #Ant 2 | build.xml 3 | ant.properties 4 | local.properties 5 | proguard.cfg 6 | proguard-project.txt 7 | 8 | ### Eclipse ### 9 | *.pydevproject 10 | .project 11 | .metadata 12 | bin/** 13 | tmp/** 14 | tmp/**/* 15 | *.tmp 16 | *.bak 17 | *.swp 18 | *~.nib 19 | local.properties 20 | .classpath 21 | .settings/ 22 | .loadpath 23 | 24 | # External tool builders 25 | .externalToolBuilders/ 26 | 27 | # Locally stored "Eclipse launch configurations" 28 | *.launch 29 | 30 | # CDT-specific 31 | .cproject 32 | 33 | # PDT-specific 34 | .buildpath 35 | 36 | ### Android ### 37 | # built application files 38 | *.apk 39 | *.ap_ 40 | 41 | # files for the dex VM 42 | *.dex 43 | 44 | # Java class files 45 | *.class 46 | 47 | # generated files 48 | bin/ 49 | gen/ 50 | 51 | # Local configuration file (sdk path, etc) 52 | local.properties 53 | 54 | # Eclipse project files 55 | .classpath 56 | .project 57 | 58 | # Proguard folder generated by Eclipse 59 | proguard/ 60 | 61 | # Proguard folder generated by Intellij 62 | proguard_logs/ 63 | 64 | # Intellij project files 65 | *.iml 66 | *.ipr 67 | *.iws 68 | .idea/ 69 | 70 | adt-bundle-windows-x86_64/ 71 | 72 | ### Java ### 73 | *.class 74 | 75 | # Package Files # 76 | *.jar 77 | *.war 78 | *.ear 79 | 80 | ### IntelliJ ### 81 | *.iml 82 | *.ipr 83 | *.iws 84 | .idea/ 85 | 86 | ### Windows ### 87 | # Windows image file caches 88 | Thumbs.db 89 | ehthumbs.db 90 | 91 | # Folder config file 92 | Desktop.ini 93 | 94 | # Recycle Bin used on file shares 95 | $RECYCLE.BIN/ 96 | 97 | ### Linux ### 98 | .* 99 | !.gitignore 100 | !.git* 101 | *~ 102 | 103 | 104 | ### OSX ### 105 | .DS_Store 106 | .AppleDouble 107 | .LSOverride 108 | Icon 109 | 110 | 111 | # Thumbnails 112 | ._* 113 | 114 | # Files that might appear on external disk 115 | .Spotlight-V100 116 | .Trashes 117 | 118 | ### Maven ### 119 | target/ 120 | 121 | ### Gradle ### 122 | # Exclude Folder List # 123 | .gradle/ 124 | build/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | android: 4 | components: 5 | - build-tools-20.0.0 6 | - android-16 7 | licenses: 8 | - android-sdk-license-5be876d5 9 | 10 | jdk: 11 | - oraclejdk7 12 | - oraclejdk8 13 | 14 | branches: 15 | except: 16 | - gh-pages 17 | 18 | notifications: 19 | email: false 20 | 21 | sudo: false 22 | 23 | cache: 24 | directories: 25 | - $HOME/.m2 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ProgressButton 2 | ============== 3 | 4 | ProgressButton is a custom progress indicator with a tiny footprint. 5 | The default implementation provides a pin progress button as seen on the [Android design site][1]. 6 | ![Pin Progress Button Android developer][4] 7 | 8 | Based on the sample code provided by [Roman Nurik][2] on [Google Code][3]. 9 | For more information, refer to the [website][5]. 10 | Try out the sample application in the `progressbutton-samples/` folder of the project. 11 | 12 | Developed By 13 | ============ 14 | 15 | * Prateek Srivastava - 16 | 17 | Download 18 | ============ 19 | 20 | TheGrab it via Maven: 21 | 22 | ```xml 23 | 24 | com.f2prateek.progressbutton 25 | progressbutton 26 | (insert latest version) 27 | 28 | ``` 29 | or Gradle: 30 | ```groovy 31 | compile 'com.f2prateek.progressbutton:progressbutton:(insert latest version)@aar' 32 | ``` 33 | 34 | License 35 | ======= 36 | 37 | Copyright 2013 Prateek Srivastava 38 | Copyright 2012 Roman Nurik 39 | 40 | Licensed under the Apache License, Version 2.0 (the "License"); 41 | you may not use this file except in compliance with the License. 42 | You may obtain a copy of the License at 43 | 44 | http://www.apache.org/licenses/LICENSE-2.0 45 | 46 | Unless required by applicable law or agreed to in writing, software 47 | distributed under the License is distributed on an "AS IS" BASIS, 48 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 49 | See the License for the specific language governing permissions and 50 | limitations under the License. 51 | 52 | 53 | [1]: https://developer.android.com/design/building-blocks/progress.html#custom-indicators 54 | [2]: https://plus.google.com/+RomanNurik/posts/TbCkqQN4AEk 55 | [3]: https://code.google.com/p/romannurik-code/source/browse/misc/pinprogress 56 | [4]: https://developer.android.com/design/media/progress_activity_custom.png 57 | [5]: http://f2prateek.com/progressbutton/ -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /deploy_website.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | REPO="git@github.com:f2prateek/progressbutton.git" 6 | GROUP_ID="com.f2prateek.progressbutton" 7 | ARTIFACT_ID="progressbutton" 8 | 9 | DIR=temp-clone 10 | 11 | # Delete any existing temporary website clone 12 | rm -rf $DIR 13 | 14 | # Clone the current repo into temp folder 15 | git clone $REPO $DIR 16 | 17 | # Move working directory into temp folder 18 | cd $DIR 19 | 20 | # Checkout and track the gh-pages branch 21 | git checkout -t origin/gh-pages 22 | 23 | # Delete everything 24 | rm -rf * 25 | 26 | # Copy website files from real repo 27 | cp -R ../website/* . 28 | 29 | # Download the latest javadoc 30 | curl -L "http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=$GROUP_ID&a=$ARTIFACT_ID&v=LATEST&c=javadoc" > javadoc.zip 31 | mkdir javadoc 32 | unzip javadoc.zip -d javadoc 33 | rm javadoc.zip 34 | 35 | # Stage all files in git and create a commit 36 | git add . 37 | git add -u 38 | git commit -m "Website at $(date)" 39 | 40 | # Push the new files up to GitHub 41 | git push origin gh-pages 42 | 43 | # Delete our temp folder 44 | cd .. 45 | rm -rf $DIR 46 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 4.0.0 7 | 8 | 9 | org.sonatype.oss 10 | oss-parent 11 | 7 12 | 13 | 14 | com.f2prateek.progressbutton 15 | progressbutton-parent 16 | pom 17 | 2.1.1-SNAPSHOT 18 | 19 | Android Progress Button (Parent) 20 | ProgressButton is a custom progress indicator with a tiny footprint. 21 | https://github.com/f2prateek/progressbutton 22 | 2012 23 | 24 | 25 | progressbutton 26 | progressbutton-samples 27 | 28 | 29 | 30 | https://github.com/f2prateek/progressbutton 31 | scm:git:git://github.com/f2prateek/progressbutton.git 32 | scm:git:git@github.com:f2prateek/progressbutton.git 33 | HEAD 34 | 35 | 36 | 37 | 38 | Apache License Version 2.0 39 | http://www.apache.org/licenses/LICENSE-2.0.txt 40 | repo 41 | 42 | 43 | 44 | 45 | GitHub Issues 46 | https://github.com/f2prateek/progressbutton/issues 47 | 48 | 49 | 50 | UTF-8 51 | UTF-8 52 | 53 | 1.6 54 | 4.1.1.4 55 | 19 56 | 57 | 4.10 58 | 2.2 59 | 2.0M10 60 | 1.9.5 61 | 62 | 63 | 64 | 65 | 66 | com.google.android 67 | android 68 | ${android.version} 69 | 70 | 71 | junit 72 | junit 73 | ${junit.version} 74 | 75 | 76 | org.robolectric 77 | robolectric 78 | ${robolectric.version} 79 | 80 | 81 | org.easytesting 82 | fest-assert-core 83 | ${fest.version} 84 | 85 | 86 | org.mockito 87 | mockito-core 88 | ${mockito.version} 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.apache.maven.plugins 98 | maven-compiler-plugin 99 | 3.0 100 | 101 | ${java.version} 102 | ${java.version} 103 | 104 | 105 | 106 | 107 | com.simpligility.maven.plugins 108 | android-maven-plugin 109 | 4.1.1 110 | 111 | 112 | ${android.platform} 113 | 114 | 115 | true 116 | 117 | 118 | 119 | 120 | 121 | 122 | org.apache.maven.plugins 123 | maven-release-plugin 124 | 2.4.2 125 | 126 | true 127 | 128 | 129 | 130 | 131 | org.apache.maven.plugins 132 | maven-checkstyle-plugin 133 | 2.11 134 | 135 | true 136 | 137 | ../checkstyle.xml 138 | true 139 | 140 | 141 | 142 | verify 143 | 144 | checkstyle 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /progressbutton-samples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 4.0.0 7 | 8 | 9 | com.f2prateek.progressbutton 10 | progressbutton-parent 11 | 2.1.1-SNAPSHOT 12 | ../pom.xml 13 | 14 | 15 | progressbutton-samples 16 | Progress Button Samples 17 | apk 18 | 19 | 20 | 21 | com.google.android 22 | android 23 | provided 24 | 25 | 26 | com.f2prateek.progressbutton 27 | progressbutton 28 | ${project.version} 29 | apklib 30 | 31 | 32 | 33 | 34 | 35 | 36 | com.simpligility.maven.plugins 37 | android-maven-plugin 38 | true 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /progressbutton-samples/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /progressbutton-samples/src/main/java/com/f2prateek/progressbutton/samples/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.f2prateek.progressbutton.samples; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.widget.CompoundButton; 6 | import android.widget.LinearLayout; 7 | import android.widget.SeekBar; 8 | import android.widget.ToggleButton; 9 | import com.f2prateek.progressbutton.ProgressButton; 10 | 11 | /** 12 | * Examples. 13 | * 1 : Default 14 | * 2 : Pinned 15 | * 3 : Pinned Clickable 16 | * 4 : Colored Clickable 17 | *

18 | * Row 1 : from xml 19 | * Row 2 : from code (duplicating row 1) 20 | */ 21 | public class MainActivity extends Activity { 22 | public void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_main); 25 | 26 | final ProgressButton progressButton1 = (ProgressButton) findViewById(R.id.pin_progress_1); 27 | final ProgressButton progressButton2 = (ProgressButton) findViewById(R.id.pin_progress_2); 28 | final ProgressButton progressButton3 = (ProgressButton) findViewById(R.id.pin_progress_3); 29 | final ProgressButton progressButton4 = (ProgressButton) findViewById(R.id.pin_progress_4); 30 | 31 | final ProgressButton progressButton9 = (ProgressButton) findViewById(R.id.pin_progress_9); 32 | final ProgressButton progressButton10 = (ProgressButton) findViewById(R.id.pin_progress_10); 33 | 34 | final LinearLayout container = (LinearLayout) findViewById(R.id.container); 35 | 36 | /** 37 | * Default implementation of the {@link ProgressButton}. 38 | * By default, the {@link ProgressButton} is not clickable, and is unpinned. 39 | */ 40 | final ProgressButton progressButton5 = addProgressButton(container); 41 | 42 | /** 43 | * A {@link ProgressButton} that starts pinned, and is not clickable, so it 44 | * stays pinned. 45 | * 46 | * @see ProgressButton#setPinned(boolean) 47 | * @see View#setClickable(boolean) 48 | */ 49 | final ProgressButton progressButton6 = addProgressButton(container); 50 | progressButton6.setPinned(true); 51 | 52 | /** 53 | * A progress button that starts pinned, and is clickable, so it's 54 | * pinned state can be changed by the user. 55 | * @see ProgressButton#setPinned(boolean) 56 | * @see View#setClickable(boolean) 57 | */ 58 | final ProgressButton progressButton7 = addProgressButton(container); 59 | progressButton7.setPinned(true); 60 | progressButton7.setClickable(true); 61 | progressButton7.setFocusable(true); 62 | 63 | /** 64 | * An example of how to use style the button in code. 65 | * @see ProgressButton#setPinnedDrawable(android.graphics.drawable.Drawable) 66 | * @see ProgressButton#setUnpinnedDrawable(android.graphics.drawable.Drawable) 67 | * @see ProgressButton#setShadowDrawable(android.graphics.drawable.Drawable) 68 | * @see ProgressButton#setProgressColor(int) 69 | * @see ProgressButton#setCircleColor(int) 70 | */ 71 | final ProgressButton progressButton8 = addProgressButton(container); 72 | progressButton8.setProgressColor(getResources().getColor(R.color.holo_green_light)); 73 | progressButton8.setCircleColor(getResources().getColor(R.color.holo_green_dark)); 74 | progressButton8.setClickable(true); 75 | progressButton8.setFocusable(true); 76 | 77 | CompoundButton.OnCheckedChangeListener checkedChangeListener = 78 | new CompoundButton.OnCheckedChangeListener() { 79 | @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { 80 | updatePinProgressContentDescription((ProgressButton) compoundButton); 81 | } 82 | }; 83 | 84 | progressButton1.setOnCheckedChangeListener(checkedChangeListener); 85 | progressButton2.setOnCheckedChangeListener(checkedChangeListener); 86 | progressButton3.setOnCheckedChangeListener(checkedChangeListener); 87 | progressButton4.setOnCheckedChangeListener(checkedChangeListener); 88 | progressButton5.setOnCheckedChangeListener(checkedChangeListener); 89 | progressButton6.setOnCheckedChangeListener(checkedChangeListener); 90 | progressButton7.setOnCheckedChangeListener(checkedChangeListener); 91 | progressButton8.setOnCheckedChangeListener(checkedChangeListener); 92 | 93 | SeekBar progressSeekBar = (SeekBar) findViewById(R.id.progress_seek_bar); 94 | progressSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 95 | @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 96 | updateProgressButton(progressButton1, seekBar); 97 | updateProgressButton(progressButton2, seekBar); 98 | updateProgressButton(progressButton3, seekBar); 99 | updateProgressButton(progressButton4, seekBar); 100 | updateProgressButton(progressButton5, seekBar); 101 | updateProgressButton(progressButton6, seekBar); 102 | updateProgressButton(progressButton7, seekBar); 103 | updateProgressButton(progressButton8, seekBar); 104 | updateProgressButton(progressButton9, seekBar); 105 | updateProgressButton(progressButton10, seekBar); 106 | } 107 | 108 | @Override public void onStartTrackingTouch(SeekBar seekBar) { 109 | } 110 | 111 | @Override public void onStopTrackingTouch(SeekBar seekBar) { 112 | } 113 | }); 114 | 115 | updateProgressButton(progressButton1, progressSeekBar); 116 | updateProgressButton(progressButton2, progressSeekBar); 117 | updateProgressButton(progressButton3, progressSeekBar); 118 | updateProgressButton(progressButton4, progressSeekBar); 119 | updateProgressButton(progressButton5, progressSeekBar); 120 | updateProgressButton(progressButton6, progressSeekBar); 121 | updateProgressButton(progressButton7, progressSeekBar); 122 | updateProgressButton(progressButton8, progressSeekBar); 123 | updateProgressButton(progressButton9, progressSeekBar); 124 | updateProgressButton(progressButton10, progressSeekBar); 125 | 126 | final ToggleButton toggleButton = (ToggleButton) findViewById(R.id.toggle_button); 127 | toggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 128 | @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 129 | if (isChecked) { 130 | // Here, I explicitly call startAnimating 131 | // If you want a progress button that starts in an animating state, 132 | // use the `animating` attribute via xml and set it to true 133 | // You can control the animation speed, width of the strip that is displayed and 134 | // animation delay 135 | progressButton9.startAnimating(); 136 | progressButton10.startAnimating(); 137 | } else { 138 | progressButton9.stopAnimating(); 139 | progressButton10.stopAnimating(); 140 | } 141 | } 142 | }); 143 | } 144 | 145 | /** 146 | * Helper method to update the progressButton's progress and it's 147 | * content description. 148 | */ 149 | private void updateProgressButton(ProgressButton progressButton, SeekBar progressSeekBar) { 150 | progressButton.setProgress(progressSeekBar.getProgress()); 151 | updatePinProgressContentDescription(progressButton); 152 | } 153 | 154 | /** 155 | * Helper method to update the progressButton's content description. 156 | */ 157 | private void updatePinProgressContentDescription(ProgressButton button) { 158 | int progress = button.getProgress(); 159 | if (progress <= 0) { 160 | button.setContentDescription(getString( 161 | button.isChecked() ? R.string.content_desc_pinned_not_downloaded 162 | : R.string.content_desc_unpinned_not_downloaded)); 163 | } else if (progress >= button.getMax()) { 164 | button.setContentDescription(getString( 165 | button.isChecked() ? R.string.content_desc_pinned_downloaded 166 | : R.string.content_desc_unpinned_downloaded)); 167 | } else { 168 | button.setContentDescription(getString( 169 | button.isChecked() ? R.string.content_desc_pinned_downloading 170 | : R.string.content_desc_unpinned_downloading)); 171 | } 172 | } 173 | 174 | /** 175 | * Helper function that creates a new progress button, adds it to the given layout. 176 | * Returns a reference to the progress button for customization. 177 | */ 178 | private ProgressButton addProgressButton(LinearLayout container) { 179 | final LinearLayout.LayoutParams layoutParams = 180 | new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f); 181 | final ProgressButton progressButton = new ProgressButton(this); 182 | progressButton.setLayoutParams(layoutParams); 183 | container.addView(progressButton); 184 | return progressButton; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /progressbutton-samples/src/main/res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f2prateek/progressbutton/ca47a9676173cdab5b2d8063a947ed2cc3c7641d/progressbutton-samples/src/main/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /progressbutton-samples/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 21 | 22 | 28 | 29 | 33 | 34 | 39 | 40 | 41 | 45 | 46 | 50 | 56 | 62 | 68 | 74 | 75 | 76 | 80 | 81 | 86 | 87 | 93 | 94 | 98 | 104 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /progressbutton-samples/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | #ff99cc00 22 | #ff669900 23 | 24 | -------------------------------------------------------------------------------- /progressbutton-samples/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Progress Button 5 | 6 | Pinned to device and downloaded 7 | Pinned to device and downloading 8 | Pinned to device and waiting to download 9 | 10 | 11 | Temporarily downloaded 12 | Temporarily downloading 13 | Not downloaded 14 | 15 | Via XML: 16 | Via Code: 17 | Start Animating 18 | Stop Animating 19 | 20 | -------------------------------------------------------------------------------- /progressbutton-samples/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 21 | 24 | 28 | 33 | 37 | 44 | -------------------------------------------------------------------------------- /progressbutton/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 4.0.0 7 | 8 | 9 | com.f2prateek.progressbutton 10 | progressbutton-parent 11 | 2.1.1-SNAPSHOT 12 | ../pom.xml 13 | 14 | 15 | progressbutton 16 | Android Progress Button 17 | apklib 18 | 19 | 20 | 21 | com.google.android 22 | android 23 | provided 24 | 25 | 26 | junit 27 | junit 28 | test 29 | 30 | 31 | org.robolectric 32 | robolectric 33 | test 34 | 35 | 36 | org.easytesting 37 | fest-assert-core 38 | test 39 | 40 | 41 | org.mockito 42 | mockito-core 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | com.simpligility.maven.plugins 51 | android-maven-plugin 52 | true 53 | 54 | 55 | create-all-formats 56 | package 57 | 58 | aar 59 | apklib 60 | 61 | 62 | 63 | 64 | 65 | org.codehaus.mojo 66 | build-helper-maven-plugin 67 | 68 | 69 | package 70 | 71 | attach-artifact 72 | 73 | 74 | 75 | 76 | aar 77 | ${project.build.directory}/${project.build.finalName}.aar 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /progressbutton/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /progressbutton/src/main/java/com/f2prateek/progressbutton/ProgressButton.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Prateek Srivastava (@f2prateek) 3 | * Copyright 2012 Roman Nurik 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.f2prateek.progressbutton; 19 | 20 | import android.content.Context; 21 | import android.content.res.Resources; 22 | import android.content.res.TypedArray; 23 | import android.graphics.Canvas; 24 | import android.graphics.Paint; 25 | import android.graphics.Rect; 26 | import android.graphics.RectF; 27 | import android.graphics.drawable.Drawable; 28 | import android.os.Handler; 29 | import android.os.Message; 30 | import android.os.Parcel; 31 | import android.os.Parcelable; 32 | import android.util.AttributeSet; 33 | import android.widget.CompoundButton; 34 | 35 | /** 36 | * A two-state button that indicates whether some related content is pinned 37 | * (the checked state) or unpinned (the unchecked state), and the download 38 | * progress for this content. 39 | *

40 | * See Android 41 | * Design: Progress & Activity for more details on this custom 42 | * indicator. 43 | *

44 | * The example on the website is also the default visual implementation that is provided. 45 | * By default the button is not clickable (by the user). If you want the user to be able to control 46 | * the state, 47 | * use {@link android.view.View#setClickable(boolean)}. 48 | * 49 | * @see android.view.View#setClickable(boolean) 50 | * @see android.view.View#setFocusable(boolean) 51 | */ 52 | public class ProgressButton extends CompoundButton { 53 | 54 | /** The maximum progress. Defaults to 100. */ 55 | private int mMax = 100; 56 | /** The current progress. Defaults to 0. */ 57 | private int mProgress = 0; 58 | /** The drawable used as the shadow. */ 59 | private Drawable mShadowDrawable; 60 | /** The drawable displayed when the user unpins an item. */ 61 | private Drawable mUnpinnedDrawable; 62 | /** The drawable displayed when the user pins an item. */ 63 | private Drawable mPinnedDrawable; 64 | /** The paint for the circle. */ 65 | private Paint mCirclePaint; 66 | /** True if the view is animating. Defaults to false. */ 67 | private boolean mAnimating = false; 68 | /** Animation speed. Defaults to 1. */ 69 | private int mAnimationSpeed = 1; 70 | /** Delay between animation frames. Defaults to 50. */ 71 | private int mAnimationDelay = 50; 72 | /** Width of the animation strip. Defaults to 6. */ 73 | private int mAnimationStripWidth = 6; 74 | /** Internal variable to track animation state. */ 75 | private int mAnimationProgress = 0; 76 | 77 | /** 78 | * The paint to show the progress. 79 | * 80 | * @see #mProgress 81 | */ 82 | private Paint mProgressPaint; 83 | private Rect mTempRect = new Rect(); 84 | private RectF mTempRectF = new RectF(); 85 | private int mDrawableSize; 86 | private int mInnerSize; 87 | 88 | private Handler mAnimationHandler = new Handler() { 89 | /** 90 | * This is the code that will increment the progress variable 91 | * and so spin the wheel 92 | */ 93 | @Override public void handleMessage(Message msg) { 94 | if (mAnimating) { 95 | invalidate(); 96 | mAnimationProgress += mAnimationSpeed; 97 | if (mAnimationProgress > mMax) { 98 | mAnimationProgress = mProgress; 99 | } 100 | mAnimationHandler.sendEmptyMessageDelayed(0, mAnimationDelay); 101 | } 102 | } 103 | }; 104 | 105 | public ProgressButton(Context context) { 106 | this(context, null); 107 | } 108 | 109 | public ProgressButton(Context context, AttributeSet attrs) { 110 | this(context, attrs, R.attr.progressButtonStyle); 111 | } 112 | 113 | public ProgressButton(Context context, AttributeSet attrs, int defStyle) { 114 | super(context, attrs, defStyle); 115 | init(context, attrs, defStyle); 116 | } 117 | 118 | /** 119 | * Initialise the {@link ProgressButton}. 120 | * 121 | * @param context the application environment 122 | * @param attrs Attribute Set provided 123 | * @param defStyle The style resource to pull from 124 | */ 125 | private void init(Context context, AttributeSet attrs, int defStyle) { 126 | // Attribute initialization 127 | final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProgressButton, defStyle, 128 | R.style.ProgressButton_Pin_Compat); 129 | final Resources res = getResources(); 130 | 131 | mProgress = a.getInteger(R.styleable.ProgressButton_progress, mProgress); 132 | mMax = a.getInteger(R.styleable.ProgressButton_max, mMax); 133 | 134 | int circleColor = res.getColor(R.color.progress_default_circle_color); 135 | circleColor = a.getColor(R.styleable.ProgressButton_circleColor, circleColor); 136 | int progressColor = res.getColor(R.color.progress_default_progress_color); 137 | progressColor = a.getColor(R.styleable.ProgressButton_progressColor, progressColor); 138 | 139 | int pinnedDrawable = 140 | a.getResourceId(R.styleable.ProgressButton_pinnedDrawable, R.drawable.pin_progress_pinned); 141 | mPinnedDrawable = res.getDrawable(pinnedDrawable); 142 | mPinnedDrawable.setCallback(this); 143 | 144 | int unpinnedDrawable = a.getResourceId(R.styleable.ProgressButton_unpinnedDrawable, 145 | R.drawable.pin_progress_unpinned); 146 | mUnpinnedDrawable = res.getDrawable(unpinnedDrawable); 147 | mUnpinnedDrawable.setCallback(this); 148 | 149 | int shadowDrawable = 150 | a.getResourceId(R.styleable.ProgressButton_shadowDrawable, R.drawable.pin_progress_shadow); 151 | mShadowDrawable = res.getDrawable(shadowDrawable); 152 | mShadowDrawable.setCallback(this); 153 | 154 | mInnerSize = res.getDimensionPixelSize(R.dimen.progress_inner_size); 155 | mInnerSize = a.getDimensionPixelSize(R.styleable.ProgressButton_innerSize, mInnerSize); 156 | 157 | setChecked(a.getBoolean(R.styleable.ProgressButton_pinned, false)); 158 | setClickable(a.getBoolean(R.styleable.ProgressButton_android_clickable, false)); 159 | setFocusable(a.getBoolean(R.styleable.ProgressButton_android_focusable, false)); 160 | setBackgroundDrawable(a.getDrawable(R.styleable.ProgressButton_android_background)); 161 | 162 | mAnimating = a.getBoolean(R.styleable.ProgressButton_animating, mAnimating); 163 | mAnimationSpeed = a.getInteger(R.styleable.ProgressButton_animationSpeed, mAnimationSpeed); 164 | mAnimationDelay = a.getInteger(R.styleable.ProgressButton_animationDelay, mAnimationDelay); 165 | mAnimationStripWidth = 166 | a.getInteger(R.styleable.ProgressButton_animationStripWidth, mAnimationStripWidth); 167 | 168 | a.recycle(); 169 | 170 | mDrawableSize = mShadowDrawable.getIntrinsicWidth(); 171 | 172 | mCirclePaint = new Paint(); 173 | mCirclePaint.setColor(circleColor); 174 | mCirclePaint.setAntiAlias(true); 175 | 176 | mProgressPaint = new Paint(); 177 | mProgressPaint.setColor(progressColor); 178 | mProgressPaint.setAntiAlias(true); 179 | 180 | if (mAnimating) { 181 | startAnimating(); 182 | } 183 | } 184 | 185 | /** Returns the maximum progress value. */ 186 | public int getMax() { 187 | return mMax; 188 | } 189 | 190 | /** Sets the maximum progress value. Defaults to 100. */ 191 | public void setMax(int max) { 192 | if (max <= 0 || max < mProgress) { 193 | throw new IllegalArgumentException( 194 | String.format("Max (%d) must be > 0 and >= %d", max, mProgress)); 195 | } 196 | mMax = max; 197 | invalidate(); 198 | } 199 | 200 | /** Returns the current progress from 0 to max. */ 201 | public int getProgress() { 202 | return mProgress; 203 | } 204 | 205 | /** 206 | * Sets the current progress (must be between 0 and max). 207 | * 208 | * @see #setMax(int) 209 | */ 210 | public void setProgress(int progress) { 211 | if (progress > mMax || progress < 0) { 212 | throw new IllegalArgumentException( 213 | String.format("Progress (%d) must be between %d and %d", progress, 0, mMax)); 214 | } 215 | mProgress = progress; 216 | invalidate(); 217 | } 218 | 219 | /** 220 | * Sets the current progress and maximum progress value, both of which 221 | * must be valid values. 222 | * 223 | * @see #setMax(int) 224 | * @see #setProgress(int) 225 | */ 226 | public void setProgressAndMax(int progress, int max) { 227 | if (progress > max || progress < 0) { 228 | throw new IllegalArgumentException( 229 | String.format("Progress (%d) must be between %d and %d", progress, 0, max)); 230 | } else if (max <= 0) { 231 | throw new IllegalArgumentException(String.format("Max (%d) must be > 0", max)); 232 | } 233 | 234 | mProgress = progress; 235 | mMax = max; 236 | invalidate(); 237 | } 238 | 239 | /** Get the color used to display the progress level. */ 240 | public int getProgressColor() { 241 | return mProgressPaint.getColor(); 242 | } 243 | 244 | /** Sets the color used to display the progress level. */ 245 | public void setProgressColor(int progressColor) { 246 | mProgressPaint.setColor(progressColor); 247 | invalidate(); 248 | } 249 | 250 | /** Get the color used to display the progress background. */ 251 | public int getCircleColor() { 252 | return mCirclePaint.getColor(); 253 | } 254 | 255 | /** Sets the color used to display the progress background. */ 256 | public void setCircleColor(int circleColor) { 257 | mCirclePaint.setColor(circleColor); 258 | invalidate(); 259 | } 260 | 261 | /** Get the drawable that is displayed when the item is pinned. */ 262 | public Drawable getPinnedDrawable() { 263 | return mPinnedDrawable; 264 | } 265 | 266 | /** Set the drawable that is displayed when the item is pinned. */ 267 | public void setPinnedDrawable(Drawable pinnedDrawable) { 268 | mPinnedDrawable = pinnedDrawable; 269 | invalidate(); 270 | } 271 | 272 | /** Get the drawable that is displayed when the item is unpinned. */ 273 | public Drawable getUnpinnedDrawable() { 274 | return mUnpinnedDrawable; 275 | } 276 | 277 | /** Set the drawable that is displayed when the item is unpinned. */ 278 | public void setUnpinnedDrawable(Drawable unpinnedDrawable) { 279 | mUnpinnedDrawable = unpinnedDrawable; 280 | invalidate(); 281 | } 282 | 283 | /** Get the drawable that is displayed as the shadow. */ 284 | public Drawable getShadowDrawable() { 285 | return mShadowDrawable; 286 | } 287 | 288 | /** Set the drawable that is displayed as the shadow. */ 289 | public void setShadowDrawable(Drawable shadowDrawable) { 290 | mShadowDrawable = shadowDrawable; 291 | mDrawableSize = mShadowDrawable.getIntrinsicWidth(); 292 | invalidate(); 293 | } 294 | 295 | public int getInnerSize() { 296 | return mInnerSize; 297 | } 298 | 299 | public void setInnerSize(int innerSize) { 300 | mInnerSize = innerSize; 301 | invalidate(); 302 | } 303 | 304 | /** 305 | * Get whether the button is pinned or not. 306 | * Equivalent to {@link CompoundButton#isChecked()} 307 | */ 308 | public boolean isPinned() { 309 | return isChecked(); 310 | } 311 | 312 | /** 313 | * Set whether the button is pinned or not. 314 | * Equivalent to {@link CompoundButton#setChecked(boolean)} 315 | */ 316 | public void setPinned(boolean pinned) { 317 | setChecked(pinned); 318 | invalidate(); 319 | } 320 | 321 | /** Returns true if the button is animating. */ 322 | public boolean isAnimating() { 323 | return mAnimating; 324 | } 325 | 326 | /** Get the animation speed. */ 327 | public int getAnimationSpeed() { 328 | return mAnimationSpeed; 329 | } 330 | 331 | /** Get the animation delay. */ 332 | public int getAnimationDelay() { 333 | return mAnimationDelay; 334 | } 335 | 336 | /** Get the width of the animation strip. */ 337 | public int getAnimationStripWidth() { 338 | return mAnimationStripWidth; 339 | } 340 | 341 | /** Set the animation speed. This speed controls what progress we jump by in the animation. */ 342 | public void setAnimationSpeed(int animationSpeed) { 343 | mAnimationSpeed = animationSpeed; 344 | } 345 | 346 | /** Set the delay of the animation. This controls the duration between each frame. */ 347 | public void setAnimationDelay(int animationDelay) { 348 | mAnimationDelay = animationDelay; 349 | } 350 | 351 | /** Set the width of the animation strip. */ 352 | public void setAnimationStripWidth(int animationStripWidth) { 353 | mAnimationStripWidth = animationStripWidth; 354 | } 355 | 356 | /** Start animating the button. */ 357 | public void startAnimating() { 358 | if (!mAnimating) { 359 | mAnimating = true; 360 | mAnimationProgress = mProgress; 361 | mAnimationHandler.sendEmptyMessage(0); 362 | } 363 | } 364 | 365 | /** Stop animating the button. */ 366 | public void stopAnimating() { 367 | mAnimating = false; 368 | mAnimationProgress = mProgress; 369 | mAnimationHandler.removeMessages(0); 370 | invalidate(); 371 | } 372 | 373 | @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 374 | setMeasuredDimension(resolveSize(mDrawableSize, widthMeasureSpec), 375 | resolveSize(mDrawableSize, heightMeasureSpec)); 376 | } 377 | 378 | @Override protected void drawableStateChanged() { 379 | super.drawableStateChanged(); 380 | if (mPinnedDrawable.isStateful()) { 381 | mPinnedDrawable.setState(getDrawableState()); 382 | } 383 | if (mUnpinnedDrawable.isStateful()) { 384 | mUnpinnedDrawable.setState(getDrawableState()); 385 | } 386 | if (mShadowDrawable.isStateful()) { 387 | mShadowDrawable.setState(getDrawableState()); 388 | } 389 | } 390 | 391 | @Override protected void onDraw(Canvas canvas) { 392 | super.onDraw(canvas); 393 | 394 | mTempRect.set(0, 0, mDrawableSize, mDrawableSize); 395 | mTempRect.offset((getWidth() - mDrawableSize) / 2, (getHeight() - mDrawableSize) / 2); 396 | 397 | mTempRectF.set(-0.5f, -0.5f, mInnerSize + 0.5f, mInnerSize + 0.5f); 398 | mTempRectF.offset((getWidth() - mInnerSize) / 2, (getHeight() - mInnerSize) / 2); 399 | 400 | canvas.drawArc(mTempRectF, 0, 360, true, mCirclePaint); 401 | canvas.drawArc(mTempRectF, -90, 360 * mProgress / mMax, true, mProgressPaint); 402 | 403 | if (mAnimating) { 404 | canvas.drawArc(mTempRectF, -90 + (360 * mAnimationProgress / mMax), mAnimationStripWidth, 405 | true, mProgressPaint); 406 | } 407 | 408 | Drawable iconDrawable = isChecked() ? mPinnedDrawable : mUnpinnedDrawable; 409 | iconDrawable.setBounds(mTempRect); 410 | iconDrawable.draw(canvas); 411 | 412 | mShadowDrawable.setBounds(mTempRect); 413 | mShadowDrawable.draw(canvas); 414 | } 415 | 416 | @Override public Parcelable onSaveInstanceState() { 417 | Parcelable superState = super.onSaveInstanceState(); 418 | if (isSaveEnabled()) { 419 | SavedState ss = new SavedState(superState); 420 | ss.mMax = mMax; 421 | ss.mProgress = mProgress; 422 | return ss; 423 | } 424 | return superState; 425 | } 426 | 427 | @Override public void onRestoreInstanceState(Parcelable state) { 428 | if (!(state instanceof SavedState)) { 429 | super.onRestoreInstanceState(state); 430 | return; 431 | } 432 | 433 | SavedState ss = (SavedState) state; 434 | super.onRestoreInstanceState(ss.getSuperState()); 435 | 436 | mMax = ss.mMax; 437 | mProgress = ss.mProgress; 438 | } 439 | 440 | /** A {@link android.os.Parcelable} representing the {@link ProgressButton}'s state. */ 441 | public static class SavedState extends BaseSavedState { 442 | public static final Creator CREATOR = new Creator() { 443 | @Override public SavedState createFromParcel(Parcel parcel) { 444 | return new SavedState(parcel); 445 | } 446 | 447 | @Override public SavedState[] newArray(int size) { 448 | return new SavedState[size]; 449 | } 450 | }; 451 | private int mProgress; 452 | private int mMax; 453 | 454 | public SavedState(Parcelable superState) { 455 | super(superState); 456 | } 457 | 458 | private SavedState(Parcel in) { 459 | super(in); 460 | mProgress = in.readInt(); 461 | mMax = in.readInt(); 462 | } 463 | 464 | @Override public void writeToParcel(Parcel out, int flags) { 465 | super.writeToParcel(out, flags); 466 | out.writeInt(mProgress); 467 | out.writeInt(mMax); 468 | } 469 | } 470 | } 471 | -------------------------------------------------------------------------------- /progressbutton/src/main/res/drawable-xhdpi/pin_progress_pinned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f2prateek/progressbutton/ca47a9676173cdab5b2d8063a947ed2cc3c7641d/progressbutton/src/main/res/drawable-xhdpi/pin_progress_pinned.png -------------------------------------------------------------------------------- /progressbutton/src/main/res/drawable-xhdpi/pin_progress_shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f2prateek/progressbutton/ca47a9676173cdab5b2d8063a947ed2cc3c7641d/progressbutton/src/main/res/drawable-xhdpi/pin_progress_shadow.png -------------------------------------------------------------------------------- /progressbutton/src/main/res/drawable-xhdpi/pin_progress_unpinned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f2prateek/progressbutton/ca47a9676173cdab5b2d8063a947ed2cc3c7641d/progressbutton/src/main/res/drawable-xhdpi/pin_progress_unpinned.png -------------------------------------------------------------------------------- /progressbutton/src/main/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /progressbutton/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /progressbutton/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | #80ffffff 20 | #33b5e5 21 | -------------------------------------------------------------------------------- /progressbutton/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 28dp 20 | 21 | -------------------------------------------------------------------------------- /progressbutton/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 40 | 41 | 42 | 45 | 46 | -------------------------------------------------------------------------------- /progressbutton/src/test/java/com/f2prateek/progressbutton/ProgressButtonTest.java: -------------------------------------------------------------------------------- 1 | package com.f2prateek.progressbutton; 2 | 3 | import android.app.Activity; 4 | import android.os.Parcelable; 5 | import android.widget.CompoundButton; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.robolectric.Robolectric; 10 | import org.robolectric.RobolectricTestRunner; 11 | import org.robolectric.annotation.Config; 12 | 13 | import static org.fest.assertions.api.Assertions.assertThat; 14 | import static org.fest.assertions.api.Fail.fail; 15 | import static org.mockito.Mockito.mock; 16 | import static org.mockito.Mockito.verify; 17 | 18 | @RunWith(RobolectricTestRunner.class) @Config(manifest = "src/main/AndroidManifest.xml") 19 | public class ProgressButtonTest { 20 | private Activity activity; 21 | private ProgressButton button; 22 | 23 | @Before public void setUp() { 24 | activity = Robolectric.buildActivity(Activity.class).create().get(); 25 | button = new ProgressButton(activity); 26 | } 27 | 28 | @Test public void pint() { 29 | button.setPinned(false); 30 | assertThat(button.isChecked()).isEqualTo(button.isPinned()).isFalse(); 31 | 32 | button.setPinned(true); 33 | assertThat(button.isChecked()).isEqualTo(button.isPinned()).isTrue(); 34 | 35 | button.setChecked(false); 36 | assertThat(button.isChecked()).isEqualTo(button.isPinned()).isFalse(); 37 | } 38 | 39 | @Test public void toggle() { 40 | // Setup 41 | button.setPinned(false); 42 | assertThat(button.isChecked()).isEqualTo(button.isPinned()).isFalse(); 43 | 44 | // Toggle twice and verify each time 45 | button.toggle(); 46 | assertThat(button.isChecked()).isEqualTo(button.isPinned()).isTrue(); 47 | button.toggle(); 48 | assertThat(button.isChecked()).isEqualTo(button.isPinned()).isFalse(); 49 | } 50 | 51 | @Test public void validProgressValueZero() { 52 | button.setProgress(0); 53 | assertThat(button.getProgress()).isEqualTo(0); 54 | } 55 | 56 | @Test public void validProgressValue() { 57 | button.setProgress(50); 58 | assertThat(button.getProgress()).isEqualTo(50); 59 | } 60 | 61 | @Test public void validProgressValueMax() { 62 | button.setProgress(100); 63 | assertThat(button.getProgress()).isEqualTo(100); 64 | } 65 | 66 | @Test public void invalidProgressValue() { 67 | try { 68 | button.setProgress(101); 69 | fail("Setting progress > max should throw"); 70 | } catch (IllegalArgumentException e) { 71 | } 72 | } 73 | 74 | @Test public void anotherInvalidProgressValue() { 75 | try { 76 | button.setProgress(-1); 77 | fail("Setting progress < 0 should throw"); 78 | } catch (IllegalArgumentException e) { 79 | } 80 | } 81 | 82 | @Test public void setMaxToUnderZero() { 83 | try { 84 | button.setMax(-1); 85 | fail("Setting max <= 0 should throw"); 86 | } catch (IllegalArgumentException e) { 87 | } 88 | } 89 | 90 | @Test public void setMaxToZero() { 91 | try { 92 | button.setMax(0); 93 | fail("Setting max <= 0 should throw"); 94 | } catch (IllegalArgumentException e) { 95 | } 96 | } 97 | 98 | @Test public void onCheckedChangeListenerIsNotified() { 99 | CompoundButton.OnCheckedChangeListener publisher = 100 | mock(CompoundButton.OnCheckedChangeListener.class); 101 | button.setOnCheckedChangeListener(publisher); 102 | button.setPinned(true); 103 | verify(publisher).onCheckedChanged(button, true); 104 | button.setPinned(false); 105 | verify(publisher).onCheckedChanged(button, false); 106 | } 107 | 108 | @Test public void onCheckedChangeListenerIsNotifiedOnToggle() { 109 | button.setPinned(true); 110 | CompoundButton.OnCheckedChangeListener publisher = 111 | mock(CompoundButton.OnCheckedChangeListener.class); 112 | button.setOnCheckedChangeListener(publisher); 113 | button.toggle(); 114 | verify(publisher).onCheckedChanged(button, false); 115 | } 116 | 117 | @Test public void onSaveInstanceState() { 118 | button.setProgress(72); 119 | button.setMax(842); 120 | final Parcelable parcelable = button.onSaveInstanceState(); 121 | button.setProgress(2); 122 | button.setMax(50); 123 | assertThat(button.getProgress()).isEqualTo(2); 124 | assertThat(button.getMax()).isEqualTo(50); 125 | 126 | button.onRestoreInstanceState(parcelable); 127 | assertThat(button.getProgress()).isEqualTo(72); 128 | assertThat(button.getMax()).isEqualTo(842); 129 | } 130 | 131 | @Test public void settingMaxLessThanProgress() { 132 | button.setProgress(25); 133 | try { 134 | button.setMax(10); 135 | fail("Setting max < progress should throw"); 136 | } catch (IllegalArgumentException e) { 137 | } 138 | } 139 | 140 | @Test public void settingProgressAndMax() { 141 | button.setMax(10); 142 | 143 | button.setProgressAndMax(20, 50); 144 | assertThat(button.getProgress()).isEqualTo(20); 145 | assertThat(button.getMax()).isEqualTo(50); 146 | 147 | button.setProgressAndMax(2, 5); 148 | assertThat(button.getProgress()).isEqualTo(2); 149 | assertThat(button.getMax()).isEqualTo(5); 150 | } 151 | 152 | @Test public void settingInvalidProgressAndMax() { 153 | try { 154 | button.setProgressAndMax(10, 5); 155 | fail("Setting progress > max should throw"); 156 | } catch (IllegalArgumentException e) { 157 | } 158 | 159 | try { 160 | button.setProgressAndMax(0, 0); 161 | fail("Setting max = 0 should throw"); 162 | } catch (IllegalArgumentException e) { 163 | } 164 | 165 | try { 166 | button.setProgressAndMax(-1, 10); 167 | fail("Setting progress < 0 should throw"); 168 | } catch (IllegalArgumentException e) { 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /sample-app-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f2prateek/progressbutton/ca47a9676173cdab5b2d8063a947ed2cc3c7641d/sample-app-screenshot.png -------------------------------------------------------------------------------- /website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Progress Button 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

16 |
17 |
18 |
19 |

Progress Button

20 |
21 |
22 | 23 | 27 | 28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |

A custom progress indicator with a tiny footprint for Android

36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | 45 |

Introduction

46 |

ProgressButton is a custom progress indicator with a tiny footprint. The default implementation provides a pin progress button as seen on the Android design site.

47 | 48 |

States

49 | 50 |

Sample application screenshot.

51 | 52 |

Usage

53 |

XML

54 |

Simply define ProgressButton in your layout files.

55 |
<com.f2prateek.progressbutton.ProgressButton
 56 |     android:layout_width="wrap_content"
 57 |     android:layout_height="wrap_content"/>
 58 | 
59 |

Java

60 |

Instantiate an instance of ProgressButton with a context.

61 |
ProgressButton button = new ProgressButton(context);
62 | 63 |

Customize

64 |

ProgressButton is highly customizable. You can supply your own resources to style it and give it a unique look. As a subclass of CompoundButton, you can use any of those attributes as well. Custom attributes defined for ProgressButton include:

65 |
    66 |
  • progress : The current progress to display as an int
  • 67 |
  • max : The maximum progress that can be displayed an int
  • 68 |
  • circleColor : The color to display the progress background
  • 69 |
  • progressColor : The color to indicate the progress
  • 70 |
  • pinnedDrawable : Drawable displayed when the button is unpinned
  • 71 |
  • unpinnedDrawable : Drawable displayed when the button is pinned
  • 72 |
  • shadowDrawable : Drawable that acts as the shadow of the button
  • 73 |
  • innerSize : Area inside the view that should be 74 | filled with the progressColor 75 |
  • 76 |
  • animating : boolean start the button 77 | in an animating mode 78 |
  • 79 |
  • animationStripWidth : Width of the animation bar 80 |
  • 81 |
  • animationSpeed : The progress with which each frame 82 | of the animation appears to 'jump' 83 |
  • 84 |
  • animationDelay : Delay before each animation frame. 85 |
  • 86 |
  • pinned : Whether the button is pinned or not
  • 87 |
88 | 89 |

Look under the /samples folder to see an example of how to use these attributes. The corresponding app is available on the Play Store.

90 | 91 | 92 |

Download

93 |

The source code to ProgressButton, its samples, and this website is available on GitHub.

94 | 95 |

Maven

96 |
<dependency>
 97 |     <groupId>com.f2prateek.progressbutton</groupId>
 98 |     <artifactId>progressbutton</artifactId>
 99 |     <version>(insert latest version)</version>
100 | </dependency>
101 |

Gradle

102 |
compile 'com.f2prateek.progressbutton:progressbutton:(insert latest version)@aar'
103 | 
104 | 105 |

Contributing

106 |

If you would like to contribute code you can do so through GitHub by forking the repository and sending a pull request.

107 |

When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. Please also make sure your code compiles by running mvn clean verify.

108 | 109 |

License

110 |
Copyright 2013 Prateek Srivastava
111 | Copyright 2012 Roman Nurik
112 | 
113 | Licensed under the Apache License, Version 2.0 (the "License");
114 | you may not use this file except in compliance with the License.
115 | You may obtain a copy of the License at
116 | 
117 | http://www.apache.org/licenses/LICENSE-2.0
118 | 
119 | Unless required by applicable law or agreed to in writing, software
120 | distributed under the License is distributed on an "AS IS" BASIS,
121 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
122 | See the License for the specific language governing permissions and
123 | limitations under the License.
124 |
125 |
126 |
127 | 133 | 137 |
138 |
139 |
140 |
141 |
142 | 143 | 144 | 145 | 146 | 147 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /website/static/app-theme.css: -------------------------------------------------------------------------------- 1 | /* http://www.colorhexa.com/b94948 */ 2 | 3 | /*** Primary ***/ 4 | 5 | header, 6 | #subtitle, 7 | a.dl { 8 | background-color: #b94948; 9 | } 10 | 11 | .content-nav li.active a, 12 | .content-nav li.active a:hover { 13 | border-left-color: #b94948; 14 | } 15 | 16 | /*** One step left on the monochromatic scale ***/ 17 | 18 | header menu li a:hover, 19 | a.dl:hover { 20 | background-color: #a74140; 21 | } 22 | a { 23 | color: #a74140; 24 | } 25 | 26 | /*** Three steps left on the monochromatic scale ***/ 27 | 28 | a:hover { 29 | color: #833332; 30 | } 31 | 32 | 33 | /****************************************************************\ 34 | **** Syntax highlighting styles ******************************** 35 | \****************************************************************/ 36 | 37 | .pln { color: #000; } 38 | .str { color: #953a39; } 39 | .kwd { color: #666; } 40 | .com { color: #953a39; } 41 | .typ { color: #222; } 42 | .lit { color: #953a39; } 43 | .pun { color: #888; } 44 | .opn { color: #888; } 45 | .clo { color: #888; } 46 | .tag { color: #953a39; } 47 | .atn { color: #606; } 48 | .atv { color: #080; } 49 | .dec { color: #606; } 50 | .var { color: #606; } 51 | .fun { color: #f00; } 52 | -------------------------------------------------------------------------------- /website/static/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Roboto', sans-serif; 3 | font-size: 15px; 4 | } 5 | body { 6 | background-color: #f6f6f6; 7 | padding-bottom: 50px; 8 | padding-top: 80px; 9 | } 10 | 11 | header { 12 | min-height: 80px; 13 | color: #f6f6f6; 14 | position: fixed; 15 | top: 0; 16 | left: 0; 17 | width: 100%; 18 | z-index: 99; 19 | } 20 | header h1 { 21 | margin: 10px 0; 22 | font-size: 50px; 23 | line-height: 60px; 24 | font-weight: 100; 25 | text-rendering: auto; 26 | } 27 | header menu { 28 | margin: 20px 0 0; 29 | padding: 0; 30 | height: 40px; 31 | } 32 | header menu ul { 33 | margin: 0; 34 | padding: 0; 35 | float: right; 36 | } 37 | header menu li { 38 | list-style: none; 39 | float: left; 40 | margin: 0; 41 | padding: 0; 42 | } 43 | header menu li a { 44 | display: inline-block; 45 | height: 40px; 46 | font-size: 17px; 47 | line-height: 40px; 48 | padding: 0 20px; 49 | color: #f6f6f6; 50 | } 51 | header menu li a:hover { 52 | color: #f6f6f6; 53 | text-decoration: none; 54 | } 55 | header menu li a img { 56 | margin: 0; 57 | padding: 5px 0; 58 | vertical-align: bottom; 59 | width: 30px; 60 | height: 30px; 61 | } 62 | 63 | #subtitle { 64 | position: absolute; 65 | top: 80px; 66 | left: 0; 67 | width: 100%; 68 | } 69 | h2 { 70 | font-weight: 200; 71 | font-size: 26px; 72 | line-height: 30px; 73 | padding: 15px 0; 74 | margin: 0; 75 | color: #eee; 76 | } 77 | h2 strong { 78 | font-weight: 300; 79 | } 80 | 81 | a.dl { 82 | font-weight: 300; 83 | font-size: 30px; 84 | line-height: 40px; 85 | padding: 3px 10px; 86 | display: inline-block; 87 | border-radius: 6px; 88 | color: #f0f0f0; 89 | margin: 5px 0; 90 | } 91 | a.dl:hover { 92 | color: #f0f0f0; 93 | text-decoration: none; 94 | } 95 | 96 | .content-nav { 97 | margin-top: 130px; 98 | width: 220px; 99 | } 100 | .content-nav.affix { 101 | top: 0; 102 | } 103 | .content-nav li.active a, .content-nav li.active a:hover { 104 | background-color: transparent; 105 | color: #555; 106 | border-left-width: 2px; 107 | } 108 | .content-nav .secondary a { 109 | color: #aaa; 110 | } 111 | .content-nav .secondary a:hover { 112 | color: #888; 113 | } 114 | 115 | h3 { 116 | font-weight: 300; 117 | font-style: italic; 118 | color: #888; 119 | font-size: 20px; 120 | padding-top: 115px; 121 | margin-top: 0; 122 | } 123 | 124 | h4 { 125 | font-weight: 400; 126 | text-transform: uppercase; 127 | color: #888; 128 | font-size: 15px; 129 | padding-top: 20px; 130 | } 131 | 132 | p.license { 133 | font-family: fixed-width; 134 | } 135 | 136 | .row .logo { 137 | text-align: center; 138 | margin-top: 150px; 139 | } 140 | .row .logo img { 141 | height: 30px; 142 | } 143 | 144 | pre, code { 145 | color: #666; 146 | } 147 | code { 148 | border: 0; 149 | background-color: transparent; 150 | } 151 | 152 | .screenshot { 153 | text-align: center; 154 | } 155 | 156 | /* Widescreen desktop. */ 157 | @media (min-width: 1200px) { 158 | .content-nav { 159 | width: 270px; 160 | } 161 | } 162 | 163 | /* Smaller width browser, tablets. */ 164 | @media (max-width: 979px) { 165 | .content-nav { 166 | width: 166px; 167 | } 168 | } 169 | 170 | /* One-column mobile display. */ 171 | @media (max-width: 767px) { 172 | header { 173 | position: absolute; 174 | top: 0; 175 | left: 0; 176 | width: 100%; 177 | padding-left: 20px; 178 | } 179 | header menu { 180 | display: none; 181 | } 182 | #subtitle { 183 | position: absolute; 184 | top: 80px; 185 | left: 0; 186 | width: 100%; 187 | padding-left: 20px; 188 | } 189 | .content-nav { 190 | display: none; 191 | } 192 | } -------------------------------------------------------------------------------- /website/static/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap.js by @fat & @mdo 3 | * Copyright 2012 Twitter, Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0.txt 5 | */ 6 | !function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||s.toggleClass("open"),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'

'}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length"+t+""})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'',item:'
  • ',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery); -------------------------------------------------------------------------------- /website/static/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.6.2pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",version:"3.6.2pre",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;do2.v?-1:1});var newVersions=[];for(var i=0;i0?e.push(this):(t[r](1),o=t[r]()>0,o&&e.push(this),t[r](0))}}),e.length||this.each(function(){"BODY"===this.nodeName&&(e=[this])}),"first"===t.el&&e.length>1&&(e=[e[0]]),e};l.fn.extend({scrollable:function(l){var t=r.call(this,{dir:l});return this.pushStack(t)},firstScrollable:function(l){var t=r.call(this,{el:"first",dir:l});return this.pushStack(t)},smoothScroll:function(e){e=e||{};var o=l.extend({},l.fn.smoothScroll.defaults,e),r=l.smoothScroll.filterPath(location.pathname);return this.unbind("click.smoothscroll").bind("click.smoothscroll",function(e){var n=this,s=l(this),c=o.exclude,i=o.excludeWithin,a=0,f=0,h=!0,u={},d=location.hostname===n.hostname||!n.hostname,m=o.scrollTarget||(l.smoothScroll.filterPath(n.pathname)||r)===r,p=t(n.hash);if(o.scrollTarget||d&&m&&p){for(;h&&c.length>a;)s.is(t(c[a++]))&&(h=!1);for(;h&&i.length>f;)s.closest(i[f++]).length&&(h=!1)}else h=!1;h&&(e.preventDefault(),l.extend(u,o,{scrollTarget:o.scrollTarget||p,link:n}),l.smoothScroll(u))}),this}}),l.smoothScroll=function(t,e){var o,r,n,s,c=0,i="offset",a="scrollTop",f={},h={};"number"==typeof t?(o=l.fn.smoothScroll.defaults,n=t):(o=l.extend({},l.fn.smoothScroll.defaults,t||{}),o.scrollElement&&(i="position","static"==o.scrollElement.css("position")&&o.scrollElement.css("position","relative"))),o=l.extend({link:null},o),a="left"==o.direction?"scrollLeft":a,o.scrollElement?(r=o.scrollElement,c=r[a]()):r=l("html, body").firstScrollable(),o.beforeScroll.call(r,o),n="number"==typeof t?t:e||l(o.scrollTarget)[i]()&&l(o.scrollTarget)[i]()[o.direction]||0,f[a]=n+c+o.offset,s=o.speed,"auto"===s&&(s=f[a]||r.scrollTop(),s/=o.autoCoefficent),h={duration:s,easing:o.easing,complete:function(){o.afterScroll.call(o.link,o)}},o.step&&(h.step=o.step),r.length?r.stop().animate(f,h):o.afterScroll.call(o.link,o)},l.smoothScroll.version=e,l.smoothScroll.filterPath=function(l){return l.replace(/^\//,"").replace(/(index|default).[a-zA-Z]{3,4}$/,"").replace(/\/$/,"")},l.fn.smoothScroll.defaults=o})(jQuery); -------------------------------------------------------------------------------- /website/static/prettify.js: -------------------------------------------------------------------------------- 1 | !function(){var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function S(a){function d(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=r[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),e=[],a= 3 | b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,f=b.length;a122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;ah[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(g(h[1])));c.push("]");return c.join("")}function s(e){for(var a=e.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],f=0,h=0;f=2&&e==="["?a[f]=b(l):e!=="\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var x=0,m=!1,j=!1,k=0,c=a.length;k=5&&"lang-"===w.substring(0,5))&&!(t&&typeof t[1]==="string"))f=!1,w="src";f||(r[z]=w)}h=c;c+=z.length;if(f){f=t[1];var l=z.indexOf(f),B=l+f.length;t[2]&&(B=z.length-t[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}a.g=k}var b={},s;(function(){for(var g=a.concat(d),j=[],k={},c=0,i=g.length;c=0;)b[n.charAt(e)]=r;r=r[1];n=""+r;k.hasOwnProperty(n)||(j.push(r),k[n]=q)}j.push(/[\S\s]/);s=S(j)})();var x=d.length;return g}function v(a){var d=[],g=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,q])):d.push(["com", 11 | /^#[^\n\r]*/,q,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,q]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));if(b=a.regexLiterals){var s=(b=b>1?"":"\n\r")?".":"[\\S\\s]";g.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+s+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+ 12 | s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/, 13 | q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d= 14 | c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | O=[N,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],E=[E,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],P=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | Q=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],W=[y,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/, 21 | V=/\S/,X=v({keywords:[M,O,E,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};p(X,["default-code"]);p(C([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-", 22 | /^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);p(C([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/], 23 | ["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);p(v({keywords:M,hashComments:!0,cStyleComments:!0,types:R}),["c","cc","cpp","cxx","cyc","m"]);p(v({keywords:"null,true,false"}),["json"]);p(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:R}), 24 | ["cs"]);p(v({keywords:N,cStyleComments:!0}),["java"]);p(v({keywords:y,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);p(v({keywords:Q, 25 | hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]); 26 | p(C([],[["str",/^[\S\s]+/]]),["regex"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:D.prettyPrintOne=function(a,d,g){var b=document.createElement("div");b.innerHTML="
    "+a+"
    ";b=b.firstChild;g&&J(b,g,!0);K({h:d,j:g,c:b,i:1}); 27 | return b.innerHTML},prettyPrint:D.prettyPrint=function(a,d){function g(){for(var b=D.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;i